diff --git a/.github/workflows/adapter-code-coverage.yml b/.github/workflows/adapter-code-coverage.yml index b74ecebb446..b19251d6c7f 100644 --- a/.github/workflows/adapter-code-coverage.yml +++ b/.github/workflows/adapter-code-coverage.yml @@ -10,12 +10,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: 1.20.5 - name: Checkout pull request branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{github.event.pull_request.head.ref}} @@ -23,25 +23,26 @@ jobs: - name: Get adapter directories id: get_directories - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: result-encoding: string script: | const utils = require('./.github/workflows/helpers/pull-request-utils.js') - function directoryExtractor(filepath) { - // extract directory name from filepath of the form adapters//*.go - if (filepath.startsWith("adapters/") && filepath.split("/").length > 2) { + function directoryExtractor(filepath, status) { + // extract directory name only if file is not removed and file is in adapters directory + if (status != "removed" && filepath.startsWith("adapters/") && filepath.split("/").length > 2) { return filepath.split("/")[1] } return "" } const helper = utils.diffHelper({github, context}) - const files = await helper.getDirectories(directoryExtractor) - return files.length == 0 ? "" : JSON.stringify(files); + const directories = await helper.getDirectories(directoryExtractor) + // run coverage for maximum of 2 directories + return (directories.length == 0 || directories.length > 2) ? "" : JSON.stringify(directories) - name: Run coverage tests id: run_coverage - if: ${{ steps.get_directories.outputs.result }} != "" + if: steps.get_directories.outputs.result != '' run: | git config --global url."https://${USERNAME}:${TOKEN}@git.pubmatic.com".insteadOf "https://git.pubmatic.com" @@ -74,14 +75,14 @@ jobs: USERNAME: ${{ secrets.PM_OPENWRAP_CICD_USERNAME }} - name: Checkout coverage-preview branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 ref: coverage-preview repository: PubMatic-OpenWrap/prebid-server - name: Commit coverage files to coverage-preview branch - if: ${{ steps.run_coverage.outputs.coverage_dir }} != "" + if: steps.run_coverage.outputs.coverage_dir != '' id: commit_coverage run: | directory=.github/preview/${{ github.run_id }}_$(date +%s) @@ -95,12 +96,12 @@ jobs: echo "remote_coverage_preview_dir=${directory}" >> $GITHUB_OUTPUT - name: Checkout master branch - if: ${{ steps.get_directories.outputs.result }} != "" + if: steps.get_directories.outputs.result != '' run: git checkout master - name: Add coverage summary to pull request - if: ${{ steps.run_coverage.outputs.coverage_dir }} != "" && ${{ steps.commit_coverage.outputs.remote_coverage_preview_dir }} != "" - uses: actions/github-script@v6 + if: steps.run_coverage.outputs.coverage_dir != '' && steps.commit_coverage.outputs.remote_coverage_preview_dir != '' + uses: actions/github-script@v7 with: script: | const utils = require('./.github/workflows/helpers/pull-request-utils.js') diff --git a/.github/workflows/helpers/pull-request-utils.js b/.github/workflows/helpers/pull-request-utils.js index 78ab0bb2bc5..73c80396473 100644 --- a/.github/workflows/helpers/pull-request-utils.js +++ b/.github/workflows/helpers/pull-request-utils.js @@ -1,7 +1,9 @@ const synchronizeEvent = "synchronize", openedEvent = "opened", completedStatus = "completed", - resultSize = 100 + resultSize = 100, + adminPermission = "admin", + writePermission = "write" class diffHelper { constructor(input) { @@ -213,8 +215,8 @@ class diffHelper { }) const directories = [] - for (const { filename } of data) { - const directory = directoryExtractor(filename) + for (const { filename, status } of data) { + const directory = directoryExtractor(filename, status) if (directory != "" && !directories.includes(directory)) { directories.push(directory) } @@ -407,8 +409,31 @@ class coverageHelper { } } +class userHelper { + constructor(input) { + this.owner = input.context.repo.owner + this.repo = input.context.repo.repo + this.github = input.github + this.user = input.user + } + + /* + Checks if the user has write permissions for the repository + @returns {boolean} - returns true if the user has write permissions, otherwise false + */ + async hasWritePermissions() { + const { data } = await this.github.rest.repos.getCollaboratorPermissionLevel({ + owner: this.owner, + repo: this.repo, + username: this.user, + }) + return data.permission === writePermission || data.permission === adminPermission + } +} + module.exports = { diffHelper: (input) => new diffHelper(input), semgrepHelper: (input) => new semgrepHelper(input), coverageHelper: (input) => new coverageHelper(input), + userHelper: (input) => new userHelper(input), } diff --git a/.github/workflows/issue_prioritization.yml b/.github/workflows/issue_prioritization.yml index 3843507b26e..ec58073d653 100644 --- a/.github/workflows/issue_prioritization.yml +++ b/.github/workflows/issue_prioritization.yml @@ -10,7 +10,7 @@ jobs: steps: - name: Generate token id: generate_token - uses: tibdex/github-app-token@36464acb844fc53b9b8b2401da68844f6b05ebb0 + uses: tibdex/github-app-token@v2.1.0 with: app_id: ${{ secrets.PBS_PROJECT_APP_ID }} private_key: ${{ secrets.PBS_PROJECT_APP_PEM }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fd790699a14..d3559732077 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,11 +6,12 @@ on: releaseType: type: choice options: + - major - minor - patch default: minor required: true - description: 'minor: v0.X.0, patch: v0.0.X' + description: 'major: vX.0.0, minor: v0.X.0, patch: v0.0.X' debug: type: boolean default: true @@ -24,13 +25,25 @@ jobs: permissions: contents: read steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + repository: ${{ github.repository }} + ref: master - name: Check user permission - uses: actions-cool/check-user-permission@v2.2.0 + uses: actions/github-script@v7 id: check with: - require: 'write' + github-token: ${{ secrets.GITHUB_TOKEN }} + result-encoding: string + script: | + const utils = require('./.github/workflows/helpers/pull-request-utils.js') + const helper = utils.userHelper({github, context, user: '${{ github.actor }}'}) + const hasPermission = await helper.hasWritePermissions() + return hasPermission outputs: - hasWritePermission: ${{ steps.check.outputs.require-result }} + hasWritePermission: ${{ steps.check.outputs.result }} build-master: name: Build master @@ -39,7 +52,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@master + uses: actions/checkout@v4 with: fetch-depth: 0 repository: ${{ github.repository }} @@ -51,13 +64,12 @@ jobs: publish-tag: name: Publish tag needs: build-master - if: contains(needs.check-permission.outputs.hasWritePermission, 'true') permissions: contents: write runs-on: ubuntu-latest steps: - name: Checkout Prebid Server - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Create & publish tag @@ -78,11 +90,18 @@ jobs: nextTag='' releaseType=${{ inputs.releaseType }} - if [ $releaseType == "minor" ]; then - # increment minor version and reset patch version - nextTag=$(echo "${currentTag}" | awk -F. '{OFS="."; $2+=1; $3=0; print $0}') + if [ $releaseType == "major" ]; then + # PBS-GO skipped the v1.0.0 major release - https://github.com/prebid/prebid-server/issues/3068 + # If the current tag is v0.x.x, the script sets the next release tag to v2.0.0 + # Otherwise, the script increments the major version by 1 and sets the minor and patch versions to zero + # For example, v2.x.x will be incremented to v3.0.0 + major=$(echo "${currentTag}" | awk -F. '{gsub(/^v/, "", $1); if($1 == 0) $1=2; else $1+=1; print $1}') + nextTag="v${major}.0.0" + elif [ $releaseType == "minor" ]; then + # Increment minor version and reset patch version + nextTag=$(echo "${currentTag}" | awk -F. '{OFS="."; $2+=1; $3=0; print $0}') else - # increment patch version + # Increment patch version nextTag=$(echo "${currentTag}" | awk -F. '{OFS="."; $3+=1; print $0}') fi @@ -103,7 +122,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout Prebid Server - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Build image diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index baa10b93963..09a0fd56791 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # Resolves to empty string for push events and falls back to HEAD. ref: ${{ github.event.pull_request.head.sha }} @@ -29,6 +29,6 @@ jobs: severity: 'CRITICAL,HIGH' - name: Upload Results To GitHub Security Tab - uses: github/codeql-action/upload-sarif@v2 + uses: github/codeql-action/upload-sarif@v3 with: sarif_file: 'trivy-results.sarif' \ No newline at end of file diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml index 565bb9b871a..b641d5667ed 100644 --- a/.github/workflows/semgrep.yml +++ b/.github/workflows/semgrep.yml @@ -9,7 +9,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 ref: ${{github.event.pull_request.head.ref}} @@ -17,7 +17,7 @@ jobs: - name: Calculate diff id: calculate_diff - uses: actions/github-script@v6 + uses: actions/github-script@v7 with: result-encoding: string script: | @@ -52,7 +52,7 @@ jobs: - name: Add pull request comment id: add_pull_request_comment if: contains(steps.should_run_semgrep.outputs.hasChanges, 'true') - uses: actions/github-script@v6.4.1 + uses: actions/github-script@v7 with: github-token: ${{ secrets.GITHUB_TOKEN }} result-encoding: string diff --git a/.github/workflows/validate-merge.yml b/.github/workflows/validate-merge.yml index 0fe3e5523d3..8e6ecfbb599 100644 --- a/.github/workflows/validate-merge.yml +++ b/.github/workflows/validate-merge.yml @@ -13,12 +13,12 @@ jobs: steps: - name: Install Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: 1.20.5 - name: Checkout Merged Branch - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Validate run: | diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index b28ac8ee5f8..a3d0f243548 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -16,12 +16,12 @@ jobs: steps: - name: Install Go - uses: actions/setup-go@v4 + uses: actions/setup-go@v5 with: go-version: ${{ matrix.go-version }} - name: Checkout Code - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: # Resolves to empty string for push events and falls back to HEAD. ref: ${{ github.event.pull_request.head.sha }} diff --git a/.gitignore b/.gitignore index 6d9482a87bd..51200f7c41d 100644 --- a/.gitignore +++ b/.gitignore @@ -30,8 +30,8 @@ vendor # build artifacts prebid-server -build -debug +/build +/debug __debug_bin # config files @@ -41,6 +41,7 @@ inventory_url.yaml # generated log files during tests analytics/config/testFiles/ analytics/config/xyz* +analytics/build/xyz* analytics/filesystem/testFiles/ # autogenerated version file diff --git a/Dockerfile b/Dockerfile index 090d9a79954..bff68b614b3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -20,7 +20,7 @@ RUN go mod tidy RUN go mod vendor ARG TEST="true" RUN if [ "$TEST" != "false" ]; then ./validate.sh ; fi -RUN go build -mod=vendor -ldflags "-X github.com/prebid/prebid-server/version.Ver=`git describe --tags | sed 's/^v//'` -X github.com/prebid/prebid-server/version.Rev=`git rev-parse HEAD`" . +RUN go build -mod=vendor -ldflags "-X github.com/prebid/prebid-server/v2/version.Ver=`git describe --tags | sed 's/^v//'` -X github.com/prebid/prebid-server/v2/version.Rev=`git rev-parse HEAD`" . FROM ubuntu:20.04 AS release LABEL maintainer="hans.hjort@xandr.com" diff --git a/Makefile b/Makefile index a8a6fc6204e..5ca684b4421 100644 --- a/Makefile +++ b/Makefile @@ -15,7 +15,7 @@ test: deps ifeq "$(adapter)" "" ./validate.sh else - go test github.com/prebid/prebid-server/adapters/$(adapter) -bench=. + go test github.com/prebid/prebid-server/v2/adapters/$(adapter) -bench=. endif # build-modules generates modules/builder.go file which provides a list of all available modules @@ -33,6 +33,10 @@ image: # format runs format format: ./scripts/format.sh -f true + +# formatcheck runs format for diagnostics, without modifying the code +formatcheck: + ./scripts/format.sh -f false mockgen: mockgeninstall mockgendb mockgencache mockgenmetrics @@ -43,20 +47,20 @@ mockgeninstall: mockgendb: mkdir -p modules/pubmatic/openwrap/database/mock modules/pubmatic/openwrap/database/mock_driver mockgen database/sql/driver Driver,Connector,Conn,DriverContext > modules/pubmatic/openwrap/database/mock_driver/mock.go - mockgen github.com/PubMatic-OpenWrap/prebid-server/modules/pubmatic/openwrap/database Database > modules/pubmatic/openwrap/database/mock/mock.go + mockgen github.com/PubMatic-OpenWrap/prebid-server/v2/modules/pubmatic/openwrap/database Database > modules/pubmatic/openwrap/database/mock/mock.go mockgencache: mkdir -p modules/pubmatic/openwrap/cache/mock - mockgen github.com/PubMatic-OpenWrap/prebid-server/modules/pubmatic/openwrap/cache Cache > modules/pubmatic/openwrap/cache/mock/mock.go + mockgen github.com/PubMatic-OpenWrap/prebid-server/v2/modules/pubmatic/openwrap/cache Cache > modules/pubmatic/openwrap/cache/mock/mock.go mockgenmetrics: mkdir -p modules/pubmatic/openwrap/metrics/mock - mockgen github.com/PubMatic-OpenWrap/prebid-server/modules/pubmatic/openwrap/metrics MetricsEngine > modules/pubmatic/openwrap/metrics/mock/mock.go + mockgen github.com/PubMatic-OpenWrap/prebid-server/v2/modules/pubmatic/openwrap/metrics MetricsEngine > modules/pubmatic/openwrap/metrics/mock/mock.go mockgenlogger: mkdir -p analytics/pubmatic/mhttp/mock - mockgen github.com/PubMatic-OpenWrap/prebid-server/analytics/pubmatic/mhttp HttpCallInterface,MultiHttpContextInterface > analytics/pubmatic/mhttp/mock/mock.go + mockgen github.com/PubMatic-OpenWrap/prebid-server/v2/analytics/pubmatic/mhttp HttpCallInterface,MultiHttpContextInterface > analytics/pubmatic/mhttp/mock/mock.go mockgenpublisherfeature: mkdir -p modules/pubmatic/openwrap/publisherfeature - mockgen github.com/PubMatic-OpenWrap/prebid-server/modules/pubmatic/openwrap/publisherfeature Feature > modules/pubmatic/openwrap/publisherfeature/mock/mock.go \ No newline at end of file + mockgen github.com/PubMatic-OpenWrap/prebid-server/v2/modules/pubmatic/openwrap/publisherfeature Feature > modules/pubmatic/openwrap/publisherfeature/mock/mock.go diff --git a/README.md b/README.md index b041a3a10c9..e4503083941 100644 --- a/README.md +++ b/README.md @@ -2,31 +2,44 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/prebid/prebid-server?style=flat-square)](https://goreportcard.com/report/github.com/prebid/prebid-server) ![Go Version](https://img.shields.io/github/go-mod/go-version/prebid/prebid-server?style=flat-square) -# Prebid Server +
+
+

Prebid Server Logo

+
-Prebid Server is an open source implementation of Server-Side Header Bidding. -It is managed by [Prebid.org](https://prebid.org/about/), -and upholds the principles from the [Prebid Code of Conduct](https://prebid.org/code-of-conduct/). +Prebid Server is an open-source solution for running real-time advertising auctions in the cloud. This project is part of the Prebid ecosystem, seamlessly integrating with Prebid.js and the Prebid Mobile SDKs to deliver world-class header bidding for any ad format and for any type of digital media. -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](./adapters) package. If you don't see the one you want, feel free to [contribute it](https://docs.prebid.org/prebid-server/developers/add-new-bidder-go.html). +## Getting Started +- [What is Prebid Server](https://docs.prebid.org/prebid-server/overview/prebid-server-overview.html) +- [Intro to Header Bidding](https://docs.prebid.org/overview/intro-to-header-bidding.html) +- [Header Bidding with Prebid](https://docs.prebid.org/overview/intro.html#header-bidding-with-prebid) +- [API Endpoints](https://docs.prebid.org/prebid-server/endpoints/pbs-endpoint-overview.html) + +## Configuring -For more information, see: +When hosting Prebid Server or developing locally, **you must set a default GDPR value**. This configuration determines whether GDPR is enabled when no regulatory signal is available in the request, where a value of `"0"` disables it by default and a value of `"1"` enables it. This is required as there is no consensus on a good default. -- [What is Prebid?](https://docs.prebid.org/overview/intro.html) -- [Prebid Server Overview](https://docs.prebid.org/prebid-server/overview/prebid-server-overview.html) -- [Current Bidders](https://docs.prebid.org/dev-docs/pbs-bidders.html) +Refer to the [configuration guide](docs/developers/configuration.md) for additional information and a list of available configuration options. -Please consider [registering your Prebid Server](https://docs.prebid.org/prebid-server/hosting/pbs-hosting.html#optional-registration) to get on the mailing list for updates, etc. +## Hosting Prebid Server +> [!NOTE] +> Please consider [registering as a Prebid Server host](https://docs.prebid.org/prebid-server/hosting/pbs-hosting.html#optional-registration) to join the mailing list for updates and feedback. -## Installation +The quickest way to host Prebid Server is to deploy our [official Docker image](https://hub.docker.com/r/prebid/prebid-server). If you're hosting the container with Kubernetes, you can configure Prebid Server with environment variables [using a pod file](https://kubernetes.io/docs/tasks/inject-data-application/define-interdependent-environment-variables/) or [using a config map](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#configure-all-key-value-pairs-in-a-configmap-as-container-environment-variables). Alternatively, you can use a configuration file [embedded in a config map](https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#populate-a-volume-with-data-stored-in-a-configmap) which Prebid Server will read from at the path `/etc/config`. -First install [Go](https://golang.org/doc/install) version 1.19 or newer. +For deploying a fork, you can create a custom Docker container using the command: +``` bash +docker build --platform linux/amd64 -t prebid-server . +``` +or compile a standalone binary using the command: +``` bash +go build . +``` +Ensure that you deploy the `/static` directory, as Prebid Server requires those files at startup. -Note that prebid-server is using [Go modules](https://blog.golang.org/using-go-modules). -We officially support the most recent two major versions of the Go runtime. However, if you'd like to use a version <1.13 and are inside GOPATH `GO111MODULE` needs to be set to `GO111MODULE=on`. +## Developing -Download and prepare Prebid Server: +Prebid Server requires [Go](https://go.dev) version 1.19 or newer. You can develop on any operating system that Go supports; however, please note that our helper scripts are written in bash. ```bash cd YOUR_DIRECTORY @@ -34,45 +47,62 @@ git clone https://github.com/PubMatic-OpenWrap/prebid-server src/github.com/PubM cd src/github.com/PubMatic-OpenWrap/prebid-server ``` -Run the automated tests: +3. Download Dependencies +``` bash +go mod download +``` +3. Verify Tests Pass ```bash ./validate.sh ``` -Or just run the server locally: - +4. Run The Server ```bash -go build . -./prebid-server +go run . ``` -Run format: -``` -make format -``` -or -```bash -./scripts/format.sh -f true -``` +By default, Prebid Server will attach to port 8000. To confirm the server is running, visit `http://localhost:8000/` in your web browser. + +### Code Style +To maintain consistency in the project's code, please: + +- Follow the recommendations set by [Effective Go](https://go.dev/doc/effective_go). This article provides a comprehensive guide on how to write idiomatic Go code, covering topics such as naming and formatting. Many IDEs will automatically format your code upon save. If you need to manaully format your code, either run the bash script or execute the make step: + ``` + ./scripts/format.sh -f true + ``` + ``` + make format + ``` + +- Prefer small functions with descriptive names instead of complex functions with comments. This approach helps make the code more readable, maintainable, and testable. -Load the landing page in your browser at `http://localhost:8000/`. -For the full API reference, see [the endpoint documentation](https://docs.prebid.org/prebid-server/endpoints/pbs-endpoint-overview.html) +- Do not discard errors. You should implement appropriate error handling, such as gracefully falling back to a default behavior or bubbling up an error. + +### IDE Recommendation -## Go Modules +An option for developing Prebid Server in a reproducible environment isolated from your host OS is using Visual Studio Code with [Remote Container Setup](devcontainer.md). This is a recommendation, not a requirement. This approach is especially useful if you are developing on Windows, since the Remote Container runs within WSL providing you with the capability to execute bash scripts. -The packages within this repository are intended to be used as part of the Prebid Server compiled binary. If you -choose to import Prebid Server packages in other projects, please understand we make no promises on the stability -of exported types. +## Importing Prebid Server + +Prebid Server is not currently intended to be imported by other projects. Go Modules is used to manage dependencies, which also makes it possible to import Prebid Server packages. This is not supported. We offer no guarantees regarding the stability of packages and do not adhere to semantic versioning guidelines. ## Contributing +> [!IMPORTANT] +> All contributions must follow the [Prebid Code of Conduct](https://prebid.org/code-of-conduct/) and the [Prebid Module Rules](https://docs.prebid.org/dev-docs/module-rules.html). -Want to [add an adapter](https://docs.prebid.org/prebid-server/developers/add-new-bidder-go.html)? Found a bug? Great! +### Bid Adapter +Bid Adapters transform OpenRTB requests and responses for communicating with a bidding server. This may be as simple as a passthrough or as complex as mapping to a custom data model. We invite you to contribute an adapter for your company. Consult our guide on [building a bid adapter](https://docs.prebid.org/prebid-server/developers/add-new-bidder-go.html) for more information. -Or better yet, [open a pull request](https://github.com/PubMatic-OpenWrap/prebid-server/compare) with the changes you'd like to see. +### Analytics Module +Analytics Modules enable business intelligence tools to collect data from Prebid Server to provide publishers and hosts with valuable insights into their header bidding traffic. We welcome you to contribute a module for your platform. Refer to our guide on [building an analytics module](https://docs.prebid.org/prebid-server/developers/pbs-build-an-analytics-adapter.html) for further information. -## IDE Recommendations +### Auction Module +Auction Modules allow hosts to extend the behavior of Prebid Server at specfic spots in the auction pipeline using existing modules or by developing custom functionality. Auction Modules may provide creative validation, traffic optimization, and real time data services amoung many other potential uses. We welcome vendors and community members to contribute modules that publishers and hosts may find useful. Consult our guide on [building an auction module](https://docs.prebid.org/prebid-server/developers/add-a-module.html) for more information. +Or better yet, [open a pull request](https://github.com/PubMatic-OpenWrap/prebid-server/compare) with the changes you'd like to see. -The quickest way to start developing Prebid Server in a reproducible environment isolated from your host OS -is by using Visual Studio Code with [Remote Container Setup](devcontainer.md). +### Feature +We welcome everyone to contribute to this project by implementing a specification or by proposing a new feature. Please review the [prioritized project board](https://github.com/orgs/prebid/projects/4), where you can select an issue labeled "Ready For Dev". To avoid redundant effort, kindly leave a comment on the issue stating your intention to take it on. To propose a feature, [open a new issue](https://github.com/prebid/prebid-server/issues/new/choose) with as much detail as possible for consideration by the Prebid Server Committee. +### Bug Fix +Bug reports may be submitted by [opening a new issue](https://github.com/prebid/prebid-server/issues/new/choose) and describing the error in detail with the steps to reproduce and example data. A member of the core development team will validate the bug and discuss next steps. You're encouraged to open an exploratory draft pull request to either demonstrate the bug by adding a test or offering a potential fix. diff --git a/account/account.go b/account/account.go index 156ad5e6d39..37d36761cca 100644 --- a/account/account.go +++ b/account/account.go @@ -2,32 +2,27 @@ package account import ( "context" - "encoding/json" "fmt" - "github.com/buger/jsonparser" + "github.com/prebid/go-gdpr/consentconstants" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/util/iputil" - jsonpatch "gopkg.in/evanphx/json-patch.v4" + + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/util/iputil" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) // GetAccount looks up the config.Account object referenced by the given accountID, with access rules applied func GetAccount(ctx context.Context, cfg *config.Configuration, fetcher stored_requests.AccountFetcher, accountID string, me metrics.MetricsEngine) (account *config.Account, errs []error) { - // Check BlacklistedAcctMap until we have deprecated it - if _, found := cfg.BlacklistedAcctMap[accountID]; found { - return nil, []error{&errortypes.BlacklistedAcct{ - Message: fmt.Sprintf("Prebid-server has disabled Account ID: %s, please reach out to the prebid server host.", accountID), - }} - } if cfg.AccountRequired && accountID == metrics.PublisherUnknown { return nil, []error{&errortypes.AcctRequired{ Message: fmt.Sprintf("Prebid-server has been configured to discard requests without a valid Account ID. Please reach out to the prebid server host."), }} } + if accountJSON, accErrs := fetcher.FetchAccount(ctx, cfg.AccountDefaultsJSON(), accountID); len(accErrs) > 0 || accountJSON == nil { // accountID does not reference a valid account for _, e := range accErrs { @@ -49,45 +44,12 @@ func GetAccount(ctx context.Context, cfg *config.Configuration, fetcher stored_r } else { // accountID resolved to a valid account, merge with AccountDefaults for a complete config account = &config.Account{} - err := json.Unmarshal(accountJSON, account) - - // this logic exists for backwards compatibility. If the initial unmarshal fails above, we attempt to - // resolve it by converting the GDPR enforce purpose fields and then attempting an unmarshal again before - // declaring a malformed account error. - // unmarshal fetched account to determine if it is well-formed - var deprecatedPurposeFields []string - if _, ok := err.(*json.UnmarshalTypeError); ok { - // attempt to convert deprecated GDPR enforce purpose fields to resolve issue - accountJSON, err, deprecatedPurposeFields = ConvertGDPREnforcePurposeFields(accountJSON) - // unmarshal again to check if unmarshal error still exists after GDPR field conversion - err = json.Unmarshal(accountJSON, account) - - if _, ok := err.(*json.UnmarshalTypeError); ok { - return nil, []error{&errortypes.MalformedAcct{ - Message: fmt.Sprintf("The prebid-server account config for account id \"%s\" is malformed. Please reach out to the prebid server host.", accountID), - }} - } - } - usingGDPRChannelEnabled := useGDPRChannelEnabled(account) - usingCCPAChannelEnabled := useCCPAChannelEnabled(account) - - if usingGDPRChannelEnabled { - me.RecordAccountGDPRChannelEnabledWarning(accountID) - } - if usingCCPAChannelEnabled { - me.RecordAccountCCPAChannelEnabledWarning(accountID) - } - for _, purposeName := range deprecatedPurposeFields { - me.RecordAccountGDPRPurposeWarning(accountID, purposeName) - } - if len(deprecatedPurposeFields) > 0 || usingGDPRChannelEnabled || usingCCPAChannelEnabled { - me.RecordAccountUpgradeStatus(accountID) + if err := jsonutil.UnmarshalValid(accountJSON, account); err != nil { + return nil, []error{&errortypes.MalformedAcct{ + Message: fmt.Sprintf("The prebid-server account config for account id \"%s\" is malformed. Please reach out to the prebid server host.", accountID), + }} } - if err != nil { - errs = append(errs, err) - return nil, errs - } // Fill in ID if needed, so it can be left out of account definition if len(account.ID) == 0 { account.ID = accountID @@ -97,7 +59,7 @@ func GetAccount(ctx context.Context, cfg *config.Configuration, fetcher stored_r setDerivedConfig(account) } if account.Disabled { - errs = append(errs, &errortypes.BlacklistedAcct{ + errs = append(errs, &errortypes.AccountDisabled{ Message: fmt.Sprintf("Prebid-server has disabled Account ID: %s, please reach out to the prebid server host.", accountID), }) return nil, errs @@ -111,9 +73,6 @@ func GetAccount(ctx context.Context, cfg *config.Configuration, fetcher stored_r account.Privacy.IPv4Config.AnonKeepBits = iputil.IPv4DefaultMaskingBitSize } - // set the value of events.enabled field based on deprecated events_enabled field and ensure backward compatibility - deprecateEventsEnabledField(account) - return account, nil } @@ -152,7 +111,7 @@ func setDerivedConfig(account *config.Account) { if pc.VendorExceptions == nil { continue } - pc.VendorExceptionMap = make(map[openrtb_ext.BidderName]struct{}) + pc.VendorExceptionMap = make(map[string]struct{}) for _, v := range pc.VendorExceptions { pc.VendorExceptionMap[v] = struct{}{} } @@ -178,102 +137,3 @@ func setDerivedConfig(account *config.Account) { } } } - -// PatchAccount represents the GDPR portion of a publisher account configuration that can be mutated -// for backwards compatibility reasons -type PatchAccount struct { - GDPR map[string]*PatchAccountGDPRPurpose `json:"gdpr"` -} - -// PatchAccountGDPRPurpose represents account-specific GDPR purpose configuration data that can be mutated -// for backwards compatibility reasons -type PatchAccountGDPRPurpose struct { - EnforceAlgo string `json:"enforce_algo,omitempty"` - EnforcePurpose *bool `json:"enforce_purpose,omitempty"` -} - -// ConvertGDPREnforcePurposeFields is responsible for ensuring account GDPR config backwards compatibility -// given the recent type change of gdpr.purpose{1-10}.enforce_purpose from a string to a bool. This function -// iterates over each GDPR purpose config and sets enforce_purpose and enforce_algo to the appropriate -// bool and string values respectively if enforce_purpose is a string and enforce_algo is not set -func ConvertGDPREnforcePurposeFields(config []byte) (newConfig []byte, err error, deprecatedPurposeFields []string) { - gdprJSON, _, _, err := jsonparser.Get(config, "gdpr") - if err != nil && err == jsonparser.KeyPathNotFoundError { - return config, nil, deprecatedPurposeFields - } - if err != nil { - return nil, err, deprecatedPurposeFields - } - - newAccount := PatchAccount{ - GDPR: map[string]*PatchAccountGDPRPurpose{}, - } - - for i := 1; i <= 10; i++ { - purposeName := fmt.Sprintf("purpose%d", i) - - enforcePurpose, purposeDataType, _, err := jsonparser.Get(gdprJSON, purposeName, "enforce_purpose") - if err != nil && err == jsonparser.KeyPathNotFoundError { - continue - } - if err != nil { - return nil, err, deprecatedPurposeFields - } - if purposeDataType != jsonparser.String { - continue - } else { - deprecatedPurposeFields = append(deprecatedPurposeFields, purposeName) - } - - _, _, _, err = jsonparser.Get(gdprJSON, purposeName, "enforce_algo") - if err != nil && err != jsonparser.KeyPathNotFoundError { - return nil, err, deprecatedPurposeFields - } - if err == nil { - continue - } - - newEnforcePurpose := false - if string(enforcePurpose) == "full" { - newEnforcePurpose = true - } - - newAccount.GDPR[purposeName] = &PatchAccountGDPRPurpose{ - EnforceAlgo: "full", - EnforcePurpose: &newEnforcePurpose, - } - } - - patchConfig, err := json.Marshal(newAccount) - if err != nil { - return nil, err, deprecatedPurposeFields - } - - newConfig, err = jsonpatch.MergePatch(config, patchConfig) - if err != nil { - return nil, err, deprecatedPurposeFields - } - - return newConfig, nil, deprecatedPurposeFields -} - -func useGDPRChannelEnabled(account *config.Account) bool { - return account.GDPR.ChannelEnabled.IsSet() && !account.GDPR.IntegrationEnabled.IsSet() -} - -func useCCPAChannelEnabled(account *config.Account) bool { - return account.CCPA.ChannelEnabled.IsSet() && !account.CCPA.IntegrationEnabled.IsSet() -} - -// deprecateEventsEnabledField is responsible for ensuring backwards compatibility of "events_enabled" field. -// This function favors "events.enabled" field over deprecated "events_enabled" field, if values for both are set. -// If only deprecated "events_enabled" field is set then it sets the same value to "events.enabled" field. -func deprecateEventsEnabledField(account *config.Account) { - if account != nil { - if account.Events.Enabled == nil { - account.Events.Enabled = account.EventsEnabled - } - // assign the old value to the new value so old and new are always the same even though the new value is what is used in the application code. - account.EventsEnabled = account.Events.Enabled - } -} diff --git a/account/account_test.go b/account/account_test.go index d2694abc5a1..7788f7c430d 100644 --- a/account/account_test.go +++ b/account/account_test.go @@ -6,36 +6,23 @@ import ( "fmt" "testing" - "github.com/buger/jsonparser" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/util/iputil" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/util/iputil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) var mockAccountData = map[string]json.RawMessage{ - "valid_acct": json.RawMessage(`{"disabled":false}`), - "invalid_acct_ipv6_ipv4": json.RawMessage(`{"disabled":false, "privacy": {"ipv6": {"anon_keep_bits": -32}, "ipv4": {"anon_keep_bits": -16}}}`), - "disabled_acct": json.RawMessage(`{"disabled":true}`), - "malformed_acct": json.RawMessage(`{"disabled":"invalid type"}`), - "gdpr_channel_enabled_acct": json.RawMessage(`{"disabled":false,"gdpr":{"channel_enabled":{"amp":true}}}`), - "ccpa_channel_enabled_acct": json.RawMessage(`{"disabled":false,"ccpa":{"channel_enabled":{"amp":true}}}`), - "gdpr_channel_enabled_deprecated_purpose_acct": json.RawMessage(`{"disabled":false,"gdpr":{"purpose1":{"enforce_purpose":"full"}, "channel_enabled":{"amp":true}}}`), - "gdpr_deprecated_purpose1": json.RawMessage(`{"disabled":false,"gdpr":{"purpose1":{"enforce_purpose":"full"}}}`), - "gdpr_deprecated_purpose2": json.RawMessage(`{"disabled":false,"gdpr":{"purpose2":{"enforce_purpose":"full"}}}`), - "gdpr_deprecated_purpose3": json.RawMessage(`{"disabled":false,"gdpr":{"purpose3":{"enforce_purpose":"full"}}}`), - "gdpr_deprecated_purpose4": json.RawMessage(`{"disabled":false,"gdpr":{"purpose4":{"enforce_purpose":"full"}}}`), - "gdpr_deprecated_purpose5": json.RawMessage(`{"disabled":false,"gdpr":{"purpose5":{"enforce_purpose":"full"}}}`), - "gdpr_deprecated_purpose6": json.RawMessage(`{"disabled":false,"gdpr":{"purpose6":{"enforce_purpose":"full"}}}`), - "gdpr_deprecated_purpose7": json.RawMessage(`{"disabled":false,"gdpr":{"purpose7":{"enforce_purpose":"full"}}}`), - "gdpr_deprecated_purpose8": json.RawMessage(`{"disabled":false,"gdpr":{"purpose8":{"enforce_purpose":"full"}}}`), - "gdpr_deprecated_purpose9": json.RawMessage(`{"disabled":false,"gdpr":{"purpose9":{"enforce_purpose":"full"}}}`), - "gdpr_deprecated_purpose10": json.RawMessage(`{"disabled":false,"gdpr":{"purpose10":{"enforce_purpose":"full"}}}`), + "valid_acct": json.RawMessage(`{"disabled":false}`), + "invalid_acct_ipv6_ipv4": json.RawMessage(`{"disabled":false, "privacy": {"ipv6": {"anon_keep_bits": -32}, "ipv4": {"anon_keep_bits": -16}}}`), + "disabled_acct": json.RawMessage(`{"disabled":true}`), + "malformed_acct": json.RawMessage(`{"disabled":"invalid type"}`), + "gdpr_channel_enabled_acct": json.RawMessage(`{"disabled":false,"gdpr":{"channel_enabled":{"amp":true}}}`), + "ccpa_channel_enabled_acct": json.RawMessage(`{"disabled":false,"ccpa":{"channel_enabled":{"amp":true}}}`), } type mockAccountFetcher struct { @@ -61,19 +48,16 @@ func TestGetAccount(t *testing.T) { // expected error, or nil if account should be found err error }{ - // Blacklisted account is always rejected even in permissive setup - {accountID: "bad_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}}, - // empty pubID {accountID: unknown, required: false, disabled: false, err: nil}, {accountID: unknown, required: true, disabled: false, err: &errortypes.AcctRequired{}}, - {accountID: unknown, required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, + {accountID: unknown, required: false, disabled: true, err: &errortypes.AccountDisabled{}}, {accountID: unknown, required: true, disabled: true, err: &errortypes.AcctRequired{}}, // pubID given but is not a valid host account (does not exist) {accountID: "doesnt_exist_acct", required: false, disabled: false, err: nil}, {accountID: "doesnt_exist_acct", required: true, disabled: false, err: nil}, - {accountID: "doesnt_exist_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, + {accountID: "doesnt_exist_acct", required: false, disabled: true, err: &errortypes.AccountDisabled{}}, {accountID: "doesnt_exist_acct", required: true, disabled: true, err: &errortypes.AcctRequired{}}, // pubID given and matches a valid host account with Disabled: false @@ -85,16 +69,10 @@ func TestGetAccount(t *testing.T) { {accountID: "invalid_acct_ipv6_ipv4", required: true, disabled: false, err: nil, checkDefaultIP: true}, // pubID given and matches a host account explicitly disabled (Disabled: true on account json) - {accountID: "disabled_acct", required: false, disabled: false, err: &errortypes.BlacklistedAcct{}}, - {accountID: "disabled_acct", required: true, disabled: false, err: &errortypes.BlacklistedAcct{}}, - {accountID: "disabled_acct", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, - {accountID: "disabled_acct", required: true, disabled: true, err: &errortypes.BlacklistedAcct{}}, - - // pubID given and matches a host account with Disabled: false and GDPR purpose data to convert - {accountID: "gdpr_deprecated_purpose1", required: false, disabled: false, err: nil}, - {accountID: "gdpr_deprecated_purpose1", required: true, disabled: false, err: nil}, - {accountID: "gdpr_deprecated_purpose1", required: false, disabled: true, err: nil}, - {accountID: "gdpr_deprecated_purpose1", required: true, disabled: true, err: nil}, + {accountID: "disabled_acct", required: false, disabled: false, err: &errortypes.AccountDisabled{}}, + {accountID: "disabled_acct", required: true, disabled: false, err: &errortypes.AccountDisabled{}}, + {accountID: "disabled_acct", required: false, disabled: true, err: &errortypes.AccountDisabled{}}, + {accountID: "disabled_acct", required: true, disabled: true, err: &errortypes.AccountDisabled{}}, // pubID given and matches a host account that has a malformed config {accountID: "malformed_acct", required: false, disabled: false, err: &errortypes.MalformedAcct{}}, @@ -105,7 +83,7 @@ func TestGetAccount(t *testing.T) { // account not provided (does not exist) {accountID: "", required: false, disabled: false, err: nil}, {accountID: "", required: true, disabled: false, err: nil}, - {accountID: "", required: false, disabled: true, err: &errortypes.BlacklistedAcct{}}, + {accountID: "", required: false, disabled: true, err: &errortypes.AccountDisabled{}}, {accountID: "", required: true, disabled: true, err: &errortypes.AcctRequired{}}, } @@ -113,15 +91,13 @@ func TestGetAccount(t *testing.T) { description := fmt.Sprintf(`ID=%s/required=%t/disabled=%t`, test.accountID, test.required, test.disabled) t.Run(description, func(t *testing.T) { cfg := &config.Configuration{ - BlacklistedAcctMap: map[string]bool{"bad_acct": true}, - AccountRequired: test.required, - AccountDefaults: config.Account{Disabled: test.disabled}, + AccountRequired: test.required, + AccountDefaults: config.Account{Disabled: test.disabled}, } fetcher := &mockAccountFetcher{} assert.NoError(t, cfg.MarshalAccountDefaults()) metrics := &metrics.MetricsEngineMock{} - metrics.Mock.On("RecordAccountGDPRPurposeWarning", mock.Anything, mock.Anything).Return() metrics.Mock.On("RecordAccountUpgradeStatus", mock.Anything, mock.Anything).Return() account, errors := GetAccount(context.Background(), cfg, fetcher, test.accountID, metrics) @@ -146,7 +122,7 @@ func TestGetAccount(t *testing.T) { func TestSetDerivedConfig(t *testing.T) { tests := []struct { description string - purpose1VendorExceptions []openrtb_ext.BidderName + purpose1VendorExceptions []string feature1VendorExceptions []openrtb_ext.BidderName basicEnforcementVendors []string enforceAlgo string @@ -158,11 +134,11 @@ func TestSetDerivedConfig(t *testing.T) { }, { description: "One purpose 1 vendor exception", - purpose1VendorExceptions: []openrtb_ext.BidderName{"appnexus"}, + purpose1VendorExceptions: []string{"appnexus"}, }, { description: "Multiple purpose 1 vendor exceptions", - purpose1VendorExceptions: []openrtb_ext.BidderName{"appnexus", "rubicon"}, + purpose1VendorExceptions: []string{"appnexus", "rubicon"}, }, { description: "Nil feature 1 vendor exceptions", @@ -216,7 +192,7 @@ func TestSetDerivedConfig(t *testing.T) { setDerivedConfig(&account) - purpose1ExceptionMapKeys := make([]openrtb_ext.BidderName, 0) + purpose1ExceptionMapKeys := make([]string, 0) for k := range account.GDPR.Purpose1.VendorExceptionMap { purpose1ExceptionMapKeys = append(purpose1ExceptionMapKeys, k) } @@ -238,418 +214,3 @@ func TestSetDerivedConfig(t *testing.T) { assert.Equal(t, account.GDPR.Purpose1.EnforceAlgoID, tt.wantEnforceAlgoID, tt.description) } } - -func TestConvertGDPREnforcePurposeFields(t *testing.T) { - enforcePurposeNo := `{"enforce_purpose":"no"}` - enforcePurposeNoMapped := `{"enforce_algo":"full", "enforce_purpose":false}` - enforcePurposeFull := `{"enforce_purpose":"full"}` - enforcePurposeFullMapped := `{"enforce_algo":"full", "enforce_purpose":true}` - - tests := []struct { - description string - giveConfig []byte - wantConfig []byte - wantErr error - wantPurposeFields []string - }{ - { - description: "config is nil", - giveConfig: nil, - wantConfig: nil, - wantErr: nil, - wantPurposeFields: nil, - }, - { - description: "config is empty - no gdpr key", - giveConfig: []byte(``), - wantConfig: []byte(``), - wantErr: nil, - wantPurposeFields: nil, - }, - { - description: "gdpr present but empty", - giveConfig: []byte(`{"gdpr": {}}`), - wantConfig: []byte(`{"gdpr": {}}`), - wantErr: nil, - wantPurposeFields: nil, - }, - { - description: "gdpr present but invalid", - giveConfig: []byte(`{"gdpr": {`), - wantConfig: nil, - wantErr: jsonparser.MalformedJsonError, - wantPurposeFields: nil, - }, - { - description: "gdpr.purpose1 present but empty", - giveConfig: []byte(`{"gdpr":{"purpose1":{}}}`), - wantConfig: []byte(`{"gdpr":{"purpose1":{}}}`), - wantErr: nil, - wantPurposeFields: nil, - }, - { - description: "gdpr.purpose1.enforce_purpose is set to bool", - giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_purpose":true}}}`), - wantConfig: []byte(`{"gdpr":{"purpose1":{"enforce_purpose":true}}}`), - wantErr: nil, - wantPurposeFields: nil, - }, - { - description: "gdpr.purpose1.enforce_purpose is set to string full", - giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_purpose":"full"}}}`), - wantConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":true}}}`), - wantErr: nil, - wantPurposeFields: []string{"purpose1"}, - }, - { - description: "gdpr.purpose1.enforce_purpose is set to string no", - giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_purpose":"no"}}}`), - wantConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":false}}}`), - wantErr: nil, - wantPurposeFields: []string{"purpose1"}, - }, - { - description: "gdpr.purpose1.enforce_purpose is set to string no and other fields are untouched during conversion", - giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_purpose":"no", "enforce_vendors":true}}}`), - wantConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":false, "enforce_vendors":true}}}`), - wantErr: nil, - wantPurposeFields: []string{"purpose1"}, - }, - { - description: "gdpr.purpose1.enforce_purpose is set but invalid", - giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_purpose":}}}`), - wantConfig: nil, - wantErr: jsonparser.MalformedJsonError, - wantPurposeFields: nil, - }, - { - description: "gdpr.purpose1.enforce_algo is set", - giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full"}}}`), - wantConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full"}}}`), - wantErr: nil, - wantPurposeFields: nil, - }, - { - description: "gdpr.purpose1.enforce_purpose is set to string and enforce_algo is set", - giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":"full"}}}`), - wantConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":"full", "enforce_purpose":"full"}}}`), - wantErr: nil, - wantPurposeFields: []string{"purpose1"}, - }, - { - description: "gdpr.purpose1.enforce_purpose is set to string and enforce_algo is set but invalid", - giveConfig: []byte(`{"gdpr":{"purpose1":{"enforce_algo":, "enforce_purpose":"full"}}}`), - wantConfig: nil, - wantErr: jsonparser.MalformedJsonError, - wantPurposeFields: []string{"purpose1"}, - }, - { - description: "gdpr.purpose{1-10}.enforce_purpose are set to strings no and full alternating", - giveConfig: []byte(`{"gdpr":{` + - `"purpose1":` + enforcePurposeNo + - `,"purpose2":` + enforcePurposeFull + - `,"purpose3":` + enforcePurposeNo + - `,"purpose4":` + enforcePurposeFull + - `,"purpose5":` + enforcePurposeNo + - `,"purpose6":` + enforcePurposeFull + - `,"purpose7":` + enforcePurposeNo + - `,"purpose8":` + enforcePurposeFull + - `,"purpose9":` + enforcePurposeNo + - `,"purpose10":` + enforcePurposeFull + - `}}`), - wantConfig: []byte(`{"gdpr":{` + - `"purpose1":` + enforcePurposeNoMapped + - `,"purpose2":` + enforcePurposeFullMapped + - `,"purpose3":` + enforcePurposeNoMapped + - `,"purpose4":` + enforcePurposeFullMapped + - `,"purpose5":` + enforcePurposeNoMapped + - `,"purpose6":` + enforcePurposeFullMapped + - `,"purpose7":` + enforcePurposeNoMapped + - `,"purpose8":` + enforcePurposeFullMapped + - `,"purpose9":` + enforcePurposeNoMapped + - `,"purpose10":` + enforcePurposeFullMapped + - `}}`), - wantErr: nil, - wantPurposeFields: []string{"purpose1", "purpose2", "purpose3", "purpose4", "purpose5", "purpose6", "purpose7", "purpose8", "purpose9", "purpose10"}, - }, - } - - for _, tt := range tests { - metricsMock := &metrics.MetricsEngineMock{} - metricsMock.Mock.On("RecordAccountGDPRPurposeWarning", mock.Anything, mock.Anything).Return() - metricsMock.Mock.On("RecordAccountUpgradeStatus", mock.Anything, mock.Anything).Return() - - newConfig, err, deprecatedPurposeFields := ConvertGDPREnforcePurposeFields(tt.giveConfig) - if tt.wantErr != nil { - assert.Error(t, err, tt.description) - } - - if len(tt.wantConfig) == 0 { - assert.Equal(t, tt.wantConfig, newConfig, tt.description) - } else { - assert.JSONEq(t, string(tt.wantConfig), string(newConfig), tt.description) - } - assert.Equal(t, tt.wantPurposeFields, deprecatedPurposeFields, tt.description) - } -} - -func TestGdprCcpaChannelEnabledMetrics(t *testing.T) { - cfg := &config.Configuration{} - fetcher := &mockAccountFetcher{} - assert.NoError(t, cfg.MarshalAccountDefaults()) - - testCases := []struct { - name string - givenAccountID string - givenMetric string - expectedMetricCount int - }{ - { - name: "ChannelEnabledGDPR", - givenAccountID: "gdpr_channel_enabled_acct", - givenMetric: "RecordAccountGDPRChannelEnabledWarning", - expectedMetricCount: 1, - }, - { - name: "ChannelEnabledCCPA", - givenAccountID: "ccpa_channel_enabled_acct", - givenMetric: "RecordAccountCCPAChannelEnabledWarning", - expectedMetricCount: 1, - }, - { - name: "NotChannelEnabledCCPA", - givenAccountID: "valid_acct", - givenMetric: "RecordAccountCCPAChannelEnabledWarning", - expectedMetricCount: 0, - }, - { - name: "NotChannelEnabledGDPR", - givenAccountID: "valid_acct", - givenMetric: "RecordAccountGDPRChannelEnabledWarning", - expectedMetricCount: 0, - }, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - metrics := &metrics.MetricsEngineMock{} - metrics.Mock.On(test.givenMetric, mock.Anything, mock.Anything).Return() - metrics.Mock.On("RecordAccountUpgradeStatus", mock.Anything, mock.Anything).Return() - - _, _ = GetAccount(context.Background(), cfg, fetcher, test.givenAccountID, metrics) - - metrics.AssertNumberOfCalls(t, test.givenMetric, test.expectedMetricCount) - }) - } -} - -func TestGdprPurposeWarningMetrics(t *testing.T) { - cfg := &config.Configuration{} - fetcher := &mockAccountFetcher{} - assert.NoError(t, cfg.MarshalAccountDefaults()) - - testCases := []struct { - name string - givenMetric string - givenAccountID string - givenConfig []byte - expectedMetricCount int - }{ - { - name: "Purpose1MetricCalled", - givenAccountID: "gdpr_deprecated_purpose1", - expectedMetricCount: 1, - }, - { - name: "Purpose2MetricCalled", - givenAccountID: "gdpr_deprecated_purpose2", - expectedMetricCount: 1, - }, - { - name: "Purpose3MetricCalled", - givenAccountID: "gdpr_deprecated_purpose3", - expectedMetricCount: 1, - }, - { - name: "Purpose4MetricCalled", - givenAccountID: "gdpr_deprecated_purpose4", - expectedMetricCount: 1, - }, - { - name: "Purpose5MetricCalled", - givenAccountID: "gdpr_deprecated_purpose5", - expectedMetricCount: 1, - }, - { - name: "Purpose6MetricCalled", - givenAccountID: "gdpr_deprecated_purpose6", - expectedMetricCount: 1, - }, - { - name: "Purpose7MetricCalled", - givenAccountID: "gdpr_deprecated_purpose7", - expectedMetricCount: 1, - }, - { - name: "Purpose8MetricCalled", - givenAccountID: "gdpr_deprecated_purpose8", - expectedMetricCount: 1, - }, - { - name: "Purpose9MetricCalled", - givenAccountID: "gdpr_deprecated_purpose9", - expectedMetricCount: 1, - }, - { - name: "Purpose10MetricCalled", - givenAccountID: "gdpr_deprecated_purpose10", - expectedMetricCount: 1, - }, - { - name: "PurposeMetricNotCalled", - givenAccountID: "valid_acct", - expectedMetricCount: 0, - }, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - metrics := &metrics.MetricsEngineMock{} - metrics.Mock.On("RecordAccountGDPRPurposeWarning", mock.Anything, mock.Anything).Return() - metrics.Mock.On("RecordAccountUpgradeStatus", mock.Anything, mock.Anything).Return() - - _, _ = GetAccount(context.Background(), cfg, fetcher, test.givenAccountID, metrics) - - metrics.AssertNumberOfCalls(t, "RecordAccountGDPRPurposeWarning", test.expectedMetricCount) - }) - - } -} - -func TestAccountUpgradeStatusGetAccount(t *testing.T) { - cfg := &config.Configuration{} - fetcher := &mockAccountFetcher{} - assert.NoError(t, cfg.MarshalAccountDefaults()) - - testCases := []struct { - name string - givenAccountIDs []string - givenMetrics []string - expectedMetricCount int - }{ - { - name: "MultipleDeprecatedConfigs", - givenAccountIDs: []string{"gdpr_channel_enabled_deprecated_purpose_acct"}, - givenMetrics: []string{"RecordAccountGDPRChannelEnabledWarning", "RecordAccountGDPRPurposeWarning"}, - expectedMetricCount: 1, - }, - { - name: "ZeroDeprecatedConfigs", - givenAccountIDs: []string{"valid_acct"}, - givenMetrics: []string{}, - expectedMetricCount: 0, - }, - { - name: "OneDeprecatedConfigPurpose", - givenAccountIDs: []string{"gdpr_deprecated_purpose1"}, - givenMetrics: []string{"RecordAccountGDPRPurposeWarning"}, - expectedMetricCount: 1, - }, - { - name: "OneDeprecatedConfigGDPRChannelEnabled", - givenAccountIDs: []string{"gdpr_channel_enabled_acct"}, - givenMetrics: []string{"RecordAccountGDPRChannelEnabledWarning"}, - expectedMetricCount: 1, - }, - { - name: "OneDeprecatedConfigCCPAChannelEnabled", - givenAccountIDs: []string{"ccpa_channel_enabled_acct"}, - givenMetrics: []string{"RecordAccountCCPAChannelEnabledWarning"}, - expectedMetricCount: 1, - }, - { - name: "MultipleAccountsWithDeprecatedConfigs", - givenAccountIDs: []string{"gdpr_channel_enabled_acct", "gdpr_deprecated_purpose1"}, - givenMetrics: []string{"RecordAccountGDPRChannelEnabledWarning", "RecordAccountGDPRPurposeWarning"}, - expectedMetricCount: 2, - }, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - metrics := &metrics.MetricsEngineMock{} - for _, metric := range test.givenMetrics { - metrics.Mock.On(metric, mock.Anything, mock.Anything).Return() - } - metrics.Mock.On("RecordAccountUpgradeStatus", mock.Anything, mock.Anything).Return() - - for _, accountID := range test.givenAccountIDs { - _, _ = GetAccount(context.Background(), cfg, fetcher, accountID, metrics) - } - metrics.AssertNumberOfCalls(t, "RecordAccountUpgradeStatus", test.expectedMetricCount) - }) - } -} - -func TestDeprecateEventsEnabledField(t *testing.T) { - testCases := []struct { - name string - account *config.Account - want *bool - }{ - { - name: "account is nil", - account: nil, - want: nil, - }, - { - name: "account.EventsEnabled is nil, account.Events.Enabled is nil", - account: &config.Account{ - EventsEnabled: nil, - Events: config.Events{ - Enabled: nil, - }, - }, - want: nil, - }, - { - name: "account.EventsEnabled is nil, account.Events.Enabled is non-nil", - account: &config.Account{ - EventsEnabled: nil, - Events: config.Events{ - Enabled: ptrutil.ToPtr(true), - }, - }, - want: ptrutil.ToPtr(true), - }, - { - name: "account.EventsEnabled is non-nil, account.Events.Enabled is nil", - account: &config.Account{ - EventsEnabled: ptrutil.ToPtr(true), - Events: config.Events{ - Enabled: nil, - }, - }, - want: ptrutil.ToPtr(true), - }, - { - name: "account.EventsEnabled is non-nil, account.Events.Enabled is non-nil", - account: &config.Account{ - EventsEnabled: ptrutil.ToPtr(false), - Events: config.Events{ - Enabled: ptrutil.ToPtr(true), - }, - }, - want: ptrutil.ToPtr(true), - }, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - deprecateEventsEnabledField(test.account) - if test.account != nil { - assert.Equal(t, test.want, test.account.Events.Enabled) - } - }) - } -} diff --git a/adapters/33across/33across.go b/adapters/33across/33across.go index 26349e8426b..b9062468a73 100644 --- a/adapters/33across/33across.go +++ b/adapters/33across/33across.go @@ -5,12 +5,12 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type TtxAdapter struct { @@ -243,8 +243,8 @@ func (a *TtxAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequ func validateVideoParams(video *openrtb2.Video, prod string) (*openrtb2.Video, error) { videoCopy := *video - if videoCopy.W == 0 || - videoCopy.H == 0 || + if (videoCopy.W == nil || *videoCopy.W == 0) || + (videoCopy.H == nil || *videoCopy.H == 0) || videoCopy.Protocols == nil || videoCopy.MIMEs == nil || videoCopy.PlaybackMethod == nil { diff --git a/adapters/33across/33across_test.go b/adapters/33across/33across_test.go index bdc546a9627..c84ca0ad1d2 100644 --- a/adapters/33across/33across_test.go +++ b/adapters/33across/33across_test.go @@ -3,9 +3,9 @@ package ttx import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/33across/33acrosstest/supplemental/video-validation-fail-size-null.json b/adapters/33across/33acrosstest/supplemental/video-validation-fail-size-null.json new file mode 100644 index 00000000000..c9389fb86c2 --- /dev/null +++ b/adapters/33across/33acrosstest/supplemental/video-validation-fail-size-null.json @@ -0,0 +1,29 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "protocols": [2], + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "bidder": { + "siteId": "fake-site-id", + "productId": "siab" + } + } + } + ], + "site": {} + }, + + "expectedMakeRequestsErrors": [ + { + "value": "One or more invalid or missing video field(s) w, h, protocols, mimes, playbackmethod", + "comparison": "literal" + } + ] +} diff --git a/adapters/33across/33acrosstest/supplemental/video-validation-fail-size-partial.json b/adapters/33across/33acrosstest/supplemental/video-validation-fail-size-partial.json new file mode 100644 index 00000000000..230eaac05df --- /dev/null +++ b/adapters/33across/33acrosstest/supplemental/video-validation-fail-size-partial.json @@ -0,0 +1,30 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "h": 100, + "protocols": [2], + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "bidder": { + "siteId": "fake-site-id", + "productId": "siab" + } + } + } + ], + "site": {} + }, + + "expectedMakeRequestsErrors": [ + { + "value": "One or more invalid or missing video field(s) w, h, protocols, mimes, playbackmethod", + "comparison": "literal" + } + ] +} diff --git a/adapters/33across/33acrosstest/supplemental/video-validation-fail-size-zero.json b/adapters/33across/33acrosstest/supplemental/video-validation-fail-size-zero.json new file mode 100644 index 00000000000..32927e2b455 --- /dev/null +++ b/adapters/33across/33acrosstest/supplemental/video-validation-fail-size-zero.json @@ -0,0 +1,31 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 0, + "h": 0, + "protocols": [2], + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "bidder": { + "siteId": "fake-site-id", + "productId": "siab" + } + } + } + ], + "site": {} + }, + + "expectedMakeRequestsErrors": [ + { + "value": "One or more invalid or missing video field(s) w, h, protocols, mimes, playbackmethod", + "comparison": "literal" + } + ] +} diff --git a/adapters/33across/params_test.go b/adapters/33across/params_test.go index 2d488c4148c..ba985b2b250 100644 --- a/adapters/33across/params_test.go +++ b/adapters/33across/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/33across.json diff --git a/adapters/aax/aax.go b/adapters/aax/aax.go index 86994c6dea2..2ad39b1446b 100644 --- a/adapters/aax/aax.go +++ b/adapters/aax/aax.go @@ -6,11 +6,11 @@ import ( "net/http" "net/url" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/aax/aax_test.go b/adapters/aax/aax_test.go index 6a5eaed5dfe..c4fd1c392aa 100644 --- a/adapters/aax/aax_test.go +++ b/adapters/aax/aax_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/aax/params_test.go b/adapters/aax/params_test.go index edf9fb6fc48..bdfa46a9e63 100644 --- a/adapters/aax/params_test.go +++ b/adapters/aax/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/aax.json diff --git a/adapters/aceex/aceex.go b/adapters/aceex/aceex.go index 61863f0b8a8..450d82b4df3 100644 --- a/adapters/aceex/aceex.go +++ b/adapters/aceex/aceex.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/aceex/aceex_test.go b/adapters/aceex/aceex_test.go index ec0e0fec710..71c26ed0bed 100644 --- a/adapters/aceex/aceex_test.go +++ b/adapters/aceex/aceex_test.go @@ -3,9 +3,9 @@ package aceex import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/aceex/params_test.go b/adapters/aceex/params_test.go index 220adb23379..cb6445c491a 100644 --- a/adapters/aceex/params_test.go +++ b/adapters/aceex/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) var validParams = []string{ diff --git a/adapters/acuityads/acuityads.go b/adapters/acuityads/acuityads.go index 4370beb72d1..183f1168d9f 100644 --- a/adapters/acuityads/acuityads.go +++ b/adapters/acuityads/acuityads.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type AcuityAdsAdapter struct { diff --git a/adapters/acuityads/acuityads_test.go b/adapters/acuityads/acuityads_test.go index ea9d4f24352..c426d02c533 100644 --- a/adapters/acuityads/acuityads_test.go +++ b/adapters/acuityads/acuityads_test.go @@ -3,9 +3,9 @@ package acuityads import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/acuityads/params_test.go b/adapters/acuityads/params_test.go index 892fe9a646d..3c7b3a97914 100644 --- a/adapters/acuityads/params_test.go +++ b/adapters/acuityads/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) var validParams = []string{ diff --git a/adapters/adapterstest/adapter_test_util.go b/adapters/adapterstest/adapter_test_util.go index 46527356fe2..d58437b8fd8 100644 --- a/adapters/adapterstest/adapter_test_util.go +++ b/adapters/adapterstest/adapter_test_util.go @@ -8,7 +8,7 @@ import ( "net/http" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" ) // OrtbMockService Represents a scaffolded OpenRTB service. diff --git a/adapters/adapterstest/test_json.go b/adapters/adapterstest/test_json.go index 39b1e945f7d..5b6c56d2cee 100644 --- a/adapters/adapterstest/test_json.go +++ b/adapters/adapterstest/test_json.go @@ -11,10 +11,10 @@ import ( "testing" "github.com/mitchellh/copystructure" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" "github.com/yudai/gojsondiff" "github.com/yudai/gojsondiff/formatter" diff --git a/adapters/adelement/adelement.go b/adapters/adelement/adelement.go new file mode 100644 index 00000000000..2203aa6d6ea --- /dev/null +++ b/adapters/adelement/adelement.go @@ -0,0 +1,139 @@ +package adelement + +import ( + "encoding/json" + "fmt" + "text/template" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +type adapter struct { + endpoint *template.Template +} + +// Builder builds a new instance of the Adelement adapter for the given bidder with the given config. +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + template, err := template.New("endpointTemplate").Parse(config.Endpoint) + if err != nil { + return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) + } + + bidder := &adapter{ + endpoint: template, + } + return bidder, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + adelementExt, err := getImpressionExt(&request.Imp[0]) + if err != nil { + return nil, []error{err} + } + + url, err := a.buildEndpointURL(adelementExt) + if err != nil { + return nil, []error{err} + } + + requestJSON, err := json.Marshal(request) + if err != nil { + return nil, []error{err} + } + + requestData := &adapters.RequestData{ + Method: "POST", + Uri: url, + Body: requestJSON, + } + + return []*adapters.RequestData{requestData}, nil +} + +func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtAdelement, error) { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, &errortypes.BadInput{ + Message: "ext.bidder not provided", + } + } + var adelementExt openrtb_ext.ExtAdelement + if err := json.Unmarshal(bidderExt.Bidder, &adelementExt); err != nil { + return nil, &errortypes.BadInput{ + Message: "ext.bidder not provided", + } + } + imp.Ext = nil + return &adelementExt, nil +} + +func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtAdelement) (string, error) { + endpointParams := macros.EndpointTemplateParams{SupplyId: params.SupplyId} + return macros.ResolveMacros(a.endpoint, endpointParams) +} + +func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if adapters.IsResponseStatusCodeNoContent(responseData) { + return nil, nil + } + + if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil { + return nil, []error{err} + } + + var response openrtb2.BidResponse + if err := json.Unmarshal(responseData.Body, &response); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: "Bad Server Response", + }} + } + + if len(response.SeatBid) == 0 { + return nil, []error{&errortypes.BadServerResponse{ + Message: "Empty SeatBid array", + }} + } + + var bidErrs []error + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) + bidResponse.Currency = response.Cur + for _, seatBid := range response.SeatBid { + for i := range seatBid.Bid { + bid := seatBid.Bid[i] + bidType, err := getBidType(bid) + if err != nil { + // could not determinate media type, append an error and continue with the next bid. + bidErrs = append(bidErrs, err) + continue + } + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: bidType, + }) + } + } + return bidResponse, bidErrs +} + +func getBidType(bid openrtb2.Bid) (openrtb_ext.BidType, error) { + // determinate media type by bid response field mtype + switch bid.MType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo, nil + case openrtb2.MarkupAudio: + return openrtb_ext.BidTypeAudio, nil + case openrtb2.MarkupNative: + return openrtb_ext.BidTypeNative, nil + } + + return "", &errortypes.BadInput{ + Message: fmt.Sprintf("Could not define media type for impression: %s", bid.ImpID), + } +} diff --git a/adapters/adelement/adelement_test.go b/adapters/adelement/adelement_test.go new file mode 100644 index 00000000000..d1c58bbebb4 --- /dev/null +++ b/adapters/adelement/adelement_test.go @@ -0,0 +1,28 @@ +package adelement + +import ( + "testing" + + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderAdelement, config.Adapter{ + Endpoint: "http://test.adelement.com/openrtb2/auction?supply_id={{.SupplyId}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 196}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "adelementtest", bidder) +} + +func TestEndpointTemplateMalformed(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderAdelement, config.Adapter{ + Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 196}) + + assert.Error(t, buildErr) +} diff --git a/adapters/adelement/adelementtest/exemplary/audio-app.json b/adapters/adelement/adelementtest/exemplary/audio-app.json new file mode 100644 index 00000000000..5384c847b0d --- /dev/null +++ b/adapters/adelement/adelementtest/exemplary/audio-app.json @@ -0,0 +1,90 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "test-imp-id", + "audio": { + "mimes": ["audio/mp4"], + "protocols": [9,10] + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3" + }, + "imp": [ + { + "id": "test-imp-id", + "audio": { + "mimes": ["audio/mp4"], + "protocols": [9,10] + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "adelement", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "mtype": 3 + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "mtype": 3 + }, + "type": "audio" + } + ] + } + ] +} diff --git a/adapters/adelement/adelementtest/exemplary/audio-web.json b/adapters/adelement/adelementtest/exemplary/audio-web.json new file mode 100644 index 00000000000..39d1b9ab456 --- /dev/null +++ b/adapters/adelement/adelementtest/exemplary/audio-web.json @@ -0,0 +1,90 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "audio": { + "mimes": ["audio/mp4"], + "protocols": [9,10] + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "test-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "buyeruid": "be5e209ad46927520000000000000000" + }, + "imp": [ + { + "id": "test-imp-id", + "audio": { + "mimes": ["audio/mp4"], + "protocols": [9,10] + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "adelement", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "mtype": 3 + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "mtype": 3 + }, + "type": "audio" + } + ] + } + ] +} diff --git a/adapters/adelement/adelementtest/exemplary/banner-app.json b/adapters/adelement/adelementtest/exemplary/banner-app.json new file mode 100644 index 00000000000..2f2b18ffb6a --- /dev/null +++ b/adapters/adelement/adelementtest/exemplary/banner-app.json @@ -0,0 +1,139 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "w": 320, + "h": 50 + }, + "tagid": "ogTAGID" + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 320, + "h": 50, + "mtype": 1 + } + ], + "seat": "adelement" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "adelement": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/adelement/adelementtest/exemplary/banner-web.json b/adapters/adelement/adelementtest/exemplary/banner-web.json new file mode 100644 index 00000000000..fc731fbe1de --- /dev/null +++ b/adapters/adelement/adelementtest/exemplary/banner-web.json @@ -0,0 +1,127 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "banner": { + "w":320, + "h":50 + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "banner": { + "w":320, + "h":50 + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 320, + "h": 50, + "mtype": 1 + } + ], + "seat": "adelement" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "adelement": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "1", + "price": 3.5, + "adm": "awesome-markup", + "crid": "20", + "adomain": [ + "awesome.com" + ], + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/adelement/adelementtest/exemplary/native-app.json b/adapters/adelement/adelementtest/exemplary/native-app.json new file mode 100644 index 00000000000..4ced9789e63 --- /dev/null +++ b/adapters/adelement/adelementtest/exemplary/native-app.json @@ -0,0 +1,136 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "native": { + "ver":"1.1", + "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}" + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "native": { + "ver":"1.1", + "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}" + }, + "tagid": "ogTAGID" + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "mtype": 4 + } + ], + "seat": "adelement" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "adelement": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "crid": "20", + "adomain": [ + "awesome.com" + ], + "mtype": 4 + }, + "type": "native" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/adelement/adelementtest/exemplary/native-web.json b/adapters/adelement/adelementtest/exemplary/native-web.json new file mode 100644 index 00000000000..7c9faad7579 --- /dev/null +++ b/adapters/adelement/adelementtest/exemplary/native-web.json @@ -0,0 +1,124 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ipv6": "2607:fb90:f27:4512:d800:cb23:a603:e245", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "native": { + "ver":"1.1", + "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}" + }, + "ext": { + "bidder": { + "host": "ep1", + "supply_id": "1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ipv6": "2607:fb90:f27:4512:d800:cb23:a603:e245", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "native": { + "ver":"1.1", + "request":"{\"adunit\":2,\"assets\":[{\"id\":3,\"img\":{\"h\":120,\"hmin\":0,\"type\":3,\"w\":180,\"wmin\":0},\"required\":1},{\"id\":0,\"required\":1,\"title\":{\"len\":25}},{\"data\":{\"len\":25,\"type\":1},\"id\":4,\"required\":1},{\"data\":{\"len\":140,\"type\":2},\"id\":6,\"required\":1}],\"context\":1,\"layout\":1,\"contextsubtype\":11,\"plcmtcnt\":1,\"plcmttype\":2,\"ver\":\"1.1\",\"ext\":{\"banner\":{\"w\":320,\"h\":50}}}" + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "mtype": 4 + } + ], + "seat": "acuityads" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "acuityads": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "crid": "20", + "adomain": [ + "awesome.com" + ], + "mtype": 4 + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/adelement/adelementtest/exemplary/video-app.json b/adapters/adelement/adelementtest/exemplary/video-app.json new file mode 100644 index 00000000000..c53f2391020 --- /dev/null +++ b/adapters/adelement/adelementtest/exemplary/video-app.json @@ -0,0 +1,149 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "tagid": "ogTAGID" + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "mtype": 2 + } + ], + "seat": "adelement" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "adelement": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "expectedBidResponses": [ + { + "bids":[ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "crid": "20", + "adomain": [ + "awesome.com" + ], + "w": 1280, + "h": 720, + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/adelement/adelementtest/exemplary/video-web.json b/adapters/adelement/adelementtest/exemplary/video-web.json new file mode 100644 index 00000000000..7248d0ac191 --- /dev/null +++ b/adapters/adelement/adelementtest/exemplary/video-web.json @@ -0,0 +1,147 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "mtype": 2, + "ext": { + "prebid": { + "type": "video" + } + } + } + ], + "seat": "adelement" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "adelement": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 1280, + "h": 720, + "mtype": 2, + "ext": { + "prebid": { + "type": "video" + } + } + }, + "type":"video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/adelement/adelementtest/supplemental/empty-seatbid-array.json b/adapters/adelement/adelementtest/supplemental/empty-seatbid-array.json new file mode 100644 index 00000000000..64fdde7e0b1 --- /dev/null +++ b/adapters/adelement/adelementtest/supplemental/empty-seatbid-array.json @@ -0,0 +1,119 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "tagid": "ogTAGID" + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "adelement": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "mockResponse": { + "status": 200, + "body": "invalid response" + }, + "expectedMakeBidsErrors": [ + { + "value": "Empty SeatBid array", + "comparison": "literal" + } + ] +} diff --git a/adapters/adelement/adelementtest/supplemental/invalid-aceex-ext-object.json b/adapters/adelement/adelementtest/supplemental/invalid-aceex-ext-object.json new file mode 100644 index 00000000000..77752d01edf --- /dev/null +++ b/adapters/adelement/adelementtest/supplemental/invalid-aceex-ext-object.json @@ -0,0 +1,29 @@ +{ + "expectedMakeRequestsErrors": [ + { + "value": "ext.bidder not provided", + "comparison": "literal" + } + ], + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "my-adcode", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": "Awesome" + } + ], + "site": { + "page": "test.com" + } + }, + "httpCalls": [] +} diff --git a/adapters/adelement/adelementtest/supplemental/invalid-response.json b/adapters/adelement/adelementtest/supplemental/invalid-response.json new file mode 100644 index 00000000000..b44dcaf462f --- /dev/null +++ b/adapters/adelement/adelementtest/supplemental/invalid-response.json @@ -0,0 +1,94 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "banner": { + "w":320, + "h":50 + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "w":320, + "h":50 + }, + "tagid": "ogTAGID" + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": "invalid response" + } + }], + "expectedMakeBidsErrors": [ + { + "value": "Bad Server Response", + "comparison": "literal" + } + ] +} diff --git a/adapters/adelement/adelementtest/supplemental/status-code-bad-request.json b/adapters/adelement/adelementtest/supplemental/status-code-bad-request.json new file mode 100644 index 00000000000..d2db833803b --- /dev/null +++ b/adapters/adelement/adelementtest/supplemental/status-code-bad-request.json @@ -0,0 +1,92 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + }, + "tagid": "ogTAGID" + } + ], + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 400 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/adelement/adelementtest/supplemental/status-code-no-content.json b/adapters/adelement/adelementtest/supplemental/status-code-no-content.json new file mode 100644 index 00000000000..a830cbcfc97 --- /dev/null +++ b/adapters/adelement/adelementtest/supplemental/status-code-no-content.json @@ -0,0 +1,68 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "imp": [{ + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + }] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "some-request-id", + "imp": [{ + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + } + }], + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 204 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [] +} \ No newline at end of file diff --git a/adapters/adelement/adelementtest/supplemental/status-code-other-error.json b/adapters/adelement/adelementtest/supplemental/status-code-other-error.json new file mode 100644 index 00000000000..f5a383d1577 --- /dev/null +++ b/adapters/adelement/adelementtest/supplemental/status-code-other-error.json @@ -0,0 +1,78 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 306 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 306. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/adelement/adelementtest/supplemental/status-code-service-unavailable.json b/adapters/adelement/adelementtest/supplemental/status-code-service-unavailable.json new file mode 100644 index 00000000000..abec808b12e --- /dev/null +++ b/adapters/adelement/adelementtest/supplemental/status-code-service-unavailable.json @@ -0,0 +1,78 @@ + +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "supply_id": "1" + } + } + } + ] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "http://test.adelement.com/openrtb2/auction?supply_id=1", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "tagid": "ogTAGID", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 120, + "maxduration": 150, + "w": 640, + "h": 480 + } + } + ], + "site": { + "page": "test.com", + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 503 + } + }], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 503. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/adf/adf.go b/adapters/adf/adf.go index 7ff817559cc..4b6ff2b76c7 100644 --- a/adapters/adf/adf.go +++ b/adapters/adf/adf.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/adf/adf_test.go b/adapters/adf/adf_test.go index bf8af8d6845..20e4f3dde32 100644 --- a/adapters/adf/adf_test.go +++ b/adapters/adf/adf_test.go @@ -3,9 +3,9 @@ package adf import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adf/params_test.go b/adapters/adf/params_test.go index 0b05519df3b..dc0d84927bc 100644 --- a/adapters/adf/params_test.go +++ b/adapters/adf/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/adf.json diff --git a/adapters/adgeneration/adgeneration.go b/adapters/adgeneration/adgeneration.go index a2a10ed51f2..92ea0c3adc8 100644 --- a/adapters/adgeneration/adgeneration.go +++ b/adapters/adgeneration/adgeneration.go @@ -10,11 +10,11 @@ import ( "strconv" "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type AdgenerationAdapter struct { diff --git a/adapters/adgeneration/adgeneration_test.go b/adapters/adgeneration/adgeneration_test.go index c204fbd320d..802db77e313 100644 --- a/adapters/adgeneration/adgeneration_test.go +++ b/adapters/adgeneration/adgeneration_test.go @@ -4,11 +4,11 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adgeneration/params_test.go b/adapters/adgeneration/params_test.go index 062d122ac08..58400e96656 100644 --- a/adapters/adgeneration/params_test.go +++ b/adapters/adgeneration/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/adhese/adhese.go b/adapters/adhese/adhese.go index 9c84676c379..f27848f023d 100644 --- a/adapters/adhese/adhese.go +++ b/adapters/adhese/adhese.go @@ -10,12 +10,12 @@ import ( "strings" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type AdheseAdapter struct { @@ -141,6 +141,9 @@ func (a *AdheseAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalR return nil, []error{err, WrapServerError(fmt.Sprintf("Response %v could not be parsed as generic Adhese bid.", string(response.Body)))} } + if len(adheseBidResponseArray) == 0 { + return nil, nil + } var adheseBid = adheseBidResponseArray[0] if adheseBid.Origin == "JERLICIA" { diff --git a/adapters/adhese/adhese_test.go b/adapters/adhese/adhese_test.go index d09a29ee9bd..2b70bb001a6 100644 --- a/adapters/adhese/adhese_test.go +++ b/adapters/adhese/adhese_test.go @@ -3,9 +3,9 @@ package adhese import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adhese/adhesetest/supplemental/res-no_bids_200.json b/adapters/adhese/adhesetest/supplemental/res-no_bids_200.json new file mode 100644 index 00000000000..2a778bdf0b4 --- /dev/null +++ b/adapters/adhese/adhesetest/supplemental/res-no_bids_200.json @@ -0,0 +1,54 @@ +{ + "mockBidRequest": { + "id": "test-req", + "user": { + "ext": { + "consent" : "dummy" + } + }, + "imp": [ + { + "id": "test-req", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "account": "demo", + "location": "_adhese_prebid_demo_", + "format": "leaderboard", + "targets": + { + "ci": ["gent", "brussels"], + "ag": ["55"], + "tl": ["all"] + } + } + } + } + ], + "site": { + "id": "test", + "publisher": { + "id": "123" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://ads-demo.adhese.com/json/sl_adhese_prebid_demo_-leaderboard/ag55/cigent;brussels/tlall/xtdummy" + }, + "mockResponse": { + "status": 200, + "body": [] + } + } + ], + "expectedBidResponses": [] +} \ No newline at end of file diff --git a/adapters/adhese/adhesetest/supplemental/res-no_bids.json b/adapters/adhese/adhesetest/supplemental/res-no_bids_204.json similarity index 100% rename from adapters/adhese/adhesetest/supplemental/res-no_bids.json rename to adapters/adhese/adhesetest/supplemental/res-no_bids_204.json diff --git a/adapters/adhese/params_test.go b/adapters/adhese/params_test.go index 45024749b2d..1a0aa381cb1 100644 --- a/adapters/adhese/params_test.go +++ b/adapters/adhese/params_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/adhese/utils.go b/adapters/adhese/utils.go index 4a6c79c8e94..a6b7d7aae8d 100644 --- a/adapters/adhese/utils.go +++ b/adapters/adhese/utils.go @@ -1,6 +1,6 @@ package adhese -import "github.com/prebid/openrtb/v19/openrtb2" +import "github.com/prebid/openrtb/v20/openrtb2" type AdheseOriginData struct { Priority string `json:"priority"` diff --git a/adapters/adkernel/adkernel.go b/adapters/adkernel/adkernel.go index 5fae001d7dd..09a2358a951 100644 --- a/adapters/adkernel/adkernel.go +++ b/adapters/adkernel/adkernel.go @@ -5,14 +5,23 @@ import ( "fmt" "net/http" "strconv" + "strings" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +const ( + mf_suffix = "__mf" + mf_suffix_banner = "b" + mf_suffix + mf_suffix_video = "v" + mf_suffix + mf_suffix_audio = "a" + mf_suffix + mf_suffix_native = "n" + mf_suffix ) type adkernelAdapter struct { @@ -78,9 +87,6 @@ func validateImpression(imp *openrtb2.Imp, impExt *openrtb_ext.ExtImpAdkernel) e if impExt.ZoneId < 1 { return newBadInputError(fmt.Sprintf("Invalid zoneId value: %d. Ignoring imp id=%s", impExt.ZoneId, imp.ID)) } - if imp.Video == nil && imp.Banner == nil && imp.Native == nil { - return newBadInputError(fmt.Sprintf("Invalid imp id=%s. Expected imp.banner / imp.video / imp.native", imp.ID)) - } return nil } @@ -90,54 +96,75 @@ func dispatchImpressions(imps []openrtb2.Imp, impsExt []openrtb_ext.ExtImpAdkern errors := make([]error, 0) for idx := range imps { imp := imps[idx] - err := compatImpression(&imp) - if err != nil { - errors = append(errors, err) - continue - } + imp.Ext = nil impExt := impsExt[idx] if res[impExt] == nil { res[impExt] = make([]openrtb2.Imp, 0) } - res[impExt] = append(res[impExt], imp) + if isMultiFormatImp(&imp) { + splImps := splitMultiFormatImp(&imp) + res[impExt] = append(res[impExt], splImps...) + } else { + res[impExt] = append(res[impExt], imp) + } } return res, errors } -// Alter impression info to comply with adkernel platform requirements -func compatImpression(imp *openrtb2.Imp) error { - imp.Ext = nil //do not forward ext to adkernel platform - if imp.Banner != nil { - return compatBannerImpression(imp) - } +func isMultiFormatImp(imp *openrtb2.Imp) bool { + count := 0 if imp.Video != nil { - return compatVideoImpression(imp) + count++ + } + if imp.Audio != nil { + count++ + } + if imp.Banner != nil { + count++ } if imp.Native != nil { - return compatNativeImpression(imp) + count++ } - return newBadInputError("Invalid impression") + return count > 1 } -func compatBannerImpression(imp *openrtb2.Imp) error { - imp.Audio = nil - imp.Video = nil - imp.Native = nil - return nil -} +func splitMultiFormatImp(imp *openrtb2.Imp) []openrtb2.Imp { + splitImps := make([]openrtb2.Imp, 0, 4) + if imp.Banner != nil { + impCopy := *imp + impCopy.Video = nil + impCopy.Native = nil + impCopy.Audio = nil + impCopy.ID += mf_suffix_banner + splitImps = append(splitImps, impCopy) + } + if imp.Video != nil { + impCopy := *imp + impCopy.Banner = nil + impCopy.Native = nil + impCopy.Audio = nil + impCopy.ID += mf_suffix_video + splitImps = append(splitImps, impCopy) + } -func compatVideoImpression(imp *openrtb2.Imp) error { - imp.Banner = nil - imp.Audio = nil - imp.Native = nil - return nil -} + if imp.Native != nil { + impCopy := *imp + impCopy.Banner = nil + impCopy.Video = nil + impCopy.Audio = nil + impCopy.ID += mf_suffix_native + splitImps = append(splitImps, impCopy) + } -func compatNativeImpression(imp *openrtb2.Imp) error { - imp.Banner = nil - imp.Audio = nil - imp.Video = nil - return nil + if imp.Audio != nil { + impCopy := *imp + impCopy.Banner = nil + impCopy.Video = nil + impCopy.Native = nil + impCopy.ID += mf_suffix_audio + splitImps = append(splitImps, impCopy) + } + return splitImps } func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpAdkernel, error) { @@ -232,19 +259,45 @@ func (adapter *adkernelAdapter) MakeBids(internalRequest *openrtb2.BidRequest, e bidResponse.Currency = bidResp.Cur for i := 0; i < len(seatBid.Bid); i++ { bid := seatBid.Bid[i] + origImpId := bid.ImpID + typeSuffix := "" + if strings.HasSuffix(origImpId, mf_suffix) { + sfxStart := len(origImpId) - len(mf_suffix) - 1 + typeSuffix = origImpId[sfxStart:] + bid.ImpID = origImpId[:sfxStart] + } bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ Bid: &bid, - BidType: getMediaTypeForImpID(bid.ImpID, internalRequest.Imp), + BidType: getMediaTypeForImpID(bid.ImpID, typeSuffix, internalRequest.Imp), }) } return bidResponse, nil } // getMediaTypeForImp figures out which media type this bid is for -func getMediaTypeForImpID(impID string, imps []openrtb2.Imp) openrtb_ext.BidType { - for _, imp := range imps { - if imp.ID == impID && imp.Banner != nil { +func getMediaTypeForImpID(impID string, typeSuffix string, imps []openrtb2.Imp) openrtb_ext.BidType { + if len(typeSuffix) > 0 { + if typeSuffix == mf_suffix_banner { return openrtb_ext.BidTypeBanner + } else if typeSuffix == mf_suffix_video { + return openrtb_ext.BidTypeVideo + } else if typeSuffix == mf_suffix_audio { + return openrtb_ext.BidTypeAudio + } else if typeSuffix == mf_suffix_native { + return openrtb_ext.BidTypeNative + } + } + for _, imp := range imps { + if imp.ID == impID { + if imp.Banner != nil { + return openrtb_ext.BidTypeBanner + } else if imp.Video != nil { + return openrtb_ext.BidTypeVideo + } else if imp.Audio != nil { + return openrtb_ext.BidTypeAudio + } else if imp.Native != nil { + return openrtb_ext.BidTypeNative + } } } return openrtb_ext.BidTypeVideo diff --git a/adapters/adkernel/adkernel_test.go b/adapters/adkernel/adkernel_test.go index ae35f712400..e7ff02e4c03 100644 --- a/adapters/adkernel/adkernel_test.go +++ b/adapters/adkernel/adkernel_test.go @@ -3,15 +3,15 @@ package adkernel import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) func TestJsonSamples(t *testing.T) { bidder, buildErr := Builder(openrtb_ext.BidderAdkernel, config.Adapter{ - Endpoint: "https://pbs.adksrv.com/hb?zone={{.ZoneID}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + Endpoint: "http://pbs.adksrv.com/hb?zone={{.ZoneID}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) if buildErr != nil { t.Fatalf("Builder returned unexpected error %v", buildErr) diff --git a/adapters/adkernel/adkerneltest/exemplary/multiformat-impression.json b/adapters/adkernel/adkerneltest/exemplary/multiformat-impression.json index b8fd88dcdae..49c5249f0cc 100644 --- a/adapters/adkernel/adkerneltest/exemplary/multiformat-impression.json +++ b/adapters/adkernel/adkerneltest/exemplary/multiformat-impression.json @@ -14,6 +14,10 @@ "banner" : { "format": [{"w": 300,"h": 250}] }, + "native" : { + "request": "{native: \"request\"}", + "ver": "1.2" + }, "ext": { "bidder": { "zoneId": 102, @@ -36,15 +40,30 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://pbs.adksrv.com/hb?zone=102", + "uri": "http://pbs.adksrv.com/hb?zone=102", "body": { "id": "0000000000001", "imp": [ { - "id": "multi-adunit", + "id": "multi-adunitb__mf", "banner": { "format": [{"w": 300, "h": 250}] } + }, + { + "id": "multi-adunitv__mf", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 480 + } + }, + { + "id": "multi-adunitn__mf", + "native" : { + "request": "{native: \"request\"}", + "ver": "1.2" + } } ], "app": { @@ -65,7 +84,7 @@ "bid": [ { "id": "bid02", - "impid": "multi-adunit", + "impid": "multi-adunitb__mf", "price": 2.25, "cid": "1001", "crid": "2002", @@ -74,6 +93,30 @@ "adomain": [ "tag-example.com" ] + }, + { + "id": "bid03", + "impid": "multi-adunitn__mf", + "price": 1.25, + "cid": "601", + "crid": "702", + "adid": "702", + "adm": "", + "adomain": [ + "tag-example.com" + ] + }, + { + "id": "bid04", + "impid": "multi-adunita__mf", + "price": 1.0, + "cid": "161", + "crid": "172", + "adid": "172", + "nurl": "http://adkernel.com/win?f=nurl", + "adomain": [ + "tag-example.com" + ] } ] } @@ -101,6 +144,36 @@ "crid": "2002" }, "type": "banner" + }, + { + "bid": { + "id": "bid03", + "impid": "multi-adunit", + "price": 1.25, + "adm": "", + "adomain": [ + "tag-example.com" + ], + "cid": "601", + "crid": "702", + "adid": "702" + }, + "type": "native" + }, + { + "bid": { + "id": "bid04", + "impid": "multi-adunit", + "price": 1.0, + "nurl": "http://adkernel.com/win?f=nurl", + "adomain": [ + "tag-example.com" + ], + "cid": "161", + "crid": "172", + "adid": "172" + }, + "type": "audio" } ] } diff --git a/adapters/adkernel/adkerneltest/exemplary/single-banner-impression.json b/adapters/adkernel/adkerneltest/exemplary/single-banner-impression.json index 24f86378fe5..6a01b847c5f 100644 --- a/adapters/adkernel/adkerneltest/exemplary/single-banner-impression.json +++ b/adapters/adkernel/adkerneltest/exemplary/single-banner-impression.json @@ -30,7 +30,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://pbs.adksrv.com/hb?zone=101", + "uri": "http://pbs.adksrv.com/hb?zone=101", "body": { "id": "0000000000001", "imp": [ diff --git a/adapters/adkernel/adkerneltest/exemplary/single-video-impression.json b/adapters/adkernel/adkerneltest/exemplary/single-video-impression.json index b13d974616e..cd458826702 100644 --- a/adapters/adkernel/adkerneltest/exemplary/single-video-impression.json +++ b/adapters/adkernel/adkerneltest/exemplary/single-video-impression.json @@ -33,7 +33,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://pbs.adksrv.com/hb?zone=102", + "uri": "http://pbs.adksrv.com/hb?zone=102", "body": { "id": "0000000000001", "imp": [ diff --git a/adapters/adkernelAdn/adkernelAdn.go b/adapters/adkernelAdn/adkernelAdn.go index 45e9e41c10c..a6255f89820 100644 --- a/adapters/adkernelAdn/adkernelAdn.go +++ b/adapters/adkernelAdn/adkernelAdn.go @@ -7,12 +7,12 @@ import ( "strconv" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adkernelAdnAdapter struct { diff --git a/adapters/adkernelAdn/adkernelAdn_test.go b/adapters/adkernelAdn/adkernelAdn_test.go index 651d82be3b6..e43d00bf0bf 100644 --- a/adapters/adkernelAdn/adkernelAdn_test.go +++ b/adapters/adkernelAdn/adkernelAdn_test.go @@ -3,9 +3,9 @@ package adkernelAdn import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adman/adman.go b/adapters/adman/adman.go index 5350fa7cb86..6611e0cdee8 100644 --- a/adapters/adman/adman.go +++ b/adapters/adman/adman.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // AdmanAdapter struct diff --git a/adapters/adman/adman_test.go b/adapters/adman/adman_test.go index 608232cc4b8..5617035c713 100644 --- a/adapters/adman/adman_test.go +++ b/adapters/adman/adman_test.go @@ -3,9 +3,9 @@ package adman import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adman/params_test.go b/adapters/adman/params_test.go index a80c2a44b8b..9d7e0c16d51 100644 --- a/adapters/adman/params_test.go +++ b/adapters/adman/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // TestValidParams makes sure that the adman schema accepts all imp.ext fields which we intend to support. diff --git a/adapters/admixer/admixer.go b/adapters/admixer/admixer.go index 9a07a8922a9..b0f8b1b2e56 100644 --- a/adapters/admixer/admixer.go +++ b/adapters/admixer/admixer.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type AdmixerAdapter struct { diff --git a/adapters/admixer/admixer_test.go b/adapters/admixer/admixer_test.go index 766f890cdf7..5985d4303c9 100644 --- a/adapters/admixer/admixer_test.go +++ b/adapters/admixer/admixer_test.go @@ -3,9 +3,9 @@ package admixer import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/admixer/params_test.go b/adapters/admixer/params_test.go index bfa75a4884f..af85569b460 100644 --- a/adapters/admixer/params_test.go +++ b/adapters/admixer/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/admixer.json diff --git a/adapters/adnuntius/adnuntius.go b/adapters/adnuntius/adnuntius.go index 3f91d7403f0..c1d71cc1ab4 100644 --- a/adapters/adnuntius/adnuntius.go +++ b/adapters/adnuntius/adnuntius.go @@ -9,12 +9,12 @@ import ( "strings" "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/timeutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/timeutil" ) type QueryString map[string]string @@ -33,12 +33,21 @@ type adnAdunit struct { type extDeviceAdnuntius struct { NoCookies bool `json:"noCookies,omitempty"` } +type siteExt struct { + Data interface{} `json:"data"` +} type Ad struct { Bid struct { Amount float64 Currency string } + NetBid struct { + Amount float64 + } + GrossBid struct { + Amount float64 + } DealID string `json:"dealId,omitempty"` AdId string CreativeWidth string @@ -65,9 +74,10 @@ type adnMetaData struct { Usi string `json:"usi,omitempty"` } type adnRequest struct { - AdUnits []adnAdunit `json:"adUnits"` - MetaData adnMetaData `json:"metaData,omitempty"` - Context string `json:"context,omitempty"` + AdUnits []adnAdunit `json:"adUnits"` + MetaData adnMetaData `json:"metaData,omitempty"` + Context string `json:"context,omitempty"` + KeyValues interface{} `json:"kv,omitempty"` } type RequestExt struct { @@ -132,7 +142,6 @@ func makeEndpointUrl(ortbRequest openrtb2.BidRequest, a *adapter, noCookies bool if deviceExt.NoCookies { noCookies = true } - } _, offset := a.time.Now().Zone() @@ -194,6 +203,7 @@ func (a *adapter) generateRequests(ortbRequest openrtb2.BidRequest) ([]*adapters Message: fmt.Sprintf("ignoring imp id=%s, Adnuntius supports only Banner", imp.ID), }} } + var bidderExt adapters.ExtImpBidder if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { return nil, []error{&errortypes.BadInput{ @@ -204,7 +214,7 @@ func (a *adapter) generateRequests(ortbRequest openrtb2.BidRequest) ([]*adapters var adnuntiusExt openrtb_ext.ImpExtAdnunitus if err := json.Unmarshal(bidderExt.Bidder, &adnuntiusExt); err != nil { return nil, []error{&errortypes.BadInput{ - Message: fmt.Sprintf("Error unmarshalling ExtImpBmtm: %s", err.Error()), + Message: fmt.Sprintf("Error unmarshalling ExtImpValues: %s", err.Error()), }} } @@ -242,11 +252,31 @@ func (a *adapter) generateRequests(ortbRequest openrtb2.BidRequest) ([]*adapters site = ortbRequest.Site.Page } + extSite, erro := getSiteExtAsKv(&ortbRequest) + if erro != nil { + return nil, []error{fmt.Errorf("failed to parse site Ext: %v", err)} + } + for _, networkAdunits := range networkAdunitMap { adnuntiusRequest := adnRequest{ - AdUnits: networkAdunits, - Context: site, + AdUnits: networkAdunits, + Context: site, + KeyValues: extSite.Data, + } + + var extUser openrtb_ext.ExtUser + if ortbRequest.User != nil && ortbRequest.User.Ext != nil { + if err := json.Unmarshal(ortbRequest.User.Ext, &extUser); err != nil { + return nil, []error{fmt.Errorf("failed to parse Ext User: %v", err)} + } + } + + // Will change when our adserver can accept multiple user IDS + if extUser.Eids != nil && len(extUser.Eids) > 0 { + if len(extUser.Eids[0].UIDs) > 0 { + adnuntiusRequest.MetaData.Usi = extUser.Eids[0].UIDs[0].ID + } } ortbUser := ortbRequest.User @@ -303,6 +333,16 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, externalRequest *adapte return bidResponse, nil } +func getSiteExtAsKv(request *openrtb2.BidRequest) (siteExt, error) { + var extSite siteExt + if request.Site != nil && request.Site.Ext != nil { + if err := json.Unmarshal(request.Site.Ext, &extSite); err != nil { + return extSite, fmt.Errorf("failed to parse ExtSite in Adnuntius: %v", err) + } + } + return extSite, nil +} + func getGDPR(request *openrtb2.BidRequest) (string, string, error) { gdpr := "" @@ -328,7 +368,7 @@ func getGDPR(request *openrtb2.BidRequest) (string, string, error) { return gdpr, consent, nil } -func generateAdResponse(ad Ad, impId string, html string, request *openrtb2.BidRequest) (*openrtb2.Bid, []error) { +func generateAdResponse(ad Ad, imp openrtb2.Imp, html string, request *openrtb2.BidRequest) (*openrtb2.Bid, []error) { creativeWidth, widthErr := strconv.ParseInt(ad.CreativeWidth, 10, 64) if widthErr != nil { @@ -344,6 +384,31 @@ func generateAdResponse(ad Ad, impId string, html string, request *openrtb2.BidR }} } + price := ad.Bid.Amount + + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Error unmarshalling ExtImpBidder: %s", err.Error()), + }} + } + + var adnuntiusExt openrtb_ext.ImpExtAdnunitus + if err := json.Unmarshal(bidderExt.Bidder, &adnuntiusExt); err != nil { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Error unmarshalling ExtImpValues: %s", err.Error()), + }} + } + + if adnuntiusExt.BidType != "" { + if strings.EqualFold(string(adnuntiusExt.BidType), "net") { + price = ad.NetBid.Amount + } + if strings.EqualFold(string(adnuntiusExt.BidType), "gross") { + price = ad.GrossBid.Amount + } + } + adDomain := []string{} for _, url := range ad.DestinationUrls { domainArray := strings.Split(url, "/") @@ -353,14 +418,14 @@ func generateAdResponse(ad Ad, impId string, html string, request *openrtb2.BidR bid := openrtb2.Bid{ ID: ad.AdId, - ImpID: impId, + ImpID: imp.ID, W: creativeWidth, H: creativeHeight, AdID: ad.AdId, DealID: ad.DealID, CID: ad.LineItemId, CrID: ad.CreativeId, - Price: ad.Bid.Amount * 1000, + Price: price * 1000, AdM: html, ADomain: adDomain, } @@ -377,7 +442,7 @@ func generateBidResponse(adnResponse *AdnResponse, request *openrtb2.BidRequest) adunitMap[adnRespAdunit.TargetId] = adnRespAdunit } - for i, imp := range request.Imp { + for _, imp := range request.Imp { auId, _, _, err := jsonparser.Get(imp.Ext, "bidder", "auId") if err != nil { @@ -394,7 +459,7 @@ func generateBidResponse(adnResponse *AdnResponse, request *openrtb2.BidRequest) ad := adunit.Ads[0] currency = ad.Bid.Currency - adBid, err := generateAdResponse(ad, request.Imp[i].ID, adunit.Html, request) + adBid, err := generateAdResponse(ad, imp, adunit.Html, request) if err != nil { return nil, []error{&errortypes.BadInput{ Message: fmt.Sprintf("Error at ad generation"), @@ -407,7 +472,7 @@ func generateBidResponse(adnResponse *AdnResponse, request *openrtb2.BidRequest) }) for _, deal := range adunit.Deals { - dealBid, err := generateAdResponse(deal, request.Imp[i].ID, deal.Html, request) + dealBid, err := generateAdResponse(deal, imp, deal.Html, request) if err != nil { return nil, []error{&errortypes.BadInput{ Message: fmt.Sprintf("Error at ad generation"), diff --git a/adapters/adnuntius/adnuntius_test.go b/adapters/adnuntius/adnuntius_test.go index 9c431c2a315..f6edb313708 100644 --- a/adapters/adnuntius/adnuntius_test.go +++ b/adapters/adnuntius/adnuntius_test.go @@ -4,10 +4,10 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adnuntius/adnuntiustest/supplemental/check-gross-bids.json b/adapters/adnuntius/adnuntiustest/supplemental/check-gross-bids.json new file mode 100644 index 00000000000..d6301fe71cf --- /dev/null +++ b/adapters/adnuntius/adnuntiustest/supplemental/check-gross-bids.json @@ -0,0 +1,103 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "user": { + "id": "1kjh3429kjh295jkl" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "auId": "123", + "bidType": "gross" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://whatever.url?format=json&tzo=0", + "body": { + "adUnits": [ + { + "auId": "123", + "targetId": "123-test-imp-id", + "dimensions": [[300,250],[300,600]] + } + ], + "metaData": { + "usi": "1kjh3429kjh295jkl" + }, + "context": "unknown" + } + }, + "mockResponse": { + "status": 200, + "body": { + "adUnits": [ + { + "auId": "0000000000000123", + "targetId": "123-test-imp-id", + "html": "", + "responseId": "adn-rsp-900646517", + "ads": [ + { + "destinationUrls": { + "url": "http://www.google.com" + }, + "bid": { "amount": 20.0, "currency": "NOK" }, + "grossBid": {"amount": 0.1, "currency": "NOK"}, + "netBid": {"amount": 0.075, "currency": "NOK"}, + "adId": "adn-id-1559784094", + "creativeWidth": "980", + "creativeHeight": "240", + "creativeId": "jn9hpzvlsf8cpdmm", + "lineItemId": "q7y9qm5b0xt9htrv" + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "adn-id-1559784094", + "impid": "test-imp-id", + "price": 100, + "adm": "", + "adid": "adn-id-1559784094", + "adomain": [ + "google.com" + ], + "cid": "q7y9qm5b0xt9htrv", + "crid": "jn9hpzvlsf8cpdmm", + "w": 980, + "h": 240 + }, + "type": "banner" + } + ], + "currency": "NOK" + } + ] +} diff --git a/adapters/adnuntius/adnuntiustest/supplemental/check-net-bids.json b/adapters/adnuntius/adnuntiustest/supplemental/check-net-bids.json new file mode 100644 index 00000000000..ebb25b2b7ad --- /dev/null +++ b/adapters/adnuntius/adnuntiustest/supplemental/check-net-bids.json @@ -0,0 +1,103 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "user": { + "id": "1kjh3429kjh295jkl" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "auId": "123", + "bidType": "net" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://whatever.url?format=json&tzo=0", + "body": { + "adUnits": [ + { + "auId": "123", + "targetId": "123-test-imp-id", + "dimensions": [[300,250],[300,600]] + } + ], + "metaData": { + "usi": "1kjh3429kjh295jkl" + }, + "context": "unknown" + } + }, + "mockResponse": { + "status": 200, + "body": { + "adUnits": [ + { + "auId": "0000000000000123", + "targetId": "123-test-imp-id", + "html": "", + "responseId": "adn-rsp-900646517", + "ads": [ + { + "destinationUrls": { + "url": "http://www.google.com" + }, + "bid": { "amount": 20.0, "currency": "NOK" }, + "grossBid": {"amount": 0.1, "currency": "NOK"}, + "netBid": {"amount": 0.075, "currency": "NOK"}, + "adId": "adn-id-1559784094", + "creativeWidth": "980", + "creativeHeight": "240", + "creativeId": "jn9hpzvlsf8cpdmm", + "lineItemId": "q7y9qm5b0xt9htrv" + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "adn-id-1559784094", + "impid": "test-imp-id", + "price": 75, + "adm": "", + "adid": "adn-id-1559784094", + "adomain": [ + "google.com" + ], + "cid": "q7y9qm5b0xt9htrv", + "crid": "jn9hpzvlsf8cpdmm", + "w": 980, + "h": 240 + }, + "type": "banner" + } + ], + "currency": "NOK" + } + ] +} diff --git a/adapters/adnuntius/adnuntiustest/supplemental/check-price-type-error.json b/adapters/adnuntius/adnuntiustest/supplemental/check-price-type-error.json new file mode 100644 index 00000000000..89016087a43 --- /dev/null +++ b/adapters/adnuntius/adnuntiustest/supplemental/check-price-type-error.json @@ -0,0 +1,38 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "user": { + "id": "1kjh3429kjh295jkl" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "auId": "123", + "bidType": 123 + } + } + } + ] + }, + "httpCalls": [], + "expectedMakeRequestsErrors": [ + { + "value": "Error unmarshalling ExtImpValues: json: cannot unmarshal number into Go struct field ImpExtAdnunitus.bidType of type string", + "comparison": "literal" + } + ] +} diff --git a/adapters/adnuntius/adnuntiustest/supplemental/site-ext.json b/adapters/adnuntius/adnuntiustest/supplemental/site-ext.json new file mode 100644 index 00000000000..1d1ef3d3586 --- /dev/null +++ b/adapters/adnuntius/adnuntiustest/supplemental/site-ext.json @@ -0,0 +1,113 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "user": { + "id": "1kjh3429kjh295jkl" + }, + "site": { + "ext":{ + "data" : { + "key": ["value"] + } + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "auId": "123" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://whatever.url?format=json&tzo=0", + "body": { + "adUnits": [ + { + "auId": "123", + "targetId": "123-test-imp-id", + "dimensions": [[300,250],[300,600]] + } + ], + "kv": { + "key": ["value"] + }, + "metaData": { + "usi": "1kjh3429kjh295jkl" + }, + "context": "unknown" + } + }, + "mockResponse": { + "status": 200, + "body": { + "adUnits": [ + { + "auId": "0000000000000123", + "targetId": "123-test-imp-id", + "html": "", + "responseId": "adn-rsp-900646517", + "ads": [ + { + "destinationUrls": { + "url": "http://www.google.com" + }, + "bid": { + "amount": 20.0, + "currency": "NOK" + }, + "adId": "adn-id-1559784094", + "creativeWidth": "980", + "creativeHeight": "240", + "creativeId": "jn9hpzvlsf8cpdmm", + "lineItemId": "q7y9qm5b0xt9htrv" + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "adn-id-1559784094", + "impid": "test-imp-id", + "price": 20000, + "adm": "", + "adid": "adn-id-1559784094", + "adomain": [ + "google.com" + ], + "cid": "q7y9qm5b0xt9htrv", + "crid": "jn9hpzvlsf8cpdmm", + "w": 980, + "h": 240 + }, + "type": "banner" + } + ], + "currency": "NOK" + } + ] +} \ No newline at end of file diff --git a/adapters/adnuntius/adnuntiustest/supplemental/user-ext.json b/adapters/adnuntius/adnuntiustest/supplemental/user-ext.json new file mode 100644 index 00000000000..7d5f89377b7 --- /dev/null +++ b/adapters/adnuntius/adnuntiustest/supplemental/user-ext.json @@ -0,0 +1,112 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "user": { + "ext":{ + "eids" : [ + { + "source": "idProvider", + "uids": [ + { "id": "userId", "atype": 1, "ext": { "stype": "ppuid" } } + ] + } + ] + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "auId": "123" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://whatever.url?format=json&tzo=0", + "body": { + "adUnits": [ + { + "auId": "123", + "targetId": "123-test-imp-id", + "dimensions": [[300,250],[300,600]] + } + ], + "metaData": { + "usi": "userId" + }, + "context": "unknown" + } + }, + "mockResponse": { + "status": 200, + "body": { + "adUnits": [ + { + "auId": "0000000000000123", + "targetId": "123-test-imp-id", + "html": "", + "responseId": "adn-rsp-900646517", + "ads": [ + { + "destinationUrls": { + "url": "http://www.google.com" + }, + "bid": { + "amount": 20.0, + "currency": "NOK" + }, + "adId": "adn-id-1559784094", + "creativeWidth": "980", + "creativeHeight": "240", + "creativeId": "jn9hpzvlsf8cpdmm", + "lineItemId": "q7y9qm5b0xt9htrv" + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "adn-id-1559784094", + "impid": "test-imp-id", + "price": 20000, + "adm": "", + "adid": "adn-id-1559784094", + "adomain": [ + "google.com" + ], + "cid": "q7y9qm5b0xt9htrv", + "crid": "jn9hpzvlsf8cpdmm", + "w": 980, + "h": 240 + }, + "type": "banner" + } + ], + "currency": "NOK" + } + ] +} \ No newline at end of file diff --git a/adapters/adnuntius/params_test.go b/adapters/adnuntius/params_test.go index c3b42018340..259b5145801 100644 --- a/adapters/adnuntius/params_test.go +++ b/adapters/adnuntius/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/adnuntius.json diff --git a/adapters/adocean/adocean.go b/adapters/adocean/adocean.go index a4e6223be6d..778a6f60a6b 100644 --- a/adapters/adocean/adocean.go +++ b/adapters/adocean/adocean.go @@ -13,12 +13,12 @@ import ( "strings" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const adapterVersion = "1.3.0" diff --git a/adapters/adocean/adocean_test.go b/adapters/adocean/adocean_test.go index 8d646cb9ca0..d7fe33b8de5 100644 --- a/adapters/adocean/adocean_test.go +++ b/adapters/adocean/adocean_test.go @@ -3,9 +3,9 @@ package adocean import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adocean/params_test.go b/adapters/adocean/params_test.go index 18625b5e85e..f35dc1aaf87 100644 --- a/adapters/adocean/params_test.go +++ b/adapters/adocean/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/adoppler/adoppler.go b/adapters/adoppler/adoppler.go index 90070e8145d..1af0b72017d 100644 --- a/adapters/adoppler/adoppler.go +++ b/adapters/adoppler/adoppler.go @@ -8,12 +8,12 @@ import ( "net/url" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const DefaultClient = "app" @@ -65,7 +65,7 @@ func (ads *AdopplerAdapter) MakeRequests( for _, imp := range req.Imp { ext, err := unmarshalExt(imp.Ext) if err != nil { - errs = append(errs, &errortypes.BadInput{err.Error()}) + errs = append(errs, &errortypes.BadInput{Message: err.Error()}) continue } @@ -83,7 +83,7 @@ func (ads *AdopplerAdapter) MakeRequests( if err != nil { e := fmt.Sprintf("Unable to build bid URI: %s", err.Error()) - errs = append(errs, &errortypes.BadInput{e}) + errs = append(errs, &errortypes.BadInput{Message: e}) continue } data := &adapters.RequestData{ @@ -110,11 +110,11 @@ func (ads *AdopplerAdapter) MakeBids( return nil, nil } if resp.StatusCode == http.StatusBadRequest { - return nil, []error{&errortypes.BadInput{"bad request"}} + return nil, []error{&errortypes.BadInput{Message: "bad request"}} } if resp.StatusCode != http.StatusOK { err := &errortypes.BadServerResponse{ - fmt.Sprintf("unexpected status: %d", resp.StatusCode), + Message: fmt.Sprintf("unexpected status: %d", resp.StatusCode), } return nil, []error{err} } @@ -123,7 +123,7 @@ func (ads *AdopplerAdapter) MakeBids( err := json.Unmarshal(resp.Body, &bidResp) if err != nil { err := &errortypes.BadServerResponse{ - fmt.Sprintf("invalid body: %s", err.Error()), + Message: fmt.Sprintf("invalid body: %s", err.Error()), } return nil, []error{err} } @@ -132,7 +132,7 @@ func (ads *AdopplerAdapter) MakeBids( for _, imp := range intReq.Imp { if _, ok := impTypes[imp.ID]; ok { return nil, []error{&errortypes.BadInput{ - fmt.Sprintf("duplicate $.imp.id %s", imp.ID), + Message: fmt.Sprintf("duplicate $.imp.id %s", imp.ID), }} } if imp.Banner != nil { @@ -145,7 +145,7 @@ func (ads *AdopplerAdapter) MakeBids( impTypes[imp.ID] = openrtb_ext.BidTypeNative } else { return nil, []error{&errortypes.BadInput{ - "one of $.imp.banner, $.imp.video, $.imp.audio and $.imp.native field required", + Message: "one of $.imp.banner, $.imp.video, $.imp.audio and $.imp.native field required", }} } } @@ -156,7 +156,7 @@ func (ads *AdopplerAdapter) MakeBids( tp, ok := impTypes[bid.ImpID] if !ok { err := &errortypes.BadServerResponse{ - fmt.Sprintf("unknown impid: %s", bid.ImpID), + Message: fmt.Sprintf("unknown impid: %s", bid.ImpID), } return nil, []error{err} } @@ -165,11 +165,11 @@ func (ads *AdopplerAdapter) MakeBids( if tp == openrtb_ext.BidTypeVideo { adsExt, err := unmarshalAdsExt(bid.Ext) if err != nil { - return nil, []error{&errortypes.BadServerResponse{err.Error()}} + return nil, []error{&errortypes.BadServerResponse{Message: err.Error()}} } if adsExt == nil || adsExt.Video == nil { return nil, []error{&errortypes.BadServerResponse{ - "$.seatbid.bid.ext.ads.video required", + Message: "$.seatbid.bid.ext.ads.video required", }} } bidVideo = &openrtb_ext.ExtBidPrebidVideo{ diff --git a/adapters/adoppler/adoppler_test.go b/adapters/adoppler/adoppler_test.go index 9f026b2f29c..d05118c4800 100644 --- a/adapters/adoppler/adoppler_test.go +++ b/adapters/adoppler/adoppler_test.go @@ -3,9 +3,9 @@ package adoppler import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adot/adot.go b/adapters/adot/adot.go index fbba9fee467..a0274c29ddf 100644 --- a/adapters/adot/adot.go +++ b/adapters/adot/adot.go @@ -7,11 +7,11 @@ import ( "strconv" "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/adot/adot_test.go b/adapters/adot/adot_test.go index 2f35f2a85fa..236287fffa0 100644 --- a/adapters/adot/adot_test.go +++ b/adapters/adot/adot_test.go @@ -4,11 +4,11 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adot/params_test.go b/adapters/adot/params_test.go index 6760419b470..a47ff2ff4eb 100644 --- a/adapters/adot/params_test.go +++ b/adapters/adot/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/adot.json diff --git a/adapters/adpone/adpone.go b/adapters/adpone/adpone.go index bf7ffb93992..f5c9121e86d 100644 --- a/adapters/adpone/adpone.go +++ b/adapters/adpone/adpone.go @@ -5,12 +5,12 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/errortypes" ) // Builder builds a new instance of the Adpone adapter for the given bidder with the given config. diff --git a/adapters/adpone/adpone_test.go b/adapters/adpone/adpone_test.go index 7b01a382587..bd4f5ed514e 100644 --- a/adapters/adpone/adpone_test.go +++ b/adapters/adpone/adpone_test.go @@ -3,9 +3,9 @@ package adpone import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const testsDir = "adponetest" diff --git a/adapters/adprime/adprime.go b/adapters/adprime/adprime.go index 836abe26d83..b53ab4aa7dc 100644 --- a/adapters/adprime/adprime.go +++ b/adapters/adprime/adprime.go @@ -6,11 +6,11 @@ import ( "net/http" "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // AdprimeAdapter struct @@ -148,7 +148,8 @@ func (a *AdprimeAdapter) MakeBids(internalRequest *openrtb2.BidRequest, external for _, sb := range bidResp.SeatBid { for i := range sb.Bid { - bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp) + bidType, err := getBidMediaType(&sb.Bid[i]) + if err != nil { errs = append(errs, err) } else { @@ -163,22 +164,15 @@ func (a *AdprimeAdapter) MakeBids(internalRequest *openrtb2.BidRequest, external return bidResponse, errs } -func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) { - for _, imp := range imps { - if imp.ID == impID { - if imp.Banner != nil { - return openrtb_ext.BidTypeBanner, nil - } - if imp.Video != nil { - return openrtb_ext.BidTypeVideo, nil - } - if imp.Native != nil { - return openrtb_ext.BidTypeNative, nil - } - } - } - - return "", &errortypes.BadInput{ - Message: fmt.Sprintf("Failed to find impression \"%s\"", impID), +func getBidMediaType(bid *openrtb2.Bid) (openrtb_ext.BidType, error) { + switch bid.MType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo, nil + case openrtb2.MarkupNative: + return openrtb_ext.BidTypeNative, nil + default: + return "", fmt.Errorf("Unable to fetch mediaType in multi-format: %s", bid.ImpID) } } diff --git a/adapters/adprime/adprime_test.go b/adapters/adprime/adprime_test.go index e5cf7df8df5..34dde15d1ba 100644 --- a/adapters/adprime/adprime_test.go +++ b/adapters/adprime/adprime_test.go @@ -3,9 +3,9 @@ package adprime import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adprime/adprimetest/exemplary/simple-banner.json b/adapters/adprime/adprimetest/exemplary/simple-banner.json index e96abe5cff1..88ed1770d14 100644 --- a/adapters/adprime/adprimetest/exemplary/simple-banner.json +++ b/adapters/adprime/adprimetest/exemplary/simple-banner.json @@ -91,6 +91,7 @@ "dealid": "test_dealid", "w": 300, "h": 250, + "mtype": 1, "ext": { "prebid": { "type": "banner" @@ -120,6 +121,7 @@ "dealid": "test_dealid", "w": 300, "h": 250, + "mtype": 1, "ext": { "prebid": { "type": "banner" diff --git a/adapters/adprime/adprimetest/exemplary/simple-native.json b/adapters/adprime/adprimetest/exemplary/simple-native.json index 0ea8a0a1e3f..4ec94edeb50 100644 --- a/adapters/adprime/adprimetest/exemplary/simple-native.json +++ b/adapters/adprime/adprimetest/exemplary/simple-native.json @@ -75,6 +75,7 @@ "dealid": "test_dealid", "w": 300, "h": 250, + "mtype": 4, "ext": { "prebid": { "type": "native" @@ -104,6 +105,7 @@ "dealid": "test_dealid", "w": 300, "h": 250, + "mtype": 4, "ext": { "prebid": { "type": "native" diff --git a/adapters/adprime/adprimetest/exemplary/simple-video.json b/adapters/adprime/adprimetest/exemplary/simple-video.json index 164e3040b1d..722cfd29612 100644 --- a/adapters/adprime/adprimetest/exemplary/simple-video.json +++ b/adapters/adprime/adprimetest/exemplary/simple-video.json @@ -87,6 +87,7 @@ "cid": "test_cid", "crid": "test_crid", "dealid": "test_dealid", + "mtype": 2, "ext": { "prebid": { "type": "video" @@ -115,6 +116,7 @@ "cid": "test_cid", "crid": "test_crid", "dealid": "test_dealid", + "mtype": 2, "ext": { "prebid": { "type": "video" diff --git a/adapters/adprime/adprimetest/exemplary/simple-web-banner.json b/adapters/adprime/adprimetest/exemplary/simple-web-banner.json index 74797b199b6..db518934055 100644 --- a/adapters/adprime/adprimetest/exemplary/simple-web-banner.json +++ b/adapters/adprime/adprimetest/exemplary/simple-web-banner.json @@ -91,6 +91,7 @@ "dealid": "test_dealid", "w": 468, "h": 60, + "mtype": 1, "ext": { "prebid": { "type": "banner" @@ -120,6 +121,7 @@ "dealid": "test_dealid", "w": 468, "h": 60, + "mtype": 1, "ext": { "prebid": { "type": "banner" diff --git a/adapters/adprime/adprimetest/exemplary/withAudiences.json b/adapters/adprime/adprimetest/exemplary/withAudiences.json index ed8e1c003b3..40a0304656b 100644 --- a/adapters/adprime/adprimetest/exemplary/withAudiences.json +++ b/adapters/adprime/adprimetest/exemplary/withAudiences.json @@ -95,6 +95,7 @@ "dealid": "test_dealid", "w": 300, "h": 250, + "mtype": 1, "ext": { "prebid": { "type": "banner" @@ -124,6 +125,7 @@ "dealid": "test_dealid", "w": 300, "h": 250, + "mtype": 1, "ext": { "prebid": { "type": "banner" diff --git a/adapters/adprime/adprimetest/exemplary/withKeywords.json b/adapters/adprime/adprimetest/exemplary/withKeywords.json index 3e1528fe78e..b7018097902 100644 --- a/adapters/adprime/adprimetest/exemplary/withKeywords.json +++ b/adapters/adprime/adprimetest/exemplary/withKeywords.json @@ -93,6 +93,7 @@ "dealid": "test_dealid", "w": 300, "h": 250, + "mtype": 1, "ext": { "prebid": { "type": "banner" @@ -122,6 +123,7 @@ "dealid": "test_dealid", "w": 300, "h": 250, + "mtype": 1, "ext": { "prebid": { "type": "banner" diff --git a/adapters/adprime/adprimetest/supplemental/bad_media_type.json b/adapters/adprime/adprimetest/supplemental/bad_media_type.json index d58833702f5..3dce9019174 100644 --- a/adapters/adprime/adprimetest/supplemental/bad_media_type.json +++ b/adapters/adprime/adprimetest/supplemental/bad_media_type.json @@ -85,7 +85,7 @@ "expectedBidResponses": [{"currency":"USD","bids":[]}], "expectedMakeBidsErrors": [ { - "value": "Failed to find impression \"test-imp-id\"", + "value": "Unable to fetch mediaType in multi-format: test-imp-id", "comparison": "literal" } ] diff --git a/adapters/adprime/params_test.go b/adapters/adprime/params_test.go index b466c658ede..67bbebf0086 100644 --- a/adapters/adprime/params_test.go +++ b/adapters/adprime/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // TestValidParams makes sure that the adprime schema accepts all imp.ext fields which we intend to support. diff --git a/adapters/adquery/adquery.go b/adapters/adquery/adquery.go index 6a7dafa0ccb..e14c52f183d 100644 --- a/adapters/adquery/adquery.go +++ b/adapters/adquery/adquery.go @@ -8,11 +8,11 @@ import ( "strconv" "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const ( @@ -34,17 +34,14 @@ func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) ( } func (a *adapter) MakeRequests(request *openrtb2.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { - headers := http.Header{} - headers.Add("Content-Type", "application/json;charset=utf-8") - headers.Add("Accept", "application/json") - headers.Add("x-openrtb-version", "2.5") + headers := buildHeaders(request) var result []*adapters.RequestData var errs []error for _, imp := range request.Imp { ext, err := parseExt(imp.Ext) if err != nil { - errs = append(errs, &errortypes.BadInput{err.Error()}) + errs = append(errs, &errortypes.BadInput{Message: err.Error()}) continue } @@ -112,13 +109,27 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData return bidResponse, nil } +func buildHeaders(bidReq *openrtb2.BidRequest) http.Header { + headers := http.Header{} + + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + headers.Add("X-Openrtb-Version", "2.5") + + if bidReq.Device != nil && len(bidReq.Device.IP) > 0 { + headers.Add("X-Forwarded-For", bidReq.Device.IP) + } + + return headers +} + func buildRequest(bidReq *openrtb2.BidRequest, imp *openrtb2.Imp, ext *openrtb_ext.ImpExtAdQuery) *BidderRequest { userId := "" if bidReq.User != nil { userId = bidReq.User.ID } - return &BidderRequest{ + bidderRequest := &BidderRequest{ V: prebidVersion, PlacementCode: ext.PlacementID, AuctionId: "", @@ -132,6 +143,18 @@ func buildRequest(bidReq *openrtb2.BidRequest, imp *openrtb2.Imp, ext *openrtb_e BidderRequestsCount: 1, Sizes: getImpSizes(imp), } + + if bidReq.Device != nil { + bidderRequest.BidIp = bidReq.Device.IP + bidderRequest.BidIpv6 = bidReq.Device.IPv6 + bidderRequest.BidUa = bidReq.Device.UA + } + + if bidReq.Site != nil { + bidderRequest.BidPageUrl = bidReq.Site.Page + } + + return bidderRequest } func parseExt(ext json.RawMessage) (*openrtb_ext.ImpExtAdQuery, error) { diff --git a/adapters/adquery/adquery_test.go b/adapters/adquery/adquery_test.go index 228d835d6c4..792e7c553a8 100644 --- a/adapters/adquery/adquery_test.go +++ b/adapters/adquery/adquery_test.go @@ -1,15 +1,16 @@ package adquery import ( - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" "testing" + + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { bidder, buildErr := Builder(openrtb_ext.BidderAdquery, config.Adapter{ - Endpoint: "https://bidder.adquery.io/prebid/bid"}, + Endpoint: "https://bidder2.adquery.io/prebid/bid"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 902, DataCenter: "2"}) if buildErr != nil { diff --git a/adapters/adquery/adquerytest/exemplary/empty.json b/adapters/adquery/adquerytest/exemplary/empty.json index 04f1d21bcab..055d9829e63 100644 --- a/adapters/adquery/adquerytest/exemplary/empty.json +++ b/adapters/adquery/adquerytest/exemplary/empty.json @@ -4,9 +4,16 @@ "user": { "id": "xyz" }, + "site": { + "page": "https://www.example.com" + }, + "device": { + "ip": "104.28.131.104", + "ua": "PostmanRuntime/7.26.8" + }, "imp": [], "bidder": "adquery" }, "httpCalls": [], "expectedBidResponses": [] -} \ No newline at end of file +} diff --git a/adapters/adquery/adquerytest/exemplary/many-imps.json b/adapters/adquery/adquerytest/exemplary/many-imps.json index a3985c87dc1..b3f5f8c9a78 100644 --- a/adapters/adquery/adquerytest/exemplary/many-imps.json +++ b/adapters/adquery/adquerytest/exemplary/many-imps.json @@ -4,6 +4,13 @@ "user": { "id": "d93f2a0e5f0fe2cc3a6e" }, + "site": { + "page": "https://www.example.com" + }, + "device": { + "ip": "104.28.131.104", + "ua": "PostmanRuntime/7.26.8" + }, "imp": [ { "id": "1", @@ -27,7 +34,8 @@ "type": "banner" } } - },{ + }, + { "id": "2", "tagid": "test-banner-imp-id-2", "bidder": "adquery", @@ -52,21 +60,25 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], - "X-Openrtb-Version": ["2.5"] + "X-Openrtb-Version": ["2.5"], + "X-Forwarded-For": ["104.28.131.104"] }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "104.28.131.104", + "bidIpv6": "", + "bidPageUrl": "https://www.example.com", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "PostmanRuntime/7.26.8", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "320x100,300x250", "type": "banner", @@ -98,9 +110,7 @@ "urlAdq": "https://adquery.io", "creationId": 7211, "currency": "EUR", - "adDomains": [ - "https://s1.adquery.io" - ], + "adDomains": ["https://s1.adquery.io"], "tag": " ", "adqLib": "https://api.adquery.io/js/adquery-0.1.js?time=10", "mediaType": { @@ -117,23 +127,28 @@ } } } - },{ + }, + { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], - "X-Openrtb-Version": ["2.5"] + "X-Openrtb-Version": ["2.5"], + "X-Forwarded-For": ["104.28.131.104"] }, "body": { "adUnitCode": "test-banner-imp-id-2", - "bidId": "22e26bd9a702bc2", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc2", + "bidIp": "104.28.131.104", + "bidIpv6": "", + "bidPageUrl": "https://www.example.com", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "PostmanRuntime/7.26.8", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f898", "sizes": "300x250", "type": "banner", @@ -165,9 +180,7 @@ "urlAdq": "https://adquery.io", "creationId": 7211, "currency": "EUR", - "adDomains": [ - "https://s1.adquery.io" - ], + "adDomains": ["https://s1.adquery.io"], "tag": " ", "adqLib": "https://api.adquery.io/js/adquery-0.1.js?time=10", "mediaType": { @@ -224,4 +237,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/adapters/adquery/adquerytest/exemplary/no-currency.json b/adapters/adquery/adquerytest/exemplary/no-currency.json index e97e4b9beaa..301eec783fa 100644 --- a/adapters/adquery/adquerytest/exemplary/no-currency.json +++ b/adapters/adquery/adquerytest/exemplary/no-currency.json @@ -4,6 +4,13 @@ "user": { "id": "d93f2a0e5f0fe2cc3a6e" }, + "site": { + "page": "https://www.example.com" + }, + "device": { + "ip": "104.28.131.104", + "ua": "PostmanRuntime/7.26.8" + }, "imp": [ { "id": "1", @@ -22,10 +29,10 @@ ] }, "ext": { - "bidder": { - "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", - "type": "banner" - } + "bidder": { + "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "type": "banner" + } } } ], @@ -34,21 +41,25 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], - "X-Openrtb-Version": ["2.5"] + "X-Openrtb-Version": ["2.5"], + "X-Forwarded-For": ["104.28.131.104"] }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "104.28.131.104", + "bidIpv6": "", + "bidPageUrl": "https://www.example.com", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "PostmanRuntime/7.26.8", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "320x100,300x250", "type": "banner", @@ -79,9 +90,7 @@ "domain": "https://bidder.adquery.io", "urlAdq": "https://adquery.io", "creationId": 7211, - "adDomains": [ - "https://s1.adquery.io" - ], + "adDomains": ["https://s1.adquery.io"], "tag": " ", "adqLib": "https://api.adquery.io/js/adquery-0.1.js?time=10", "mediaType": { @@ -120,4 +129,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/adapters/adquery/adquerytest/exemplary/ok.json b/adapters/adquery/adquerytest/exemplary/ok.json index e725e055293..573fb2336e6 100644 --- a/adapters/adquery/adquerytest/exemplary/ok.json +++ b/adapters/adquery/adquerytest/exemplary/ok.json @@ -4,6 +4,14 @@ "user": { "id": "d93f2a0e5f0fe2cc3a6e" }, + "site": { + "page": "https://www.example.com" + }, + "device": { + "ip": "104.28.131.104", + "ipv6": "2001:4860:4860::8888", + "ua": "PostmanRuntime/7.26.8" + }, "imp": [ { "id": "1", @@ -22,10 +30,10 @@ ] }, "ext": { - "bidder": { - "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", - "type": "banner" - } + "bidder": { + "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "type": "banner" + } } } ], @@ -34,21 +42,25 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], - "X-Openrtb-Version": ["2.5"] + "X-Openrtb-Version": ["2.5"], + "X-Forwarded-For": ["104.28.131.104"] }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "104.28.131.104", + "bidIpv6": "2001:4860:4860::8888", + "bidPageUrl": "https://www.example.com", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "PostmanRuntime/7.26.8", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "320x100,300x250", "type": "banner", @@ -80,9 +92,7 @@ "urlAdq": "https://adquery.io", "creationId": 7211, "currency": "EUR", - "adDomains": [ - "https://s1.adquery.io" - ], + "adDomains": ["https://s1.adquery.io"], "tag": " ", "adqLib": "https://api.adquery.io/js/adquery-0.1.js?time=10", "mediaType": { @@ -121,4 +131,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/adapters/adquery/adquerytest/exemplary/single-imp-banner-format.json b/adapters/adquery/adquerytest/exemplary/single-imp-banner-format.json index 7a0092d5f5a..5b2a54a70d3 100644 --- a/adapters/adquery/adquerytest/exemplary/single-imp-banner-format.json +++ b/adapters/adquery/adquerytest/exemplary/single-imp-banner-format.json @@ -4,6 +4,13 @@ "user": { "id": "d93f2a0e5f0fe2cc3a6e" }, + "site": { + "page": "https://www.example.com" + }, + "device": { + "ip": "104.28.131.104", + "ua": "PostmanRuntime/7.26.8" + }, "imp": [ { "id": "1", @@ -26,21 +33,25 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], - "X-Openrtb-Version": ["2.5"] + "X-Openrtb-Version": ["2.5"], + "X-Forwarded-For": ["104.28.131.104"] }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "104.28.131.104", + "bidIpv6": "", + "bidPageUrl": "https://www.example.com", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "PostmanRuntime/7.26.8", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "320x100", "type": "banner", @@ -72,9 +83,7 @@ "urlAdq": "https://adquery.io", "creationId": 7211, "currency": "PLN", - "adDomains": [ - "https://s1.adquery.io" - ], + "adDomains": ["https://s1.adquery.io"], "tag": " ", "adqLib": "https://api.adquery.io/js/adquery-0.1.js?time=10", "mediaType": { @@ -112,4 +121,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/adapters/adquery/adquerytest/supplemental/data-null.json b/adapters/adquery/adquerytest/supplemental/data-null.json index 0d21a2bd9f7..8bbe8dc501f 100644 --- a/adapters/adquery/adquerytest/supplemental/data-null.json +++ b/adapters/adquery/adquerytest/supplemental/data-null.json @@ -34,7 +34,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], @@ -42,13 +42,16 @@ }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "", + "bidIpv6": "", + "bidPageUrl": "", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "320x100,300x250", "type": "banner", @@ -69,4 +72,4 @@ "bids": [] } ] -} \ No newline at end of file +} diff --git a/adapters/adquery/adquerytest/supplemental/invalid-numerical-values.json b/adapters/adquery/adquerytest/supplemental/invalid-numerical-values.json index daff3d0828c..a9664e5232e 100644 --- a/adapters/adquery/adquerytest/supplemental/invalid-numerical-values.json +++ b/adapters/adquery/adquerytest/supplemental/invalid-numerical-values.json @@ -22,10 +22,10 @@ ] }, "ext": { - "bidder": { - "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", - "type": "banner" - } + "bidder": { + "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "type": "banner" + } } } ], @@ -34,7 +34,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], @@ -42,13 +42,16 @@ }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "", + "bidIpv6": "", + "bidPageUrl": "", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "320x100,300x250", "type": "banner", @@ -80,9 +83,7 @@ "urlAdq": "https://adquery.io", "creationId": 7211, "currency": "PLN", - "adDomains": [ - "https://s1.adquery.io" - ], + "adDomains": ["https://s1.adquery.io"], "tag": " ", "adqLib": "https://api.adquery.io/js/adquery-0.1.js?time=10", "mediaType": { @@ -101,14 +102,18 @@ } } ], - "expectedMakeBidsErrors": [{ - "value": "strconv.ParseFloat: parsing \"$4.14\": invalid syntax", - "comparison": "literal" - },{ - "value": "strconv.ParseInt: parsing \"320px\": invalid syntax", - "comparison": "literal" - },{ - "value": "strconv.ParseInt: parsing \"50px\": invalid syntax", - "comparison": "literal" - }] -} \ No newline at end of file + "expectedMakeBidsErrors": [ + { + "value": "strconv.ParseFloat: parsing \"$4.14\": invalid syntax", + "comparison": "literal" + }, + { + "value": "strconv.ParseInt: parsing \"320px\": invalid syntax", + "comparison": "literal" + }, + { + "value": "strconv.ParseInt: parsing \"50px\": invalid syntax", + "comparison": "literal" + } + ] +} diff --git a/adapters/adquery/adquerytest/supplemental/malformed-resp.json b/adapters/adquery/adquerytest/supplemental/malformed-resp.json index f7aa271e6fe..8ab1763c0be 100644 --- a/adapters/adquery/adquerytest/supplemental/malformed-resp.json +++ b/adapters/adquery/adquerytest/supplemental/malformed-resp.json @@ -22,10 +22,10 @@ ] }, "ext": { - "bidder": { - "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", - "type": "banner" - } + "bidder": { + "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "type": "banner" + } } } ], @@ -34,7 +34,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], @@ -42,13 +42,16 @@ }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "", + "bidIpv6": "", + "bidPageUrl": "", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "320x100,300x250", "type": "banner", @@ -80,9 +83,7 @@ "urlAdq": "https://adquery.io", "creationId": "string-identifier", "currency": "PLN", - "adDomains": [ - "https://s1.adquery.io" - ], + "adDomains": ["https://s1.adquery.io"], "tag": " ", "adqLib": "https://api.adquery.io/js/adquery-0.1.js?time=10", "mediaType": { @@ -101,8 +102,10 @@ } } ], - "expectedMakeBidsErrors": [{ - "value": "json: cannot unmarshal string into Go struct field ResponseData.data.creationId of type int64", - "comparison": "literal" - }] -} \ No newline at end of file + "expectedMakeBidsErrors": [ + { + "value": "json: cannot unmarshal string into Go struct field ResponseData.data.creationId of type int64", + "comparison": "literal" + } + ] +} diff --git a/adapters/adquery/adquerytest/supplemental/mediatype-unknown.json b/adapters/adquery/adquerytest/supplemental/mediatype-unknown.json index bf3cdc63f45..47b05d836f9 100644 --- a/adapters/adquery/adquerytest/supplemental/mediatype-unknown.json +++ b/adapters/adquery/adquerytest/supplemental/mediatype-unknown.json @@ -22,10 +22,10 @@ ] }, "ext": { - "bidder": { - "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", - "type": "banner" - } + "bidder": { + "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "type": "banner" + } } } ], @@ -34,7 +34,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], @@ -42,13 +42,16 @@ }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "", + "bidIpv6": "", + "bidPageUrl": "", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "320x100,300x250", "type": "banner", @@ -80,9 +83,7 @@ "urlAdq": "https://adquery.io", "creationId": 7211, "currency": "PLN", - "adDomains": [ - "https://s1.adquery.io" - ], + "adDomains": ["https://s1.adquery.io"], "tag": " ", "adqLib": "https://api.adquery.io/js/adquery-0.1.js?time=10", "mediaType": { @@ -101,8 +102,10 @@ } } ], - "expectedMakeBidsErrors": [{ - "value": "unsupported MediaType: unknown", - "comparison": "literal" - }] -} \ No newline at end of file + "expectedMakeBidsErrors": [ + { + "value": "unsupported MediaType: unknown", + "comparison": "literal" + } + ] +} diff --git a/adapters/adquery/adquerytest/supplemental/mediatype-video.json b/adapters/adquery/adquerytest/supplemental/mediatype-video.json index 7becebab291..a6d19e7fc5a 100644 --- a/adapters/adquery/adquerytest/supplemental/mediatype-video.json +++ b/adapters/adquery/adquerytest/supplemental/mediatype-video.json @@ -22,10 +22,10 @@ ] }, "ext": { - "bidder": { - "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", - "type": "banner" - } + "bidder": { + "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "type": "banner" + } } } ], @@ -34,7 +34,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], @@ -42,13 +42,16 @@ }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "", + "bidIpv6": "", + "bidPageUrl": "", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "320x100,300x250", "type": "banner", @@ -80,9 +83,7 @@ "urlAdq": "https://adquery.io", "creationId": 7211, "currency": "PLN", - "adDomains": [ - "https://s1.adquery.io" - ], + "adDomains": ["https://s1.adquery.io"], "tag": " ", "adqLib": "https://api.adquery.io/js/adquery-0.1.js?time=10", "mediaType": { @@ -101,8 +102,10 @@ } } ], - "expectedMakeBidsErrors": [{ - "value": "unsupported MediaType: video", - "comparison": "literal" - }] -} \ No newline at end of file + "expectedMakeBidsErrors": [ + { + "value": "unsupported MediaType: video", + "comparison": "literal" + } + ] +} diff --git a/adapters/adquery/adquerytest/supplemental/no-device.json b/adapters/adquery/adquerytest/supplemental/no-device.json new file mode 100644 index 00000000000..9876cd57c88 --- /dev/null +++ b/adapters/adquery/adquerytest/supplemental/no-device.json @@ -0,0 +1,128 @@ +{ + "mockBidRequest": { + "id": "22e26bd9a702bc", + "user": { + "id": "d93f2a0e5f0fe2cc3a6e" + }, + "site": { + "page": "http://www.example.com" + }, + "imp": [ + { + "id": "1", + "tagid": "test-banner-imp-id", + "bidder": "adquery", + "banner": { + "format": [ + { + "w": 320, + "h": 100 + }, + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "type": "banner" + } + } + } + ], + "bidder": "adquery" + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bidder2.adquery.io/prebid/bid", + "headers": { + "Accept": ["application/json"], + "Content-Type": ["application/json;charset=utf-8"], + "X-Openrtb-Version": ["2.5"] + }, + "body": { + "adUnitCode": "test-banner-imp-id", + "bidder": "adquery", + "bidderRequestId": "22e26bd9a702bc", + "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "", + "bidIpv6": "", + "bidPageUrl": "http://www.example.com", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "", + "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "sizes": "320x100,300x250", + "type": "banner", + "v": "server" + } + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "data": { + "requestId": "22e26bd9a702bc1", + "emission_id": "22e26bd9a702bc1", + "eventTracker": "https://bidder.adquery.io/prebid/ev/d30f79cf7fef47bd7a5611719f936539bec0d2e9/22e26bd9a702bc?ad=1", + "externalEmissionCodes": "", + "impressionTracker": "https://bidder.adquery.io/prebid/im/d30f79cf7fef47bd7a5611719f936539bec0d2e9/22e26bd9a702bc?ad=1", + "viewabilityTracker": "https://bidder.adquery.io/prebid/vi/d30f79cf7fef47bd7a5611719f936539bec0d2e9/22e26bd9a702bc?ad=1", + "clickTracker": "https://bidder.adquery.io/prebid/cl/d30f79cf7fef47bd7a5611719f936539bec0d2e9/22e26bd9a702bc?url=https%3A%2F%2Fbrodacid.pl%2F%3Futm_source%3Dmobile_open%26utm_medium%3Dcpc%26utm_campaign%3Dhi2023", + "link": "https://brodacid.pl/?utm_source=mobile_open&utm_medium=cpc&utm_campaign=hi2023", + "logo": "https://api.adquery.io/img/adquery.png", + "medias": [ + { + "src": "banner/2023-06-05/17591", + "ext": "zip", + "type": 3 + } + ], + "domain": "https://bidder.adquery.io", + "urlAdq": "https://adquery.io", + "creationId": 7211, + "currency": "EUR", + "adDomains": ["https://s1.adquery.io"], + "tag": " ", + "adqLib": "https://api.adquery.io/js/adquery-0.1.js?time=10", + "mediaType": { + "width": "320", + "height": "50", + "name": "banner", + "type": "banner320x50" + }, + "cpm": "4.14", + "qid": "fc08aacb07eac44421ed", + "width": "320", + "height": "50", + "isExpand": false + } + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "22e26bd9a702bc1", + "impid": "1", + "price": 4.14, + "adm": " ", + "adomain": ["https://s1.adquery.io"], + "crid": "7211", + "w": 320, + "h": 50 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/adquery/adquerytest/supplemental/no-imp-banner-measures.json b/adapters/adquery/adquerytest/supplemental/no-imp-banner-measures.json index 953d6de5d8d..c745c9a7c30 100644 --- a/adapters/adquery/adquerytest/supplemental/no-imp-banner-measures.json +++ b/adapters/adquery/adquerytest/supplemental/no-imp-banner-measures.json @@ -23,7 +23,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], @@ -31,13 +31,16 @@ }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "", + "bidIpv6": "", + "bidPageUrl": "", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "", "type": "banner", @@ -58,4 +61,4 @@ "bids": [] } ] -} \ No newline at end of file +} diff --git a/adapters/adquery/adquerytest/supplemental/no-imp-banner.json b/adapters/adquery/adquerytest/supplemental/no-imp-banner.json index 8589d97f606..f7de622587f 100644 --- a/adapters/adquery/adquerytest/supplemental/no-imp-banner.json +++ b/adapters/adquery/adquerytest/supplemental/no-imp-banner.json @@ -22,7 +22,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], @@ -30,13 +30,16 @@ }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "", + "bidIpv6": "", + "bidPageUrl": "", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "", "type": "banner", @@ -57,4 +60,4 @@ "bids": [] } ] -} \ No newline at end of file +} diff --git a/adapters/adquery/adquerytest/supplemental/no-site.json b/adapters/adquery/adquerytest/supplemental/no-site.json new file mode 100644 index 00000000000..20305f52b11 --- /dev/null +++ b/adapters/adquery/adquerytest/supplemental/no-site.json @@ -0,0 +1,130 @@ +{ + "mockBidRequest": { + "id": "22e26bd9a702bc", + "user": { + "id": "d93f2a0e5f0fe2cc3a6e" + }, + "device": { + "ip": "104.28.131.104", + "ua": "PostmanRuntime/7.26.8" + }, + "imp": [ + { + "id": "1", + "tagid": "test-banner-imp-id", + "bidder": "adquery", + "banner": { + "format": [ + { + "w": 320, + "h": 100 + }, + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "type": "banner" + } + } + } + ], + "bidder": "adquery" + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bidder2.adquery.io/prebid/bid", + "headers": { + "Accept": ["application/json"], + "Content-Type": ["application/json;charset=utf-8"], + "X-Openrtb-Version": ["2.5"], + "X-Forwarded-For": ["104.28.131.104"] + }, + "body": { + "adUnitCode": "test-banner-imp-id", + "bidder": "adquery", + "bidderRequestId": "22e26bd9a702bc", + "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "104.28.131.104", + "bidIpv6": "", + "bidPageUrl": "", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "PostmanRuntime/7.26.8", + "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "sizes": "320x100,300x250", + "type": "banner", + "v": "server" + } + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "data": { + "requestId": "22e26bd9a702bc1", + "emission_id": "22e26bd9a702bc1", + "eventTracker": "https://bidder.adquery.io/prebid/ev/d30f79cf7fef47bd7a5611719f936539bec0d2e9/22e26bd9a702bc?ad=1", + "externalEmissionCodes": "", + "impressionTracker": "https://bidder.adquery.io/prebid/im/d30f79cf7fef47bd7a5611719f936539bec0d2e9/22e26bd9a702bc?ad=1", + "viewabilityTracker": "https://bidder.adquery.io/prebid/vi/d30f79cf7fef47bd7a5611719f936539bec0d2e9/22e26bd9a702bc?ad=1", + "clickTracker": "https://bidder.adquery.io/prebid/cl/d30f79cf7fef47bd7a5611719f936539bec0d2e9/22e26bd9a702bc?url=https%3A%2F%2Fbrodacid.pl%2F%3Futm_source%3Dmobile_open%26utm_medium%3Dcpc%26utm_campaign%3Dhi2023", + "link": "https://brodacid.pl/?utm_source=mobile_open&utm_medium=cpc&utm_campaign=hi2023", + "logo": "https://api.adquery.io/img/adquery.png", + "medias": [ + { + "src": "banner/2023-06-05/17591", + "ext": "zip", + "type": 3 + } + ], + "domain": "https://bidder.adquery.io", + "urlAdq": "https://adquery.io", + "creationId": 7211, + "currency": "EUR", + "adDomains": ["https://s1.adquery.io"], + "tag": " ", + "adqLib": "https://api.adquery.io/js/adquery-0.1.js?time=10", + "mediaType": { + "width": "320", + "height": "50", + "name": "banner", + "type": "banner320x50" + }, + "cpm": "4.14", + "qid": "fc08aacb07eac44421ed", + "width": "320", + "height": "50", + "isExpand": false + } + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "22e26bd9a702bc1", + "impid": "1", + "price": 4.14, + "adm": " ", + "adomain": ["https://s1.adquery.io"], + "crid": "7211", + "w": 320, + "h": 50 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/adquery/adquerytest/supplemental/resp-bad-request.json b/adapters/adquery/adquerytest/supplemental/resp-bad-request.json index cb869625720..f5e807ebad5 100644 --- a/adapters/adquery/adquerytest/supplemental/resp-bad-request.json +++ b/adapters/adquery/adquerytest/supplemental/resp-bad-request.json @@ -22,10 +22,10 @@ ] }, "ext": { - "bidder": { - "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", - "type": "banner" - } + "bidder": { + "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "type": "banner" + } } } ], @@ -34,7 +34,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], @@ -42,13 +42,16 @@ }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "", + "bidIpv6": "", + "bidPageUrl": "", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "320x100,300x250", "type": "banner", @@ -61,8 +64,10 @@ } } ], - "expectedMakeBidsErrors": [{ - "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", - "comparison": "literal" - }] -} \ No newline at end of file + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/adquery/adquerytest/supplemental/resp-no-content.json b/adapters/adquery/adquerytest/supplemental/resp-no-content.json index 5817e15a533..1ce24c17081 100644 --- a/adapters/adquery/adquerytest/supplemental/resp-no-content.json +++ b/adapters/adquery/adquerytest/supplemental/resp-no-content.json @@ -22,10 +22,10 @@ ] }, "ext": { - "bidder": { - "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", - "type": "banner" - } + "bidder": { + "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "type": "banner" + } } } ], @@ -34,7 +34,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], @@ -42,13 +42,16 @@ }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "", + "bidIpv6": "", + "bidPageUrl": "", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "320x100,300x250", "type": "banner", @@ -61,6 +64,5 @@ } } ], - "expectedBidResponses": [ - ] -} \ No newline at end of file + "expectedBidResponses": [] +} diff --git a/adapters/adquery/adquerytest/supplemental/resp-server-error.json b/adapters/adquery/adquerytest/supplemental/resp-server-error.json index 05c1cae8488..2e8e42ba927 100644 --- a/adapters/adquery/adquerytest/supplemental/resp-server-error.json +++ b/adapters/adquery/adquerytest/supplemental/resp-server-error.json @@ -22,10 +22,10 @@ ] }, "ext": { - "bidder": { - "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", - "type": "banner" - } + "bidder": { + "placementId": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", + "type": "banner" + } } } ], @@ -34,7 +34,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bidder.adquery.io/prebid/bid", + "uri": "https://bidder2.adquery.io/prebid/bid", "headers": { "Accept": ["application/json"], "Content-Type": ["application/json;charset=utf-8"], @@ -42,13 +42,16 @@ }, "body": { "adUnitCode": "test-banner-imp-id", - "bidId": "22e26bd9a702bc1", - "bidQid": "d93f2a0e5f0fe2cc3a6e", - "bidPageUrl": "", "bidder": "adquery", "bidderRequestId": "22e26bd9a702bc", - "bidRequestsCount": 1, "bidderRequestsCount": 1, + "bidId": "22e26bd9a702bc1", + "bidIp": "", + "bidIpv6": "", + "bidPageUrl": "", + "bidQid": "d93f2a0e5f0fe2cc3a6e", + "bidRequestsCount": 1, + "bidUa": "", "placementCode": "6d93f2a0e5f0fe2cc3a6e9e3ade964b43b07f897", "sizes": "320x100,300x250", "type": "banner", @@ -61,8 +64,10 @@ } } ], - "expectedMakeBidsErrors": [{ - "value": "Unexpected status code: 500. Run with request.debug = 1 for more info", - "comparison": "literal" - }] -} \ No newline at end of file + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/adquery/params_test.go b/adapters/adquery/params_test.go index cba021007d3..e72b0b02ea3 100644 --- a/adapters/adquery/params_test.go +++ b/adapters/adquery/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/adquery/types.go b/adapters/adquery/types.go index e46afaea63e..dab9619a7f5 100644 --- a/adapters/adquery/types.go +++ b/adapters/adquery/types.go @@ -1,6 +1,6 @@ package adquery -import "github.com/prebid/prebid-server/openrtb_ext" +import "github.com/prebid/prebid-server/v2/openrtb_ext" type BidderRequest struct { V string `json:"v"` @@ -10,6 +10,9 @@ type BidderRequest struct { AdUnitCode string `json:"adUnitCode"` BidQid string `json:"bidQid"` BidId string `json:"bidId"` + BidIp string `json:"bidIp"` + BidIpv6 string `json:"bidIpv6"` + BidUa string `json:"bidUa"` Bidder string `json:"bidder"` BidPageUrl string `json:"bidPageUrl"` BidderRequestId string `json:"bidderRequestId"` diff --git a/adapters/adrino/adrino.go b/adapters/adrino/adrino.go index a63ad9beef6..4510285adc0 100644 --- a/adapters/adrino/adrino.go +++ b/adapters/adrino/adrino.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/adrino/adrino_test.go b/adapters/adrino/adrino_test.go index 7566f3ed499..e969868c135 100644 --- a/adapters/adrino/adrino_test.go +++ b/adapters/adrino/adrino_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adrino/params_test.go b/adapters/adrino/params_test.go index f82f08ce9e0..0ad36e6e4d6 100644 --- a/adapters/adrino/params_test.go +++ b/adapters/adrino/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/adrino.json diff --git a/adapters/adsinteractive/adsinteractive.go b/adapters/adsinteractive/adsinteractive.go index 04edf774b80..b7dc9fd041f 100644 --- a/adapters/adsinteractive/adsinteractive.go +++ b/adapters/adsinteractive/adsinteractive.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/adsinteractive/adsinteractive_test.go b/adapters/adsinteractive/adsinteractive_test.go index 9a1397b799f..bed577c6003 100644 --- a/adapters/adsinteractive/adsinteractive_test.go +++ b/adapters/adsinteractive/adsinteractive_test.go @@ -3,9 +3,9 @@ package adsinteractive import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const testsDir = "adsinteractivetest" diff --git a/adapters/adsinteractive/params_test.go b/adapters/adsinteractive/params_test.go index 2561fc864da..caff03a2697 100644 --- a/adapters/adsinteractive/params_test.go +++ b/adapters/adsinteractive/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/adsinteractive.json diff --git a/adapters/adtarget/adtarget.go b/adapters/adtarget/adtarget.go index 00f797eccf8..54f25205790 100644 --- a/adapters/adtarget/adtarget.go +++ b/adapters/adtarget/adtarget.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type AdtargetAdapter struct { diff --git a/adapters/adtarget/adtarget_test.go b/adapters/adtarget/adtarget_test.go index 2ee45041b09..2813ea2c195 100644 --- a/adapters/adtarget/adtarget_test.go +++ b/adapters/adtarget/adtarget_test.go @@ -3,9 +3,9 @@ package adtarget import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adtarget/params_test.go b/adapters/adtarget/params_test.go index 4c39639fb7b..d0993215086 100644 --- a/adapters/adtarget/params_test.go +++ b/adapters/adtarget/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/adtarget.json diff --git a/adapters/adtelligent/adtelligent.go b/adapters/adtelligent/adtelligent.go index e2f5ef82cab..45d4d291c4e 100644 --- a/adapters/adtelligent/adtelligent.go +++ b/adapters/adtelligent/adtelligent.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type AdtelligentAdapter struct { diff --git a/adapters/adtelligent/adtelligent_test.go b/adapters/adtelligent/adtelligent_test.go index 948710387b3..905ce013840 100644 --- a/adapters/adtelligent/adtelligent_test.go +++ b/adapters/adtelligent/adtelligent_test.go @@ -3,9 +3,9 @@ package adtelligent import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adtelligent/params_test.go b/adapters/adtelligent/params_test.go index 227920b25b4..f86a7641af9 100644 --- a/adapters/adtelligent/params_test.go +++ b/adapters/adtelligent/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/adtelligent.json diff --git a/adapters/adtrgtme/adtrgtme.go b/adapters/adtrgtme/adtrgtme.go index 254bf5051e9..e0a724146fa 100644 --- a/adapters/adtrgtme/adtrgtme.go +++ b/adapters/adtrgtme/adtrgtme.go @@ -5,12 +5,12 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/adtrgtme/adtrgtme_test.go b/adapters/adtrgtme/adtrgtme_test.go index 91d9b233ffe..07bfea3c652 100644 --- a/adapters/adtrgtme/adtrgtme_test.go +++ b/adapters/adtrgtme/adtrgtme_test.go @@ -3,9 +3,9 @@ package adtrgtme import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/adtrgtme/params_test.go b/adapters/adtrgtme/params_test.go index e89f8423ed4..4745c323887 100644 --- a/adapters/adtrgtme/params_test.go +++ b/adapters/adtrgtme/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) var validParams = []string{ diff --git a/adapters/advangelists/advangelists.go b/adapters/advangelists/advangelists.go index 19bb4c326e2..fc23973f040 100644 --- a/adapters/advangelists/advangelists.go +++ b/adapters/advangelists/advangelists.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type AdvangelistsAdapter struct { diff --git a/adapters/advangelists/advangelists_test.go b/adapters/advangelists/advangelists_test.go index e4c5debaa79..5165ef1f3a7 100644 --- a/adapters/advangelists/advangelists_test.go +++ b/adapters/advangelists/advangelists_test.go @@ -3,9 +3,9 @@ package advangelists import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/advangelists/params_test.go b/adapters/advangelists/params_test.go index a58217a0ffd..966967ba312 100644 --- a/adapters/advangelists/params_test.go +++ b/adapters/advangelists/params_test.go @@ -2,8 +2,9 @@ package advangelists import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/adview/adview.go b/adapters/adview/adview.go index d2181b6591a..d880cc4956a 100644 --- a/adapters/adview/adview.go +++ b/adapters/adview/adview.go @@ -7,18 +7,22 @@ import ( "strings" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { endpoint *template.Template } +type adviewBidExt struct { + BidType int `json:"formattype,omitempty"` +} + // Builder builds a new instance of the adview adapter for the given bidder with the given config. func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { endpointTemplate, err := template.New("endpointTemplate").Parse(config.Endpoint) @@ -33,63 +37,78 @@ func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server co } func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { - var bidderExt adapters.ExtImpBidder - imp := &request.Imp[0] - if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { - return nil, []error{&errortypes.BadInput{ - Message: fmt.Sprintf("invalid imp.ext, %s", err.Error()), - }} - } - //use adview - var advImpExt openrtb_ext.ExtImpAdView - if err := json.Unmarshal(bidderExt.Bidder, &advImpExt); err != nil { - return nil, []error{&errortypes.BadInput{ - Message: fmt.Sprintf("invalid bidderExt.Bidder, %s", err.Error()), - }} - } - imp.TagID = advImpExt.MasterTagID //tagid means posid - //for adview bid request - if imp.Banner != nil { - if len(imp.Banner.Format) != 0 { - bannerCopy := *imp.Banner - bannerCopy.H = &imp.Banner.Format[0].H - bannerCopy.W = &imp.Banner.Format[0].W - imp.Banner = &bannerCopy + var requests []*adapters.RequestData + var errors []error + + //must copy the original request. + requestCopy := *request + for _, imp := range request.Imp { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + errors = append(errors, &errortypes.BadInput{ + Message: fmt.Sprintf("invalid imp.ext, %s", err.Error()), + }) + continue + } + //use adview + var advImpExt openrtb_ext.ExtImpAdView + if err := json.Unmarshal(bidderExt.Bidder, &advImpExt); err != nil { + errors = append(errors, &errortypes.BadInput{ + Message: fmt.Sprintf("invalid bidderExt.Bidder, %s", err.Error()), + }) + continue } - } - // Check if imp comes with bid floor amount defined in a foreign currency - if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" { - // Convert to US dollars - convertedValue, err := requestInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD") - if err != nil { - return nil, []error{err} + imp.TagID = advImpExt.MasterTagID //tagid means posid + //for adview bid request + if imp.Banner != nil { + if len(imp.Banner.Format) != 0 { + bannerCopy := *imp.Banner + bannerCopy.H = &imp.Banner.Format[0].H + bannerCopy.W = &imp.Banner.Format[0].W + imp.Banner = &bannerCopy + } } - // Update after conversion. All imp elements inside request.Imp are shallow copies - // therefore, their non-pointer values are not shared memory and are safe to modify. - imp.BidFloorCur = "USD" - imp.BidFloor = convertedValue - } - // Set the CUR of bid to USD after converting all floors - request.Cur = []string{"USD"} + // Check if imp comes with bid floor amount defined in a foreign currency + if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" { + // Convert to US dollars + convertedValue, err := requestInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD") + if err != nil { + errors = append(errors, err) + continue + } + // Update after conversion. All imp elements inside request.Imp are shallow copies + // therefore, their non-pointer values are not shared memory and are safe to modify. + imp.BidFloorCur = "USD" + imp.BidFloor = convertedValue + } - url, err := a.buildEndpointURL(&advImpExt) - if err != nil { - return nil, []error{err} - } + // Set the CUR of bid to USD after converting all floors + requestCopy.Cur = []string{"USD"} + requestCopy.Imp = []openrtb2.Imp{imp} - reqJSON, err := json.Marshal(request) - if err != nil { - return nil, []error{err} - } + url, err := a.buildEndpointURL(&advImpExt) + if err != nil { + errors = append(errors, err) + continue + } + + reqJSON, err := json.Marshal(requestCopy) //request + if err != nil { + errors = append(errors, err) + continue + } - return []*adapters.RequestData{{ - Method: http.MethodPost, - Body: reqJSON, - Uri: url, - }}, nil + requestData := &adapters.RequestData{ + Method: http.MethodPost, + Uri: url, + Body: reqJSON, + } + requests = append(requests, requestData) + } + return requests, errors } func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { @@ -117,12 +136,13 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.R } bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) - bidResponse.Currency = "USD" //we just support USD for resp + //we just support USD for resp + bidResponse.Currency = "USD" var errors []error for _, seatBid := range response.SeatBid { for i, bid := range seatBid.Bid { - bidType, err := getMediaTypeForImp(bid.ImpID, request.Imp) + bidType, err := getMediaTypeForBid(bid) if err != nil { errors = append(errors, err) continue @@ -143,17 +163,15 @@ func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtImpAdView) (string, er return macros.ResolveMacros(a.endpoint, endpointParams) } -func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) { - mediaType := openrtb_ext.BidTypeBanner - for _, imp := range imps { - if imp.ID == impID { - if imp.Video != nil { - mediaType = openrtb_ext.BidTypeVideo - } else if imp.Native != nil { - mediaType = openrtb_ext.BidTypeNative - } - return mediaType, nil - } +func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { + switch bid.MType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo, nil + case openrtb2.MarkupNative: + return openrtb_ext.BidTypeNative, nil + default: + return "", fmt.Errorf("Unable to fetch mediaType in impID: %s, mType: %d", bid.ImpID, bid.MType) } - return mediaType, nil } diff --git a/adapters/adview/adview_test.go b/adapters/adview/adview_test.go index d0c993cfb56..2045586e97d 100644 --- a/adapters/adview/adview_test.go +++ b/adapters/adview/adview_test.go @@ -3,9 +3,9 @@ package adview import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/adview/adviewtest/exemplary/banner-app-format.json b/adapters/adview/adviewtest/exemplary/banner-app-format.json index 0ab4951511e..f35f9db72c6 100644 --- a/adapters/adview/adviewtest/exemplary/banner-app-format.json +++ b/adapters/adview/adviewtest/exemplary/banner-app-format.json @@ -115,7 +115,11 @@ ], "crid": "20", "w": 320, - "h": 50 + "h": 50, + "mtype": 1, + "ext": { + "formattype": 0 + } } ], "type": "banner", @@ -148,7 +152,11 @@ ], "crid": "20", "w": 320, - "h": 50 + "h": 50, + "mtype": 1, + "ext": { + "formattype": 0 + } }, "type": "banner" } diff --git a/adapters/adview/adviewtest/exemplary/banner-app-resp-no-formattype.json b/adapters/adview/adviewtest/exemplary/banner-app-resp-no-formattype.json new file mode 100644 index 00000000000..e3b92913dc6 --- /dev/null +++ b/adapters/adview/adviewtest/exemplary/banner-app-resp-no-formattype.json @@ -0,0 +1,150 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "ifa":"00000000-0000-0000-0000-000000000000", + "language": "en", + "dnt": 0 + }, + "tmax": 1000, + "user": { + "buyeruid": "awesome-user" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "w": 320, + "h": 50 + }, + "ext": { + "bidder": { + "placementId": "posid00001", + "accountId": "accountid01" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bid.adview.com/agent/thirdAdxService/accountid01", + "body": { + "id": "some-request-id", + "cur": ["USD"], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "ifa":"00000000-0000-0000-0000-000000000000", + "language": "en", + "dnt": 0 + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "w": 320, + "h": 50 + }, + "tagid": "posid00001", + "ext": { + "bidder": { + "placementId": "posid00001", + "accountId": "accountid01" + } + } + } + ], + "app": { + "id": "123456789", + "name": "Awesome App", + "bundle": "com.app.awesome", + "domain": "awesomeapp.com", + "cat": [ + "IAB22-1" + ], + "publisher": { + "id": "123456789" + } + }, + "user": { + "buyeruid": "awesome-user" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "awesome-resp-id", + "seatbid": [ + { + "bid": [ + { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 320, + "h": 50, + "mtype": 1 + } + ], + "type": "banner", + "seat": "adview" + } + ], + "cur": "USD", + "ext": { + "responsetimemillis": { + "adview": 154 + }, + "tmaxrequest": 1000 + } + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "a3ae1b4e2fc24a4fb45540082e98e161", + "impid": "some-impression-id", + "price": 3.5, + "adm": "awesome-markup", + "adomain": [ + "awesome.com" + ], + "crid": "20", + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/adview/adviewtest/exemplary/banner-app.json b/adapters/adview/adviewtest/exemplary/banner-app.json index 6aad1e8dc05..2c740504ab3 100644 --- a/adapters/adview/adviewtest/exemplary/banner-app.json +++ b/adapters/adview/adviewtest/exemplary/banner-app.json @@ -105,7 +105,11 @@ ], "crid": "20", "w": 320, - "h": 50 + "h": 50, + "mtype": 1, + "ext": { + "formattype": 0 + } } ], "type": "banner", @@ -138,7 +142,11 @@ ], "crid": "20", "w": 320, - "h": 50 + "h": 50, + "mtype": 1, + "ext": { + "formattype": 0 + } }, "type": "banner" } diff --git a/adapters/adview/adviewtest/exemplary/native-app.json b/adapters/adview/adviewtest/exemplary/native-app.json index 804494e5ff5..aea0f58d1b2 100644 --- a/adapters/adview/adviewtest/exemplary/native-app.json +++ b/adapters/adview/adviewtest/exemplary/native-app.json @@ -103,7 +103,11 @@ "adomain": [ "awesome.com" ], - "crid": "20" + "crid": "20", + "mtype": 4, + "ext": { + "formattype": 2 + } } ], "type": "native", @@ -132,9 +136,13 @@ "price": 3.5, "adm": "awesome-markup", "crid": "20", + "mtype": 4, "adomain": [ "awesome.com" - ] + ], + "ext": { + "formattype": 2 + } }, "type": "native" } diff --git a/adapters/adview/adviewtest/exemplary/video-app.json b/adapters/adview/adviewtest/exemplary/video-app.json index 57c9b85598b..8697fc0fdef 100644 --- a/adapters/adview/adviewtest/exemplary/video-app.json +++ b/adapters/adview/adviewtest/exemplary/video-app.json @@ -115,7 +115,11 @@ ], "crid": "20", "w": 1280, - "h": 720 + "h": 720, + "mtype": 2, + "ext": { + "formattype": 1 + } } ], "seat": "adview" @@ -147,7 +151,11 @@ "awesome.com" ], "w": 1280, - "h": 720 + "h": 720, + "mtype": 2, + "ext": { + "formattype": 1 + } }, "type": "video" } diff --git a/adapters/adview/params_test.go b/adapters/adview/params_test.go index 6d124e9b556..d5e498645e0 100644 --- a/adapters/adview/params_test.go +++ b/adapters/adview/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/adxcg/adxcg.go b/adapters/adxcg/adxcg.go index 6b489d322b0..6c1a4f78b2b 100644 --- a/adapters/adxcg/adxcg.go +++ b/adapters/adxcg/adxcg.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // Builder builds a new instance of the Adxcg adapter for the given bidder with the given config. diff --git a/adapters/adxcg/adxcg_test.go b/adapters/adxcg/adxcg_test.go index aa5f955c372..f117f7b2ba1 100644 --- a/adapters/adxcg/adxcg_test.go +++ b/adapters/adxcg/adxcg_test.go @@ -3,9 +3,9 @@ package adxcg import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const testsDir = "adxcgtest" diff --git a/adapters/adyoulike/adyoulike.go b/adapters/adyoulike/adyoulike.go index e00e95dccb5..d1d05f6b3de 100644 --- a/adapters/adyoulike/adyoulike.go +++ b/adapters/adyoulike/adyoulike.go @@ -7,11 +7,11 @@ import ( "strings" "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { diff --git a/adapters/adyoulike/adyoulike_test.go b/adapters/adyoulike/adyoulike_test.go index d3000f673fc..2cf7a2b49ec 100644 --- a/adapters/adyoulike/adyoulike_test.go +++ b/adapters/adyoulike/adyoulike_test.go @@ -3,9 +3,9 @@ package adyoulike import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const testsBidderEndpoint = "https://localhost/bid/4" diff --git a/adapters/adyoulike/params_test.go b/adapters/adyoulike/params_test.go index 8aebaf2844e..6eb9e09a3cb 100644 --- a/adapters/adyoulike/params_test.go +++ b/adapters/adyoulike/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/adyoulike.json diff --git a/adapters/aidem/aidem.go b/adapters/aidem/aidem.go index 9748f32c957..b50f5a7db61 100644 --- a/adapters/aidem/aidem.go +++ b/adapters/aidem/aidem.go @@ -4,16 +4,18 @@ import ( "encoding/json" "fmt" "net/http" - - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "text/template" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { - endpoint string + EndpointTemplate *template.Template } func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { @@ -23,12 +25,22 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.E return nil, []error{err} } + impExt, err := getImpressionExt(&request.Imp[0]) + if err != nil { + return nil, []error{err} + } + + url, err := a.buildEndpointURL(impExt) + if err != nil { + return nil, []error{err} + } + headers := http.Header{} headers.Add("Content-Type", "application/json;charset=utf-8") return []*adapters.RequestData{{ Method: "POST", - Uri: a.endpoint, + Uri: url, Body: reqJson, Headers: headers, }}, nil @@ -81,9 +93,15 @@ func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest // Builder builds a new instance of the AIDEM adapter for the given bidder with the given config. func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { - return &adapter{ - endpoint: config.Endpoint, - }, nil + urlTemplate, err := template.New("endpointTemplate").Parse(config.Endpoint) + if err != nil { + return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) + } + + bidder := &adapter{ + EndpointTemplate: urlTemplate, + } + return bidder, nil } func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { @@ -100,3 +118,25 @@ func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { return "", fmt.Errorf("Unable to fetch mediaType in multi-format: %s", bid.ImpID) } } + +func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpAidem, error) { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, &errortypes.BadInput{ + Message: err.Error(), + } + } + var AIDEMExt openrtb_ext.ExtImpAidem + if err := json.Unmarshal(bidderExt.Bidder, &AIDEMExt); err != nil { + return nil, &errortypes.BadInput{ + Message: err.Error(), + } + } + return &AIDEMExt, nil +} + +// Builds enpoint url based on adapter-specific pub settings from imp.ext +func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtImpAidem) (string, error) { + endpointParams := macros.EndpointTemplateParams{PublisherID: params.PublisherId} + return macros.ResolveMacros(a.EndpointTemplate, endpointParams) +} diff --git a/adapters/aidem/aidem_test.go b/adapters/aidem/aidem_test.go index 03bcc7e0fb5..558b1c040de 100644 --- a/adapters/aidem/aidem_test.go +++ b/adapters/aidem/aidem_test.go @@ -5,14 +5,14 @@ import ( "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { bidder, buildErr := Builder(openrtb_ext.BidderAidem, config.Adapter{ - Endpoint: "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + Endpoint: "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id={{.PublisherID}}", }, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) if buildErr != nil { @@ -26,5 +26,5 @@ func TestEndpointTemplateMalformed(t *testing.T) { _, buildErr := Builder(openrtb_ext.BidderAidem, config.Adapter{ Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) - assert.Nil(t, buildErr) + assert.Error(t, buildErr) } diff --git a/adapters/aidem/aidemtest/exemplary/multi-format.json b/adapters/aidem/aidemtest/exemplary/multi-format.json index 0c940d4ba59..bc0c467b646 100644 --- a/adapters/aidem/aidemtest/exemplary/multi-format.json +++ b/adapters/aidem/aidemtest/exemplary/multi-format.json @@ -35,7 +35,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id=1234", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/aidem/aidemtest/exemplary/multi-imps-multi-bid.json b/adapters/aidem/aidemtest/exemplary/multi-imps-multi-bid.json index 5c2b36948f7..68c87ee11be 100644 --- a/adapters/aidem/aidemtest/exemplary/multi-imps-multi-bid.json +++ b/adapters/aidem/aidemtest/exemplary/multi-imps-multi-bid.json @@ -41,7 +41,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id=1234", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/aidem/aidemtest/exemplary/multi-imps-single-bid.json b/adapters/aidem/aidemtest/exemplary/multi-imps-single-bid.json index 9eae7101a90..e628323ad33 100644 --- a/adapters/aidem/aidemtest/exemplary/multi-imps-single-bid.json +++ b/adapters/aidem/aidemtest/exemplary/multi-imps-single-bid.json @@ -41,7 +41,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id=1234", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/aidem/aidemtest/exemplary/no-bid.json b/adapters/aidem/aidemtest/exemplary/no-bid.json index 7418425f10b..c67a205ef58 100644 --- a/adapters/aidem/aidemtest/exemplary/no-bid.json +++ b/adapters/aidem/aidemtest/exemplary/no-bid.json @@ -24,7 +24,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id=1234", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/aidem/aidemtest/exemplary/optional-params.json b/adapters/aidem/aidemtest/exemplary/optional-params.json index 69511fb595c..856e0c5cdf1 100644 --- a/adapters/aidem/aidemtest/exemplary/optional-params.json +++ b/adapters/aidem/aidemtest/exemplary/optional-params.json @@ -25,7 +25,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id=1234", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/aidem/aidemtest/exemplary/simple-banner.json b/adapters/aidem/aidemtest/exemplary/simple-banner.json index 73db297ee42..d39ac30e459 100644 --- a/adapters/aidem/aidemtest/exemplary/simple-banner.json +++ b/adapters/aidem/aidemtest/exemplary/simple-banner.json @@ -24,7 +24,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id=1234", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/aidem/aidemtest/exemplary/simple-video.json b/adapters/aidem/aidemtest/exemplary/simple-video.json index 0daaffaa8cf..94eb98cd8a1 100644 --- a/adapters/aidem/aidemtest/exemplary/simple-video.json +++ b/adapters/aidem/aidemtest/exemplary/simple-video.json @@ -27,7 +27,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id=1234", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/aidem/aidemtest/supplemental/invalid-req-400-status-code-bad-request.json b/adapters/aidem/aidemtest/supplemental/invalid-req-400-status-code-bad-request.json index 3dea13ef7c9..e0a1f6e9837 100644 --- a/adapters/aidem/aidemtest/supplemental/invalid-req-400-status-code-bad-request.json +++ b/adapters/aidem/aidemtest/supplemental/invalid-req-400-status-code-bad-request.json @@ -41,7 +41,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id=1234", "body": { "id": "some-request-id", "imp": [ diff --git a/adapters/aidem/aidemtest/supplemental/invalid-res-200-status-code-empty-bids.json b/adapters/aidem/aidemtest/supplemental/invalid-res-200-status-code-empty-bids.json index dd64125f467..33b412b46e3 100644 --- a/adapters/aidem/aidemtest/supplemental/invalid-res-200-status-code-empty-bids.json +++ b/adapters/aidem/aidemtest/supplemental/invalid-res-200-status-code-empty-bids.json @@ -41,7 +41,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id=1234", "body": { "id": "some-request-id", "imp": [ diff --git a/adapters/aidem/aidemtest/supplemental/invalid-resp-multi-imp-type.json b/adapters/aidem/aidemtest/supplemental/invalid-resp-multi-imp-type.json index 95f97d31f72..0c390f6595d 100644 --- a/adapters/aidem/aidemtest/supplemental/invalid-resp-multi-imp-type.json +++ b/adapters/aidem/aidemtest/supplemental/invalid-resp-multi-imp-type.json @@ -57,7 +57,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id=1234", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/aidem/aidemtest/supplemental/valid-req-200-bid-response-from-aidem.json b/adapters/aidem/aidemtest/supplemental/valid-req-200-bid-response-from-aidem.json index cbd534cd91c..16cb98baf21 100644 --- a/adapters/aidem/aidemtest/supplemental/valid-req-200-bid-response-from-aidem.json +++ b/adapters/aidem/aidemtest/supplemental/valid-req-200-bid-response-from-aidem.json @@ -44,7 +44,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id=1234", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/aidem/aidemtest/supplemental/valid-req-204-response-from-aidem.json b/adapters/aidem/aidemtest/supplemental/valid-req-204-response-from-aidem.json index ddd4e5a7735..d51c6da9932 100644 --- a/adapters/aidem/aidemtest/supplemental/valid-req-204-response-from-aidem.json +++ b/adapters/aidem/aidemtest/supplemental/valid-req-204-response-from-aidem.json @@ -28,7 +28,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request?billing_id=1234", "body": { "app": { "bundle": "com.example.app" diff --git a/adapters/aidem/params_test.go b/adapters/aidem/params_test.go index 36190c0bc9f..4d2c5c14cbc 100644 --- a/adapters/aidem/params_test.go +++ b/adapters/aidem/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/aidem.json TODO: MUST BE CREATED diff --git a/adapters/aja/aja.go b/adapters/aja/aja.go index 5a8afb00045..edf9f316d4d 100644 --- a/adapters/aja/aja.go +++ b/adapters/aja/aja.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type AJAAdapter struct { diff --git a/adapters/aja/aja_test.go b/adapters/aja/aja_test.go index 75e35bedeb0..de4f1d13dab 100644 --- a/adapters/aja/aja_test.go +++ b/adapters/aja/aja_test.go @@ -3,9 +3,9 @@ package aja import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const testsBidderEndpoint = "https://localhost/bid/4" diff --git a/adapters/algorix/algorix.go b/adapters/algorix/algorix.go index 07f2b123389..d6b1ea074fa 100644 --- a/adapters/algorix/algorix.go +++ b/adapters/algorix/algorix.go @@ -7,12 +7,12 @@ import ( "net/url" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/algorix/algorix_test.go b/adapters/algorix/algorix_test.go index 762b00dcee4..a401713d290 100644 --- a/adapters/algorix/algorix_test.go +++ b/adapters/algorix/algorix_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/algorix/params_test.go b/adapters/algorix/params_test.go index 227b9e0a6d4..7017a43c730 100644 --- a/adapters/algorix/params_test.go +++ b/adapters/algorix/params_test.go @@ -2,8 +2,9 @@ package algorix import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/alkimi/alkimi.go b/adapters/alkimi/alkimi.go new file mode 100644 index 00000000000..4fe0cdefc8f --- /dev/null +++ b/adapters/alkimi/alkimi.go @@ -0,0 +1,193 @@ +package alkimi + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "strconv" + "strings" + + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/floors" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +const price_macro = "${AUCTION_PRICE}" + +type adapter struct { + endpoint string +} + +type extObj struct { + AlkimiBidderExt openrtb_ext.ExtImpAlkimi `json:"bidder"` +} + +// Builder builds a new instance of the Alkimi adapter for the given bidder with the given config. +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + endpointURL, err := url.Parse(config.Endpoint) + if err != nil || len(endpointURL.String()) == 0 { + return nil, fmt.Errorf("invalid endpoint: %v", err) + } + + bidder := &adapter{ + endpoint: endpointURL.String(), + } + return bidder, nil +} + +// MakeRequests creates Alkimi adapter requests +func (adapter *adapter) MakeRequests(request *openrtb2.BidRequest, req *adapters.ExtraRequestInfo) (reqsBidder []*adapters.RequestData, errs []error) { + reqCopy := *request + + updated, errs := updateImps(reqCopy) + if len(errs) > 0 || len(reqCopy.Imp) != len(updated) { + return nil, errs + } + + reqCopy.Imp = updated + encoded, err := json.Marshal(reqCopy) + if err != nil { + errs = append(errs, err) + } else { + reqBidder := buildBidderRequest(adapter, encoded) + reqsBidder = append(reqsBidder, reqBidder) + } + return +} + +func updateImps(bidRequest openrtb2.BidRequest) ([]openrtb2.Imp, []error) { + var errs []error + + updatedImps := make([]openrtb2.Imp, 0, len(bidRequest.Imp)) + for _, imp := range bidRequest.Imp { + + var bidderExt adapters.ExtImpBidder + var extImpAlkimi openrtb_ext.ExtImpAlkimi + + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + errs = append(errs, err) + continue + } + + if err := json.Unmarshal(bidderExt.Bidder, &extImpAlkimi); err != nil { + errs = append(errs, err) + continue + } + + var bidFloorPrice floors.Price + bidFloorPrice.FloorMinCur = imp.BidFloorCur + bidFloorPrice.FloorMin = imp.BidFloor + + if len(bidFloorPrice.FloorMinCur) > 0 && bidFloorPrice.FloorMin > 0 { + imp.BidFloor = bidFloorPrice.FloorMin + } else { + imp.BidFloor = extImpAlkimi.BidFloor + } + imp.Instl = extImpAlkimi.Instl + imp.Exp = extImpAlkimi.Exp + + temp := extObj{AlkimiBidderExt: extImpAlkimi} + temp.AlkimiBidderExt.AdUnitCode = imp.ID + + extJson, err := json.Marshal(temp) + if err != nil { + errs = append(errs, err) + continue + } + imp.Ext = extJson + updatedImps = append(updatedImps, imp) + } + + return updatedImps, errs +} + +func buildBidderRequest(adapter *adapter, encoded []byte) *adapters.RequestData { + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + reqBidder := &adapters.RequestData{ + Method: "POST", + Uri: adapter.endpoint, + Body: encoded, + Headers: headers, + } + return reqBidder +} + +// MakeBids will parse the bids from the Alkimi server +func (adapter *adapter) MakeBids(request *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + var errs []error + + if adapters.IsResponseStatusCodeNoContent(response) { + return nil, nil + } + + if err := adapters.CheckResponseStatusCodeForErrors(response); err != nil { + return nil, []error{err} + } + + var bidResp openrtb2.BidResponse + err := json.Unmarshal(response.Body, &bidResp) + if err != nil { + return nil, []error{err} + } + + seatBidCount := len(bidResp.SeatBid) + if seatBidCount == 0 { + return nil, []error{&errortypes.BadServerResponse{ + Message: "Empty SeatBid array", + }} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) + for _, seatBid := range bidResp.SeatBid { + for _, bid := range seatBid.Bid { + copyBid := bid + resolveMacros(©Bid) + impId := copyBid.ImpID + imp := request.Imp + bidType, err := getMediaTypeForImp(impId, imp) + if err != nil { + errs = append(errs, err) + continue + } + bidderBid := &adapters.TypedBid{ + Bid: ©Bid, + BidType: bidType, + } + bidResponse.Bids = append(bidResponse.Bids, bidderBid) + } + } + return bidResponse, errs +} + +func resolveMacros(bid *openrtb2.Bid) { + strPrice := strconv.FormatFloat(bid.Price, 'f', -1, 64) + bid.NURL = strings.Replace(bid.NURL, price_macro, strPrice, -1) + bid.AdM = strings.Replace(bid.AdM, price_macro, strPrice, -1) +} + +func getMediaTypeForImp(impId string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) { + for _, imp := range imps { + if imp.ID == impId { + if imp.Banner != nil { + return openrtb_ext.BidTypeBanner, nil + } + if imp.Video != nil { + return openrtb_ext.BidTypeVideo, nil + } + if imp.Audio != nil { + return openrtb_ext.BidTypeAudio, nil + } + } + } + return "", &errortypes.BadInput{ + Message: fmt.Sprintf("Failed to find imp \"%s\"", impId), + } +} diff --git a/adapters/alkimi/alkimi_test.go b/adapters/alkimi/alkimi_test.go new file mode 100644 index 00000000000..b745f0feb95 --- /dev/null +++ b/adapters/alkimi/alkimi_test.go @@ -0,0 +1,57 @@ +package alkimi + +import ( + "testing" + + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +const ( + alkimiTestEndpoint = "https://exchange.alkimi-onboarding.com/server/bid" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder( + openrtb_ext.BidderAlkimi, + config.Adapter{Endpoint: alkimiTestEndpoint}, + config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}, + ) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "alkimitest", bidder) +} + +func TestEndpointEmpty(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderAlkimi, config.Adapter{ + Endpoint: ""}, config.Server{ExternalUrl: alkimiTestEndpoint, GvlID: 1, DataCenter: "2"}) + assert.Error(t, buildErr) +} + +func TestEndpointMalformed(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderAlkimi, config.Adapter{ + Endpoint: " http://leading.space.is.invalid"}, config.Server{ExternalUrl: alkimiTestEndpoint, GvlID: 1, DataCenter: "2"}) + assert.Error(t, buildErr) +} + +func TestBuilder(t *testing.T) { + bidder, buildErr := buildBidder() + if buildErr != nil { + t.Fatalf("Failed to build bidder: %v", buildErr) + } + assert.NotNil(t, bidder) +} + +func buildBidder() (adapters.Bidder, error) { + return Builder( + openrtb_ext.BidderAlkimi, + config.Adapter{Endpoint: alkimiTestEndpoint}, + config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}, + ) +} diff --git a/adapters/alkimi/alkimitest/exemplary/simple-audio.json b/adapters/alkimi/alkimitest/exemplary/simple-audio.json new file mode 100644 index 00000000000..09ac2131a12 --- /dev/null +++ b/adapters/alkimi/alkimitest/exemplary/simple-audio.json @@ -0,0 +1,143 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "iPad" + }, + "site": { + "domain": "www.example.com", + "page": "http://www.example.com", + "publisher": { + "domain": "example.com" + }, + "ext": { + "amp": 0 + } + }, + "imp": [ + { + "id": "test-imp-id", + "tagid": "test", + "audio": { + "mimes": [ + "audio/mpeg", + "audio/mp3" + ], + "minduration": 5, + "maxduration": 30, + "minbitrate": 32, + "maxbitrate": 128 + }, + "bidfloor": 0.7, + "bidfloorcur": "USD", + "ext": { + "bidder": { + "token": "XXX", + "bidFloor": 0.5 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://exchange.alkimi-onboarding.com/server/bid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "tagid": "test", + "audio": { + "mimes": [ + "audio/mpeg", + "audio/mp3" + ], + "minduration": 5, + "maxduration": 30, + "minbitrate": 32, + "maxbitrate": 128 + }, + "bidfloor": 0.7, + "bidfloorcur": "USD", + "ext": { + "bidder": { + "token": "XXX", + "bidFloor": 0.5, + "adUnitCode": "test-imp-id", + "exp": 0, + "instl": 0 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com", + "publisher": { + "domain": "example.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ip": "123.123.123.123", + "ua": "iPad" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.9, + "adm": "<\/Error><\/Impression>00:00:15<\/Duration><\/Tracking><\/TrackingEvents><\/ClickThrough><\/VideoClicks><\/MediaFile><\/MediaFiles><\/Linear><\/Creative><\/Creatives><\/InLine><\/Ad><\/VAST>", + "cid": "test_cid", + "crid": "test_crid", + "ext": { + "prebid": { + "type": "audio" + } + } + } + ], + "seat": "alkimi" + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.9, + "adm": "<\/Error><\/Impression>00:00:15<\/Duration><\/Tracking><\/TrackingEvents><\/ClickThrough><\/VideoClicks><\/MediaFile><\/MediaFiles><\/Linear><\/Creative><\/Creatives><\/InLine><\/Ad><\/VAST>", + "cid": "test_cid", + "crid": "test_crid", + "ext": { + "prebid": { + "type": "audio" + } + } + }, + "type": "audio" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/alkimi/alkimitest/exemplary/simple-banner.json b/adapters/alkimi/alkimitest/exemplary/simple-banner.json new file mode 100644 index 00000000000..96512989596 --- /dev/null +++ b/adapters/alkimi/alkimitest/exemplary/simple-banner.json @@ -0,0 +1,150 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "iPad" + }, + "site": { + "domain": "www.example.com", + "page": "http://www.example.com", + "publisher": { + "domain": "example.com" + }, + "ext": { + "amp": 0 + } + }, + "imp": [ + { + "id": "test-imp-id", + "tagid": "test", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "token": "XXX", + "bidFloor": 0.5 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://exchange.alkimi-onboarding.com/server/bid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "tagid": "test", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "bidfloor": 0.5, + "ext": { + "bidder": { + "token": "XXX", + "bidFloor": 0.5, + "adUnitCode": "test-imp-id", + "exp": 0, + "instl": 0 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com", + "publisher": { + "domain": "example.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ip": "123.123.123.123", + "ua": "iPad" + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test_bid_id", + "impid": "test-imp-id", + "price": 0.9, + "adm": "
<\/a><\/div>\"}", - "ext": { - "prebid": { - "type": "native" - } - } - } - ], - "seat": "123" - } - ], - "bidid": "8141327771600527856", - "cur": "EUR" - } - } - } - ], - "expectedBidResponses": [ - { - "currency": "EUR", - "bids": [ - { - "bid": { - "id": "some-id", - "impid": "test-imp-id", - "price": 1, - "adid": "69595837", - "adm": "{\"assets\":[{\"id\": 2,\"img\":{\"url\":\"http://example.com/p/creative-image/5e/b6/de/c3/5eb6dec3-4854-4dcd-980a-347f36ab502e.jpg\",\"w\": 3000,\"h\": 2250}},{\"id\": 1,\"title\":{\"text\":\"This is an example Prebid Native creative\"}},{\"id\": 3,\"data\":{\"value\":\"Prebid.org\"}},{\"id\": 4,\"data\":{\"value\":\"This is a Prebid Native Creative. There are many like it, but this one is mine.\"}}],\"link\":{\"url\":\"http://example.com/click?AAAAAAAA8D8AAAAAAADwPwAAAAAAAAAAAAAAAAAA8D8AAAAAAADwPwhdYz3ZyNFNG3fXpZUyLXNZ0o5aAAAAACrElgC-AwAAvgMAAAIAAAC98iUEeP4QAAAAAABVU0QAVVNEAAEAAQARIAAAAAABAgQCAAAAAAEAhBaSXgAAAAA./pp=1/cnd=%21OwwGAQiGmooHEL3llyEY-PxDIAQoADoRZGVmYXVsdCNOWU0yOjQwMjM./bn=75922/test=1/referrer=prebid.org/clickenc=http%3A%2F%2Fprebid.org%2Fdev-docs%2Fshow-native-ads.html\"},\"imptrackers\":[\"http://example.com/openrtb_win?e=wqT_3QLFBqBFAwAAAwDWAAUBCNmku9QFEIi6jeuTm_LoTRib7t2u2tLMlnMqNgkAAAECCPA_EQEHEAAA8D8ZCQkIAAAhCQkI8D8pEQkAMQkJqAAAMKqI2wQ4vgdAvgdIAlC95ZchWPj8Q2AAaJFAeJLRBIABAYoBA1VTRJIFBvBQmAEBoAEBqAEBsAEAuAECwAEEyAEC0AEJ2AEA4AEB8AEAigI7dWYoJ2EnLCAxMzc2ODYwLCAxNTE5MzA5NDAxKTt1ZigncicsIDY5NTk1ODM3Nh4A8IqSAvUBIXRETkdfUWlHbW9vSEVMM2xseUVZQUNENF9FTXdBRGdBUUFSSXZnZFFxb2piQkZnQVlMTURhQUJ3QUhnQWdBRUFpQUVBa0FFQm1BRUJvQUVCcUFFRHNBRUF1UUVwaTRpREFBRHdQOEVCS1l1SWd3QUE4RF9KQVhfelYzek1zXzBfMlFFQUFBAQMkRHdQLUFCQVBVQgEOLEFKZ0NBS0FDQUxVQwUQBEwwCQjwTE1BQ0FNZ0NBT0FDQU9nQ0FQZ0NBSUFEQVpBREFKZ0RBYWdEaHBxS0I3b0RFV1JsWm1GMWJIUWpUbGxOTWpvME1ESXqaAjkhT3d3R0FRNvgA8E4tUHhESUFRb0FEb1JaR1ZtWVhWc2RDTk9XVTB5T2pRd01qTS7YAugH4ALH0wHqAgpwcmViaWQub3Jn8gIRCgZBRFZfSUQSBzEzNzY4NjDyARQMQ1BHXwEUNDM1MDMwOTjyAhEKBUNQARPwmQgxNDg0NzIzOIADAYgDAZADAJgDFKADAaoDAMADkBzIAwDYAwDgAwDoAwD4AwOABACSBAkvb3BlbnJ0YjKYBACiBAwxNTIuMTkzLjYuNzSoBJrMI7IEDAgAEAAYACAAMAA4ALgEAMAEAMgEANIEEWRlZmF1bHQjTllNMjo0MDIz2gQCCADgBADwBL3llyGIBQGYBQCgBf____8FA1ABqgULc29tZS1yZXEtaWTABQDJBQAFARTwP9IFCQkFC2QAAADYBQHgBQHwBd4C-gUECAAQAJAGAZgGAA..&s=08b1535744639c904684afe46e3c6c0e4786089f&test=1&referrer=prebid.org&pp=1\"],\"jstracker\":\"\"}", - "ext": { - "prebid": { - "type": "native" - } - } - }, - "type": "native" - } - ] - } - ] -} \ No newline at end of file diff --git a/adapters/suntContent/suntContenttest/supplemental/invalid_tag_id.json b/adapters/suntContent/suntContenttest/supplemental/invalid_tag_id.json deleted file mode 100644 index 1493d336f9d..00000000000 --- a/adapters/suntContent/suntContenttest/supplemental/invalid_tag_id.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "expectedMakeRequestsErrors": [ - { - "value": "could not unmarshal openrtb_ext.ImpExtSuntContent: json: cannot unmarshal number into Go struct field ImpExtSuntContent.adUnitID of type string", - "comparison": "literal" - } - ], - "mockBidRequest": { - "id": "test-request-id", - "site": { - "publisher": { - "id": "foo", - "name": "foo" - } - }, - "imp": [ - { - "id": "test-imp-id", - "native": { - "request": "{\"plcmtcnt\":1,\"plcmttype\":2,\"privacy\":1,\"context\":1,\"contextsubtype\":12,\"eventtrackers\":[{\"event\":1,\"methods\":[1,2]},{\"event\":2,\"methods\":[1]}],\"assets\":[{\"data\":{\"type\":12},\"required\":1},{\"title\":{\"len\":50},\"required\":1},{\"img\":{\"w\":80,\"h\":80,\"type\":1},\"required\":1},{\"img\":{\"w\":1200,\"h\":627,\"type\":3},\"required\":1},{\"data\":{\"type\":3},\"required\":0},{\"data\":{\"len\":100,\"type\":2},\"required\":1},{\"video\":{\"mimes\":[\"video/mpeg\",\"video/mp4\"],\"minduration\":2,\"protocols\":[2,5],\"maxduration\":2,\"ext\":{\"playbackmethod\":[1,2]}},\"required\":1}],\"ver\":\"1.2\"}" - }, - "ext": { - "bidder": { - "adUnitId": 1234 - } - } - } - ] - } -} \ No newline at end of file diff --git a/adapters/suntContent/suntContenttest/supplemental/status_bad_request.json b/adapters/suntContent/suntContenttest/supplemental/status_bad_request.json deleted file mode 100644 index d7e62bb90cb..00000000000 --- a/adapters/suntContent/suntContenttest/supplemental/status_bad_request.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "mockBidRequest": { - "id": "test-request-id", - "imp": [ - { - "id": "test-imp-id", - "banner": { - "format": [ - { - "w": 300, - "h": 250 - }, - { - "w": 300, - "h": 600 - } - ] - }, - "ext": { - "bidder": { - "adUnitId": "example-tag-id" - } - } - } - ] - }, - "httpCalls": [ - { - "expectedRequest": { - "uri": "https://mockup.suntcontent.com/", - "body": { - "cur": [ - "EUR" - ], - "id": "test-request-id", - "imp": [ - { - "id": "test-imp-id", - "banner": { - "format": [ - { - "w": 300, - "h": 250 - }, - { - "w": 300, - "h": 600 - } - ] - }, - "tagid": "example-tag-id", - "ext": { - "bidder": { - "adUnitId": "example-tag-id" - } - } - } - ] - } - }, - "mockResponse": { - "status": 400 - } - } - ], - "expectedMakeBidsErrors": [ - { - "value": "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.", - "comparison": "literal" - } - ] -} \ No newline at end of file diff --git a/adapters/suntContent/suntContenttest/supplemental/status_no_content.json b/adapters/suntContent/suntContenttest/supplemental/status_no_content.json deleted file mode 100644 index 8eda774c657..00000000000 --- a/adapters/suntContent/suntContenttest/supplemental/status_no_content.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "mockBidRequest": { - "id": "test-request-id", - "imp": [ - { - "id": "test-imp-id", - "banner": { - "format": [ - { - "w": 300, - "h": 250 - }, - { - "w": 300, - "h": 600 - } - ] - }, - "ext": { - "bidder": { - "adUnitId": "example-tag-id" - } - } - } - ] - }, - "httpCalls": [ - { - "expectedRequest": { - "uri": "https://mockup.suntcontent.com/", - "body": { - "cur": [ - "EUR" - ], - "id": "test-request-id", - "imp": [ - { - "id": "test-imp-id", - "banner": { - "format": [ - { - "w": 300, - "h": 250 - }, - { - "w": 300, - "h": 600 - } - ] - }, - "tagid": "example-tag-id", - "ext": { - "bidder": { - "adUnitId": "example-tag-id" - } - } - } - ] - } - }, - "mockResponse": { - "status": 204 - } - } - ] -} \ No newline at end of file diff --git a/adapters/suntContent/suntContenttest/supplemental/status_not_ok.json b/adapters/suntContent/suntContenttest/supplemental/status_not_ok.json deleted file mode 100644 index 07618aed7fa..00000000000 --- a/adapters/suntContent/suntContenttest/supplemental/status_not_ok.json +++ /dev/null @@ -1,72 +0,0 @@ -{ - "mockBidRequest": { - "id": "test-request-id", - "imp": [ - { - "id": "test-imp-id", - "banner": { - "format": [ - { - "w": 300, - "h": 250 - }, - { - "w": 300, - "h": 600 - } - ] - }, - "ext": { - "bidder": { - "adUnitId": "example-tag-id" - } - } - } - ] - }, - "httpCalls": [ - { - "expectedRequest": { - "uri": "https://mockup.suntcontent.com/", - "body": { - "cur": [ - "EUR" - ], - "id": "test-request-id", - "imp": [ - { - "id": "test-imp-id", - "banner": { - "format": [ - { - "w": 300, - "h": 250 - }, - { - "w": 300, - "h": 600 - } - ] - }, - "tagid": "example-tag-id", - "ext": { - "bidder": { - "adUnitId": "example-tag-id" - } - } - } - ] - } - }, - "mockResponse": { - "status": 404 - } - } - ], - "expectedMakeBidsErrors": [ - { - "value": "Unexpected status code: 404. Run with request.debug = 1 for more info.", - "comparison": "literal" - } - ] -} \ No newline at end of file diff --git a/adapters/taboola/params_test.go b/adapters/taboola/params_test.go index 51a9833cdcb..adcaa0334f7 100644 --- a/adapters/taboola/params_test.go +++ b/adapters/taboola/params_test.go @@ -2,8 +2,9 @@ package taboola import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/taboola/taboola.go b/adapters/taboola/taboola.go index 087f661104f..85a129eb341 100644 --- a/adapters/taboola/taboola.go +++ b/adapters/taboola/taboola.go @@ -8,14 +8,14 @@ import ( "strings" "text/template" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/taboola/taboola_test.go b/adapters/taboola/taboola_test.go index 320d08da22f..bd674440150 100644 --- a/adapters/taboola/taboola_test.go +++ b/adapters/taboola/taboola_test.go @@ -1,14 +1,12 @@ package taboola import ( - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/stretchr/testify/assert" "testing" -) -import ( - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/stretchr/testify/assert" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/tappx/params_test.go b/adapters/tappx/params_test.go index 8a248345994..ddfcbeb021f 100644 --- a/adapters/tappx/params_test.go +++ b/adapters/tappx/params_test.go @@ -2,8 +2,9 @@ package tappx import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/tappx/tappx.go b/adapters/tappx/tappx.go index d66a10a2bbe..a0343ccd78d 100644 --- a/adapters/tappx/tappx.go +++ b/adapters/tappx/tappx.go @@ -10,12 +10,12 @@ import ( "text/template" "time" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const TAPPX_BIDDER_VERSION = "1.5" diff --git a/adapters/tappx/tappx_test.go b/adapters/tappx/tappx_test.go index c1b711426fb..b3c6f3fe625 100644 --- a/adapters/tappx/tappx_test.go +++ b/adapters/tappx/tappx_test.go @@ -4,9 +4,9 @@ import ( "regexp" "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/teads/models.go b/adapters/teads/models.go new file mode 100644 index 00000000000..5b63c163197 --- /dev/null +++ b/adapters/teads/models.go @@ -0,0 +1,40 @@ +package teads + +import ( + "encoding/json" + "text/template" +) + +type adapter struct { + endpointTemplate *template.Template +} + +type defaultBidderImpExtension struct { + Bidder bidder `json:"bidder"` +} + +type bidder struct { + PlacementId int `json:"placementId"` +} + +type teadsImpExtension struct { + KV teadsKV `json:"kv"` +} + +type teadsKV struct { + PlacementId int `json:"placementId"` +} + +type teadsBidExt struct { + Prebid teadsPrebidExt `json:"prebid"` +} + +type teadsPrebidExt struct { + Meta teadsPrebidMeta `json:"meta"` +} + +type teadsPrebidMeta struct { + RendererName string `json:"rendererName"` + RendererVersion string `json:"rendererVersion"` + RendererData json.RawMessage `json:"rendererData"` +} diff --git a/adapters/teads/teads.go b/adapters/teads/teads.go new file mode 100644 index 00000000000..9eaaad4fd2d --- /dev/null +++ b/adapters/teads/teads.go @@ -0,0 +1,202 @@ +package teads + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "strconv" + "text/template" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +// Builder builds a new instance of the Teads adapter for the given bidder with the given config. +func Builder(_ openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + template, err := template.New("endpointTemplate").Parse(config.Endpoint) + if err != nil { + return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) + } + + bidder := &adapter{ + endpointTemplate: template, + } + return bidder, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + if len(request.Imp) == 0 { + return nil, []error{&errortypes.BadInput{ + Message: "No impression in the bid request", + }} + } + + endpointURL, err := a.buildEndpointURL() + if endpointURL == "" { + return nil, []error{err} + } + + if err := updateImpObject(request.Imp); err != nil { + return nil, []error{err} + } + + reqJSON, err := json.Marshal(request) + if err != nil { + return nil, []error{&errortypes.BadInput{ + Message: "Error parsing BidRequest object", + }} + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + return []*adapters.RequestData{{ + Method: "POST", + Uri: endpointURL, + Body: reqJSON, + Headers: headers, + }}, []error{} +} + +func updateImpObject(imps []openrtb2.Imp) error { + for i := range imps { + imp := &imps[i] + + if imp.Banner != nil { + if len(imp.Banner.Format) != 0 { + bannerCopy := *imp.Banner + bannerCopy.H = &imp.Banner.Format[0].H + bannerCopy.W = &imp.Banner.Format[0].W + imp.Banner = &bannerCopy + } + } + + var defaultImpExt defaultBidderImpExtension + if err := json.Unmarshal(imp.Ext, &defaultImpExt); err != nil { + return &errortypes.BadInput{ + Message: "Error parsing Imp.Ext object", + } + } + if defaultImpExt.Bidder.PlacementId == 0 { + return &errortypes.BadInput{ + Message: "placementId should not be 0.", + } + } + imp.TagID = strconv.Itoa(defaultImpExt.Bidder.PlacementId) + teadsImpExt := &teadsImpExtension{ + KV: teadsKV{ + PlacementId: defaultImpExt.Bidder.PlacementId, + }, + } + if extJson, err := json.Marshal(teadsImpExt); err != nil { + return &errortypes.BadInput{ + Message: "Error stringify Imp.Ext object", + } + } else { + imp.Ext = extJson + } + } + return nil +} + +// Builds enpoint url based on adapter-specific pub settings from imp.ext +func (a *adapter) buildEndpointURL() (string, error) { + endpointParams := macros.EndpointTemplateParams{} + host, err := macros.ResolveMacros(a.endpointTemplate, endpointParams) + + if err != nil { + return "", &errortypes.BadInput{ + Message: "Unable to parse endpoint url template: " + err.Error(), + } + } + + thisURI, err := url.Parse(host) + if err != nil { + return "", &errortypes.BadInput{ + Message: "Malformed URL: " + err.Error(), + } + } + + return thisURI.String(), nil +} + +func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, _ *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if adapters.IsResponseStatusCodeNoContent(response) { + return nil, nil + } + + err := adapters.CheckResponseStatusCodeForErrors(response) + if err != nil { + return nil, []error{err} + } + + var bidResp openrtb2.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidderResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid)) + + for _, sb := range bidResp.SeatBid { + for i := 0; i < len(sb.Bid); i++ { + bid := sb.Bid[i] + + bidExtTeads, err := getTeadsRendererFromBidExt(bid.Ext) + if err != nil { + return nil, err + } + bidType, err := getMediaTypeForImp(bid.ImpID, internalRequest.Imp) + if err != nil { + return nil, err + } + bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidMeta: &openrtb_ext.ExtBidPrebidMeta{ + RendererName: bidExtTeads.Prebid.Meta.RendererName, + RendererVersion: bidExtTeads.Prebid.Meta.RendererVersion, + }, + BidType: bidType, + }) + } + } + if bidResp.Cur != "" { + bidderResponse.Currency = bidResp.Cur + } + return bidderResponse, nil +} + +func getTeadsRendererFromBidExt(ext json.RawMessage) (*teadsBidExt, []error) { + var bidExtTeads teadsBidExt + if err := json.Unmarshal(ext, &bidExtTeads); err != nil { + return nil, []error{err} + } + if bidExtTeads.Prebid.Meta.RendererName == "" { + return nil, []error{&errortypes.BadInput{ + Message: "RendererName should not be empty if present", + }} + } + if bidExtTeads.Prebid.Meta.RendererVersion == "" { + return nil, []error{&errortypes.BadInput{ + Message: "RendererVersion should not be empty if present", + }} + } + return &bidExtTeads, nil +} + +func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, []error) { + for _, imp := range imps { + if imp.ID == impID { + if imp.Video != nil { + return openrtb_ext.BidTypeVideo, nil + } + return openrtb_ext.BidTypeBanner, nil + } + } + return openrtb_ext.BidType(""), []error{&errortypes.BadInput{ + Message: "Imp ids were not equals", + }} +} diff --git a/adapters/teads/teads_test.go b/adapters/teads/teads_test.go new file mode 100644 index 00000000000..c9f807ace21 --- /dev/null +++ b/adapters/teads/teads_test.go @@ -0,0 +1,28 @@ +package teads + +import ( + "testing" + + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderTeads, config.Adapter{ + Endpoint: "https://psrv.teads.tv/prebid-server/bid-request"}, config.Server{ExternalUrl: "https://psrv.teads.tv/prebid-server/bid-request", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "teadstest", bidder) +} + +func TestEndpointTemplateMalformed(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderTeads, config.Adapter{ + Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "https://psrv.teads.tv/prebid-server/bid-request", GvlID: 1, DataCenter: "2"}) + + assert.Error(t, buildErr) +} diff --git a/adapters/teads/teadstest/exemplary/simple-banner-with-format.json b/adapters/teads/teadstest/exemplary/simple-banner-with-format.json new file mode 100644 index 00000000000..ffaf849bfd8 --- /dev/null +++ b/adapters/teads/teadstest/exemplary/simple-banner-with-format.json @@ -0,0 +1,168 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://psrv.teads.tv/prebid-server/bid-request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "tagid": "125", + "banner": { + "w": 300, + "h": 250, + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "kv": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "39312703-e970-4914-ae56-8e7d7d1fd16b", + "tagid": "125", + "seatbid": [ + { + "seat": "teads", + "bid": [ + { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "teads", + "rendererVersion": "5.0.25", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "teads", + "rendererVersion": "5.0.25", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/teads/teadstest/exemplary/simple-banner.json b/adapters/teads/teadstest/exemplary/simple-banner.json new file mode 100644 index 00000000000..43a28614f9c --- /dev/null +++ b/adapters/teads/teadstest/exemplary/simple-banner.json @@ -0,0 +1,158 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://psrv.teads.tv/prebid-server/bid-request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "tagid": "125", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "kv": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "39312703-e970-4914-ae56-8e7d7d1fd16b", + "tagid": "125", + "seatbid": [ + { + "seat": "teads", + "bid": [ + { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "teads", + "rendererVersion": "5.0.25", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "teads", + "rendererVersion": "5.0.25", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/teads/teadstest/exemplary/simple-video.json b/adapters/teads/teadstest/exemplary/simple-video.json new file mode 100644 index 00000000000..de273aba904 --- /dev/null +++ b/adapters/teads/teadstest/exemplary/simple-video.json @@ -0,0 +1,184 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://psrv.teads.tv/prebid-server/bid-request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "tagid": "125", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "kv": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "39312703-e970-4914-ae56-8e7d7d1fd16b", + "tagid": "125", + "seatbid": [ + { + "seat": "teads", + "bid": [ + { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "teads", + "rendererVersion": "5.0.25", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "teads", + "rendererVersion": "5.0.25", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/teads/teadstest/supplemental/bid-id-does-not-match.json b/adapters/teads/teadstest/supplemental/bid-id-does-not-match.json new file mode 100644 index 00000000000..b2e5c9e7aba --- /dev/null +++ b/adapters/teads/teadstest/supplemental/bid-id-does-not-match.json @@ -0,0 +1,152 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://psrv.teads.tv/prebid-server/bid-request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "tagid": "125", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "kv": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "39312703-e970-4914-ae56-8e7d7d1fd16b", + "cur": "EUR", + "tagid": "125", + "seatbid": [ + { + "seat": "teads", + "bid": [ + { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "does-not-match", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "teads", + "rendererVersion": "5.0.25", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + } + ] + } + ] + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Imp ids were not equals", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/teads/teadstest/supplemental/currency-empty-string.json b/adapters/teads/teadstest/supplemental/currency-empty-string.json new file mode 100644 index 00000000000..9168f265cae --- /dev/null +++ b/adapters/teads/teadstest/supplemental/currency-empty-string.json @@ -0,0 +1,185 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://psrv.teads.tv/prebid-server/bid-request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "tagid": "125", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "kv": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "39312703-e970-4914-ae56-8e7d7d1fd16b", + "cur": "EUR", + "tagid": "125", + "seatbid": [ + { + "seat": "teads", + "bid": [ + { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "teads", + "rendererVersion": "5.0.25", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "teads", + "rendererVersion": "5.0.25", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/teads/teadstest/supplemental/no-impression-response.json b/adapters/teads/teadstest/supplemental/no-impression-response.json new file mode 100644 index 00000000000..de273aba904 --- /dev/null +++ b/adapters/teads/teadstest/supplemental/no-impression-response.json @@ -0,0 +1,184 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://psrv.teads.tv/prebid-server/bid-request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "tagid": "125", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "kv": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "39312703-e970-4914-ae56-8e7d7d1fd16b", + "tagid": "125", + "seatbid": [ + { + "seat": "teads", + "bid": [ + { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "teads", + "rendererVersion": "5.0.25", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "teads", + "rendererVersion": "5.0.25", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/kubient/kubienttest/supplemental/no-imps.json b/adapters/teads/teadstest/supplemental/no-impression.json similarity index 76% rename from adapters/kubient/kubienttest/supplemental/no-imps.json rename to adapters/teads/teadstest/supplemental/no-impression.json index 189adf9a932..7b1cdceb9e1 100644 --- a/adapters/kubient/kubienttest/supplemental/no-imps.json +++ b/adapters/teads/teadstest/supplemental/no-impression.json @@ -1,8 +1,8 @@ { "mockBidRequest": { - "id": "test-no-imp-request-id", - "imp": [] + "id": "test-request-id" }, + "expectedMakeRequestsErrors": [ { "value": "No impression in the bid request", diff --git a/adapters/kubient/kubienttest/supplemental/missing-zoneid.json b/adapters/teads/teadstest/supplemental/no-placementId.json similarity index 70% rename from adapters/kubient/kubienttest/supplemental/missing-zoneid.json rename to adapters/teads/teadstest/supplemental/no-placementId.json index cfd616621e2..e343bc82a34 100644 --- a/adapters/kubient/kubienttest/supplemental/missing-zoneid.json +++ b/adapters/teads/teadstest/supplemental/no-placementId.json @@ -3,16 +3,12 @@ "id": "test-request-id", "imp": [ { - "id": "test-missing-req-param-id", + "id": "test-imp-id-1", "banner": { "format": [ { "w": 300, "h": 250 - }, - { - "w": 300, - "h": 600 } ] }, @@ -22,10 +18,11 @@ } ] }, + "expectedMakeRequestsErrors": [ { - "value": "zoneid is empty", + "value": "placementId should not be 0.", "comparison": "literal" } ] -} +} \ No newline at end of file diff --git a/adapters/teads/teadstest/supplemental/renderer-name-empty.json b/adapters/teads/teadstest/supplemental/renderer-name-empty.json new file mode 100644 index 00000000000..9d35cf2fe71 --- /dev/null +++ b/adapters/teads/teadstest/supplemental/renderer-name-empty.json @@ -0,0 +1,143 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://psrv.teads.tv/prebid-server/bid-request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "tagid": "125", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3 + ], + "w": 940, + "h": 560 + }, + "ext": { + "kv": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "", + "version": "5.0.25", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "39312703-e970-4914-ae56-8e7d7d1fd16b", + "tagid": "125", + "seatbid": [ + { + "seat": "teads", + "bid": [ + { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "", + "rendererVersion": "5.0.25", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + } + ] + } + ] + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "RendererName should not be empty if present", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/teads/teadstest/supplemental/renderer-version-empty.json b/adapters/teads/teadstest/supplemental/renderer-version-empty.json new file mode 100644 index 00000000000..cb1a47d6fbe --- /dev/null +++ b/adapters/teads/teadstest/supplemental/renderer-version-empty.json @@ -0,0 +1,143 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://psrv.teads.tv/prebid-server/bid-request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "tagid": "125", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3 + ], + "w": 940, + "h": 560 + }, + "ext": { + "kv": { + "placementId": 125 + } + } + } + ], + "ext": { + "prebid": { + "sdk": { + "renderers": [ + { + "name": "teads", + "version": "", + "data": { + "resize": true, + "sdkEngineVersion": "189" + } + } + ] + } + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "39312703-e970-4914-ae56-8e7d7d1fd16b", + "tagid": "125", + "seatbid": [ + { + "seat": "teads", + "bid": [ + { + "id": "695ac187-fb3f-4d1f-8d5d-099c5e4c4d28", + "impid": "b6321d41-3840-4cb3-baad-b6fc5b0c8553", + "price": 33, + "nurl": "https://localhost:8080/prebid-server/win-notice?data=base64&clearingPrice=${AUCTION_PRICE}", + "adm": "{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"ads\":[{\"settings\":{\"values\":{\"animations\":{\"expand\":0,\"collapse\":0.5},\"placementId\":2,\"adType\":\"video\",\"placementFormat\":\"inread\",\"allowedPlayer\":\"any\",\"pageId\":2},\"components\":{\"closeButton\":{\"display\":false,\"countdown\":0},\"credits\":{\"display\":false},\"soundButton\":{\"display\":true,\"countdown\":0,\"type\":\"equalizer\"},\"label\":{\"display\":false},\"slider\":{\"closeButtonDisplay\":false}},\"behaviors\":{\"smartPosition\":{\"top\":false,\"corner\":false,\"mustBypassWhitelist\":true},\"slider\":{\"enable\":false},\"friendly\":false,\"playerClick\":\"fullscreen\",\"soundStart\":{\"type\":\"mute\"},\"soundMute\":\"threshold\",\"soundOver\":\"over\",\"launch\":\"auto\",\"videoStart\":\"threshold\",\"videoPause\":\"threshold\",\"secure\":false}},\"type\":\"VastXml\",\"content\":\"Teads Technology\",\"scenario_id\":971105412,\"dsp_campaign_id\":\"1\",\"dsp_creative_id\":\"1\",\"insertion_id\":1,\"placement_id\":2,\"portfolio_item_id\":971104812}],\"wigoEnabled\":false,\"placementMetadata\":{\"2\":{\"adCallTrackingUrl\":\"https://localhost:18281/track?action=adCall&pid=2&pageId=2&auctid=39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6&vid=708ca808-ec55-4d97-ab81-9c4777e16058&hb_provider=prebid-server&hb_ad_unit_code=742d38c4-7994-4c2b-ac82-18d3a64ba3c7&env=thirdparty-inapp>c=1&gdpr_apply=false&gac=1&gap=1&ca=false&bsg=uncat&bsias=uncat&pfid=971104812&gid=1&brid=0&cid=1&rpm_reason=3&ut=1&p=5fwoPMJCquIB-txdmwQS0l79-hhHVnlTzyR9mmnBMtZRceP6-q31KzCfLpS8WTNaw_sXr-hkOFBxaxa-jyLblbVc&cts=1685971107773&cs=267268361555465193905\",\"auctionId\":\"39312703-e970-4914-ae56-8e7d7d1fd16b__b6321d41-3840-4cb3-baad-b6fc5b0c8553__c0f2e6ba-63d0-4e20-ab41-fe0822eb65a6\"}},\"viewerId\":\"708ca808-ec55-4d97-ab81-9c4777e16058\"}", + "adid": "1", + "adomain": [ + "teads.com" + ], + "cid": "1", + "crid": "1", + "cat": [ + "IAB1-6", + "IAB10-5" + ], + "ext": { + "prebid": { + "meta": { + "rendererName": "teads", + "rendererVersion": "", + "rendererData": { + "resize": true, + "sdkEngineVersion": "189" + } + } + } + } + } + ] + } + ] + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "RendererVersion should not be empty if present", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/kubient/kubienttest/supplemental/status_400.json b/adapters/teads/teadstest/supplemental/status-400.json similarity index 80% rename from adapters/kubient/kubienttest/supplemental/status_400.json rename to adapters/teads/teadstest/supplemental/status-400.json index 29438cc3b8b..98b1875402c 100644 --- a/adapters/kubient/kubienttest/supplemental/status_400.json +++ b/adapters/teads/teadstest/supplemental/status-400.json @@ -14,7 +14,7 @@ }, "ext": { "bidder": { - "zoneid": "102" + "placementId": 1 } } } @@ -24,23 +24,26 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://127.0.0.1:5000/bid", + "uri": "https://psrv.teads.tv/prebid-server/bid-request", "body": { "id": "test-request-id", "imp": [ { "id": "test-imp-id", + "tagid": "1", "banner": { "format": [ { "w": 300, "h": 250 } - ] + ], + "w": 300, + "h": 250 }, "ext": { - "bidder": { - "zoneid": "102" + "kv": { + "placementId": 1 } } } @@ -53,11 +56,10 @@ } } ], - "expectedMakeBidsErrors": [ { "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", "comparison": "literal" } ] -} +} \ No newline at end of file diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/bad_response.json b/adapters/teads/teadstest/supplemental/status-500.json similarity index 69% rename from adapters/nanointeractive/nanointeractivetest/supplemental/bad_response.json rename to adapters/teads/teadstest/supplemental/status-500.json index 587c952a042..4db6eed0ec8 100644 --- a/adapters/nanointeractive/nanointeractivetest/supplemental/bad_response.json +++ b/adapters/teads/teadstest/supplemental/status-500.json @@ -14,7 +14,7 @@ }, "ext": { "bidder": { - "pid": "213" + "placementId": 1 } } } @@ -24,23 +24,26 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://ad.audiencemanager.de/hbs", + "uri": "https://psrv.teads.tv/prebid-server/bid-request", "body": { "id": "test-request-id", "imp": [ { "id": "test-imp-id", + "tagid": "1", "banner": { "format": [ { "w": 300, "h": 250 } - ] + ], + "w": 300, + "h": 250 }, "ext": { - "bidder": { - "pid": "213" + "kv": { + "placementId": 1 } } } @@ -48,16 +51,15 @@ } }, "mockResponse": { - "status": 200, - "body": "{\"id\"data.lost" + "status": 500, + "body": {} } } ], - "expectedMakeBidsErrors": [ { - "value": "bad server body response", + "value": "Unexpected status code: 500. Run with request.debug = 1 for more info", "comparison": "literal" } ] -} +} \ No newline at end of file diff --git a/adapters/telaria/params_test.go b/adapters/telaria/params_test.go index efa3fba1be9..9e451ca091e 100644 --- a/adapters/telaria/params_test.go +++ b/adapters/telaria/params_test.go @@ -2,8 +2,9 @@ package telaria import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/telaria/telaria.go b/adapters/telaria/telaria.go index bbe600178f4..ba2d10ae50a 100644 --- a/adapters/telaria/telaria.go +++ b/adapters/telaria/telaria.go @@ -6,11 +6,11 @@ import ( "net/http" "strconv" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const Endpoint = "https://ads.tremorhub.com/ad/rtb/prebid" diff --git a/adapters/telaria/telaria_test.go b/adapters/telaria/telaria_test.go index f8008835ac3..3c7d1bea46e 100644 --- a/adapters/telaria/telaria_test.go +++ b/adapters/telaria/telaria_test.go @@ -3,9 +3,9 @@ package telaria import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/tpmn/params_test.go b/adapters/tpmn/params_test.go index 7bd7c478638..4715d910855 100644 --- a/adapters/tpmn/params_test.go +++ b/adapters/tpmn/params_test.go @@ -2,8 +2,9 @@ package tpmn import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" ) var validParams = []string{ diff --git a/adapters/tpmn/tpmn.go b/adapters/tpmn/tpmn.go index 7afe94e5f79..68f4dc50a5d 100644 --- a/adapters/tpmn/tpmn.go +++ b/adapters/tpmn/tpmn.go @@ -6,10 +6,10 @@ import ( "net/http" "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // TpmnAdapter struct diff --git a/adapters/tpmn/tpmn_test.go b/adapters/tpmn/tpmn_test.go index 6fbd85936f1..7170dbb3d5f 100644 --- a/adapters/tpmn/tpmn_test.go +++ b/adapters/tpmn/tpmn_test.go @@ -3,9 +3,9 @@ package tpmn import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/trafficgate/params_test.go b/adapters/trafficgate/params_test.go index 4dc2c792bc9..adc11c08335 100644 --- a/adapters/trafficgate/params_test.go +++ b/adapters/trafficgate/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // TestValidParams makes sure that the trafficgate schema accepts all imp.ext fields which we intend to support. diff --git a/adapters/trafficgate/trafficgate.go b/adapters/trafficgate/trafficgate.go index 3c9ebe9ba98..b7ae0356a95 100644 --- a/adapters/trafficgate/trafficgate.go +++ b/adapters/trafficgate/trafficgate.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/trafficgate/trafficgate_test.go b/adapters/trafficgate/trafficgate_test.go index 326c50523fe..473c9d5d5c3 100644 --- a/adapters/trafficgate/trafficgate_test.go +++ b/adapters/trafficgate/trafficgate_test.go @@ -3,9 +3,9 @@ package trafficgate import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/triplelift/triplelift.go b/adapters/triplelift/triplelift.go index 0e7fbe4a462..080fe326362 100644 --- a/adapters/triplelift/triplelift.go +++ b/adapters/triplelift/triplelift.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type TripleliftAdapter struct { diff --git a/adapters/triplelift/triplelift_test.go b/adapters/triplelift/triplelift_test.go index add71b05788..c4468a93faa 100644 --- a/adapters/triplelift/triplelift_test.go +++ b/adapters/triplelift/triplelift_test.go @@ -3,9 +3,9 @@ package triplelift import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/triplelift_native/triplelift_native.go b/adapters/triplelift_native/triplelift_native.go index 9131c79a975..64934ca0869 100644 --- a/adapters/triplelift_native/triplelift_native.go +++ b/adapters/triplelift_native/triplelift_native.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type TripleliftNativeAdapter struct { diff --git a/adapters/triplelift_native/triplelift_native_test.go b/adapters/triplelift_native/triplelift_native_test.go index 18e157a41cd..c1c82501b32 100644 --- a/adapters/triplelift_native/triplelift_native_test.go +++ b/adapters/triplelift_native/triplelift_native_test.go @@ -3,9 +3,9 @@ package triplelift_native import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/ucfunnel/params_test.go b/adapters/ucfunnel/params_test.go index b721925e72a..9bba397a084 100644 --- a/adapters/ucfunnel/params_test.go +++ b/adapters/ucfunnel/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/ucfunnel.json diff --git a/adapters/ucfunnel/ucfunnel.go b/adapters/ucfunnel/ucfunnel.go index a0d86a0fa29..5ff65599b30 100644 --- a/adapters/ucfunnel/ucfunnel.go +++ b/adapters/ucfunnel/ucfunnel.go @@ -6,11 +6,11 @@ import ( "net/http" "net/url" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type UcfunnelAdapter struct { diff --git a/adapters/ucfunnel/ucfunnel_test.go b/adapters/ucfunnel/ucfunnel_test.go index a906b9279e8..cc81f4715e6 100644 --- a/adapters/ucfunnel/ucfunnel_test.go +++ b/adapters/ucfunnel/ucfunnel_test.go @@ -5,10 +5,10 @@ import ( "fmt" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestMakeRequests(t *testing.T) { diff --git a/adapters/undertone/params_test.go b/adapters/undertone/params_test.go index b48d08188d5..3144f757078 100644 --- a/adapters/undertone/params_test.go +++ b/adapters/undertone/params_test.go @@ -2,8 +2,9 @@ package undertone import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/undertone/undertone.go b/adapters/undertone/undertone.go index 7f8dde35abb..fa960aac976 100644 --- a/adapters/undertone/undertone.go +++ b/adapters/undertone/undertone.go @@ -6,12 +6,12 @@ import ( "net/http" "strconv" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const adapterId = 4 diff --git a/adapters/undertone/undertone_test.go b/adapters/undertone/undertone_test.go index d7e6d52339b..c08460e8627 100644 --- a/adapters/undertone/undertone_test.go +++ b/adapters/undertone/undertone_test.go @@ -1,10 +1,11 @@ package undertone import ( - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" "testing" + + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/unicorn/params_test.go b/adapters/unicorn/params_test.go index 9313183fbfa..fd76995d1c0 100644 --- a/adapters/unicorn/params_test.go +++ b/adapters/unicorn/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/unicorn/unicorn.go b/adapters/unicorn/unicorn.go index 5048d9d2394..fb9a6069319 100644 --- a/adapters/unicorn/unicorn.go +++ b/adapters/unicorn/unicorn.go @@ -7,11 +7,11 @@ import ( "net/http" "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/unicorn/unicorn_test.go b/adapters/unicorn/unicorn_test.go index 084be78498a..6c1e5aa73f2 100644 --- a/adapters/unicorn/unicorn_test.go +++ b/adapters/unicorn/unicorn_test.go @@ -3,9 +3,9 @@ package unicorn import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/unruly/params_test.go b/adapters/unruly/params_test.go index e9607358a59..f8feea872c8 100644 --- a/adapters/unruly/params_test.go +++ b/adapters/unruly/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/unruly/unruly.go b/adapters/unruly/unruly.go index 1f4bf6b0203..6a9c55cc561 100644 --- a/adapters/unruly/unruly.go +++ b/adapters/unruly/unruly.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/unruly/unruly_test.go b/adapters/unruly/unruly_test.go index b5d837abea5..8407ba15212 100644 --- a/adapters/unruly/unruly_test.go +++ b/adapters/unruly/unruly_test.go @@ -3,9 +3,9 @@ package unruly import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/vastbidder/bidder_macro.go b/adapters/vastbidder/bidder_macro.go index 3c585abd2b0..d2c69c83fc9 100644 --- a/adapters/vastbidder/bidder_macro.go +++ b/adapters/vastbidder/bidder_macro.go @@ -9,11 +9,11 @@ import ( "time" "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // BidderMacro default implementation @@ -265,7 +265,7 @@ func (tag *BidderMacro) MacroBlockedApp(key string) string { // MacroFD contains definition for FD Parameter func (tag *BidderMacro) MacroFD(key string) string { if nil != tag.Request.Source { - return strconv.Itoa(int(tag.Request.Source.FD)) + return strconv.Itoa(int(*tag.Request.Source.FD)) } return "" } @@ -417,16 +417,16 @@ func (tag *BidderMacro) MacroVideoProtocols(key string) string { // MacroVideoPlayerWidth contains definition for VideoPlayerWidth Parameter func (tag *BidderMacro) MacroVideoPlayerWidth(key string) string { - if nil != tag.Imp.Video && tag.Imp.Video.W > 0 { - return strconv.FormatInt(int64(tag.Imp.Video.W), intBase) + if nil != tag.Imp.Video && tag.Imp.Video.W != nil && *tag.Imp.Video.W > 0 { + return strconv.FormatInt(int64(*tag.Imp.Video.W), intBase) } return "" } // MacroVideoPlayerHeight contains definition for VideoPlayerHeight Parameter func (tag *BidderMacro) MacroVideoPlayerHeight(key string) string { - if nil != tag.Imp.Video && tag.Imp.Video.H > 0 { - return strconv.FormatInt(int64(tag.Imp.Video.H), intBase) + if nil != tag.Imp.Video && tag.Imp.Video.H != nil && *tag.Imp.Video.H > 0 { + return strconv.FormatInt(int64(*tag.Imp.Video.H), intBase) } return "" } @@ -524,8 +524,8 @@ func (tag *BidderMacro) MacroVideoMaximumBitRate(key string) string { // MacroVideoBoxing contains definition for VideoBoxing Parameter func (tag *BidderMacro) MacroVideoBoxing(key string) string { - if nil != tag.Imp.Video && tag.Imp.Video.BoxingAllowed > 0 { - return strconv.FormatInt(int64(tag.Imp.Video.BoxingAllowed), intBase) + if nil != tag.Imp.Video && tag.Imp.Video.BoxingAllowed != nil && *tag.Imp.Video.BoxingAllowed > 0 { + return strconv.FormatInt(int64(*tag.Imp.Video.BoxingAllowed), intBase) } return "" } @@ -615,8 +615,8 @@ func (tag *BidderMacro) MacroSiteSearch(key string) string { // MacroSiteMobile contains definition for SiteMobile Parameter func (tag *BidderMacro) MacroSiteMobile(key string) string { - if !tag.IsApp && tag.Request.Site.Mobile > 0 { - return strconv.FormatInt(int64(tag.Request.Site.Mobile), intBase) + if !tag.IsApp && tag.Request.Site.Mobile != nil && *tag.Request.Site.Mobile > 0 { + return strconv.FormatInt(int64(*tag.Request.Site.Mobile), intBase) } return "" } @@ -665,8 +665,8 @@ func (tag *BidderMacro) MacroAppVersion(key string) string { // MacroAppPaid contains definition for AppPaid Parameter func (tag *BidderMacro) MacroAppPaid(key string) string { - if tag.IsApp && tag.Request.App.Paid != 0 { - return strconv.FormatInt(int64(tag.Request.App.Paid), intBase) + if tag.IsApp && tag.Request.App.Paid != nil && *tag.Request.App.Paid != 0 { + return strconv.FormatInt(int64(*tag.Request.App.Paid), intBase) } return "" } @@ -708,10 +708,10 @@ func (tag *BidderMacro) MacroPageCategory(key string) string { // MacroPrivacyPolicy contains definition for PrivacyPolicy Parameter func (tag *BidderMacro) MacroPrivacyPolicy(key string) string { var value int8 = 0 - if tag.IsApp { - value = tag.Request.App.PrivacyPolicy - } else { - value = tag.Request.Site.PrivacyPolicy + if tag.IsApp && tag.Request.App.PrivacyPolicy != nil { + value = *tag.Request.App.PrivacyPolicy + } else if tag.Request.Site != nil && tag.Request.Site.PrivacyPolicy != nil { + value = *tag.Request.Site.PrivacyPolicy } if value > 0 { return strconv.FormatInt(int64(value), intBase) @@ -902,7 +902,7 @@ func (tag *BidderMacro) MacroContentKeywords(key string) string { // MacroContentLiveStream contains definition for ContentLiveStream Parameter func (tag *BidderMacro) MacroContentLiveStream(key string) string { if nil != tag.Content { - return strconv.FormatInt(int64(tag.Content.LiveStream), intBase) + return strconv.FormatInt(int64(*tag.Content.LiveStream), intBase) } return "" } @@ -910,7 +910,7 @@ func (tag *BidderMacro) MacroContentLiveStream(key string) string { // MacroContentSourceRelationship contains definition for ContentSourceRelationship Parameter func (tag *BidderMacro) MacroContentSourceRelationship(key string) string { if nil != tag.Content { - return strconv.FormatInt(int64(tag.Content.SourceRelationship), intBase) + return strconv.FormatInt(int64(*tag.Content.SourceRelationship), intBase) } return "" } @@ -934,7 +934,7 @@ func (tag *BidderMacro) MacroContentLanguage(key string) string { // MacroContentEmbeddable contains definition for ContentEmbeddable Parameter func (tag *BidderMacro) MacroContentEmbeddable(key string) string { if nil != tag.Content { - return strconv.FormatInt(int64(tag.Content.Embeddable), intBase) + return strconv.FormatInt(int64(*tag.Content.Embeddable), intBase) } return "" } @@ -1054,7 +1054,7 @@ func (tag *BidderMacro) MacroDeviceHeight(key string) string { // MacroDeviceJS contains definition for DeviceJS Parameter func (tag *BidderMacro) MacroDeviceJS(key string) string { if nil != tag.Request.Device { - return strconv.FormatInt(int64(tag.Request.Device.JS), intBase) + return strconv.FormatInt(int64(*tag.Request.Device.JS), intBase) } return "" } @@ -1136,7 +1136,7 @@ func (tag *BidderMacro) MacroDeviceMACMD5(key string) string { // MacroLatitude contains definition for Latitude Parameter func (tag *BidderMacro) MacroLatitude(key string) string { if tag.HasGeo { - return fmt.Sprintf("%g", tag.Request.Device.Geo.Lat) + return fmt.Sprintf("%g", *tag.Request.Device.Geo.Lat) } return "" } @@ -1144,7 +1144,7 @@ func (tag *BidderMacro) MacroLatitude(key string) string { // MacroLongitude contains definition for Longitude Parameter func (tag *BidderMacro) MacroLongitude(key string) string { if tag.HasGeo { - return fmt.Sprintf("%g", tag.Request.Device.Geo.Lon) + return fmt.Sprintf("%g", *tag.Request.Device.Geo.Lon) } return "" } diff --git a/adapters/vastbidder/bidder_macro_test.go b/adapters/vastbidder/bidder_macro_test.go index ddf2493e22e..9ecf4560f39 100644 --- a/adapters/vastbidder/bidder_macro_test.go +++ b/adapters/vastbidder/bidder_macro_test.go @@ -5,9 +5,10 @@ import ( "net/http" "testing" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -660,7 +661,7 @@ func TestBidderMacro_MacroTest(t *testing.T) { BAdv: []string{`badv-1`, `badv-2`}, BApp: []string{`bapp-1`, `bapp-2`}, Source: &openrtb2.Source{ - FD: 1, + FD: ptrutil.ToPtr[int8](1), TID: `source-tid`, PChain: `source-pchain`, }, @@ -704,8 +705,8 @@ func TestBidderMacro_MacroTest(t *testing.T) { MaxDuration: 60, Protocols: []adcom1.MediaCreativeSubtype{adcom1.CreativeVAST30, adcom1.CreativeVAST40Wrapper}, Protocol: adcom1.CreativeVAST40Wrapper, - W: 640, - H: 480, + W: ptrutil.ToPtr[int64](640), + H: ptrutil.ToPtr[int64](480), StartDelay: new(adcom1.StartDelay), Placement: adcom1.VideoPlacementInStream, Linearity: adcom1.LinearityLinear, @@ -717,7 +718,7 @@ func TestBidderMacro_MacroTest(t *testing.T) { MaxExtended: 10, MinBitRate: 360, MaxBitRate: 1080, - BoxingAllowed: 1, + BoxingAllowed: ptrutil.ToPtr[int8](1), PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackPageLoadSoundOn, adcom1.PlaybackClickSoundOn}, PlaybackEnd: adcom1.PlaybackCompletion, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryStreaming, adcom1.DeliveryDownload}, @@ -736,8 +737,8 @@ func TestBidderMacro_MacroTest(t *testing.T) { Page: `site-page-url`, Ref: `site-referer-url`, Search: `site-search-keywords`, - Mobile: 1, - PrivacyPolicy: 2, + Mobile: ptrutil.ToPtr[int8](1), + PrivacyPolicy: ptrutil.ToPtr[int8](2), Keywords: `site-keywords`, Publisher: &openrtb2.Publisher{ ID: `site-pub-id`, @@ -763,11 +764,11 @@ func TestBidderMacro_MacroTest(t *testing.T) { UserRating: `2.2`, QAGMediaRating: adcom1.MediaRatingAll, Keywords: `site-cnt-keywords`, - LiveStream: 1, - SourceRelationship: 1, + LiveStream: ptrutil.ToPtr[int8](1), + SourceRelationship: ptrutil.ToPtr[int8](1), Len: 100, Language: `english`, - Embeddable: 1, + Embeddable: ptrutil.ToPtr[int8](1), Producer: &openrtb2.Producer{ ID: `site-cnt-prod-id`, Name: `site-cnt-prod-name`, @@ -787,7 +788,7 @@ func TestBidderMacro_MacroTest(t *testing.T) { OSV: `os-version`, H: 1024, W: 2048, - JS: 1, + JS: ptrutil.ToPtr[int8](1), Language: `device-lang`, ConnectionType: new(adcom1.ConnectionType), IFA: `ifa`, @@ -798,8 +799,8 @@ func TestBidderMacro_MacroTest(t *testing.T) { MACSHA1: `macsha1`, MACMD5: `macmd5`, Geo: &openrtb2.Geo{ - Lat: 1.1, - Lon: 2.2, + Lat: ptrutil.ToPtr[float64](1.1), + Lon: ptrutil.ToPtr[float64](2.2), Country: `country`, Region: `region`, City: `city`, @@ -959,7 +960,7 @@ func TestBidderMacro_MacroTest(t *testing.T) { BAdv: []string{`badv-1`, `badv-2`}, BApp: []string{`bapp-1`, `bapp-2`}, Source: &openrtb2.Source{ - FD: 1, + FD: ptrutil.ToPtr[int8](1), TID: `source-tid`, PChain: `source-pchain`, }, @@ -1003,8 +1004,8 @@ func TestBidderMacro_MacroTest(t *testing.T) { MaxDuration: 60, Protocols: []adcom1.MediaCreativeSubtype{adcom1.CreativeVAST30, adcom1.CreativeVAST40Wrapper}, Protocol: adcom1.CreativeVAST40Wrapper, - W: 640, - H: 480, + W: ptrutil.ToPtr[int64](640), + H: ptrutil.ToPtr[int64](480), StartDelay: new(adcom1.StartDelay), Placement: adcom1.VideoPlacementInStream, Linearity: adcom1.LinearityLinear, @@ -1016,7 +1017,7 @@ func TestBidderMacro_MacroTest(t *testing.T) { MaxExtended: 10, MinBitRate: 360, MaxBitRate: 1080, - BoxingAllowed: 1, + BoxingAllowed: ptrutil.ToPtr[int8](1), PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackPageLoadSoundOn, adcom1.PlaybackClickSoundOn}, PlaybackEnd: adcom1.PlaybackCompletion, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryStreaming, adcom1.DeliveryDownload}, @@ -1030,13 +1031,13 @@ func TestBidderMacro_MacroTest(t *testing.T) { Bundle: `app-bundle`, StoreURL: `app-store-url`, Ver: `app-version`, - Paid: 1, + Paid: ptrutil.ToPtr[int8](1), Name: `app-name`, Domain: `app-domain`, Cat: []string{`app-cat1`, `app-cat2`}, SectionCat: []string{`app-sec-cat1`, `app-sec-cat2`}, PageCat: []string{`app-page-cat1`, `app-page-cat2`}, - PrivacyPolicy: 2, + PrivacyPolicy: ptrutil.ToPtr[int8](2), Keywords: `app-keywords`, Publisher: &openrtb2.Publisher{ ID: `app-pub-id`, @@ -1062,11 +1063,11 @@ func TestBidderMacro_MacroTest(t *testing.T) { UserRating: `2.2`, QAGMediaRating: adcom1.MediaRatingAll, Keywords: `app-cnt-keywords`, - LiveStream: 1, - SourceRelationship: 1, + LiveStream: ptrutil.ToPtr[int8](1), + SourceRelationship: ptrutil.ToPtr[int8](1), Len: 100, Language: `english`, - Embeddable: 1, + Embeddable: ptrutil.ToPtr[int8](1), Producer: &openrtb2.Producer{ ID: `app-cnt-prod-id`, Name: `app-cnt-prod-name`, @@ -1085,7 +1086,7 @@ func TestBidderMacro_MacroTest(t *testing.T) { OSV: `os-version`, H: 1024, W: 2048, - JS: 1, + JS: ptrutil.ToPtr[int8](1), Language: `device-lang`, ConnectionType: new(adcom1.ConnectionType), IFA: `ifa`, @@ -1096,8 +1097,8 @@ func TestBidderMacro_MacroTest(t *testing.T) { MACSHA1: `macsha1`, MACMD5: `macmd5`, Geo: &openrtb2.Geo{ - Lat: 1.1, - Lon: 2.2, + Lat: ptrutil.ToPtr[float64](1.1), + Lon: ptrutil.ToPtr[float64](2.2), Country: `country`, Region: `region`, City: `city`, diff --git a/adapters/vastbidder/ibidder_macro.go b/adapters/vastbidder/ibidder_macro.go index 7ffbc721282..5b5c9b55511 100644 --- a/adapters/vastbidder/ibidder_macro.go +++ b/adapters/vastbidder/ibidder_macro.go @@ -3,9 +3,9 @@ package vastbidder import ( "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // IBidderMacro interface will capture all macro definition diff --git a/adapters/vastbidder/itag_response_handler.go b/adapters/vastbidder/itag_response_handler.go index 2e50b92453c..af8e5df9fc3 100644 --- a/adapters/vastbidder/itag_response_handler.go +++ b/adapters/vastbidder/itag_response_handler.go @@ -3,8 +3,8 @@ package vastbidder import ( "errors" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" ) // ITagRequestHandler parse bidder request diff --git a/adapters/vastbidder/macro_processor_test.go b/adapters/vastbidder/macro_processor_test.go index d4f77f43f8a..c8f9d69bc57 100644 --- a/adapters/vastbidder/macro_processor_test.go +++ b/adapters/vastbidder/macro_processor_test.go @@ -3,7 +3,7 @@ package vastbidder import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" ) diff --git a/adapters/vastbidder/sample_spotx_macro.go.bak b/adapters/vastbidder/sample_spotx_macro.go.bak index 8f3aafbdcc7..6c04861cf40 100644 --- a/adapters/vastbidder/sample_spotx_macro.go.bak +++ b/adapters/vastbidder/sample_spotx_macro.go.bak @@ -1,7 +1,7 @@ package vastbidder import ( - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) //SpotxMacro default implementation diff --git a/adapters/vastbidder/tagbidder.go b/adapters/vastbidder/tagbidder.go index bf3f4af1bc0..195349d5182 100644 --- a/adapters/vastbidder/tagbidder.go +++ b/adapters/vastbidder/tagbidder.go @@ -1,10 +1,10 @@ package vastbidder import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // TagBidder is default implementation of ITagBidder diff --git a/adapters/vastbidder/tagbidder_test.go b/adapters/vastbidder/tagbidder_test.go index 253477eedea..35f1d02a263 100644 --- a/adapters/vastbidder/tagbidder_test.go +++ b/adapters/vastbidder/tagbidder_test.go @@ -4,10 +4,10 @@ import ( "net/http" "testing" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/vastbidder/util.go b/adapters/vastbidder/util.go index 59877913a96..6cff6167ed3 100644 --- a/adapters/vastbidder/util.go +++ b/adapters/vastbidder/util.go @@ -10,8 +10,8 @@ import ( "strconv" "strings" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func ObjectArrayToString(len int, separator string, cb func(i int) string) string { diff --git a/adapters/vastbidder/vast_tag_response_handler.go b/adapters/vastbidder/vast_tag_response_handler.go index 38b149a919e..0eea1c39722 100644 --- a/adapters/vastbidder/vast_tag_response_handler.go +++ b/adapters/vastbidder/vast_tag_response_handler.go @@ -11,10 +11,10 @@ import ( "github.com/beevik/etree" "github.com/golang/glog" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) var durationRegExp = regexp.MustCompile(`^([01]?\d|2[0-3]):([0-5]?\d):([0-5]?\d)(\.(\d{1,3}))?$`) diff --git a/adapters/vastbidder/vast_tag_response_handler_test.go b/adapters/vastbidder/vast_tag_response_handler_test.go index 59cbae2eae2..b539719b7af 100644 --- a/adapters/vastbidder/vast_tag_response_handler_test.go +++ b/adapters/vastbidder/vast_tag_response_handler_test.go @@ -8,9 +8,9 @@ import ( "testing" "github.com/beevik/etree" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/videobyte/params_test.go b/adapters/videobyte/params_test.go index b638d4585c6..dbc815fd76d 100644 --- a/adapters/videobyte/params_test.go +++ b/adapters/videobyte/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/videobyte.json diff --git a/adapters/videobyte/videobyte.go b/adapters/videobyte/videobyte.go index 2dc6df84895..7a4bebc3a41 100644 --- a/adapters/videobyte/videobyte.go +++ b/adapters/videobyte/videobyte.go @@ -6,12 +6,12 @@ import ( "net/http" "net/url" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" ) type adapter struct { diff --git a/adapters/videobyte/videobyte_test.go b/adapters/videobyte/videobyte_test.go index d4dda0606f8..9e566a20ef2 100644 --- a/adapters/videobyte/videobyte_test.go +++ b/adapters/videobyte/videobyte_test.go @@ -3,9 +3,9 @@ package videobyte import ( "testing" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" - "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/videoheroes/params_test.go b/adapters/videoheroes/params_test.go index d79f83245a4..66e1e6a2788 100644 --- a/adapters/videoheroes/params_test.go +++ b/adapters/videoheroes/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/videoheroes/videoheroes.go b/adapters/videoheroes/videoheroes.go index d4efcab2c90..8999d4d0736 100755 --- a/adapters/videoheroes/videoheroes.go +++ b/adapters/videoheroes/videoheroes.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/videoheroes/videoheroes_test.go b/adapters/videoheroes/videoheroes_test.go index ac60d56e175..7c0b2268f51 100644 --- a/adapters/videoheroes/videoheroes_test.go +++ b/adapters/videoheroes/videoheroes_test.go @@ -3,9 +3,9 @@ package videoheroes import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/vidoomy/params_test.go b/adapters/vidoomy/params_test.go index 63ffb462c19..40c17029f9e 100644 --- a/adapters/vidoomy/params_test.go +++ b/adapters/vidoomy/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/vidoomy.json diff --git a/adapters/vidoomy/vidoomy.go b/adapters/vidoomy/vidoomy.go index 7e7e9d64eb3..f96d577d719 100644 --- a/adapters/vidoomy/vidoomy.go +++ b/adapters/vidoomy/vidoomy.go @@ -5,12 +5,13 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) type adapter struct { @@ -97,8 +98,8 @@ func changeRequestForBidService(request *openrtb2.BidRequest) error { return fmt.Errorf("no sizes provided for Banner %v", banner.Format) } - banner.W = openrtb2.Int64Ptr(banner.Format[0].W) - banner.H = openrtb2.Int64Ptr(banner.Format[0].H) + banner.W = ptrutil.ToPtr(banner.Format[0].W) + banner.H = ptrutil.ToPtr(banner.Format[0].H) return nil } diff --git a/adapters/vidoomy/vidoomy_test.go b/adapters/vidoomy/vidoomy_test.go index 60cd2c9d967..7acc477ae1c 100644 --- a/adapters/vidoomy/vidoomy_test.go +++ b/adapters/vidoomy/vidoomy_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestVidoomyBidderEndpointConfig(t *testing.T) { diff --git a/adapters/visiblemeasures/params_test.go b/adapters/visiblemeasures/params_test.go index 7bcc1cf60cf..ed74ef1ad35 100644 --- a/adapters/visiblemeasures/params_test.go +++ b/adapters/visiblemeasures/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/visiblemeasures/visiblemeasures.go b/adapters/visiblemeasures/visiblemeasures.go index 3d6a96640e9..4973d33d377 100644 --- a/adapters/visiblemeasures/visiblemeasures.go +++ b/adapters/visiblemeasures/visiblemeasures.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/visiblemeasures/visiblemeasures_test.go b/adapters/visiblemeasures/visiblemeasures_test.go index 8c1759c010e..8970ccb1e43 100644 --- a/adapters/visiblemeasures/visiblemeasures_test.go +++ b/adapters/visiblemeasures/visiblemeasures_test.go @@ -3,9 +3,9 @@ package visiblemeasures import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/visx/params_test.go b/adapters/visx/params_test.go index e53d2cda007..0646d221e27 100644 --- a/adapters/visx/params_test.go +++ b/adapters/visx/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/visx/visx.go b/adapters/visx/visx.go index 713c2693990..aa1707cfff2 100644 --- a/adapters/visx/visx.go +++ b/adapters/visx/visx.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type VisxAdapter struct { diff --git a/adapters/visx/visx_test.go b/adapters/visx/visx_test.go index 5fc58a1f83d..8cdccc2e653 100644 --- a/adapters/visx/visx_test.go +++ b/adapters/visx/visx_test.go @@ -3,9 +3,9 @@ package visx import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/vox/params_test.go b/adapters/vox/params_test.go index be148d3b32d..e23a57d9b30 100644 --- a/adapters/vox/params_test.go +++ b/adapters/vox/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/vox/vox.go b/adapters/vox/vox.go index 0b56fcbf9d7..73e60b31191 100644 --- a/adapters/vox/vox.go +++ b/adapters/vox/vox.go @@ -3,10 +3,11 @@ package vox import ( "encoding/json" "fmt" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/vox/vox_test.go b/adapters/vox/vox_test.go index 95d11a8ad79..dfb345b2e02 100644 --- a/adapters/vox/vox_test.go +++ b/adapters/vox/vox_test.go @@ -3,9 +3,9 @@ package vox import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/vrtcal/params_test.go b/adapters/vrtcal/params_test.go index d45d3b39013..0e30dd6fcc9 100644 --- a/adapters/vrtcal/params_test.go +++ b/adapters/vrtcal/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) //Vrtcal doesn't currently require any custom fields. This file is included for conformity only diff --git a/adapters/vrtcal/vrtcal.go b/adapters/vrtcal/vrtcal.go index ab47eddb441..f97d555f363 100644 --- a/adapters/vrtcal/vrtcal.go +++ b/adapters/vrtcal/vrtcal.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type VrtcalAdapter struct { diff --git a/adapters/vrtcal/vrtcal_test.go b/adapters/vrtcal/vrtcal_test.go index 31e6c78e2c1..a4ba917922f 100644 --- a/adapters/vrtcal/vrtcal_test.go +++ b/adapters/vrtcal/vrtcal_test.go @@ -3,9 +3,9 @@ package vrtcal import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/xeworks/params_test.go b/adapters/xeworks/params_test.go index 68d36096049..1c14b3a0989 100644 --- a/adapters/xeworks/params_test.go +++ b/adapters/xeworks/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) var validParams = []string{ diff --git a/adapters/xeworks/xeworks.go b/adapters/xeworks/xeworks.go index 35e551b1034..ab1d68ca543 100644 --- a/adapters/xeworks/xeworks.go +++ b/adapters/xeworks/xeworks.go @@ -6,12 +6,12 @@ import ( "net/http" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type bidType struct { diff --git a/adapters/xeworks/xeworks_test.go b/adapters/xeworks/xeworks_test.go index 4869a05a229..db7e26c9bae 100644 --- a/adapters/xeworks/xeworks_test.go +++ b/adapters/xeworks/xeworks_test.go @@ -3,9 +3,9 @@ package xeworks import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/yahooAds/params_test.go b/adapters/yahooAds/params_test.go index c0deaaa32c9..dbbc2c84adb 100644 --- a/adapters/yahooAds/params_test.go +++ b/adapters/yahooAds/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/yahooAds.json diff --git a/adapters/yahooAds/yahooAds.go b/adapters/yahooAds/yahooAds.go index 3597d0e359c..e99cf2e9f74 100644 --- a/adapters/yahooAds/yahooAds.go +++ b/adapters/yahooAds/yahooAds.go @@ -5,11 +5,12 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) type adapter struct { @@ -212,8 +213,8 @@ func validateBanner(banner *openrtb2.Banner) error { return fmt.Errorf("No sizes provided for Banner %v", banner.Format) } - banner.W = openrtb2.Int64Ptr(banner.Format[0].W) - banner.H = openrtb2.Int64Ptr(banner.Format[0].H) + banner.W = ptrutil.ToPtr(banner.Format[0].W) + banner.H = ptrutil.ToPtr(banner.Format[0].H) return nil } diff --git a/adapters/yahooAds/yahooAds_test.go b/adapters/yahooAds/yahooAds_test.go index 924eabd5ec1..ae9103d141e 100644 --- a/adapters/yahooAds/yahooAds_test.go +++ b/adapters/yahooAds/yahooAds_test.go @@ -5,9 +5,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestYahooAdsBidderEndpointConfig(t *testing.T) { diff --git a/adapters/yandex/params_test.go b/adapters/yandex/params_test.go new file mode 100644 index 00000000000..e65c82ff159 --- /dev/null +++ b/adapters/yandex/params_test.go @@ -0,0 +1,87 @@ +package yandex + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json schema. %v", err) + } + + for _, p := range validParams { + if err := validator.Validate(openrtb_ext.BidderYandex, json.RawMessage(p)); err != nil { + t.Errorf("Schema rejected valid params: %s", p) + } + } +} + +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json schema. %v", err) + } + + for _, p := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderYandex, json.RawMessage(p)); err == nil { + t.Errorf("Schema allowed invalid params: %s", p) + } + } +} + +var validParams = []string{ + `{"page_id": 123123, "imp_id": 123}`, + `{"placement_id": "A-123123-123"}`, + `{"placement_id": "B-A-123123-123"}`, + `{"placement_id": "123123-123"}`, +} + +var invalidParams = []string{ + `{"pageId": 123123, "impId": 123}`, + `{"page_id": 0, "imp_id": 0}`, + `{"page_id": "123123", "imp_id": "123"}`, + `{"page_id": "123123", "imp_id": "123", "placement_id": "123123"}`, + `{"page_id": "123123"}`, + `{"imp_id": "123"}`, + `{"placement_id": 123123}`, + `{"placement_id": "123123"}`, + `{"placement_id": "A-123123"}`, + `{"placement_id": "B-A-123123"}`, + `{}`, +} + +func TestValidPlacementIdMapper(t *testing.T) { + for ext, expectedPlacementId := range validPlacementIds { + val, err := mapExtToPlacementID(ext) + + assert.Equal(t, &expectedPlacementId, val) + assert.NoError(t, err) + } +} + +func TestInvalidPlacementIdMapper(t *testing.T) { + for _, ext := range invalidPlacementIds { + _, err := mapExtToPlacementID(ext) + + assert.Error(t, err) + } +} + +var validPlacementIds = map[openrtb_ext.ExtImpYandex]yandexPlacementID{ + {PlacementID: "A-12345-1"}: {PageID: "12345", ImpID: "1"}, + {PlacementID: "B-A-123123-123"}: {PageID: "123123", ImpID: "123"}, + {PlacementID: "111-222"}: {PageID: "111", ImpID: "222"}, + {PageID: 111, ImpID: 222}: {PageID: "111", ImpID: "222"}, +} + +var invalidPlacementIds = []openrtb_ext.ExtImpYandex{ + {PlacementID: "123123"}, + {PlacementID: "A-123123"}, + {PlacementID: "B-A-123123"}, + {PlacementID: "C-B-A-123123"}, +} diff --git a/adapters/yandex/yandex.go b/adapters/yandex/yandex.go new file mode 100644 index 00000000000..6d5dae82224 --- /dev/null +++ b/adapters/yandex/yandex.go @@ -0,0 +1,339 @@ +package yandex + +import ( + "encoding/json" + "fmt" + "net/http" + "net/url" + "strconv" + "strings" + "text/template" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +const ( + refererQueryKey = "target-ref" + currencyQueryKey = "ssp-cur" + impIdQueryKey = "imp-id" +) + +// Composite id of an ad placement +type yandexPlacementID struct { + PageID string + ImpID string +} + +type adapter struct { + endpoint *template.Template +} + +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + template, err := template.New("endpointTemplate").Parse(config.Endpoint) + if err != nil { + return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) + } + + bidder := &adapter{ + endpoint: template, + } + + return bidder, nil +} + +func (a *adapter) MakeRequests(requestData *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var ( + requests []*adapters.RequestData + errors []error + ) + + referer := getReferer(requestData) + currency := getCurrency(requestData) + + for i := range requestData.Imp { + imp := requestData.Imp[i] + + placementId, err := getYandexPlacementId(imp) + if err != nil { + errors = append(errors, err) + continue + } + + if err := modifyImp(&imp); err != nil { + errors = append(errors, err) + continue + } + + resolvedUrl, err := a.resolveUrl(*placementId, referer, currency) + if err != nil { + errors = append(errors, err) + continue + } + + splittedRequestData := splitRequestDataByImp(requestData, imp) + + requestBody, err := json.Marshal(splittedRequestData) + if err != nil { + errors = append(errors, err) + continue + } + + requests = append(requests, &adapters.RequestData{ + Method: "POST", + Uri: resolvedUrl, + Body: requestBody, + Headers: getHeaders(&splittedRequestData), + }) + } + + return requests, errors +} + +func getHeaders(request *openrtb2.BidRequest) http.Header { + headers := http.Header{} + + if request.Device != nil && request.Site != nil { + addNonEmptyHeaders(&headers, map[string]string{ + "Referer": request.Site.Page, + "Accept-Language": request.Device.Language, + "User-Agent": request.Device.UA, + "X-Forwarded-For": request.Device.IP, + "X-Real-Ip": request.Device.IP, + "Content-Type": "application/json;charset=utf-8", + "Accept": "application/json", + }) + } + + return headers +} + +func addNonEmptyHeaders(headers *http.Header, headerValues map[string]string) { + for key, value := range headerValues { + if len(value) > 0 { + headers.Add(key, value) + } + } +} + +// Request is in shared memory, so we have to make a shallow copy for further modification (imp is already a shallow copy) +func splitRequestDataByImp(request *openrtb2.BidRequest, imp openrtb2.Imp) openrtb2.BidRequest { + requestCopy := *request + requestCopy.Imp = []openrtb2.Imp{imp} + + return requestCopy +} + +func getYandexPlacementId(imp openrtb2.Imp) (*yandexPlacementID, error) { + var ext adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &ext); err != nil { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("imp %s: unable to unmarshal ext", imp.ID), + } + } + + var yandexExt openrtb_ext.ExtImpYandex + if err := json.Unmarshal(ext.Bidder, &yandexExt); err != nil { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("imp %s: unable to unmarshal ext.bidder: %v", imp.ID, err), + } + } + + placementID, err := mapExtToPlacementID(yandexExt) + if err != nil { + return nil, err + } + + return placementID, nil +} + +func mapExtToPlacementID(yandexExt openrtb_ext.ExtImpYandex) (*yandexPlacementID, error) { + var placementID yandexPlacementID + + if len(yandexExt.PlacementID) == 0 { + placementID.ImpID = strconv.Itoa(int(yandexExt.ImpID)) + placementID.PageID = strconv.Itoa(int(yandexExt.PageID)) + return &placementID, nil + } + + idParts := strings.Split(yandexExt.PlacementID, "-") + + numericIdParts := []string{} + + for _, idPart := range idParts { + if _, err := strconv.Atoi(idPart); err == nil { + numericIdParts = append(numericIdParts, idPart) + } + } + + if len(numericIdParts) < 2 { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("invalid placement id, it must contain two parts: %s", yandexExt.PlacementID), + } + } + + placementID.ImpID = numericIdParts[len(numericIdParts)-1] + placementID.PageID = numericIdParts[len(numericIdParts)-2] + + return &placementID, nil +} + +func modifyImp(imp *openrtb2.Imp) error { + if imp.Banner != nil { + banner, err := modifyBanner(*imp.Banner) + if banner != nil { + imp.Banner = banner + } + return err + } + + if imp.Native != nil { + return nil + } + + return &errortypes.BadInput{ + Message: fmt.Sprintf("Unsupported format. Yandex only supports banner and native types. Ignoring imp id #%s", imp.ID), + } +} + +func modifyBanner(banner openrtb2.Banner) (*openrtb2.Banner, error) { + format := banner.Format + + if banner.W == nil || banner.H == nil || *banner.W == 0 || *banner.H == 0 { + if len(format) == 0 { + return nil, &errortypes.BadInput{ + Message: "Invalid size provided for Banner", + } + } + + firstFormat := format[0] + banner.H = &firstFormat.H + banner.W = &firstFormat.W + } + + return &banner, nil +} + +// "Un-templates" the endpoint by replacing macroses and adding the required query parameters +func (a *adapter) resolveUrl(placementID yandexPlacementID, referer string, currency string) (string, error) { + params := macros.EndpointTemplateParams{PageID: placementID.PageID} + + endpointStr, err := macros.ResolveMacros(a.endpoint, params) + if err != nil { + return "", err + } + + parsedUrl, err := url.Parse(endpointStr) + if err != nil { + return "", err + } + + addNonEmptyQueryParams(parsedUrl, map[string]string{ + refererQueryKey: referer, + currencyQueryKey: currency, + impIdQueryKey: placementID.ImpID, + }) + + return parsedUrl.String(), nil +} + +func addNonEmptyQueryParams(url *url.URL, queryMap map[string]string) { + query := url.Query() + for key, value := range queryMap { + if len(value) > 0 { + query.Add(key, value) + } + } + + url.RawQuery = query.Encode() +} + +func getReferer(request *openrtb2.BidRequest) string { + if request.Site == nil { + return "" + } + + return request.Site.Domain +} + +func getCurrency(request *openrtb2.BidRequest) string { + if len(request.Cur) == 0 { + return "" + } + + return request.Cur[0] +} + +func (a *adapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { + + if adapters.IsResponseStatusCodeNoContent(responseData) { + return nil, nil + } + + if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil { + return nil, []error{err} + } + + var bidResponse openrtb2.BidResponse + if err := json.Unmarshal(responseData.Body, &bidResponse); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Bad server response: %d", err), + }} + } + + bidResponseWithCapacity := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) + + var errors []error + + impMap := map[string]*openrtb2.Imp{} + for i := range request.Imp { + imp := request.Imp[i] + + impMap[imp.ID] = &imp + } + + for _, seatBid := range bidResponse.SeatBid { + for i := range seatBid.Bid { + bid := seatBid.Bid[i] + + imp, exists := impMap[bid.ImpID] + if !exists { + errors = append(errors, &errortypes.BadInput{ + Message: fmt.Sprintf("Invalid bid imp ID #%s does not match any imp IDs from the original bid request", bid.ImpID), + }) + continue + } + + bidType, err := getBidType(*imp) + if err != nil { + errors = append(errors, err) + continue + } + + bidResponseWithCapacity.Bids = append(bidResponseWithCapacity.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: bidType, + }) + } + } + + return bidResponseWithCapacity, errors +} + +func getBidType(imp openrtb2.Imp) (openrtb_ext.BidType, error) { + if imp.Native != nil { + return openrtb_ext.BidTypeNative, nil + } + + if imp.Banner != nil { + return openrtb_ext.BidTypeBanner, nil + } + + return "", &errortypes.BadInput{ + Message: fmt.Sprintf("Processing an invalid impression; cannot resolve impression type for imp #%s", imp.ID), + } +} diff --git a/adapters/yandex/yandex_test.go b/adapters/yandex/yandex_test.go new file mode 100644 index 00000000000..6978cd8bb54 --- /dev/null +++ b/adapters/yandex/yandex_test.go @@ -0,0 +1,29 @@ +package yandex + +import ( + "testing" + + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderYandex, + config.Adapter{Endpoint: "https://bs-metadsp.yandex.ru/prebid/{{.PageID}}?ssp-id=10500"}, + config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "yandextest", bidder) +} + +func TestEndpointTemplateMalformed(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderYandex, config.Adapter{ + Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + assert.Error(t, buildErr) +} diff --git a/adapters/yandex/yandextest/exemplary/native.json b/adapters/yandex/yandextest/exemplary/native.json new file mode 100644 index 00000000000..f9b3888593b --- /dev/null +++ b/adapters/yandex/yandextest/exemplary/native.json @@ -0,0 +1,134 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "native": { + "request": "{}", + "ver": "1" + }, + "ext": { + "bidder": { + "placement_id": "R-134001-1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bs-metadsp.yandex.ru/prebid/134001?imp-id=1&ssp-cur=USD&ssp-id=10500&target-ref=www.example.com", + "headers": { + "User-Agent": [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Real-Ip": [ + "127.0.0.1" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Referer": [ + "http://www.example.com" + ], + "Accept": [ + "application/json" + ], + "Accept-Language": [ + "EN" + ] + }, + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "native": { + "request": "{}", + "ver": "1" + }, + "ext": { + "bidder": { + "placement_id": "R-134001-1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + } + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "crid": "crid", + "adm": "adm_content", + "h": 600, + "w": 300 + } + ] + } + ], + "bidid": "bid_id", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "adm": "adm_content", + "crid": "crid", + "w": 300, + "h": 600 + }, + "type": "native" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/yandex/yandextest/exemplary/simple-banner.json b/adapters/yandex/yandextest/exemplary/simple-banner.json new file mode 100644 index 00000000000..477e4ef5848 --- /dev/null +++ b/adapters/yandex/yandextest/exemplary/simple-banner.json @@ -0,0 +1,134 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "placement_id": "134001-1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bs-metadsp.yandex.ru/prebid/134001?imp-id=1&ssp-cur=USD&ssp-id=10500&target-ref=www.example.com", + "headers": { + "User-Agent": [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Real-Ip": [ + "127.0.0.1" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Referer": [ + "http://www.example.com" + ], + "Accept": [ + "application/json" + ], + "Accept-Language": [ + "EN" + ] + }, + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "placement_id": "134001-1" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1", + "language": "EN" + }, + "tmax": 500 + } + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "crid": "crid", + "adm": "content", + "h": 600, + "w": 300 + } + ] + } + ], + "bidid": "bid_id", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "bid_id", + "impid": "imp_id", + "price": 1.25, + "adm": "content", + "crid": "crid", + "w": 300, + "h": 600 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/yandex/yandextest/supplemental/multiple-imps-some-malformed.json b/adapters/yandex/yandextest/supplemental/multiple-imps-some-malformed.json new file mode 100644 index 00000000000..299f4013392 --- /dev/null +++ b/adapters/yandex/yandextest/supplemental/multiple-imps-some-malformed.json @@ -0,0 +1,145 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id1", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 111, + "imp_id": 1 + } + } + }, + { + "id": "imp_id2", + "ext": { + "bidder": { + "page_id": 222, + "imp_id": 2 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bs-metadsp.yandex.ru/prebid/111?imp-id=1&ssp-cur=USD&ssp-id=10500&target-ref=www.example.com", + "headers": { + "User-Agent": [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Real-Ip": [ + "127.0.0.1" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Referer": [ + "http://www.example.com" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id1", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 111, + "imp_id": 1 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + } + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id1", + "impid": "imp_id1", + "price": 1.25, + "crid": "crid", + "adm": "adm001", + "h": 600, + "w": 300 + } + ] + } + ], + "bidid": "bid_id" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "bid_id1", + "impid": "imp_id1", + "price": 1.25, + "adm": "adm001", + "crid": "crid", + "w": 300, + "h": 600 + }, + "type": "banner" + } + ] + } + ], + "expectedMakeRequestsErrors": [ + { + "value": "Unsupported format", + "comparison": "regex" + } + ] +} \ No newline at end of file diff --git a/adapters/yandex/yandextest/supplemental/multiple-imps.json b/adapters/yandex/yandextest/supplemental/multiple-imps.json new file mode 100644 index 00000000000..b83f51ebc3c --- /dev/null +++ b/adapters/yandex/yandextest/supplemental/multiple-imps.json @@ -0,0 +1,235 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id1", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 111, + "imp_id": 1 + } + } + }, + { + "id": "imp_id2", + "banner": { + "w": 400, + "h": 800 + }, + "ext": { + "bidder": { + "placement_id": "222-2" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bs-metadsp.yandex.ru/prebid/111?imp-id=1&ssp-cur=USD&ssp-id=10500&target-ref=www.example.com", + "headers": { + "User-Agent": [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Real-Ip": [ + "127.0.0.1" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Referer": [ + "http://www.example.com" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id1", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 111, + "imp_id": 1 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + } + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id1", + "impid": "imp_id1", + "price": 1.25, + "crid": "crid", + "adm": "adm001", + "h": 600, + "w": 300 + } + ] + } + ], + "bidid": "bid_id" + } + } + }, + { + "expectedRequest": { + "uri": "https://bs-metadsp.yandex.ru/prebid/222?imp-id=2&ssp-cur=USD&ssp-id=10500&target-ref=www.example.com", + "headers": { + "User-Agent": [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Real-Ip": [ + "127.0.0.1" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Referer": [ + "http://www.example.com" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id2", + "banner": { + "w": 400, + "h": 800 + }, + "ext": { + "bidder": { + "placement_id": "222-2" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + } + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id2", + "impid": "imp_id2", + "price": 1.25, + "crid": "crid", + "adm": "adm001", + "h": 800, + "w": 400 + } + ] + } + ], + "bidid": "bid_id" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "bid_id1", + "impid": "imp_id1", + "price": 1.25, + "adm": "adm001", + "crid": "crid", + "w": 300, + "h": 600 + }, + "type": "banner" + } + ] + }, + { + "bids": [ + { + "bid": { + "id": "bid_id2", + "impid": "imp_id2", + "price": 1.25, + "adm": "adm001", + "crid": "crid", + "w": 400, + "h": 800 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/yandex/yandextest/supplemental/simple-banner-empty-response.json b/adapters/yandex/yandextest/supplemental/simple-banner-empty-response.json new file mode 100644 index 00000000000..ffc067ebe7a --- /dev/null +++ b/adapters/yandex/yandextest/supplemental/simple-banner-empty-response.json @@ -0,0 +1,100 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 134001, + "imp_id": 1 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bs-metadsp.yandex.ru/prebid/134001?imp-id=1&ssp-cur=USD&ssp-id=10500&target-ref=www.example.com", + "headers": { + "User-Agent": [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Real-Ip": [ + "127.0.0.1" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Referer": [ + "http://www.example.com" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 134001, + "imp_id": 1 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + } + }, + "mockResponse": { + "status": 200, + "headers": {} + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Bad server response", + "comparison": "regex" + } + ] +} \ No newline at end of file diff --git a/adapters/yandex/yandextest/supplemental/simple-banner-empty-seatbid.json b/adapters/yandex/yandextest/supplemental/simple-banner-empty-seatbid.json new file mode 100644 index 00000000000..eafb27a41ca --- /dev/null +++ b/adapters/yandex/yandextest/supplemental/simple-banner-empty-seatbid.json @@ -0,0 +1,105 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 134001, + "imp_id": 1 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bs-metadsp.yandex.ru/prebid/134001?imp-id=1&ssp-cur=USD&ssp-id=10500&target-ref=www.example.com", + "headers": { + "User-Agent": [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Real-Ip": [ + "127.0.0.1" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Referer": [ + "http://www.example.com" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 134001, + "imp_id": 1 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + } + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": null, + "bidid": "bid_id", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [] + } + ], + "expectedMakeBidsErrors": [] +} \ No newline at end of file diff --git a/adapters/yandex/yandextest/supplemental/simple-banner-sizes.json b/adapters/yandex/yandextest/supplemental/simple-banner-sizes.json new file mode 100644 index 00000000000..23a142887db --- /dev/null +++ b/adapters/yandex/yandextest/supplemental/simple-banner-sizes.json @@ -0,0 +1,232 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "invalid_width", + "banner": { + "w": 0, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 111, + "imp_id": 1 + } + } + }, + { + "id": "invalid_height", + "banner": { + "w": 400, + "h": 0 + }, + "ext": { + "bidder": { + "page_id": 222, + "imp_id": 2 + } + } + }, + { + "id": "invalid_size", + "banner": { + "w": 0, + "h": 0 + }, + "ext": { + "bidder": { + "page_id": 222, + "imp_id": 3 + } + } + }, + { + "id": "invalid_no_size_no_formats", + "banner": {}, + "ext": { + "bidder": { + "page_id": 222, + "imp_id": 5 + } + } + }, + { + "id": "invalid_size", + "banner": { + "w": 0, + "h": 0 + }, + "ext": { + "bidder": { + "page_id": 222, + "imp_id": 3 + } + } + }, + { + "id": "no_size_but_valid_formats", + "banner": { + "format": [ + { + "w": 600, + "h": 500 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "page_id": 222, + "imp_id": 4 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bs-metadsp.yandex.ru/prebid/222?imp-id=4&ssp-cur=USD&ssp-id=10500&target-ref=www.example.com", + "headers": { + "User-Agent": [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Real-Ip": [ + "127.0.0.1" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Referer": [ + "http://www.example.com" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "request_id", + "imp": [ + { + "id": "no_size_but_valid_formats", + "banner": { + "w": 600, + "h": 500, + "format": [ + { + "w": 600, + "h": 500 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "page_id": 222, + "imp_id": 4 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + } + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id4", + "impid": "no_size_but_valid_formats", + "price": 1.25, + "crid": "crid", + "adm": "adm001", + "h": 800, + "w": 400 + } + ] + } + ], + "bidid": "bid_id" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "bid_id4", + "impid": "no_size_but_valid_formats", + "price": 1.25, + "adm": "adm001", + "crid": "crid", + "w": 400, + "h": 800 + }, + "type": "banner" + } + ] + } + ], + "expectedMakeRequestsErrors": [ + { + "value": "Invalid size", + "comparison": "regex" + }, + { + "value": "Invalid size", + "comparison": "regex" + }, + { + "value": "Invalid size", + "comparison": "regex" + }, + { + "value": "Invalid size", + "comparison": "regex" + }, + { + "value": "Invalid size", + "comparison": "regex" + } + ] +} \ No newline at end of file diff --git a/adapters/yandex/yandextest/supplemental/simple-banner-status-400.json b/adapters/yandex/yandextest/supplemental/simple-banner-status-400.json new file mode 100644 index 00000000000..78d2813bb4f --- /dev/null +++ b/adapters/yandex/yandextest/supplemental/simple-banner-status-400.json @@ -0,0 +1,101 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 134001, + "imp_id": 1 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bs-metadsp.yandex.ru/prebid/134001?imp-id=1&ssp-cur=USD&ssp-id=10500&target-ref=www.example.com", + "headers": { + "User-Agent": [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Real-Ip": [ + "127.0.0.1" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Referer": [ + "http://www.example.com" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 134001, + "imp_id": 1 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + } + }, + "mockResponse": { + "status": 400, + "headers": {}, + "body": {} + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code", + "comparison": "regex" + } + ] +} \ No newline at end of file diff --git a/adapters/yandex/yandextest/supplemental/simple-banner-unknown-imp.json b/adapters/yandex/yandextest/supplemental/simple-banner-unknown-imp.json new file mode 100644 index 00000000000..5c94be30605 --- /dev/null +++ b/adapters/yandex/yandextest/supplemental/simple-banner-unknown-imp.json @@ -0,0 +1,124 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 134001, + "imp_id": 1 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bs-metadsp.yandex.ru/prebid/134001?imp-id=1&ssp-cur=USD&ssp-id=10500&target-ref=www.example.com", + "headers": { + "User-Agent": [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Real-Ip": [ + "127.0.0.1" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Referer": [ + "http://www.example.com" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 134001, + "imp_id": 1 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + } + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": { + "id": "request_id", + "seatbid": [ + { + "bid": [ + { + "id": "bid_id", + "impid": "imp_id_unknown", + "price": 1.25, + "crid": "crid", + "adm": "content", + "h": 600, + "w": 300 + } + ] + } + ], + "bidid": "bid_id", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [] + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Invalid bid imp", + "comparison": "regex" + } + ] +} \ No newline at end of file diff --git a/adapters/yandex/yandextest/supplemental/simple-banner-unparsable-body.json b/adapters/yandex/yandextest/supplemental/simple-banner-unparsable-body.json new file mode 100644 index 00000000000..0adfdb40acf --- /dev/null +++ b/adapters/yandex/yandextest/supplemental/simple-banner-unparsable-body.json @@ -0,0 +1,101 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 134001, + "imp_id": 1 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bs-metadsp.yandex.ru/prebid/134001?imp-id=1&ssp-cur=USD&ssp-id=10500&target-ref=www.example.com", + "headers": { + "User-Agent": [ + "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36" + ], + "X-Forwarded-For": [ + "127.0.0.1" + ], + "X-Real-Ip": [ + "127.0.0.1" + ], + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Referer": [ + "http://www.example.com" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "banner": { + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "page_id": 134001, + "imp_id": 1 + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com" + }, + "cur": [ + "USD" + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + } + }, + "mockResponse": { + "status": 200, + "headers": {}, + "body": "invalid" + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Bad server response", + "comparison": "regex" + } + ] +} \ No newline at end of file diff --git a/adapters/yandex/yandextest/supplemental/unknown-banner.json b/adapters/yandex/yandextest/supplemental/unknown-banner.json new file mode 100644 index 00000000000..a01b9037180 --- /dev/null +++ b/adapters/yandex/yandextest/supplemental/unknown-banner.json @@ -0,0 +1,32 @@ +{ + "mockBidRequest": { + "id": "request_id", + "imp": [ + { + "id": "imp_id", + "video": { + "w": 100, + "h": 200 + }, + "ext": { + "bidder": { + "page_id": 134001, + "imp_id": 1 + } + } + } + ], + "device": { + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", + "ip": "127.0.0.1" + }, + "tmax": 500 + }, + "httpCalls": [], + "expectedMakeRequestsErrors": [ + { + "value": "Unsupported format", + "comparison": "regex" + } + ] +} \ No newline at end of file diff --git a/adapters/yeahmobi/params_test.go b/adapters/yeahmobi/params_test.go index 997bf93a53f..805be75da30 100644 --- a/adapters/yeahmobi/params_test.go +++ b/adapters/yeahmobi/params_test.go @@ -2,8 +2,9 @@ package yeahmobi import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/yeahmobi/yeahmobi.go b/adapters/yeahmobi/yeahmobi.go index d25b2eab541..1c14d997684 100644 --- a/adapters/yeahmobi/yeahmobi.go +++ b/adapters/yeahmobi/yeahmobi.go @@ -7,15 +7,15 @@ import ( "net/url" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) -type YeahmobiAdapter struct { +type adapter struct { EndpointTemplate *template.Template } @@ -26,16 +26,16 @@ func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server co return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) } - bidder := &YeahmobiAdapter{ + bidder := &adapter{ EndpointTemplate: template, } return bidder, nil } -func (adapter *YeahmobiAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { var adapterRequests []*adapters.RequestData - adapterRequest, errs := adapter.makeRequest(request) + adapterRequest, errs := a.makeRequest(request) if errs == nil { adapterRequests = append(adapterRequests, adapterRequest) } @@ -43,7 +43,7 @@ func (adapter *YeahmobiAdapter) MakeRequests(request *openrtb2.BidRequest, reqIn return adapterRequests, errs } -func (adapter *YeahmobiAdapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, []error) { +func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, []error) { var errs []error yeahmobiExt, errs := getYeahmobiExt(request) @@ -51,7 +51,7 @@ func (adapter *YeahmobiAdapter) makeRequest(request *openrtb2.BidRequest) (*adap if yeahmobiExt == nil { return nil, errs } - endPoint, err := adapter.getEndpoint(yeahmobiExt) + endPoint, err := a.getEndpoint(yeahmobiExt) if err != nil { return nil, append(errs, err) } @@ -124,12 +124,12 @@ func getYeahmobiExt(request *openrtb2.BidRequest) (*openrtb_ext.ExtImpYeahmobi, } -func (adapter *YeahmobiAdapter) getEndpoint(ext *openrtb_ext.ExtImpYeahmobi) (string, error) { - return macros.ResolveMacros(adapter.EndpointTemplate, macros.EndpointTemplateParams{Host: "gw-" + url.QueryEscape(ext.ZoneId) + "-bid.yeahtargeter.com"}) +func (a *adapter) getEndpoint(ext *openrtb_ext.ExtImpYeahmobi) (string, error) { + return macros.ResolveMacros(a.EndpointTemplate, macros.EndpointTemplateParams{Host: "gw-" + url.QueryEscape(ext.ZoneId) + "-bid.yeahtargeter.com"}) } // MakeBids make the bids for the bid response. -func (a *YeahmobiAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { +func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { if response.StatusCode == http.StatusNoContent { return nil, nil } diff --git a/adapters/yeahmobi/yeahmobi_test.go b/adapters/yeahmobi/yeahmobi_test.go index 0b1c39ef214..c1c7be35105 100644 --- a/adapters/yeahmobi/yeahmobi_test.go +++ b/adapters/yeahmobi/yeahmobi_test.go @@ -3,9 +3,9 @@ package yeahmobi import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/yieldlab/params_test.go b/adapters/yieldlab/params_test.go index ed0d2863629..74a91ac3bf7 100644 --- a/adapters/yieldlab/params_test.go +++ b/adapters/yieldlab/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/yieldlab.json diff --git a/adapters/yieldlab/types.go b/adapters/yieldlab/types.go index 90612700713..9d3bc6a2fa6 100644 --- a/adapters/yieldlab/types.go +++ b/adapters/yieldlab/types.go @@ -1,18 +1,60 @@ package yieldlab import ( + "github.com/prebid/prebid-server/v2/openrtb_ext" "strconv" "time" ) type bidResponse struct { - ID uint64 `json:"id"` - Price uint `json:"price"` - Advertiser string `json:"advertiser"` - Adsize string `json:"adsize"` - Pid uint64 `json:"pid"` - Did uint64 `json:"did"` - Pvid string `json:"pvid"` + ID uint64 `json:"id"` + Price uint `json:"price"` + Advertiser string `json:"advertiser"` + Adsize string `json:"adsize"` + Pid uint64 `json:"pid"` + Did uint64 `json:"did"` + Pvid string `json:"pvid"` + DSA *dsaResponse `json:"dsa,omitempty"` +} + +// dsaResponse defines Digital Service Act (DSA) parameters from Yieldlab yieldprobe response. +type dsaResponse struct { + Behalf string `json:"behalf,omitempty"` + Paid string `json:"paid,omitempty"` + Adrender *int `json:"adrender,omitempty"` + Transparency []dsaTransparency `json:"transparency,omitempty"` +} + +// openRTBExtRegsWithDSA defines the contract for bidrequest.regs.ext with the missing DSA property. +// +// The openrtb_ext.ExtRegs needs to be extended on yieldlab adapter level until DSA has been implemented +// by the prebid server team (https://github.com/prebid/prebid-server/issues/3424). +type openRTBExtRegsWithDSA struct { + openrtb_ext.ExtRegs + DSA *dsaRequest `json:"dsa,omitempty"` +} + +// responseExtWithDSA defines seatbid.bid.ext with the DSA object. +type responseExtWithDSA struct { + DSA dsaResponse `json:"dsa"` +} + +// dsaRequest defines Digital Service Act (DSA) parameter +// as specified by the OpenRTB 2.X DSA Transparency community extension. +// +// Should rather come from openrtb_ext package but will be defined here until DSA has been +// implemented by the prebid server team (https://github.com/prebid/prebid-server/issues/3424). +type dsaRequest struct { + Required *int `json:"dsarequired"` + PubRender *int `json:"pubrender"` + DataToPub *int `json:"datatopub"` + Transparency []dsaTransparency `json:"transparency"` +} + +// dsaTransparency Digital Service Act (DSA) transparency object +type dsaTransparency struct { + Domain string `json:"domain,omitempty"` + Params []int `json:"dsaparams,omitempty"` } type cacheBuster func() string diff --git a/adapters/yieldlab/yieldlab.go b/adapters/yieldlab/yieldlab.go index 74ee6bd7220..ed7c283ead9 100644 --- a/adapters/yieldlab/yieldlab.go +++ b/adapters/yieldlab/yieldlab.go @@ -11,11 +11,12 @@ import ( "golang.org/x/text/currency" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) // YieldlabAdapter connects the Yieldlab API to prebid server @@ -66,8 +67,8 @@ func (a *YieldlabAdapter) makeEndpointURL(req *openrtb2.BidRequest, params *open } if req.Device.Geo != nil { - q.Set("lat", fmt.Sprintf("%v", req.Device.Geo.Lat)) - q.Set("lon", fmt.Sprintf("%v", req.Device.Geo.Lon)) + q.Set("lat", fmt.Sprintf("%v", ptrutil.ValueOrDefault(req.Device.Geo.Lat))) + q.Set("lon", fmt.Sprintf("%v", ptrutil.ValueOrDefault(req.Device.Geo.Lon))) } } @@ -95,11 +96,94 @@ func (a *YieldlabAdapter) makeEndpointURL(req *openrtb2.BidRequest, params *open } } + dsa, err := getDSA(req) + if err != nil { + return "", err + } + if dsa != nil { + if dsa.Required != nil { + q.Set("dsarequired", strconv.Itoa(*dsa.Required)) + } + if dsa.PubRender != nil { + q.Set("dsapubrender", strconv.Itoa(*dsa.PubRender)) + } + if dsa.DataToPub != nil { + q.Set("dsadatatopub", strconv.Itoa(*dsa.DataToPub)) + } + if len(dsa.Transparency) != 0 { + transparencyParam := makeDSATransparencyURLParam(dsa.Transparency) + if len(transparencyParam) != 0 { + q.Set("dsatransparency", transparencyParam) + } + } + } + uri.RawQuery = q.Encode() return uri.String(), nil } +// getDSA extracts the Digital Service Act (DSA) properties from the request. +func getDSA(req *openrtb2.BidRequest) (*dsaRequest, error) { + if req.Regs == nil || req.Regs.Ext == nil { + return nil, nil + } + + var extRegs openRTBExtRegsWithDSA + err := json.Unmarshal(req.Regs.Ext, &extRegs) + if err != nil { + return nil, fmt.Errorf("failed to parse Regs.Ext object from Yieldlab response: %v", err) + } + + return extRegs.DSA, nil +} + +// makeDSATransparencyURLParam creates the transparency url parameter +// as specified by the OpenRTB 2.X DSA Transparency community extension. +// +// Example result: platform1domain.com~1~~SSP2domain.com~1_2 +func makeDSATransparencyURLParam(transparencyObjects []dsaTransparency) string { + valueSeparator, itemSeparator, objectSeparator := "_", "~", "~~" + + var b strings.Builder + + concatParams := func(params []int) { + b.WriteString(strconv.Itoa(params[0])) + for _, param := range params[1:] { + b.WriteString(valueSeparator) + b.WriteString(strconv.Itoa(param)) + } + } + + concatTransparency := func(object dsaTransparency) { + if len(object.Domain) == 0 { + return + } + + b.WriteString(object.Domain) + if len(object.Params) != 0 { + b.WriteString(itemSeparator) + concatParams(object.Params) + } + } + + concatTransparencies := func(objects []dsaTransparency) { + if len(objects) == 0 { + return + } + + concatTransparency(objects[0]) + for _, obj := range objects[1:] { + b.WriteString(objectSeparator) + concatTransparency(obj) + } + } + + concatTransparencies(transparencyObjects) + + return b.String() +} + func (a *YieldlabAdapter) makeFormats(req *openrtb2.BidRequest) (bool, string) { var formats []string const sizesSeparator, adslotSizesSeparator = "|", "," @@ -252,6 +336,7 @@ func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa } } + var bidErrors []error for _, bid := range bids { width, height, err := splitSize(bid.Adsize) if err != nil { @@ -268,7 +353,13 @@ func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa if imp, exists := adslotToImpMap[strconv.FormatUint(bid.ID, 10)]; !exists { continue } else { - var bidType openrtb_ext.BidType + extJson, err := makeResponseExt(bid) + if err != nil { + bidErrors = append(bidErrors, err) + // skip as bids with missing ext.dsa will be discarded anyway + continue + } + responseBid := &openrtb2.Bid{ ID: strconv.FormatUint(bid.ID, 10), Price: float64(bid.Price) / 100, @@ -277,8 +368,10 @@ func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa DealID: strconv.FormatUint(bid.Pid, 10), W: int64(width), H: int64(height), + Ext: extJson, } + var bidType openrtb_ext.BidType if imp.Video != nil { bidType = openrtb_ext.BidTypeVideo responseBid.NURL = a.makeAdSourceURL(internalRequest, req, bid) @@ -298,7 +391,18 @@ func (a *YieldlabAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externa } } - return bidderResponse, nil + return bidderResponse, bidErrors +} + +func makeResponseExt(bid *bidResponse) (json.RawMessage, error) { + if bid.DSA != nil { + extJson, err := json.Marshal(responseExtWithDSA{*bid.DSA}) + if err != nil { + return nil, fmt.Errorf("failed to make JSON for seatbid.bid.ext for adslotID %v. This is most likely a programming issue", bid.ID) + } + return extJson, nil + } + return nil, nil } func (a *YieldlabAdapter) findBidReq(adslotID uint64, params []*openrtb_ext.ExtImpYieldlab) *openrtb_ext.ExtImpYieldlab { diff --git a/adapters/yieldlab/yieldlab_test.go b/adapters/yieldlab/yieldlab_test.go index d3fc9f3eb1d..49dcc31e598 100644 --- a/adapters/yieldlab/yieldlab_test.go +++ b/adapters/yieldlab/yieldlab_test.go @@ -5,12 +5,12 @@ import ( "strconv" "testing" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const testURL = "https://ad.yieldlab.net/testing/" @@ -257,6 +257,98 @@ func Test_makeSupplyChain(t *testing.T) { } } +func Test_makeDSATransparencyUrlParam(t *testing.T) { + tests := []struct { + name string + transparencies []dsaTransparency + expected string + }{ + { + name: "No transparency objects", + transparencies: []dsaTransparency{}, + expected: "", + }, + { + name: "Nil transparency", + transparencies: nil, + expected: "", + }, + { + name: "Params without a domain", + transparencies: []dsaTransparency{ + { + Params: []int{1, 2}, + }, + }, + expected: "", + }, + { + name: "Params without a params", + transparencies: []dsaTransparency{ + { + Domain: "domain.com", + }, + }, + expected: "domain.com", + }, + { + name: "One object; No Params", + transparencies: []dsaTransparency{ + { + Domain: "domain.com", + Params: []int{}, + }, + }, + expected: "domain.com", + }, + { + name: "One object; One Param", + transparencies: []dsaTransparency{ + { + Domain: "domain.com", + Params: []int{1}, + }, + }, + expected: "domain.com~1", + }, + { + name: "Three domain objects", + transparencies: []dsaTransparency{ + { + Domain: "domain1.com", + Params: []int{1, 2}, + }, + { + Domain: "domain2.com", + Params: []int{3, 4}, + }, + { + Domain: "domain3.com", + Params: []int{5, 6}, + }, + }, + expected: "domain1.com~1_2~~domain2.com~3_4~~domain3.com~5_6", + }, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + actual := makeDSATransparencyURLParam(test.transparencies) + assert.Equal(t, test.expected, actual) + }) + } +} + +func Test_getDSA_invalidRequestExt(t *testing.T) { + req := &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"DSA":"wrongValueType"}`)}, + } + + dsa, err := getDSA(req) + + assert.NotNil(t, err) + assert.Nil(t, dsa) +} + func TestYieldlabAdapter_makeEndpointURL_invalidEndpoint(t *testing.T) { bidder, buildErr := Builder(openrtb_ext.BidderYieldlab, config.Adapter{ Endpoint: "test$:/something§"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) diff --git a/adapters/yieldlab/yieldlabtest/exemplary/dsa.json b/adapters/yieldlab/yieldlabtest/exemplary/dsa.json new file mode 100644 index 00000000000..0c815835797 --- /dev/null +++ b/adapters/yieldlab/yieldlabtest/exemplary/dsa.json @@ -0,0 +1,169 @@ +{ + "mockBidRequest": { + "id": "test-request-with-DSA", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "adslotId": "12345", + "supplyId": "123456789", + "targeting": { + "key1": "value1", + "key2": "value2" + }, + "extId": "abc" + } + } + } + ], + "device": { + "ifa": "hello-ads", + "devicetype": 4, + "connectiontype": 6, + "geo": { + "lat": 51.499488, + "lon": -0.128953 + }, + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36", + "ip": "169.254.13.37", + "h": 1098, + "w": 814 + }, + "regs": { + "ext": { + "gdpr": 1, + "dsa": { + "dsarequired": 3, + "pubrender": 0, + "datatopub": 2, + "transparency": [ + { + "domain": "platform1domain.com", + "dsaparams": [ + 1 + ] + }, + { + "domain": "SSP2domain.com", + "dsaparams": [ + 1, + 2 + ] + } + ] + } + } + }, + "site": { + "id": "fake-site-id", + "publisher": { + "id": "1" + }, + "page": "http://localhost:9090/gdpr.html" + }, + "user": { + "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c", + "ext": { + "consent": "BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": [ + "application/json" + ], + "Cookie": [ + "id=34a53e82-0dc3-4815-8b7e-b725ede0361c" + ], + "Referer": [ + "http://localhost:9090/gdpr.html" + ], + "User-Agent": [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" + ], + "X-Forwarded-For": [ + "169.254.13.37" + ] + }, + "uri": "https://ad.yieldlab.net/testing/12345?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&content=json&dsadatatopub=2&dsapubrender=0&dsarequired=3&dsatransparency=platform1domain.com~1~~SSP2domain.com~1_2&gdpr=1&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&sizes=12345%3A728x90&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads" + }, + "mockResponse": { + "status": 200, + "body": [ + { + "id": 12345, + "price": 201, + "advertiser": "yieldlab", + "adsize": "728x90", + "pid": 1234, + "did": 5678, + "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5", + "dsa": { + "required": 3, + "behalf": "on behalf of yieldlab", + "paid": "by yieldlab", + "transparency": [ + { + "domain": "yieldlab.de", + "dsaparams": [ + 1, + 2 + ] + } + ], + "adrender": 0 + } + } + ] + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "adm": "", + "crid": "12345123433", + "dealid": "1234", + "ext": { + "dsa": { + "adrender": 0, + "transparency": [ + { + "domain": "yieldlab.de", + "dsaparams": [ + 1, + 2 + ] + } + ], + "behalf": "on behalf of yieldlab", + "paid": "by yieldlab" + } + }, + "id": "12345", + "impid": "test-imp-id", + "price": 2.01, + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/yieldlab/yieldlabtest/supplemental/dsa_empty.json b/adapters/yieldlab/yieldlabtest/supplemental/dsa_empty.json new file mode 100644 index 00000000000..594e5bb4d0b --- /dev/null +++ b/adapters/yieldlab/yieldlabtest/supplemental/dsa_empty.json @@ -0,0 +1,127 @@ +{ + "mockBidRequest": { + "id": "test-request-with-empty-DSA", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "adslotId": "12345", + "supplyId": "123456789", + "targeting": { + "key1": "value1", + "key2": "value2" + }, + "extId": "abc" + } + } + } + ], + "device": { + "ifa": "hello-ads", + "devicetype": 4, + "connectiontype": 6, + "geo": { + "lat": 51.499488, + "lon": -0.128953 + }, + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36", + "ip": "169.254.13.37", + "h": 1098, + "w": 814 + }, + "regs": { + "ext": { + "gdpr": 1, + "dsa": { + } + } + }, + "site": { + "id": "fake-site-id", + "publisher": { + "id": "1" + }, + "page": "http://localhost:9090/gdpr.html" + }, + "user": { + "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c", + "ext": { + "consent": "BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": [ + "application/json" + ], + "Cookie": [ + "id=34a53e82-0dc3-4815-8b7e-b725ede0361c" + ], + "Referer": [ + "http://localhost:9090/gdpr.html" + ], + "User-Agent": [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" + ], + "X-Forwarded-For": [ + "169.254.13.37" + ] + }, + "uri": "https://ad.yieldlab.net/testing/12345?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&content=json&gdpr=1&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&sizes=12345%3A728x90&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads" + }, + "mockResponse": { + "status": 200, + "body": [ + { + "id": 12345, + "price": 201, + "advertiser": "yieldlab", + "adsize": "728x90", + "pid": 1234, + "did": 5678, + "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5", + "dsa": { + } + } + ] + + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "adm": "", + "crid": "12345123433", + "dealid": "1234", + "ext": { + "dsa": { + } + }, + "id": "12345", + "impid": "test-imp-id", + "price": 2.01, + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/yieldlab/yieldlabtest/supplemental/dsa_empty_transparency.json b/adapters/yieldlab/yieldlabtest/supplemental/dsa_empty_transparency.json new file mode 100644 index 00000000000..c966a7166a5 --- /dev/null +++ b/adapters/yieldlab/yieldlabtest/supplemental/dsa_empty_transparency.json @@ -0,0 +1,139 @@ +{ + "mockBidRequest": { + "id": "test-request-with-empty-DSA-transparency", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "adslotId": "12345", + "supplyId": "123456789", + "targeting": { + "key1": "value1", + "key2": "value2" + }, + "extId": "abc" + } + } + } + ], + "device": { + "ifa": "hello-ads", + "devicetype": 4, + "connectiontype": 6, + "geo": { + "lat": 51.499488, + "lon": -0.128953 + }, + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36", + "ip": "169.254.13.37", + "h": 1098, + "w": 814 + }, + "regs": { + "ext": { + "gdpr": 1, + "dsa": { + "transparency": [ + { + } + ] + } + } + }, + "site": { + "id": "fake-site-id", + "publisher": { + "id": "1" + }, + "page": "http://localhost:9090/gdpr.html" + }, + "user": { + "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c", + "ext": { + "consent": "BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": [ + "application/json" + ], + "Cookie": [ + "id=34a53e82-0dc3-4815-8b7e-b725ede0361c" + ], + "Referer": [ + "http://localhost:9090/gdpr.html" + ], + "User-Agent": [ + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36" + ], + "X-Forwarded-For": [ + "169.254.13.37" + ] + }, + "uri": "https://ad.yieldlab.net/testing/12345?consent=BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02&content=json&gdpr=1&ids=ylid%3A34a53e82-0dc3-4815-8b7e-b725ede0361c&lat=51.499488&lon=-0.128953&pvid=true&sizes=12345%3A728x90&t=key1%3Dvalue1%26key2%3Dvalue2&ts=testing&yl_rtb_connectiontype=6&yl_rtb_devicetype=4&yl_rtb_ifa=hello-ads" + }, + "mockResponse": { + "status": 200, + "body": [ + { + "id": 12345, + "price": 201, + "advertiser": "yieldlab", + "adsize": "728x90", + "pid": 1234, + "did": 5678, + "pvid": "40cb3251-1e1e-4cfd-8edc-7d32dc1a21e5", + "dsa": { + "transparency": [ + { + } + ] + } + } + ] + + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "adm": "", + "crid": "12345123433", + "dealid": "1234", + "ext": { + "dsa": { + "transparency": [ + { + } + ] + } + }, + "id": "12345", + "impid": "test-imp-id", + "price": 2.01, + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/yieldlab/yieldlabtest/supplemental/invalid_reg_ext.json b/adapters/yieldlab/yieldlabtest/supplemental/invalid_reg_ext.json new file mode 100644 index 00000000000..1723b8fbe80 --- /dev/null +++ b/adapters/yieldlab/yieldlabtest/supplemental/invalid_reg_ext.json @@ -0,0 +1,67 @@ +{ + "mockBidRequest": { + "id": "test-request-with-wrong-DSA-type", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "adslotId": "12345", + "supplyId": "123456789", + "targeting": { + "key1": "value1", + "key2": "value2" + }, + "extId": "abc" + } + } + } + ], + "device": { + "ifa": "hello-ads", + "devicetype": 4, + "connectiontype": 6, + "geo": { + "lat": 51.499488, + "lon": -0.128953 + }, + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36", + "ip": "169.254.13.37", + "h": 1098, + "w": 814 + }, + "regs": { + "ext": { + "gdpr": 1, + "DSA": "" + } + }, + "site": { + "id": "fake-site-id", + "publisher": { + "id": "1" + }, + "page": "http://localhost:9090/gdpr.html" + }, + "user": { + "buyeruid": "34a53e82-0dc3-4815-8b7e-b725ede0361c", + "ext": { + "consent": "BOlOrv1OlOr2EAAABADECg-AAAApp7v______9______9uz_Ov_v_f__33e8__9v_l_7_-___u_-3zd4u_1vf99yfm1-7etr3tp_87ues2_Xur__79__3z3_9phP78k89r7337Ew-v02" + } + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "failed to parse Regs.Ext object from Yieldlab response: json: cannot unmarshal string into Go struct field openRTBExtRegsWithDSA.dsa of type yieldlab.dsaRequest", + "comparison": "literal" + } + ] +} diff --git a/adapters/yieldmo/params_test.go b/adapters/yieldmo/params_test.go index d94c7ff035b..647c21abb90 100644 --- a/adapters/yieldmo/params_test.go +++ b/adapters/yieldmo/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // This file actually intends to test static/bidder-params/yieldmo.json diff --git a/adapters/yieldmo/yieldmo.go b/adapters/yieldmo/yieldmo.go index bf8410b294b..1d2ad308a2c 100644 --- a/adapters/yieldmo/yieldmo.go +++ b/adapters/yieldmo/yieldmo.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type YieldmoAdapter struct { @@ -30,6 +30,10 @@ type Ext struct { Gpid string `json:"gpid,omitempty"` } +type ExtBid struct { + MediaType string `json:"mediatype,omitempty"` +} + func (a *YieldmoAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { var errs []error var adapterRequests []*adapters.RequestData @@ -138,14 +142,18 @@ func (a *YieldmoAdapter) MakeBids(internalRequest *openrtb2.BidRequest, external for _, sb := range bidResp.SeatBid { for i := range sb.Bid { + bidType, err := getMediaTypeForImp(sb.Bid[i]) + if err != nil { + continue + } + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ Bid: &sb.Bid[i], - BidType: getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp), + BidType: bidType, }) } } return bidResponse, nil - } // Builder builds a new instance of the Yieldmo adapter for the given bidder with the given config. @@ -156,12 +164,21 @@ func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server co return bidder, nil } -func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType { - //default to video unless banner exists in impression - for _, imp := range imps { - if imp.ID == impId && imp.Banner != nil { - return openrtb_ext.BidTypeBanner - } +// Retrieve the media type corresponding to the bid from the bid.ext object +func getMediaTypeForImp(bid openrtb2.Bid) (openrtb_ext.BidType, error) { + var bidExt ExtBid + if err := json.Unmarshal(bid.Ext, &bidExt); err != nil { + return "", &errortypes.BadInput{Message: err.Error()} + } + + switch bidExt.MediaType { + case "banner": + return openrtb_ext.BidTypeBanner, nil + case "video": + return openrtb_ext.BidTypeVideo, nil + case "native": + return openrtb_ext.BidTypeNative, nil + default: + return "", fmt.Errorf("invalid BidType: %s", bidExt.MediaType) } - return openrtb_ext.BidTypeVideo } diff --git a/adapters/yieldmo/yieldmo_test.go b/adapters/yieldmo/yieldmo_test.go index 1d9426d0643..f89d4849a2c 100644 --- a/adapters/yieldmo/yieldmo_test.go +++ b/adapters/yieldmo/yieldmo_test.go @@ -3,9 +3,9 @@ package yieldmo import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/yieldmo/yieldmotest/exemplary/app-banner.json b/adapters/yieldmo/yieldmotest/exemplary/app-banner.json index e674cf11539..92609cae16a 100644 --- a/adapters/yieldmo/yieldmotest/exemplary/app-banner.json +++ b/adapters/yieldmo/yieldmotest/exemplary/app-banner.json @@ -65,7 +65,11 @@ "adm": "some-test-ad", "crid": "crid_10", "h": 250, - "w": 300 + "w": 300, + "ext": + { + "mediatype": "banner" + } } ] } @@ -87,7 +91,11 @@ "adm": "some-test-ad", "crid": "crid_10", "w": 300, - "h": 250 + "h": 250, + "ext": + { + "mediatype": "banner" + } }, "type": "banner" } diff --git a/adapters/yieldmo/yieldmotest/exemplary/app_video.json b/adapters/yieldmo/yieldmotest/exemplary/app_video.json index e33c37f69bf..6f94a30b0d8 100644 --- a/adapters/yieldmo/yieldmotest/exemplary/app_video.json +++ b/adapters/yieldmo/yieldmotest/exemplary/app_video.json @@ -63,7 +63,11 @@ "adm": "some-test-ad", "crid": "crid_10", "h": 250, - "w": 300 + "w": 300, + "ext": + { + "mediatype": "video" + } } ] } @@ -85,7 +89,11 @@ "adm": "some-test-ad", "crid": "crid_10", "w": 300, - "h": 250 + "h": 250, + "ext": + { + "mediatype": "video" + } }, "type": "video" } diff --git a/adapters/yieldmo/yieldmotest/exemplary/simple-banner.json b/adapters/yieldmo/yieldmotest/exemplary/simple-banner.json index 11739ca1d32..40943900980 100644 --- a/adapters/yieldmo/yieldmotest/exemplary/simple-banner.json +++ b/adapters/yieldmo/yieldmotest/exemplary/simple-banner.json @@ -21,6 +21,15 @@ ], "site": { "id": "fake-site-id" + }, + "regs": { + "coppa": 1, + "ext": { + "gdpr": 1, + "us_privacy": "uspConsentString", + "gpp": "gppString", + "gpp_sid": [6] + } } }, "httpCalls": [ @@ -47,6 +56,15 @@ ], "site": { "id": "fake-site-id" + }, + "regs": { + "coppa": 1, + "ext": { + "gdpr": 1, + "us_privacy": "uspConsentString", + "gpp": "gppString", + "gpp_sid": [6] + } } } }, @@ -65,7 +83,11 @@ "adm": "some-test-ad", "crid": "crid_10", "h": 250, - "w": 300 + "w": 300, + "ext": + { + "mediatype": "banner" + } } ] } @@ -87,7 +109,11 @@ "adm": "some-test-ad", "crid": "crid_10", "w": 300, - "h": 250 + "h": 250, + "ext": + { + "mediatype": "banner" + } }, "type": "banner" } diff --git a/adapters/yieldmo/yieldmotest/exemplary/simple_video.json b/adapters/yieldmo/yieldmotest/exemplary/simple_video.json index aaf53124365..ec65bc98b6c 100644 --- a/adapters/yieldmo/yieldmotest/exemplary/simple_video.json +++ b/adapters/yieldmo/yieldmotest/exemplary/simple_video.json @@ -63,7 +63,11 @@ "adm": "some-test-ad", "crid": "crid_10", "h": 250, - "w": 300 + "w": 300, + "ext": + { + "mediatype": "video" + } } ] } @@ -85,7 +89,11 @@ "adm": "some-test-ad", "crid": "crid_10", "w": 300, - "h": 250 + "h": 250, + "ext": + { + "mediatype": "video" + } }, "type": "video" } diff --git a/adapters/yieldmo/yieldmotest/exemplary/with_gpid.json b/adapters/yieldmo/yieldmotest/exemplary/with_gpid.json index bd9e911058a..d155f5db67f 100644 --- a/adapters/yieldmo/yieldmotest/exemplary/with_gpid.json +++ b/adapters/yieldmo/yieldmotest/exemplary/with_gpid.json @@ -73,7 +73,11 @@ "adm": "some-test-ad", "crid": "crid_10", "h": 250, - "w": 300 + "w": 300, + "ext": + { + "mediatype": "banner" + } } ] } @@ -95,7 +99,11 @@ "adm": "some-test-ad", "crid": "crid_10", "w": 300, - "h": 250 + "h": 250, + "ext": + { + "mediatype": "banner" + } }, "type": "banner" } diff --git a/adapters/yieldone/params_test.go b/adapters/yieldone/params_test.go index 6048ea5d7dc..623928839ef 100644 --- a/adapters/yieldone/params_test.go +++ b/adapters/yieldone/params_test.go @@ -2,8 +2,9 @@ package yieldone import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestValidParams(t *testing.T) { diff --git a/adapters/yieldone/yieldone.go b/adapters/yieldone/yieldone.go index 2d5f1d81173..1431396413d 100644 --- a/adapters/yieldone/yieldone.go +++ b/adapters/yieldone/yieldone.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type YieldoneAdapter struct { diff --git a/adapters/yieldone/yieldone_test.go b/adapters/yieldone/yieldone_test.go index 12d634d463d..1847ef9bf06 100644 --- a/adapters/yieldone/yieldone_test.go +++ b/adapters/yieldone/yieldone_test.go @@ -3,9 +3,9 @@ package yieldone import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { diff --git a/adapters/zeroclickfraud/zeroclickfraud.go b/adapters/zeroclickfraud/zeroclickfraud.go index 235f678d7bb..6f416d96785 100644 --- a/adapters/zeroclickfraud/zeroclickfraud.go +++ b/adapters/zeroclickfraud/zeroclickfraud.go @@ -7,12 +7,12 @@ import ( "strconv" "text/template" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type ZeroClickFraudAdapter struct { diff --git a/adapters/zeroclickfraud/zeroclickfraud_test.go b/adapters/zeroclickfraud/zeroclickfraud_test.go index e07c43ff7a2..6e2a7e23b4c 100644 --- a/adapters/zeroclickfraud/zeroclickfraud_test.go +++ b/adapters/zeroclickfraud/zeroclickfraud_test.go @@ -3,9 +3,9 @@ package zeroclickfraud import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adapters/zeta_global_ssp/zeta_global_ssp-test/exemplary/banner.json b/adapters/zeta_global_ssp/zeta_global_ssp-test/exemplary/banner.json index cdfaf37e45d..8e04138937c 100644 --- a/adapters/zeta_global_ssp/zeta_global_ssp-test/exemplary/banner.json +++ b/adapters/zeta_global_ssp/zeta_global_ssp-test/exemplary/banner.json @@ -44,7 +44,7 @@ "application/json;charset=utf-8" ] }, - "uri": "http://whatever.url", + "uri": "https://ssp.disqus.com/bid/prebid-server?sid=11", "body": { "id": "some-request-id", "imp": [ diff --git a/adapters/zeta_global_ssp/zeta_global_ssp-test/exemplary/no-bid.json b/adapters/zeta_global_ssp/zeta_global_ssp-test/exemplary/no-bid.json index 68aabbed257..3e1ee805552 100644 --- a/adapters/zeta_global_ssp/zeta_global_ssp-test/exemplary/no-bid.json +++ b/adapters/zeta_global_ssp/zeta_global_ssp-test/exemplary/no-bid.json @@ -44,7 +44,7 @@ "application/json;charset=utf-8" ] }, - "uri": "http://whatever.url", + "uri": "https://ssp.disqus.com/bid/prebid-server?sid=11", "body": { "id": "some-request-id", "imp": [ diff --git a/adapters/zeta_global_ssp/zeta_global_ssp-test/exemplary/video.json b/adapters/zeta_global_ssp/zeta_global_ssp-test/exemplary/video.json index 248f4b0487a..bc1e496cb27 100644 --- a/adapters/zeta_global_ssp/zeta_global_ssp-test/exemplary/video.json +++ b/adapters/zeta_global_ssp/zeta_global_ssp-test/exemplary/video.json @@ -34,7 +34,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://whatever.url", + "uri": "https://ssp.disqus.com/bid/prebid-server?sid=11", "headers": { "Content-Type": [ "application/json;charset=utf-8" diff --git a/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/bad-request.json b/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/bad-request.json index 38f3bd326d0..bf2af3bf25b 100644 --- a/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/bad-request.json +++ b/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/bad-request.json @@ -44,7 +44,7 @@ "application/json;charset=utf-8" ] }, - "uri": "http://whatever.url", + "uri": "https://ssp.disqus.com/bid/prebid-server?sid=11", "body": { "id": "some-request-id", "imp": [ diff --git a/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/invalid-bid-type.json b/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/invalid-bid-type.json index 4a55670720d..0aba5f2ca36 100644 --- a/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/invalid-bid-type.json +++ b/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/invalid-bid-type.json @@ -44,7 +44,7 @@ "application/json;charset=utf-8" ] }, - "uri": "http://whatever.url", + "uri": "https://ssp.disqus.com/bid/prebid-server?sid=11", "body": { "id": "some-request-id", "imp": [ diff --git a/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/no-bid-type.json.json b/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/no-bid-type.json.json index 1992586435f..1f5a92c4fdf 100644 --- a/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/no-bid-type.json.json +++ b/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/no-bid-type.json.json @@ -44,7 +44,7 @@ "application/json;charset=utf-8" ] }, - "uri": "http://whatever.url", + "uri": "https://ssp.disqus.com/bid/prebid-server?sid=11", "body": { "id": "some-request-id", "imp": [ diff --git a/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/server-error.json b/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/server-error.json index 037c7307889..27e71c31255 100644 --- a/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/server-error.json +++ b/adapters/zeta_global_ssp/zeta_global_ssp-test/supplemental/server-error.json @@ -44,7 +44,7 @@ "application/json;charset=utf-8" ] }, - "uri": "http://whatever.url", + "uri": "https://ssp.disqus.com/bid/prebid-server?sid=11", "body": { "id": "some-request-id", "imp": [ diff --git a/adapters/zeta_global_ssp/zeta_global_ssp.go b/adapters/zeta_global_ssp/zeta_global_ssp.go index 7a5f3395724..59af0d4d75a 100644 --- a/adapters/zeta_global_ssp/zeta_global_ssp.go +++ b/adapters/zeta_global_ssp/zeta_global_ssp.go @@ -5,11 +5,11 @@ import ( "fmt" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type adapter struct { diff --git a/adapters/zeta_global_ssp/zeta_global_ssp_test.go b/adapters/zeta_global_ssp/zeta_global_ssp_test.go index 3b7be288fa1..fabfa5efed8 100644 --- a/adapters/zeta_global_ssp/zeta_global_ssp_test.go +++ b/adapters/zeta_global_ssp/zeta_global_ssp_test.go @@ -3,14 +3,14 @@ package zeta_global_ssp import ( "testing" - "github.com/prebid/prebid-server/adapters/adapterstest" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestJsonSamples(t *testing.T) { bidder, buildErr := Builder(openrtb_ext.BidderZetaGlobalSsp, config.Adapter{ - Endpoint: "http://whatever.url"}, + Endpoint: "https://ssp.disqus.com/bid/prebid-server?sid=11"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) if buildErr != nil { diff --git a/adapters/zmaticoo/params_test.go b/adapters/zmaticoo/params_test.go new file mode 100644 index 00000000000..23c599e81b5 --- /dev/null +++ b/adapters/zmaticoo/params_test.go @@ -0,0 +1,48 @@ +package zmaticoo + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderZmaticoo, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected zmaticoo params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the zmaticoo schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderZmaticoo, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"pubId": "11233", "zoneId": "sin"}`, + `{"pubId": "11244", "zoneId": "iad"}`, +} + +var invalidParams = []string{ + `{"pubId": "11233"}`, + `{"zoneId": "aaa"}`, + `{"pubId": 123, "zoneId": "sin"}`, + `{"pubId": "", "zoneId": "iad"}`, + `{"pubId": "11233", "zoneId": ""}`, +} diff --git a/adapters/zmaticoo/zmaticoo.go b/adapters/zmaticoo/zmaticoo.go new file mode 100644 index 00000000000..8460f232fad --- /dev/null +++ b/adapters/zmaticoo/zmaticoo.go @@ -0,0 +1,150 @@ +package zmaticoo + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +type adapter struct { + endpoint string +} + +// Builder builds a new instance of the zmaticoo adapter for the given bidder with the given config. +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + return &adapter{ + endpoint: config.Endpoint, + }, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + adapterRequest, errs := a.makeRequest(request) + if errs != nil { + return nil, errs + } + return []*adapters.RequestData{adapterRequest}, nil + +} + +func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, []error) { + errs := validateZmaticooExt(request) + if errs != nil { + return nil, errs + } + err := transform(request) + if err != nil { + return nil, append(errs, err) + } + reqBody, err := json.Marshal(request) + if err != nil { + return nil, append(errs, err) + } + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + return &adapters.RequestData{ + Method: "POST", + Uri: a.endpoint, + Body: reqBody, + Headers: headers, + }, errs +} + +func transform(request *openrtb2.BidRequest) error { + for i, imp := range request.Imp { + if imp.Native != nil { + var nativeRequest map[string]interface{} + nativeCopyRequest := make(map[string]interface{}) + if err := json.Unmarshal([]byte(request.Imp[i].Native.Request), &nativeRequest); err != nil { + return err + } + _, exists := nativeRequest["native"] + if exists { + continue + } + nativeCopyRequest["native"] = nativeRequest + nativeReqByte, err := json.Marshal(nativeCopyRequest) + if err != nil { + return err + } + nativeCopy := *request.Imp[i].Native + nativeCopy.Request = string(nativeReqByte) + request.Imp[i].Native = &nativeCopy + } + } + return nil +} + +func validateZmaticooExt(request *openrtb2.BidRequest) []error { + var extImpZmaticoo openrtb_ext.ExtImpZmaticoo + var errs []error + for _, imp := range request.Imp { + var extBidder adapters.ExtImpBidder + err := json.Unmarshal(imp.Ext, &extBidder) + if err != nil { + errs = append(errs, err) + continue + } + err = json.Unmarshal(extBidder.Bidder, &extImpZmaticoo) + if err != nil { + errs = append(errs, err) + continue + } + if extImpZmaticoo.ZoneId == "" || extImpZmaticoo.PubId == "" { + errs = append(errs, fmt.Errorf("imp.ext.pubId or imp.ext.zoneId required")) + continue + } + } + return errs + +} + +// MakeBids make the bids for the bid response. +func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if adapters.IsResponseStatusCodeNoContent(response) { + return nil, nil + } + if err := adapters.CheckResponseStatusCodeForErrors(response); err != nil { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode), + }} + } + var bidResp openrtb2.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + var errs []error + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(internalRequest.Imp)) + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + mediaType, err := getMediaTypeForBid(sb.Bid[i]) + if err != nil { + errs = append(errs, err) + continue + } + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: mediaType, + }) + } + } + return bidResponse, errs +} + +func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { + switch bid.MType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + case openrtb2.MarkupNative: + return openrtb_ext.BidTypeNative, nil + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo, nil + default: + return "", fmt.Errorf("unrecognized bid type in response from zmaticoo for bid %s", bid.ImpID) + } +} diff --git a/adapters/zmaticoo/zmaticoo_test.go b/adapters/zmaticoo/zmaticoo_test.go new file mode 100644 index 00000000000..1effdd2afa7 --- /dev/null +++ b/adapters/zmaticoo/zmaticoo_test.go @@ -0,0 +1,20 @@ +package zmaticoo + +import ( + "testing" + + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderZmaticoo, config.Adapter{ + Endpoint: "https://bid.zmaticoo.com/prebid/bid"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "zmaticootest", bidder) +} diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/status_204.json b/adapters/zmaticoo/zmaticootest/exemplary/no-bid.json similarity index 78% rename from adapters/nanointeractive/nanointeractivetest/supplemental/status_204.json rename to adapters/zmaticoo/zmaticootest/exemplary/no-bid.json index ed4d8ff38b8..d0e5d17ed9a 100644 --- a/adapters/nanointeractive/nanointeractivetest/supplemental/status_204.json +++ b/adapters/zmaticoo/zmaticootest/exemplary/no-bid.json @@ -8,23 +8,23 @@ "format": [ { "w": 300, - "h": 250 + "h": 50 } ] }, "ext": { "bidder": { - "pid": "123" + "pubId": "fake-pub-id", + "zoneId": "sin" } } } ] }, - "httpCalls": [ { "expectedRequest": { - "uri": "https://ad.audiencemanager.de/hbs", + "uri": "https://bid.zmaticoo.com/prebid/bid", "body": { "id": "test-request-id", "imp": [ @@ -34,13 +34,14 @@ "format": [ { "w": 300, - "h": 250 + "h": 50 } ] }, "ext": { "bidder": { - "pid": "123" + "pubId": "fake-pub-id", + "zoneId": "sin" } } } @@ -53,6 +54,5 @@ } } ], - "expectedBidResponses": [] } diff --git a/adapters/zmaticoo/zmaticootest/exemplary/simple-banner.json b/adapters/zmaticoo/zmaticootest/exemplary/simple-banner.json new file mode 100644 index 00000000000..22d104ed0cd --- /dev/null +++ b/adapters/zmaticoo/zmaticootest/exemplary/simple-banner.json @@ -0,0 +1,93 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "pubId": "fake-pub-id", + "zoneId": "sin" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bid.zmaticoo.com/prebid/bid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "pubId": "fake-pub-id", + "zoneId": "sin" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "ttx", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some-ads", + "crid": "crid_testid", + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some-ads", + "crid": "crid_testid", + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/zmaticoo/zmaticootest/exemplary/simple-native-1.1.json b/adapters/zmaticoo/zmaticootest/exemplary/simple-native-1.1.json new file mode 100644 index 00000000000..307e3d873be --- /dev/null +++ b/adapters/zmaticoo/zmaticootest/exemplary/simple-native-1.1.json @@ -0,0 +1,83 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"native\":{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"required\":1,\"img\":{\"type\":3,\"wmin\":128,\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"]}},{\"id\":7,\"required\":1,\"data\":{\"type\":2,\"len\":120}}]}}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "pubId": "fake-pub-id", + "zoneId": "sin" + } + } + } + ] + }, + "httpcalls": [ + { + "expectedRequest": { + "uri": "https://bid.zmaticoo.com/prebid/bid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"native\":{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"required\":1,\"img\":{\"type\":3,\"wmin\":128,\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"]}},{\"id\":7,\"required\":1,\"data\":{\"type\":2,\"len\":120}}]}}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "pubId": "fake-pub-id", + "zoneId": "sin" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "8400d766-58b3-47d4-80d7-6658b337d403", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid", + "mtype": 4 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8400d766-58b3-47d4-80d7-6658b337d403", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid", + "mtype": 4 + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/zmaticoo/zmaticootest/exemplary/simple-native.json b/adapters/zmaticoo/zmaticootest/exemplary/simple-native.json new file mode 100644 index 00000000000..6cff9b86148 --- /dev/null +++ b/adapters/zmaticoo/zmaticootest/exemplary/simple-native.json @@ -0,0 +1,83 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"required\":1,\"img\":{\"type\":3,\"wmin\":128,\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"]}},{\"id\":7,\"required\":1,\"data\":{\"type\":2,\"len\":120}}]}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "pubId": "fake-pub-id", + "zoneId": "sin" + } + } + } + ] + }, + "httpcalls": [ + { + "expectedRequest": { + "uri": "https://bid.zmaticoo.com/prebid/bid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"native\":{\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"img\":{\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"],\"type\":3,\"wmin\":128},\"required\":1},{\"data\":{\"len\":120,\"type\":2},\"id\":7,\"required\":1}],\"context\":1,\"plcmtcnt\":1,\"plcmttype\":4,\"ver\":\"1.2\"}}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "pubId": "fake-pub-id", + "zoneId": "sin" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "8400d766-58b3-47d4-80d7-6658b337d403", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid", + "mtype": 4 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8400d766-58b3-47d4-80d7-6658b337d403", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid", + "mtype": 4 + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/zmaticoo/zmaticootest/exemplary/simple-video.json b/adapters/zmaticoo/zmaticootest/exemplary/simple-video.json new file mode 100644 index 00000000000..5ff93efa332 --- /dev/null +++ b/adapters/zmaticoo/zmaticootest/exemplary/simple-video.json @@ -0,0 +1,91 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 300, + "h": 250, + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "pubId": "fake-pub-id", + "zoneId": "sin" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bid.zmaticoo.com/prebid/bid", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 300, + "h": 250, + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "pubId": "fake-pub-id", + "zoneId": "sin" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "ttx", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some-ads", + "crid": "crid_testid", + "mtype": 2 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some-ads", + "crid": "crid_testid", + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/engagebdr/engagebdrtest/supplemental/no-imp-sspid.json b/adapters/zmaticoo/zmaticootest/supplemental/bad_imp_ext.json similarity index 58% rename from adapters/engagebdr/engagebdrtest/supplemental/no-imp-sspid.json rename to adapters/zmaticoo/zmaticootest/supplemental/bad_imp_ext.json index d193bf779fc..444e1e7a8d8 100644 --- a/adapters/engagebdr/engagebdrtest/supplemental/no-imp-sspid.json +++ b/adapters/zmaticoo/zmaticootest/supplemental/bad_imp_ext.json @@ -5,22 +5,17 @@ { "id": "test-imp-id", "banner": { - "w": 300, - "h": 250 + "format": [{"w": 300, "h": 50}] }, - "ext": { - "bidder": { - } - } + "ext": "aaa" } ] }, "expectedMakeRequestsErrors": [ { - "value": "Ignoring imp id=test-imp-id, no sspid present.", + "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder", "comparison": "literal" } ] } - diff --git a/adapters/engagebdr/engagebdrtest/supplemental/no-imp-ext-bidder.json b/adapters/zmaticoo/zmaticootest/supplemental/bad_imp_ext_bidder.json similarity index 53% rename from adapters/engagebdr/engagebdrtest/supplemental/no-imp-ext-bidder.json rename to adapters/zmaticoo/zmaticootest/supplemental/bad_imp_ext_bidder.json index 9aa8177fc8b..433bac5e564 100644 --- a/adapters/engagebdr/engagebdrtest/supplemental/no-imp-ext-bidder.json +++ b/adapters/zmaticoo/zmaticootest/supplemental/bad_imp_ext_bidder.json @@ -5,20 +5,23 @@ { "id": "test-imp-id", "banner": { - "w": 300, - "h": 250 + "format": [ + { + "w": 300, + "h": 50 + } + ] }, "ext": { + "bidder": "aa" } } ] }, - "expectedMakeRequestsErrors": [ { - "value": "Ignoring imp id=test-imp-id, error while decoding impExt, err: unexpected end of JSON input.", + "value": "json: cannot unmarshal string into Go value of type openrtb_ext.ExtImpZmaticoo", "comparison": "literal" } ] } - diff --git a/adapters/kubient/kubienttest/supplemental/bad_response.json b/adapters/zmaticoo/zmaticootest/supplemental/bad_response.json similarity index 73% rename from adapters/kubient/kubienttest/supplemental/bad_response.json rename to adapters/zmaticoo/zmaticootest/supplemental/bad_response.json index 832dc975088..9827081e41a 100644 --- a/adapters/kubient/kubienttest/supplemental/bad_response.json +++ b/adapters/zmaticoo/zmaticootest/supplemental/bad_response.json @@ -8,13 +8,14 @@ "format": [ { "w": 300, - "h": 250 + "h": 50 } ] }, "ext": { "bidder": { - "zoneid": "23" + "pubId": "fake-pub-id", + "zoneId": "sin" } } } @@ -23,7 +24,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "http://127.0.0.1:5000/bid", + "uri": "https://bid.zmaticoo.com/prebid/bid", "body": { "id": "test-request-id", "imp": [ @@ -33,13 +34,14 @@ "format": [ { "w": 300, - "h": 250 + "h": 50 } ] }, "ext": { "bidder": { - "zoneid": "23" + "pubId": "fake-pub-id", + "zoneId": "sin" } } } @@ -48,14 +50,14 @@ }, "mockResponse": { "status": 200, - "body": "{\"id\"data.lost" + "body": "{\"id\":test-request-id" } } ], "expectedMakeBidsErrors": [ { - "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse", - "comparison": "literal" + "comparison": "literal", + "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse" } ] } diff --git a/adapters/zmaticoo/zmaticootest/supplemental/empty_imp_ext_bidder.json b/adapters/zmaticoo/zmaticootest/supplemental/empty_imp_ext_bidder.json new file mode 100644 index 00000000000..43ee7a7214c --- /dev/null +++ b/adapters/zmaticoo/zmaticootest/supplemental/empty_imp_ext_bidder.json @@ -0,0 +1,30 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "pubId": "", + "zoneId": "" + } + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "imp.ext.pubId or imp.ext.zoneId required", + "comparison": "literal" + } + ] +} diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/status_400.json b/adapters/zmaticoo/zmaticootest/supplemental/status_400.json similarity index 73% rename from adapters/nanointeractive/nanointeractivetest/supplemental/status_400.json rename to adapters/zmaticoo/zmaticootest/supplemental/status_400.json index f02bd478656..a03b5e36e0a 100644 --- a/adapters/nanointeractive/nanointeractivetest/supplemental/status_400.json +++ b/adapters/zmaticoo/zmaticootest/supplemental/status_400.json @@ -8,23 +8,23 @@ "format": [ { "w": 300, - "h": 250 + "h": 50 } ] }, "ext": { "bidder": { - "pid": "123" + "pubId": "fake-pub-id", + "zoneId": "sin" } } } ] }, - "httpCalls": [ { "expectedRequest": { - "uri": "https://ad.audiencemanager.de/hbs", + "uri": "https://bid.zmaticoo.com/prebid/bid", "body": { "id": "test-request-id", "imp": [ @@ -34,13 +34,14 @@ "format": [ { "w": 300, - "h": 250 + "h": 50 } ] }, "ext": { "bidder": { - "pid": "123" + "pubId": "fake-pub-id", + "zoneId": "sin" } } } @@ -53,11 +54,10 @@ } } ], - "expectedMakeBidsErrors": [ { - "value": "Invalid request.", - "comparison": "literal" + "comparison": "literal", + "value": "Unexpected status code: 400." } ] } diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/status_418.json b/adapters/zmaticoo/zmaticootest/supplemental/status_500.json similarity index 71% rename from adapters/nanointeractive/nanointeractivetest/supplemental/status_418.json rename to adapters/zmaticoo/zmaticootest/supplemental/status_500.json index b7ed65da2af..732b2cb6e1e 100644 --- a/adapters/nanointeractive/nanointeractivetest/supplemental/status_418.json +++ b/adapters/zmaticoo/zmaticootest/supplemental/status_500.json @@ -8,23 +8,23 @@ "format": [ { "w": 300, - "h": 250 + "h": 50 } ] }, "ext": { "bidder": { - "pid": "123" + "pubId": "fake-pub-id", + "zoneId": "sin" } } } ] }, - "httpCalls": [ { "expectedRequest": { - "uri": "https://ad.audiencemanager.de/hbs", + "uri": "https://bid.zmaticoo.com/prebid/bid", "body": { "id": "test-request-id", "imp": [ @@ -34,13 +34,14 @@ "format": [ { "w": 300, - "h": 250 + "h": 50 } ] }, "ext": { "bidder": { - "pid": "123" + "pubId": "fake-pub-id", + "zoneId": "sin" } } } @@ -48,16 +49,15 @@ } }, "mockResponse": { - "status": 418, + "status": 500, "body": {} } } ], - "expectedMakeBidsErrors": [ { - "value": "unexpected HTTP status 418.", - "comparison": "literal" + "comparison": "literal", + "value": "Unexpected status code: 500." } ] } diff --git a/adservertargeting/adservertargeting.go b/adservertargeting/adservertargeting.go index 94d64579e66..d53af49f36c 100644 --- a/adservertargeting/adservertargeting.go +++ b/adservertargeting/adservertargeting.go @@ -6,8 +6,8 @@ import ( "strings" "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type DataSource string diff --git a/adservertargeting/adservertargeting_test.go b/adservertargeting/adservertargeting_test.go index 4a651fdd2be..14daa0beb26 100644 --- a/adservertargeting/adservertargeting_test.go +++ b/adservertargeting/adservertargeting_test.go @@ -5,9 +5,10 @@ import ( "net/url" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" ) @@ -42,7 +43,7 @@ func TestExtractAdServerTargeting(t *testing.T) { p := "https://www.test-url.com?ampkey=testAmpKey&data-override-height=400" u, _ := url.Parse(p) params := u.Query() - reqBytes, err := json.Marshal(r) + reqBytes, err := jsonutil.Marshal(r) assert.NoError(t, err, "unexpected req marshal error") res, warnings := collect(rw, reqBytes, params) @@ -248,7 +249,7 @@ func TestProcessAdServerTargetingFull(t *testing.T) { bidResponseExt := &openrtb_ext.ExtBidResponse{Warnings: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)} - reqBytes, err := json.Marshal(r) + reqBytes, err := jsonutil.Marshal(r) assert.NoError(t, err, "unexpected req marshal error") targetingKeyLen := 0 resResp := Apply(rw, reqBytes, resp, params, bidResponseExt, &targetingKeyLen) @@ -331,7 +332,7 @@ func TestProcessAdServerTargetingWarnings(t *testing.T) { bidResponseExt := &openrtb_ext.ExtBidResponse{Warnings: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)} - reqBytes, err := json.Marshal(r) + reqBytes, err := jsonutil.Marshal(r) assert.NoError(t, err, "unexpected req marshal error") resResp := Apply(rw, reqBytes, resp, params, bidResponseExt, nil) assert.Len(t, resResp.SeatBid, 2, "Incorrect response: seat bid number") diff --git a/adservertargeting/reqcache.go b/adservertargeting/reqcache.go index cb2edac9e4a..dfe429d5056 100644 --- a/adservertargeting/reqcache.go +++ b/adservertargeting/reqcache.go @@ -4,7 +4,8 @@ import ( "encoding/json" "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) type requestCache struct { @@ -24,7 +25,7 @@ func (reqImpCache *requestCache) GetImpsData() ([]json.RawMessage, error) { } var impsData []json.RawMessage - err = json.Unmarshal(imps, &impsData) + err = jsonutil.Unmarshal(imps, &impsData) if err != nil { return nil, err } @@ -48,7 +49,7 @@ func (bidsCache *bidsCache) GetBid(bidderName, bidId string, bid openrtb2.Bid) ( } _, bidExists := bidsCache.bids[bidderName][bidId] if !bidExists { - bidBytes, err := json.Marshal(bid) + bidBytes, err := jsonutil.Marshal(bid) if err != nil { return nil, err } diff --git a/adservertargeting/requestcache_test.go b/adservertargeting/requestcache_test.go index cbafe0f44eb..248f11a8441 100644 --- a/adservertargeting/requestcache_test.go +++ b/adservertargeting/requestcache_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" ) diff --git a/adservertargeting/requestlookup.go b/adservertargeting/requestlookup.go index debf19830db..d3284525b50 100644 --- a/adservertargeting/requestlookup.go +++ b/adservertargeting/requestlookup.go @@ -3,11 +3,12 @@ package adservertargeting import ( "encoding/json" "fmt" - "github.com/buger/jsonparser" - "github.com/pkg/errors" - "github.com/prebid/prebid-server/openrtb_ext" "net/url" "strings" + + "github.com/buger/jsonparser" + "github.com/pkg/errors" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func getAdServerTargeting(reqWrapper *openrtb_ext.RequestWrapper) ([]openrtb_ext.AdServerTarget, error) { diff --git a/adservertargeting/requestlookup_test.go b/adservertargeting/requestlookup_test.go index cd86364558e..3de9bd72322 100644 --- a/adservertargeting/requestlookup_test.go +++ b/adservertargeting/requestlookup_test.go @@ -5,8 +5,8 @@ import ( "net/url" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adservertargeting/respdataprocessor.go b/adservertargeting/respdataprocessor.go index 94a391d08bb..5ec305c1451 100644 --- a/adservertargeting/respdataprocessor.go +++ b/adservertargeting/respdataprocessor.go @@ -6,8 +6,9 @@ import ( "strings" "github.com/pkg/errors" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" jsonpatch "gopkg.in/evanphx/json-patch.v4" ) @@ -79,7 +80,7 @@ func buildBidExt(targetingData map[string]string, Targeting: targetingDataTruncated, }, } - bidExtTargeting, err := json.Marshal(bidExtTargetingData) + bidExtTargeting, err := jsonutil.Marshal(bidExtTargetingData) if err != nil { warnings = append(warnings, createWarning(err.Error())) return nil diff --git a/adservertargeting/respdataprocessor_test.go b/adservertargeting/respdataprocessor_test.go index 1118458e0f6..6970cfaceac 100644 --- a/adservertargeting/respdataprocessor_test.go +++ b/adservertargeting/respdataprocessor_test.go @@ -6,9 +6,9 @@ import ( "strings" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/adservertargeting/utils.go b/adservertargeting/utils.go index 8093a4b6974..136f7900e24 100644 --- a/adservertargeting/utils.go +++ b/adservertargeting/utils.go @@ -1,11 +1,12 @@ package adservertargeting import ( + "strings" + "github.com/buger/jsonparser" "github.com/pkg/errors" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "strings" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func splitAndGet(path string, data []byte, delimiter string) (string, error) { diff --git a/amp/parse.go b/amp/parse.go index 34f1a3cacb4..12663ee93bd 100644 --- a/amp/parse.go +++ b/amp/parse.go @@ -9,11 +9,11 @@ import ( tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/privacy/ccpa" + "github.com/prebid/prebid-server/v2/privacy/gdpr" ) // Params defines the parameters of an AMP request. @@ -73,7 +73,7 @@ func ReadPolicy(ampParams Params, pbsConfigGDPREnabled bool) (privacy.PolicyWrit warningMsg = validateTCf2ConsentString(ampParams.Consent) } case ConsentUSPrivacy: - rv = ccpa.ConsentWriter{ampParams.Consent} + rv = ccpa.ConsentWriter{Consent: ampParams.Consent} if ccpa.ValidateConsent(ampParams.Consent) { if parseGdprApplies(ampParams.GdprApplies) == 1 { // Log warning because AMP request comes with both a valid CCPA string and gdpr_applies set to true @@ -85,7 +85,7 @@ func ReadPolicy(ampParams Params, pbsConfigGDPREnabled bool) (privacy.PolicyWrit } default: if ccpa.ValidateConsent(ampParams.Consent) { - rv = ccpa.ConsentWriter{ampParams.Consent} + rv = ccpa.ConsentWriter{Consent: ampParams.Consent} if parseGdprApplies(ampParams.GdprApplies) == 1 { warningMsg = "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string" } diff --git a/amp/parse_test.go b/amp/parse_test.go index 9f981fd30e0..f2f097284c5 100644 --- a/amp/parse_test.go +++ b/amp/parse_test.go @@ -4,11 +4,11 @@ import ( "net/http" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/privacy/ccpa" + "github.com/prebid/prebid-server/v2/privacy/gdpr" "github.com/stretchr/testify/assert" ) @@ -284,18 +284,24 @@ func TestPrivacyReader(t *testing.T) { ampParams: Params{Consent: "1YYY"}, }, expected: expectedResults{ - policyWriter: ccpa.ConsentWriter{"1YYY"}, + policyWriter: ccpa.ConsentWriter{Consent: "1YYY"}, warning: nil, }, }, { desc: "No consent type, valid CCPA consent string and gdpr_applies set to true: expect a CCPA consent writer and a warning", in: testInput{ - ampParams: Params{Consent: "1YYY", GdprApplies: &boolTrue}, + ampParams: Params{ + Consent: "1YYY", + GdprApplies: &boolTrue, + }, }, expected: expectedResults{ - policyWriter: ccpa.ConsentWriter{"1YYY"}, - warning: &errortypes.Warning{Message: "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string", WarningCode: errortypes.InvalidPrivacyConsentWarningCode}, + policyWriter: ccpa.ConsentWriter{Consent: "1YYY"}, + warning: &errortypes.Warning{ + Message: "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string", + WarningCode: errortypes.InvalidPrivacyConsentWarningCode, + }, }, }, { @@ -304,8 +310,11 @@ func TestPrivacyReader(t *testing.T) { ampParams: Params{Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA"}, }, expected: expectedResults{ - policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8One}, - warning: nil, + policyWriter: gdpr.ConsentWriter{ + Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", + RegExtGDPR: &int8One, + }, + warning: nil, }, }, }, @@ -316,41 +325,63 @@ func TestPrivacyReader(t *testing.T) { { desc: "Unrecognized consent type was specified and invalid consent string provided: expect nil policy writer and a warning", in: testInput{ - ampParams: Params{ConsentType: 101, Consent: "NOT_CCPA_NOR_GDPR_TCF2"}, + ampParams: Params{ + ConsentType: 101, + Consent: "NOT_CCPA_NOR_GDPR_TCF2", + }, }, expected: expectedResults{ policyWriter: privacy.NilPolicyWriter{}, - warning: &errortypes.Warning{Message: "Consent string 'NOT_CCPA_NOR_GDPR_TCF2' is not recognized as one of the supported formats CCPA or TCF2.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode}, + warning: &errortypes.Warning{ + Message: "Consent string 'NOT_CCPA_NOR_GDPR_TCF2' is not recognized as one of the supported formats CCPA or TCF2.", + WarningCode: errortypes.InvalidPrivacyConsentWarningCode, + }, }, }, { desc: "Unrecognized consent type specified but query params come with a valid CCPA consent string: expect a CCPA consent writer and no error nor warning", in: testInput{ - ampParams: Params{ConsentType: 101, Consent: "1YYY"}, + ampParams: Params{ + ConsentType: 101, + Consent: "1YYY", + }, }, expected: expectedResults{ - policyWriter: ccpa.ConsentWriter{"1YYY"}, + policyWriter: ccpa.ConsentWriter{Consent: "1YYY"}, warning: nil, }, }, { desc: "Unrecognized consent type, valid CCPA consent string and gdpr_applies set to true: expect a CCPA consent writer and a warning", in: testInput{ - ampParams: Params{ConsentType: 101, Consent: "1YYY", GdprApplies: &boolTrue}, + ampParams: Params{ + ConsentType: 101, + Consent: "1YYY", + GdprApplies: &boolTrue, + }, }, expected: expectedResults{ - policyWriter: ccpa.ConsentWriter{"1YYY"}, - warning: &errortypes.Warning{Message: "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string", WarningCode: errortypes.InvalidPrivacyConsentWarningCode}, + policyWriter: ccpa.ConsentWriter{Consent: "1YYY"}, + warning: &errortypes.Warning{ + Message: "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string", + WarningCode: errortypes.InvalidPrivacyConsentWarningCode, + }, }, }, { desc: "Unrecognized consent type, valid TCF2 consent string and gdpr_applies not set: expect GDPR consent writer and no error nor warning", in: testInput{ - ampParams: Params{ConsentType: 101, Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA"}, + ampParams: Params{ + ConsentType: 101, + Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", + }, }, expected: expectedResults{ - policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8One}, - warning: nil, + policyWriter: gdpr.ConsentWriter{ + Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", + RegExtGDPR: &int8One, + }, + warning: nil, }, }, }, @@ -361,51 +392,91 @@ func TestPrivacyReader(t *testing.T) { { desc: "GDPR consent string is invalid, but consent type is TCF2: return a valid GDPR writer and warn about the GDPR string being invalid", in: testInput{ - ampParams: Params{Consent: "INVALID_GDPR", ConsentType: ConsentTCF2, GdprApplies: nil}, + ampParams: Params{ + Consent: "INVALID_GDPR", + ConsentType: ConsentTCF2, + GdprApplies: nil, + }, }, expected: expectedResults{ - policyWriter: gdpr.ConsentWriter{"INVALID_GDPR", &int8One}, - warning: &errortypes.Warning{Message: "Consent string 'INVALID_GDPR' is not a valid TCF2 consent string.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode}, + policyWriter: gdpr.ConsentWriter{ + Consent: "INVALID_GDPR", + RegExtGDPR: &int8One, + }, + warning: &errortypes.Warning{ + Message: "Consent string 'INVALID_GDPR' is not a valid TCF2 consent string.", + WarningCode: errortypes.InvalidPrivacyConsentWarningCode, + }, }, }, { desc: "GDPR consent string is invalid, consent type is TCF2, gdpr_applies is set to true: return a valid GDPR writer and warn about the GDPR string being invalid", in: testInput{ - ampParams: Params{Consent: "INVALID_GDPR", ConsentType: ConsentTCF2, GdprApplies: &boolFalse}, + ampParams: Params{ + Consent: "INVALID_GDPR", + ConsentType: ConsentTCF2, + GdprApplies: &boolFalse, + }, }, expected: expectedResults{ - policyWriter: gdpr.ConsentWriter{"INVALID_GDPR", &int8Zero}, - warning: &errortypes.Warning{Message: "Consent string 'INVALID_GDPR' is not a valid TCF2 consent string.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode}, + policyWriter: gdpr.ConsentWriter{ + Consent: "INVALID_GDPR", + RegExtGDPR: &int8Zero, + }, + warning: &errortypes.Warning{ + Message: "Consent string 'INVALID_GDPR' is not a valid TCF2 consent string.", + WarningCode: errortypes.InvalidPrivacyConsentWarningCode, + }, }, }, { desc: "Valid GDPR consent string, gdpr_applies is set to false, return a valid GDPR writer, no warning", in: testInput{ - ampParams: Params{Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", ConsentType: ConsentTCF2, GdprApplies: &boolFalse}, + ampParams: Params{ + Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", + ConsentType: ConsentTCF2, + GdprApplies: &boolFalse, + }, }, expected: expectedResults{ - policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8Zero}, - warning: nil, + policyWriter: gdpr.ConsentWriter{ + Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", + RegExtGDPR: &int8Zero, + }, + warning: nil, }, }, { desc: "Valid GDPR consent string, gdpr_applies is set to true, return a valid GDPR writer and no warning", in: testInput{ - ampParams: Params{Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", ConsentType: ConsentTCF2, GdprApplies: &boolTrue}, + ampParams: Params{ + Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", + ConsentType: ConsentTCF2, + GdprApplies: &boolTrue, + }, }, expected: expectedResults{ - policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8One}, - warning: nil, + policyWriter: gdpr.ConsentWriter{ + Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", + RegExtGDPR: &int8One, + }, + warning: nil, }, }, { desc: "Valid GDPR consent string, return a valid GDPR writer and no warning", in: testInput{ - ampParams: Params{Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", ConsentType: ConsentTCF2}, + ampParams: Params{ + Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", + ConsentType: ConsentTCF2, + }, }, expected: expectedResults{ - policyWriter: gdpr.ConsentWriter{"CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", &int8One}, - warning: nil, + policyWriter: gdpr.ConsentWriter{ + Consent: "CPdiPIJPdiPIJACABBENAzCv_____3___wAAAQNd_X9cAAAAAAAA", + RegExtGDPR: &int8One, + }, + warning: nil, }, }, }, @@ -416,30 +487,46 @@ func TestPrivacyReader(t *testing.T) { { desc: "CCPA consent string is invalid: return a valid writer a warning about the string being invalid", in: testInput{ - ampParams: Params{Consent: "XXXX", ConsentType: ConsentUSPrivacy}, + ampParams: Params{ + Consent: "XXXX", + ConsentType: ConsentUSPrivacy, + }, }, expected: expectedResults{ - policyWriter: ccpa.ConsentWriter{"XXXX"}, - warning: &errortypes.Warning{Message: "Consent string 'XXXX' is not a valid CCPA consent string.", WarningCode: errortypes.InvalidPrivacyConsentWarningCode}, + policyWriter: ccpa.ConsentWriter{Consent: "XXXX"}, + warning: &errortypes.Warning{ + Message: "Consent string 'XXXX' is not a valid CCPA consent string.", + WarningCode: errortypes.InvalidPrivacyConsentWarningCode, + }, }, }, { desc: "Valid CCPA consent string, gdpr_applies is set to true: return a valid GDPR writer and warn about the gdpr_applies value.", in: testInput{ - ampParams: Params{Consent: "1YYY", ConsentType: ConsentUSPrivacy, GdprApplies: &boolTrue}, + ampParams: Params{ + Consent: "1YYY", + ConsentType: ConsentUSPrivacy, + GdprApplies: &boolTrue, + }, }, expected: expectedResults{ - policyWriter: ccpa.ConsentWriter{"1YYY"}, - warning: &errortypes.Warning{Message: "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string", WarningCode: errortypes.InvalidPrivacyConsentWarningCode}, + policyWriter: ccpa.ConsentWriter{Consent: "1YYY"}, + warning: &errortypes.Warning{ + Message: "AMP request gdpr_applies value was ignored because provided consent string is a CCPA consent string", + WarningCode: errortypes.InvalidPrivacyConsentWarningCode, + }, }, }, { desc: "Valid CCPA consent string, return a valid GDPR writer and no warning", in: testInput{ - ampParams: Params{Consent: "1YYY", ConsentType: ConsentUSPrivacy}, + ampParams: Params{ + Consent: "1YYY", + ConsentType: ConsentUSPrivacy, + }, }, expected: expectedResults{ - policyWriter: ccpa.ConsentWriter{"1YYY"}, + policyWriter: ccpa.ConsentWriter{Consent: "1YYY"}, warning: nil, }, }, @@ -469,19 +556,34 @@ func TestBuildGdprTCF2ConsentWriter(t *testing.T) { expectedWriter gdpr.ConsentWriter }{ { - desc: "gdpr_applies not set", - inParams: Params{Consent: consentString}, - expectedWriter: gdpr.ConsentWriter{consentString, &int8One}, + desc: "gdpr_applies not set", + inParams: Params{Consent: consentString}, + expectedWriter: gdpr.ConsentWriter{ + Consent: consentString, + RegExtGDPR: &int8One, + }, }, { - desc: "gdpr_applies set to false", - inParams: Params{Consent: consentString, GdprApplies: &boolFalse}, - expectedWriter: gdpr.ConsentWriter{consentString, &int8Zero}, + desc: "gdpr_applies set to false", + inParams: Params{ + Consent: consentString, + GdprApplies: &boolFalse, + }, + expectedWriter: gdpr.ConsentWriter{ + Consent: consentString, + RegExtGDPR: &int8Zero, + }, }, { - desc: "gdpr_applies set to true", - inParams: Params{Consent: consentString, GdprApplies: &boolTrue}, - expectedWriter: gdpr.ConsentWriter{consentString, &int8One}, + desc: "gdpr_applies set to true", + inParams: Params{ + Consent: consentString, + GdprApplies: &boolTrue, + }, + expectedWriter: gdpr.ConsentWriter{ + Consent: consentString, + RegExtGDPR: &int8One, + }, }, } for _, tc := range testCases { diff --git a/analytics/build/build.go b/analytics/build/build.go new file mode 100644 index 00000000000..f499a921b6f --- /dev/null +++ b/analytics/build/build.go @@ -0,0 +1,136 @@ +package build + +import ( + "github.com/benbjohnson/clock" + "github.com/golang/glog" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/analytics/clients" + "github.com/prebid/prebid-server/v2/analytics/filesystem" + "github.com/prebid/prebid-server/v2/analytics/pubmatic" + "github.com/prebid/prebid-server/v2/analytics/pubstack" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/ortb" + "github.com/prebid/prebid-server/v2/privacy" +) + +// Modules that need to be logged to need to be initialized here +func New(analytics *config.Analytics) analytics.Runner { + modules := make(enabledAnalytics, 0) + if len(analytics.File.Filename) > 0 { + if mod, err := filesystem.NewFileLogger(analytics.File.Filename); err == nil { + modules["filelogger"] = mod + } else { + glog.Fatalf("Could not initialize FileLogger for file %v :%v", analytics.File.Filename, err) + } + } + + if analytics.Pubstack.Enabled { + pubstackModule, err := pubstack.NewModule( + clients.GetDefaultHttpInstance(), + analytics.Pubstack.ScopeId, + analytics.Pubstack.IntakeUrl, + analytics.Pubstack.ConfRefresh, + analytics.Pubstack.Buffers.EventCount, + analytics.Pubstack.Buffers.BufferSize, + analytics.Pubstack.Buffers.Timeout, + clock.New()) + if err == nil { + modules["pubstack"] = pubstackModule + } else { + glog.Errorf("Could not initialize PubstackModule: %v", err) + } + } + if analytics.PubMatic.Enabled { + modules["pubmatic"] = pubmatic.NewHTTPLogger(analytics.PubMatic) + } + return modules +} + +// Collection of all the correctly configured analytics modules - implements the PBSAnalyticsModule interface +type enabledAnalytics map[string]analytics.Module + +func (ea enabledAnalytics) LogAuctionObject(ao *analytics.AuctionObject, ac privacy.ActivityControl) { + for name, module := range ea { + if isAllowed, cloneBidderReq := evaluateActivities(ao.RequestWrapper, ac, name); isAllowed { + if cloneBidderReq != nil { + ao.RequestWrapper = cloneBidderReq + } + module.LogAuctionObject(ao) + } + } +} + +func (ea enabledAnalytics) LogVideoObject(vo *analytics.VideoObject, ac privacy.ActivityControl) { + for name, module := range ea { + if isAllowed, cloneBidderReq := evaluateActivities(vo.RequestWrapper, ac, name); isAllowed { + if cloneBidderReq != nil { + vo.RequestWrapper = cloneBidderReq + } + module.LogVideoObject(vo) + } + + } +} + +func (ea enabledAnalytics) LogCookieSyncObject(cso *analytics.CookieSyncObject) { + for _, module := range ea { + module.LogCookieSyncObject(cso) + } +} + +func (ea enabledAnalytics) LogSetUIDObject(so *analytics.SetUIDObject) { + for _, module := range ea { + module.LogSetUIDObject(so) + } +} + +func (ea enabledAnalytics) LogAmpObject(ao *analytics.AmpObject, ac privacy.ActivityControl) { + for name, module := range ea { + if isAllowed, cloneBidderReq := evaluateActivities(ao.RequestWrapper, ac, name); isAllowed { + if cloneBidderReq != nil { + ao.RequestWrapper = cloneBidderReq + } + module.LogAmpObject(ao) + } + } +} + +func (ea enabledAnalytics) LogNotificationEventObject(ne *analytics.NotificationEvent, ac privacy.ActivityControl) { + for name, module := range ea { + component := privacy.Component{Type: privacy.ComponentTypeAnalytics, Name: name} + if ac.Allow(privacy.ActivityReportAnalytics, component, privacy.ActivityRequest{}) { + module.LogNotificationEventObject(ne) + } + } +} + +func evaluateActivities(rw *openrtb_ext.RequestWrapper, ac privacy.ActivityControl, componentName string) (bool, *openrtb_ext.RequestWrapper) { + // returned nil request wrapper means that request wrapper was not modified by activities and doesn't have to be changed in analytics object + // it is needed in order to use one function for all analytics objects with RequestWrapper + component := privacy.Component{Type: privacy.ComponentTypeAnalytics, Name: componentName} + if !ac.Allow(privacy.ActivityReportAnalytics, component, privacy.ActivityRequest{}) { + return false, nil + } + blockUserFPD := !ac.Allow(privacy.ActivityTransmitUserFPD, component, privacy.ActivityRequest{}) + blockPreciseGeo := !ac.Allow(privacy.ActivityTransmitPreciseGeo, component, privacy.ActivityRequest{}) + + if !blockUserFPD && !blockPreciseGeo { + return true, nil + } + + cloneReq := &openrtb_ext.RequestWrapper{ + BidRequest: ortb.CloneBidRequestPartial(rw.BidRequest), + } + + if blockUserFPD { + privacy.ScrubUserFPD(cloneReq) + } + if blockPreciseGeo { + ipConf := privacy.IPConf{IPV6: ac.IPv6Config, IPV4: ac.IPv4Config} + privacy.ScrubGeoAndDeviceIP(cloneReq, ipConf) + } + + cloneReq.RebuildRequest() + return true, cloneReq +} diff --git a/analytics/build/build_test.go b/analytics/build/build_test.go new file mode 100644 index 00000000000..d9b433cec4b --- /dev/null +++ b/analytics/build/build_test.go @@ -0,0 +1,357 @@ +package build + +import ( + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/iputil" + + "net/http" + "os" + "testing" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/stretchr/testify/assert" +) + +const TEST_DIR string = "testFiles" + +func TestSampleModule(t *testing.T) { + var count int + am := initAnalytics(&count) + am.LogAuctionObject(&analytics.AuctionObject{ + Status: http.StatusOK, + Errors: nil, + Response: &openrtb2.BidResponse{}, + }, privacy.ActivityControl{}) + if count != 1 { + t.Errorf("PBSAnalyticsModule failed at LogAuctionObject") + } + + am.LogSetUIDObject(&analytics.SetUIDObject{ + Status: http.StatusOK, + Bidder: "bidders string", + UID: "uid", + Errors: nil, + Success: true, + }) + if count != 2 { + t.Errorf("PBSAnalyticsModule failed at LogSetUIDObject") + } + + am.LogCookieSyncObject(&analytics.CookieSyncObject{}) + if count != 3 { + t.Errorf("PBSAnalyticsModule failed at LogCookieSyncObject") + } + + am.LogAmpObject(&analytics.AmpObject{}, privacy.ActivityControl{}) + if count != 4 { + t.Errorf("PBSAnalyticsModule failed at LogAmpObject") + } + + am.LogVideoObject(&analytics.VideoObject{}, privacy.ActivityControl{}) + if count != 5 { + t.Errorf("PBSAnalyticsModule failed at LogVideoObject") + } + + am.LogNotificationEventObject(&analytics.NotificationEvent{}, privacy.ActivityControl{}) + if count != 6 { + t.Errorf("PBSAnalyticsModule failed at LogNotificationEventObject") + } +} + +type sampleModule struct { + count *int +} + +func (m *sampleModule) LogAuctionObject(ao *analytics.AuctionObject) { *m.count++ } + +func (m *sampleModule) LogVideoObject(vo *analytics.VideoObject) { *m.count++ } + +func (m *sampleModule) LogCookieSyncObject(cso *analytics.CookieSyncObject) { *m.count++ } + +func (m *sampleModule) LogSetUIDObject(so *analytics.SetUIDObject) { *m.count++ } + +func (m *sampleModule) LogAmpObject(ao *analytics.AmpObject) { *m.count++ } + +func (m *sampleModule) LogNotificationEventObject(ne *analytics.NotificationEvent) { *m.count++ } + +func initAnalytics(count *int) analytics.Runner { + modules := make(enabledAnalytics, 0) + modules["sampleModule"] = &sampleModule{count} + return &modules +} + +func TestNewPBSAnalytics(t *testing.T) { + pbsAnalytics := New(&config.Analytics{}) + instance := pbsAnalytics.(enabledAnalytics) + + assert.Equal(t, len(instance), 0) +} + +func TestNewPBSAnalytics_FileLogger(t *testing.T) { + if _, err := os.Stat(TEST_DIR); os.IsNotExist(err) { + if err = os.MkdirAll(TEST_DIR, 0755); err != nil { + t.Fatalf("Could not create test directory for FileLogger") + } + } + defer os.RemoveAll(TEST_DIR) + mod := New(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}}) + switch modType := mod.(type) { + case enabledAnalytics: + if len(enabledAnalytics(modType)) != 1 { + t.Fatalf("Failed to add analytics module") + } + default: + t.Fatalf("Failed to initialize analytics module") + } + + pbsAnalytics := New(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}}) + instance := pbsAnalytics.(enabledAnalytics) + + assert.Equal(t, len(instance), 1) +} + +func TestNewPBSAnalytics_Pubstack(t *testing.T) { + + pbsAnalyticsWithoutError := New(&config.Analytics{ + Pubstack: config.Pubstack{ + Enabled: true, + ScopeId: "scopeId", + IntakeUrl: "https://pubstack.io/intake", + Buffers: config.PubstackBuffer{ + BufferSize: "100KB", + EventCount: 0, + Timeout: "30s", + }, + ConfRefresh: "2h", + }, + }) + instanceWithoutError := pbsAnalyticsWithoutError.(enabledAnalytics) + + assert.Equal(t, len(instanceWithoutError), 1) + + pbsAnalyticsWithError := New(&config.Analytics{ + Pubstack: config.Pubstack{ + Enabled: true, + }, + }) + instanceWithError := pbsAnalyticsWithError.(enabledAnalytics) + assert.Equal(t, len(instanceWithError), 0) +} + +func TestSampleModuleActivitiesAllowed(t *testing.T) { + var count int + am := initAnalytics(&count) + + acAllowed := privacy.NewActivityControl(getActivityConfig("sampleModule", true, true, true)) + + ao := &analytics.AuctionObject{ + Status: http.StatusOK, + Errors: nil, + Response: &openrtb2.BidResponse{}, + } + + am.LogAuctionObject(ao, acAllowed) + if count != 1 { + t.Errorf("PBSAnalyticsModule failed at LogAuctionObject") + } + + am.LogAmpObject(&analytics.AmpObject{}, acAllowed) + if count != 2 { + t.Errorf("PBSAnalyticsModule failed at LogAmpObject") + } + + am.LogVideoObject(&analytics.VideoObject{}, acAllowed) + if count != 3 { + t.Errorf("PBSAnalyticsModule failed at LogVideoObject") + } + + am.LogNotificationEventObject(&analytics.NotificationEvent{}, acAllowed) + if count != 4 { + t.Errorf("PBSAnalyticsModule failed at LogNotificationEventObject") + } +} + +func TestSampleModuleActivitiesAllowedAndDenied(t *testing.T) { + var count int + am := initAnalytics(&count) + + acAllowed := privacy.NewActivityControl(getActivityConfig("sampleModule", true, false, true)) + + rw := &openrtb_ext.RequestWrapper{BidRequest: getDefaultBidRequest()} + ao := &analytics.AuctionObject{ + RequestWrapper: rw, + Status: http.StatusOK, + Errors: nil, + Response: &openrtb2.BidResponse{}, + } + + am.LogAuctionObject(ao, acAllowed) + if count != 1 { + t.Errorf("PBSAnalyticsModule failed at LogAuctionObject") + } + + am.LogAmpObject(&analytics.AmpObject{RequestWrapper: rw}, acAllowed) + if count != 2 { + t.Errorf("PBSAnalyticsModule failed at LogAmpObject") + } + + am.LogVideoObject(&analytics.VideoObject{RequestWrapper: rw}, acAllowed) + if count != 3 { + t.Errorf("PBSAnalyticsModule failed at LogVideoObject") + } + + am.LogNotificationEventObject(&analytics.NotificationEvent{}, acAllowed) + if count != 4 { + t.Errorf("PBSAnalyticsModule failed at LogNotificationEventObject") + } +} + +func TestSampleModuleActivitiesDenied(t *testing.T) { + var count int + am := initAnalytics(&count) + + acDenied := privacy.NewActivityControl(getActivityConfig("sampleModule", false, true, true)) + + ao := &analytics.AuctionObject{ + Status: http.StatusOK, + Errors: nil, + Response: &openrtb2.BidResponse{}, + } + + am.LogAuctionObject(ao, acDenied) + if count != 0 { + t.Errorf("PBSAnalyticsModule failed at LogAuctionObject") + } + + am.LogAmpObject(&analytics.AmpObject{}, acDenied) + if count != 0 { + t.Errorf("PBSAnalyticsModule failed at LogAmpObject") + } + + am.LogVideoObject(&analytics.VideoObject{}, acDenied) + if count != 0 { + t.Errorf("PBSAnalyticsModule failed at LogVideoObject") + } + + am.LogNotificationEventObject(&analytics.NotificationEvent{}, acDenied) + if count != 0 { + t.Errorf("PBSAnalyticsModule failed at LogNotificationEventObject") + } +} + +func TestEvaluateActivities(t *testing.T) { + testCases := []struct { + description string + givenActivityControl privacy.ActivityControl + expectedRequest *openrtb_ext.RequestWrapper + expectedAllowActivities bool + }{ + { + description: "all blocked", + givenActivityControl: privacy.NewActivityControl(getActivityConfig("sampleModule", false, false, false)), + expectedRequest: nil, + expectedAllowActivities: false, + }, + { + description: "all allowed", + givenActivityControl: privacy.NewActivityControl(getActivityConfig("sampleModule", true, true, true)), + expectedRequest: nil, + expectedAllowActivities: true, + }, + + { + description: "ActivityTransmitUserFPD and ActivityTransmitPreciseGeo disabled", + givenActivityControl: privacy.NewActivityControl(getActivityConfig("sampleModule", true, false, false)), + expectedRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ID: "test_request", User: &openrtb2.User{ID: ""}, Device: &openrtb2.Device{IFA: "", IP: "127.0.0.0"}}, + }, + expectedAllowActivities: true, + }, + { + description: "ActivityTransmitUserFPD enabled, ActivityTransmitPreciseGeo disabled", + givenActivityControl: privacy.NewActivityControl(getActivityConfig("sampleModule", true, true, false)), + expectedRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ID: "test_request", User: &openrtb2.User{ID: "user-id"}, Device: &openrtb2.Device{IFA: "device-ifa", IP: "127.0.0.0"}}, + }, + expectedAllowActivities: true, + }, + } + + for _, test := range testCases { + t.Run(test.description, func(t *testing.T) { + rw := &openrtb_ext.RequestWrapper{BidRequest: getDefaultBidRequest()} + resActivityAllowed, resRequest := evaluateActivities(rw, test.givenActivityControl, "sampleModule") + assert.Equal(t, test.expectedAllowActivities, resActivityAllowed) + if test.expectedRequest != nil { + assert.Equal(t, test.expectedRequest.User.ID, resRequest.User.ID) + assert.Equal(t, test.expectedRequest.Device.IFA, resRequest.Device.IFA) + assert.Equal(t, test.expectedRequest.Device.IP, resRequest.Device.IP) + } else { + assert.Nil(t, resRequest) + } + + }) + } + +} + +func getDefaultBidRequest() *openrtb2.BidRequest { + return &openrtb2.BidRequest{ + ID: "test_request", + User: &openrtb2.User{ID: "user-id"}, + Device: &openrtb2.Device{IFA: "device-ifa", IP: "127.0.0.1"}} + +} + +func getActivityConfig(componentName string, allowReportAnalytics, allowTransmitUserFPD, allowTransmitPreciseGeo bool) *config.AccountPrivacy { + return &config.AccountPrivacy{ + AllowActivities: &config.AllowActivities{ + ReportAnalytics: config.Activity{ + Default: ptrutil.ToPtr(true), + Rules: []config.ActivityRule{ + { + Allow: allowReportAnalytics, + Condition: config.ActivityCondition{ + ComponentName: []string{componentName}, + ComponentType: []string{"analytics"}, + }, + }, + }, + }, + TransmitUserFPD: config.Activity{ + Default: ptrutil.ToPtr(true), + Rules: []config.ActivityRule{ + { + Allow: allowTransmitUserFPD, + Condition: config.ActivityCondition{ + ComponentName: []string{componentName}, + ComponentType: []string{"analytics"}, + }, + }, + }, + }, + TransmitPreciseGeo: config.Activity{ + Default: ptrutil.ToPtr(true), + Rules: []config.ActivityRule{ + { + Allow: allowTransmitPreciseGeo, + Condition: config.ActivityCondition{ + ComponentName: []string{componentName}, + ComponentType: []string{"analytics"}, + }, + }, + }, + }, + }, + IPv4Config: config.IPv4{ + AnonKeepBits: iputil.IPv4DefaultMaskingBitSize, + }, + IPv6Config: config.IPv6{ + AnonKeepBits: iputil.IPv6DefaultMaskingBitSize, + }, + } +} diff --git a/analytics/config/config_ow.go b/analytics/build/config_ow.go similarity index 51% rename from analytics/config/config_ow.go rename to analytics/build/config_ow.go index 13ebb463ef8..faf43d3d370 100644 --- a/analytics/config/config_ow.go +++ b/analytics/build/config_ow.go @@ -1,20 +1,20 @@ -package config +package build import ( "fmt" - "github.com/prebid/prebid-server/analytics" + "github.com/prebid/prebid-server/v2/analytics" ) // EnableAnalyticsModule will add the new module into the list of enabled analytics modules -var EnableAnalyticsModule = func(module analytics.PBSAnalyticsModule, moduleList analytics.PBSAnalyticsModule) (analytics.PBSAnalyticsModule, error) { +var EnableAnalyticsModule = func(module analytics.Module, moduleList analytics.Runner) (analytics.Runner, error) { if module == nil { return nil, fmt.Errorf("module to be added is nil") } enabledModuleList, ok := moduleList.(enabledAnalytics) if !ok { - return nil, fmt.Errorf("failed to convert moduleList interface from analytics.PBSAnalyticsModule to analytics.enabledAnalytics") + return nil, fmt.Errorf("failed to convert moduleList interface from analytics.Module to analytics.enabledAnalytics") } - enabledModuleList = append(enabledModuleList, module) + enabledModuleList["pubstack"] = module return enabledModuleList, nil } diff --git a/analytics/config/config_ow_test.go b/analytics/build/config_ow_test.go similarity index 83% rename from analytics/config/config_ow_test.go rename to analytics/build/config_ow_test.go index a8e7d928b8d..c85328c9436 100644 --- a/analytics/config/config_ow_test.go +++ b/analytics/build/config_ow_test.go @@ -1,11 +1,11 @@ -package config +package build import ( "errors" "testing" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/analytics/filesystem" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/analytics/filesystem" "github.com/stretchr/testify/assert" ) @@ -18,8 +18,8 @@ func TestEnableAnalyticsModule(t *testing.T) { } type arg struct { - moduleList analytics.PBSAnalyticsModule - module analytics.PBSAnalyticsModule + moduleList analytics.Runner + module analytics.Module } type want struct { @@ -35,7 +35,7 @@ func TestEnableAnalyticsModule(t *testing.T) { { description: "add non-nil module to nil module-list", args: arg{moduleList: nil, module: file}, - wants: want{len: 0, error: errors.New("failed to convert moduleList interface from analytics.PBSAnalyticsModule to analytics.enabledAnalytics")}, + wants: want{len: 0, error: errors.New("failed to convert moduleList interface from analytics.Module to analytics.enabledAnalytics")}, }, { description: "add nil module to non-nil module-list", diff --git a/analytics/config/config.go b/analytics/config/config.go deleted file mode 100644 index 708375de150..00000000000 --- a/analytics/config/config.go +++ /dev/null @@ -1,86 +0,0 @@ -package config - -import ( - "github.com/benbjohnson/clock" - "github.com/golang/glog" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/analytics/clients" - "github.com/prebid/prebid-server/analytics/filesystem" - "github.com/prebid/prebid-server/analytics/pubmatic" - "github.com/prebid/prebid-server/analytics/pubstack" - "github.com/prebid/prebid-server/config" -) - -// Modules that need to be logged to need to be initialized here -func NewPBSAnalytics(analytics *config.Analytics) analytics.PBSAnalyticsModule { - modules := make(enabledAnalytics, 0) - if len(analytics.File.Filename) > 0 { - if mod, err := filesystem.NewFileLogger(analytics.File.Filename); err == nil { - modules = append(modules, mod) - } else { - glog.Fatalf("Could not initialize FileLogger for file %v :%v", analytics.File.Filename, err) - } - } - - if analytics.Pubstack.Enabled { - pubstackModule, err := pubstack.NewModule( - clients.GetDefaultHttpInstance(), - analytics.Pubstack.ScopeId, - analytics.Pubstack.IntakeUrl, - analytics.Pubstack.ConfRefresh, - analytics.Pubstack.Buffers.EventCount, - analytics.Pubstack.Buffers.BufferSize, - analytics.Pubstack.Buffers.Timeout, - clock.New()) - if err == nil { - modules = append(modules, pubstackModule) - } else { - glog.Errorf("Could not initialize PubstackModule: %v", err) - } - } - - if analytics.PubMatic.Enabled { - modules = append(modules, pubmatic.NewHTTPLogger(analytics.PubMatic)) - } - - return modules -} - -// Collection of all the correctly configured analytics modules - implements the PBSAnalyticsModule interface -type enabledAnalytics []analytics.PBSAnalyticsModule - -func (ea enabledAnalytics) LogAuctionObject(ao *analytics.AuctionObject) { - for _, module := range ea { - module.LogAuctionObject(ao) - } -} - -func (ea enabledAnalytics) LogVideoObject(vo *analytics.VideoObject) { - for _, module := range ea { - module.LogVideoObject(vo) - } -} - -func (ea enabledAnalytics) LogCookieSyncObject(cso *analytics.CookieSyncObject) { - for _, module := range ea { - module.LogCookieSyncObject(cso) - } -} - -func (ea enabledAnalytics) LogSetUIDObject(so *analytics.SetUIDObject) { - for _, module := range ea { - module.LogSetUIDObject(so) - } -} - -func (ea enabledAnalytics) LogAmpObject(ao *analytics.AmpObject) { - for _, module := range ea { - module.LogAmpObject(ao) - } -} - -func (ea enabledAnalytics) LogNotificationEventObject(ne *analytics.NotificationEvent) { - for _, module := range ea { - module.LogNotificationEventObject(ne) - } -} diff --git a/analytics/config/config_test.go b/analytics/config/config_test.go deleted file mode 100644 index c0ad9c26a16..00000000000 --- a/analytics/config/config_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package config - -import ( - "net/http" - "os" - "testing" - - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/stretchr/testify/assert" - - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" -) - -const TEST_DIR string = "testFiles" - -func TestSampleModule(t *testing.T) { - var count int - am := initAnalytics(&count) - am.LogAuctionObject(&analytics.AuctionObject{ - Status: http.StatusOK, - Errors: nil, - Response: &openrtb2.BidResponse{}, - }) - if count != 1 { - t.Errorf("PBSAnalyticsModule failed at LogAuctionObject") - } - - am.LogSetUIDObject(&analytics.SetUIDObject{ - Status: http.StatusOK, - Bidder: "bidders string", - UID: "uid", - Errors: nil, - Success: true, - }) - if count != 2 { - t.Errorf("PBSAnalyticsModule failed at LogSetUIDObject") - } - - am.LogCookieSyncObject(&analytics.CookieSyncObject{}) - if count != 3 { - t.Errorf("PBSAnalyticsModule failed at LogCookieSyncObject") - } - - am.LogAmpObject(&analytics.AmpObject{}) - if count != 4 { - t.Errorf("PBSAnalyticsModule failed at LogAmpObject") - } - - am.LogVideoObject(&analytics.VideoObject{}) - if count != 5 { - t.Errorf("PBSAnalyticsModule failed at LogVideoObject") - } - - am.LogNotificationEventObject(&analytics.NotificationEvent{}) - if count != 6 { - t.Errorf("PBSAnalyticsModule failed at LogNotificationEventObject") - } -} - -type sampleModule struct { - count *int -} - -func (m *sampleModule) LogAuctionObject(ao *analytics.AuctionObject) { *m.count++ } - -func (m *sampleModule) LogVideoObject(vo *analytics.VideoObject) { *m.count++ } - -func (m *sampleModule) LogCookieSyncObject(cso *analytics.CookieSyncObject) { *m.count++ } - -func (m *sampleModule) LogSetUIDObject(so *analytics.SetUIDObject) { *m.count++ } - -func (m *sampleModule) LogAmpObject(ao *analytics.AmpObject) { *m.count++ } - -func (m *sampleModule) LogNotificationEventObject(ne *analytics.NotificationEvent) { *m.count++ } - -func initAnalytics(count *int) analytics.PBSAnalyticsModule { - modules := make(enabledAnalytics, 0) - modules = append(modules, &sampleModule{count}) - return &modules -} - -func TestNewPBSAnalytics(t *testing.T) { - pbsAnalytics := NewPBSAnalytics(&config.Analytics{}) - instance := pbsAnalytics.(enabledAnalytics) - - assert.Equal(t, len(instance), 0) -} - -func TestNewPBSAnalytics_FileLogger(t *testing.T) { - if _, err := os.Stat(TEST_DIR); os.IsNotExist(err) { - if err = os.MkdirAll(TEST_DIR, 0755); err != nil { - t.Fatalf("Could not create test directory for FileLogger") - } - } - defer os.RemoveAll(TEST_DIR) - mod := NewPBSAnalytics(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}}) - switch modType := mod.(type) { - case enabledAnalytics: - if len(enabledAnalytics(modType)) != 1 { - t.Fatalf("Failed to add analytics module") - } - default: - t.Fatalf("Failed to initialize analytics module") - } - - pbsAnalytics := NewPBSAnalytics(&config.Analytics{File: config.FileLogs{Filename: TEST_DIR + "/test"}}) - instance := pbsAnalytics.(enabledAnalytics) - - assert.Equal(t, len(instance), 1) -} - -func TestNewPBSAnalytics_Pubstack(t *testing.T) { - - pbsAnalyticsWithoutError := NewPBSAnalytics(&config.Analytics{ - Pubstack: config.Pubstack{ - Enabled: true, - ScopeId: "scopeId", - IntakeUrl: "https://pubstack.io/intake", - Buffers: config.PubstackBuffer{ - BufferSize: "100KB", - EventCount: 0, - Timeout: "30s", - }, - ConfRefresh: "2h", - }, - }) - instanceWithoutError := pbsAnalyticsWithoutError.(enabledAnalytics) - - assert.Equal(t, len(instanceWithoutError), 1) - - pbsAnalyticsWithError := NewPBSAnalytics(&config.Analytics{ - Pubstack: config.Pubstack{ - Enabled: true, - }, - }) - instanceWithError := pbsAnalyticsWithError.(enabledAnalytics) - assert.Equal(t, len(instanceWithError), 0) -} diff --git a/analytics/core.go b/analytics/core.go index eca93741bd2..c9e15180f44 100644 --- a/analytics/core.go +++ b/analytics/core.go @@ -3,16 +3,16 @@ package analytics import ( "time" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) -// PBSAnalyticsModule must be implemented by analytics modules to extract the required information and logging +// Module must be implemented by analytics modules to extract the required information and logging // activities. Do not use marshal the parameter objects directly as they can change over time. Use a separate // model for each analytics module and transform as appropriate. -type PBSAnalyticsModule interface { +type Module interface { LogAuctionObject(*AuctionObject) LogVideoObject(*VideoObject) LogCookieSyncObject(*CookieSyncObject) diff --git a/analytics/filesystem/file_module.go b/analytics/filesystem/file_module.go index 9a357529c3a..01f9c27bf43 100644 --- a/analytics/filesystem/file_module.go +++ b/analytics/filesystem/file_module.go @@ -2,12 +2,12 @@ package filesystem import ( "bytes" - "encoding/json" "fmt" "github.com/chasex/glog" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/analytics" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) type RequestType string @@ -86,7 +86,7 @@ func (f *FileLogger) LogNotificationEventObject(ne *analytics.NotificationEvent) } // Method to initialize the analytic module -func NewFileLogger(filename string) (analytics.PBSAnalyticsModule, error) { +func NewFileLogger(filename string) (analytics.Module, error) { options := glog.LogOptions{ File: filename, Flag: glog.LstdFlags, @@ -120,7 +120,7 @@ func jsonifyAuctionObject(ao *analytics.AuctionObject) string { } } - b, err := json.Marshal(&struct { + b, err := jsonutil.Marshal(&struct { Type RequestType `json:"type"` *logAuction }{ @@ -153,7 +153,7 @@ func jsonifyVideoObject(vo *analytics.VideoObject) string { } } - b, err := json.Marshal(&struct { + b, err := jsonutil.Marshal(&struct { Type RequestType `json:"type"` *logVideo }{ @@ -178,7 +178,7 @@ func jsonifyCookieSync(cso *analytics.CookieSyncObject) string { } } - b, err := json.Marshal(&struct { + b, err := jsonutil.Marshal(&struct { Type RequestType `json:"type"` *logUserSync }{ @@ -205,7 +205,7 @@ func jsonifySetUIDObject(so *analytics.SetUIDObject) string { } } - b, err := json.Marshal(&struct { + b, err := jsonutil.Marshal(&struct { Type RequestType `json:"type"` *logSetUID }{ @@ -239,7 +239,7 @@ func jsonifyAmpObject(ao *analytics.AmpObject) string { } } - b, err := json.Marshal(&struct { + b, err := jsonutil.Marshal(&struct { Type RequestType `json:"type"` *logAMP }{ @@ -263,7 +263,7 @@ func jsonifyNotificationEventObject(ne *analytics.NotificationEvent) string { } } - b, err := json.Marshal(&struct { + b, err := jsonutil.Marshal(&struct { Type RequestType `json:"type"` *logNotificationEvent }{ diff --git a/analytics/filesystem/file_module_test.go b/analytics/filesystem/file_module_test.go index 9843a8ab108..0e0831d14f1 100644 --- a/analytics/filesystem/file_module_test.go +++ b/analytics/filesystem/file_module_test.go @@ -6,10 +6,10 @@ import ( "strings" "testing" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" ) const TEST_DIR string = "testFiles" diff --git a/analytics/filesystem/model.go b/analytics/filesystem/model.go index 9fc7a6e19a2..013a92593a4 100644 --- a/analytics/filesystem/model.go +++ b/analytics/filesystem/model.go @@ -3,11 +3,11 @@ package filesystem import ( "time" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type logAuction struct { diff --git a/analytics/pubmatic/helper.go b/analytics/pubmatic/helper.go index c8907410a42..01a94bea9e4 100644 --- a/analytics/pubmatic/helper.go +++ b/analytics/pubmatic/helper.go @@ -8,8 +8,8 @@ import ( "time" "github.com/golang/glog" - "github.com/prebid/prebid-server/analytics/pubmatic/mhttp" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/analytics/pubmatic/mhttp" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) // PrepareLoggerURL returns the url for OW logger call diff --git a/analytics/pubmatic/helper_test.go b/analytics/pubmatic/helper_test.go index e156d942973..6d5e69a45ce 100644 --- a/analytics/pubmatic/helper_test.go +++ b/analytics/pubmatic/helper_test.go @@ -6,10 +6,10 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/prebid/prebid-server/analytics/pubmatic/mhttp" - mock_mhttp "github.com/prebid/prebid-server/analytics/pubmatic/mhttp/mock" - mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/analytics/pubmatic/mhttp" + mock_mhttp "github.com/prebid/prebid-server/v2/analytics/pubmatic/mhttp/mock" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/analytics/pubmatic/logger.go b/analytics/pubmatic/logger.go index c214c4ab73b..a27fa550342 100644 --- a/analytics/pubmatic/logger.go +++ b/analytics/pubmatic/logger.go @@ -8,15 +8,15 @@ import ( "net/http" "strconv" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/customdimensions" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/utils" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/customdimensions" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/utils" + "github.com/prebid/prebid-server/v2/openrtb_ext" uuid "github.com/satori/go.uuid" ) diff --git a/analytics/pubmatic/logger_test.go b/analytics/pubmatic/logger_test.go index 8c83c386728..60697cc571a 100644 --- a/analytics/pubmatic/logger_test.go +++ b/analytics/pubmatic/logger_test.go @@ -6,15 +6,15 @@ import ( "net/url" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/hooks/hookanalytics" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/analytics/pubmatic/mhttp/mock/mock.go b/analytics/pubmatic/mhttp/mock/mock.go index 9f4256b5706..1c898851934 100644 --- a/analytics/pubmatic/mhttp/mock/mock.go +++ b/analytics/pubmatic/mhttp/mock/mock.go @@ -6,7 +6,7 @@ package mock_mhttp import ( gomock "github.com/golang/mock/gomock" - mhttp "github.com/prebid/prebid-server/analytics/pubmatic/mhttp" + mhttp "github.com/prebid/prebid-server/v2/analytics/pubmatic/mhttp" reflect "reflect" sync "sync" ) diff --git a/analytics/pubmatic/pubmatic.go b/analytics/pubmatic/pubmatic.go index f623a9a571f..956102efea6 100644 --- a/analytics/pubmatic/pubmatic.go +++ b/analytics/pubmatic/pubmatic.go @@ -5,12 +5,12 @@ import ( "sync" "github.com/golang/glog" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/analytics/pubmatic/mhttp" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/analytics/pubmatic/mhttp" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) type RequestType string @@ -85,7 +85,7 @@ func (ow HTTPLogger) LogNotificationEventObject(ne *analytics.NotificationEvent) } // Method to initialize the analytic module -func NewHTTPLogger(cfg config.PubMaticWL) analytics.PBSAnalyticsModule { +func NewHTTPLogger(cfg config.PubMaticWL) analytics.Module { once.Do(func() { mhttp.Init(cfg.MaxClients, cfg.MaxConnections, cfg.MaxCalls, cfg.RespTimeout) diff --git a/analytics/pubmatic/pubmatic_test.go b/analytics/pubmatic/pubmatic_test.go index 901e9ac78c5..652b5dc2a37 100644 --- a/analytics/pubmatic/pubmatic_test.go +++ b/analytics/pubmatic/pubmatic_test.go @@ -3,11 +3,11 @@ package pubmatic import ( "testing" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks/hookanalytics" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/analytics/pubmatic/record.go b/analytics/pubmatic/record.go index 91bbf2731f2..a408e6a6775 100644 --- a/analytics/pubmatic/record.go +++ b/analytics/pubmatic/record.go @@ -3,10 +3,10 @@ package pubmatic import ( "encoding/json" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // WloggerRecord structure for wrapper analytics logger object diff --git a/analytics/pubmatic/record_test.go b/analytics/pubmatic/record_test.go index f1bfb2eb141..86d25d6e119 100644 --- a/analytics/pubmatic/record_test.go +++ b/analytics/pubmatic/record_test.go @@ -3,10 +3,10 @@ package pubmatic import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/analytics/pubstack/configupdate.go b/analytics/pubstack/configupdate.go index 622161a04f2..0ecaaef05c3 100644 --- a/analytics/pubstack/configupdate.go +++ b/analytics/pubstack/configupdate.go @@ -6,7 +6,7 @@ import ( "net/url" "time" - "github.com/prebid/prebid-server/util/task" + "github.com/prebid/prebid-server/v2/util/task" ) // ConfigUpdateTask publishes configurations until the stop channel is signaled. diff --git a/analytics/pubstack/eventchannel/sender.go b/analytics/pubstack/eventchannel/sender.go index 951de4d414e..fe068b1555f 100644 --- a/analytics/pubstack/eventchannel/sender.go +++ b/analytics/pubstack/eventchannel/sender.go @@ -3,10 +3,11 @@ package eventchannel import ( "bytes" "fmt" - "github.com/golang/glog" "net/http" "net/url" "path" + + "github.com/golang/glog" ) type Sender = func(payload []byte) error @@ -26,6 +27,7 @@ func NewHttpSender(client *http.Client, endpoint string) Sender { if err != nil { return err } + resp.Body.Close() if resp.StatusCode != http.StatusOK { glog.Errorf("[pubstack] Wrong code received %d instead of %d", resp.StatusCode, http.StatusOK) diff --git a/analytics/pubstack/helpers/json.go b/analytics/pubstack/helpers/json.go index 368c79e3f6a..c46393cbed8 100644 --- a/analytics/pubstack/helpers/json.go +++ b/analytics/pubstack/helpers/json.go @@ -1,11 +1,11 @@ package helpers import ( - "encoding/json" "fmt" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/analytics" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) func JsonifyAuctionObject(ao *analytics.AuctionObject, scope string) ([]byte, error) { @@ -26,7 +26,7 @@ func JsonifyAuctionObject(ao *analytics.AuctionObject, scope string) ([]byte, er } } - b, err := json.Marshal(&struct { + b, err := jsonutil.Marshal(&struct { Scope string `json:"scope"` *logAuction }{ @@ -59,7 +59,7 @@ func JsonifyVideoObject(vo *analytics.VideoObject, scope string) ([]byte, error) } } - b, err := json.Marshal(&struct { + b, err := jsonutil.Marshal(&struct { Scope string `json:"scope"` *logVideo }{ @@ -84,7 +84,7 @@ func JsonifyCookieSync(cso *analytics.CookieSyncObject, scope string) ([]byte, e } } - b, err := json.Marshal(&struct { + b, err := jsonutil.Marshal(&struct { Scope string `json:"scope"` *logUserSync }{ @@ -111,7 +111,7 @@ func JsonifySetUIDObject(so *analytics.SetUIDObject, scope string) ([]byte, erro } } - b, err := json.Marshal(&struct { + b, err := jsonutil.Marshal(&struct { Scope string `json:"scope"` *logSetUID }{ @@ -145,7 +145,7 @@ func JsonifyAmpObject(ao *analytics.AmpObject, scope string) ([]byte, error) { } } - b, err := json.Marshal(&struct { + b, err := jsonutil.Marshal(&struct { Scope string `json:"scope"` *logAMP }{ diff --git a/analytics/pubstack/helpers/json_test.go b/analytics/pubstack/helpers/json_test.go index 07ead724929..79055d16eb5 100644 --- a/analytics/pubstack/helpers/json_test.go +++ b/analytics/pubstack/helpers/json_test.go @@ -4,8 +4,8 @@ import ( "net/http" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/analytics" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/analytics" "github.com/stretchr/testify/assert" ) diff --git a/analytics/pubstack/helpers/model.go b/analytics/pubstack/helpers/model.go index 91b86d7fc86..2af6a19e439 100644 --- a/analytics/pubstack/helpers/model.go +++ b/analytics/pubstack/helpers/model.go @@ -3,11 +3,11 @@ package helpers import ( "time" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type logAuction struct { diff --git a/analytics/pubstack/pubstack_module.go b/analytics/pubstack/pubstack_module.go index 987c935f884..535118c0000 100644 --- a/analytics/pubstack/pubstack_module.go +++ b/analytics/pubstack/pubstack_module.go @@ -12,9 +12,9 @@ import ( "github.com/benbjohnson/clock" "github.com/golang/glog" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/analytics/pubstack/eventchannel" - "github.com/prebid/prebid-server/analytics/pubstack/helpers" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/analytics/pubstack/eventchannel" + "github.com/prebid/prebid-server/v2/analytics/pubstack/helpers" ) type Configuration struct { @@ -50,7 +50,7 @@ type PubstackModule struct { clock clock.Clock } -func NewModule(client *http.Client, scope, endpoint, configRefreshDelay string, maxEventCount int, maxByteSize, maxTime string, clock clock.Clock) (analytics.PBSAnalyticsModule, error) { +func NewModule(client *http.Client, scope, endpoint, configRefreshDelay string, maxEventCount int, maxByteSize, maxTime string, clock clock.Clock) (analytics.Module, error) { configUpdateTask, err := NewConfigUpdateHttpTask( client, scope, @@ -63,7 +63,7 @@ func NewModule(client *http.Client, scope, endpoint, configRefreshDelay string, return NewModuleWithConfigTask(client, scope, endpoint, maxEventCount, maxByteSize, maxTime, configUpdateTask, clock) } -func NewModuleWithConfigTask(client *http.Client, scope, endpoint string, maxEventCount int, maxByteSize, maxTime string, configTask ConfigUpdateTask, clock clock.Clock) (analytics.PBSAnalyticsModule, error) { +func NewModuleWithConfigTask(client *http.Client, scope, endpoint string, maxEventCount int, maxByteSize, maxTime string, configTask ConfigUpdateTask, clock clock.Clock) (analytics.Module, error) { glog.Infof("[pubstack] Initializing module scope=%s endpoint=%s\n", scope, endpoint) // parse args diff --git a/analytics/pubstack/pubstack_module_test.go b/analytics/pubstack/pubstack_module_test.go index 504a1cfe17e..911de4c6959 100644 --- a/analytics/pubstack/pubstack_module_test.go +++ b/analytics/pubstack/pubstack_module_test.go @@ -8,8 +8,8 @@ import ( "time" "github.com/benbjohnson/clock" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -50,47 +50,47 @@ func TestNewModuleSuccess(t *testing.T) { tests := []struct { description string feature string - logObject func(analytics.PBSAnalyticsModule) + logObject func(analytics.Module) }{ { description: "auction events are only published when logging an auction object with auction feature on", feature: auction, - logObject: func(module analytics.PBSAnalyticsModule) { + logObject: func(module analytics.Module) { module.LogAuctionObject(&analytics.AuctionObject{Status: http.StatusOK}) }, }, { description: "AMP events are only published when logging an AMP object with AMP feature on", feature: amp, - logObject: func(module analytics.PBSAnalyticsModule) { + logObject: func(module analytics.Module) { module.LogAmpObject(&analytics.AmpObject{Status: http.StatusOK}) }, }, { description: "video events are only published when logging a video object with video feature on", feature: video, - logObject: func(module analytics.PBSAnalyticsModule) { + logObject: func(module analytics.Module) { module.LogVideoObject(&analytics.VideoObject{Status: http.StatusOK}) }, }, { description: "cookie events are only published when logging a cookie object with cookie feature on", feature: cookieSync, - logObject: func(module analytics.PBSAnalyticsModule) { + logObject: func(module analytics.Module) { module.LogCookieSyncObject(&analytics.CookieSyncObject{Status: http.StatusOK}) }, }, { description: "setUID events are only published when logging a setUID object with setUID feature on", feature: setUID, - logObject: func(module analytics.PBSAnalyticsModule) { + logObject: func(module analytics.Module) { module.LogSetUIDObject(&analytics.SetUIDObject{Status: http.StatusOK}) }, }, { description: "Ignore excluded fields from marshal", feature: auction, - logObject: func(module analytics.PBSAnalyticsModule) { + logObject: func(module analytics.Module) { module.LogAuctionObject(&analytics.AuctionObject{ RequestWrapper: &openrtb_ext.RequestWrapper{}, SeatNonBid: []openrtb_ext.SeatNonBid{ diff --git a/analytics/runner.go b/analytics/runner.go new file mode 100644 index 00000000000..7a2c56f77dd --- /dev/null +++ b/analytics/runner.go @@ -0,0 +1,14 @@ +package analytics + +import ( + "github.com/prebid/prebid-server/v2/privacy" +) + +type Runner interface { + LogAuctionObject(*AuctionObject, privacy.ActivityControl) + LogVideoObject(*VideoObject, privacy.ActivityControl) + LogCookieSyncObject(*CookieSyncObject) + LogSetUIDObject(*SetUIDObject) + LogAmpObject(*AmpObject, privacy.ActivityControl) + LogNotificationEventObject(*NotificationEvent, privacy.ActivityControl) +} diff --git a/bidadjustment/apply.go b/bidadjustment/apply.go index 4fa3b737b16..16b8305a01a 100644 --- a/bidadjustment/apply.go +++ b/bidadjustment/apply.go @@ -3,8 +3,8 @@ package bidadjustment import ( "math" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const ( diff --git a/bidadjustment/apply_test.go b/bidadjustment/apply_test.go index c0eb74ab419..3fa46b6bcb2 100644 --- a/bidadjustment/apply_test.go +++ b/bidadjustment/apply_test.go @@ -3,9 +3,9 @@ package bidadjustment import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) diff --git a/bidadjustment/build_rules.go b/bidadjustment/build_rules.go index bccb3bc86cf..f028d78616a 100644 --- a/bidadjustment/build_rules.go +++ b/bidadjustment/build_rules.go @@ -1,8 +1,8 @@ package bidadjustment import ( - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const ( diff --git a/bidadjustment/build_rules_test.go b/bidadjustment/build_rules_test.go index 263a782130e..b3a9a930559 100644 --- a/bidadjustment/build_rules_test.go +++ b/bidadjustment/build_rules_test.go @@ -3,9 +3,9 @@ package bidadjustment import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/bidadjustment/validate.go b/bidadjustment/validate.go index c0ae3d4a27b..f34ef48ba49 100644 --- a/bidadjustment/validate.go +++ b/bidadjustment/validate.go @@ -3,7 +3,7 @@ package bidadjustment import ( "math" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // Validate checks whether all provided bid adjustments are valid or not against the requirements defined in the issue diff --git a/bidadjustment/validate_test.go b/bidadjustment/validate_test.go index a0b4eb436eb..caf4188bb5e 100644 --- a/bidadjustment/validate_test.go +++ b/bidadjustment/validate_test.go @@ -3,7 +3,7 @@ package bidadjustment import ( "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/config/account.go b/config/account.go index b162c7e2898..aea066679dd 100644 --- a/config/account.go +++ b/config/account.go @@ -7,8 +7,8 @@ import ( "strings" "github.com/prebid/go-gdpr/consentconstants" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/iputil" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/iputil" ) // ChannelType enumerates the values of integrations Prebid Server can configure for an account @@ -20,6 +20,7 @@ const ( ChannelApp ChannelType = "app" ChannelVideo ChannelType = "video" ChannelWeb ChannelType = "web" + ChannelDOOH ChannelType = "dooh" ) // Account represents a publisher account configuration @@ -27,7 +28,6 @@ type Account struct { ID string `mapstructure:"id" json:"id"` Disabled bool `mapstructure:"disabled" json:"disabled"` CacheTTL DefaultTTLs `mapstructure:"cache_ttl" json:"cache_ttl"` - EventsEnabled *bool `mapstructure:"events_enabled" json:"events_enabled"` // Deprecated: Use events.enabled instead. CCPA AccountCCPA `mapstructure:"ccpa" json:"ccpa"` GDPR AccountGDPR `mapstructure:"gdpr" json:"gdpr"` DebugAllow bool `mapstructure:"debug_allow" json:"debug_allow"` @@ -52,22 +52,10 @@ type CookieSync struct { DefaultCoopSync *bool `mapstructure:"default_coop_sync" json:"default_coop_sync"` } -type AccountFloorFetch struct { - Enabled bool `mapstructure:"enabled" json:"enabled"` - URL string `mapstructure:"url" json:"url"` - Timeout int `mapstructure:"timeout_ms" json:"timeout_ms"` - MaxFileSize int `mapstructure:"max_file_size_kb" json:"max_file_size_kb"` - MaxRules int `mapstructure:"max_rules" json:"max_rules"` - MaxAge int `mapstructure:"max_age_sec" json:"max_age_sec"` - Period int `mapstructure:"period_sec" json:"period_sec"` - AccountID string `mapstructure:"accountID" json:"accountID"` -} - // AccountCCPA represents account-specific CCPA configuration type AccountCCPA struct { - Enabled *bool `mapstructure:"enabled" json:"enabled,omitempty"` - IntegrationEnabled AccountChannel `mapstructure:"integration_enabled" json:"integration_enabled"` - ChannelEnabled AccountChannel `mapstructure:"channel_enabled" json:"channel_enabled"` + Enabled *bool `mapstructure:"enabled" json:"enabled,omitempty"` + ChannelEnabled AccountChannel `mapstructure:"channel_enabled" json:"channel_enabled"` } type AccountPriceFloors struct { @@ -78,11 +66,23 @@ type AccountPriceFloors struct { UseDynamicData bool `mapstructure:"use_dynamic_data" json:"use_dynamic_data"` MaxRule int `mapstructure:"max_rules" json:"max_rules"` MaxSchemaDims int `mapstructure:"max_schema_dims" json:"max_schema_dims"` - Fetch AccountFloorFetch `mapstructure:"fetch" json:"fetch"` + Fetcher AccountFloorFetch `mapstructure:"fetch" json:"fetch"` } -func (pf *AccountPriceFloors) validate(errs []error) []error { +// AccountFloorFetch defines the configuration for dynamic floors fetching. +type AccountFloorFetch struct { + Enabled bool `mapstructure:"enabled" json:"enabled"` + URL string `mapstructure:"url" json:"url"` + Timeout int `mapstructure:"timeout_ms" json:"timeout_ms"` + MaxFileSizeKB int `mapstructure:"max_file_size_kb" json:"max_file_size_kb"` + MaxRules int `mapstructure:"max_rules" json:"max_rules"` + MaxAge int `mapstructure:"max_age_sec" json:"max_age_sec"` + Period int `mapstructure:"period_sec" json:"period_sec"` + MaxSchemaDims int `mapstructure:"max_schema_dims" json:"max_schema_dims"` + AccountID string `mapstructure:"accountID" json:"accountID"` +} +func (pf *AccountPriceFloors) validate(errs []error) []error { if pf.EnforceFloorsRate < 0 || pf.EnforceFloorsRate > 100 { errs = append(errs, fmt.Errorf(`account_defaults.price_floors.enforce_floors_rate should be between 0 and 100`)) } @@ -95,29 +95,34 @@ func (pf *AccountPriceFloors) validate(errs []error) []error { errs = append(errs, fmt.Errorf(`account_defaults.price_floors.max_schema_dims should be between 0 and 20`)) } - if pf.Fetch.Period > pf.Fetch.MaxAge { + if pf.Fetcher.Period > pf.Fetcher.MaxAge { errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.period_sec should be less than account_defaults.price_floors.fetch.max_age_sec`)) } - if pf.Fetch.Period < 300 { + if pf.Fetcher.Period < 300 { errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.period_sec should not be less than 300 seconds`)) } - if !(pf.Fetch.MaxAge >= 600 && pf.Fetch.MaxAge < math.MaxInt32) { + if pf.Fetcher.MaxAge < 600 { errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.max_age_sec should not be less than 600 seconds and greater than maximum integer value`)) } - if !(pf.Fetch.Timeout > 10 && pf.Fetch.Timeout < 10000) { - errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.timeout_ms should be between 10 to 10,000 mili seconds`)) + if !(pf.Fetcher.Timeout > 10 && pf.Fetcher.Timeout < 10000) { + errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.timeout_ms should be between 10 to 10,000 miliseconds`)) + } + + if pf.Fetcher.MaxRules < 0 { + errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.max_rules should be greater than or equal to 0`)) } - if !(pf.Fetch.MaxRules >= 0 && pf.Fetch.MaxRules < math.MaxInt32) { - errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.max_rules should not be less than 0 seconds and greater than maximum integer value`)) + if pf.Fetcher.MaxFileSizeKB < 0 { + errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.max_file_size_kb should be greater than or equal to 0`)) } - if !(pf.Fetch.MaxFileSize >= 0 && pf.Fetch.MaxFileSize < math.MaxInt32) { - errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.max_file_size_kb should not be less than 0 seconds and greater than maximum integer value`)) + if !(pf.Fetcher.MaxSchemaDims >= 0 && pf.Fetcher.MaxSchemaDims < 20) { + errs = append(errs, fmt.Errorf(`account_defaults.price_floors.fetch.max_schema_dims should not be less than 0 and greater than 20`)) } + return errs } @@ -130,17 +135,14 @@ func (pf *AccountPriceFloors) IsAdjustForBidAdjustmentEnabled() bool { func (a *AccountCCPA) EnabledForChannelType(channelType ChannelType) *bool { if channelEnabled := a.ChannelEnabled.GetByChannelType(channelType); channelEnabled != nil { return channelEnabled - } else if integrationEnabled := a.IntegrationEnabled.GetByChannelType(channelType); integrationEnabled != nil { - return integrationEnabled } return a.Enabled } // AccountGDPR represents account-specific GDPR configuration type AccountGDPR struct { - Enabled *bool `mapstructure:"enabled" json:"enabled,omitempty"` - IntegrationEnabled AccountChannel `mapstructure:"integration_enabled" json:"integration_enabled"` - ChannelEnabled AccountChannel `mapstructure:"channel_enabled" json:"channel_enabled"` + Enabled *bool `mapstructure:"enabled" json:"enabled,omitempty"` + ChannelEnabled AccountChannel `mapstructure:"channel_enabled" json:"channel_enabled"` // Array of basic enforcement vendors that is used to create the hash table so vendor names can be instantly accessed BasicEnforcementVendors []string `mapstructure:"basic_enforcement_vendors" json:"basic_enforcement_vendors"` BasicEnforcementVendorsMap map[string]struct{} @@ -165,8 +167,6 @@ type AccountGDPR struct { func (a *AccountGDPR) EnabledForChannelType(channelType ChannelType) *bool { if channelEnabled := a.ChannelEnabled.GetByChannelType(channelType); channelEnabled != nil { return channelEnabled - } else if integrationEnabled := a.IntegrationEnabled.GetByChannelType(channelType); integrationEnabled != nil { - return integrationEnabled } return a.Enabled } @@ -228,7 +228,7 @@ func (a *AccountGDPR) PurposeEnforcingVendors(purpose consentconstants.Purpose) } // PurposeVendorExceptions returns the vendor exception map for a given purpose. -func (a *AccountGDPR) PurposeVendorExceptions(purpose consentconstants.Purpose) (value map[openrtb_ext.BidderName]struct{}, exists bool) { +func (a *AccountGDPR) PurposeVendorExceptions(purpose consentconstants.Purpose) (value map[string]struct{}, exists bool) { c, exists := a.PurposeConfigs[purpose] if exists && c.VendorExceptionMap != nil { @@ -263,8 +263,8 @@ type AccountGDPRPurpose struct { EnforcePurpose *bool `mapstructure:"enforce_purpose" json:"enforce_purpose,omitempty"` EnforceVendors *bool `mapstructure:"enforce_vendors" json:"enforce_vendors,omitempty"` // Array of vendor exceptions that is used to create the hash table VendorExceptionMap so vendor names can be instantly accessed - VendorExceptions []openrtb_ext.BidderName `mapstructure:"vendor_exceptions" json:"vendor_exceptions"` - VendorExceptionMap map[openrtb_ext.BidderName]struct{} + VendorExceptions []string `mapstructure:"vendor_exceptions" json:"vendor_exceptions"` + VendorExceptionMap map[string]struct{} } // AccountGDPRSpecialFeature represents account-specific GDPR special feature configuration @@ -287,6 +287,7 @@ type AccountChannel struct { App *bool `mapstructure:"app" json:"app,omitempty"` Video *bool `mapstructure:"video" json:"video,omitempty"` Web *bool `mapstructure:"web" json:"web,omitempty"` + DOOH *bool `mapstructure:"dooh" json:"dooh,omitempty"` } // GetByChannelType looks up the account integration enabled setting for the specified channel type @@ -302,6 +303,8 @@ func (a *AccountChannel) GetByChannelType(channelType ChannelType) *bool { channelEnabled = a.Video case ChannelWeb: channelEnabled = a.Web + case ChannelDOOH: + channelEnabled = a.DOOH } return channelEnabled @@ -331,14 +334,20 @@ func (m AccountModules) ModuleConfig(id string) (json.RawMessage, error) { return m[vendor][module], nil } -func (a *AccountChannel) IsSet() bool { - return a.AMP != nil || a.App != nil || a.Video != nil || a.Web != nil -} - type AccountPrivacy struct { AllowActivities *AllowActivities `mapstructure:"allowactivities" json:"allowactivities"` IPv6Config IPv6 `mapstructure:"ipv6" json:"ipv6"` IPv4Config IPv4 `mapstructure:"ipv4" json:"ipv4"` + PrivacySandbox PrivacySandbox `mapstructure:"privacysandbox" json:"privacysandbox"` +} + +type PrivacySandbox struct { + CookieDeprecation CookieDeprecation `mapstructure:"cookiedeprecation"` +} + +type CookieDeprecation struct { + Enabled bool `mapstructure:"enabled"` + TTLSec int `mapstructure:"ttl_sec"` } type IPv6 struct { diff --git a/config/account_test.go b/config/account_test.go index 34c9cf53a24..c529af09f15 100644 --- a/config/account_test.go +++ b/config/account_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/prebid/go-gdpr/consentconstants" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -14,68 +14,46 @@ func TestAccountGDPREnabledForChannelType(t *testing.T) { trueValue, falseValue := true, false tests := []struct { - description string - giveChannelType ChannelType - giveGDPREnabled *bool - giveWebGDPREnabled *bool - giveWebGDPREnabledForIntegration *bool - wantEnabled *bool + description string + giveChannelType ChannelType + giveGDPREnabled *bool + giveWebGDPREnabled *bool + wantEnabled *bool }{ { - description: "GDPR Web channel enabled, general GDPR disabled", - giveChannelType: ChannelWeb, - giveGDPREnabled: &falseValue, - giveWebGDPREnabled: &trueValue, - giveWebGDPREnabledForIntegration: nil, - wantEnabled: &trueValue, + description: "GDPR Web channel enabled, general GDPR disabled", + giveChannelType: ChannelWeb, + giveGDPREnabled: &falseValue, + giveWebGDPREnabled: &trueValue, + wantEnabled: &trueValue, }, { - description: "GDPR Web channel disabled, general GDPR enabled", - giveChannelType: ChannelWeb, - giveGDPREnabled: &trueValue, - giveWebGDPREnabled: &falseValue, - giveWebGDPREnabledForIntegration: nil, - wantEnabled: &falseValue, + description: "GDPR Web channel disabled, general GDPR enabled", + giveChannelType: ChannelWeb, + giveGDPREnabled: &trueValue, + giveWebGDPREnabled: &falseValue, + wantEnabled: &falseValue, }, { - description: "GDPR Web channel unspecified, general GDPR disabled", - giveChannelType: ChannelWeb, - giveGDPREnabled: &falseValue, - giveWebGDPREnabled: nil, - giveWebGDPREnabledForIntegration: nil, - wantEnabled: &falseValue, + description: "GDPR Web channel unspecified, general GDPR disabled", + giveChannelType: ChannelWeb, + giveGDPREnabled: &falseValue, + giveWebGDPREnabled: nil, + wantEnabled: &falseValue, }, { - description: "GDPR Web channel unspecified, general GDPR enabled", - giveChannelType: ChannelWeb, - giveGDPREnabled: &trueValue, - giveWebGDPREnabled: nil, - giveWebGDPREnabledForIntegration: nil, - wantEnabled: &trueValue, + description: "GDPR Web channel unspecified, general GDPR enabled", + giveChannelType: ChannelWeb, + giveGDPREnabled: &trueValue, + giveWebGDPREnabled: nil, + wantEnabled: &trueValue, }, { - description: "GDPR Web channel unspecified, general GDPR unspecified", - giveChannelType: ChannelWeb, - giveGDPREnabled: nil, - giveWebGDPREnabled: nil, - giveWebGDPREnabledForIntegration: nil, - wantEnabled: nil, - }, - { - description: "Inegration Enabled is set, and channel enabled isn't", - giveChannelType: ChannelWeb, - giveGDPREnabled: &falseValue, - giveWebGDPREnabled: nil, - giveWebGDPREnabledForIntegration: &trueValue, - wantEnabled: &trueValue, - }, - { - description: "Inegration Enabled is set, and channel enabled is set, channel should have precedence", - giveChannelType: ChannelWeb, - giveGDPREnabled: &falseValue, - giveWebGDPREnabled: &trueValue, - giveWebGDPREnabledForIntegration: &falseValue, - wantEnabled: &trueValue, + description: "GDPR Web channel unspecified, general GDPR unspecified", + giveChannelType: ChannelWeb, + giveGDPREnabled: nil, + giveWebGDPREnabled: nil, + wantEnabled: nil, }, } @@ -86,9 +64,6 @@ func TestAccountGDPREnabledForChannelType(t *testing.T) { ChannelEnabled: AccountChannel{ Web: tt.giveWebGDPREnabled, }, - IntegrationEnabled: AccountChannel{ - Web: tt.giveWebGDPREnabledForIntegration, - }, }, } @@ -107,68 +82,46 @@ func TestAccountCCPAEnabledForChannelType(t *testing.T) { trueValue, falseValue := true, false tests := []struct { - description string - giveChannelType ChannelType - giveCCPAEnabled *bool - giveWebCCPAEnabled *bool - giveWebCCPAEnabledForIntegration *bool - wantEnabled *bool + description string + giveChannelType ChannelType + giveCCPAEnabled *bool + giveWebCCPAEnabled *bool + wantEnabled *bool }{ { - description: "CCPA Web channel enabled, general CCPA disabled", - giveChannelType: ChannelWeb, - giveCCPAEnabled: &falseValue, - giveWebCCPAEnabled: &trueValue, - giveWebCCPAEnabledForIntegration: nil, - wantEnabled: &trueValue, - }, - { - description: "CCPA Web channel disabled, general CCPA enabled", - giveChannelType: ChannelWeb, - giveCCPAEnabled: &trueValue, - giveWebCCPAEnabled: &falseValue, - giveWebCCPAEnabledForIntegration: nil, - wantEnabled: &falseValue, - }, - { - description: "CCPA Web channel unspecified, general CCPA disabled", - giveChannelType: ChannelWeb, - giveCCPAEnabled: &falseValue, - giveWebCCPAEnabled: nil, - giveWebCCPAEnabledForIntegration: nil, - wantEnabled: &falseValue, + description: "CCPA Web channel enabled, general CCPA disabled", + giveChannelType: ChannelWeb, + giveCCPAEnabled: &falseValue, + giveWebCCPAEnabled: &trueValue, + wantEnabled: &trueValue, }, { - description: "CCPA Web channel unspecified, general CCPA enabled", - giveChannelType: ChannelWeb, - giveCCPAEnabled: &trueValue, - giveWebCCPAEnabled: nil, - giveWebCCPAEnabledForIntegration: nil, - wantEnabled: &trueValue, + description: "CCPA Web channel disabled, general CCPA enabled", + giveChannelType: ChannelWeb, + giveCCPAEnabled: &trueValue, + giveWebCCPAEnabled: &falseValue, + wantEnabled: &falseValue, }, { - description: "CCPA Web channel unspecified, general CCPA unspecified", - giveChannelType: ChannelWeb, - giveCCPAEnabled: nil, - giveWebCCPAEnabled: nil, - giveWebCCPAEnabledForIntegration: nil, - wantEnabled: nil, + description: "CCPA Web channel unspecified, general CCPA disabled", + giveChannelType: ChannelWeb, + giveCCPAEnabled: &falseValue, + giveWebCCPAEnabled: nil, + wantEnabled: &falseValue, }, { - description: "Inegration Enabled is set, and channel enabled isn't", - giveChannelType: ChannelWeb, - giveCCPAEnabled: &falseValue, - giveWebCCPAEnabled: nil, - giveWebCCPAEnabledForIntegration: &trueValue, - wantEnabled: &trueValue, + description: "CCPA Web channel unspecified, general CCPA enabled", + giveChannelType: ChannelWeb, + giveCCPAEnabled: &trueValue, + giveWebCCPAEnabled: nil, + wantEnabled: &trueValue, }, { - description: "Inegration Enabled is set, and channel enabled is set, channel should have precedence", - giveChannelType: ChannelWeb, - giveCCPAEnabled: &falseValue, - giveWebCCPAEnabled: &trueValue, - giveWebCCPAEnabledForIntegration: &falseValue, - wantEnabled: &trueValue, + description: "CCPA Web channel unspecified, general CCPA unspecified", + giveChannelType: ChannelWeb, + giveCCPAEnabled: nil, + giveWebCCPAEnabled: nil, + wantEnabled: nil, }, } @@ -179,9 +132,6 @@ func TestAccountCCPAEnabledForChannelType(t *testing.T) { ChannelEnabled: AccountChannel{ Web: tt.giveWebCCPAEnabled, }, - IntegrationEnabled: AccountChannel{ - Web: tt.giveWebCCPAEnabledForIntegration, - }, }, } @@ -205,6 +155,7 @@ func TestAccountChannelGetByChannelType(t *testing.T) { giveAppEnabled *bool giveVideoEnabled *bool giveWebEnabled *bool + giveDOOHEnabled *bool giveChannelType ChannelType wantEnabled *bool }{ @@ -276,6 +227,23 @@ func TestAccountChannelGetByChannelType(t *testing.T) { giveChannelType: ChannelWeb, wantEnabled: &trueValue, }, + { + description: "DOOH channel setting unspecified, returns nil", + giveChannelType: ChannelDOOH, + wantEnabled: nil, + }, + { + description: "DOOH channel disabled, returns false", + giveDOOHEnabled: &falseValue, + giveChannelType: ChannelDOOH, + wantEnabled: &falseValue, + }, + { + description: "DOOH channel enabled, returns true", + giveDOOHEnabled: &trueValue, + giveChannelType: ChannelDOOH, + wantEnabled: &trueValue, + }, } for _, tt := range tests { @@ -284,6 +252,7 @@ func TestAccountChannelGetByChannelType(t *testing.T) { App: tt.giveAppEnabled, Video: tt.giveVideoEnabled, Web: tt.giveWebEnabled, + DOOH: tt.giveDOOHEnabled, } result := accountChannel.GetByChannelType(tt.giveChannelType) @@ -507,48 +476,40 @@ func TestPurposeVendorExceptions(t *testing.T) { tests := []struct { description string givePurposeConfigNil bool - givePurpose1ExceptionMap map[openrtb_ext.BidderName]struct{} - givePurpose2ExceptionMap map[openrtb_ext.BidderName]struct{} + givePurpose1ExceptionMap map[string]struct{} + givePurpose2ExceptionMap map[string]struct{} givePurpose consentconstants.Purpose - wantExceptionMap map[openrtb_ext.BidderName]struct{} - wantExceptionMapSet bool + wantExceptionMap map[string]struct{} }{ { description: "Purpose config is nil", givePurposeConfigNil: true, givePurpose: 1, - // wantExceptionMap: map[openrtb_ext.BidderName]struct{}{}, - wantExceptionMap: nil, - wantExceptionMapSet: false, + wantExceptionMap: nil, }, { - description: "Nil - exception map not defined for purpose", - givePurpose: 1, - // wantExceptionMap: map[openrtb_ext.BidderName]struct{}{}, - wantExceptionMap: nil, - wantExceptionMapSet: false, + description: "Nil - exception map not defined for purpose", + givePurpose: 1, + wantExceptionMap: nil, }, { description: "Empty - exception map empty for purpose", givePurpose: 1, - givePurpose1ExceptionMap: map[openrtb_ext.BidderName]struct{}{}, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{}, - wantExceptionMapSet: true, + givePurpose1ExceptionMap: map[string]struct{}{}, + wantExceptionMap: map[string]struct{}{}, }, { description: "Nonempty - exception map with multiple entries for purpose", givePurpose: 1, - givePurpose1ExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}}, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}}, - wantExceptionMapSet: true, + givePurpose1ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}}, + wantExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}}, }, { description: "Nonempty - exception map with multiple entries for different purpose", givePurpose: 2, - givePurpose1ExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}}, - givePurpose2ExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}}, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}}, - wantExceptionMapSet: true, + givePurpose1ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}}, + givePurpose2ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}}, + wantExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}}, }, } @@ -569,7 +530,11 @@ func TestPurposeVendorExceptions(t *testing.T) { value, present := accountGDPR.PurposeVendorExceptions(tt.givePurpose) assert.Equal(t, tt.wantExceptionMap, value, tt.description) - assert.Equal(t, tt.wantExceptionMapSet, present, tt.description) + if tt.wantExceptionMap == nil { + assert.Equal(t, false, present) + } else { + assert.Equal(t, true, present) + } } } @@ -825,41 +790,7 @@ func TestModulesGetConfig(t *testing.T) { } } -func TestAccountChannelIsSet(t *testing.T) { - trueBool := true - falseBool := false - - testCases := []struct { - name string - givenAccountChannel *AccountChannel - expected bool - }{ - { - name: "AccountChannelSetAllFields", - givenAccountChannel: &AccountChannel{AMP: &trueBool, App: &falseBool, Video: &falseBool, Web: &falseBool}, - expected: true, - }, - { - name: "AccountChannelNotSet", - givenAccountChannel: &AccountChannel{}, - expected: false, - }, - { - name: "AccountChannelSetAmpOnly", - givenAccountChannel: &AccountChannel{AMP: &trueBool}, - expected: true, - }, - } - - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - assert.Equal(t, test.expected, test.givenAccountChannel.IsSet()) - }) - } -} - func TestAccountPriceFloorsValidate(t *testing.T) { - tests := []struct { description string pf *AccountPriceFloors @@ -871,13 +802,10 @@ func TestAccountPriceFloorsValidate(t *testing.T) { EnforceFloorsRate: 100, MaxRule: 200, MaxSchemaDims: 10, - Fetch: AccountFloorFetch{ - Enabled: true, - Timeout: 11, - MaxFileSize: 1, - MaxRules: 1, - MaxAge: 700, - Period: 400, + Fetcher: AccountFloorFetch{ + Period: 300, + MaxAge: 600, + Timeout: 12, }, }, }, @@ -885,13 +813,10 @@ func TestAccountPriceFloorsValidate(t *testing.T) { description: "Invalid configuration: EnforceFloorRate:110", pf: &AccountPriceFloors{ EnforceFloorsRate: 110, - Fetch: AccountFloorFetch{ - Enabled: true, - Timeout: 11, - MaxFileSize: 1, - MaxRules: 1, - MaxAge: 700, - Period: 400, + Fetcher: AccountFloorFetch{ + Period: 300, + MaxAge: 600, + Timeout: 12, }, }, want: []error{errors.New("account_defaults.price_floors.enforce_floors_rate should be between 0 and 100")}, @@ -900,13 +825,10 @@ func TestAccountPriceFloorsValidate(t *testing.T) { description: "Invalid configuration: EnforceFloorRate:-10", pf: &AccountPriceFloors{ EnforceFloorsRate: -10, - Fetch: AccountFloorFetch{ - Enabled: true, - Timeout: 11, - MaxFileSize: 1, - MaxRules: 1, - MaxAge: 700, - Period: 400, + Fetcher: AccountFloorFetch{ + Period: 300, + MaxAge: 600, + Timeout: 12, }, }, want: []error{errors.New("account_defaults.price_floors.enforce_floors_rate should be between 0 and 100")}, @@ -915,13 +837,10 @@ func TestAccountPriceFloorsValidate(t *testing.T) { description: "Invalid configuration: MaxRule:-20", pf: &AccountPriceFloors{ MaxRule: -20, - Fetch: AccountFloorFetch{ - Enabled: true, - Timeout: 11, - MaxFileSize: 1, - MaxRules: 1, - MaxAge: 700, - Period: 400, + Fetcher: AccountFloorFetch{ + Period: 300, + MaxAge: 600, + Timeout: 12, }, }, want: []error{errors.New("account_defaults.price_floors.max_rules should be between 0 and 2147483647")}, @@ -930,106 +849,115 @@ func TestAccountPriceFloorsValidate(t *testing.T) { description: "Invalid configuration: MaxSchemaDims:100", pf: &AccountPriceFloors{ MaxSchemaDims: 100, - Fetch: AccountFloorFetch{ - Enabled: true, - Timeout: 11, - MaxFileSize: 1, - MaxRules: 1, - MaxAge: 700, - Period: 400, + Fetcher: AccountFloorFetch{ + Period: 300, + MaxAge: 600, + Timeout: 12, }, }, want: []error{errors.New("account_defaults.price_floors.max_schema_dims should be between 0 and 20")}, }, { - description: "Max Age is less than Period", + description: "Invalid period for fetch", pf: &AccountPriceFloors{ EnforceFloorsRate: 100, - Fetch: AccountFloorFetch{ - Enabled: true, - Timeout: 500, - MaxFileSize: 1, - MaxRules: 1, - MaxAge: 700, - Period: 800, + MaxRule: 200, + MaxSchemaDims: 10, + Fetcher: AccountFloorFetch{ + Period: 100, + MaxAge: 600, + Timeout: 12, }, }, - want: []error{errors.New("account_defaults.price_floors.fetch.period_sec should be less than account_defaults.price_floors.fetch.max_age_sec")}, + want: []error{errors.New("account_defaults.price_floors.fetch.period_sec should not be less than 300 seconds")}, }, { - description: "Period is less than 300", + description: "Invalid max age for fetch", pf: &AccountPriceFloors{ EnforceFloorsRate: 100, - Fetch: AccountFloorFetch{ - Enabled: true, - Timeout: 500, - MaxFileSize: 1, - MaxRules: 1, - MaxAge: 700, - Period: 200, + MaxRule: 200, + MaxSchemaDims: 10, + Fetcher: AccountFloorFetch{ + Period: 300, + MaxAge: 500, + Timeout: 12, }, }, - want: []error{errors.New("account_defaults.price_floors.fetch.period_sec should not be less than 300 seconds")}, + want: []error{errors.New("account_defaults.price_floors.fetch.max_age_sec should not be less than 600 seconds and greater than maximum integer value")}, + }, + { + description: "Period is greater than max age", + pf: &AccountPriceFloors{ + EnforceFloorsRate: 100, + MaxRule: 200, + MaxSchemaDims: 10, + Fetcher: AccountFloorFetch{ + Period: 700, + MaxAge: 600, + Timeout: 12, + }, + }, + want: []error{errors.New("account_defaults.price_floors.fetch.period_sec should be less than account_defaults.price_floors.fetch.max_age_sec")}, }, { - description: "Invalid Max age", + description: "Invalid timeout", pf: &AccountPriceFloors{ EnforceFloorsRate: 100, - Fetch: AccountFloorFetch{ - Enabled: true, - Timeout: 500, - MaxFileSize: 1, - MaxRules: 1, - MaxAge: 500, - Period: 400, + MaxRule: 200, + MaxSchemaDims: 10, + Fetcher: AccountFloorFetch{ + Period: 300, + MaxAge: 600, + Timeout: 4, }, }, - want: []error{errors.New("account_defaults.price_floors.fetch.max_age_sec should not be less than 600 seconds and greater than maximum integer value")}, + want: []error{errors.New("account_defaults.price_floors.fetch.timeout_ms should be between 10 to 10,000 miliseconds")}, }, { - description: "Invalid Timeout", + description: "Invalid Max Rules", pf: &AccountPriceFloors{ EnforceFloorsRate: 100, - Fetch: AccountFloorFetch{ - Enabled: true, - Timeout: 1, - MaxFileSize: 1, - MaxRules: 1, - MaxAge: 700, - Period: 400, + MaxRule: 200, + MaxSchemaDims: 10, + Fetcher: AccountFloorFetch{ + Period: 300, + MaxAge: 600, + Timeout: 12, + MaxRules: -2, }, }, - want: []error{errors.New("account_defaults.price_floors.fetch.timeout_ms should be between 10 to 10,000 mili seconds")}, + want: []error{errors.New("account_defaults.price_floors.fetch.max_rules should be greater than or equal to 0")}, }, { - description: "Invalid Max rules", + description: "Invalid Max File size", pf: &AccountPriceFloors{ EnforceFloorsRate: 100, - Fetch: AccountFloorFetch{ - Enabled: true, - Timeout: 11, - MaxFileSize: 1, - MaxRules: -1, - MaxAge: 700, - Period: 400, + MaxRule: 200, + MaxSchemaDims: 10, + Fetcher: AccountFloorFetch{ + Period: 300, + MaxAge: 600, + Timeout: 12, + MaxFileSizeKB: -1, }, }, - want: []error{errors.New("account_defaults.price_floors.fetch.max_rules should not be less than 0 seconds and greater than maximum integer value")}, + want: []error{errors.New("account_defaults.price_floors.fetch.max_file_size_kb should be greater than or equal to 0")}, }, { - description: "Invalid Max file size", + description: "Invalid max_schema_dims", pf: &AccountPriceFloors{ EnforceFloorsRate: 100, - Fetch: AccountFloorFetch{ - Enabled: true, - Timeout: 11, - MaxFileSize: -1, - MaxRules: 1, - MaxAge: 700, - Period: 400, + MaxRule: 200, + MaxSchemaDims: 10, + Fetcher: AccountFloorFetch{ + Period: 300, + MaxAge: 600, + Timeout: 12, + MaxFileSizeKB: 10, + MaxSchemaDims: 40, }, }, - want: []error{errors.New("account_defaults.price_floors.fetch.max_file_size_kb should not be less than 0 seconds and greater than maximum integer value")}, + want: []error{errors.New("account_defaults.price_floors.fetch.max_schema_dims should not be less than 0 and greater than 20")}, }, } for _, tt := range tests { diff --git a/config/bidderinfo.go b/config/bidderinfo.go index 559038c0ffe..d78f5722552 100644 --- a/config/bidderinfo.go +++ b/config/bidderinfo.go @@ -9,10 +9,8 @@ import ( "strings" "text/template" - "github.com/golang/glog" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/sliceutil" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" validator "github.com/asaskevich/govalidator" "gopkg.in/yaml.v3" @@ -38,9 +36,6 @@ type BidderInfo struct { Experiment BidderInfoExperiment `yaml:"experiment" mapstructure:"experiment"` - // needed for backwards compatibility - UserSyncURL string `yaml:"usersync_url" mapstructure:"usersync_url"` - // needed for Rubicon XAPI AdapterXAPI `yaml:"xapi" mapstructure:"xapi"` @@ -78,6 +73,7 @@ type MaintainerInfo struct { type CapabilitiesInfo struct { App *PlatformInfo `yaml:"app" mapstructure:"app"` Site *PlatformInfo `yaml:"site" mapstructure:"site"` + DOOH *PlatformInfo `yaml:"dooh" mapstructure:"dooh"` } // PlatformInfo specifies the supported media types for a bidder. @@ -128,6 +124,20 @@ type Syncer struct { // SupportCORS identifies if CORS is supported for the user syncing endpoints. SupportCORS *bool `yaml:"supportCors" mapstructure:"support_cors"` + + // FormatOverride allows a bidder to override their callback type "b" for iframe, "i" for redirect + FormatOverride string `yaml:"formatOverride" mapstructure:"format_override"` + + // Enabled signifies whether a bidder is enabled/disabled for user sync + Enabled *bool `yaml:"enabled" mapstructure:"enabled"` + + // SkipWhen allows bidders to specify when they don't want to sync + SkipWhen *SkipWhen `yaml:"skipwhen" mapstructure:"skipwhen"` +} + +type SkipWhen struct { + GDPR bool `yaml:"gdpr" mapstructure:"gdpr"` + GPPSID []string `yaml:"gpp_sid" mapstructure:"gpp_sid"` } // SyncerEndpoint specifies the configuration of the URL returned by the /cookie_sync endpoint @@ -199,6 +209,11 @@ type InfoReaderFromDisk struct { Path string } +const ( + SyncResponseFormatIFrame = "b" // b = blank HTML response + SyncResponseFormatRedirect = "i" // i = image response +) + func (r InfoReaderFromDisk) Read() (map[string][]byte, error) { bidderConfigs, err := os.ReadDir(r.Path) if err != nil { @@ -320,11 +335,13 @@ func processBidderAliases(aliasNillableFieldsByBidder map[string]aliasNillableFi if aliasBidderInfo.PlatformID == "" { aliasBidderInfo.PlatformID = parentBidderInfo.PlatformID } - if aliasBidderInfo.Syncer == nil { - aliasBidderInfo.Syncer = parentBidderInfo.Syncer - } - if aliasBidderInfo.UserSyncURL == "" { - aliasBidderInfo.UserSyncURL = parentBidderInfo.UserSyncURL + if aliasBidderInfo.Syncer == nil && parentBidderInfo.Syncer != nil { + syncerKey := aliasBidderInfo.AliasOf + if parentBidderInfo.Syncer.Key != "" { + syncerKey = parentBidderInfo.Syncer.Key + } + syncer := Syncer{Key: syncerKey} + aliasBidderInfo.Syncer = &syncer } if alias.Disabled == nil { aliasBidderInfo.Disabled = parentBidderInfo.Disabled @@ -461,7 +478,9 @@ func validateAliasCapabilities(aliasBidderInfo BidderInfo, infos BidderInfos, bi return fmt.Errorf("capabilities for alias: %s should be a subset of capabilities for parent bidder: %s", bidderName, aliasBidderInfo.AliasOf) } - if (aliasBidderInfo.Capabilities.App != nil && parentBidder.Capabilities.App == nil) || (aliasBidderInfo.Capabilities.Site != nil && parentBidder.Capabilities.Site == nil) { + if (aliasBidderInfo.Capabilities.App != nil && parentBidder.Capabilities.App == nil) || + (aliasBidderInfo.Capabilities.Site != nil && parentBidder.Capabilities.Site == nil) || + (aliasBidderInfo.Capabilities.DOOH != nil && parentBidder.Capabilities.DOOH == nil) { return fmt.Errorf("capabilities for alias: %s should be a subset of capabilities for parent bidder: %s", bidderName, aliasBidderInfo.AliasOf) } @@ -476,6 +495,12 @@ func validateAliasCapabilities(aliasBidderInfo BidderInfo, infos BidderInfos, bi return err } } + + if aliasBidderInfo.Capabilities.DOOH != nil && parentBidder.Capabilities.DOOH != nil { + if err := isAliasPlatformInfoSubsetOfParent(*parentBidder.Capabilities.DOOH, *aliasBidderInfo.Capabilities.DOOH, bidderName, aliasBidderInfo.AliasOf); err != nil { + return err + } + } } return nil @@ -501,8 +526,8 @@ func validateCapabilities(info *CapabilitiesInfo, bidderName string) error { return fmt.Errorf("missing required field: capabilities for adapter: %s", bidderName) } - if info.App == nil && info.Site == nil { - return fmt.Errorf("at least one of capabilities.site or capabilities.app must exist for adapter: %s", bidderName) + if info.App == nil && info.Site == nil && info.DOOH == nil { + return fmt.Errorf("at least one of capabilities.site, capabilities.app, or capabilities.dooh must exist for adapter: %s", bidderName) } if info.App != nil { @@ -513,9 +538,16 @@ func validateCapabilities(info *CapabilitiesInfo, bidderName string) error { if info.Site != nil { if err := validatePlatformInfo(info.Site); err != nil { - return fmt.Errorf("capabilities.site failed validation: %v, for adapter: %s", err, bidderName) + return fmt.Errorf("capabilities.site failed validation: %v for adapter: %s", err, bidderName) } } + + if info.DOOH != nil { + if err := validatePlatformInfo(info.DOOH); err != nil { + return fmt.Errorf("capabilities.dooh failed validation: %v for adapter: %s", err, bidderName) + } + } + return nil } @@ -538,6 +570,10 @@ func validateSyncer(bidderInfo BidderInfo) error { return nil } + if bidderInfo.Syncer.FormatOverride != SyncResponseFormatIFrame && bidderInfo.Syncer.FormatOverride != SyncResponseFormatRedirect && bidderInfo.Syncer.FormatOverride != "" { + return fmt.Errorf("syncer could not be created, invalid format override value: %s", bidderInfo.Syncer.FormatOverride) + } + for _, supports := range bidderInfo.Syncer.Supports { if !strings.EqualFold(supports, "iframe") && !strings.EqualFold(supports, "redirect") { return fmt.Errorf("syncer could not be created, invalid supported endpoint: %s", supports) @@ -547,106 +583,79 @@ func validateSyncer(bidderInfo BidderInfo) error { return nil } -func applyBidderInfoConfigOverrides(configBidderInfos BidderInfos, fsBidderInfos BidderInfos, normalizeBidderName func(string) (openrtb_ext.BidderName, bool)) (BidderInfos, error) { - for bidderName, bidderInfo := range configBidderInfos { - normalizedBidderName, bidderNameExists := normalizeBidderName(bidderName) - if !bidderNameExists { +func applyBidderInfoConfigOverrides(configBidderInfos nillableFieldBidderInfos, fsBidderInfos BidderInfos, normalizeBidderName func(string) (openrtb_ext.BidderName, bool)) (BidderInfos, error) { + mergedBidderInfos := make(map[string]BidderInfo, len(fsBidderInfos)) + + for bidderName, configBidderInfo := range configBidderInfos { + normalizedBidderName, exists := normalizeBidderName(bidderName) + if !exists { return nil, fmt.Errorf("error setting configuration for bidder %s: unknown bidder", bidderName) } - if fsBidderCfg, exists := fsBidderInfos[string(normalizedBidderName)]; exists { - bidderInfo.Syncer = bidderInfo.Syncer.Override(fsBidderCfg.Syncer) - - if bidderInfo.Endpoint == "" && len(fsBidderCfg.Endpoint) > 0 { - bidderInfo.Endpoint = fsBidderCfg.Endpoint - } - if bidderInfo.ExtraAdapterInfo == "" && len(fsBidderCfg.ExtraAdapterInfo) > 0 { - bidderInfo.ExtraAdapterInfo = fsBidderCfg.ExtraAdapterInfo - } - if bidderInfo.Maintainer == nil && fsBidderCfg.Maintainer != nil { - bidderInfo.Maintainer = fsBidderCfg.Maintainer - } - if bidderInfo.Capabilities == nil && fsBidderCfg.Capabilities != nil { - bidderInfo.Capabilities = fsBidderCfg.Capabilities - } - if bidderInfo.Debug == nil && fsBidderCfg.Debug != nil { - bidderInfo.Debug = fsBidderCfg.Debug - } - if bidderInfo.GVLVendorID == 0 && fsBidderCfg.GVLVendorID > 0 { - bidderInfo.GVLVendorID = fsBidderCfg.GVLVendorID - } - if bidderInfo.XAPI.Username == "" && fsBidderCfg.XAPI.Username != "" { - bidderInfo.XAPI.Username = fsBidderCfg.XAPI.Username - } - if bidderInfo.XAPI.Password == "" && fsBidderCfg.XAPI.Password != "" { - bidderInfo.XAPI.Password = fsBidderCfg.XAPI.Password - } - if bidderInfo.XAPI.Tracker == "" && fsBidderCfg.XAPI.Tracker != "" { - bidderInfo.XAPI.Tracker = fsBidderCfg.XAPI.Tracker - } - if bidderInfo.PlatformID == "" && fsBidderCfg.PlatformID != "" { - bidderInfo.PlatformID = fsBidderCfg.PlatformID - } - if bidderInfo.AppSecret == "" && fsBidderCfg.AppSecret != "" { - bidderInfo.AppSecret = fsBidderCfg.AppSecret - } - if bidderInfo.EndpointCompression == "" && fsBidderCfg.EndpointCompression != "" { - bidderInfo.EndpointCompression = fsBidderCfg.EndpointCompression - } - if bidderInfo.OpenRTB == nil && fsBidderCfg.OpenRTB != nil { - bidderInfo.OpenRTB = fsBidderCfg.OpenRTB - } - - // validate and try to apply the legacy usersync_url configuration in attempt to provide - // an easier upgrade path. be warned, this will break if the bidder adds a second syncer - // type and will eventually be removed after we've given hosts enough time to upgrade to - // the new config. - if bidderInfo.UserSyncURL != "" { - if fsBidderCfg.Syncer == nil { - return nil, fmt.Errorf("adapters.%s.usersync_url cannot be applied, bidder does not define a user sync", strings.ToLower(bidderName)) - } - - endpointsCount := 0 - if bidderInfo.Syncer.IFrame != nil { - bidderInfo.Syncer.IFrame.URL = bidderInfo.UserSyncURL - endpointsCount++ - } - if bidderInfo.Syncer.Redirect != nil { - bidderInfo.Syncer.Redirect.URL = bidderInfo.UserSyncURL - endpointsCount++ - } - - // use Supports as a hint if there are no good defaults provided - if endpointsCount == 0 { - if sliceutil.ContainsStringIgnoreCase(bidderInfo.Syncer.Supports, "iframe") { - bidderInfo.Syncer.IFrame = &SyncerEndpoint{URL: bidderInfo.UserSyncURL} - endpointsCount++ - } - if sliceutil.ContainsStringIgnoreCase(bidderInfo.Syncer.Supports, "redirect") { - bidderInfo.Syncer.Redirect = &SyncerEndpoint{URL: bidderInfo.UserSyncURL} - endpointsCount++ - } - } - - if endpointsCount == 0 { - return nil, fmt.Errorf("adapters.%s.usersync_url cannot be applied, bidder does not define user sync endpoints and does not define supported endpoints", strings.ToLower(bidderName)) - } - - // if the bidder defines both an iframe and redirect endpoint, we can't be sure which config value to - // override, and it wouldn't be both. this is a fatal configuration error. - if endpointsCount > 1 { - return nil, fmt.Errorf("adapters.%s.usersync_url cannot be applied, bidder defines multiple user sync endpoints or supports multiple endpoints", strings.ToLower(bidderName)) - } + fsBidderInfo, exists := fsBidderInfos[string(normalizedBidderName)] + if !exists { + return nil, fmt.Errorf("error finding configuration for bidder %s: unknown bidder", bidderName) + } - // provide a warning that this compatibility layer is temporary - glog.Warningf("adapters.%s.usersync_url is deprecated and will be removed in a future version, please update to the latest user sync config values", strings.ToLower(bidderName)) - } + mergedBidderInfo := fsBidderInfo + mergedBidderInfo.Syncer = configBidderInfo.bidderInfo.Syncer.Override(fsBidderInfo.Syncer) + if len(configBidderInfo.bidderInfo.Endpoint) > 0 { + mergedBidderInfo.Endpoint = configBidderInfo.bidderInfo.Endpoint + } + if len(configBidderInfo.bidderInfo.ExtraAdapterInfo) > 0 { + mergedBidderInfo.ExtraAdapterInfo = configBidderInfo.bidderInfo.ExtraAdapterInfo + } + if configBidderInfo.bidderInfo.Maintainer != nil { + mergedBidderInfo.Maintainer = configBidderInfo.bidderInfo.Maintainer + } + if configBidderInfo.bidderInfo.Capabilities != nil { + mergedBidderInfo.Capabilities = configBidderInfo.bidderInfo.Capabilities + } + if configBidderInfo.bidderInfo.Debug != nil { + mergedBidderInfo.Debug = configBidderInfo.bidderInfo.Debug + } + if configBidderInfo.bidderInfo.GVLVendorID > 0 { + mergedBidderInfo.GVLVendorID = configBidderInfo.bidderInfo.GVLVendorID + } + if configBidderInfo.bidderInfo.XAPI.Username != "" { + mergedBidderInfo.XAPI.Username = configBidderInfo.bidderInfo.XAPI.Username + } + if configBidderInfo.bidderInfo.XAPI.Password != "" { + mergedBidderInfo.XAPI.Password = configBidderInfo.bidderInfo.XAPI.Password + } + if configBidderInfo.bidderInfo.XAPI.Tracker != "" { + mergedBidderInfo.XAPI.Tracker = configBidderInfo.bidderInfo.XAPI.Tracker + } + if configBidderInfo.bidderInfo.PlatformID != "" { + mergedBidderInfo.PlatformID = configBidderInfo.bidderInfo.PlatformID + } + if configBidderInfo.bidderInfo.AppSecret != "" { + mergedBidderInfo.AppSecret = configBidderInfo.bidderInfo.AppSecret + } + if configBidderInfo.nillableFields.Disabled != nil { + mergedBidderInfo.Disabled = configBidderInfo.bidderInfo.Disabled + } + if configBidderInfo.nillableFields.ModifyingVastXmlAllowed != nil { + mergedBidderInfo.ModifyingVastXmlAllowed = configBidderInfo.bidderInfo.ModifyingVastXmlAllowed + } + if configBidderInfo.bidderInfo.Experiment.AdsCert.Enabled == true { + mergedBidderInfo.Experiment.AdsCert.Enabled = true + } + if configBidderInfo.bidderInfo.EndpointCompression != "" { + mergedBidderInfo.EndpointCompression = configBidderInfo.bidderInfo.EndpointCompression + } + if configBidderInfo.bidderInfo.OpenRTB != nil { + mergedBidderInfo.OpenRTB = configBidderInfo.bidderInfo.OpenRTB + } - fsBidderInfos[string(normalizedBidderName)] = bidderInfo - } else { - return nil, fmt.Errorf("error finding configuration for bidder %s: unknown bidder", bidderName) + mergedBidderInfos[string(normalizedBidderName)] = mergedBidderInfo + } + for bidderName, fsBidderInfo := range fsBidderInfos { + if _, exists := mergedBidderInfos[bidderName]; !exists { + mergedBidderInfos[bidderName] = fsBidderInfo } } - return fsBidderInfos, nil + + return mergedBidderInfos, nil } // Override returns a new Syncer object where values in the original are replaced by non-empty/non-default diff --git a/config/bidderinfo_test.go b/config/bidderinfo_test.go index 4df773f0b5f..3305e34e88a 100644 --- a/config/bidderinfo_test.go +++ b/config/bidderinfo_test.go @@ -7,8 +7,9 @@ import ( "gopkg.in/yaml.v3" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) const testInfoFilesPathValid = "./test/bidder-info-valid" @@ -31,6 +32,9 @@ capabilities: - banner - video - native + dooh: + mediaTypes: + - banner modifyingVastXmlAllowed: true debug: allow: true @@ -47,7 +51,6 @@ disabled: false extra_info: extra-info app_secret: app-secret platform_id: 123 -usersync_url: user-url userSync: key: foo default: iframe @@ -92,6 +95,9 @@ func TestLoadBidderInfoFromDisk(t *testing.T) { Site: &PlatformInfo{ MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative}, }, + DOOH: &PlatformInfo{ + MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo}, + }, }, Syncer: &Syncer{ Key: "foo", @@ -176,6 +182,9 @@ func TestProcessBidderInfo(t *testing.T) { Site: &PlatformInfo{ MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative}, }, + DOOH: &PlatformInfo{ + MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}, + }, }, Debug: &DebugInfo{ Allow: true, @@ -208,7 +217,6 @@ func TestProcessBidderInfo(t *testing.T) { UserMacro: "UID", }, }, - UserSyncURL: "user-url", XAPI: AdapterXAPI{ Username: "uname", Password: "pwd", @@ -225,6 +233,9 @@ func TestProcessBidderInfo(t *testing.T) { Site: &PlatformInfo{ MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative}, }, + DOOH: &PlatformInfo{ + MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}, + }, }, Debug: &DebugInfo{ Allow: true, @@ -250,14 +261,7 @@ func TestProcessBidderInfo(t *testing.T) { PlatformID: "123", Syncer: &Syncer{ Key: "foo", - IFrame: &SyncerEndpoint{ - URL: "https://foo.com/sync?mode=iframe&r={{.RedirectURL}}", - RedirectURL: "https://redirect/setuid/iframe", - ExternalURL: "https://iframe.host", - UserMacro: "UID", - }, }, - UserSyncURL: "user-url", XAPI: AdapterXAPI{ Username: "uname", Password: "pwd", @@ -267,6 +271,7 @@ func TestProcessBidderInfo(t *testing.T) { }, }, } + for _, test := range testCases { reader := StubInfoReader{test.bidderInfos} bidderInfos, err := processBidderInfos(reader, mockNormalizeBidderName) @@ -275,13 +280,11 @@ func TestProcessBidderInfo(t *testing.T) { } else { assert.Equal(t, test.expectedBidderInfos, bidderInfos, "incorrect bidder infos for test case: %s", test.description) } - } - } func TestProcessAliasBidderInfo(t *testing.T) { - parentBidderInfo := BidderInfo{ + parentWithSyncerKey := BidderInfo{ AppSecret: "app-secret", Capabilities: &CapabilitiesInfo{ App: &PlatformInfo{ @@ -322,7 +325,6 @@ func TestProcessAliasBidderInfo(t *testing.T) { UserMacro: "UID", }, }, - UserSyncURL: "user-url", XAPI: AdapterXAPI{ Username: "uname", Password: "pwd", @@ -370,15 +372,35 @@ func TestProcessAliasBidderInfo(t *testing.T) { UserMacro: "alias-UID", }, }, - UserSyncURL: "alias-user-url", XAPI: AdapterXAPI{ Username: "alias-uname", Password: "alias-pwd", Tracker: "alias-tracker", }, } - bidderB := parentBidderInfo + bidderB := parentWithSyncerKey bidderB.AliasOf = "bidderA" + bidderB.Syncer = &Syncer{ + Key: bidderB.Syncer.Key, + } + + parentWithoutSyncerKey := BidderInfo{ + Syncer: &Syncer{ + IFrame: &SyncerEndpoint{ + URL: "https://foo.com/sync?mode=iframe&r={{.RedirectURL}}", + RedirectURL: "https://redirect/setuid/iframe", + ExternalURL: "https://iframe.host", + UserMacro: "UID", + }, + }, + } + + bidderC := parentWithoutSyncerKey + bidderC.AliasOf = "bidderA" + bidderC.Syncer = &Syncer{ + Key: "bidderA", + } + testCases := []struct { description string aliasInfos map[string]aliasNillableFields @@ -387,7 +409,7 @@ func TestProcessAliasBidderInfo(t *testing.T) { expectedErr error }{ { - description: "inherit all parent info in alias bidder", + description: "inherit all parent info in alias bidder, use parent syncer key as syncer alias key", aliasInfos: map[string]aliasNillableFields{ "bidderB": { Disabled: nil, @@ -397,14 +419,34 @@ func TestProcessAliasBidderInfo(t *testing.T) { }, }, bidderInfos: BidderInfos{ - "bidderA": parentBidderInfo, + "bidderA": parentWithSyncerKey, "bidderB": BidderInfo{ AliasOf: "bidderA", // all other fields should be inherited from parent bidder }, }, expectedErr: nil, - expectedBidderInfos: BidderInfos{"bidderA": parentBidderInfo, "bidderB": bidderB}, + expectedBidderInfos: BidderInfos{"bidderA": parentWithSyncerKey, "bidderB": bidderB}, + }, + { + description: "inherit all parent info in alias bidder, use parent name as syncer alias key", + aliasInfos: map[string]aliasNillableFields{ + "bidderC": { + Disabled: nil, + ModifyingVastXmlAllowed: nil, + Experiment: nil, + XAPI: nil, + }, + }, + bidderInfos: BidderInfos{ + "bidderA": parentWithoutSyncerKey, + "bidderC": BidderInfo{ + AliasOf: "bidderA", + // all other fields should be inherited from parent bidder + }, + }, + expectedErr: nil, + expectedBidderInfos: BidderInfos{"bidderA": parentWithoutSyncerKey, "bidderC": bidderC}, }, { description: "all bidder info specified for alias, do not inherit from parent bidder", @@ -417,11 +459,11 @@ func TestProcessAliasBidderInfo(t *testing.T) { }, }, bidderInfos: BidderInfos{ - "bidderA": parentBidderInfo, + "bidderA": parentWithSyncerKey, "bidderB": aliasBidderInfo, }, expectedErr: nil, - expectedBidderInfos: BidderInfos{"bidderA": parentBidderInfo, "bidderB": aliasBidderInfo}, + expectedBidderInfos: BidderInfos{"bidderA": parentWithSyncerKey, "bidderB": aliasBidderInfo}, }, { description: "invalid alias", @@ -449,7 +491,7 @@ func TestProcessAliasBidderInfo(t *testing.T) { if test.expectedErr != nil { assert.Equal(t, test.expectedErr, err) } else { - assert.Equal(t, test.expectedBidderInfos, bidderInfos) + assert.Equal(t, test.expectedBidderInfos, bidderInfos, test.description) } } } @@ -533,7 +575,7 @@ func TestBidderInfoValidationPositive(t *testing.T) { Endpoint: "http://bidderB.com/openrtb2", PlatformID: "B", Maintainer: &MaintainerInfo{ - Email: "maintainer@bidderA.com", + Email: "maintainer@bidderB.com", }, GVLVendorID: 2, Capabilities: &CapabilitiesInfo{ @@ -551,6 +593,7 @@ func TestBidderInfoValidationPositive(t *testing.T) { URL: "http://bidderB.com/usersync", UserMacro: "UID", }, + FormatOverride: SyncResponseFormatRedirect, }, }, "bidderC": BidderInfo{ @@ -569,6 +612,26 @@ func TestBidderInfoValidationPositive(t *testing.T) { }, AliasOf: "bidderB", }, + "bidderD": BidderInfo{ + Endpoint: "http://bidderD.com/openrtb2", + PlatformID: "D", + Maintainer: &MaintainerInfo{ + Email: "maintainer@bidderD.com", + }, + GVLVendorID: 3, + Capabilities: &CapabilitiesInfo{ + DOOH: &PlatformInfo{ + MediaTypes: []openrtb_ext.BidType{ + openrtb_ext.BidTypeVideo, + openrtb_ext.BidTypeNative, + openrtb_ext.BidTypeBanner, + }, + }, + }, + Syncer: &Syncer{ + FormatOverride: SyncResponseFormatIFrame, + }, + }, } errs := bidderInfos.validate(make([]error, 0)) assert.Len(t, errs, 0, "All bidder infos should be correct") @@ -769,7 +832,7 @@ func TestBidderInfoValidationNegative(t *testing.T) { }, }, { - "One bidder missing capabilities site and app", + "One bidder missing capabilities site and app and dooh", BidderInfos{ "bidderA": BidderInfo{ Endpoint: "http://bidderA.com/openrtb2", @@ -780,7 +843,7 @@ func TestBidderInfoValidationNegative(t *testing.T) { }, }, []error{ - errors.New("at least one of capabilities.site or capabilities.app must exist for adapter: bidderA"), + errors.New("at least one of capabilities.site, capabilities.app, or capabilities.dooh must exist for adapter: bidderA"), }, }, { @@ -804,6 +867,27 @@ func TestBidderInfoValidationNegative(t *testing.T) { errors.New("capabilities.app failed validation: unrecognized media type at index 0: incorrect for adapter: bidderA"), }, }, + { + "One bidder incorrect capabilities for dooh", + BidderInfos{ + "bidderA": BidderInfo{ + Endpoint: "http://bidderA.com/openrtb2", + Maintainer: &MaintainerInfo{ + Email: "maintainer@bidderA.com", + }, + Capabilities: &CapabilitiesInfo{ + DOOH: &PlatformInfo{ + MediaTypes: []openrtb_ext.BidType{ + "incorrect", + }, + }, + }, + }, + }, + []error{ + errors.New("capabilities.dooh failed validation: unrecognized media type at index 0: incorrect for adapter: bidderA"), + }, + }, { "One bidder nil capabilities", BidderInfos{ @@ -1008,7 +1092,7 @@ func TestBidderInfoValidationNegative(t *testing.T) { }, }, []error{ - errors.New("at least one of capabilities.site or capabilities.app must exist for adapter: bidderA"), + errors.New("at least one of capabilities.site, capabilities.app, or capabilities.dooh must exist for adapter: bidderA"), errors.New("capabilities for alias: bidderB should be a subset of capabilities for parent bidder: bidderA"), }, }, @@ -1238,6 +1322,37 @@ func TestBidderInfoValidationNegative(t *testing.T) { errors.New("parent bidder: bidderC not found for an alias: bidderB"), }, }, + { + "Invalid format override value", + BidderInfos{ + "bidderB": BidderInfo{ + Endpoint: "http://bidderA.com/openrtb2", + Maintainer: &MaintainerInfo{ + Email: "maintainer@bidderA.com", + }, + Capabilities: &CapabilitiesInfo{ + App: &PlatformInfo{ + MediaTypes: []openrtb_ext.BidType{ + openrtb_ext.BidTypeBanner, + openrtb_ext.BidTypeNative, + }, + }, + Site: &PlatformInfo{ + MediaTypes: []openrtb_ext.BidType{ + openrtb_ext.BidTypeBanner, + openrtb_ext.BidTypeNative, + }, + }, + }, + Syncer: &Syncer{ + FormatOverride: "x", + }, + }, + }, + []error{ + errors.New("syncer could not be created, invalid format override value: x"), + }, + }, } for _, test := range testCases { @@ -1408,122 +1523,65 @@ func TestSyncerEndpointOverride(t *testing.T) { } func TestApplyBidderInfoConfigSyncerOverrides(t *testing.T) { - var testCases = []struct { - description string - givenFsBidderInfos BidderInfos - givenConfigBidderInfos BidderInfos - expectedError string - expectedBidderInfos BidderInfos - }{ - { - description: "Syncer Override", - givenFsBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "original"}}}, - givenConfigBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}, - expectedBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}, - }, - { - description: "UserSyncURL Override IFrame", - givenFsBidderInfos: BidderInfos{"a": {Syncer: &Syncer{IFrame: &SyncerEndpoint{URL: "original"}}}}, - givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}}, - expectedBidderInfos: BidderInfos{"a": {UserSyncURL: "override", Syncer: &Syncer{IFrame: &SyncerEndpoint{URL: "override"}}}}, - }, - { - description: "UserSyncURL Supports IFrame", - givenFsBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Supports: []string{"iframe"}}}}, - givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}}, - expectedBidderInfos: BidderInfos{"a": {UserSyncURL: "override", Syncer: &Syncer{Supports: []string{"iframe"}, IFrame: &SyncerEndpoint{URL: "override"}}}}, - }, - { - description: "UserSyncURL Override Redirect", - givenFsBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Supports: []string{"redirect"}}}}, - givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}}, - expectedBidderInfos: BidderInfos{"a": {UserSyncURL: "override", Syncer: &Syncer{Supports: []string{"redirect"}, Redirect: &SyncerEndpoint{URL: "override"}}}}, - }, - { - description: "UserSyncURL Supports Redirect", - givenFsBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Redirect: &SyncerEndpoint{URL: "original"}}}}, - givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}}, - expectedBidderInfos: BidderInfos{"a": {UserSyncURL: "override", Syncer: &Syncer{Redirect: &SyncerEndpoint{URL: "override"}}}}, - }, - { - description: "UserSyncURL Override Syncer Not Defined", - givenFsBidderInfos: BidderInfos{"a": {}}, - givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}}, - expectedError: "adapters.a.usersync_url cannot be applied, bidder does not define a user sync", - }, - { - description: "UserSyncURL Override Syncer Endpoints Not Defined", - givenFsBidderInfos: BidderInfos{"a": {Syncer: &Syncer{}}}, - givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}}, - expectedError: "adapters.a.usersync_url cannot be applied, bidder does not define user sync endpoints and does not define supported endpoints", - }, - { - description: "UserSyncURL Override Ambiguous", - givenFsBidderInfos: BidderInfos{"a": {Syncer: &Syncer{IFrame: &SyncerEndpoint{URL: "originalIFrame"}, Redirect: &SyncerEndpoint{URL: "originalRedirect"}}}}, - givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}}, - expectedError: "adapters.a.usersync_url cannot be applied, bidder defines multiple user sync endpoints or supports multiple endpoints", - }, - { - description: "UserSyncURL Supports Ambiguous", - givenFsBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Supports: []string{"iframe", "redirect"}}}}, - givenConfigBidderInfos: BidderInfos{"a": {UserSyncURL: "override"}}, - expectedError: "adapters.a.usersync_url cannot be applied, bidder defines multiple user sync endpoints or supports multiple endpoints", - }, - } - - for _, test := range testCases { - bidderInfos, resultErr := applyBidderInfoConfigOverrides(test.givenConfigBidderInfos, test.givenFsBidderInfos, mockNormalizeBidderName) - if test.expectedError == "" { - assert.NoError(t, resultErr, test.description+":err") - assert.Equal(t, test.expectedBidderInfos, bidderInfos, test.description+":result") - } else { - assert.EqualError(t, resultErr, test.expectedError, test.description+":err") + var ( + givenFileSystem = BidderInfos{"a": {Syncer: &Syncer{Key: "original"}}} + givenConfig = nillableFieldBidderInfos{ + "a": { + bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}, + }, } - } + expected = BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}} + ) + + result, resultErr := applyBidderInfoConfigOverrides(givenConfig, givenFileSystem, mockNormalizeBidderName) + assert.NoError(t, resultErr) + assert.Equal(t, expected, result) } func TestApplyBidderInfoConfigOverrides(t *testing.T) { + falseValue := false + var testCases = []struct { description string givenFsBidderInfos BidderInfos - givenConfigBidderInfos BidderInfos + givenConfigBidderInfos nillableFieldBidderInfos expectedError string expectedBidderInfos BidderInfos }{ { description: "Don't override endpoint", givenFsBidderInfos: BidderInfos{"a": {Endpoint: "original"}}, - givenConfigBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {Endpoint: "original", Syncer: &Syncer{Key: "override"}}}, }, { description: "Override endpoint", givenFsBidderInfos: BidderInfos{"a": {Endpoint: "original"}}, - givenConfigBidderInfos: BidderInfos{"a": {Endpoint: "override", Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Endpoint: "override", Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {Endpoint: "override", Syncer: &Syncer{Key: "override"}}}, }, { description: "Don't override ExtraAdapterInfo", givenFsBidderInfos: BidderInfos{"a": {ExtraAdapterInfo: "original"}}, - givenConfigBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {ExtraAdapterInfo: "original", Syncer: &Syncer{Key: "override"}}}, }, { description: "Override ExtraAdapterInfo", givenFsBidderInfos: BidderInfos{"a": {ExtraAdapterInfo: "original"}}, - givenConfigBidderInfos: BidderInfos{"a": {ExtraAdapterInfo: "override", Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{ExtraAdapterInfo: "override", Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {ExtraAdapterInfo: "override", Syncer: &Syncer{Key: "override"}}}, }, { description: "Don't override Maintainer", givenFsBidderInfos: BidderInfos{"a": {Maintainer: &MaintainerInfo{Email: "original"}}}, - givenConfigBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {Maintainer: &MaintainerInfo{Email: "original"}, Syncer: &Syncer{Key: "override"}}}, }, { description: "Override maintainer", givenFsBidderInfos: BidderInfos{"a": {Maintainer: &MaintainerInfo{Email: "original"}}}, - givenConfigBidderInfos: BidderInfos{"a": {Maintainer: &MaintainerInfo{Email: "override"}, Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Maintainer: &MaintainerInfo{Email: "override"}, Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {Maintainer: &MaintainerInfo{Email: "override"}, Syncer: &Syncer{Key: "override"}}}, }, { @@ -1531,7 +1589,7 @@ func TestApplyBidderInfoConfigOverrides(t *testing.T) { givenFsBidderInfos: BidderInfos{"a": { Capabilities: &CapabilitiesInfo{App: &PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeVideo}}}, }}, - givenConfigBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": { Syncer: &Syncer{Key: "override"}, Capabilities: &CapabilitiesInfo{App: &PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeVideo}}}, @@ -1542,10 +1600,10 @@ func TestApplyBidderInfoConfigOverrides(t *testing.T) { givenFsBidderInfos: BidderInfos{"a": { Capabilities: &CapabilitiesInfo{App: &PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeVideo}}}, }}, - givenConfigBidderInfos: BidderInfos{"a": { + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{ Syncer: &Syncer{Key: "override"}, Capabilities: &CapabilitiesInfo{App: &PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}}}, - }}, + }}}, expectedBidderInfos: BidderInfos{"a": { Syncer: &Syncer{Key: "override"}, Capabilities: &CapabilitiesInfo{App: &PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}}}, @@ -1554,25 +1612,25 @@ func TestApplyBidderInfoConfigOverrides(t *testing.T) { { description: "Don't override Debug", givenFsBidderInfos: BidderInfos{"a": {Debug: &DebugInfo{Allow: true}}}, - givenConfigBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {Debug: &DebugInfo{Allow: true}, Syncer: &Syncer{Key: "override"}}}, }, { description: "Override Debug", givenFsBidderInfos: BidderInfos{"a": {Debug: &DebugInfo{Allow: true}}}, - givenConfigBidderInfos: BidderInfos{"a": {Debug: &DebugInfo{Allow: false}, Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Debug: &DebugInfo{Allow: false}, Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {Debug: &DebugInfo{Allow: false}, Syncer: &Syncer{Key: "override"}}}, }, { description: "Don't override GVLVendorID", givenFsBidderInfos: BidderInfos{"a": {GVLVendorID: 5}}, - givenConfigBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {GVLVendorID: 5, Syncer: &Syncer{Key: "override"}}}, }, { description: "Override GVLVendorID", givenFsBidderInfos: BidderInfos{"a": {}}, - givenConfigBidderInfos: BidderInfos{"a": {GVLVendorID: 5, Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{GVLVendorID: 5, Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {GVLVendorID: 5, Syncer: &Syncer{Key: "override"}}}, }, { @@ -1580,7 +1638,7 @@ func TestApplyBidderInfoConfigOverrides(t *testing.T) { givenFsBidderInfos: BidderInfos{"a": { XAPI: AdapterXAPI{Username: "username1", Password: "password2", Tracker: "tracker3"}, }}, - givenConfigBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": { XAPI: AdapterXAPI{Username: "username1", Password: "password2", Tracker: "tracker3"}, Syncer: &Syncer{Key: "override"}}}, @@ -1589,9 +1647,9 @@ func TestApplyBidderInfoConfigOverrides(t *testing.T) { description: "Override XAPI", givenFsBidderInfos: BidderInfos{"a": { XAPI: AdapterXAPI{Username: "username", Password: "password", Tracker: "tracker"}}}, - givenConfigBidderInfos: BidderInfos{"a": { + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{ XAPI: AdapterXAPI{Username: "username1", Password: "password2", Tracker: "tracker3"}, - Syncer: &Syncer{Key: "override"}}}, + Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": { XAPI: AdapterXAPI{Username: "username1", Password: "password2", Tracker: "tracker3"}, Syncer: &Syncer{Key: "override"}}}, @@ -1599,50 +1657,92 @@ func TestApplyBidderInfoConfigOverrides(t *testing.T) { { description: "Don't override PlatformID", givenFsBidderInfos: BidderInfos{"a": {PlatformID: "PlatformID"}}, - givenConfigBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {PlatformID: "PlatformID", Syncer: &Syncer{Key: "override"}}}, }, { description: "Override PlatformID", givenFsBidderInfos: BidderInfos{"a": {PlatformID: "PlatformID1"}}, - givenConfigBidderInfos: BidderInfos{"a": {PlatformID: "PlatformID2", Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{PlatformID: "PlatformID2", Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {PlatformID: "PlatformID2", Syncer: &Syncer{Key: "override"}}}, }, { description: "Don't override AppSecret", givenFsBidderInfos: BidderInfos{"a": {AppSecret: "AppSecret"}}, - givenConfigBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {AppSecret: "AppSecret", Syncer: &Syncer{Key: "override"}}}, }, { description: "Override AppSecret", givenFsBidderInfos: BidderInfos{"a": {AppSecret: "AppSecret1"}}, - givenConfigBidderInfos: BidderInfos{"a": {AppSecret: "AppSecret2", Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{AppSecret: "AppSecret2", Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {AppSecret: "AppSecret2", Syncer: &Syncer{Key: "override"}}}, }, { description: "Don't override EndpointCompression", givenFsBidderInfos: BidderInfos{"a": {EndpointCompression: "GZIP"}}, - givenConfigBidderInfos: BidderInfos{"a": {Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {EndpointCompression: "GZIP", Syncer: &Syncer{Key: "override"}}}, }, { description: "Override EndpointCompression", givenFsBidderInfos: BidderInfos{"a": {EndpointCompression: "GZIP"}}, - givenConfigBidderInfos: BidderInfos{"a": {EndpointCompression: "LZ77", Syncer: &Syncer{Key: "override"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{EndpointCompression: "LZ77", Syncer: &Syncer{Key: "override"}}}}, expectedBidderInfos: BidderInfos{"a": {EndpointCompression: "LZ77", Syncer: &Syncer{Key: "override"}}}, }, + { + description: "Don't override Disabled", + givenFsBidderInfos: BidderInfos{"a": {Disabled: true}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Disabled: false, Syncer: &Syncer{Key: "override"}}, nillableFields: bidderInfoNillableFields{Disabled: nil}}}, + expectedBidderInfos: BidderInfos{"a": {Disabled: true, Syncer: &Syncer{Key: "override"}}}, + }, + { + description: "Override Disabled", + givenFsBidderInfos: BidderInfos{"a": {Disabled: true}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Disabled: false, Syncer: &Syncer{Key: "override"}}, nillableFields: bidderInfoNillableFields{Disabled: &falseValue}}}, + expectedBidderInfos: BidderInfos{"a": {Disabled: false, Syncer: &Syncer{Key: "override"}}}, + }, + { + description: "Don't override ModifyingVastXmlAllowed", + givenFsBidderInfos: BidderInfos{"a": {ModifyingVastXmlAllowed: true}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{ModifyingVastXmlAllowed: false, Syncer: &Syncer{Key: "override"}}, nillableFields: bidderInfoNillableFields{ModifyingVastXmlAllowed: nil}}}, + expectedBidderInfos: BidderInfos{"a": {ModifyingVastXmlAllowed: true, Syncer: &Syncer{Key: "override"}}}, + }, + { + description: "Override ModifyingVastXmlAllowed", + givenFsBidderInfos: BidderInfos{"a": {ModifyingVastXmlAllowed: true}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{ModifyingVastXmlAllowed: false, Syncer: &Syncer{Key: "override"}}, nillableFields: bidderInfoNillableFields{ModifyingVastXmlAllowed: &falseValue}}}, + expectedBidderInfos: BidderInfos{"a": {ModifyingVastXmlAllowed: false, Syncer: &Syncer{Key: "override"}}}, + }, { description: "Don't override OpenRTB", - givenFsBidderInfos: BidderInfos{"a": {OpenRTB: &OpenRTBInfo{Version: "v1", GPPSupported: true}}}, - givenConfigBidderInfos: BidderInfos{"a": {}}, - expectedBidderInfos: BidderInfos{"a": {OpenRTB: &OpenRTBInfo{Version: "v1", GPPSupported: true}}}, + givenFsBidderInfos: BidderInfos{"a": {OpenRTB: &OpenRTBInfo{Version: "1"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}}}, + expectedBidderInfos: BidderInfos{"a": {OpenRTB: &OpenRTBInfo{Version: "1"}, Syncer: &Syncer{Key: "override"}}}, }, { description: "Override OpenRTB", - givenFsBidderInfos: BidderInfos{"a": {OpenRTB: &OpenRTBInfo{Version: "v1", GPPSupported: true}}}, - givenConfigBidderInfos: BidderInfos{"a": {OpenRTB: &OpenRTBInfo{Version: "v2", GPPSupported: false}}}, - expectedBidderInfos: BidderInfos{"a": {OpenRTB: &OpenRTBInfo{Version: "v2", GPPSupported: false}}}, + givenFsBidderInfos: BidderInfos{"a": {OpenRTB: &OpenRTBInfo{Version: "1"}}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{OpenRTB: &OpenRTBInfo{Version: "2"}, Syncer: &Syncer{Key: "override"}}}}, + expectedBidderInfos: BidderInfos{"a": {OpenRTB: &OpenRTBInfo{Version: "2"}, Syncer: &Syncer{Key: "override"}}}, + }, + { + description: "Don't override AliasOf", + givenFsBidderInfos: BidderInfos{"a": {AliasOf: "Alias1"}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{}}}, + expectedBidderInfos: BidderInfos{"a": {AliasOf: "Alias1"}}, + }, + { + description: "Attempt override AliasOf but ignored", + givenFsBidderInfos: BidderInfos{"a": {AliasOf: "Alias1"}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{AliasOf: "Alias2"}}}, + expectedBidderInfos: BidderInfos{"a": {AliasOf: "Alias1"}}, + }, + { + description: "Two bidder infos: One with overrides and one without", + givenFsBidderInfos: BidderInfos{"a": {Endpoint: "original"}, "b": {Endpoint: "b endpoint"}}, + givenConfigBidderInfos: nillableFieldBidderInfos{"a": {bidderInfo: BidderInfo{Endpoint: "override", Syncer: &Syncer{Key: "override"}}}}, + expectedBidderInfos: BidderInfos{"a": {Endpoint: "override", Syncer: &Syncer{Key: "override"}}, "b": {Endpoint: "b endpoint"}}, }, } for _, test := range testCases { @@ -1654,26 +1754,31 @@ func TestApplyBidderInfoConfigOverrides(t *testing.T) { func TestApplyBidderInfoConfigOverridesInvalid(t *testing.T) { var testCases = []struct { - description string - givenFsBidderInfos BidderInfos - givenConfigBidderInfos BidderInfos - expectedError string - expectedBidderInfos BidderInfos + description string + givenFsBidderInfos BidderInfos + givenNillableFieldBidderInfos nillableFieldBidderInfos + expectedError string + expectedBidderInfos BidderInfos }{ { - description: "Bidder doesn't exists in bidder list", - givenConfigBidderInfos: BidderInfos{"unknown": {Syncer: &Syncer{Key: "override"}}}, - expectedError: "error setting configuration for bidder unknown: unknown bidder", + description: "Bidder doesn't exists in bidder list", + givenNillableFieldBidderInfos: nillableFieldBidderInfos{"unknown": nillableFieldBidderInfo{ + bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}, + }}, + expectedError: "error setting configuration for bidder unknown: unknown bidder", }, { - description: "Bidder doesn't exists in file system", - givenFsBidderInfos: BidderInfos{"unknown": {Endpoint: "original"}}, - givenConfigBidderInfos: BidderInfos{"bidderA": {Syncer: &Syncer{Key: "override"}}}, - expectedError: "error finding configuration for bidder bidderA: unknown bidder", + description: "Bidder doesn't exists in file system", + givenFsBidderInfos: BidderInfos{"unknown": {Endpoint: "original"}}, + givenNillableFieldBidderInfos: nillableFieldBidderInfos{"bidderA": nillableFieldBidderInfo{ + bidderInfo: BidderInfo{Syncer: &Syncer{Key: "override"}}, + }}, + expectedError: "error finding configuration for bidder bidderA: unknown bidder", }, } for _, test := range testCases { - _, err := applyBidderInfoConfigOverrides(test.givenConfigBidderInfos, test.givenFsBidderInfos, mockNormalizeBidderName) + + _, err := applyBidderInfoConfigOverrides(test.givenNillableFieldBidderInfos, test.givenFsBidderInfos, mockNormalizeBidderName) assert.ErrorContains(t, err, test.expectedError, test.description+":err") } } @@ -1681,10 +1786,24 @@ func TestApplyBidderInfoConfigOverridesInvalid(t *testing.T) { func TestReadFullYamlBidderConfig(t *testing.T) { bidder := "bidderA" bidderInf := BidderInfo{} + err := yaml.Unmarshal([]byte(fullBidderYAMLConfig), &bidderInf) - actualBidderInfo, err := applyBidderInfoConfigOverrides(BidderInfos{bidder: bidderInf}, BidderInfos{bidder: {Syncer: &Syncer{Supports: []string{"iframe"}}}}, mockNormalizeBidderName) + require.NoError(t, err) - assert.NoError(t, err, "Error wasn't expected") + bidderInfoOverrides := nillableFieldBidderInfos{ + bidder: nillableFieldBidderInfo{ + bidderInfo: bidderInf, + nillableFields: bidderInfoNillableFields{ + Disabled: &bidderInf.Disabled, + ModifyingVastXmlAllowed: &bidderInf.ModifyingVastXmlAllowed, + }, + }, + } + bidderInfoBase := BidderInfos{ + bidder: {Syncer: &Syncer{Supports: []string{"iframe"}}}, + } + actualBidderInfo, err := applyBidderInfoConfigOverrides(bidderInfoOverrides, bidderInfoBase, mockNormalizeBidderName) + require.NoError(t, err) expectedBidderInfo := BidderInfos{ bidder: { @@ -1699,6 +1818,9 @@ func TestReadFullYamlBidderConfig(t *testing.T) { Site: &PlatformInfo{ MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo, openrtb_ext.BidTypeNative}, }, + DOOH: &PlatformInfo{ + MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}, + }, }, ModifyingVastXmlAllowed: true, Debug: &DebugInfo{ @@ -1718,11 +1840,10 @@ func TestReadFullYamlBidderConfig(t *testing.T) { ExtraAdapterInfo: "extra-info", AppSecret: "app-secret", PlatformID: "123", - UserSyncURL: "user-url", Syncer: &Syncer{ Key: "foo", IFrame: &SyncerEndpoint{ - URL: "user-url", + URL: "https://foo.com/sync?mode=iframe&r={{.RedirectURL}}", RedirectURL: "https://redirect/setuid/iframe", ExternalURL: "https://iframe.host", UserMacro: "UID", diff --git a/config/compression.go b/config/compression.go index db85202b4a8..2fe8e7b22ac 100644 --- a/config/compression.go +++ b/config/compression.go @@ -1,6 +1,6 @@ package config -import "github.com/prebid/prebid-server/util/httputil" +import "github.com/prebid/prebid-server/v2/util/httputil" type Compression struct { Request CompressionInfo `mapstructure:"request"` diff --git a/config/compression_test.go b/config/compression_test.go index cd9048cd99e..230d1912345 100644 --- a/config/compression_test.go +++ b/config/compression_test.go @@ -3,7 +3,7 @@ package config import ( "testing" - "github.com/prebid/prebid-server/util/httputil" + "github.com/prebid/prebid-server/v2/util/httputil" "github.com/stretchr/testify/assert" ) diff --git a/config/config.go b/config/config.go index d31f7c47201..8e82b674cd0 100644 --- a/config/config.go +++ b/config/config.go @@ -6,16 +6,15 @@ import ( "fmt" "net/url" "reflect" - "strconv" "strings" "time" "github.com/golang/glog" "github.com/prebid/go-gdpr/consentconstants" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/spf13/viper" ) @@ -29,7 +28,6 @@ type Configuration struct { Client HTTPClient `mapstructure:"http_client"` CacheClient HTTPClient `mapstructure:"http_client_cache"` AdminPort int `mapstructure:"admin_port"` - EnableGzip bool `mapstructure:"enable_gzip"` Compression Compression `mapstructure:"compression"` // GarbageCollectorThreshold allocates virtual memory (in bytes) which is not used by PBS but // serves as a hack to trigger the garbage collector only when the heap reaches at least this size. @@ -70,9 +68,6 @@ type Configuration struct { // Array of blacklisted apps that is used to create the hash table BlacklistedAppMap so App.ID's can be instantly accessed. BlacklistedApps []string `mapstructure:"blacklisted_apps,flow"` BlacklistedAppMap map[string]bool - // Array of blacklisted accounts that is used to create the hash table BlacklistedAcctMap so Account.ID's can be instantly accessed. - BlacklistedAccts []string `mapstructure:"blacklisted_accts,flow"` - BlacklistedAcctMap map[string]bool // Is publisher/account ID required to be submitted in the OpenRTB2 request AccountRequired bool `mapstructure:"account_required"` // AccountDefaults defines default settings for valid accounts that are partially defined @@ -112,7 +107,16 @@ type Configuration struct { } type PriceFloors struct { - Enabled bool `mapstructure:"enabled"` + Enabled bool `mapstructure:"enabled"` + Fetcher PriceFloorFetcher `mapstructure:"fetcher"` +} + +type PriceFloorFetcher struct { + HttpClient HTTPClient `mapstructure:"http_client"` + CacheSize int `mapstructure:"cache_size_mb"` + Worker int `mapstructure:"worker"` + Capacity int `mapstructure:"capacity"` + MaxRetries int `mapstructure:"max_retries"` } type VendorListScheduler struct { @@ -156,12 +160,8 @@ func (cfg *Configuration) validate(v *viper.Viper) []error { glog.Warning(`With account_defaults.disabled=true, host-defined accounts must exist and have "disabled":false. All other requests will be rejected.`) } - if cfg.PriceFloors.Enabled { - glog.Warning(`cfg.PriceFloors.Enabled will currently not do anything as price floors feature is still under development.`) - } - - if len(cfg.AccountDefaults.Events.VASTEvents) > 0 { - errs = append(errs, errors.New("account_defaults.Events.VASTEvents has no effect as the feature is under development.")) + if cfg.AccountDefaults.Events.Enabled { + glog.Warning(`account_defaults.events has no effect as the feature is under development.`) } errs = cfg.Experiment.validate(errs) @@ -390,13 +390,13 @@ func (t *TCF2) PurposeEnforcingVendors(purpose consentconstants.Purpose) (enforc // PurposeVendorExceptions returns the vendor exception map for a given purpose if it exists, otherwise it returns // an empty map of vendor exceptions -func (t *TCF2) PurposeVendorExceptions(purpose consentconstants.Purpose) (vendorExceptions map[openrtb_ext.BidderName]struct{}) { +func (t *TCF2) PurposeVendorExceptions(purpose consentconstants.Purpose) (vendorExceptions map[string]struct{}) { c, exists := t.PurposeConfigs[purpose] if exists && c.VendorExceptionMap != nil { return c.VendorExceptionMap } - return make(map[openrtb_ext.BidderName]struct{}, 0) + return make(map[string]struct{}, 0) } // FeatureOneEnforced checks if special feature one is enforced. If it is enforced, PBS will determine whether geo @@ -426,15 +426,14 @@ func (t *TCF2) PurposeOneTreatmentAccessAllowed() bool { // Making a purpose struct so purpose specific details can be added later. type TCF2Purpose struct { - Enabled bool `mapstructure:"enabled"` // Deprecated: Use enforce_purpose instead EnforceAlgo string `mapstructure:"enforce_algo"` // Integer representation of enforcement algo for performance improvement on compares EnforceAlgoID TCF2EnforcementAlgo EnforcePurpose bool `mapstructure:"enforce_purpose"` EnforceVendors bool `mapstructure:"enforce_vendors"` // Array of vendor exceptions that is used to create the hash table VendorExceptionMap so vendor names can be instantly accessed - VendorExceptions []openrtb_ext.BidderName `mapstructure:"vendor_exceptions"` - VendorExceptionMap map[openrtb_ext.BidderName]struct{} + VendorExceptions []string `mapstructure:"vendor_exceptions"` + VendorExceptionMap map[string]struct{} } type TCF2SpecialFeature struct { @@ -476,11 +475,6 @@ func (cfg *CurrencyConverter) validate(errs []error) []error { return errs } -type PriceFloorFetcher struct { - Worker int `mapstructure:"worker"` - Capacity int `mapstructure:"capacity"` -} - // FileLogs Corresponding config for FileLogger as a PBS Analytics Module type FileLogs struct { Filename string `mapstructure:"filename"` @@ -727,9 +721,6 @@ func New(v *viper.Viper, bidderInfos BidderInfos, normalizeBidderName func(strin // Update account defaults and generate base json for patch c.AccountDefaults.CacheTTL = c.CacheURL.DefaultTTLs // comment this out to set explicitly in config - // Update the deprecated and new events enabled values for account defaults. - c.AccountDefaults.EventsEnabled, c.AccountDefaults.Events.Enabled = migrateConfigEventsEnabled(c.AccountDefaults.EventsEnabled, c.AccountDefaults.Events.Enabled) - if err := c.MarshalAccountDefaults(); err != nil { return nil, err } @@ -771,13 +762,13 @@ func New(v *viper.Viper, bidderInfos BidderInfos, normalizeBidderName func(strin } } - // To look for a purpose's vendor exceptions in O(1) time, for each purpose we fill this hash table with bidders - // located in the VendorExceptions field of the GDPR.TCF2.PurposeX struct defined in this file + // To look for a purpose's vendor exceptions in O(1) time, for each purpose we fill this hash table with bidders/analytics + // adapters located in the VendorExceptions field of the GDPR.TCF2.PurposeX struct defined in this file for _, pc := range c.GDPR.TCF2.PurposeConfigs { - pc.VendorExceptionMap = make(map[openrtb_ext.BidderName]struct{}) + pc.VendorExceptionMap = make(map[string]struct{}) for v := 0; v < len(pc.VendorExceptions); v++ { - bidderName := pc.VendorExceptions[v] - pc.VendorExceptionMap[bidderName] = struct{}{} + adapterName := pc.VendorExceptions[v] + pc.VendorExceptionMap[adapterName] = struct{}{} } } @@ -796,17 +787,14 @@ func New(v *viper.Viper, bidderInfos BidderInfos, normalizeBidderName func(strin c.BlacklistedAppMap[c.BlacklistedApps[i]] = true } - // To look for a request's account id in O(1) time, we fill this hash table located in the - // the BlacklistedAccts field of the Configuration struct defined in this file - c.BlacklistedAcctMap = make(map[string]bool) - for i := 0; i < len(c.BlacklistedAccts); i++ { - c.BlacklistedAcctMap[c.BlacklistedAccts[i]] = true - } - // Migrate combo stored request config to separate stored_reqs and amp stored_reqs configs. resolvedStoredRequestsConfig(&c) - mergedBidderInfos, err := applyBidderInfoConfigOverrides(c.BidderInfos, bidderInfos, normalizeBidderName) + configBidderInfosWithNillableFields, err := setConfigBidderInfoNillableFields(v, c.BidderInfos) + if err != nil { + return nil, err + } + mergedBidderInfos, err := applyBidderInfoConfigOverrides(configBidderInfosWithNillableFields, bidderInfos, normalizeBidderName) if err != nil { return nil, err } @@ -821,10 +809,40 @@ func New(v *viper.Viper, bidderInfos BidderInfos, normalizeBidderName func(strin return &c, nil } +type bidderInfoNillableFields struct { + Disabled *bool `yaml:"disabled" mapstructure:"disabled"` + ModifyingVastXmlAllowed *bool `yaml:"modifyingVastXmlAllowed" mapstructure:"modifyingVastXmlAllowed"` +} +type nillableFieldBidderInfos map[string]nillableFieldBidderInfo +type nillableFieldBidderInfo struct { + nillableFields bidderInfoNillableFields + bidderInfo BidderInfo +} + +func setConfigBidderInfoNillableFields(v *viper.Viper, bidderInfos BidderInfos) (nillableFieldBidderInfos, error) { + if len(bidderInfos) == 0 || v == nil { + return nil, nil + } + infos := make(nillableFieldBidderInfos, len(bidderInfos)) + + for bidderName, bidderInfo := range bidderInfos { + info := nillableFieldBidderInfo{bidderInfo: bidderInfo} + + if err := v.UnmarshalKey("adapters."+bidderName+".disabled", &info.nillableFields.Disabled); err != nil { + return nil, fmt.Errorf("viper failed to unmarshal bidder config disabled: %v", err) + } + if err := v.UnmarshalKey("adapters."+bidderName+".modifyingvastxmlallowed", &info.nillableFields.ModifyingVastXmlAllowed); err != nil { + return nil, fmt.Errorf("viper failed to unmarshal bidder config modifyingvastxmlallowed: %v", err) + } + infos[bidderName] = info + } + return infos, nil +} + // MarshalAccountDefaults compiles AccountDefaults into the JSON format used for merge patch func (cfg *Configuration) MarshalAccountDefaults() error { var err error - if cfg.accountDefaultsJSON, err = json.Marshal(cfg.AccountDefaults); err != nil { + if cfg.accountDefaultsJSON, err = jsonutil.Marshal(cfg.AccountDefaults); err != nil { glog.Warningf("converting %+v to json: %v", cfg.AccountDefaults, err) } return err @@ -924,6 +942,25 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) { v.SetDefault("category_mapping.filesystem.enabled", true) v.SetDefault("category_mapping.filesystem.directorypath", "./static/category-mapping") v.SetDefault("category_mapping.http.endpoint", "") + v.SetDefault("stored_requests.database.connection.driver", "") + v.SetDefault("stored_requests.database.connection.dbname", "") + v.SetDefault("stored_requests.database.connection.host", "") + v.SetDefault("stored_requests.database.connection.port", 0) + v.SetDefault("stored_requests.database.connection.user", "") + v.SetDefault("stored_requests.database.connection.password", "") + v.SetDefault("stored_requests.database.connection.query_string", "") + v.SetDefault("stored_requests.database.connection.tls.root_cert", "") + v.SetDefault("stored_requests.database.connection.tls.client_cert", "") + v.SetDefault("stored_requests.database.connection.tls.client_key", "") + v.SetDefault("stored_requests.database.fetcher.query", "") + v.SetDefault("stored_requests.database.fetcher.amp_query", "") + v.SetDefault("stored_requests.database.initialize_caches.timeout_ms", 0) + v.SetDefault("stored_requests.database.initialize_caches.query", "") + v.SetDefault("stored_requests.database.initialize_caches.amp_query", "") + v.SetDefault("stored_requests.database.poll_for_updates.refresh_rate_seconds", 0) + v.SetDefault("stored_requests.database.poll_for_updates.timeout_ms", 0) + v.SetDefault("stored_requests.database.poll_for_updates.query", "") + v.SetDefault("stored_requests.database.poll_for_updates.amp_query", "") v.SetDefault("stored_requests.filesystem.enabled", false) v.SetDefault("stored_requests.filesystem.directorypath", "./stored_requests/data/by_id") v.SetDefault("stored_requests.directorypath", "./stored_requests/data/by_id") @@ -941,6 +978,25 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) { v.SetDefault("stored_requests.http_events.timeout_ms", 0) // stored_video is short for stored_video_requests. // PBS is not in the business of storing video content beyond the normal prebid cache system. + v.SetDefault("stored_video_req.database.connection.driver", "") + v.SetDefault("stored_video_req.database.connection.dbname", "") + v.SetDefault("stored_video_req.database.connection.host", "") + v.SetDefault("stored_video_req.database.connection.port", 0) + v.SetDefault("stored_video_req.database.connection.user", "") + v.SetDefault("stored_video_req.database.connection.password", "") + v.SetDefault("stored_video_req.database.connection.query_string", "") + v.SetDefault("stored_video_req.database.connection.tls.root_cert", "") + v.SetDefault("stored_video_req.database.connection.tls.client_cert", "") + v.SetDefault("stored_video_req.database.connection.tls.client_key", "") + v.SetDefault("stored_video_req.database.fetcher.query", "") + v.SetDefault("stored_video_req.database.fetcher.amp_query", "") + v.SetDefault("stored_video_req.database.initialize_caches.timeout_ms", 0) + v.SetDefault("stored_video_req.database.initialize_caches.query", "") + v.SetDefault("stored_video_req.database.initialize_caches.amp_query", "") + v.SetDefault("stored_video_req.database.poll_for_updates.refresh_rate_seconds", 0) + v.SetDefault("stored_video_req.database.poll_for_updates.timeout_ms", 0) + v.SetDefault("stored_video_req.database.poll_for_updates.query", "") + v.SetDefault("stored_video_req.database.poll_for_updates.amp_query", "") v.SetDefault("stored_video_req.filesystem.enabled", false) v.SetDefault("stored_video_req.filesystem.directorypath", "") v.SetDefault("stored_video_req.http.endpoint", "") @@ -954,6 +1010,25 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) { v.SetDefault("stored_video_req.http_events.endpoint", "") v.SetDefault("stored_video_req.http_events.refresh_rate_seconds", 0) v.SetDefault("stored_video_req.http_events.timeout_ms", 0) + v.SetDefault("stored_responses.database.connection.driver", "") + v.SetDefault("stored_responses.database.connection.dbname", "") + v.SetDefault("stored_responses.database.connection.host", "") + v.SetDefault("stored_responses.database.connection.port", 0) + v.SetDefault("stored_responses.database.connection.user", "") + v.SetDefault("stored_responses.database.connection.password", "") + v.SetDefault("stored_responses.database.connection.query_string", "") + v.SetDefault("stored_responses.database.connection.tls.root_cert", "") + v.SetDefault("stored_responses.database.connection.tls.client_cert", "") + v.SetDefault("stored_responses.database.connection.tls.client_key", "") + v.SetDefault("stored_responses.database.fetcher.query", "") + v.SetDefault("stored_responses.database.fetcher.amp_query", "") + v.SetDefault("stored_responses.database.initialize_caches.timeout_ms", 0) + v.SetDefault("stored_responses.database.initialize_caches.query", "") + v.SetDefault("stored_responses.database.initialize_caches.amp_query", "") + v.SetDefault("stored_responses.database.poll_for_updates.refresh_rate_seconds", 0) + v.SetDefault("stored_responses.database.poll_for_updates.timeout_ms", 0) + v.SetDefault("stored_responses.database.poll_for_updates.query", "") + v.SetDefault("stored_responses.database.poll_for_updates.amp_query", "") v.SetDefault("stored_responses.filesystem.enabled", false) v.SetDefault("stored_responses.filesystem.directorypath", "") v.SetDefault("stored_responses.http.endpoint", "") @@ -1014,16 +1089,16 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) { v.SetDefault("gdpr.tcf2.purpose8.enforce_vendors", true) v.SetDefault("gdpr.tcf2.purpose9.enforce_vendors", true) v.SetDefault("gdpr.tcf2.purpose10.enforce_vendors", true) - v.SetDefault("gdpr.tcf2.purpose1.vendor_exceptions", []openrtb_ext.BidderName{}) - v.SetDefault("gdpr.tcf2.purpose2.vendor_exceptions", []openrtb_ext.BidderName{}) - v.SetDefault("gdpr.tcf2.purpose3.vendor_exceptions", []openrtb_ext.BidderName{}) - v.SetDefault("gdpr.tcf2.purpose4.vendor_exceptions", []openrtb_ext.BidderName{}) - v.SetDefault("gdpr.tcf2.purpose5.vendor_exceptions", []openrtb_ext.BidderName{}) - v.SetDefault("gdpr.tcf2.purpose6.vendor_exceptions", []openrtb_ext.BidderName{}) - v.SetDefault("gdpr.tcf2.purpose7.vendor_exceptions", []openrtb_ext.BidderName{}) - v.SetDefault("gdpr.tcf2.purpose8.vendor_exceptions", []openrtb_ext.BidderName{}) - v.SetDefault("gdpr.tcf2.purpose9.vendor_exceptions", []openrtb_ext.BidderName{}) - v.SetDefault("gdpr.tcf2.purpose10.vendor_exceptions", []openrtb_ext.BidderName{}) + v.SetDefault("gdpr.tcf2.purpose1.vendor_exceptions", []string{}) + v.SetDefault("gdpr.tcf2.purpose2.vendor_exceptions", []string{}) + v.SetDefault("gdpr.tcf2.purpose3.vendor_exceptions", []string{}) + v.SetDefault("gdpr.tcf2.purpose4.vendor_exceptions", []string{}) + v.SetDefault("gdpr.tcf2.purpose5.vendor_exceptions", []string{}) + v.SetDefault("gdpr.tcf2.purpose6.vendor_exceptions", []string{}) + v.SetDefault("gdpr.tcf2.purpose7.vendor_exceptions", []string{}) + v.SetDefault("gdpr.tcf2.purpose8.vendor_exceptions", []string{}) + v.SetDefault("gdpr.tcf2.purpose9.vendor_exceptions", []string{}) + v.SetDefault("gdpr.tcf2.purpose10.vendor_exceptions", []string{}) v.SetDefault("gdpr.amp_exception", false) v.SetDefault("gdpr.eea_countries", []string{"ALA", "AUT", "BEL", "BGR", "HRV", "CYP", "CZE", "DNK", "EST", "FIN", "FRA", "GUF", "DEU", "GIB", "GRC", "GLP", "GGY", "HUN", "ISL", "IRL", "IMN", "ITA", "JEY", "LVA", @@ -1050,16 +1125,33 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) { v.SetDefault("account_defaults.price_floors.use_dynamic_data", false) v.SetDefault("account_defaults.price_floors.max_rules", 100) v.SetDefault("account_defaults.price_floors.max_schema_dims", 3) - v.SetDefault("account_defaults.events_enabled", false) v.SetDefault("account_defaults.price_floors.fetch.enabled", false) + v.SetDefault("account_defaults.price_floors.fetch.url", "") + v.SetDefault("account_defaults.events_enabled", false) v.SetDefault("account_defaults.price_floors.fetch.timeout_ms", 3000) v.SetDefault("account_defaults.price_floors.fetch.max_file_size_kb", 100) v.SetDefault("account_defaults.price_floors.fetch.max_rules", 1000) v.SetDefault("account_defaults.price_floors.fetch.max_age_sec", 86400) v.SetDefault("account_defaults.price_floors.fetch.period_sec", 3600) + v.SetDefault("account_defaults.price_floors.fetch.max_schema_dims", 0) + v.SetDefault("account_defaults.privacy.privacysandbox.cookiedeprecation.enabled", false) + v.SetDefault("account_defaults.privacy.privacysandbox.cookiedeprecation.ttl_sec", 604800) + + v.SetDefault("account_defaults.events_enabled", false) v.SetDefault("account_defaults.privacy.ipv6.anon_keep_bits", 56) v.SetDefault("account_defaults.privacy.ipv4.anon_keep_bits", 24) + //Defaults for Price floor fetcher + v.SetDefault("price_floors.fetcher.worker", 20) + v.SetDefault("price_floors.fetcher.capacity", 20000) + v.SetDefault("price_floors.fetcher.cache_size_mb", 64) + v.SetDefault("price_floors.fetcher.http_client.max_connections_per_host", 0) // unlimited + v.SetDefault("price_floors.fetcher.http_client.max_idle_connections", 40) + v.SetDefault("price_floors.fetcher.http_client.max_idle_connections_per_host", 2) + v.SetDefault("price_floors.fetcher.http_client.idle_connection_timeout_seconds", 60) + v.SetDefault("price_floors.fetcher.max_retries", 10) + + v.SetDefault("account_defaults.events_enabled", false) v.SetDefault("compression.response.enable_gzip", false) v.SetDefault("compression.request.enable_gzip", false) @@ -1096,8 +1188,6 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) { v.SetDefault("request_validation.ipv4_private_networks", []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16", "169.254.0.0/16", "127.0.0.0/8"}) v.SetDefault("request_validation.ipv6_private_networks", []string{"::1/128", "fc00::/7", "fe80::/10", "ff00::/8", "2001:db8::/32"}) - bindDatabaseEnvVars(v) - // Set environment variable support: v.SetEnvKeyReplacer(strings.NewReplacer(".", "_")) v.SetTypeByDefaultValue(true) @@ -1105,27 +1195,9 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) { v.AutomaticEnv() v.ReadInConfig() - // Migrate config settings to maintain compatibility with old configs - migrateConfig(v) - migrateConfigPurposeOneTreatment(v) - migrateConfigSpecialFeature1(v) - migrateConfigTCF2PurposeFlags(v) - migrateConfigDatabaseConnection(v) - migrateConfigCompression(v) - // These defaults must be set after the migrate functions because those functions look for the presence of these // config fields and there isn't a way to detect presence of a config field using the viper package if a default // is set. Viper IsSet and Get functions consider default values. - v.SetDefault("gdpr.tcf2.purpose1.enabled", true) - v.SetDefault("gdpr.tcf2.purpose2.enabled", true) - v.SetDefault("gdpr.tcf2.purpose3.enabled", true) - v.SetDefault("gdpr.tcf2.purpose4.enabled", true) - v.SetDefault("gdpr.tcf2.purpose5.enabled", true) - v.SetDefault("gdpr.tcf2.purpose6.enabled", true) - v.SetDefault("gdpr.tcf2.purpose7.enabled", true) - v.SetDefault("gdpr.tcf2.purpose8.enabled", true) - v.SetDefault("gdpr.tcf2.purpose9.enabled", true) - v.SetDefault("gdpr.tcf2.purpose10.enabled", true) v.SetDefault("gdpr.tcf2.purpose1.enforce_algo", TCF2EnforceAlgoFull) v.SetDefault("gdpr.tcf2.purpose2.enforce_algo", TCF2EnforceAlgoFull) v.SetDefault("gdpr.tcf2.purpose3.enforce_algo", TCF2EnforceAlgoFull) @@ -1152,10 +1224,9 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) { v.SetDefault("gdpr.tcf2.special_feature1.vendor_exceptions", []openrtb_ext.BidderName{}) v.SetDefault("price_floors.enabled", false) - v.SetDefault("enable_gzip", false) - // Defaults for account_defaults.events.default_url v.SetDefault("account_defaults.events.default_url", "https://PBS_HOST/event?t=##PBS-EVENTTYPE##&vtype=##PBS-VASTEVENT##&b=##PBS-BIDID##&f=i&a=##PBS-ACCOUNTID##&ts=##PBS-TIMESTAMP##&bidder=##PBS-BIDDER##&int=##PBS-INTEGRATION##&mt=##PBS-MEDIATYPE##&ch=##PBS-CHANNEL##&aid=##PBS-AUCTIONID##&l=##PBS-LINEID##") + v.SetDefault("account_defaults.events.enabled", false) v.SetDefault("experiment.adscert.mode", "off") v.SetDefault("experiment.adscert.inprocess.origin", "") @@ -1175,303 +1246,6 @@ func SetupViper(v *viper.Viper, filename string, bidderInfos BidderInfos) { v.SetDefault("price_floor_fetcher.capacity", 20000) } -func migrateConfig(v *viper.Viper) { - // if stored_requests.filesystem is not a map in conf file as expected from defaults, - // means we have old-style settings; migrate them to new filesystem map to avoid breaking viper - if _, ok := v.Get("stored_requests.filesystem").(map[string]interface{}); !ok { - glog.Warning("stored_requests.filesystem should be changed to stored_requests.filesystem.enabled") - glog.Warning("stored_requests.directorypath should be changed to stored_requests.filesystem.directorypath") - m := v.GetStringMap("stored_requests.filesystem") - m["enabled"] = v.GetBool("stored_requests.filesystem") - m["directorypath"] = v.GetString("stored_requests.directorypath") - v.Set("stored_requests.filesystem", m) - } -} - -func migrateConfigCompression(v *viper.Viper) { - oldField := "enable_gzip" - newField := "compression.response.enable_gzip" - if v.IsSet(oldField) { - oldConfig := v.GetBool(oldField) - if v.IsSet(newField) { - glog.Warningf("using %s and ignoring deprecated %s", newField, oldField) - } else { - glog.Warningf("%s is deprecated and should be changed to %s", oldField, newField) - v.Set(newField, oldConfig) - } - } -} - -func migrateConfigPurposeOneTreatment(v *viper.Viper) { - if oldConfig, ok := v.Get("gdpr.tcf2.purpose_one_treatement").(map[string]interface{}); ok { - if v.IsSet("gdpr.tcf2.purpose_one_treatment") { - glog.Warning("using gdpr.tcf2.purpose_one_treatment and ignoring deprecated gdpr.tcf2.purpose_one_treatement") - } else { - glog.Warning("gdpr.tcf2.purpose_one_treatement.enabled should be changed to gdpr.tcf2.purpose_one_treatment.enabled") - glog.Warning("gdpr.tcf2.purpose_one_treatement.access_allowed should be changed to gdpr.tcf2.purpose_one_treatment.access_allowed") - v.Set("gdpr.tcf2.purpose_one_treatment", oldConfig) - } - } -} - -func migrateConfigSpecialFeature1(v *viper.Viper) { - if oldConfig, ok := v.Get("gdpr.tcf2.special_purpose1").(map[string]interface{}); ok { - if v.IsSet("gdpr.tcf2.special_feature1") { - glog.Warning("using gdpr.tcf2.special_feature1 and ignoring deprecated gdpr.tcf2.special_purpose1") - } else { - glog.Warning("gdpr.tcf2.special_purpose1.enabled is deprecated and should be changed to gdpr.tcf2.special_feature1.enforce") - glog.Warning("gdpr.tcf2.special_purpose1.vendor_exceptions is deprecated and should be changed to gdpr.tcf2.special_feature1.vendor_exceptions") - v.Set("gdpr.tcf2.special_feature1.enforce", oldConfig["enabled"]) - v.Set("gdpr.tcf2.special_feature1.vendor_exceptions", oldConfig["vendor_exceptions"]) - } - } -} - -func migrateConfigTCF2PurposeFlags(v *viper.Viper) { - migrateConfigTCF2EnforcePurposeFlags(v) - migrateConfigTCF2PurposeEnabledFlags(v) -} - -func migrateConfigTCF2EnforcePurposeFlags(v *viper.Viper) { - for i := 1; i <= 10; i++ { - algoField := fmt.Sprintf("gdpr.tcf2.purpose%d.enforce_algo", i) - purposeField := fmt.Sprintf("gdpr.tcf2.purpose%d.enforce_purpose", i) - - if !v.IsSet(purposeField) { - continue - } - if _, ok := v.Get(purposeField).(string); !ok { - continue - } - if v.IsSet(algoField) { - glog.Warningf("using %s and ignoring deprecated %s string type", algoField, purposeField) - } else { - v.Set(algoField, TCF2EnforceAlgoFull) - - glog.Warningf("setting %s to \"%s\" based on deprecated %s string type \"%s\"", algoField, TCF2EnforceAlgoFull, purposeField, v.GetString(purposeField)) - } - - oldPurposeFieldValue := v.GetString(purposeField) - newPurposeFieldValue := "false" - if oldPurposeFieldValue == TCF2EnforceAlgoFull { - newPurposeFieldValue = "true" - } - - glog.Warningf("converting %s from string \"%s\" to bool \"%s\"; string type is deprecated", purposeField, oldPurposeFieldValue, newPurposeFieldValue) - v.Set(purposeField, newPurposeFieldValue) - } -} - -func migrateConfigTCF2PurposeEnabledFlags(v *viper.Viper) { - for i := 1; i <= 10; i++ { - oldField := fmt.Sprintf("gdpr.tcf2.purpose%d.enabled", i) - newField := fmt.Sprintf("gdpr.tcf2.purpose%d.enforce_purpose", i) - - if v.IsSet(oldField) { - oldConfig := v.GetBool(oldField) - if v.IsSet(newField) { - glog.Warningf("using %s and ignoring deprecated %s", newField, oldField) - } else { - glog.Warningf("%s is deprecated and should be changed to %s", oldField, newField) - v.Set(newField, oldConfig) - } - } - - if v.IsSet(newField) { - v.Set(oldField, strconv.FormatBool(v.GetBool(newField))) - } - } -} - -func migrateConfigDatabaseConnection(v *viper.Viper) { - - type QueryParamMigration struct { - old string - new string - } - - type QueryMigration struct { - name string - params []QueryParamMigration - } - - type Migration struct { - old string - new string - fields []string - queryMigrations []QueryMigration - } - - queryParamMigrations := struct { - RequestIdList QueryParamMigration - ImpIdList QueryParamMigration - IdList QueryParamMigration - LastUpdated QueryParamMigration - }{ - RequestIdList: QueryParamMigration{ - old: "%REQUEST_ID_LIST%", - new: "$REQUEST_ID_LIST", - }, - ImpIdList: QueryParamMigration{ - old: "%IMP_ID_LIST%", - new: "$IMP_ID_LIST", - }, - IdList: QueryParamMigration{ - old: "%ID_LIST%", - new: "$ID_LIST", - }, - LastUpdated: QueryParamMigration{ - old: "$1", - new: "$LAST_UPDATED", - }, - } - - queryMigrations := []QueryMigration{ - { - name: "fetcher.query", - params: []QueryParamMigration{queryParamMigrations.RequestIdList, queryParamMigrations.ImpIdList, queryParamMigrations.IdList}, - }, - { - name: "fetcher.amp_query", - params: []QueryParamMigration{queryParamMigrations.RequestIdList, queryParamMigrations.ImpIdList, queryParamMigrations.IdList}, - }, - { - name: "poll_for_updates.query", - params: []QueryParamMigration{queryParamMigrations.LastUpdated}, - }, - { - name: "poll_for_updates.amp_query", - params: []QueryParamMigration{queryParamMigrations.LastUpdated}, - }, - } - - migrations := []Migration{ - { - old: "stored_requests.postgres", - new: "stored_requests.database", - fields: []string{ - "connection.dbname", - "connection.host", - "connection.port", - "connection.user", - "connection.password", - "fetcher.query", - "fetcher.amp_query", - "initialize_caches.timeout_ms", - "initialize_caches.query", - "initialize_caches.amp_query", - "poll_for_updates.refresh_rate_seconds", - "poll_for_updates.timeout_ms", - "poll_for_updates.query", - "poll_for_updates.amp_query", - }, - queryMigrations: queryMigrations, - }, - { - old: "stored_video_req.postgres", - new: "stored_video_req.database", - fields: []string{ - "connection.dbname", - "connection.host", - "connection.port", - "connection.user", - "connection.password", - "fetcher.query", - "initialize_caches.timeout_ms", - "initialize_caches.query", - "poll_for_updates.refresh_rate_seconds", - "poll_for_updates.timeout_ms", - "poll_for_updates.query", - }, - queryMigrations: queryMigrations, - }, - { - old: "stored_responses.postgres", - new: "stored_responses.database", - fields: []string{ - "connection.dbname", - "connection.host", - "connection.port", - "connection.user", - "connection.password", - "fetcher.query", - "initialize_caches.timeout_ms", - "initialize_caches.query", - "poll_for_updates.refresh_rate_seconds", - "poll_for_updates.timeout_ms", - "poll_for_updates.query", - }, - queryMigrations: queryMigrations, - }, - } - - for _, migration := range migrations { - driverField := migration.new + ".connection.driver" - newConfigInfoPresent := isConfigInfoPresent(v, migration.new, migration.fields) - oldConfigInfoPresent := isConfigInfoPresent(v, migration.old, migration.fields) - - if !newConfigInfoPresent && oldConfigInfoPresent { - glog.Warning(fmt.Sprintf("%s is deprecated and should be changed to %s", migration.old, migration.new)) - glog.Warning(fmt.Sprintf("%s is not set, using default (postgres)", driverField)) - v.Set(driverField, "postgres") - - for _, field := range migration.fields { - oldField := migration.old + "." + field - newField := migration.new + "." + field - if v.IsSet(oldField) { - glog.Warning(fmt.Sprintf("%s is deprecated and should be changed to %s", oldField, newField)) - v.Set(newField, v.Get(oldField)) - } - } - - for _, queryMigration := range migration.queryMigrations { - oldQueryField := migration.old + "." + queryMigration.name - newQueryField := migration.new + "." + queryMigration.name - queryString := v.GetString(oldQueryField) - for _, queryParam := range queryMigration.params { - if strings.Contains(queryString, queryParam.old) { - glog.Warning(fmt.Sprintf("Query param %s for %s is deprecated and should be changed to %s", queryParam.old, oldQueryField, queryParam.new)) - queryString = strings.ReplaceAll(queryString, queryParam.old, queryParam.new) - v.Set(newQueryField, queryString) - } - } - } - } else if newConfigInfoPresent && oldConfigInfoPresent { - glog.Warning(fmt.Sprintf("using %s and ignoring deprecated %s", migration.new, migration.old)) - - for _, field := range migration.fields { - oldField := migration.old + "." + field - newField := migration.new + "." + field - if v.IsSet(oldField) { - glog.Warning(fmt.Sprintf("using %s and ignoring deprecated %s", newField, oldField)) - } - } - } - } -} - -// migrateConfigEventsEnabled is responsible for ensuring backward compatibility of events_enabled field. -// This function copies the value of newField "events.enabled" and set it to the oldField "events_enabled". -// This is necessary to achieve the desired order of precedence favoring the account values over the host values -// given the account fetcher JSON merge mechanics. -func migrateConfigEventsEnabled(oldFieldValue *bool, newFieldValue *bool) (updatedOldFieldValue, updatedNewFieldValue *bool) { - newField := "account_defaults.events.enabled" - oldField := "account_defaults.events_enabled" - - updatedOldFieldValue = oldFieldValue - if oldFieldValue != nil { - glog.Warningf("%s is deprecated and should be changed to %s", oldField, newField) - } - if newFieldValue != nil { - if oldFieldValue != nil { - glog.Warningf("using %s and ignoring deprecated %s", newField, oldField) - } - updatedOldFieldValue = ptrutil.ToPtr(*newFieldValue) - } - - return updatedOldFieldValue, nil -} - func isConfigInfoPresent(v *viper.Viper, prefix string, fields []string) bool { prefix = prefix + "." for _, field := range fields { @@ -1483,60 +1257,6 @@ func isConfigInfoPresent(v *viper.Viper, prefix string, fields []string) bool { return false } -func bindDatabaseEnvVars(v *viper.Viper) { - v.BindEnv("stored_requests.database.connection.driver") - v.BindEnv("stored_requests.database.connection.dbname") - v.BindEnv("stored_requests.database.connection.host") - v.BindEnv("stored_requests.database.connection.port") - v.BindEnv("stored_requests.database.connection.user") - v.BindEnv("stored_requests.database.connection.password") - v.BindEnv("stored_requests.database.connection.query_string") - v.BindEnv("stored_requests.database.connection.tls.root_cert") - v.BindEnv("stored_requests.database.connection.tls.client_cert") - v.BindEnv("stored_requests.database.connection.tls.client_key") - v.BindEnv("stored_requests.database.fetcher.query") - v.BindEnv("stored_requests.database.fetcher.amp_query") - v.BindEnv("stored_requests.database.initialize_caches.timeout_ms") - v.BindEnv("stored_requests.database.initialize_caches.query") - v.BindEnv("stored_requests.database.initialize_caches.amp_query") - v.BindEnv("stored_requests.database.poll_for_updates.refresh_rate_seconds") - v.BindEnv("stored_requests.database.poll_for_updates.timeout_ms") - v.BindEnv("stored_requests.database.poll_for_updates.query") - v.BindEnv("stored_requests.database.poll_for_updates.amp_query") - v.BindEnv("stored_video_req.database.connection.driver") - v.BindEnv("stored_video_req.database.connection.dbname") - v.BindEnv("stored_video_req.database.connection.host") - v.BindEnv("stored_video_req.database.connection.port") - v.BindEnv("stored_video_req.database.connection.user") - v.BindEnv("stored_video_req.database.connection.password") - v.BindEnv("stored_video_req.database.connection.query_string") - v.BindEnv("stored_video_req.database.connection.tls.root_cert") - v.BindEnv("stored_video_req.database.connection.tls.client_cert") - v.BindEnv("stored_video_req.database.connection.tls.client_key") - v.BindEnv("stored_video_req.database.fetcher.query") - v.BindEnv("stored_video_req.database.initialize_caches.timeout_ms") - v.BindEnv("stored_video_req.database.initialize_caches.query") - v.BindEnv("stored_video_req.database.poll_for_updates.refresh_rate_seconds") - v.BindEnv("stored_video_req.database.poll_for_updates.timeout_ms") - v.BindEnv("stored_video_req.database.poll_for_updates.query") - v.BindEnv("stored_responses.database.connection.driver") - v.BindEnv("stored_responses.database.connection.dbname") - v.BindEnv("stored_responses.database.connection.host") - v.BindEnv("stored_responses.database.connection.port") - v.BindEnv("stored_responses.database.connection.user") - v.BindEnv("stored_responses.database.connection.password") - v.BindEnv("stored_responses.database.connection.query_string") - v.BindEnv("stored_responses.database.connection.tls.root_cert") - v.BindEnv("stored_responses.database.connection.tls.client_cert") - v.BindEnv("stored_responses.database.connection.tls.client_key") - v.BindEnv("stored_responses.database.fetcher.query") - v.BindEnv("stored_responses.database.initialize_caches.timeout_ms") - v.BindEnv("stored_responses.database.initialize_caches.query") - v.BindEnv("stored_responses.database.poll_for_updates.refresh_rate_seconds") - v.BindEnv("stored_responses.database.poll_for_updates.timeout_ms") - v.BindEnv("stored_responses.database.poll_for_updates.query") -} - func setBidderDefaults(v *viper.Viper, bidder string) { adapterCfgPrefix := "adapters." + bidder v.BindEnv(adapterCfgPrefix + ".disabled") diff --git a/config/config_test.go b/config/config_test.go index a546449e4a7..fe0dd116162 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -5,14 +5,12 @@ import ( "errors" "net" "os" - "strconv" "strings" "testing" "time" "github.com/prebid/go-gdpr/consentconstants" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/spf13/viper" "github.com/stretchr/testify/assert" ) @@ -35,7 +33,6 @@ var bidderInfos = BidderInfos{ MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}, }, }, - UserSyncURL: "http://bidder2.com/usersync", }, } @@ -178,9 +175,16 @@ func TestDefaults(t *testing.T) { //Assert the price floor default values cmpBools(t, "price_floors.enabled", false, cfg.PriceFloors.Enabled) + cmpInts(t, "price_floors.fetcher.worker", 20, cfg.PriceFloors.Fetcher.Worker) + cmpInts(t, "price_floors.fetcher.capacity", 20000, cfg.PriceFloors.Fetcher.Capacity) + cmpInts(t, "price_floors.fetcher.cache_size_mb", 64, cfg.PriceFloors.Fetcher.CacheSize) + cmpInts(t, "price_floors.fetcher.http_client.max_connections_per_host", 0, cfg.PriceFloors.Fetcher.HttpClient.MaxConnsPerHost) + cmpInts(t, "price_floors.fetcher.http_client.max_idle_connections", 40, cfg.PriceFloors.Fetcher.HttpClient.MaxIdleConns) + cmpInts(t, "price_floors.fetcher.http_client.max_idle_connections_per_host", 2, cfg.PriceFloors.Fetcher.HttpClient.MaxIdleConnsPerHost) + cmpInts(t, "price_floors.fetcher.http_client.idle_connection_timeout_seconds", 60, cfg.PriceFloors.Fetcher.HttpClient.IdleConnTimeout) + cmpInts(t, "price_floors.fetcher.max_retries", 10, cfg.PriceFloors.Fetcher.MaxRetries) // Assert compression related defaults - cmpBools(t, "enable_gzip", false, cfg.EnableGzip) cmpBools(t, "compression.request.enable_gzip", false, cfg.Compression.Request.GZIP) cmpBools(t, "compression.response.enable_gzip", false, cfg.Compression.Response.GZIP) @@ -191,14 +195,18 @@ func TestDefaults(t *testing.T) { cmpBools(t, "account_defaults.price_floors.use_dynamic_data", false, cfg.AccountDefaults.PriceFloors.UseDynamicData) cmpInts(t, "account_defaults.price_floors.max_rules", 100, cfg.AccountDefaults.PriceFloors.MaxRule) cmpInts(t, "account_defaults.price_floors.max_schema_dims", 3, cfg.AccountDefaults.PriceFloors.MaxSchemaDims) - cmpBools(t, "account_defaults.events_enabled", *cfg.AccountDefaults.EventsEnabled, false) - cmpNils(t, "account_defaults.events.enabled", cfg.AccountDefaults.Events.Enabled) - cmpBools(t, "account_defaults.price_floors.fetch.enabled", false, cfg.AccountDefaults.PriceFloors.Fetch.Enabled) - cmpInts(t, "account_defaults.price_floors.fetch.timeout_ms", 3000, cfg.AccountDefaults.PriceFloors.Fetch.Timeout) - cmpInts(t, "account_defaults.price_floors.fetch.max_file_size_kb", 100, cfg.AccountDefaults.PriceFloors.Fetch.MaxFileSize) - cmpInts(t, "account_defaults.price_floors.fetch.max_rules", 1000, cfg.AccountDefaults.PriceFloors.Fetch.MaxRules) - cmpInts(t, "account_defaults.price_floors.fetch.max_age_sec", 86400, cfg.AccountDefaults.PriceFloors.Fetch.MaxAge) - cmpInts(t, "account_defaults.price_floors.fetch.period_sec", 3600, cfg.AccountDefaults.PriceFloors.Fetch.Period) + cmpBools(t, "account_defaults.price_floors.fetch.enabled", false, cfg.AccountDefaults.PriceFloors.Fetcher.Enabled) + cmpStrings(t, "account_defaults.price_floors.fetch.url", "", cfg.AccountDefaults.PriceFloors.Fetcher.URL) + cmpInts(t, "account_defaults.price_floors.fetch.timeout_ms", 3000, cfg.AccountDefaults.PriceFloors.Fetcher.Timeout) + cmpInts(t, "account_defaults.price_floors.fetch.max_file_size_kb", 100, cfg.AccountDefaults.PriceFloors.Fetcher.MaxFileSizeKB) + cmpInts(t, "account_defaults.price_floors.fetch.max_rules", 1000, cfg.AccountDefaults.PriceFloors.Fetcher.MaxRules) + cmpInts(t, "account_defaults.price_floors.fetch.period_sec", 3600, cfg.AccountDefaults.PriceFloors.Fetcher.Period) + cmpInts(t, "account_defaults.price_floors.fetch.max_age_sec", 86400, cfg.AccountDefaults.PriceFloors.Fetcher.MaxAge) + cmpInts(t, "account_defaults.price_floors.fetch.max_schema_dims", 0, cfg.AccountDefaults.PriceFloors.Fetcher.MaxSchemaDims) + cmpBools(t, "account_defaults.privacy.privacysandbox.cookiedeprecation.enabled", false, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.Enabled) + cmpInts(t, "account_defaults.privacy.privacysandbox.cookiedeprecation.ttl_sec", 604800, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.TTLSec) + + cmpBools(t, "account_defaults.events.enabled", false, cfg.AccountDefaults.Events.Enabled) cmpInts(t, "price_floor_fetcher.worker", 20, cfg.PriceFloorFetcher.Worker) cmpInts(t, "price_floor_fetcher.capacity", 20000, cfg.PriceFloorFetcher.Capacity) @@ -221,94 +229,84 @@ func TestDefaults(t *testing.T) { expectedTCF2 := TCF2{ Enabled: true, Purpose1: TCF2Purpose{ - Enabled: true, EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: true, - VendorExceptions: []openrtb_ext.BidderName{}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + VendorExceptions: []string{}, + VendorExceptionMap: map[string]struct{}{}, }, Purpose2: TCF2Purpose{ - Enabled: true, EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: true, - VendorExceptions: []openrtb_ext.BidderName{}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + VendorExceptions: []string{}, + VendorExceptionMap: map[string]struct{}{}, }, Purpose3: TCF2Purpose{ - Enabled: true, EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: true, - VendorExceptions: []openrtb_ext.BidderName{}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + VendorExceptions: []string{}, + VendorExceptionMap: map[string]struct{}{}, }, Purpose4: TCF2Purpose{ - Enabled: true, EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: true, - VendorExceptions: []openrtb_ext.BidderName{}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + VendorExceptions: []string{}, + VendorExceptionMap: map[string]struct{}{}, }, Purpose5: TCF2Purpose{ - Enabled: true, EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: true, - VendorExceptions: []openrtb_ext.BidderName{}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + VendorExceptions: []string{}, + VendorExceptionMap: map[string]struct{}{}, }, Purpose6: TCF2Purpose{ - Enabled: true, EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: true, - VendorExceptions: []openrtb_ext.BidderName{}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + VendorExceptions: []string{}, + VendorExceptionMap: map[string]struct{}{}, }, Purpose7: TCF2Purpose{ - Enabled: true, EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: true, - VendorExceptions: []openrtb_ext.BidderName{}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + VendorExceptions: []string{}, + VendorExceptionMap: map[string]struct{}{}, }, Purpose8: TCF2Purpose{ - Enabled: true, EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: true, - VendorExceptions: []openrtb_ext.BidderName{}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + VendorExceptions: []string{}, + VendorExceptionMap: map[string]struct{}{}, }, Purpose9: TCF2Purpose{ - Enabled: true, EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: true, - VendorExceptions: []openrtb_ext.BidderName{}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + VendorExceptions: []string{}, + VendorExceptionMap: map[string]struct{}{}, }, Purpose10: TCF2Purpose{ - Enabled: true, EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: true, - VendorExceptions: []openrtb_ext.BidderName{}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + VendorExceptions: []string{}, + VendorExceptionMap: map[string]struct{}{}, }, SpecialFeature1: TCF2SpecialFeature{ Enforce: true, @@ -347,7 +345,6 @@ gdpr: enforce_vendors: false vendor_exceptions: ["foo1a", "foo1b"] purpose2: - enabled: false enforce_algo: "full" enforce_purpose: false enforce_vendors: false @@ -394,7 +391,6 @@ external_url: http://prebid-server.prebid.org/ host: prebid-server.prebid.org port: 1234 admin_port: 5678 -enable_gzip: false compression: request: enable_gzip: true @@ -476,8 +472,17 @@ hooks: enabled: true price_floors: enabled: true + fetcher: + worker: 20 + capacity: 20000 + cache_size_mb: 8 + http_client: + max_connections_per_host: 5 + max_idle_connections: 1 + max_idle_connections_per_host: 2 + idle_connection_timeout_seconds: 10 + max_retries: 5 account_defaults: - events_enabled: false events: enabled: true price_floors: @@ -490,16 +495,22 @@ account_defaults: max_schema_dims: 5 fetch: enabled: true - timeout_ms: 1000 - max_file_size_kb: 100 - max_rules: 1000 - max_age_sec: 36000 - period_sec: 7200 + url: http://test.com/floors + timeout_ms: 500 + max_file_size_kb: 200 + max_rules: 500 + period_sec: 2000 + max_age_sec: 6000 + max_schema_dims: 10 privacy: ipv6: anon_keep_bits: 50 ipv4: anon_keep_bits: 20 + privacysandbox: + cookiedeprecation: + enabled: true + ttl_sec: 86400 price_floor_fetcher: worker: 10 capacity: 20 @@ -510,12 +521,6 @@ tmax_adjustments: pbs_response_preparation_duration_ms: 100 `) -var oldStoredRequestsConfig = []byte(` -stored_requests: - filesystem: true - directorypath: "/somepath" -`) - func cmpStrings(t *testing.T, key, expected, actual string) { t.Helper() assert.Equal(t, expected, actual, "%s: %s != %s", key, expected, actual) @@ -599,6 +604,14 @@ func TestFullConfig(t *testing.T) { //Assert the price floor values cmpBools(t, "price_floors.enabled", true, cfg.PriceFloors.Enabled) + cmpInts(t, "price_floors.fetcher.worker", 20, cfg.PriceFloors.Fetcher.Worker) + cmpInts(t, "price_floors.fetcher.capacity", 20000, cfg.PriceFloors.Fetcher.Capacity) + cmpInts(t, "price_floors.fetcher.cache_size_mb", 8, cfg.PriceFloors.Fetcher.CacheSize) + cmpInts(t, "price_floors.fetcher.http_client.max_connections_per_host", 5, cfg.PriceFloors.Fetcher.HttpClient.MaxConnsPerHost) + cmpInts(t, "price_floors.fetcher.http_client.max_idle_connections", 1, cfg.PriceFloors.Fetcher.HttpClient.MaxIdleConns) + cmpInts(t, "price_floors.fetcher.http_client.max_idle_connections_per_host", 2, cfg.PriceFloors.Fetcher.HttpClient.MaxIdleConnsPerHost) + cmpInts(t, "price_floors.fetcher.http_client.idle_connection_timeout_seconds", 10, cfg.PriceFloors.Fetcher.HttpClient.IdleConnTimeout) + cmpInts(t, "price_floors.fetcher.max_retries", 5, cfg.PriceFloors.Fetcher.MaxRetries) cmpBools(t, "account_defaults.price_floors.enabled", true, cfg.AccountDefaults.PriceFloors.Enabled) cmpInts(t, "account_defaults.price_floors.enforce_floors_rate", 50, cfg.AccountDefaults.PriceFloors.EnforceFloorsRate) cmpBools(t, "account_defaults.price_floors.adjust_for_bid_adjustment", false, cfg.AccountDefaults.PriceFloors.AdjustForBidAdjustment) @@ -606,22 +619,26 @@ func TestFullConfig(t *testing.T) { cmpBools(t, "account_defaults.price_floors.use_dynamic_data", true, cfg.AccountDefaults.PriceFloors.UseDynamicData) cmpInts(t, "account_defaults.price_floors.max_rules", 120, cfg.AccountDefaults.PriceFloors.MaxRule) cmpInts(t, "account_defaults.price_floors.max_schema_dims", 5, cfg.AccountDefaults.PriceFloors.MaxSchemaDims) - cmpBools(t, "account_defaults.events_enabled", *cfg.AccountDefaults.EventsEnabled, true) - cmpNils(t, "account_defaults.events.enabled", cfg.AccountDefaults.Events.Enabled) - cmpBools(t, "account_defaults.price_floors.fetch.enabled", true, cfg.AccountDefaults.PriceFloors.Fetch.Enabled) - cmpInts(t, "account_defaults.price_floors.fetch.timeout_ms", 1000, cfg.AccountDefaults.PriceFloors.Fetch.Timeout) - cmpInts(t, "account_defaults.price_floors.fetch.max_file_size_kb", 100, cfg.AccountDefaults.PriceFloors.Fetch.MaxFileSize) - cmpInts(t, "account_defaults.price_floors.fetch.max_rules", 1000, cfg.AccountDefaults.PriceFloors.Fetch.MaxRules) - cmpInts(t, "account_defaults.price_floors.fetch.max_age_sec", 36000, cfg.AccountDefaults.PriceFloors.Fetch.MaxAge) - cmpInts(t, "account_defaults.price_floors.fetch.period_sec", 7200, cfg.AccountDefaults.PriceFloors.Fetch.Period) + cmpBools(t, "account_defaults.price_floors.fetch.enabled", true, cfg.AccountDefaults.PriceFloors.Fetcher.Enabled) + cmpStrings(t, "account_defaults.price_floors.fetch.url", "http://test.com/floors", cfg.AccountDefaults.PriceFloors.Fetcher.URL) + cmpInts(t, "account_defaults.price_floors.fetch.timeout_ms", 500, cfg.AccountDefaults.PriceFloors.Fetcher.Timeout) + cmpInts(t, "account_defaults.price_floors.fetch.max_file_size_kb", 200, cfg.AccountDefaults.PriceFloors.Fetcher.MaxFileSizeKB) + cmpInts(t, "account_defaults.price_floors.fetch.max_rules", 500, cfg.AccountDefaults.PriceFloors.Fetcher.MaxRules) + cmpInts(t, "account_defaults.price_floors.fetch.period_sec", 2000, cfg.AccountDefaults.PriceFloors.Fetcher.Period) + cmpInts(t, "account_defaults.price_floors.fetch.max_age_sec", 6000, cfg.AccountDefaults.PriceFloors.Fetcher.MaxAge) + cmpInts(t, "account_defaults.price_floors.fetch.max_schema_dims", 10, cfg.AccountDefaults.PriceFloors.Fetcher.MaxSchemaDims) + + cmpBools(t, "account_defaults.events.enabled", true, cfg.AccountDefaults.Events.Enabled) cmpInts(t, "price_floor_fetcher.worker", 10, cfg.PriceFloorFetcher.Worker) cmpInts(t, "price_floor_fetcher.capacity", 20, cfg.PriceFloorFetcher.Capacity) cmpInts(t, "account_defaults.privacy.ipv6.anon_keep_bits", 50, cfg.AccountDefaults.Privacy.IPv6Config.AnonKeepBits) cmpInts(t, "account_defaults.privacy.ipv4.anon_keep_bits", 20, cfg.AccountDefaults.Privacy.IPv4Config.AnonKeepBits) + cmpBools(t, "account_defaults.privacy.cookiedeprecation.enabled", true, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.Enabled) + cmpInts(t, "account_defaults.privacy.cookiedeprecation.ttl_sec", 86400, cfg.AccountDefaults.Privacy.PrivacySandbox.CookieDeprecation.TTLSec) + // Assert compression related defaults - cmpBools(t, "enable_gzip", false, cfg.EnableGzip) cmpBools(t, "compression.request.enable_gzip", true, cfg.Compression.Request.GZIP) cmpBools(t, "compression.response.enable_gzip", false, cfg.Compression.Response.GZIP) @@ -649,94 +666,84 @@ func TestFullConfig(t *testing.T) { expectedTCF2 := TCF2{ Enabled: true, Purpose1: TCF2Purpose{ - Enabled: true, // true by default EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: false, - VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo1a"), openrtb_ext.BidderName("foo1b")}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo1a"): {}, openrtb_ext.BidderName("foo1b"): {}}, + VendorExceptions: []string{"foo1a", "foo1b"}, + VendorExceptionMap: map[string]struct{}{"foo1a": {}, "foo1b": {}}, }, Purpose2: TCF2Purpose{ - Enabled: false, EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: false, EnforceVendors: false, - VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo2")}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo2"): {}}, + VendorExceptions: []string{"foo2"}, + VendorExceptionMap: map[string]struct{}{"foo2": {}}, }, Purpose3: TCF2Purpose{ - Enabled: true, // true by default EnforceAlgo: TCF2EnforceAlgoBasic, EnforceAlgoID: TCF2BasicEnforcement, EnforcePurpose: true, EnforceVendors: false, - VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo3")}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo3"): {}}, + VendorExceptions: []string{"foo3"}, + VendorExceptionMap: map[string]struct{}{"foo3": {}}, }, Purpose4: TCF2Purpose{ - Enabled: true, // true by default EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: false, - VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo4")}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo4"): {}}, + VendorExceptions: []string{"foo4"}, + VendorExceptionMap: map[string]struct{}{"foo4": {}}, }, Purpose5: TCF2Purpose{ - Enabled: true, // true by default EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: false, - VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo5")}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo5"): {}}, + VendorExceptions: []string{"foo5"}, + VendorExceptionMap: map[string]struct{}{"foo5": {}}, }, Purpose6: TCF2Purpose{ - Enabled: true, // true by default EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: false, - VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo6")}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo6"): {}}, + VendorExceptions: []string{"foo6"}, + VendorExceptionMap: map[string]struct{}{"foo6": {}}, }, Purpose7: TCF2Purpose{ - Enabled: true, // true by default EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: false, - VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo7")}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo7"): {}}, + VendorExceptions: []string{"foo7"}, + VendorExceptionMap: map[string]struct{}{"foo7": {}}, }, Purpose8: TCF2Purpose{ - Enabled: true, // true by default EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: false, - VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo8")}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo8"): {}}, + VendorExceptions: []string{"foo8"}, + VendorExceptionMap: map[string]struct{}{"foo8": {}}, }, Purpose9: TCF2Purpose{ - Enabled: true, // true by default EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: false, - VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo9")}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo9"): {}}, + VendorExceptions: []string{"foo9"}, + VendorExceptionMap: map[string]struct{}{"foo9": {}}, }, Purpose10: TCF2Purpose{ - Enabled: true, // true by default EnforceAlgo: TCF2EnforceAlgoFull, EnforceAlgoID: TCF2FullEnforcement, EnforcePurpose: true, EnforceVendors: false, - VendorExceptions: []openrtb_ext.BidderName{openrtb_ext.BidderName("foo10")}, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderName("foo10"): {}}, + VendorExceptions: []string{"foo10"}, + VendorExceptionMap: map[string]struct{}{"foo10": {}}, }, SpecialFeature1: TCF2SpecialFeature{ Enforce: true, // true by default @@ -836,10 +843,10 @@ func TestValidateConfig(t *testing.T) { }, AccountDefaults: Account{ PriceFloors: AccountPriceFloors{ - Fetch: AccountFloorFetch{ - Period: 400, - Timeout: 20, - MaxAge: 700, + Fetcher: AccountFloorFetch{ + Timeout: 100, + Period: 300, + MaxAge: 600, }, }, }, @@ -853,36 +860,15 @@ func TestValidateConfig(t *testing.T) { assert.Nil(t, err, "OpenRTB filesystem config should work. %v", err) } -func TestMigrateConfig(t *testing.T) { - v := viper.New() - SetupViper(v, "", bidderInfos) - v.Set("gdpr.default_value", "0") - v.SetConfigType("yaml") - v.ReadConfig(bytes.NewBuffer(oldStoredRequestsConfig)) - migrateConfig(v) - cfg, err := New(v, bidderInfos, mockNormalizeBidderName) - assert.NoError(t, err, "Setting up config should work but it doesn't") - cmpBools(t, "stored_requests.filesystem.enabled", true, cfg.StoredRequests.Files.Enabled) - cmpStrings(t, "stored_requests.filesystem.path", "/somepath", cfg.StoredRequests.Files.Path) -} - func TestMigrateConfigFromEnv(t *testing.T) { - if oldval, ok := os.LookupEnv("PBS_STORED_REQUESTS_FILESYSTEM"); ok { - defer os.Setenv("PBS_STORED_REQUESTS_FILESYSTEM", oldval) - } else { - defer os.Unsetenv("PBS_STORED_REQUESTS_FILESYSTEM") - } - if oldval, ok := os.LookupEnv("PBS_ADAPTERS_BIDDER1_ENDPOINT"); ok { defer os.Setenv("PBS_ADAPTERS_BIDDER1_ENDPOINT", oldval) } else { defer os.Unsetenv("PBS_ADAPTERS_BIDDER1_ENDPOINT") } - os.Setenv("PBS_STORED_REQUESTS_FILESYSTEM", "true") os.Setenv("PBS_ADAPTERS_BIDDER1_ENDPOINT", "http://bidder1_override.com") cfg, _ := newDefaultConfig(t) - cmpBools(t, "stored_requests.filesystem.enabled", true, cfg.StoredRequests.Files.Enabled) cmpStrings(t, "adapters.bidder1.endpoint", "http://bidder1_override.com", cfg.BidderInfos["bidder1"].Endpoint) } @@ -1014,1692 +1000,6 @@ func TestBidderInfoFromEnv(t *testing.T) { assert.Equal(t, "2.6", cfg.BidderInfos["bidder1"].OpenRTB.Version) } -func TestMigrateConfigPurposeOneTreatment(t *testing.T) { - oldPurposeOneTreatmentConfig := []byte(` - gdpr: - tcf2: - purpose_one_treatement: - enabled: true - access_allowed: true - `) - newPurposeOneTreatmentConfig := []byte(` - gdpr: - tcf2: - purpose_one_treatment: - enabled: true - access_allowed: true - `) - oldAndNewPurposeOneTreatmentConfig := []byte(` - gdpr: - tcf2: - purpose_one_treatement: - enabled: false - access_allowed: true - purpose_one_treatment: - enabled: true - access_allowed: false - `) - - tests := []struct { - description string - config []byte - wantPurpose1TreatmentEnabled bool - wantPurpose1TreatmentAccessAllowed bool - }{ - { - description: "New config and old config not set", - config: []byte{}, - }, - { - description: "New config not set, old config set", - config: oldPurposeOneTreatmentConfig, - wantPurpose1TreatmentEnabled: true, - wantPurpose1TreatmentAccessAllowed: true, - }, - { - description: "New config set, old config not set", - config: newPurposeOneTreatmentConfig, - wantPurpose1TreatmentEnabled: true, - wantPurpose1TreatmentAccessAllowed: true, - }, - { - description: "New config and old config set", - config: oldAndNewPurposeOneTreatmentConfig, - wantPurpose1TreatmentEnabled: true, - wantPurpose1TreatmentAccessAllowed: false, - }, - } - - for _, tt := range tests { - v := viper.New() - v.SetConfigType("yaml") - v.ReadConfig(bytes.NewBuffer(tt.config)) - - migrateConfigPurposeOneTreatment(v) - - if len(tt.config) > 0 { - assert.Equal(t, tt.wantPurpose1TreatmentEnabled, v.Get("gdpr.tcf2.purpose_one_treatment.enabled").(bool), tt.description) - assert.Equal(t, tt.wantPurpose1TreatmentAccessAllowed, v.Get("gdpr.tcf2.purpose_one_treatment.access_allowed").(bool), tt.description) - } else { - assert.Nil(t, v.Get("gdpr.tcf2.purpose_one_treatment.enabled"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose_one_treatment.access_allowed"), tt.description) - } - } -} - -func TestMigrateConfigSpecialFeature1(t *testing.T) { - oldSpecialFeature1Config := []byte(` - gdpr: - tcf2: - special_purpose1: - enabled: true - vendor_exceptions: ["appnexus"] - `) - newSpecialFeature1Config := []byte(` - gdpr: - tcf2: - special_feature1: - enforce: true - vendor_exceptions: ["appnexus"] - `) - oldAndNewSpecialFeature1Config := []byte(` - gdpr: - tcf2: - special_purpose1: - enabled: false - vendor_exceptions: ["appnexus"] - special_feature1: - enforce: true - vendor_exceptions: ["rubicon"] - `) - - tests := []struct { - description string - config []byte - wantSpecialFeature1Enforce bool - wantSpecialFeature1VendorExceptions []string - }{ - { - description: "New config and old config not set", - config: []byte{}, - }, - { - description: "New config not set, old config set", - config: oldSpecialFeature1Config, - wantSpecialFeature1Enforce: true, - wantSpecialFeature1VendorExceptions: []string{"appnexus"}, - }, - { - description: "New config set, old config not set", - config: newSpecialFeature1Config, - wantSpecialFeature1Enforce: true, - wantSpecialFeature1VendorExceptions: []string{"appnexus"}, - }, - { - description: "New config and old config set", - config: oldAndNewSpecialFeature1Config, - wantSpecialFeature1Enforce: true, - wantSpecialFeature1VendorExceptions: []string{"rubicon"}, - }, - } - - for _, tt := range tests { - v := viper.New() - v.SetConfigType("yaml") - v.ReadConfig(bytes.NewBuffer(tt.config)) - - migrateConfigSpecialFeature1(v) - - if len(tt.config) > 0 { - assert.Equal(t, tt.wantSpecialFeature1Enforce, v.Get("gdpr.tcf2.special_feature1.enforce").(bool), tt.description) - assert.Equal(t, tt.wantSpecialFeature1VendorExceptions, v.GetStringSlice("gdpr.tcf2.special_feature1.vendor_exceptions"), tt.description) - } else { - assert.Nil(t, v.Get("gdpr.tcf2.special_feature1.enforce"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.special_feature1.vendor_exceptions"), tt.description) - } - - var c Configuration - err := v.Unmarshal(&c) - assert.NoError(t, err, tt.description) - assert.Equal(t, tt.wantSpecialFeature1Enforce, c.GDPR.TCF2.SpecialFeature1.Enforce, tt.description) - - // convert expected vendor exceptions to type BidderName - expectedVendorExceptions := make([]openrtb_ext.BidderName, 0, 0) - for _, ve := range tt.wantSpecialFeature1VendorExceptions { - expectedVendorExceptions = append(expectedVendorExceptions, openrtb_ext.BidderName(ve)) - } - assert.ElementsMatch(t, expectedVendorExceptions, c.GDPR.TCF2.SpecialFeature1.VendorExceptions, tt.description) - } -} - -func TestMigrateConfigTCF2PurposeEnabledFlags(t *testing.T) { - trueStr := "true" - falseStr := "false" - - tests := []struct { - description string - config []byte - wantPurpose1EnforcePurpose string - wantPurpose2EnforcePurpose string - wantPurpose3EnforcePurpose string - wantPurpose4EnforcePurpose string - wantPurpose5EnforcePurpose string - wantPurpose6EnforcePurpose string - wantPurpose7EnforcePurpose string - wantPurpose8EnforcePurpose string - wantPurpose9EnforcePurpose string - wantPurpose10EnforcePurpose string - wantPurpose1Enabled string - wantPurpose2Enabled string - wantPurpose3Enabled string - wantPurpose4Enabled string - wantPurpose5Enabled string - wantPurpose6Enabled string - wantPurpose7Enabled string - wantPurpose8Enabled string - wantPurpose9Enabled string - wantPurpose10Enabled string - }{ - { - description: "New config and old config flags not set", - config: []byte{}, - }, - { - description: "New config not set, old config set - use old flags", - config: []byte(` - gdpr: - tcf2: - purpose1: - enabled: false - purpose2: - enabled: true - purpose3: - enabled: false - purpose4: - enabled: true - purpose5: - enabled: false - purpose6: - enabled: true - purpose7: - enabled: false - purpose8: - enabled: true - purpose9: - enabled: false - purpose10: - enabled: true - `), - wantPurpose1EnforcePurpose: falseStr, - wantPurpose2EnforcePurpose: trueStr, - wantPurpose3EnforcePurpose: falseStr, - wantPurpose4EnforcePurpose: trueStr, - wantPurpose5EnforcePurpose: falseStr, - wantPurpose6EnforcePurpose: trueStr, - wantPurpose7EnforcePurpose: falseStr, - wantPurpose8EnforcePurpose: trueStr, - wantPurpose9EnforcePurpose: falseStr, - wantPurpose10EnforcePurpose: trueStr, - wantPurpose1Enabled: falseStr, - wantPurpose2Enabled: trueStr, - wantPurpose3Enabled: falseStr, - wantPurpose4Enabled: trueStr, - wantPurpose5Enabled: falseStr, - wantPurpose6Enabled: trueStr, - wantPurpose7Enabled: falseStr, - wantPurpose8Enabled: trueStr, - wantPurpose9Enabled: falseStr, - wantPurpose10Enabled: trueStr, - }, - { - description: "New config flags set, old config flags not set - use new flags", - config: []byte(` - gdpr: - tcf2: - purpose1: - enforce_purpose: true - purpose2: - enforce_purpose: false - purpose3: - enforce_purpose: true - purpose4: - enforce_purpose: false - purpose5: - enforce_purpose: true - purpose6: - enforce_purpose: false - purpose7: - enforce_purpose: true - purpose8: - enforce_purpose: false - purpose9: - enforce_purpose: true - purpose10: - enforce_purpose: false - `), - wantPurpose1EnforcePurpose: trueStr, - wantPurpose2EnforcePurpose: falseStr, - wantPurpose3EnforcePurpose: trueStr, - wantPurpose4EnforcePurpose: falseStr, - wantPurpose5EnforcePurpose: trueStr, - wantPurpose6EnforcePurpose: falseStr, - wantPurpose7EnforcePurpose: trueStr, - wantPurpose8EnforcePurpose: falseStr, - wantPurpose9EnforcePurpose: trueStr, - wantPurpose10EnforcePurpose: falseStr, - wantPurpose1Enabled: trueStr, - wantPurpose2Enabled: falseStr, - wantPurpose3Enabled: trueStr, - wantPurpose4Enabled: falseStr, - wantPurpose5Enabled: trueStr, - wantPurpose6Enabled: falseStr, - wantPurpose7Enabled: trueStr, - wantPurpose8Enabled: falseStr, - wantPurpose9Enabled: trueStr, - wantPurpose10Enabled: falseStr, - }, - { - description: "New config flags and old config flags set - use new flags", - config: []byte(` - gdpr: - tcf2: - purpose1: - enabled: false - enforce_purpose: true - purpose2: - enabled: false - enforce_purpose: true - purpose3: - enabled: false - enforce_purpose: true - purpose4: - enabled: false - enforce_purpose: true - purpose5: - enabled: false - enforce_purpose: true - purpose6: - enabled: false - enforce_purpose: true - purpose7: - enabled: false - enforce_purpose: true - purpose8: - enabled: false - enforce_purpose: true - purpose9: - enabled: false - enforce_purpose: true - purpose10: - enabled: false - enforce_purpose: true - `), - wantPurpose1EnforcePurpose: trueStr, - wantPurpose2EnforcePurpose: trueStr, - wantPurpose3EnforcePurpose: trueStr, - wantPurpose4EnforcePurpose: trueStr, - wantPurpose5EnforcePurpose: trueStr, - wantPurpose6EnforcePurpose: trueStr, - wantPurpose7EnforcePurpose: trueStr, - wantPurpose8EnforcePurpose: trueStr, - wantPurpose9EnforcePurpose: trueStr, - wantPurpose10EnforcePurpose: trueStr, - wantPurpose1Enabled: trueStr, - wantPurpose2Enabled: trueStr, - wantPurpose3Enabled: trueStr, - wantPurpose4Enabled: trueStr, - wantPurpose5Enabled: trueStr, - wantPurpose6Enabled: trueStr, - wantPurpose7Enabled: trueStr, - wantPurpose8Enabled: trueStr, - wantPurpose9Enabled: trueStr, - wantPurpose10Enabled: trueStr, - }, - } - - for _, tt := range tests { - v := viper.New() - v.SetConfigType("yaml") - v.ReadConfig(bytes.NewBuffer(tt.config)) - - migrateConfigTCF2PurposeEnabledFlags(v) - - if len(tt.config) > 0 { - assert.Equal(t, tt.wantPurpose1EnforcePurpose, v.GetString("gdpr.tcf2.purpose1.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose2EnforcePurpose, v.GetString("gdpr.tcf2.purpose2.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose3EnforcePurpose, v.GetString("gdpr.tcf2.purpose3.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose4EnforcePurpose, v.GetString("gdpr.tcf2.purpose4.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose5EnforcePurpose, v.GetString("gdpr.tcf2.purpose5.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose6EnforcePurpose, v.GetString("gdpr.tcf2.purpose6.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose7EnforcePurpose, v.GetString("gdpr.tcf2.purpose7.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose8EnforcePurpose, v.GetString("gdpr.tcf2.purpose8.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose9EnforcePurpose, v.GetString("gdpr.tcf2.purpose9.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose10EnforcePurpose, v.GetString("gdpr.tcf2.purpose10.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose1Enabled, v.GetString("gdpr.tcf2.purpose1.enabled"), tt.description) - assert.Equal(t, tt.wantPurpose2Enabled, v.GetString("gdpr.tcf2.purpose2.enabled"), tt.description) - assert.Equal(t, tt.wantPurpose3Enabled, v.GetString("gdpr.tcf2.purpose3.enabled"), tt.description) - assert.Equal(t, tt.wantPurpose4Enabled, v.GetString("gdpr.tcf2.purpose4.enabled"), tt.description) - assert.Equal(t, tt.wantPurpose5Enabled, v.GetString("gdpr.tcf2.purpose5.enabled"), tt.description) - assert.Equal(t, tt.wantPurpose6Enabled, v.GetString("gdpr.tcf2.purpose6.enabled"), tt.description) - assert.Equal(t, tt.wantPurpose7Enabled, v.GetString("gdpr.tcf2.purpose7.enabled"), tt.description) - assert.Equal(t, tt.wantPurpose8Enabled, v.GetString("gdpr.tcf2.purpose8.enabled"), tt.description) - assert.Equal(t, tt.wantPurpose9Enabled, v.GetString("gdpr.tcf2.purpose9.enabled"), tt.description) - assert.Equal(t, tt.wantPurpose10Enabled, v.GetString("gdpr.tcf2.purpose10.enabled"), tt.description) - } else { - assert.Nil(t, v.Get("gdpr.tcf2.purpose1.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose2.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose3.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose4.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose5.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose6.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose7.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose8.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose9.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose10.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose1.enabled"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose2.enabled"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose3.enabled"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose4.enabled"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose5.enabled"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose6.enabled"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose7.enabled"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose8.enabled"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose9.enabled"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose10.enabled"), tt.description) - } - } -} - -func TestMigrateConfigTCF2PurposeFlags(t *testing.T) { - tests := []struct { - description string - config []byte - wantPurpose1EnforceAlgo string - wantPurpose1EnforcePurpose bool - wantPurpose1Enabled bool - }{ - { - description: "enforce_purpose does not set enforce_algo but sets enabled", - config: []byte(` - gdpr: - tcf2: - purpose1: - enforce_algo: "off" - enforce_purpose: "full" - enabled: false - purpose2: - enforce_purpose: "full" - enabled: false - purpose3: - enabled: false - `), - wantPurpose1EnforceAlgo: "off", - wantPurpose1EnforcePurpose: true, - wantPurpose1Enabled: true, - }, - { - description: "enforce_purpose sets enforce_algo and enabled", - config: []byte(` - gdpr: - tcf2: - purpose1: - enforce_purpose: "full" - enabled: false - `), - wantPurpose1EnforceAlgo: "full", - wantPurpose1EnforcePurpose: true, - wantPurpose1Enabled: true, - }, - { - description: "enforce_purpose does not set enforce_algo or enabled", - config: []byte(` - gdpr: - tcf2: - purpose1: - enabled: false - `), - wantPurpose1EnforceAlgo: "", - wantPurpose1EnforcePurpose: false, - wantPurpose1Enabled: false, - }, - } - - for _, tt := range tests { - v := viper.New() - v.SetConfigType("yaml") - v.ReadConfig(bytes.NewBuffer(tt.config)) - - migrateConfigTCF2PurposeFlags(v) - - assert.Equal(t, tt.wantPurpose1EnforceAlgo, v.GetString("gdpr.tcf2.purpose1.enforce_algo"), tt.description) - assert.Equal(t, tt.wantPurpose1EnforcePurpose, v.GetBool("gdpr.tcf2.purpose1.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose1Enabled, v.GetBool("gdpr.tcf2.purpose1.enabled"), tt.description) - } - -} - -func TestMigrateConfigTCF2EnforcePurposeFlags(t *testing.T) { - trueStr := "true" - falseStr := "false" - - tests := []struct { - description string - config []byte - wantEnforceAlgosSet bool - wantPurpose1EnforceAlgo string - wantPurpose2EnforceAlgo string - wantPurpose3EnforceAlgo string - wantPurpose4EnforceAlgo string - wantPurpose5EnforceAlgo string - wantPurpose6EnforceAlgo string - wantPurpose7EnforceAlgo string - wantPurpose8EnforceAlgo string - wantPurpose9EnforceAlgo string - wantPurpose10EnforceAlgo string - wantEnforcePurposesSet bool - wantPurpose1EnforcePurpose string - wantPurpose2EnforcePurpose string - wantPurpose3EnforcePurpose string - wantPurpose4EnforcePurpose string - wantPurpose5EnforcePurpose string - wantPurpose6EnforcePurpose string - wantPurpose7EnforcePurpose string - wantPurpose8EnforcePurpose string - wantPurpose9EnforcePurpose string - wantPurpose10EnforcePurpose string - }{ - { - description: "enforce_algo and enforce_purpose are not set", - config: []byte{}, - wantEnforceAlgosSet: false, - wantEnforcePurposesSet: false, - }, - { - description: "enforce_algo not set; set it based on enforce_purpose string value", - config: []byte(` - gdpr: - tcf2: - purpose1: - enforce_purpose: "full" - purpose2: - enforce_purpose: "no" - purpose3: - enforce_purpose: "full" - purpose4: - enforce_purpose: "no" - purpose5: - enforce_purpose: "full" - purpose6: - enforce_purpose: "no" - purpose7: - enforce_purpose: "full" - purpose8: - enforce_purpose: "no" - purpose9: - enforce_purpose: "full" - purpose10: - enforce_purpose: "no" - `), - wantEnforceAlgosSet: true, - wantPurpose1EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose2EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose3EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose4EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose5EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose6EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose7EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose8EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose9EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose10EnforceAlgo: TCF2EnforceAlgoFull, - wantEnforcePurposesSet: true, - wantPurpose1EnforcePurpose: trueStr, - wantPurpose2EnforcePurpose: falseStr, - wantPurpose3EnforcePurpose: trueStr, - wantPurpose4EnforcePurpose: falseStr, - wantPurpose5EnforcePurpose: trueStr, - wantPurpose6EnforcePurpose: falseStr, - wantPurpose7EnforcePurpose: trueStr, - wantPurpose8EnforcePurpose: falseStr, - wantPurpose9EnforcePurpose: trueStr, - wantPurpose10EnforcePurpose: falseStr, - }, - { - description: "enforce_algo not set; don't set it based on enforce_purpose bool value", - config: []byte(` - gdpr: - tcf2: - purpose1: - enforce_purpose: true - purpose2: - enforce_purpose: false - purpose3: - enforce_purpose: true - purpose4: - enforce_purpose: false - purpose5: - enforce_purpose: true - purpose6: - enforce_purpose: false - purpose7: - enforce_purpose: true - purpose8: - enforce_purpose: false - purpose9: - enforce_purpose: true - purpose10: - enforce_purpose: false - `), - wantEnforceAlgosSet: false, - wantEnforcePurposesSet: true, - wantPurpose1EnforcePurpose: trueStr, - wantPurpose2EnforcePurpose: falseStr, - wantPurpose3EnforcePurpose: trueStr, - wantPurpose4EnforcePurpose: falseStr, - wantPurpose5EnforcePurpose: trueStr, - wantPurpose6EnforcePurpose: falseStr, - wantPurpose7EnforcePurpose: trueStr, - wantPurpose8EnforcePurpose: falseStr, - wantPurpose9EnforcePurpose: trueStr, - wantPurpose10EnforcePurpose: falseStr, - }, - { - description: "enforce_algo is set and enforce_purpose is not; enforce_algo is unchanged", - config: []byte(` - gdpr: - tcf2: - purpose1: - enforce_algo: "full" - purpose2: - enforce_algo: "full" - purpose3: - enforce_algo: "full" - purpose4: - enforce_algo: "full" - purpose5: - enforce_algo: "full" - purpose6: - enforce_algo: "full" - purpose7: - enforce_algo: "full" - purpose8: - enforce_algo: "full" - purpose9: - enforce_algo: "full" - purpose10: - enforce_algo: "full" - `), - wantEnforceAlgosSet: true, - wantPurpose1EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose2EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose3EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose4EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose5EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose6EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose7EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose8EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose9EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose10EnforceAlgo: TCF2EnforceAlgoFull, - wantEnforcePurposesSet: false, - }, - { - description: "enforce_algo and enforce_purpose are set; enforce_algo is unchanged", - config: []byte(` - gdpr: - tcf2: - purpose1: - enforce_algo: "full" - enforce_purpose: "no" - purpose2: - enforce_algo: "full" - enforce_purpose: "no" - purpose3: - enforce_algo: "full" - enforce_purpose: "no" - purpose4: - enforce_algo: "full" - enforce_purpose: "no" - purpose5: - enforce_algo: "full" - enforce_purpose: "no" - purpose6: - enforce_algo: "full" - enforce_purpose: "no" - purpose7: - enforce_algo: "full" - enforce_purpose: "no" - purpose8: - enforce_algo: "full" - enforce_purpose: "no" - purpose9: - enforce_algo: "full" - enforce_purpose: "no" - purpose10: - enforce_algo: "full" - enforce_purpose: "no" - `), - wantEnforceAlgosSet: true, - wantPurpose1EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose2EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose3EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose4EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose5EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose6EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose7EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose8EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose9EnforceAlgo: TCF2EnforceAlgoFull, - wantPurpose10EnforceAlgo: TCF2EnforceAlgoFull, - wantEnforcePurposesSet: true, - wantPurpose1EnforcePurpose: falseStr, - wantPurpose2EnforcePurpose: falseStr, - wantPurpose3EnforcePurpose: falseStr, - wantPurpose4EnforcePurpose: falseStr, - wantPurpose5EnforcePurpose: falseStr, - wantPurpose6EnforcePurpose: falseStr, - wantPurpose7EnforcePurpose: falseStr, - wantPurpose8EnforcePurpose: falseStr, - wantPurpose9EnforcePurpose: falseStr, - wantPurpose10EnforcePurpose: falseStr, - }, - } - - for _, tt := range tests { - v := viper.New() - v.SetConfigType("yaml") - v.ReadConfig(bytes.NewBuffer(tt.config)) - - migrateConfigTCF2EnforcePurposeFlags(v) - - if tt.wantEnforceAlgosSet { - assert.Equal(t, tt.wantPurpose1EnforceAlgo, v.GetString("gdpr.tcf2.purpose1.enforce_algo"), tt.description) - assert.Equal(t, tt.wantPurpose2EnforceAlgo, v.GetString("gdpr.tcf2.purpose2.enforce_algo"), tt.description) - assert.Equal(t, tt.wantPurpose3EnforceAlgo, v.GetString("gdpr.tcf2.purpose3.enforce_algo"), tt.description) - assert.Equal(t, tt.wantPurpose4EnforceAlgo, v.GetString("gdpr.tcf2.purpose4.enforce_algo"), tt.description) - assert.Equal(t, tt.wantPurpose5EnforceAlgo, v.GetString("gdpr.tcf2.purpose5.enforce_algo"), tt.description) - assert.Equal(t, tt.wantPurpose6EnforceAlgo, v.GetString("gdpr.tcf2.purpose6.enforce_algo"), tt.description) - assert.Equal(t, tt.wantPurpose7EnforceAlgo, v.GetString("gdpr.tcf2.purpose7.enforce_algo"), tt.description) - assert.Equal(t, tt.wantPurpose8EnforceAlgo, v.GetString("gdpr.tcf2.purpose8.enforce_algo"), tt.description) - assert.Equal(t, tt.wantPurpose9EnforceAlgo, v.GetString("gdpr.tcf2.purpose9.enforce_algo"), tt.description) - assert.Equal(t, tt.wantPurpose10EnforceAlgo, v.GetString("gdpr.tcf2.purpose10.enforce_algo"), tt.description) - } else { - assert.Nil(t, v.Get("gdpr.tcf2.purpose1.enforce_algo"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose2.enforce_algo"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose3.enforce_algo"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose4.enforce_algo"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose5.enforce_algo"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose6.enforce_algo"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose7.enforce_algo"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose8.enforce_algo"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose9.enforce_algo"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose10.enforce_algo"), tt.description) - } - - if tt.wantEnforcePurposesSet { - assert.Equal(t, tt.wantPurpose1EnforcePurpose, v.GetString("gdpr.tcf2.purpose1.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose2EnforcePurpose, v.GetString("gdpr.tcf2.purpose2.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose3EnforcePurpose, v.GetString("gdpr.tcf2.purpose3.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose4EnforcePurpose, v.GetString("gdpr.tcf2.purpose4.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose5EnforcePurpose, v.GetString("gdpr.tcf2.purpose5.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose6EnforcePurpose, v.GetString("gdpr.tcf2.purpose6.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose7EnforcePurpose, v.GetString("gdpr.tcf2.purpose7.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose8EnforcePurpose, v.GetString("gdpr.tcf2.purpose8.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose9EnforcePurpose, v.GetString("gdpr.tcf2.purpose9.enforce_purpose"), tt.description) - assert.Equal(t, tt.wantPurpose10EnforcePurpose, v.GetString("gdpr.tcf2.purpose10.enforce_purpose"), tt.description) - } else { - assert.Nil(t, v.Get("gdpr.tcf2.purpose1.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose2.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose3.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose4.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose5.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose6.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose7.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose8.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose9.enforce_purpose"), tt.description) - assert.Nil(t, v.Get("gdpr.tcf2.purpose10.enforce_purpose"), tt.description) - } - } -} - -func TestMigrateConfigDatabaseConnection(t *testing.T) { - type configs struct { - old []byte - new []byte - both []byte - } - - // Stored Requests Config Migration - storedReqestsConfigs := configs{ - old: []byte(` - stored_requests: - postgres: - connection: - dbname: "old_connection_dbname" - host: "old_connection_host" - port: 1000 - user: "old_connection_user" - password: "old_connection_password" - fetcher: - query: "old_fetcher_query" - amp_query: "old_fetcher_amp_query" - initialize_caches: - timeout_ms: 1000 - query: "old_initialize_caches_query" - amp_query: "old_initialize_caches_amp_query" - poll_for_updates: - refresh_rate_seconds: 1000 - timeout_ms: 1000 - query: "old_poll_for_updates_query" - amp_query: "old_poll_for_updates_amp_query" - `), - new: []byte(` - stored_requests: - database: - connection: - dbname: "new_connection_dbname" - host: "new_connection_host" - port: 2000 - user: "new_connection_user" - password: "new_connection_password" - fetcher: - query: "new_fetcher_query" - amp_query: "new_fetcher_amp_query" - initialize_caches: - timeout_ms: 2000 - query: "new_initialize_caches_query" - amp_query: "new_initialize_caches_amp_query" - poll_for_updates: - refresh_rate_seconds: 2000 - timeout_ms: 2000 - query: "new_poll_for_updates_query" - amp_query: "new_poll_for_updates_amp_query" - `), - both: []byte(` - stored_requests: - postgres: - connection: - dbname: "old_connection_dbname" - host: "old_connection_host" - port: 1000 - user: "old_connection_user" - password: "old_connection_password" - fetcher: - query: "old_fetcher_query" - amp_query: "old_fetcher_amp_query" - initialize_caches: - timeout_ms: 1000 - query: "old_initialize_caches_query" - amp_query: "old_initialize_caches_amp_query" - poll_for_updates: - refresh_rate_seconds: 1000 - timeout_ms: 1000 - query: "old_poll_for_updates_query" - amp_query: "old_poll_for_updates_amp_query" - database: - connection: - dbname: "new_connection_dbname" - host: "new_connection_host" - port: 2000 - user: "new_connection_user" - password: "new_connection_password" - fetcher: - query: "new_fetcher_query" - amp_query: "new_fetcher_amp_query" - initialize_caches: - timeout_ms: 2000 - query: "new_initialize_caches_query" - amp_query: "new_initialize_caches_amp_query" - poll_for_updates: - refresh_rate_seconds: 2000 - timeout_ms: 2000 - query: "new_poll_for_updates_query" - amp_query: "new_poll_for_updates_amp_query" - `), - } - - storedRequestsTests := []struct { - description string - config []byte - - want_connection_dbname string - want_connection_host string - want_connection_port int - want_connection_user string - want_connection_password string - want_fetcher_query string - want_fetcher_amp_query string - want_initialize_caches_timeout_ms int - want_initialize_caches_query string - want_initialize_caches_amp_query string - want_poll_for_updates_refresh_rate_seconds int - want_poll_for_updates_timeout_ms int - want_poll_for_updates_query string - want_poll_for_updates_amp_query string - }{ - { - description: "New config and old config not set", - config: []byte{}, - }, - { - description: "New config not set, old config set", - config: storedReqestsConfigs.old, - - want_connection_dbname: "old_connection_dbname", - want_connection_host: "old_connection_host", - want_connection_port: 1000, - want_connection_user: "old_connection_user", - want_connection_password: "old_connection_password", - want_fetcher_query: "old_fetcher_query", - want_fetcher_amp_query: "old_fetcher_amp_query", - want_initialize_caches_timeout_ms: 1000, - want_initialize_caches_query: "old_initialize_caches_query", - want_initialize_caches_amp_query: "old_initialize_caches_amp_query", - want_poll_for_updates_refresh_rate_seconds: 1000, - want_poll_for_updates_timeout_ms: 1000, - want_poll_for_updates_query: "old_poll_for_updates_query", - want_poll_for_updates_amp_query: "old_poll_for_updates_amp_query", - }, - { - description: "New config set, old config not set", - config: storedReqestsConfigs.new, - - want_connection_dbname: "new_connection_dbname", - want_connection_host: "new_connection_host", - want_connection_port: 2000, - want_connection_user: "new_connection_user", - want_connection_password: "new_connection_password", - want_fetcher_query: "new_fetcher_query", - want_fetcher_amp_query: "new_fetcher_amp_query", - want_initialize_caches_timeout_ms: 2000, - want_initialize_caches_query: "new_initialize_caches_query", - want_initialize_caches_amp_query: "new_initialize_caches_amp_query", - want_poll_for_updates_refresh_rate_seconds: 2000, - want_poll_for_updates_timeout_ms: 2000, - want_poll_for_updates_query: "new_poll_for_updates_query", - want_poll_for_updates_amp_query: "new_poll_for_updates_amp_query", - }, - { - description: "New config and old config set", - config: storedReqestsConfigs.both, - - want_connection_dbname: "new_connection_dbname", - want_connection_host: "new_connection_host", - want_connection_port: 2000, - want_connection_user: "new_connection_user", - want_connection_password: "new_connection_password", - want_fetcher_query: "new_fetcher_query", - want_fetcher_amp_query: "new_fetcher_amp_query", - want_initialize_caches_timeout_ms: 2000, - want_initialize_caches_query: "new_initialize_caches_query", - want_initialize_caches_amp_query: "new_initialize_caches_amp_query", - want_poll_for_updates_refresh_rate_seconds: 2000, - want_poll_for_updates_timeout_ms: 2000, - want_poll_for_updates_query: "new_poll_for_updates_query", - want_poll_for_updates_amp_query: "new_poll_for_updates_amp_query", - }, - } - - for _, tt := range storedRequestsTests { - v := viper.New() - v.SetConfigType("yaml") - v.ReadConfig(bytes.NewBuffer(tt.config)) - - migrateConfigDatabaseConnection(v) - - if len(tt.config) > 0 { - assert.Equal(t, tt.want_connection_dbname, v.GetString("stored_requests.database.connection.dbname"), tt.description) - assert.Equal(t, tt.want_connection_host, v.GetString("stored_requests.database.connection.host"), tt.description) - assert.Equal(t, tt.want_connection_port, v.GetInt("stored_requests.database.connection.port"), tt.description) - assert.Equal(t, tt.want_connection_user, v.GetString("stored_requests.database.connection.user"), tt.description) - assert.Equal(t, tt.want_connection_password, v.GetString("stored_requests.database.connection.password"), tt.description) - assert.Equal(t, tt.want_fetcher_query, v.GetString("stored_requests.database.fetcher.query"), tt.description) - assert.Equal(t, tt.want_fetcher_amp_query, v.GetString("stored_requests.database.fetcher.amp_query"), tt.description) - assert.Equal(t, tt.want_initialize_caches_timeout_ms, v.GetInt("stored_requests.database.initialize_caches.timeout_ms"), tt.description) - assert.Equal(t, tt.want_initialize_caches_query, v.GetString("stored_requests.database.initialize_caches.query"), tt.description) - assert.Equal(t, tt.want_initialize_caches_amp_query, v.GetString("stored_requests.database.initialize_caches.amp_query"), tt.description) - assert.Equal(t, tt.want_poll_for_updates_refresh_rate_seconds, v.GetInt("stored_requests.database.poll_for_updates.refresh_rate_seconds"), tt.description) - assert.Equal(t, tt.want_poll_for_updates_timeout_ms, v.GetInt("stored_requests.database.poll_for_updates.timeout_ms"), tt.description) - assert.Equal(t, tt.want_poll_for_updates_query, v.GetString("stored_requests.database.poll_for_updates.query"), tt.description) - assert.Equal(t, tt.want_poll_for_updates_amp_query, v.GetString("stored_requests.database.poll_for_updates.amp_query"), tt.description) - } else { - assert.Nil(t, v.Get("stored_requests.database.connection.dbname"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.connection.host"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.connection.port"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.connection.user"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.connection.password"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.fetcher.query"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.fetcher.amp_query"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.initialize_caches.timeout_ms"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.initialize_caches.query"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.initialize_caches.amp_query"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.poll_for_updates.refresh_rate_seconds"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.poll_for_updates.timeout_ms"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.poll_for_updates.query"), tt.description) - assert.Nil(t, v.Get("stored_requests.database.poll_for_updates.amp_query"), tt.description) - } - } - - // Stored Video Reqs Config Migration - storedVideoReqsConfigs := configs{ - old: []byte(` - stored_video_req: - postgres: - connection: - dbname: "old_connection_dbname" - host: "old_connection_host" - port: 1000 - user: "old_connection_user" - password: "old_connection_password" - fetcher: - query: "old_fetcher_query" - initialize_caches: - timeout_ms: 1000 - query: "old_initialize_caches_query" - poll_for_updates: - refresh_rate_seconds: 1000 - timeout_ms: 1000 - query: "old_poll_for_updates_query" - `), - new: []byte(` - stored_video_req: - database: - connection: - dbname: "new_connection_dbname" - host: "new_connection_host" - port: 2000 - user: "new_connection_user" - password: "new_connection_password" - fetcher: - query: "new_fetcher_query" - initialize_caches: - timeout_ms: 2000 - query: "new_initialize_caches_query" - poll_for_updates: - refresh_rate_seconds: 2000 - timeout_ms: 2000 - query: "new_poll_for_updates_query" - `), - both: []byte(` - stored_video_req: - postgres: - connection: - dbname: "old_connection_dbname" - host: "old_connection_host" - port: 1000 - user: "old_connection_user" - password: "old_connection_password" - fetcher: - query: "old_fetcher_query" - initialize_caches: - timeout_ms: 1000 - query: "old_initialize_caches_query" - poll_for_updates: - refresh_rate_seconds: 1000 - timeout_ms: 1000 - query: "old_poll_for_updates_query" - database: - connection: - dbname: "new_connection_dbname" - host: "new_connection_host" - port: 2000 - user: "new_connection_user" - password: "new_connection_password" - fetcher: - query: "new_fetcher_query" - initialize_caches: - timeout_ms: 2000 - query: "new_initialize_caches_query" - poll_for_updates: - refresh_rate_seconds: 2000 - timeout_ms: 2000 - query: "new_poll_for_updates_query" - `), - } - - storedVideoReqsTests := []struct { - description string - config []byte - - want_connection_dbname string - want_connection_host string - want_connection_port int - want_connection_user string - want_connection_password string - want_fetcher_query string - want_initialize_caches_timeout_ms int - want_initialize_caches_query string - want_poll_for_updates_refresh_rate_seconds int - want_poll_for_updates_timeout_ms int - want_poll_for_updates_query string - }{ - { - description: "New config and old config not set", - config: []byte{}, - }, - { - description: "New config not set, old config set", - config: storedVideoReqsConfigs.old, - - want_connection_dbname: "old_connection_dbname", - want_connection_host: "old_connection_host", - want_connection_port: 1000, - want_connection_user: "old_connection_user", - want_connection_password: "old_connection_password", - want_fetcher_query: "old_fetcher_query", - want_initialize_caches_timeout_ms: 1000, - want_initialize_caches_query: "old_initialize_caches_query", - want_poll_for_updates_refresh_rate_seconds: 1000, - want_poll_for_updates_timeout_ms: 1000, - want_poll_for_updates_query: "old_poll_for_updates_query", - }, - { - description: "New config set, old config not set", - config: storedVideoReqsConfigs.new, - - want_connection_dbname: "new_connection_dbname", - want_connection_host: "new_connection_host", - want_connection_port: 2000, - want_connection_user: "new_connection_user", - want_connection_password: "new_connection_password", - want_fetcher_query: "new_fetcher_query", - want_initialize_caches_timeout_ms: 2000, - want_initialize_caches_query: "new_initialize_caches_query", - want_poll_for_updates_refresh_rate_seconds: 2000, - want_poll_for_updates_timeout_ms: 2000, - want_poll_for_updates_query: "new_poll_for_updates_query", - }, - { - description: "New config and old config set", - config: storedVideoReqsConfigs.both, - - want_connection_dbname: "new_connection_dbname", - want_connection_host: "new_connection_host", - want_connection_port: 2000, - want_connection_user: "new_connection_user", - want_connection_password: "new_connection_password", - want_fetcher_query: "new_fetcher_query", - want_initialize_caches_timeout_ms: 2000, - want_initialize_caches_query: "new_initialize_caches_query", - want_poll_for_updates_refresh_rate_seconds: 2000, - want_poll_for_updates_timeout_ms: 2000, - want_poll_for_updates_query: "new_poll_for_updates_query", - }, - } - - for _, tt := range storedVideoReqsTests { - v := viper.New() - v.SetConfigType("yaml") - v.ReadConfig(bytes.NewBuffer(tt.config)) - - migrateConfigDatabaseConnection(v) - - if len(tt.config) > 0 { - assert.Equal(t, tt.want_connection_dbname, v.Get("stored_video_req.database.connection.dbname").(string), tt.description) - assert.Equal(t, tt.want_connection_host, v.Get("stored_video_req.database.connection.host").(string), tt.description) - assert.Equal(t, tt.want_connection_port, v.Get("stored_video_req.database.connection.port").(int), tt.description) - assert.Equal(t, tt.want_connection_user, v.Get("stored_video_req.database.connection.user").(string), tt.description) - assert.Equal(t, tt.want_connection_password, v.Get("stored_video_req.database.connection.password").(string), tt.description) - assert.Equal(t, tt.want_fetcher_query, v.Get("stored_video_req.database.fetcher.query").(string), tt.description) - assert.Equal(t, tt.want_initialize_caches_timeout_ms, v.Get("stored_video_req.database.initialize_caches.timeout_ms").(int), tt.description) - assert.Equal(t, tt.want_initialize_caches_query, v.Get("stored_video_req.database.initialize_caches.query").(string), tt.description) - assert.Equal(t, tt.want_poll_for_updates_refresh_rate_seconds, v.Get("stored_video_req.database.poll_for_updates.refresh_rate_seconds").(int), tt.description) - assert.Equal(t, tt.want_poll_for_updates_timeout_ms, v.Get("stored_video_req.database.poll_for_updates.timeout_ms").(int), tt.description) - assert.Equal(t, tt.want_poll_for_updates_query, v.Get("stored_video_req.database.poll_for_updates.query").(string), tt.description) - } else { - assert.Nil(t, v.Get("stored_video_req.database.connection.dbname"), tt.description) - assert.Nil(t, v.Get("stored_video_req.database.connection.host"), tt.description) - assert.Nil(t, v.Get("stored_video_req.database.connection.port"), tt.description) - assert.Nil(t, v.Get("stored_video_req.database.connection.user"), tt.description) - assert.Nil(t, v.Get("stored_video_req.database.connection.password"), tt.description) - assert.Nil(t, v.Get("stored_video_req.database.fetcher.query"), tt.description) - assert.Nil(t, v.Get("stored_video_req.database.initialize_caches.timeout_ms"), tt.description) - assert.Nil(t, v.Get("stored_video_req.database.initialize_caches.query"), tt.description) - assert.Nil(t, v.Get("stored_video_req.database.poll_for_updates.refresh_rate_seconds"), tt.description) - assert.Nil(t, v.Get("stored_video_req.database.poll_for_updates.timeout_ms"), tt.description) - assert.Nil(t, v.Get("stored_video_req.database.poll_for_updates.query"), tt.description) - } - } - - // Stored Responses Config Migration - storedResponsesConfigs := configs{ - old: []byte(` - stored_responses: - postgres: - connection: - dbname: "old_connection_dbname" - host: "old_connection_host" - port: 1000 - user: "old_connection_user" - password: "old_connection_password" - fetcher: - query: "old_fetcher_query" - initialize_caches: - timeout_ms: 1000 - query: "old_initialize_caches_query" - poll_for_updates: - refresh_rate_seconds: 1000 - timeout_ms: 1000 - query: "old_poll_for_updates_query" - `), - new: []byte(` - stored_responses: - database: - connection: - dbname: "new_connection_dbname" - host: "new_connection_host" - port: 2000 - user: "new_connection_user" - password: "new_connection_password" - fetcher: - query: "new_fetcher_query" - initialize_caches: - timeout_ms: 2000 - query: "new_initialize_caches_query" - poll_for_updates: - refresh_rate_seconds: 2000 - timeout_ms: 2000 - query: "new_poll_for_updates_query" - `), - both: []byte(` - stored_responses: - postgres: - connection: - dbname: "old_connection_dbname" - host: "old_connection_host" - port: 1000 - user: "old_connection_user" - password: "old_connection_password" - fetcher: - query: "old_fetcher_query" - initialize_caches: - timeout_ms: 1000 - query: "old_initialize_caches_query" - poll_for_updates: - refresh_rate_seconds: 1000 - timeout_ms: 1000 - query: "old_poll_for_updates_query" - database: - connection: - dbname: "new_connection_dbname" - host: "new_connection_host" - port: 2000 - user: "new_connection_user" - password: "new_connection_password" - fetcher: - query: "new_fetcher_query" - initialize_caches: - timeout_ms: 2000 - query: "new_initialize_caches_query" - poll_for_updates: - refresh_rate_seconds: 2000 - timeout_ms: 2000 - query: "new_poll_for_updates_query" - `), - } - - storedResponsesTests := []struct { - description string - config []byte - - want_connection_dbname string - want_connection_host string - want_connection_port int - want_connection_user string - want_connection_password string - want_fetcher_query string - want_initialize_caches_timeout_ms int - want_initialize_caches_query string - want_poll_for_updates_refresh_rate_seconds int - want_poll_for_updates_timeout_ms int - want_poll_for_updates_query string - }{ - { - description: "New config and old config not set", - config: []byte{}, - }, - { - description: "New config not set, old config set", - config: storedResponsesConfigs.old, - - want_connection_dbname: "old_connection_dbname", - want_connection_host: "old_connection_host", - want_connection_port: 1000, - want_connection_user: "old_connection_user", - want_connection_password: "old_connection_password", - want_fetcher_query: "old_fetcher_query", - want_initialize_caches_timeout_ms: 1000, - want_initialize_caches_query: "old_initialize_caches_query", - want_poll_for_updates_refresh_rate_seconds: 1000, - want_poll_for_updates_timeout_ms: 1000, - want_poll_for_updates_query: "old_poll_for_updates_query", - }, - { - description: "New config set, old config not set", - config: storedResponsesConfigs.new, - - want_connection_dbname: "new_connection_dbname", - want_connection_host: "new_connection_host", - want_connection_port: 2000, - want_connection_user: "new_connection_user", - want_connection_password: "new_connection_password", - want_fetcher_query: "new_fetcher_query", - want_initialize_caches_timeout_ms: 2000, - want_initialize_caches_query: "new_initialize_caches_query", - want_poll_for_updates_refresh_rate_seconds: 2000, - want_poll_for_updates_timeout_ms: 2000, - want_poll_for_updates_query: "new_poll_for_updates_query", - }, - { - description: "New config and old config set", - config: storedResponsesConfigs.both, - - want_connection_dbname: "new_connection_dbname", - want_connection_host: "new_connection_host", - want_connection_port: 2000, - want_connection_user: "new_connection_user", - want_connection_password: "new_connection_password", - want_fetcher_query: "new_fetcher_query", - want_initialize_caches_timeout_ms: 2000, - want_initialize_caches_query: "new_initialize_caches_query", - want_poll_for_updates_refresh_rate_seconds: 2000, - want_poll_for_updates_timeout_ms: 2000, - want_poll_for_updates_query: "new_poll_for_updates_query", - }, - } - - for _, tt := range storedResponsesTests { - v := viper.New() - v.SetConfigType("yaml") - v.ReadConfig(bytes.NewBuffer(tt.config)) - - migrateConfigDatabaseConnection(v) - - if len(tt.config) > 0 { - assert.Equal(t, tt.want_connection_dbname, v.Get("stored_responses.database.connection.dbname").(string), tt.description) - assert.Equal(t, tt.want_connection_host, v.Get("stored_responses.database.connection.host").(string), tt.description) - assert.Equal(t, tt.want_connection_port, v.Get("stored_responses.database.connection.port").(int), tt.description) - assert.Equal(t, tt.want_connection_user, v.Get("stored_responses.database.connection.user").(string), tt.description) - assert.Equal(t, tt.want_connection_password, v.Get("stored_responses.database.connection.password").(string), tt.description) - assert.Equal(t, tt.want_fetcher_query, v.Get("stored_responses.database.fetcher.query").(string), tt.description) - assert.Equal(t, tt.want_initialize_caches_timeout_ms, v.Get("stored_responses.database.initialize_caches.timeout_ms").(int), tt.description) - assert.Equal(t, tt.want_initialize_caches_query, v.Get("stored_responses.database.initialize_caches.query").(string), tt.description) - assert.Equal(t, tt.want_poll_for_updates_refresh_rate_seconds, v.Get("stored_responses.database.poll_for_updates.refresh_rate_seconds").(int), tt.description) - assert.Equal(t, tt.want_poll_for_updates_timeout_ms, v.Get("stored_responses.database.poll_for_updates.timeout_ms").(int), tt.description) - assert.Equal(t, tt.want_poll_for_updates_query, v.Get("stored_responses.database.poll_for_updates.query").(string), tt.description) - } else { - assert.Nil(t, v.Get("stored_responses.database.connection.dbname"), tt.description) - assert.Nil(t, v.Get("stored_responses.database.connection.host"), tt.description) - assert.Nil(t, v.Get("stored_responses.database.connection.port"), tt.description) - assert.Nil(t, v.Get("stored_responses.database.connection.user"), tt.description) - assert.Nil(t, v.Get("stored_responses.database.connection.password"), tt.description) - assert.Nil(t, v.Get("stored_responses.database.fetcher.query"), tt.description) - assert.Nil(t, v.Get("stored_responses.database.initialize_caches.timeout_ms"), tt.description) - assert.Nil(t, v.Get("stored_responses.database.initialize_caches.query"), tt.description) - assert.Nil(t, v.Get("stored_responses.database.poll_for_updates.refresh_rate_seconds"), tt.description) - assert.Nil(t, v.Get("stored_responses.database.poll_for_updates.timeout_ms"), tt.description) - assert.Nil(t, v.Get("stored_responses.database.poll_for_updates.query"), tt.description) - } - } -} - -func TestMigrateConfigDatabaseConnectionUsingEnvVars(t *testing.T) { - tests := []struct { - description string - prefix string - setDatabaseEnvVars bool - setPostgresEnvVars bool - }{ - { - description: "stored requests old config set", - prefix: "stored_requests", - setPostgresEnvVars: true, - }, - { - description: "stored requests new config set", - prefix: "stored_requests", - setDatabaseEnvVars: true, - }, - { - description: "stored requests old and new config set", - prefix: "stored_requests", - setDatabaseEnvVars: true, - setPostgresEnvVars: true, - }, - { - description: "stored video requests old config set", - prefix: "stored_video_req", - setPostgresEnvVars: true, - }, - { - description: "stored video requests new config set", - prefix: "stored_video_req", - setDatabaseEnvVars: true, - }, - { - description: "stored video requests old and new config set", - prefix: "stored_video_req", - setDatabaseEnvVars: true, - setPostgresEnvVars: true, - }, - { - description: "stored responses old config set", - prefix: "stored_responses", - setPostgresEnvVars: true, - }, - { - description: "stored responses new config set", - prefix: "stored_responses", - setDatabaseEnvVars: true, - }, - { - description: "stored responses old and new config set", - prefix: "stored_responses", - setDatabaseEnvVars: true, - setPostgresEnvVars: true, - }, - } - - pgValues := map[string]string{ - "CONNECTION_DBNAME": "pg-dbname", - "CONNECTION_HOST": "pg-host", - "CONNECTION_PORT": "1", - "CONNECTION_USER": "pg-user", - "CONNECTION_PASSWORD": "pg-password", - "FETCHER_QUERY": "pg-fetcher-query", - "FETCHER_AMP_QUERY": "pg-fetcher-amp-query", - "INITIALIZE_CACHES_TIMEOUT_MS": "2", - "INITIALIZE_CACHES_QUERY": "pg-init-caches-query", - "INITIALIZE_CACHES_AMP_QUERY": "pg-init-caches-amp-query", - "POLL_FOR_UPDATES_REFRESH_RATE_SECONDS": "3", - "POLL_FOR_UPDATES_TIMEOUT_MS": "4", - "POLL_FOR_UPDATES_QUERY": "pg-poll-query $LAST_UPDATED", - "POLL_FOR_UPDATES_AMP_QUERY": "pg-poll-amp-query $LAST_UPDATED", - } - dbValues := map[string]string{ - "CONNECTION_DBNAME": "db-dbname", - "CONNECTION_HOST": "db-host", - "CONNECTION_PORT": "5", - "CONNECTION_USER": "db-user", - "CONNECTION_PASSWORD": "db-password", - "FETCHER_QUERY": "db-fetcher-query", - "FETCHER_AMP_QUERY": "db-fetcher-amp-query", - "INITIALIZE_CACHES_TIMEOUT_MS": "6", - "INITIALIZE_CACHES_QUERY": "db-init-caches-query", - "INITIALIZE_CACHES_AMP_QUERY": "db-init-caches-amp-query", - "POLL_FOR_UPDATES_REFRESH_RATE_SECONDS": "7", - "POLL_FOR_UPDATES_TIMEOUT_MS": "8", - "POLL_FOR_UPDATES_QUERY": "db-poll-query $LAST_UPDATED", - "POLL_FOR_UPDATES_AMP_QUERY": "db-poll-amp-query $LAST_UPDATED", - } - - for _, tt := range tests { - t.Run(tt.description, func(t *testing.T) { - prefix := "PBS_" + strings.ToUpper(tt.prefix) - - // validation rules require in memory cache type to not be "none" - // given that we want to set the poll for update queries to non-empty values - envVarName := prefix + "_IN_MEMORY_CACHE_TYPE" - if oldval, ok := os.LookupEnv(envVarName); ok { - defer os.Setenv(envVarName, oldval) - } else { - defer os.Unsetenv(envVarName) - } - os.Setenv(envVarName, "unbounded") - - if tt.setPostgresEnvVars { - for suffix, v := range pgValues { - envVarName := prefix + "_POSTGRES_" + suffix - if oldval, ok := os.LookupEnv(envVarName); ok { - defer os.Setenv(envVarName, oldval) - } else { - defer os.Unsetenv(envVarName) - } - os.Setenv(envVarName, v) - } - } - if tt.setDatabaseEnvVars { - for suffix, v := range dbValues { - envVarName := prefix + "_DATABASE_" + suffix - if oldval, ok := os.LookupEnv(envVarName); ok { - defer os.Setenv(envVarName, oldval) - } else { - defer os.Unsetenv(envVarName) - } - os.Setenv(envVarName, v) - } - } - - c, _ := newDefaultConfig(t) - - expectedDatabaseValues := map[string]string{} - if tt.setDatabaseEnvVars { - expectedDatabaseValues = dbValues - } else if tt.setPostgresEnvVars { - expectedDatabaseValues = pgValues - } - - if tt.prefix == "stored_requests" { - assert.Equal(t, expectedDatabaseValues["CONNECTION_DBNAME"], c.StoredRequests.Database.ConnectionInfo.Database, tt.description) - assert.Equal(t, expectedDatabaseValues["CONNECTION_HOST"], c.StoredRequests.Database.ConnectionInfo.Host, tt.description) - assert.Equal(t, expectedDatabaseValues["CONNECTION_PORT"], strconv.Itoa(c.StoredRequests.Database.ConnectionInfo.Port), tt.description) - assert.Equal(t, expectedDatabaseValues["CONNECTION_USER"], c.StoredRequests.Database.ConnectionInfo.Username, tt.description) - assert.Equal(t, expectedDatabaseValues["CONNECTION_PASSWORD"], c.StoredRequests.Database.ConnectionInfo.Password, tt.description) - assert.Equal(t, expectedDatabaseValues["FETCHER_QUERY"], c.StoredRequests.Database.FetcherQueries.QueryTemplate, tt.description) - assert.Equal(t, expectedDatabaseValues["INITIALIZE_CACHES_TIMEOUT_MS"], strconv.Itoa(c.StoredRequests.Database.CacheInitialization.Timeout), tt.description) - assert.Equal(t, expectedDatabaseValues["INITIALIZE_CACHES_QUERY"], c.StoredRequests.Database.CacheInitialization.Query, tt.description) - assert.Equal(t, expectedDatabaseValues["POLL_FOR_UPDATES_REFRESH_RATE_SECONDS"], strconv.Itoa(c.StoredRequests.Database.PollUpdates.RefreshRate), tt.description) - assert.Equal(t, expectedDatabaseValues["POLL_FOR_UPDATES_TIMEOUT_MS"], strconv.Itoa(c.StoredRequests.Database.PollUpdates.Timeout), tt.description) - assert.Equal(t, expectedDatabaseValues["POLL_FOR_UPDATES_QUERY"], c.StoredRequests.Database.PollUpdates.Query, tt.description) - // AMP queries are only migrated for stored requests - assert.Equal(t, expectedDatabaseValues["FETCHER_AMP_QUERY"], c.StoredRequests.Database.FetcherQueries.AmpQueryTemplate, tt.description) - assert.Equal(t, expectedDatabaseValues["INITIALIZE_CACHES_AMP_QUERY"], c.StoredRequests.Database.CacheInitialization.AmpQuery, tt.description) - assert.Equal(t, expectedDatabaseValues["POLL_FOR_UPDATES_AMP_QUERY"], c.StoredRequests.Database.PollUpdates.AmpQuery, tt.description) - } else if tt.prefix == "stored_video_req" { - assert.Equal(t, expectedDatabaseValues["CONNECTION_DBNAME"], c.StoredVideo.Database.ConnectionInfo.Database, tt.description) - assert.Equal(t, expectedDatabaseValues["CONNECTION_HOST"], c.StoredVideo.Database.ConnectionInfo.Host, tt.description) - assert.Equal(t, expectedDatabaseValues["CONNECTION_PORT"], strconv.Itoa(c.StoredVideo.Database.ConnectionInfo.Port), tt.description) - assert.Equal(t, expectedDatabaseValues["CONNECTION_USER"], c.StoredVideo.Database.ConnectionInfo.Username, tt.description) - assert.Equal(t, expectedDatabaseValues["CONNECTION_PASSWORD"], c.StoredVideo.Database.ConnectionInfo.Password, tt.description) - assert.Equal(t, expectedDatabaseValues["FETCHER_QUERY"], c.StoredVideo.Database.FetcherQueries.QueryTemplate, tt.description) - assert.Equal(t, expectedDatabaseValues["INITIALIZE_CACHES_TIMEOUT_MS"], strconv.Itoa(c.StoredVideo.Database.CacheInitialization.Timeout), tt.description) - assert.Equal(t, expectedDatabaseValues["INITIALIZE_CACHES_QUERY"], c.StoredVideo.Database.CacheInitialization.Query, tt.description) - assert.Equal(t, expectedDatabaseValues["POLL_FOR_UPDATES_REFRESH_RATE_SECONDS"], strconv.Itoa(c.StoredVideo.Database.PollUpdates.RefreshRate), tt.description) - assert.Equal(t, expectedDatabaseValues["POLL_FOR_UPDATES_TIMEOUT_MS"], strconv.Itoa(c.StoredVideo.Database.PollUpdates.Timeout), tt.description) - assert.Equal(t, expectedDatabaseValues["POLL_FOR_UPDATES_QUERY"], c.StoredVideo.Database.PollUpdates.Query, tt.description) - assert.Empty(t, c.StoredVideo.Database.FetcherQueries.AmpQueryTemplate, tt.description) - assert.Empty(t, c.StoredVideo.Database.CacheInitialization.AmpQuery, tt.description) - assert.Empty(t, c.StoredVideo.Database.PollUpdates.AmpQuery, tt.description) - } else { - assert.Equal(t, expectedDatabaseValues["CONNECTION_DBNAME"], c.StoredResponses.Database.ConnectionInfo.Database, tt.description) - assert.Equal(t, expectedDatabaseValues["CONNECTION_HOST"], c.StoredResponses.Database.ConnectionInfo.Host, tt.description) - assert.Equal(t, expectedDatabaseValues["CONNECTION_PORT"], strconv.Itoa(c.StoredResponses.Database.ConnectionInfo.Port), tt.description) - assert.Equal(t, expectedDatabaseValues["CONNECTION_USER"], c.StoredResponses.Database.ConnectionInfo.Username, tt.description) - assert.Equal(t, expectedDatabaseValues["CONNECTION_PASSWORD"], c.StoredResponses.Database.ConnectionInfo.Password, tt.description) - assert.Equal(t, expectedDatabaseValues["FETCHER_QUERY"], c.StoredResponses.Database.FetcherQueries.QueryTemplate, tt.description) - assert.Equal(t, expectedDatabaseValues["INITIALIZE_CACHES_TIMEOUT_MS"], strconv.Itoa(c.StoredResponses.Database.CacheInitialization.Timeout), tt.description) - assert.Equal(t, expectedDatabaseValues["INITIALIZE_CACHES_QUERY"], c.StoredResponses.Database.CacheInitialization.Query, tt.description) - assert.Equal(t, expectedDatabaseValues["POLL_FOR_UPDATES_REFRESH_RATE_SECONDS"], strconv.Itoa(c.StoredResponses.Database.PollUpdates.RefreshRate), tt.description) - assert.Equal(t, expectedDatabaseValues["POLL_FOR_UPDATES_TIMEOUT_MS"], strconv.Itoa(c.StoredResponses.Database.PollUpdates.Timeout), tt.description) - assert.Equal(t, expectedDatabaseValues["POLL_FOR_UPDATES_QUERY"], c.StoredResponses.Database.PollUpdates.Query, tt.description) - assert.Empty(t, c.StoredResponses.Database.FetcherQueries.AmpQueryTemplate, tt.description) - assert.Empty(t, c.StoredResponses.Database.CacheInitialization.AmpQuery, tt.description) - assert.Empty(t, c.StoredResponses.Database.PollUpdates.AmpQuery, tt.description) - } - }) - } -} - -func TestMigrateConfigDatabaseQueryParams(t *testing.T) { - - config := []byte(` - stored_requests: - postgres: - fetcher: - query: - SELECT * FROM Table1 WHERE id in (%REQUEST_ID_LIST%) - UNION ALL - SELECT * FROM Table2 WHERE id in (%IMP_ID_LIST%) - UNION ALL - SELECT * FROM Table3 WHERE id in (%ID_LIST%) - amp_query: - SELECT * FROM Table1 WHERE id in (%REQUEST_ID_LIST%) - UNION ALL - SELECT * FROM Table2 WHERE id in (%IMP_ID_LIST%) - UNION ALL - SELECT * FROM Table3 WHERE id in (%ID_LIST%) - poll_for_updates: - query: "SELECT * FROM Table1 WHERE last_updated > $1 UNION ALL SELECT * FROM Table2 WHERE last_updated > $1" - amp_query: "SELECT * FROM Table1 WHERE last_updated > $1 UNION ALL SELECT * FROM Table2 WHERE last_updated > $1" - stored_video_req: - postgres: - fetcher: - query: - SELECT * FROM Table1 WHERE id in (%REQUEST_ID_LIST%) - UNION ALL - SELECT * FROM Table2 WHERE id in (%IMP_ID_LIST%) - UNION ALL - SELECT * FROM Table3 WHERE id in (%ID_LIST%) - amp_query: - SELECT * FROM Table1 WHERE id in (%REQUEST_ID_LIST%) - UNION ALL - SELECT * FROM Table2 WHERE id in (%IMP_ID_LIST%) - UNION ALL - SELECT * FROM Table3 WHERE id in (%ID_LIST%) - poll_for_updates: - query: "SELECT * FROM Table1 WHERE last_updated > $1 UNION ALL SELECT * FROM Table2 WHERE last_updated > $1" - amp_query: "SELECT * FROM Table1 WHERE last_updated > $1 UNION ALL SELECT * FROM Table2 WHERE last_updated > $1" - stored_responses: - postgres: - fetcher: - query: - SELECT * FROM Table1 WHERE id in (%REQUEST_ID_LIST%) - UNION ALL - SELECT * FROM Table2 WHERE id in (%IMP_ID_LIST%) - UNION ALL - SELECT * FROM Table3 WHERE id in (%ID_LIST%) - amp_query: - SELECT * FROM Table1 WHERE id in (%REQUEST_ID_LIST%) - UNION ALL - SELECT * FROM Table2 WHERE id in (%IMP_ID_LIST%) - UNION ALL - SELECT * FROM Table3 WHERE id in (%ID_LIST%) - poll_for_updates: - query: "SELECT * FROM Table1 WHERE last_updated > $1 UNION ALL SELECT * FROM Table2 WHERE last_updated > $1" - amp_query: "SELECT * FROM Table1 WHERE last_updated > $1 UNION ALL SELECT * FROM Table2 WHERE last_updated > $1" - `) - - want_queries := struct { - fetcher_query string - fetcher_amp_query string - poll_for_updates_query string - poll_for_updates_amp_query string - }{ - fetcher_query: "SELECT * FROM Table1 WHERE id in ($REQUEST_ID_LIST) " + - "UNION ALL " + - "SELECT * FROM Table2 WHERE id in ($IMP_ID_LIST) " + - "UNION ALL " + - "SELECT * FROM Table3 WHERE id in ($ID_LIST)", - fetcher_amp_query: "SELECT * FROM Table1 WHERE id in ($REQUEST_ID_LIST) " + - "UNION ALL " + - "SELECT * FROM Table2 WHERE id in ($IMP_ID_LIST) " + - "UNION ALL " + - "SELECT * FROM Table3 WHERE id in ($ID_LIST)", - poll_for_updates_query: "SELECT * FROM Table1 WHERE last_updated > $LAST_UPDATED UNION ALL SELECT * FROM Table2 WHERE last_updated > $LAST_UPDATED", - poll_for_updates_amp_query: "SELECT * FROM Table1 WHERE last_updated > $LAST_UPDATED UNION ALL SELECT * FROM Table2 WHERE last_updated > $LAST_UPDATED", - } - - v := viper.New() - v.SetConfigType("yaml") - err := v.ReadConfig(bytes.NewBuffer(config)) - assert.NoError(t, err) - - migrateConfigDatabaseConnection(v) - - // stored_requests queries - assert.Equal(t, want_queries.fetcher_query, v.GetString("stored_requests.database.fetcher.query")) - assert.Equal(t, want_queries.fetcher_amp_query, v.GetString("stored_requests.database.fetcher.amp_query")) - assert.Equal(t, want_queries.poll_for_updates_query, v.GetString("stored_requests.database.poll_for_updates.query")) - assert.Equal(t, want_queries.poll_for_updates_amp_query, v.GetString("stored_requests.database.poll_for_updates.amp_query")) - - // stored_video_req queries - assert.Equal(t, want_queries.fetcher_query, v.GetString("stored_video_req.database.fetcher.query")) - assert.Equal(t, want_queries.fetcher_amp_query, v.GetString("stored_video_req.database.fetcher.amp_query")) - assert.Equal(t, want_queries.poll_for_updates_query, v.GetString("stored_video_req.database.poll_for_updates.query")) - assert.Equal(t, want_queries.poll_for_updates_amp_query, v.GetString("stored_video_req.database.poll_for_updates.amp_query")) - - // stored_responses queries - assert.Equal(t, want_queries.fetcher_query, v.GetString("stored_responses.database.fetcher.query")) - assert.Equal(t, want_queries.fetcher_amp_query, v.GetString("stored_responses.database.fetcher.amp_query")) - assert.Equal(t, want_queries.poll_for_updates_query, v.GetString("stored_responses.database.poll_for_updates.query")) - assert.Equal(t, want_queries.poll_for_updates_amp_query, v.GetString("stored_responses.database.poll_for_updates.amp_query")) -} - -func TestMigrateConfigCompression(t *testing.T) { - testCases := []struct { - desc string - config []byte - wantEnableGZIP bool - wantReqGZIPEnabled bool - wantRespGZIPEnabled bool - }{ - - { - desc: "New config and old config not set", - config: []byte{}, - wantEnableGZIP: false, - wantReqGZIPEnabled: false, - wantRespGZIPEnabled: false, - }, - { - desc: "Old config set, new config not set", - config: []byte(` - enable_gzip: true - `), - wantEnableGZIP: true, - wantRespGZIPEnabled: true, - wantReqGZIPEnabled: false, - }, - { - desc: "Old config not set, new config set", - config: []byte(` - compression: - response: - enable_gzip: true - request: - enable_gzip: false - `), - wantEnableGZIP: false, - wantRespGZIPEnabled: true, - wantReqGZIPEnabled: false, - }, - { - desc: "Old config set and new config set", - config: []byte(` - enable_gzip: true - compression: - response: - enable_gzip: false - request: - enable_gzip: true - `), - wantEnableGZIP: true, - wantRespGZIPEnabled: false, - wantReqGZIPEnabled: true, - }, - } - - for _, test := range testCases { - v := viper.New() - v.SetConfigType("yaml") - err := v.ReadConfig(bytes.NewBuffer(test.config)) - assert.NoError(t, err) - - migrateConfigCompression(v) - - assert.Equal(t, test.wantEnableGZIP, v.GetBool("enable_gzip"), test.desc) - assert.Equal(t, test.wantReqGZIPEnabled, v.GetBool("compression.request.enable_gzip"), test.desc) - assert.Equal(t, test.wantRespGZIPEnabled, v.GetBool("compression.response.enable_gzip"), test.desc) - } -} - func TestIsConfigInfoPresent(t *testing.T) { configPrefix1Field2Only := []byte(` prefix1: @@ -3073,6 +1373,155 @@ func TestSpecialFeature1VendorExceptionMap(t *testing.T) { } } +func TestSetConfigBidderInfoNillableFields(t *testing.T) { + falseValue := false + trueValue := true + + bidder1ConfigFalses := []byte(` + adapters: + bidder1: + disabled: false + modifyingVastXmlAllowed: false`) + bidder1ConfigTrues := []byte(` + adapters: + bidder1: + disabled: true + modifyingVastXmlAllowed: true`) + bidder1ConfigNils := []byte(` + adapters: + bidder1: + disabled: null + modifyingVastXmlAllowed: null`) + bidder1Bidder2ConfigMixed := []byte(` + adapters: + bidder1: + disabled: true + modifyingVastXmlAllowed: false + bidder2: + disabled: false + modifyingVastXmlAllowed: true`) + + tests := []struct { + name string + rawConfig []byte + bidderInfos BidderInfos + expected nillableFieldBidderInfos + expectError bool + }{ + { + name: "viper and bidder infos are nil", + expected: nil, + }, + { + name: "viper is nil", + bidderInfos: map[string]BidderInfo{}, + expected: nil, + }, + { + name: "bidder infos is nil", + rawConfig: []byte{}, + expected: nil, + }, + { + name: "bidder infos is empty", + bidderInfos: map[string]BidderInfo{}, + expected: nil, + }, + { + name: "one: bidder info has nillable fields as false, viper has as nil", + bidderInfos: map[string]BidderInfo{ + "bidder1": {Disabled: false, ModifyingVastXmlAllowed: false}, + }, + rawConfig: bidder1ConfigNils, + expected: nillableFieldBidderInfos{ + "bidder1": nillableFieldBidderInfo{ + nillableFields: bidderInfoNillableFields{ + Disabled: nil, + ModifyingVastXmlAllowed: nil, + }, + bidderInfo: BidderInfo{Disabled: false, ModifyingVastXmlAllowed: false}, + }, + }, + }, + { + name: "one: bidder info has nillable fields as false, viper has as false", + bidderInfos: map[string]BidderInfo{ + "bidder1": {Disabled: false, ModifyingVastXmlAllowed: false}, + }, + rawConfig: bidder1ConfigFalses, + expected: nillableFieldBidderInfos{ + "bidder1": nillableFieldBidderInfo{ + nillableFields: bidderInfoNillableFields{ + Disabled: &falseValue, + ModifyingVastXmlAllowed: &falseValue, + }, + bidderInfo: BidderInfo{Disabled: false, ModifyingVastXmlAllowed: false}, + }, + }, + }, + { + name: "one: bidder info has nillable fields as false, viper has as true", + bidderInfos: map[string]BidderInfo{ + "bidder1": {Disabled: false, ModifyingVastXmlAllowed: false}, + }, + rawConfig: bidder1ConfigTrues, + expected: nillableFieldBidderInfos{ + "bidder1": nillableFieldBidderInfo{ + nillableFields: bidderInfoNillableFields{ + Disabled: &trueValue, + ModifyingVastXmlAllowed: &trueValue, + }, + bidderInfo: BidderInfo{Disabled: false, ModifyingVastXmlAllowed: false}, + }, + }, + }, + { + name: "many with extra info: bidder infos have nillable fields as false and true, viper has as true and false", + bidderInfos: map[string]BidderInfo{ + "bidder1": {Disabled: false, ModifyingVastXmlAllowed: true, Endpoint: "endpoint a"}, + "bidder2": {Disabled: true, ModifyingVastXmlAllowed: false, Endpoint: "endpoint b"}, + }, + rawConfig: bidder1Bidder2ConfigMixed, + expected: nillableFieldBidderInfos{ + "bidder1": nillableFieldBidderInfo{ + nillableFields: bidderInfoNillableFields{ + Disabled: &trueValue, + ModifyingVastXmlAllowed: &falseValue, + }, + bidderInfo: BidderInfo{Disabled: false, ModifyingVastXmlAllowed: true, Endpoint: "endpoint a"}, + }, + "bidder2": nillableFieldBidderInfo{ + nillableFields: bidderInfoNillableFields{ + Disabled: &falseValue, + ModifyingVastXmlAllowed: &trueValue, + }, + bidderInfo: BidderInfo{Disabled: true, ModifyingVastXmlAllowed: false, Endpoint: "endpoint b"}, + }, + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + v := viper.New() + v.SetConfigType("yaml") + for bidderName := range tt.bidderInfos { + setBidderDefaults(v, strings.ToLower(bidderName)) + } + v.ReadConfig(bytes.NewBuffer(tt.rawConfig)) + + result, err := setConfigBidderInfoNillableFields(v, tt.bidderInfos) + + assert.Equal(t, tt.expected, result) + if tt.expectError { + assert.NotNil(t, err) + } else { + assert.Nil(t, err) + } + }) + } +} + func TestTCF2PurposeEnforced(t *testing.T) { tests := []struct { description string @@ -3242,40 +1691,40 @@ func TestTCF2PurposeVendorExceptions(t *testing.T) { tests := []struct { description string givePurposeConfigNil bool - givePurpose1ExceptionMap map[openrtb_ext.BidderName]struct{} - givePurpose2ExceptionMap map[openrtb_ext.BidderName]struct{} + givePurpose1ExceptionMap map[string]struct{} + givePurpose2ExceptionMap map[string]struct{} givePurpose consentconstants.Purpose - wantExceptionMap map[openrtb_ext.BidderName]struct{} + wantExceptionMap map[string]struct{} }{ { description: "Purpose config is nil", givePurposeConfigNil: true, givePurpose: 1, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + wantExceptionMap: map[string]struct{}{}, }, { description: "Nil - exception map not defined for purpose", givePurpose: 1, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + wantExceptionMap: map[string]struct{}{}, }, { description: "Empty - exception map empty for purpose", givePurpose: 1, - givePurpose1ExceptionMap: map[openrtb_ext.BidderName]struct{}{}, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + givePurpose1ExceptionMap: map[string]struct{}{}, + wantExceptionMap: map[string]struct{}{}, }, { description: "Nonempty - exception map with multiple entries for purpose", givePurpose: 1, - givePurpose1ExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}}, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}}, + givePurpose1ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}}, + wantExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}}, }, { description: "Nonempty - exception map with multiple entries for different purpose", givePurpose: 2, - givePurpose1ExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "index": {}}, - givePurpose2ExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}}, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}}, + givePurpose1ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "index": {}}, + givePurpose2ExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}}, + wantExceptionMap: map[string]struct{}{"rubicon": {}, "appnexus": {}, "openx": {}}, }, } @@ -3349,52 +1798,3 @@ func TestTCF2FeatureOneVendorException(t *testing.T) { assert.Equal(t, tt.wantIsVendorException, value, tt.description) } } - -func TestMigrateConfigEventsEnabled(t *testing.T) { - testCases := []struct { - name string - oldFieldValue *bool - newFieldValue *bool - expectedOldFieldValue *bool - expectedNewFieldValue *bool - }{ - { - name: "Both old and new fields are nil", - oldFieldValue: nil, - newFieldValue: nil, - expectedOldFieldValue: nil, - expectedNewFieldValue: nil, - }, - { - name: "Only old field is set", - oldFieldValue: ptrutil.ToPtr(true), - newFieldValue: nil, - expectedOldFieldValue: ptrutil.ToPtr(true), - expectedNewFieldValue: nil, - }, - { - name: "Only new field is set", - oldFieldValue: nil, - newFieldValue: ptrutil.ToPtr(true), - expectedOldFieldValue: ptrutil.ToPtr(true), - expectedNewFieldValue: nil, - }, - { - name: "Both old and new fields are set, override old field with new field value", - oldFieldValue: ptrutil.ToPtr(false), - newFieldValue: ptrutil.ToPtr(true), - expectedOldFieldValue: ptrutil.ToPtr(true), - expectedNewFieldValue: nil, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - updatedOldFieldValue, updatedNewFieldValue := migrateConfigEventsEnabled(tc.oldFieldValue, tc.newFieldValue) - - assert.Equal(t, tc.expectedOldFieldValue, updatedOldFieldValue) - assert.Nil(t, updatedNewFieldValue) - assert.Nil(t, tc.expectedNewFieldValue) - }) - } -} diff --git a/config/events.go b/config/events.go index 83d2df4b58d..cf3139d83ca 100644 --- a/config/events.go +++ b/config/events.go @@ -61,14 +61,14 @@ type VASTEvent struct { // within the VAST XML // Don't enable this feature. It is still under developmment. Please follow https://github.com/prebid/prebid-server/issues/1725 for more updates type Events struct { - Enabled *bool `mapstructure:"enabled" json:"enabled"` + Enabled bool `mapstructure:"enabled" json:"enabled"` DefaultURL string `mapstructure:"default_url" json:"default_url"` VASTEvents []VASTEvent `mapstructure:"vast_events" json:"vast_events,omitempty"` } // validate verifies the events object and returns error if at least one is invalid. func (e Events) validate(errs []error) []error { - if e.IsEnabled() { + if e.Enabled { if !isValidURL(e.DefaultURL) { return append(errs, errors.New("Invalid events.default_url")) } @@ -147,8 +147,3 @@ func isValidURL(eventURL string) bool { func (e VASTEvent) isTrackingEvent() bool { return e.CreateElement == TrackingVASTElement } - -// IsEnabled function returns the value of events.enabled field -func (e Events) IsEnabled() bool { - return e.Enabled != nil && *e.Enabled -} diff --git a/config/events_test.go b/config/events_test.go index 4baa9066dec..b4c196e9fa9 100644 --- a/config/events_test.go +++ b/config/events_test.go @@ -3,7 +3,6 @@ package config import ( "testing" - "github.com/prebid/prebid-server/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -262,29 +261,22 @@ func TestValidate(t *testing.T) { { description: "Empty default URL", events: Events{ - Enabled: ptrutil.ToPtr(true), + Enabled: true, }, expectErr: true, }, { description: "Events are disabled. Skips validations", events: Events{ - Enabled: ptrutil.ToPtr(false), + Enabled: false, DefaultURL: "", }, expectErr: false, }, - { - description: "Events are nil. Skip validations", - events: Events{ - Enabled: nil, - }, - expectErr: false, - }, { description: "No VAST Events and default URL present", events: Events{ - Enabled: ptrutil.ToPtr(true), + Enabled: true, DefaultURL: "http://prebid.org", }, expectErr: false, @@ -292,7 +284,7 @@ func TestValidate(t *testing.T) { { description: "Invalid VAST Event", events: Events{ - Enabled: ptrutil.ToPtr(true), + Enabled: true, DefaultURL: "http://prebid.org", VASTEvents: []VASTEvent{ {}, @@ -335,34 +327,3 @@ func TestValidateVASTEvents(t *testing.T) { assert.Equal(t, !test.expectErr, err == nil, test.description) } } - -func TestIsEnabled(t *testing.T) { - testCases := []struct { - name string - events Events - expected bool - }{ - { - name: "nil pointer", - events: Events{}, - expected: false, - }, - { - name: "event false", - events: Events{Enabled: ptrutil.ToPtr(false)}, - expected: false, - }, - { - name: "event true", - events: Events{Enabled: ptrutil.ToPtr(true)}, - expected: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - actual := tc.events.IsEnabled() - assert.Equal(t, tc.expected, actual) - }) - } -} diff --git a/config/test/bidder-info-valid/stroeerCore.yaml b/config/test/bidder-info-valid/stroeerCore.yaml index 0a1b3059cd4..ef95d2388a5 100644 --- a/config/test/bidder-info-valid/stroeerCore.yaml +++ b/config/test/bidder-info-valid/stroeerCore.yaml @@ -11,6 +11,10 @@ capabilities: - banner - video - native + dooh: + mediaTypes: + - banner + - video userSync: key: "foo" default: "iframe" diff --git a/currency/currency.go b/currency/currency.go new file mode 100644 index 00000000000..060649f9c11 --- /dev/null +++ b/currency/currency.go @@ -0,0 +1,42 @@ +package currency + +import ( + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +func GetAuctionCurrencyRates(currencyConverter *RateConverter, requestRates *openrtb_ext.ExtRequestCurrency) Conversions { + if currencyConverter == nil && requestRates == nil { + return nil + } + + if requestRates == nil { + // No bidRequest.ext.currency field was found, use PBS rates as usual + return currencyConverter.Rates() + } + + // currencyConverter will never be nil, refer main.serve(), adding this check for future usecases + if currencyConverter == nil { + return NewRates(requestRates.ConversionRates) + } + + // If bidRequest.ext.currency.usepbsrates is nil, we understand its value as true. It will be false + // only if it's explicitly set to false + usePbsRates := requestRates.UsePBSRates == nil || *requestRates.UsePBSRates + + if !usePbsRates { + // At this point, we can safely assume the ConversionRates map is not empty because + // validateCustomRates(bidReqCurrencyRates *openrtb_ext.ExtRequestCurrency) would have + // thrown an error under such conditions. + return NewRates(requestRates.ConversionRates) + } + + // Both PBS and custom rates can be used, check if ConversionRates is not empty + if len(requestRates.ConversionRates) == 0 { + // Custom rates map is empty, use PBS rates only + return currencyConverter.Rates() + } + + // Return an AggregateConversions object that includes both custom and PBS currency rates but will + // prioritize custom rates over PBS rates whenever a currency rate is found in both + return NewAggregateConversions(NewRates(requestRates.ConversionRates), currencyConverter.Rates()) +} diff --git a/currency/currency_mock.go b/currency/currency_mock.go new file mode 100644 index 00000000000..f25974380c7 --- /dev/null +++ b/currency/currency_mock.go @@ -0,0 +1,20 @@ +package currency + +import ( + "io" + "net/http" + "strings" +) + +// MockCurrencyRatesHttpClient is a simple http client mock returning a constant response body +type MockCurrencyRatesHttpClient struct { + ResponseBody string +} + +func (m *MockCurrencyRatesHttpClient) Do(req *http.Request) (*http.Response, error) { + return &http.Response{ + Status: "200 OK", + StatusCode: http.StatusOK, + Body: io.NopCloser(strings.NewReader(m.ResponseBody)), + }, nil +} diff --git a/currency/currency_test.go b/currency/currency_test.go new file mode 100644 index 00000000000..d462cc8bdab --- /dev/null +++ b/currency/currency_test.go @@ -0,0 +1,199 @@ +package currency + +import ( + "testing" + "time" + + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/stretchr/testify/assert" +) + +func TestGetAuctionCurrencyRates(t *testing.T) { + pbsRates := map[string]map[string]float64{ + "MXN": { + "USD": 20.13, + "EUR": 27.82, + "JPY": 5.09, // "MXN" to "JPY" rate not found in customRates + }, + } + + customRates := map[string]map[string]float64{ + "MXN": { + "USD": 25.00, // different rate than in pbsRates + "EUR": 27.82, // same as in pbsRates + "GBP": 31.12, // not found in pbsRates at all + }, + } + + expectedRateEngineRates := map[string]map[string]float64{ + "MXN": { + "USD": 25.00, // rates engine will prioritize the value found in custom rates + "EUR": 27.82, // same value in both the engine reads the custom entry first + "JPY": 5.09, // the engine will find it in the pbsRates conversions + "GBP": 31.12, // the engine will find it in the custom conversions + }, + } + + setupMockRateConverter := func(pbsRates map[string]map[string]float64) *RateConverter { + if pbsRates == nil { + return nil + } + + jsonPbsRates, err := jsonutil.Marshal(pbsRates) + if err != nil { + t.Fatalf("Failed to marshal PBS rates: %v", err) + } + + // Init mock currency conversion service + mockCurrencyClient := &MockCurrencyRatesHttpClient{ + ResponseBody: `{"dataAsOf":"2018-09-12","conversions":` + string(jsonPbsRates) + `}`, + } + + return NewRateConverter( + mockCurrencyClient, + "currency.fake.com", + 24*time.Hour, + ) + } + + type args struct { + currencyConverter *RateConverter + requestRates *openrtb_ext.ExtRequestCurrency + } + tests := []struct { + name string + args args + assertRates map[string]map[string]float64 + }{ + { + name: "valid ConversionRates, valid pbsRates, false UsePBSRates. Resulting rates identical to customRates", + args: args{ + currencyConverter: setupMockRateConverter(pbsRates), + requestRates: &openrtb_ext.ExtRequestCurrency{ + ConversionRates: customRates, + UsePBSRates: ptrutil.ToPtr(false), + }, + }, + assertRates: customRates, + }, + { + name: "valid ConversionRates, valid pbsRates, true UsePBSRates. Resulting rates are a mix but customRates gets priority", + args: args{ + currencyConverter: setupMockRateConverter(pbsRates), + requestRates: &openrtb_ext.ExtRequestCurrency{ + ConversionRates: customRates, + UsePBSRates: ptrutil.ToPtr(true), + }, + }, + assertRates: expectedRateEngineRates, + }, + { + name: "valid ConversionRates, nil pbsRates, false UsePBSRates. Resulting rates identical to customRates", + args: args{ + currencyConverter: nil, + requestRates: &openrtb_ext.ExtRequestCurrency{ + ConversionRates: customRates, + UsePBSRates: ptrutil.ToPtr(false), + }, + }, + assertRates: customRates, + }, + { + name: "valid ConversionRates, nil pbsRates, true UsePBSRates. Resulting rates identical to customRates", + args: args{ + currencyConverter: nil, + requestRates: &openrtb_ext.ExtRequestCurrency{ + ConversionRates: customRates, + UsePBSRates: ptrutil.ToPtr(true), + }, + }, + assertRates: customRates, + }, + { + name: "empty ConversionRates, valid pbsRates, false UsePBSRates. Because pbsRates cannot be used, disable currency conversion", + args: args{ + currencyConverter: setupMockRateConverter(pbsRates), + requestRates: &openrtb_ext.ExtRequestCurrency{ + // ConversionRates inCustomRates not initialized makes for a zero-length map + UsePBSRates: ptrutil.ToPtr(false), + }, + }, + assertRates: nil, + }, + { + name: "nil ConversionRates, valid pbsRates, true UsePBSRates. Resulting rates will be identical to pbsRates", + args: args{ + currencyConverter: setupMockRateConverter(pbsRates), + requestRates: nil, + }, + assertRates: pbsRates, + }, + { + name: "empty ConversionRates, nil pbsRates, false UsePBSRates. No conversion rates available, disable currency conversion", + args: args{ + currencyConverter: setupMockRateConverter(pbsRates), + requestRates: &openrtb_ext.ExtRequestCurrency{ + // ConversionRates inCustomRates not initialized makes for a zero-length map + UsePBSRates: ptrutil.ToPtr(false), + }, + }, + assertRates: nil, + }, + + { + name: "empty ConversionRates, nil pbsRates, true UsePBSRates. No conversion rates available, disable currency conversion", + args: args{ + currencyConverter: nil, + requestRates: &openrtb_ext.ExtRequestCurrency{ + // ConversionRates inCustomRates not initialized makes for a zero-length map + UsePBSRates: ptrutil.ToPtr(true), + }, + }, + assertRates: nil, + }, + { + name: "nil customRates, nil pbsRates. No conversion rates available, disable currency conversion", + args: args{ + currencyConverter: nil, + requestRates: nil, + }, + assertRates: nil, + }, + { + name: "empty ConversionRates, valid pbsRates, true UsePBSRates. Resulting rates will be identical to pbsRates", + args: args{ + currencyConverter: setupMockRateConverter(pbsRates), + requestRates: &openrtb_ext.ExtRequestCurrency{ + // ConversionRates inCustomRates not initialized makes for a zero-length map + UsePBSRates: ptrutil.ToPtr(true), + }, + }, + assertRates: pbsRates, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.args.currencyConverter != nil { + tt.args.currencyConverter.Run() + } + auctionRates := GetAuctionCurrencyRates(tt.args.currencyConverter, tt.args.requestRates) + if tt.args.currencyConverter == nil && tt.args.requestRates == nil && tt.assertRates == nil { + assert.Nil(t, auctionRates) + } else if tt.assertRates == nil { + rate, err := auctionRates.GetRate("USD", "MXN") + assert.Error(t, err, tt.name) + assert.Equal(t, float64(0), rate, tt.name) + } else { + for fromCurrency, rates := range tt.assertRates { + for toCurrency, expectedRate := range rates { + actualRate, err := auctionRates.GetRate(fromCurrency, toCurrency) + assert.NoError(t, err, tt.name) + assert.Equal(t, expectedRate, actualRate, tt.name) + } + } + } + }) + } +} diff --git a/currency/rate_converter.go b/currency/rate_converter.go index f28807701ae..a9ab9547f83 100644 --- a/currency/rate_converter.go +++ b/currency/rate_converter.go @@ -1,7 +1,6 @@ package currency import ( - "encoding/json" "fmt" "io" "net/http" @@ -9,8 +8,9 @@ import ( "time" "github.com/golang/glog" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/util/timeutil" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/timeutil" ) // RateConverter holds the currencies conversion rates dictionary @@ -66,7 +66,7 @@ func (rc *RateConverter) fetch() (*Rates, error) { } updatedRates := &Rates{} - err = json.Unmarshal(bytesJSON, updatedRates) + err = jsonutil.UnmarshalValid(bytesJSON, updatedRates) if err != nil { return nil, err } diff --git a/currency/rate_converter_test.go b/currency/rate_converter_test.go index 617aa02e96a..96003c7d986 100644 --- a/currency/rate_converter_test.go +++ b/currency/rate_converter_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/util/task" + "github.com/prebid/prebid-server/v2/util/task" "github.com/stretchr/testify/assert" ) diff --git a/currency/rates_test.go b/currency/rates_test.go index 23226dce8fb..254bc282dec 100644 --- a/currency/rates_test.go +++ b/currency/rates_test.go @@ -1,11 +1,12 @@ package currency import ( - "encoding/json" "errors" "testing" "github.com/stretchr/testify/assert" + + "github.com/prebid/prebid-server/v2/util/jsonutil" ) func TestUnMarshallRates(t *testing.T) { @@ -22,7 +23,7 @@ func TestUnMarshallRates(t *testing.T) { ratesJSON: `malformed`, expectedRates: Rates{}, expectsError: true, - expectedError: errors.New("invalid character 'm' looking for beginning of value"), + expectedError: errors.New("expect { or n, but found m"), }, { desc: "Valid JSON field defining valid conversion object. Expect no error", @@ -50,7 +51,7 @@ func TestUnMarshallRates(t *testing.T) { expectedError: nil, }, { - desc: "Valid JSON field defines a conversions map with repeated entries, expect error", + desc: "Valid JSON field defines a conversions map with repeated entries, last one wins", ratesJSON: `{ "conversions":{ "USD":{ @@ -58,25 +59,31 @@ func TestUnMarshallRates(t *testing.T) { "MXN":20.00 }, "USD":{ - "GBP":0.7662523901 - }, + "GBP":0.4815162342 + } } }`, - expectedRates: Rates{}, - expectsError: true, - expectedError: errors.New("invalid character '}' looking for beginning of object key string"), + expectedRates: Rates{ + Conversions: map[string]map[string]float64{ + "USD": { + "GBP": 0.4815162342, + }, + }, + }, + expectsError: false, + expectedError: nil, }, } for _, tc := range testCases { // Execute: updatedRates := Rates{} - err := json.Unmarshal([]byte(tc.ratesJSON), &updatedRates) + err := jsonutil.UnmarshalValid([]byte(tc.ratesJSON), &updatedRates) // Verify: assert.Equal(t, err != nil, tc.expectsError, tc.desc) if tc.expectsError { - assert.Equal(t, err.Error(), tc.expectedError.Error(), tc.desc) + assert.Equal(t, tc.expectedError.Error(), err.Error(), tc.desc) } assert.Equal(t, tc.expectedRates, updatedRates, tc.desc) } diff --git a/currency/validation.go b/currency/validation.go index 7a0e2aa02bd..d6429c357b6 100644 --- a/currency/validation.go +++ b/currency/validation.go @@ -5,8 +5,8 @@ import ( "golang.org/x/text/currency" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // ValidateCustomRates throws a bad input error if any of the 3-digit currency codes found in diff --git a/currency/validation_test.go b/currency/validation_test.go index d49b9824986..65f93a5f9e9 100644 --- a/currency/validation_test.go +++ b/currency/validation_test.go @@ -3,8 +3,8 @@ package currency import ( "testing" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/docs/developers/configuration.md b/docs/developers/configuration.md index 9a22fd1ac7f..39789d49541 100644 --- a/docs/developers/configuration.md +++ b/docs/developers/configuration.md @@ -1,14 +1,160 @@ # Configuration -Configuration is handled by [Viper](https://github.com/spf13/viper), which supports [many ways](https://github.com/spf13/viper#why-viper) of setting config values. +Prebid Server is configured using environment variables, a `pbs.json` file, or a `pbs.yaml` file, in that order of precedence. Configuration files are read from either the application directory or `/etc/config`. -As a general rule, Prebid Server will log its resolved config values on startup and exit immediately if they're not valid. +Upon starting, Prebid Server logs the resolved configuration to standard out with passwords and secrets redacted. If there's an error with the configuration, the application will log the error and exit. -For development, it's easiest to define your config inside a `pbs.yaml` file in the project root. +# Sections +> [!IMPORTANT] +> As we are still developing this guide, please refer to the [configuration structures in code](../../config/config.go) for a complete definition of the options. -## Available options +- [General](#general) +- [Privacy](#privacy) + - [GDPR](#gdpr) -For now, see [the contract classes](../../config/config.go) in the code. -Also note that `Viper` will also read environment variables for config values. Prebid Server will look for the prefix `PBS_` on the environment variables, and map underscores (`_`) -to periods. For example, to set `host_cookie.ttl_days` via an environment variable, set `PBS_HOST_COOKIE_TTL_DAYS` to the desired value. +# General + +### `external_url` +String value that specifies the external url to reach your Prebid Server instance. It's used for event tracking and user sync callbacks, and is shared with bidders in outgoing requests at `req.ext.prebid.server.externalurl`. Defaults to empty. + +
+ Example +

+ + JSON: + ``` + { + "external_url": "https://your-pbs-server.com" + } + ``` + + YAML: + ``` + external_url: https://your-pbs-server.com + ``` + + Environment Variable: + ``` + PBS_EXTERNAL_URL: https://your-pbs-server.com + ``` + +

+
+ +### `host` +String value that specifies the address the server will listen to for connections. If the value is empty, Prebid Server will listen on all available addresses, which is a common configuration. This value is also used for the Prometheus endpoint, if enabled. Defaults to empty. + +
+ Example +

+ + JSON: + ``` + { + "host": "127.0.0.1" + } + ``` + + YAML: + ``` + host: 127.0.0.1 + ``` + + Environment Variable: + ``` + PBS_HOST: 127.0.0.1 + ``` + +

+
+ +### `port` +Integer value that specifies the port the server will listen to for connections. Defaults to `8000`. + +
+ Example +

+ + JSON: + ``` + { + "port": 8000 + } + ``` + + YAML: + ``` + port: 8000 + ``` + + Environment Variable: + ``` + PBS_PORT: 8000 + ``` + +

+
+ +# Privacy + +## GDPR + +### `gdpr.enabled` +Boolean value that determines if GDPR processing for TCF signals is enabled. Defaults to `true`. +
+ Example +

+ + JSON: + ``` + { + "gdpr": { + "enabled": true + } + } + ``` + + YAML: + ``` + gdpr: + enabled: true + ``` + + Environment Variable: + ``` + PBS_GDPR_ENABLED: true + ``` + +

+
+ + +### `gdpr.default_value` (required) +String value that determines whether GDPR is enabled when no regulatory signal is available in the request. A value of `"0"` disables it by default and a value of `"1"` enables it. This is a required configuration value with no default. +
+ Example +

+ + JSON: + ``` + { + "gdpr": { + "default_value": "0" + } + } + ``` + + YAML: + ``` + gdpr: + default_value: "0" + ``` + + Environment Variable: + ``` + PBS_GDPR_DEFAULT_VALUE: 0 + ``` + +

+
diff --git a/docs/developers/features.md b/docs/developers/features.md deleted file mode 100644 index b9bb9053ed5..00000000000 --- a/docs/developers/features.md +++ /dev/null @@ -1,12 +0,0 @@ -# Features - -Prebid Server documentation has been moved to the prebid.org website: - -- [Adding a new bidder](https://docs.prebid.org/prebid-server/developers/add-new-bidder-go.html) -- [Adding a new analytics module](https://docs.prebid.org/prebid-server/developers/pbs-build-an-analytics-adapter.html) -- [Currency](https://docs.prebid.org/prebid-server/features/pbs-currency.html) -- [Prebid Server and GDPR](https://docs.google.com/document/d/1g0zAYc_EfqyilKD8N2qQ47uz0hdahY-t8vfb-vxZL5w/edit#heading=h.8zebax5ncz0t) -- [Prebid and TCF2](https://docs.google.com/document/d/1fBRaodKifv1pYsWY3ia-9K96VHUjd8kKvxZlOsozm8E/edit#heading=h.hlpacpauqwkx) -- [Prebid Server User ID Sync](https://docs.prebid.org/prebid-server/developers/pbs-cookie-sync.html) -- [Cookie Sync](https://docs.prebid.org/prebid-server/developers/pbs-cookie-sync.html) -- [Default Request](https://docs.prebid.org/prebid-server/features/pbs-default-request.html) diff --git a/docs/endpoints.md b/docs/endpoints.md deleted file mode 100644 index 88116144a41..00000000000 --- a/docs/endpoints.md +++ /dev/null @@ -1 +0,0 @@ -Endpoint documentation has been moved to prebid.org: [https://docs.prebid.org/prebid-server/endpoints/pbs-endpoint-overview.html](https://docs.prebid.org/prebid-server/endpoints/pbs-endpoint-overview.html) diff --git a/dsa/validate.go b/dsa/validate.go new file mode 100644 index 00000000000..034dae8b3cf --- /dev/null +++ b/dsa/validate.go @@ -0,0 +1,103 @@ +package dsa + +import ( + "errors" + + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" +) + +// Required values representing whether a DSA object is required +const ( + Required int8 = 2 // bid responses without DSA object will not be accepted + RequiredOnlinePlatform int8 = 3 // bid responses without DSA object will not be accepted, Publisher is Online Platform +) + +// PubRender values representing publisher rendering intentions +const ( + PubRenderCannotRender int8 = 0 // publisher can't render + PubRenderWillRender int8 = 2 // publisher will render +) + +// AdRender values representing buyer/advertiser rendering intentions +const ( + AdRenderWillRender int8 = 1 // buyer/advertiser will render +) + +var ( + ErrDsaMissing = errors.New("DSA object missing when required") + ErrBehalfTooLong = errors.New("DSA behalf exceeds limit of 100 chars") + ErrPaidTooLong = errors.New("DSA paid exceeds limit of 100 chars") + ErrNeitherWillRender = errors.New("DSA publisher and buyer both signal will not render") + ErrBothWillRender = errors.New("DSA publisher and buyer both signal will render") +) + +const ( + behalfMaxLength = 100 + paidMaxLength = 100 +) + +// Validate determines whether a given bid is valid from a DSA perspective. +// A bid is considered valid unless the bid request indicates that a DSA object is required +// in bid responses and the object happens to be missing from the specified bid, or if the bid +// DSA object contents are invalid +func Validate(req *openrtb_ext.RequestWrapper, bid *entities.PbsOrtbBid) error { + reqDSA := getReqDSA(req) + bidDSA := getBidDSA(bid) + + if dsaRequired(reqDSA) && bidDSA == nil { + return ErrDsaMissing + } + if bidDSA == nil { + return nil + } + if len(bidDSA.Behalf) > behalfMaxLength { + return ErrBehalfTooLong + } + if len(bidDSA.Paid) > paidMaxLength { + return ErrPaidTooLong + } + if reqDSA != nil && reqDSA.PubRender != nil && bidDSA.AdRender != nil { + if *reqDSA.PubRender == PubRenderCannotRender && *bidDSA.AdRender != AdRenderWillRender { + return ErrNeitherWillRender + } + if *reqDSA.PubRender == PubRenderWillRender && *bidDSA.AdRender == AdRenderWillRender { + return ErrBothWillRender + } + } + return nil +} + +// dsaRequired examines the bid request to determine if the dsarequired field indicates +// that bid responses include a dsa object +func dsaRequired(dsa *openrtb_ext.ExtRegsDSA) bool { + if dsa == nil || dsa.Required == nil { + return false + } + return *dsa.Required == Required || *dsa.Required == RequiredOnlinePlatform +} + +// getReqDSA retrieves the DSA object from the request +func getReqDSA(req *openrtb_ext.RequestWrapper) *openrtb_ext.ExtRegsDSA { + if req == nil { + return nil + } + regExt, err := req.GetRegExt() + if regExt == nil || err != nil { + return nil + } + return regExt.GetDSA() +} + +// getBidDSA retrieves the DSA object from the bid +func getBidDSA(bid *entities.PbsOrtbBid) *openrtb_ext.ExtBidDSA { + if bid == nil || bid.Bid == nil { + return nil + } + var bidExt openrtb_ext.ExtBid + if err := jsonutil.Unmarshal(bid.Bid.Ext, &bidExt); err != nil { + return nil + } + return bidExt.DSA +} diff --git a/dsa/validate_test.go b/dsa/validate_test.go new file mode 100644 index 00000000000..9dfa32c6efe --- /dev/null +++ b/dsa/validate_test.go @@ -0,0 +1,396 @@ +package dsa + +import ( + "encoding/json" + "strings" + "testing" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/stretchr/testify/assert" +) + +func TestValidate(t *testing.T) { + var ( + validBehalf = strings.Repeat("a", 100) + invalidBehalf = strings.Repeat("a", 101) + validPaid = strings.Repeat("a", 100) + invalidPaid = strings.Repeat("a", 101) + ) + + tests := []struct { + name string + giveRequest *openrtb_ext.RequestWrapper + giveBid *entities.PbsOrtbBid + wantError error + }{ + { + name: "nil", + giveRequest: nil, + giveBid: nil, + wantError: nil, + }, + { + name: "request_nil", + giveRequest: nil, + giveBid: &entities.PbsOrtbBid{ + Bid: &openrtb2.Bid{ + Ext: json.RawMessage(`{"dsa":{"behalf":"` + validBehalf + `","paid":"` + validPaid + `","adrender":1}}`), + }, + }, + wantError: nil, + }, + { + name: "not_required_and_bid_is_nil", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa": {"dsarequired": 0}}`), + }, + }, + }, + giveBid: nil, + wantError: nil, + }, + { + name: "not_required_and_bid_dsa_is_valid", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa": {"dsarequired": 0,"pubrender":0}}`), + }, + }, + }, + giveBid: &entities.PbsOrtbBid{ + Bid: &openrtb2.Bid{ + Ext: json.RawMessage(`{"dsa":{"behalf":"` + validBehalf + `","paid":"` + validPaid + `","adrender":1}}`), + }, + }, + wantError: nil, + }, + { + name: "required_and_bid_is_nil", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa": {"dsarequired": 2}}`), + }, + }, + }, + giveBid: nil, + wantError: ErrDsaMissing, + }, + { + name: "required_and_bid_dsa_has_invalid_behalf", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa": {"dsarequired": 2}}`), + }, + }, + }, + giveBid: &entities.PbsOrtbBid{ + Bid: &openrtb2.Bid{ + Ext: json.RawMessage(`{"dsa":{"behalf":"` + invalidBehalf + `"}}`), + }, + }, + wantError: ErrBehalfTooLong, + }, + { + name: "required_and_bid_dsa_has_invalid_paid", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa": {"dsarequired": 2}}`), + }, + }, + }, + giveBid: &entities.PbsOrtbBid{ + Bid: &openrtb2.Bid{ + Ext: json.RawMessage(`{"dsa":{"paid":"` + invalidPaid + `"}}`), + }, + }, + wantError: ErrPaidTooLong, + }, + { + name: "required_and_neither_will_render", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa": {"dsarequired": 2,"pubrender": 0}}`), + }, + }, + }, + giveBid: &entities.PbsOrtbBid{ + Bid: &openrtb2.Bid{ + Ext: json.RawMessage(`{"dsa":{"adrender": 0}}`), + }, + }, + wantError: ErrNeitherWillRender, + }, + { + name: "required_and_both_will_render", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa": {"dsarequired": 2,"pubrender": 2}}`), + }, + }, + }, + giveBid: &entities.PbsOrtbBid{ + Bid: &openrtb2.Bid{ + Ext: json.RawMessage(`{"dsa":{"adrender": 1}}`), + }, + }, + wantError: ErrBothWillRender, + }, + { + name: "required_and_bid_dsa_is_valid", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa": {"dsarequired": 2,"pubrender": 0}}`), + }, + }, + }, + giveBid: &entities.PbsOrtbBid{ + Bid: &openrtb2.Bid{ + Ext: json.RawMessage(`{"dsa":{"behalf":"` + validBehalf + `","paid":"` + validPaid + `","adrender":1}}`), + }, + }, + wantError: nil, + }, + { + name: "required_and_bid_dsa_is_valid_no_pubrender", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa": {"dsarequired": 2}}`), + }, + }, + }, + giveBid: &entities.PbsOrtbBid{ + Bid: &openrtb2.Bid{ + Ext: json.RawMessage(`{"dsa":{"behalf":"` + validBehalf + `","paid":"` + validPaid + `","adrender":2}}`), + }, + }, + wantError: nil, + }, + { + name: "required_and_bid_dsa_is_valid_no_adrender", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa": {"dsarequired": 2, "pubrender": 0}}`), + }, + }, + }, + giveBid: &entities.PbsOrtbBid{ + Bid: &openrtb2.Bid{ + Ext: json.RawMessage(`{"dsa":{"behalf":"` + validBehalf + `","paid":"` + validPaid + `"}}`), + }, + }, + wantError: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := Validate(tt.giveRequest, tt.giveBid) + if tt.wantError != nil { + assert.Equal(t, err, tt.wantError) + } else { + assert.NoError(t, err) + } + }) + } +} + +func TestDSARequired(t *testing.T) { + tests := []struct { + name string + giveReqDSA *openrtb_ext.ExtRegsDSA + wantRequired bool + }{ + { + name: "nil", + giveReqDSA: nil, + wantRequired: false, + }, + { + name: "nil_required", + giveReqDSA: &openrtb_ext.ExtRegsDSA{ + Required: nil, + }, + wantRequired: false, + }, + { + name: "not_required", + giveReqDSA: &openrtb_ext.ExtRegsDSA{ + Required: ptrutil.ToPtr[int8](0), + }, + wantRequired: false, + }, + { + name: "not_required_supported", + giveReqDSA: &openrtb_ext.ExtRegsDSA{ + Required: ptrutil.ToPtr[int8](1), + }, + wantRequired: false, + }, + { + name: "required", + giveReqDSA: &openrtb_ext.ExtRegsDSA{ + Required: ptrutil.ToPtr[int8](2), + }, + wantRequired: true, + }, + { + name: "required_online_platform", + giveReqDSA: &openrtb_ext.ExtRegsDSA{ + Required: ptrutil.ToPtr[int8](3), + }, + wantRequired: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + required := dsaRequired(tt.giveReqDSA) + assert.Equal(t, tt.wantRequired, required) + }) + } +} + +func TestGetReqDSA(t *testing.T) { + tests := []struct { + name string + giveRequest *openrtb_ext.RequestWrapper + expectedDSA *openrtb_ext.ExtRegsDSA + }{ + { + name: "req_is_nil", + giveRequest: nil, + expectedDSA: nil, + }, + { + name: "bidrequest_is_nil", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: nil, + }, + expectedDSA: nil, + }, + { + name: "req.regs_is_nil", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: nil, + }, + }, + expectedDSA: nil, + }, + { + name: "req.regs.ext_is_nil", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: nil, + }, + }, + }, + expectedDSA: nil, + }, + { + name: "req.regs.ext_is_empty", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{}`), + }, + }, + }, + expectedDSA: nil, + }, + { + name: "req.regs.ext_dsa_is_populated", + giveRequest: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa": {"dsarequired": 2}}`), + }, + }, + }, + expectedDSA: &openrtb_ext.ExtRegsDSA{ + Required: ptrutil.ToPtr[int8](2), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dsa := getReqDSA(tt.giveRequest) + assert.Equal(t, tt.expectedDSA, dsa) + }) + } +} + +func TestGetBidDSA(t *testing.T) { + tests := []struct { + name string + bid *entities.PbsOrtbBid + expectedDSA *openrtb_ext.ExtBidDSA + }{ + { + name: "bid_is_nil", + bid: nil, + expectedDSA: nil, + }, + { + name: "bid.bid_is_nil", + bid: &entities.PbsOrtbBid{ + Bid: nil, + }, + expectedDSA: nil, + }, + { + name: "bid.bid.ext_is_nil", + bid: &entities.PbsOrtbBid{ + Bid: &openrtb2.Bid{ + Ext: nil, + }, + }, + expectedDSA: nil, + }, + { + name: "bid.bid.ext_is_empty", + bid: &entities.PbsOrtbBid{ + Bid: &openrtb2.Bid{ + Ext: json.RawMessage(`{}`), + }, + }, + expectedDSA: nil, + }, + { + name: "bid.bid.ext.dsa_is_populated", + bid: &entities.PbsOrtbBid{ + Bid: &openrtb2.Bid{ + Ext: json.RawMessage(`{"dsa": {"behalf":"test1","paid":"test2","adrender":1}}`), + }, + }, + expectedDSA: &openrtb_ext.ExtBidDSA{ + Behalf: "test1", + Paid: "test2", + AdRender: ptrutil.ToPtr[int8](1), + }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + dsa := getBidDSA(tt.bid) + assert.Equal(t, tt.expectedDSA, dsa) + }) + } +} diff --git a/endpoints/cookie_sync.go b/endpoints/cookie_sync.go index 7014dde94fb..62697b0d344 100644 --- a/endpoints/cookie_sync.go +++ b/endpoints/cookie_sync.go @@ -9,27 +9,32 @@ import ( "net/http" "strconv" "strings" + "time" "github.com/golang/glog" "github.com/julienschmidt/httprouter" gpplib "github.com/prebid/go-gpp" gppConstants "github.com/prebid/go-gpp/constants" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - gppPrivacy "github.com/prebid/prebid-server/privacy/gpp" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/usersync" - stringutil "github.com/prebid/prebid-server/util/stringutil" + accountService "github.com/prebid/prebid-server/v2/account" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/privacy/ccpa" + gppPrivacy "github.com/prebid/prebid-server/v2/privacy/gpp" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/jsonutil" + stringutil "github.com/prebid/prebid-server/v2/util/stringutil" + "github.com/prebid/prebid-server/v2/util/timeutil" ) +const receiveCookieDeprecation = "receive-cookie-deprecation" + var ( errCookieSyncOptOut = errors.New("User has opted out") errCookieSyncBody = errors.New("Failed to read request body") @@ -51,7 +56,7 @@ func NewCookieSyncEndpoint( gdprPermsBuilder gdpr.PermissionsBuilder, tcf2CfgBuilder gdpr.TCF2ConfigBuilder, metrics metrics.MetricsEngine, - pbsAnalytics analytics.PBSAnalyticsModule, + analyticsRunner analytics.Runner, accountsFetcher stored_requests.AccountFetcher, bidders map[string]openrtb_ext.BidderName) HTTPRouterHandler { @@ -61,7 +66,7 @@ func NewCookieSyncEndpoint( } return &cookieSyncEndpoint{ - chooser: usersync.NewChooser(syncersByBidder), + chooser: usersync.NewChooser(syncersByBidder, bidderHashSet, config.BidderInfos), config: config, privacyConfig: usersyncPrivacyConfig{ gdprConfig: config.GDPR, @@ -71,8 +76,9 @@ func NewCookieSyncEndpoint( bidderHashSet: bidderHashSet, }, metrics: metrics, - pbsAnalytics: pbsAnalytics, + pbsAnalytics: analyticsRunner, accountsFetcher: accountsFetcher, + time: &timeutil.RealTime{}, } } @@ -81,12 +87,14 @@ type cookieSyncEndpoint struct { config *config.Configuration privacyConfig usersyncPrivacyConfig metrics metrics.MetricsEngine - pbsAnalytics analytics.PBSAnalyticsModule + pbsAnalytics analytics.Runner accountsFetcher stored_requests.AccountFetcher + time timeutil.Time } func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - request, privacyMacros, err := c.parseRequest(r) + request, privacyMacros, account, err := c.parseRequest(r) + c.setCookieDeprecationHeader(w, r, account) if err != nil { c.writeParseRequestErrorMetrics(err) c.handleError(w, err, http.StatusBadRequest) @@ -98,30 +106,31 @@ func (c *cookieSyncEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ ht usersync.SyncHostCookie(r, cookie, &c.config.HostCookie) result := c.chooser.Choose(request, cookie) + switch result.Status { case usersync.StatusBlockedByUserOptOut: c.metrics.RecordCookieSync(metrics.CookieSyncOptOut) c.handleError(w, errCookieSyncOptOut, http.StatusUnauthorized) - case usersync.StatusBlockedByGDPR: + case usersync.StatusBlockedByPrivacy: c.metrics.RecordCookieSync(metrics.CookieSyncGDPRHostCookieBlocked) - c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, nil) + c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, nil, result.BiddersEvaluated, request.Debug) case usersync.StatusOK: c.metrics.RecordCookieSync(metrics.CookieSyncOK) c.writeSyncerMetrics(result.BiddersEvaluated) - c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, result.SyncersChosen) + c.handleResponse(w, request.SyncTypeFilter, cookie, privacyMacros, result.SyncersChosen, result.BiddersEvaluated, request.Debug) } } -func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, macros.UserSyncPrivacy, error) { +func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, macros.UserSyncPrivacy, *config.Account, error) { defer r.Body.Close() body, err := io.ReadAll(r.Body) if err != nil { - return usersync.Request{}, macros.UserSyncPrivacy{}, errCookieSyncBody + return usersync.Request{}, macros.UserSyncPrivacy{}, nil, errCookieSyncBody } request := cookieSyncRequest{} - if err := json.Unmarshal(body, &request); err != nil { - return usersync.Request{}, macros.UserSyncPrivacy{}, fmt.Errorf("JSON parsing failed: %s", err.Error()) + if err := jsonutil.UnmarshalValid(body, &request); err != nil { + return usersync.Request{}, macros.UserSyncPrivacy{}, nil, fmt.Errorf("JSON parsing failed: %s", err.Error()) } if request.Account == "" { @@ -129,7 +138,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma } account, fetchErrs := accountService.GetAccount(context.Background(), c.config, c.accountsFetcher, request.Account, c.metrics) if len(fetchErrs) > 0 { - return usersync.Request{}, macros.UserSyncPrivacy{}, combineErrors(fetchErrs) + return usersync.Request{}, macros.UserSyncPrivacy{}, nil, combineErrors(fetchErrs) } request = c.setLimit(request, account.CookieSync) @@ -137,12 +146,12 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma privacyMacros, gdprSignal, privacyPolicies, err := extractPrivacyPolicies(request, c.privacyConfig.gdprConfig.DefaultValue) if err != nil { - return usersync.Request{}, macros.UserSyncPrivacy{}, err + return usersync.Request{}, macros.UserSyncPrivacy{}, account, err } //OpenWrap: do not allow publishers to bypass GDPR if c.privacyConfig.gdprConfig.DefaultValue == "1" && gdprSignal == gdpr.SignalNo { - return usersync.Request{}, macros.UserSyncPrivacy{}, errCookieSyncGDPRMandatoryByHost + return usersync.Request{}, macros.UserSyncPrivacy{}, account, errCookieSyncGDPRMandatoryByHost } ccpaParsedPolicy := ccpa.ParsedPolicy{} @@ -160,7 +169,7 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma syncTypeFilter, err := parseTypeFilter(request.FilterSettings) if err != nil { - return usersync.Request{}, macros.UserSyncPrivacy{}, err + return usersync.Request{}, macros.UserSyncPrivacy{}, account, err } gdprRequestInfo := gdpr.RequestInfo{ @@ -177,16 +186,19 @@ func (c *cookieSyncEndpoint) parseRequest(r *http.Request) (usersync.Request, ma Enabled: (request.CooperativeSync != nil && *request.CooperativeSync) || (request.CooperativeSync == nil && c.config.UserSync.Cooperative.EnabledByDefault), PriorityGroups: c.config.UserSync.PriorityGroups, }, + Debug: request.Debug, Limit: request.Limit, Privacy: usersyncPrivacy{ gdprPermissions: gdprPerms, ccpaParsedPolicy: ccpaParsedPolicy, activityControl: activityControl, activityRequest: privacy.NewRequestFromPolicies(privacyPolicies), + gdprSignal: gdprSignal, }, SyncTypeFilter: syncTypeFilter, + GPPSID: request.GPPSID, } - return rx, privacyMacros, nil + return rx, privacyMacros, account, nil } func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue string) (macros.UserSyncPrivacy, gdpr.Signal, privacy.Policies, error) { @@ -203,10 +215,10 @@ func extractPrivacyPolicies(request cookieSyncRequest, usersyncDefaultGDPRValue var gpp gpplib.GppContainer if len(request.GPP) > 0 { - var err error - gpp, err = gpplib.Parse(request.GPP) - if err != nil { - return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, err + var errs []error + gpp, errs = gpplib.Parse(request.GPP) + if len(errs) > 0 { + return macros.UserSyncPrivacy{}, gdpr.SignalNo, privacy.Policies{}, errs[0] } } @@ -373,7 +385,7 @@ func combineErrors(errs []error) error { for _, err := range errs { // preserve knowledge of special account errors switch errortypes.ReadCode(err) { - case errortypes.BlacklistedAcctErrorCode: + case errortypes.AccountDisabledErrorCode: return errCookieSyncAccountBlocked case errortypes.AcctRequiredErrorCode: return errCookieSyncAccountInvalid @@ -392,9 +404,7 @@ func (c *cookieSyncEndpoint) writeSyncerMetrics(biddersEvaluated []usersync.Bidd switch bidder.Status { case usersync.StatusOK: c.metrics.RecordSyncerRequest(bidder.SyncerKey, metrics.SyncerCookieSyncOK) - case usersync.StatusBlockedByGDPR: - c.metrics.RecordSyncerRequest(bidder.SyncerKey, metrics.SyncerCookieSyncPrivacyBlocked) - case usersync.StatusBlockedByCCPA: + case usersync.StatusBlockedByPrivacy: c.metrics.RecordSyncerRequest(bidder.SyncerKey, metrics.SyncerCookieSyncPrivacyBlocked) case usersync.StatusAlreadySynced: c.metrics.RecordSyncerRequest(bidder.SyncerKey, metrics.SyncerCookieSyncAlreadySynced) @@ -404,7 +414,7 @@ func (c *cookieSyncEndpoint) writeSyncerMetrics(biddersEvaluated []usersync.Bidd } } -func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, m macros.UserSyncPrivacy, s []usersync.SyncerChoice) { +func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.SyncTypeFilter, co *usersync.Cookie, m macros.UserSyncPrivacy, s []usersync.SyncerChoice, biddersEvaluated []usersync.BidderEvaluation, debug bool) { status := "no_cookie" if co.HasAnyLiveSyncs() { status = "ok" @@ -440,17 +450,62 @@ func (c *cookieSyncEndpoint) handleResponse(w http.ResponseWriter, tf usersync.S }) } + if debug { + biddersSeen := make(map[string]struct{}) + var debugInfo []cookieSyncResponseDebug + for _, bidderEval := range biddersEvaluated { + var debugResponse cookieSyncResponseDebug + debugResponse.Bidder = bidderEval.Bidder + if bidderEval.Status == usersync.StatusDuplicate && biddersSeen[bidderEval.Bidder] == struct{}{} { + debugResponse.Error = getDebugMessage(bidderEval.Status) + " synced as " + bidderEval.SyncerKey + debugInfo = append(debugInfo, debugResponse) + } else if bidderEval.Status != usersync.StatusOK { + debugResponse.Error = getDebugMessage(bidderEval.Status) + debugInfo = append(debugInfo, debugResponse) + } + biddersSeen[bidderEval.Bidder] = struct{}{} + } + response.Debug = debugInfo + } + c.pbsAnalytics.LogCookieSyncObject(&analytics.CookieSyncObject{ Status: http.StatusOK, BidderStatus: mapBidderStatusToAnalytics(response.BidderStatus), }) w.Header().Set("Content-Type", "application/json; charset=utf-8") + enc := json.NewEncoder(w) enc.SetEscapeHTML(false) enc.Encode(response) } +func (c *cookieSyncEndpoint) setCookieDeprecationHeader(w http.ResponseWriter, r *http.Request, account *config.Account) { + if rcd, err := r.Cookie(receiveCookieDeprecation); err == nil && rcd != nil { + return + } + if account == nil || !account.Privacy.PrivacySandbox.CookieDeprecation.Enabled { + return + } + cookie := &http.Cookie{ + Name: receiveCookieDeprecation, + Value: "1", + Secure: true, + HttpOnly: true, + Path: "/", + SameSite: http.SameSiteNoneMode, + Expires: c.time.Now().Add(time.Second * time.Duration(account.Privacy.PrivacySandbox.CookieDeprecation.TTLSec)), + } + setCookiePartitioned(w, cookie) +} + +// setCookiePartitioned temporary substitute for http.SetCookie(w, cookie) until it supports Partitioned cookie type. Refer https://github.com/golang/go/issues/62490 +func setCookiePartitioned(w http.ResponseWriter, cookie *http.Cookie) { + if v := cookie.String(); v != "" { + w.Header().Add("Set-Cookie", v+"; Partitioned;") + } +} + func mapBidderStatusToAnalytics(from []cookieSyncResponseBidder) []*analytics.CookieSyncBidder { to := make([]*analytics.CookieSyncBidder, len(from)) for i, b := range from { @@ -467,6 +522,28 @@ func mapBidderStatusToAnalytics(from []cookieSyncResponseBidder) []*analytics.Co return to } +func getDebugMessage(status usersync.Status) string { + switch status { + case usersync.StatusAlreadySynced: + return "Already in sync" + case usersync.StatusBlockedByPrivacy: + return "Rejected by privacy" + case usersync.StatusBlockedByUserOptOut: + return "Status blocked by user opt out" + case usersync.StatusDuplicate: + return "Duplicate bidder" + case usersync.StatusUnknownBidder: + return "Unsupported bidder" + case usersync.StatusUnconfiguredBidder: + return "No sync config" + case usersync.StatusTypeNotSupported: + return "Type not supported" + case usersync.StatusBlockedByDisabledUsersync: + return "Sync disabled by config" + } + return "" +} + type CookieSyncReq cookieSyncRequest type CookieSyncResp cookieSyncResponse @@ -481,6 +558,7 @@ type cookieSyncRequest struct { CooperativeSync *bool `json:"coopSync"` FilterSettings *cookieSyncRequestFilterSettings `json:"filterSettings"` Account string `json:"account"` + Debug bool `json:"debug"` } type cookieSyncRequestFilterSettings struct { @@ -496,6 +574,7 @@ type cookieSyncRequestFilter struct { type cookieSyncResponse struct { Status string `json:"status"` BidderStatus []cookieSyncResponseBidder `json:"bidder_status"` + Debug []cookieSyncResponseDebug `json:"debug,omitempty"` } type cookieSyncResponseBidder struct { @@ -510,6 +589,11 @@ type cookieSyncResponseSync struct { SupportCORS bool `json:"supportCORS,omitempty"` } +type cookieSyncResponseDebug struct { + Bidder string `json:"bidder"` + Error string `json:"error,omitempty"` +} + type usersyncPrivacyConfig struct { gdprConfig config.GDPR gdprPermissionsBuilder gdpr.PermissionsBuilder @@ -523,6 +607,7 @@ type usersyncPrivacy struct { ccpaParsedPolicy ccpa.ParsedPolicy activityControl privacy.ActivityControl activityRequest privacy.ActivityRequest + gdprSignal gdpr.Signal } func (p usersyncPrivacy) GDPRAllowsHostCookie() bool { @@ -546,3 +631,7 @@ func (p usersyncPrivacy) ActivityAllowsUserSync(bidder string) bool { privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidder}, p.activityRequest) } + +func (p usersyncPrivacy) GDPRInScope() bool { + return p.gdprSignal == gdpr.SignalYes +} diff --git a/endpoints/cookie_sync_test.go b/endpoints/cookie_sync_test.go index 5026bb2e793..e37db2b7cb8 100644 --- a/endpoints/cookie_sync_test.go +++ b/endpoints/cookie_sync_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "io" "net/http" "net/http/httptest" @@ -11,23 +12,32 @@ import ( "strings" "testing" "testing/iotest" - - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/ptrutil" - + "time" + + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/privacy/ccpa" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) +// fakeTime implements the Time interface +type fakeTime struct { + time time.Time +} + +func (ft *fakeTime) Now() time.Time { + return ft.time +} + func TestNewCookieSyncEndpoint(t *testing.T) { var ( syncersByBidder = map[string]usersync.Syncer{"a": &MockSyncer{}} @@ -42,18 +52,21 @@ func TestNewCookieSyncEndpoint(t *testing.T) { configGDPR = config.GDPR{HostVendorID: 42} configCCPAEnforce = true metrics = metrics.MetricsEngineMock{} - analytics = MockAnalytics{} + analytics = MockAnalyticsRunner{} fetcher = FakeAccountsFetcher{} bidders = map[string]openrtb_ext.BidderName{"bidderA": openrtb_ext.BidderName("bidderA"), "bidderB": openrtb_ext.BidderName("bidderB")} + bidderInfo = map[string]config.BidderInfo{"bidderA": {}, "bidderB": {}} + biddersKnown = map[string]struct{}{"bidderA": {}, "bidderB": {}} ) endpoint := NewCookieSyncEndpoint( syncersByBidder, &config.Configuration{ - UserSync: configUserSync, - HostCookie: configHostCookie, - GDPR: configGDPR, - CCPA: config.CCPA{Enforce: configCCPAEnforce}, + UserSync: configUserSync, + HostCookie: configHostCookie, + GDPR: configGDPR, + CCPA: config.CCPA{Enforce: configCCPAEnforce}, + BidderInfos: bidderInfo, }, gdprPermsBuilder, tcf2ConfigBuilder, @@ -65,12 +78,13 @@ func TestNewCookieSyncEndpoint(t *testing.T) { result := endpoint.(*cookieSyncEndpoint) expected := &cookieSyncEndpoint{ - chooser: usersync.NewChooser(syncersByBidder), + chooser: usersync.NewChooser(syncersByBidder, biddersKnown, bidderInfo), config: &config.Configuration{ - UserSync: configUserSync, - HostCookie: configHostCookie, - GDPR: configGDPR, - CCPA: config.CCPA{Enforce: configCCPAEnforce}, + UserSync: configUserSync, + HostCookie: configHostCookie, + GDPR: configGDPR, + CCPA: config.CCPA{Enforce: configCCPAEnforce}, + BidderInfos: bidderInfo, }, privacyConfig: usersyncPrivacyConfig{ gdprConfig: configGDPR, @@ -87,7 +101,7 @@ func TestNewCookieSyncEndpoint(t *testing.T) { assert.IsType(t, &cookieSyncEndpoint{}, endpoint) assert.Equal(t, expected.config, result.config) - assert.Equal(t, expected.chooser, result.chooser) + assert.ObjectsAreEqualValues(expected.chooser, result.chooser) assert.Equal(t, expected.metrics, result.metrics) assert.Equal(t, expected.pbsAnalytics, result.pbsAnalytics) assert.Equal(t, expected.accountsFetcher, result.accountsFetcher) @@ -107,14 +121,16 @@ func TestCookieSyncHandle(t *testing.T) { cookieWithSyncs.Sync("foo", "anyID") testCases := []struct { - description string - givenCookie *usersync.Cookie - givenBody io.Reader - givenChooserResult usersync.Result - expectedStatusCode int - expectedBody string - setMetricsExpectations func(*metrics.MetricsEngineMock) - setAnalyticsExpectations func(*MockAnalytics) + description string + givenCookie *usersync.Cookie + givenBody io.Reader + givenChooserResult usersync.Result + givenAccountData map[string]json.RawMessage + expectedStatusCode int + expectedBody string + setMetricsExpectations func(*metrics.MetricsEngineMock) + setAnalyticsExpectations func(*MockAnalyticsRunner) + expectedCookieDeprecationHeader bool }{ { description: "Request With Cookie", @@ -133,7 +149,7 @@ func TestCookieSyncHandle(t *testing.T) { m.On("RecordCookieSync", metrics.CookieSyncOK).Once() m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncOK).Once() }, - setAnalyticsExpectations: func(a *MockAnalytics) { + setAnalyticsExpectations: func(a *MockAnalyticsRunner) { expected := analytics.CookieSyncObject{ Status: 200, Errors: nil, @@ -165,7 +181,7 @@ func TestCookieSyncHandle(t *testing.T) { m.On("RecordCookieSync", metrics.CookieSyncOK).Once() m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncOK).Once() }, - setAnalyticsExpectations: func(a *MockAnalytics) { + setAnalyticsExpectations: func(a *MockAnalyticsRunner) { expected := analytics.CookieSyncObject{ Status: 200, Errors: nil, @@ -190,14 +206,14 @@ func TestCookieSyncHandle(t *testing.T) { SyncersChosen: []usersync.SyncerChoice{{Bidder: "a", Syncer: &syncer}}, }, expectedStatusCode: 400, - expectedBody: `JSON parsing failed: invalid character 'm' looking for beginning of value` + "\n", + expectedBody: `JSON parsing failed: expect { or n, but found m` + "\n", setMetricsExpectations: func(m *metrics.MetricsEngineMock) { m.On("RecordCookieSync", metrics.CookieSyncBadRequest).Once() }, - setAnalyticsExpectations: func(a *MockAnalytics) { + setAnalyticsExpectations: func(a *MockAnalyticsRunner) { expected := analytics.CookieSyncObject{ Status: 400, - Errors: []error{errors.New("JSON parsing failed: invalid character 'm' looking for beginning of value")}, + Errors: []error{errors.New("JSON parsing failed: expect { or n, but found m")}, BidderStatus: []*analytics.CookieSyncBidder{}, } a.On("LogCookieSyncObject", &expected).Once() @@ -217,7 +233,7 @@ func TestCookieSyncHandle(t *testing.T) { setMetricsExpectations: func(m *metrics.MetricsEngineMock) { m.On("RecordCookieSync", metrics.CookieSyncOptOut).Once() }, - setAnalyticsExpectations: func(a *MockAnalytics) { + setAnalyticsExpectations: func(a *MockAnalyticsRunner) { expected := analytics.CookieSyncObject{ Status: 401, Errors: []error{errors.New("User has opted out")}, @@ -231,7 +247,7 @@ func TestCookieSyncHandle(t *testing.T) { givenCookie: cookieWithSyncs, givenBody: strings.NewReader(`{}`), givenChooserResult: usersync.Result{ - Status: usersync.StatusBlockedByGDPR, + Status: usersync.StatusBlockedByPrivacy, BiddersEvaluated: []usersync.BidderEvaluation{{Bidder: "a", SyncerKey: "aSyncer", Status: usersync.StatusOK}}, SyncersChosen: []usersync.SyncerChoice{{Bidder: "a", Syncer: &syncer}}, }, @@ -240,7 +256,7 @@ func TestCookieSyncHandle(t *testing.T) { setMetricsExpectations: func(m *metrics.MetricsEngineMock) { m.On("RecordCookieSync", metrics.CookieSyncGDPRHostCookieBlocked).Once() }, - setAnalyticsExpectations: func(a *MockAnalytics) { + setAnalyticsExpectations: func(a *MockAnalyticsRunner) { expected := analytics.CookieSyncObject{ Status: 200, Errors: nil, @@ -249,16 +265,86 @@ func TestCookieSyncHandle(t *testing.T) { a.On("LogCookieSyncObject", &expected).Once() }, }, + { + description: "Debug Check", + givenCookie: cookieWithSyncs, + givenBody: strings.NewReader(`{"debug": true}`), + givenChooserResult: usersync.Result{ + Status: usersync.StatusOK, + BiddersEvaluated: []usersync.BidderEvaluation{{Bidder: "a", SyncerKey: "aSyncer", Status: usersync.StatusAlreadySynced}}, + SyncersChosen: []usersync.SyncerChoice{{Bidder: "a", Syncer: &syncer}}, + }, + expectedStatusCode: 200, + expectedBody: `{"status":"ok","bidder_status":[` + + `{"bidder":"a","no_cookie":true,"usersync":{"url":"aURL","type":"redirect","supportCORS":true}}` + + `],"debug":[{"bidder":"a","error":"Already in sync"}]}` + "\n", + setMetricsExpectations: func(m *metrics.MetricsEngineMock) { + m.On("RecordCookieSync", metrics.CookieSyncOK).Once() + m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncAlreadySynced).Once() + }, + setAnalyticsExpectations: func(a *MockAnalyticsRunner) { + expected := analytics.CookieSyncObject{ + Status: 200, + Errors: nil, + BidderStatus: []*analytics.CookieSyncBidder{ + { + BidderCode: "a", + NoCookie: true, + UsersyncInfo: &analytics.UsersyncInfo{URL: "aURL", Type: "redirect", SupportCORS: true}, + }, + }, + } + a.On("LogCookieSyncObject", &expected).Once() + }, + }, + { + description: "CookieDeprecation-Set", + givenCookie: cookieWithSyncs, + givenBody: strings.NewReader(`{"account": "testAccount"}`), + givenChooserResult: usersync.Result{ + Status: usersync.StatusOK, + BiddersEvaluated: []usersync.BidderEvaluation{{Bidder: "a", SyncerKey: "aSyncer", Status: usersync.StatusAlreadySynced}}, + SyncersChosen: []usersync.SyncerChoice{{Bidder: "a", Syncer: &syncer}}, + }, + givenAccountData: map[string]json.RawMessage{ + "testAccount": json.RawMessage(`{"id":"1","privacy":{"privacysandbox":{"cookiedeprecation":{"enabled":true,"ttlsec":86400}}}}`), + }, + expectedStatusCode: 200, + expectedCookieDeprecationHeader: true, + expectedBody: `{"status":"ok","bidder_status":[` + + `{"bidder":"a","no_cookie":true,"usersync":{"url":"aURL","type":"redirect","supportCORS":true}}` + + `]}` + "\n", + setMetricsExpectations: func(m *metrics.MetricsEngineMock) { + m.On("RecordCookieSync", metrics.CookieSyncOK).Once() + m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncAlreadySynced).Once() + }, + setAnalyticsExpectations: func(a *MockAnalyticsRunner) { + expected := analytics.CookieSyncObject{ + Status: 200, + Errors: nil, + BidderStatus: []*analytics.CookieSyncBidder{ + { + BidderCode: "a", + NoCookie: true, + UsersyncInfo: &analytics.UsersyncInfo{URL: "aURL", Type: "redirect", SupportCORS: true}, + }, + }, + } + a.On("LogCookieSyncObject", &expected).Once() + }, + }, } for _, test := range testCases { mockMetrics := metrics.MetricsEngineMock{} test.setMetricsExpectations(&mockMetrics) - mockAnalytics := MockAnalytics{} + mockAnalytics := MockAnalyticsRunner{} test.setAnalyticsExpectations(&mockAnalytics) - fakeAccountFetcher := FakeAccountsFetcher{} + fakeAccountFetcher := FakeAccountsFetcher{ + AccountData: test.givenAccountData, + } gdprPermsBuilder := fakePermissionsBuilder{ permissions: &fakePermissions{}, @@ -293,6 +379,7 @@ func TestCookieSyncHandle(t *testing.T) { metrics: &mockMetrics, pbsAnalytics: &mockAnalytics, accountsFetcher: &fakeAccountFetcher, + time: &fakeTime{time: time.Date(2024, 2, 22, 9, 42, 4, 13, time.UTC)}, } assert.NoError(t, endpoint.config.MarshalAccountDefaults()) @@ -300,6 +387,16 @@ func TestCookieSyncHandle(t *testing.T) { assert.Equal(t, test.expectedStatusCode, writer.Code, test.description+":status_code") assert.Equal(t, test.expectedBody, writer.Body.String(), test.description+":body") + + gotCookie := writer.Header().Get("Set-Cookie") + if test.expectedCookieDeprecationHeader { + wantCookieTTL := endpoint.time.Now().Add(time.Second * time.Duration(86400)).UTC().Format(http.TimeFormat) + wantCookie := fmt.Sprintf("receive-cookie-deprecation=1; Path=/; Expires=%v; HttpOnly; Secure; SameSite=None; Partitioned;", wantCookieTTL) + assert.Equal(t, wantCookie, gotCookie, test.description) + } else { + assert.Empty(t, gotCookie, test.description) + } + mockMetrics.AssertExpectations(t) mockAnalytics.AssertExpectations(t) } @@ -538,11 +635,13 @@ func TestCookieSyncParseRequest(t *testing.T) { gdprPermissions: &fakePermissions{}, ccpaParsedPolicy: expectedCCPAParsedPolicy, activityRequest: privacy.NewRequestFromPolicies(privacy.Policies{GPPSID: []int8{2}}), + gdprSignal: 1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), Redirect: usersync.NewSpecificBidderFilter([]string{"b"}, usersync.BidderFilterModeExclude), }, + GPPSID: "2", }, }, { @@ -578,6 +677,7 @@ func TestCookieSyncParseRequest(t *testing.T) { gdprPermissions: &fakePermissions{}, ccpaParsedPolicy: expectedCCPAParsedPolicy, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: 1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -595,6 +695,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: -1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -622,6 +723,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: -1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -649,6 +751,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: -1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -676,6 +779,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: -1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -703,6 +807,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: -1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -730,6 +835,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: -1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -757,6 +863,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: -1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -774,6 +881,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: -1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -793,6 +901,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: -1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -805,7 +914,7 @@ func TestCookieSyncParseRequest(t *testing.T) { givenBody: strings.NewReader(`malformed`), givenGDPRConfig: config.GDPR{Enabled: true, DefaultValue: "0"}, givenCCPAEnabled: true, - expectedError: "JSON parsing failed: invalid character 'm' looking for beginning of value", + expectedError: "JSON parsing failed: expect { or n, but found m", }, { description: "Invalid Type Filter", @@ -833,6 +942,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: 0, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -859,6 +969,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: -1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -911,6 +1022,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: -1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -943,6 +1055,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: -1, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -975,6 +1088,7 @@ func TestCookieSyncParseRequest(t *testing.T) { Privacy: usersyncPrivacy{ gdprPermissions: &fakePermissions{}, activityRequest: emptyActivityPoliciesRequest, + gdprSignal: 0, }, SyncTypeFilter: usersync.SyncTypeFilter{ IFrame: usersync.NewUniformBidderFilter(usersync.BidderFilterModeInclude), @@ -1014,7 +1128,7 @@ func TestCookieSyncParseRequest(t *testing.T) { }}, } assert.NoError(t, endpoint.config.MarshalAccountDefaults()) - request, privacyPolicies, err := endpoint.parseRequest(httpRequest) + request, privacyPolicies, _, err := endpoint.parseRequest(httpRequest) if test.expectedError == "" { assert.NoError(t, err, test.description+":err") @@ -1255,7 +1369,7 @@ func TestSetCooperativeSync(t *testing.T) { func TestWriteParseRequestErrorMetrics(t *testing.T) { err := errors.New("anyError") - mockAnalytics := MockAnalytics{} + mockAnalytics := MockAnalyticsRunner{} mockAnalytics.On("LogCookieSyncObject", mock.Anything) writer := httptest.NewRecorder() @@ -1478,7 +1592,7 @@ func TestParseBidderFilter(t *testing.T) { func TestCookieSyncHandleError(t *testing.T) { err := errors.New("anyError") - mockAnalytics := MockAnalytics{} + mockAnalytics := MockAnalyticsRunner{} mockAnalytics.On("LogCookieSyncObject", mock.Anything) writer := httptest.NewRecorder() @@ -1515,14 +1629,14 @@ func TestCookieSyncWriteBidderMetrics(t *testing.T) { }, { description: "One - Blocked By GDPR", - given: []usersync.BidderEvaluation{{Bidder: "a", SyncerKey: "aSyncer", Status: usersync.StatusBlockedByGDPR}}, + given: []usersync.BidderEvaluation{{Bidder: "a", SyncerKey: "aSyncer", Status: usersync.StatusBlockedByPrivacy}}, setExpectations: func(m *metrics.MetricsEngineMock) { m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncPrivacyBlocked).Once() }, }, { description: "One - Blocked By CCPA", - given: []usersync.BidderEvaluation{{Bidder: "a", SyncerKey: "aSyncer", Status: usersync.StatusBlockedByCCPA}}, + given: []usersync.BidderEvaluation{{Bidder: "a", SyncerKey: "aSyncer", Status: usersync.StatusBlockedByPrivacy}}, setExpectations: func(m *metrics.MetricsEngineMock) { m.On("RecordSyncerRequest", "aSyncer", metrics.SyncerCookieSyncPrivacyBlocked).Once() }, @@ -1587,10 +1701,22 @@ func TestCookieSyncHandleResponse(t *testing.T) { syncerWithError := MockSyncer{} syncerWithError.On("GetSync", syncTypeExpected, privacyMacros).Return(syncWithError, errors.New("anyError")).Maybe() + bidderEvalForDebug := []usersync.BidderEvaluation{ + {Bidder: "Bidder1", Status: usersync.StatusAlreadySynced}, + {Bidder: "Bidder2", Status: usersync.StatusUnknownBidder}, + {Bidder: "Bidder3", Status: usersync.StatusUnconfiguredBidder}, + {Bidder: "Bidder4", Status: usersync.StatusBlockedByPrivacy}, + {Bidder: "Bidder5", Status: usersync.StatusTypeNotSupported}, + {Bidder: "Bidder6", Status: usersync.StatusBlockedByUserOptOut}, + {Bidder: "Bidder7", Status: usersync.StatusBlockedByDisabledUsersync}, + {Bidder: "BidderA", Status: usersync.StatusDuplicate, SyncerKey: "syncerB"}, + } + testCases := []struct { description string givenCookieHasSyncs bool givenSyncersChosen []usersync.SyncerChoice + givenDebug bool expectedJSON string expectedAnalytics analytics.CookieSyncObject }{ @@ -1668,10 +1794,18 @@ func TestCookieSyncHandleResponse(t *testing.T) { expectedJSON: `{"status":"no_cookie","bidder_status":[]}` + "\n", expectedAnalytics: analytics.CookieSyncObject{Status: 200, BidderStatus: []*analytics.CookieSyncBidder{}}, }, + { + description: "Debug is true, should see all rejected bidder eval statuses in response", + givenCookieHasSyncs: true, + givenDebug: true, + givenSyncersChosen: []usersync.SyncerChoice{}, + expectedJSON: `{"status":"ok","bidder_status":[],"debug":[{"bidder":"Bidder1","error":"Already in sync"},{"bidder":"Bidder2","error":"Unsupported bidder"},{"bidder":"Bidder3","error":"No sync config"},{"bidder":"Bidder4","error":"Rejected by privacy"},{"bidder":"Bidder5","error":"Type not supported"},{"bidder":"Bidder6","error":"Status blocked by user opt out"},{"bidder":"Bidder7","error":"Sync disabled by config"},{"bidder":"BidderA","error":"Duplicate bidder synced as syncerB"}]}` + "\n", + expectedAnalytics: analytics.CookieSyncObject{Status: 200, BidderStatus: []*analytics.CookieSyncBidder{}}, + }, } for _, test := range testCases { - mockAnalytics := MockAnalytics{} + mockAnalytics := MockAnalyticsRunner{} mockAnalytics.On("LogCookieSyncObject", &test.expectedAnalytics).Once() cookie := usersync.NewCookie() @@ -1683,7 +1817,14 @@ func TestCookieSyncHandleResponse(t *testing.T) { writer := httptest.NewRecorder() endpoint := cookieSyncEndpoint{pbsAnalytics: &mockAnalytics} - endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen) + + var bidderEval []usersync.BidderEvaluation + if test.givenDebug { + bidderEval = bidderEvalForDebug + } else { + bidderEval = []usersync.BidderEvaluation{} + } + endpoint.handleResponse(writer, syncTypeFilter, cookie, privacyMacros, test.givenSyncersChosen, bidderEval, test.givenDebug) if assert.Equal(t, writer.Code, http.StatusOK, test.description+":http_status") { assert.Equal(t, writer.Header().Get("Content-Type"), "application/json; charset=utf-8", test.description+":http_header") @@ -1922,6 +2063,39 @@ func TestCookieSyncActivityControlIntegration(t *testing.T) { } } +func TestUsersyncPrivacyGDPRInScope(t *testing.T) { + testCases := []struct { + description string + givenGdprSignal gdpr.Signal + expected bool + }{ + { + description: "GDPR Signal Yes", + givenGdprSignal: gdpr.SignalYes, + expected: true, + }, + { + description: "GDPR Signal No", + givenGdprSignal: gdpr.SignalNo, + expected: false, + }, + { + description: "GDPR Signal Ambigious", + givenGdprSignal: gdpr.SignalAmbiguous, + expected: false, + }, + } + + for _, test := range testCases { + privacy := usersyncPrivacy{ + gdprSignal: test.givenGdprSignal, + } + + result := privacy.GDPRInScope() + assert.Equal(t, test.expected, result, test.description) + } +} + func TestCombineErrors(t *testing.T) { testCases := []struct { description string @@ -1945,7 +2119,7 @@ func TestCombineErrors(t *testing.T) { }, { description: "Special Case: blocked (rejected via block list)", - givenErrorList: []error{&errortypes.BlacklistedAcct{}}, + givenErrorList: []error{&errortypes.AccountDisabled{}}, expectedError: errCookieSyncAccountBlocked, }, { @@ -1960,7 +2134,7 @@ func TestCombineErrors(t *testing.T) { }, { description: "Special Case: multiple special cases, first one wins", - givenErrorList: []error{&errortypes.BlacklistedAcct{}, &errortypes.AcctRequired{}, &errortypes.MalformedAcct{}}, + givenErrorList: []error{&errortypes.AccountDisabled{}, &errortypes.AcctRequired{}, &errortypes.MalformedAcct{}}, expectedError: errCookieSyncAccountBlocked, }, } @@ -1988,7 +2162,7 @@ func (m *MockSyncer) Key() string { return args.String(0) } -func (m *MockSyncer) DefaultSyncType() usersync.SyncType { +func (m *MockSyncer) DefaultResponseFormat() usersync.SyncType { args := m.Called() return args.Get(0).(usersync.SyncType) } @@ -2003,32 +2177,32 @@ func (m *MockSyncer) GetSync(syncTypes []usersync.SyncType, privacyMacros macros return args.Get(0).(usersync.Sync), args.Error(1) } -type MockAnalytics struct { +type MockAnalyticsRunner struct { mock.Mock } -func (m *MockAnalytics) LogAuctionObject(obj *analytics.AuctionObject) { - m.Called(obj) +func (m *MockAnalyticsRunner) LogAuctionObject(obj *analytics.AuctionObject, ac privacy.ActivityControl) { + m.Called(obj, ac) } -func (m *MockAnalytics) LogVideoObject(obj *analytics.VideoObject) { - m.Called(obj) +func (m *MockAnalyticsRunner) LogVideoObject(obj *analytics.VideoObject, ac privacy.ActivityControl) { + m.Called(obj, ac) } -func (m *MockAnalytics) LogCookieSyncObject(obj *analytics.CookieSyncObject) { +func (m *MockAnalyticsRunner) LogCookieSyncObject(obj *analytics.CookieSyncObject) { m.Called(obj) } -func (m *MockAnalytics) LogSetUIDObject(obj *analytics.SetUIDObject) { +func (m *MockAnalyticsRunner) LogSetUIDObject(obj *analytics.SetUIDObject) { m.Called(obj) } -func (m *MockAnalytics) LogAmpObject(obj *analytics.AmpObject) { - m.Called(obj) +func (m *MockAnalyticsRunner) LogAmpObject(obj *analytics.AmpObject, ac privacy.ActivityControl) { + m.Called(obj, ac) } -func (m *MockAnalytics) LogNotificationEventObject(obj *analytics.NotificationEvent) { - m.Called(obj) +func (m *MockAnalyticsRunner) LogNotificationEventObject(obj *analytics.NotificationEvent, ac privacy.ActivityControl) { + m.Called(obj, ac) } type MockGDPRPerms struct { @@ -2101,3 +2275,131 @@ func getDefaultActivityConfig(componentName string, allow bool) *config.AccountP }, } } + +func TestSetCookieDeprecationHeader(t *testing.T) { + getTestRequest := func(addCookie bool) *http.Request { + r := httptest.NewRequest("POST", "/cookie_sync", nil) + if addCookie { + r.AddCookie(&http.Cookie{Name: receiveCookieDeprecation, Value: "1"}) + } + return r + } + + tests := []struct { + name string + responseWriter http.ResponseWriter + request *http.Request + account *config.Account + expectedCookieDeprecationHeader bool + }{ + { + name: "not-present-account-nil", + request: getTestRequest(false), + responseWriter: httptest.NewRecorder(), + account: nil, + expectedCookieDeprecationHeader: false, + }, + { + name: "not-present-cookiedeprecation-disabled", + request: getTestRequest(false), + responseWriter: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: false, + }, + }, + }, + }, + expectedCookieDeprecationHeader: false, + }, + { + name: "present-cookiedeprecation-disabled", + request: getTestRequest(true), + responseWriter: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: false, + }, + }, + }, + }, + expectedCookieDeprecationHeader: false, + }, + { + name: "present-cookiedeprecation-enabled", + request: getTestRequest(true), + responseWriter: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, + }, + }, + }, + }, + + expectedCookieDeprecationHeader: false, + }, + { + name: "present-account-nil", + request: getTestRequest(true), + responseWriter: httptest.NewRecorder(), + account: nil, + expectedCookieDeprecationHeader: false, + }, + { + name: "not-present-cookiedeprecation-enabled", + request: getTestRequest(false), + responseWriter: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, + }, + }, + }, + }, + expectedCookieDeprecationHeader: true, + }, + { + name: "failed-to-read-cookiedeprecation-enabled", + request: &http.Request{}, // nil cookie. error: http: named cookie not present + responseWriter: httptest.NewRecorder(), + account: &config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, + }, + }, + }, + }, + expectedCookieDeprecationHeader: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &cookieSyncEndpoint{ + time: &fakeTime{time: time.Date(2024, 2, 22, 9, 42, 4, 13, time.UTC)}, + } + c.setCookieDeprecationHeader(tt.responseWriter, tt.request, tt.account) + gotCookie := tt.responseWriter.Header().Get("Set-Cookie") + if tt.expectedCookieDeprecationHeader { + wantCookieTTL := c.time.Now().Add(time.Second * time.Duration(86400)).UTC().Format(http.TimeFormat) + wantCookie := fmt.Sprintf("receive-cookie-deprecation=1; Path=/; Expires=%v; HttpOnly; Secure; SameSite=None; Partitioned;", wantCookieTTL) + assert.Equal(t, wantCookie, gotCookie, ":set_cookie_deprecation_header") + } else { + assert.Empty(t, gotCookie, ":set_cookie_deprecation_header") + } + }) + } +} diff --git a/endpoints/currency_rates.go b/endpoints/currency_rates.go index d35cb74cea4..f08154471fe 100644 --- a/endpoints/currency_rates.go +++ b/endpoints/currency_rates.go @@ -1,12 +1,12 @@ package endpoints import ( - "encoding/json" "net/http" "time" "github.com/golang/glog" - "github.com/prebid/prebid-server/currency" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) // currencyRatesInfo holds currency rates information. @@ -60,7 +60,7 @@ func NewCurrencyRatesEndpoint(rateConverter rateConverter, fetchingInterval time currencyRateInfo := newCurrencyRatesInfo(rateConverter, fetchingInterval) return func(w http.ResponseWriter, _ *http.Request) { - jsonOutput, err := json.Marshal(currencyRateInfo) + jsonOutput, err := jsonutil.Marshal(currencyRateInfo) if err != nil { glog.Errorf("/currency/rates Critical error when trying to marshal currencyRateInfo: %v", err) w.WriteHeader(http.StatusInternalServerError) diff --git a/endpoints/currency_rates_test.go b/endpoints/currency_rates_test.go index 7fc513e7dbe..0b953c640e2 100644 --- a/endpoints/currency_rates_test.go +++ b/endpoints/currency_rates_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/currency" + "github.com/prebid/prebid-server/v2/currency" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/events/account_test.go b/endpoints/events/account_test.go index 7efb8af782b..d19a3912f59 100644 --- a/endpoints/events/account_test.go +++ b/endpoints/events/account_test.go @@ -2,7 +2,6 @@ package events import ( "errors" - "fmt" "io" "net/http" "net/http/httptest" @@ -10,24 +9,23 @@ import ( "testing" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/stored_requests" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestHandleAccountServiceErrors(t *testing.T) { tests := map[string]struct { - fetcher *mockAccountsFetcher - cfg *config.Configuration - accountID string - want struct { - code int - response string - } + fetcher *mockAccountsFetcher + cfg *config.Configuration + accountID string + wantCode int + wantResponse string }{ - "badRequest": { + "bad-request": { fetcher: &mockAccountsFetcher{ Fail: true, Error: errors.New("some error"), @@ -40,16 +38,11 @@ func TestHandleAccountServiceErrors(t *testing.T) { TimeoutMS: int64(2000), AllowUnknownBidder: false, }, }, - accountID: "testacc", - want: struct { - code int - response string - }{ - code: 400, - response: "Invalid request: some error\nInvalid request: Prebid-server could not verify the Account ID. Please reach out to the prebid server host.\n", - }, + accountID: "testacc", + wantCode: 400, + wantResponse: "Invalid request: some error\nInvalid request: Prebid-server could not verify the Account ID. Please reach out to the prebid server host.\n", }, - "malformedAccountConfig": { + "malformed-account-config": { fetcher: &mockAccountsFetcher{ Fail: true, Error: &errortypes.MalformedAcct{}, @@ -60,34 +53,25 @@ func TestHandleAccountServiceErrors(t *testing.T) { TimeoutMS: int64(2000), AllowUnknownBidder: false, }, }, - accountID: "malformed_acct", - want: struct { - code int - response string - }{ - code: 500, - response: "Invalid request: The prebid-server account config for account id \"malformed_acct\" is malformed. Please reach out to the prebid server host.\n", - }, + accountID: "malformed_acct", + wantCode: 500, + wantResponse: "Invalid request: The prebid-server account config for account id \"malformed_acct\" is malformed. Please reach out to the prebid server host.\n", }, - "serviceUnavailable": { + "service-unavailable": { fetcher: &mockAccountsFetcher{ Fail: false, }, cfg: &config.Configuration{ - BlacklistedAcctMap: map[string]bool{"testacc": true}, - MaxRequestSize: maxSize, + AccountDefaults: config.Account{}, + AccountRequired: true, + MaxRequestSize: maxSize, VTrack: config.VTrack{ TimeoutMS: int64(2000), AllowUnknownBidder: false, }, }, - accountID: "testacc", - want: struct { - code int - response string - }{ - code: 503, - response: "Invalid request: Prebid-server has disabled Account ID: testacc, please reach out to the prebid server host.\n", - }, + accountID: "disabled_acct", + wantCode: 503, + wantResponse: "Invalid request: Prebid-server has disabled Account ID: disabled_acct, please reach out to the prebid server host.\n", }, "timeout": { fetcher: &mockAccountsFetcher{ @@ -106,19 +90,13 @@ func TestHandleAccountServiceErrors(t *testing.T) { AllowUnknownBidder: false, }, }, - accountID: "testacc", - want: struct { - code int - response string - }{ - code: 504, - response: "Invalid request: context deadline exceeded\nInvalid request: Prebid-server could not verify the Account ID. Please reach out to the prebid server host.\n", - }, + accountID: "testacc", + wantCode: 504, + wantResponse: "Invalid request: context deadline exceeded\nInvalid request: Prebid-server could not verify the Account ID. Please reach out to the prebid server host.\n", }, } for name, test := range tests { - handlers := []struct { name string h httprouter.Handle @@ -137,13 +115,11 @@ func TestHandleAccountServiceErrors(t *testing.T) { // execute handler.h(recorder, handler.r, nil) d, err := io.ReadAll(recorder.Result().Body) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // validate - assert.Equal(t, test.want.code, recorder.Result().StatusCode, fmt.Sprintf("Expected %d", test.want.code)) - assert.Equal(t, test.want.response, string(d)) + assert.Equal(t, test.wantCode, recorder.Result().StatusCode) + assert.Equal(t, test.wantResponse, string(d)) }) } } diff --git a/endpoints/events/event.go b/endpoints/events/event.go index 089d5606552..b92b72f17ad 100644 --- a/endpoints/events/event.go +++ b/endpoints/events/event.go @@ -10,14 +10,17 @@ import ( "time" "unicode" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/julienschmidt/httprouter" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/util/httputil" + accountService "github.com/prebid/prebid-server/v2/account" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/util/httputil" ) const ( @@ -40,13 +43,13 @@ const integrationParamMaxLength = 64 type eventEndpoint struct { Accounts stored_requests.AccountFetcher - Analytics analytics.PBSAnalyticsModule + Analytics analytics.Runner Cfg *config.Configuration TrackingPixel *httputil.Pixel MetricsEngine metrics.MetricsEngine } -func NewEventEndpoint(cfg *config.Configuration, accounts stored_requests.AccountFetcher, analytics analytics.PBSAnalyticsModule, me metrics.MetricsEngine) httprouter.Handle { +func NewEventEndpoint(cfg *config.Configuration, accounts stored_requests.AccountFetcher, analytics analytics.Runner, me metrics.MetricsEngine) httprouter.Handle { ee := &eventEndpoint{ Accounts: accounts, Analytics: analytics, @@ -108,17 +111,19 @@ func (e *eventEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ httprou } // Check if events are enabled for the account - if !account.Events.IsEnabled() { + if !account.Events.Enabled { w.WriteHeader(http.StatusUnauthorized) w.Write([]byte(fmt.Sprintf("Account '%s' doesn't support events", eventRequest.AccountID))) return } + activities := privacy.NewActivityControl(&account.Privacy) + // handle notification event e.Analytics.LogNotificationEventObject(&analytics.NotificationEvent{ Request: eventRequest, Account: account, - }) + }, activities) // Add tracking pixel if format == image if eventRequest.Format == analytics.Image { @@ -185,7 +190,12 @@ func ParseEventRequest(r *http.Request) (*analytics.EventRequest, []error) { } // Bidder - event.Bidder = r.URL.Query().Get(BidderParameter) + bidderName := r.URL.Query().Get(BidderParameter) + if normalisedBidderName, ok := openrtb_ext.NormalizeBidderName(bidderName); ok { + bidderName = normalisedBidderName.String() + } + + event.Bidder = bidderName return event, errs } @@ -206,7 +216,7 @@ func HandleAccountServiceErrors(errs []error) (status int, messages []string) { errCode := errortypes.ReadCode(er) - if errCode == errortypes.BlacklistedAppErrorCode || errCode == errortypes.BlacklistedAcctErrorCode { + if errCode == errortypes.BlacklistedAppErrorCode || errCode == errortypes.AccountDisabledErrorCode { status = http.StatusServiceUnavailable } if errCode == errortypes.MalformedAcctErrorCode { diff --git a/endpoints/events/event_test.go b/endpoints/events/event_test.go index 95711ba1328..81d000fd8a4 100644 --- a/endpoints/events/event_test.go +++ b/endpoints/events/event_test.go @@ -12,70 +12,63 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/stored_requests" "github.com/stretchr/testify/assert" ) -// Mock Analytics Module type eventsMockAnalyticsModule struct { Fail bool Error error Invoked bool } -func (e *eventsMockAnalyticsModule) LogAuctionObject(ao *analytics.AuctionObject) { +func (e *eventsMockAnalyticsModule) LogAuctionObject(ao *analytics.AuctionObject, _ privacy.ActivityControl) { if e.Fail { panic(e.Error) } - return } -func (e *eventsMockAnalyticsModule) LogVideoObject(vo *analytics.VideoObject) { +func (e *eventsMockAnalyticsModule) LogVideoObject(vo *analytics.VideoObject, _ privacy.ActivityControl) { if e.Fail { panic(e.Error) } - return } func (e *eventsMockAnalyticsModule) LogCookieSyncObject(cso *analytics.CookieSyncObject) { if e.Fail { panic(e.Error) } - return } func (e *eventsMockAnalyticsModule) LogSetUIDObject(so *analytics.SetUIDObject) { if e.Fail { panic(e.Error) } - return } -func (e *eventsMockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject) { +func (e *eventsMockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject, _ privacy.ActivityControl) { if e.Fail { panic(e.Error) } - return } -func (e *eventsMockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent) { +func (e *eventsMockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent, _ privacy.ActivityControl) { if e.Fail { panic(e.Error) } e.Invoked = true - - return } -// Mock Account fetcher var mockAccountData = map[string]json.RawMessage{ "events_enabled": json.RawMessage(`{"events": {"enabled":true}}`), "events_disabled": json.RawMessage(`{"events": {"enabled":false}}`), "malformed_acct": json.RawMessage(`{"events": {"enabled":"invalid type"}}`), + "disabled_acct": json.RawMessage(`{"disabled": true}`), } type mockAccountsFetcher struct { @@ -102,7 +95,7 @@ func (maf mockAccountsFetcher) FetchAccount(ctx context.Context, defaultAccountJ return nil, []error{maf.Error} } - return nil, []error{stored_requests.NotFoundError{accountID, "Account"}} + return nil, []error{stored_requests.NotFoundError{ID: accountID, DataType: "Account"}} } // Tests @@ -658,6 +651,19 @@ func TestShouldParseEventCorrectly(t *testing.T) { Analytics: analytics.Enabled, }, }, + "case insensitive bidder name": { + req: httptest.NewRequest("GET", "/event?t=win&b=bidId&f=b&ts=1000&x=1&a=accountId&bidder=RubiCon&int=intType", strings.NewReader("")), + expected: &analytics.EventRequest{ + Type: analytics.Win, + BidID: "bidId", + Timestamp: 1000, + Bidder: "rubicon", + AccountID: "", + Format: analytics.Blank, + Analytics: analytics.Enabled, + Integration: "intType", + }, + }, } for name, test := range tests { diff --git a/endpoints/events/vtrack.go b/endpoints/events/vtrack.go index d320fdc6989..5d794651ba4 100644 --- a/endpoints/events/vtrack.go +++ b/endpoints/events/vtrack.go @@ -11,13 +11,15 @@ import ( "github.com/golang/glog" "github.com/julienschmidt/httprouter" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests" + accountService "github.com/prebid/prebid-server/v2/account" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/prebid_cache_client" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) const ( @@ -27,12 +29,15 @@ const ( ImpressionOpenTag = "" ) +type normalizeBidderName func(name string) (openrtb_ext.BidderName, bool) + type vtrackEndpoint struct { - Cfg *config.Configuration - Accounts stored_requests.AccountFetcher - BidderInfos config.BidderInfos - Cache prebid_cache_client.Client - MetricsEngine metrics.MetricsEngine + Cfg *config.Configuration + Accounts stored_requests.AccountFetcher + BidderInfos config.BidderInfos + Cache prebid_cache_client.Client + MetricsEngine metrics.MetricsEngine + normalizeBidderName normalizeBidderName } type BidCacheRequest struct { @@ -49,11 +54,12 @@ type CacheObject struct { func NewVTrackEndpoint(cfg *config.Configuration, accounts stored_requests.AccountFetcher, cache prebid_cache_client.Client, bidderInfos config.BidderInfos, me metrics.MetricsEngine) httprouter.Handle { vte := &vtrackEndpoint{ - Cfg: cfg, - Accounts: accounts, - BidderInfos: bidderInfos, - Cache: cache, - MetricsEngine: me, + Cfg: cfg, + Accounts: accounts, + BidderInfos: bidderInfos, + Cache: cache, + MetricsEngine: me, + normalizeBidderName: openrtb_ext.NormalizeBidderName, } return vte.Handle @@ -118,7 +124,7 @@ func (v *vtrackEndpoint) Handle(w http.ResponseWriter, r *http.Request, _ httpro } } - d, err := json.Marshal(*cachingResponse) + d, err := jsonutil.Marshal(*cachingResponse) if err != nil { w.WriteHeader(http.StatusInternalServerError) @@ -182,7 +188,7 @@ func ParseVTrackRequest(httpRequest *http.Request, maxRequestSize int64) (req *B return req, err } - if err := json.Unmarshal(requestJson, req); err != nil { + if err := jsonutil.UnmarshalValid(requestJson, req); err != nil { return req, err } @@ -203,7 +209,7 @@ func ParseVTrackRequest(httpRequest *http.Request, maxRequestSize int64) (req *B // handleVTrackRequest handles a VTrack request func (v *vtrackEndpoint) handleVTrackRequest(ctx context.Context, req *BidCacheRequest, account *config.Account, integration string) (*BidCacheResponse, []error) { - biddersAllowingVastUpdate := getBiddersAllowingVastUpdate(req, &v.BidderInfos, v.Cfg.VTrack.AllowUnknownBidder) + biddersAllowingVastUpdate := getBiddersAllowingVastUpdate(req, &v.BidderInfos, v.Cfg.VTrack.AllowUnknownBidder, v.normalizeBidderName) // cache data r, errs := v.cachePutObjects(ctx, req, biddersAllowingVastUpdate, account.ID, integration) @@ -251,11 +257,11 @@ func (v *vtrackEndpoint) cachePutObjects(ctx context.Context, req *BidCacheReque } // getBiddersAllowingVastUpdate returns a list of bidders that allow VAST XML modification -func getBiddersAllowingVastUpdate(req *BidCacheRequest, bidderInfos *config.BidderInfos, allowUnknownBidder bool) map[string]struct{} { +func getBiddersAllowingVastUpdate(req *BidCacheRequest, bidderInfos *config.BidderInfos, allowUnknownBidder bool, normalizeBidderName normalizeBidderName) map[string]struct{} { bl := map[string]struct{}{} for _, bcr := range req.Puts { - if _, ok := bl[bcr.Bidder]; isAllowVastForBidder(bcr.Bidder, bidderInfos, allowUnknownBidder) && !ok { + if _, ok := bl[bcr.Bidder]; isAllowVastForBidder(bcr.Bidder, bidderInfos, allowUnknownBidder, normalizeBidderName) && !ok { bl[bcr.Bidder] = struct{}{} } } @@ -264,12 +270,14 @@ func getBiddersAllowingVastUpdate(req *BidCacheRequest, bidderInfos *config.Bidd } // isAllowVastForBidder checks if a bidder is active and allowed to modify vast xml data -func isAllowVastForBidder(bidder string, bidderInfos *config.BidderInfos, allowUnknownBidder bool) bool { +func isAllowVastForBidder(bidder string, bidderInfos *config.BidderInfos, allowUnknownBidder bool, normalizeBidderName normalizeBidderName) bool { //if bidder is active and isModifyingVastXmlAllowed is true // check if bidder is configured - if b, ok := (*bidderInfos)[bidder]; bidderInfos != nil && ok { - // check if bidder is enabled - return b.IsEnabled() && b.ModifyingVastXmlAllowed + if normalizedBidder, ok := normalizeBidderName(bidder); ok { + if b, ok := (*bidderInfos)[normalizedBidder.String()]; bidderInfos != nil && ok { + // check if bidder is enabled + return b.IsEnabled() && b.ModifyingVastXmlAllowed + } } return allowUnknownBidder @@ -312,7 +320,7 @@ func ModifyVastXmlString(externalUrl, vast, bidid, bidder, accountID string, tim // ModifyVastXmlJSON modifies BidCacheRequest element Vast XML data func ModifyVastXmlJSON(externalUrl string, data json.RawMessage, bidid, bidder, accountId string, timestamp int64, integrationType string) json.RawMessage { var vast string - if err := json.Unmarshal(data, &vast); err != nil { + if err := jsonutil.Unmarshal(data, &vast); err != nil { // failed to decode json, fall back to string vast = string(data) } diff --git a/endpoints/events/vtrack_ow.go b/endpoints/events/vtrack_ow.go index 303554cd6b3..76e7ff7b707 100644 --- a/endpoints/events/vtrack_ow.go +++ b/endpoints/events/vtrack_ow.go @@ -9,9 +9,9 @@ import ( "github.com/beevik/etree" "github.com/golang/glog" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // standard VAST macros diff --git a/endpoints/events/vtrack_ow_test.go b/endpoints/events/vtrack_ow_test.go index 33177e66eb1..ac4cbbd06ac 100644 --- a/endpoints/events/vtrack_ow_test.go +++ b/endpoints/events/vtrack_ow_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/beevik/etree" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/events/vtrack_test.go b/endpoints/events/vtrack_test.go index ebf32dc0bba..a3853b79d77 100644 --- a/endpoints/events/vtrack_test.go +++ b/endpoints/events/vtrack_test.go @@ -12,9 +12,11 @@ import ( "strings" "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/prebid_cache_client" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" ) @@ -28,15 +30,17 @@ const ( // Mock pbs cache client type vtrackMockCacheClient struct { - Fail bool - Error error - Uuids []string + Fail bool + Error error + Uuids []string + Values []prebid_cache_client.Cacheable } func (m *vtrackMockCacheClient) PutJson(ctx context.Context, values []prebid_cache_client.Cacheable) ([]string, []error) { if m.Fail { return []string{}, []error{m.Error} } + m.Values = values return m.Uuids, []error{} } func (m *vtrackMockCacheClient) GetExtCacheData() (scheme string, host string, path string) { @@ -64,10 +68,11 @@ func TestShouldRespondWithBadRequestWhenAccountParameterIsMissing(t *testing.T) recorder := httptest.NewRecorder() e := vtrackEndpoint{ - Cfg: cfg, - BidderInfos: nil, - Cache: mockCacheClient, - Accounts: mockAccountsFetcher, + Cfg: cfg, + BidderInfos: nil, + Cache: mockCacheClient, + Accounts: mockAccountsFetcher, + normalizeBidderName: openrtb_ext.NormalizeBidderName, } // execute @@ -105,10 +110,11 @@ func TestShouldRespondWithBadRequestWhenRequestBodyIsEmpty(t *testing.T) { recorder := httptest.NewRecorder() e := vtrackEndpoint{ - Cfg: cfg, - BidderInfos: nil, - Cache: mockCacheClient, - Accounts: mockAccountsFetcher, + Cfg: cfg, + BidderInfos: nil, + Cache: mockCacheClient, + Accounts: mockAccountsFetcher, + normalizeBidderName: openrtb_ext.NormalizeBidderName, } // execute @@ -146,10 +152,11 @@ func TestShouldRespondWithBadRequestWhenRequestBodyIsInvalid(t *testing.T) { recorder := httptest.NewRecorder() e := vtrackEndpoint{ - Cfg: cfg, - BidderInfos: nil, - Cache: mockCacheClient, - Accounts: mockAccountsFetcher, + Cfg: cfg, + BidderInfos: nil, + Cache: mockCacheClient, + Accounts: mockAccountsFetcher, + normalizeBidderName: openrtb_ext.NormalizeBidderName, } // execute @@ -180,7 +187,7 @@ func TestShouldRespondWithBadRequestWhenBidIdIsMissing(t *testing.T) { }, } - reqData, err := json.Marshal(data) + reqData, err := jsonutil.Marshal(data) if err != nil { t.Fatal(err) } @@ -190,10 +197,11 @@ func TestShouldRespondWithBadRequestWhenBidIdIsMissing(t *testing.T) { recorder := httptest.NewRecorder() e := vtrackEndpoint{ - Cfg: cfg, - BidderInfos: nil, - Cache: mockCacheClient, - Accounts: mockAccountsFetcher, + Cfg: cfg, + BidderInfos: nil, + Cache: mockCacheClient, + Accounts: mockAccountsFetcher, + normalizeBidderName: openrtb_ext.NormalizeBidderName, } // execute @@ -232,7 +240,7 @@ func TestShouldRespondWithBadRequestWhenBidderIsMissing(t *testing.T) { }, } - reqData, err := json.Marshal(data) + reqData, err := jsonutil.Marshal(data) if err != nil { t.Fatal(err) } @@ -242,10 +250,11 @@ func TestShouldRespondWithBadRequestWhenBidderIsMissing(t *testing.T) { recorder := httptest.NewRecorder() e := vtrackEndpoint{ - Cfg: cfg, - BidderInfos: nil, - Cache: mockCacheClient, - Accounts: mockAccountsFetcher, + Cfg: cfg, + BidderInfos: nil, + Cache: mockCacheClient, + Accounts: mockAccountsFetcher, + normalizeBidderName: openrtb_ext.NormalizeBidderName, } // execute @@ -291,10 +300,11 @@ func TestShouldRespondWithInternalServerErrorWhenPbsCacheClientFails(t *testing. recorder := httptest.NewRecorder() e := vtrackEndpoint{ - Cfg: cfg, - BidderInfos: nil, - Cache: mockCacheClient, - Accounts: mockAccountsFetcher, + Cfg: cfg, + BidderInfos: nil, + Cache: mockCacheClient, + Accounts: mockAccountsFetcher, + normalizeBidderName: openrtb_ext.NormalizeBidderName, } // execute @@ -340,10 +350,11 @@ func TestShouldTolerateAccountNotFound(t *testing.T) { recorder := httptest.NewRecorder() e := vtrackEndpoint{ - Cfg: cfg, - BidderInfos: nil, - Cache: mockCacheClient, - Accounts: mockAccountsFetcher, + Cfg: cfg, + BidderInfos: nil, + Cache: mockCacheClient, + Accounts: mockAccountsFetcher, + normalizeBidderName: openrtb_ext.NormalizeBidderName, } // execute @@ -397,10 +408,11 @@ func TestShouldSendToCacheExpectedPutsAndUpdatableBiddersWhenBidderVastNotAllowe recorder := httptest.NewRecorder() e := vtrackEndpoint{ - Cfg: cfg, - BidderInfos: bidderInfos, - Cache: mockCacheClient, - Accounts: mockAccountsFetcher, + Cfg: cfg, + BidderInfos: bidderInfos, + Cache: mockCacheClient, + Accounts: mockAccountsFetcher, + normalizeBidderName: openrtb_ext.NormalizeBidderName, } // execute @@ -459,11 +471,15 @@ func TestShouldSendToCacheExpectedPutsAndUpdatableBiddersWhenBidderVastAllowed(t recorder := httptest.NewRecorder() + var mockNormalizeBidderName normalizeBidderName = func(name string) (openrtb_ext.BidderName, bool) { + return openrtb_ext.BidderName(name), true + } e := vtrackEndpoint{ - Cfg: cfg, - BidderInfos: bidderInfos, - Cache: mockCacheClient, - Accounts: mockAccountsFetcher, + Cfg: cfg, + BidderInfos: bidderInfos, + Cache: mockCacheClient, + Accounts: mockAccountsFetcher, + normalizeBidderName: mockNormalizeBidderName, } // execute @@ -478,6 +494,95 @@ func TestShouldSendToCacheExpectedPutsAndUpdatableBiddersWhenBidderVastAllowed(t assert.Equal(t, 200, recorder.Result().StatusCode, "Expected 200 when account is not found and request is valid") assert.Equal(t, "{\"responses\":[{\"uuid\":\"uuid1\"},{\"uuid\":\"uuid2\"}]}", string(d), "Expected 200 when account is found and request is valid") assert.Equal(t, "application/json", recorder.Header().Get("Content-Type")) + assert.Len(t, mockCacheClient.Values, 2) + assert.Contains(t, string(mockCacheClient.Values[0].Data), "bidder=bidder") + assert.Contains(t, string(mockCacheClient.Values[1].Data), "bidder=updatable_bidder") +} + +func TestShouldSendToCacheExpectedPutsAndUpdatableCaseSensitiveBiddersWhenBidderVastAllowed(t *testing.T) { + // mock pbs cache client + mockCacheClient := &vtrackMockCacheClient{ + Fail: false, + Uuids: []string{"uuid1", "uuid2"}, + } + + // mock AccountsFetcher + mockAccountsFetcher := &mockAccountsFetcher{ + Fail: false, + } + + // config + cfg := &config.Configuration{ + MaxRequestSize: maxSize, VTrack: config.VTrack{ + TimeoutMS: int64(2000), AllowUnknownBidder: false, + }, + AccountDefaults: config.Account{}, + } + cfg.MarshalAccountDefaults() + + // bidder info + bidderInfos := make(config.BidderInfos) + bidderInfos["appnexus"] = config.BidderInfo{ + Disabled: false, + ModifyingVastXmlAllowed: true, + } + + d, err := getVTrackRequestData(true, true) + assert.NoError(t, err) + + cacheReq := &BidCacheRequest{ + Puts: []prebid_cache_client.Cacheable{ + { + Type: prebid_cache_client.TypeXML, + BidID: "bidId1", + Bidder: "APPNEXUS", // case sensitive name + Data: d, + TTLSeconds: 3600, + Timestamp: 1000, + }, + { + Type: prebid_cache_client.TypeXML, + BidID: "bidId2", + Bidder: "ApPnExUs", // case sensitive name + Data: d, + TTLSeconds: 3600, + Timestamp: 1000, + }, + }, + } + buf := &bytes.Buffer{} + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + err = enc.Encode(cacheReq) + assert.NoError(t, err) + data := buf.String() + + req := httptest.NewRequest("POST", "/vtrack?a=events_enabled", strings.NewReader(data)) + + recorder := httptest.NewRecorder() + e := vtrackEndpoint{ + Cfg: cfg, + BidderInfos: bidderInfos, + Cache: mockCacheClient, + Accounts: mockAccountsFetcher, + normalizeBidderName: openrtb_ext.NormalizeBidderName, + } + + // execute + e.Handle(recorder, req, nil) + + d, err = io.ReadAll(recorder.Result().Body) + if err != nil { + t.Fatal(err) + } + + // validate + assert.Equal(t, 200, recorder.Result().StatusCode, "Expected 200 when account is not found and request is valid") + assert.Equal(t, "{\"responses\":[{\"uuid\":\"uuid1\"},{\"uuid\":\"uuid2\"}]}", string(d), "Expected 200 when account is found and request is valid") + assert.Equal(t, "application/json", recorder.Header().Get("Content-Type")) + assert.Len(t, mockCacheClient.Values, 2) + assert.Contains(t, string(mockCacheClient.Values[0].Data), "bidder=APPNEXUS") + assert.Contains(t, string(mockCacheClient.Values[1].Data), "bidder=ApPnExUs") } func TestShouldSendToCacheExpectedPutsAndUpdatableUnknownBiddersWhenUnknownBidderIsAllowed(t *testing.T) { @@ -515,10 +620,11 @@ func TestShouldSendToCacheExpectedPutsAndUpdatableUnknownBiddersWhenUnknownBidde recorder := httptest.NewRecorder() e := vtrackEndpoint{ - Cfg: cfg, - BidderInfos: bidderInfos, - Cache: mockCacheClient, - Accounts: mockAccountsFetcher, + Cfg: cfg, + BidderInfos: bidderInfos, + Cache: mockCacheClient, + Accounts: mockAccountsFetcher, + normalizeBidderName: openrtb_ext.NormalizeBidderName, } // execute @@ -571,10 +677,11 @@ func TestShouldReturnBadRequestWhenRequestExceedsMaxRequestSize(t *testing.T) { recorder := httptest.NewRecorder() e := vtrackEndpoint{ - Cfg: cfg, - BidderInfos: bidderInfos, - Cache: mockCacheClient, - Accounts: mockAccountsFetcher, + Cfg: cfg, + BidderInfos: bidderInfos, + Cache: mockCacheClient, + Accounts: mockAccountsFetcher, + normalizeBidderName: openrtb_ext.NormalizeBidderName, } // execute @@ -615,10 +722,11 @@ func TestShouldRespondWithInternalErrorPbsCacheIsNotConfigured(t *testing.T) { recorder := httptest.NewRecorder() e := vtrackEndpoint{ - Cfg: cfg, - BidderInfos: nil, - Cache: nil, - Accounts: mockAccountsFetcher, + Cfg: cfg, + BidderInfos: nil, + Cache: nil, + Accounts: mockAccountsFetcher, + normalizeBidderName: openrtb_ext.NormalizeBidderName, } // execute diff --git a/endpoints/getuids.go b/endpoints/getuids.go index f420c64fa6b..ea87ce70568 100644 --- a/endpoints/getuids.go +++ b/endpoints/getuids.go @@ -4,8 +4,8 @@ import ( "net/http" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/usersync" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/usersync" "encoding/json" ) diff --git a/endpoints/getuids_test.go b/endpoints/getuids_test.go index 7988acbaffe..c496d3e270b 100644 --- a/endpoints/getuids_test.go +++ b/endpoints/getuids_test.go @@ -5,7 +5,7 @@ import ( "net/http/httptest" "testing" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/info/bidders.go b/endpoints/info/bidders.go index 9984abe216d..7cbad5e26f6 100644 --- a/endpoints/info/bidders.go +++ b/endpoints/info/bidders.go @@ -1,14 +1,14 @@ package info import ( - "encoding/json" "net/http" "sort" "strings" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) var invalidEnabledOnlyMsg = []byte(`Invalid value for 'enabledonly' query param, must be of boolean type`) @@ -103,7 +103,7 @@ func prepareBiddersResponseAll(bidders config.BidderInfos, aliases map[string]st } sort.Strings(bidderNames) - return json.Marshal(bidderNames) + return jsonutil.Marshal(bidderNames) } func prepareBiddersResponseAllBaseOnly(bidders config.BidderInfos) ([]byte, error) { @@ -116,7 +116,7 @@ func prepareBiddersResponseAllBaseOnly(bidders config.BidderInfos) ([]byte, erro } sort.Strings(bidderNames) - return json.Marshal(bidderNames) + return jsonutil.Marshal(bidderNames) } func prepareBiddersResponseEnabledOnly(bidders config.BidderInfos, aliases map[string]string) ([]byte, error) { @@ -135,8 +135,7 @@ func prepareBiddersResponseEnabledOnly(bidders config.BidderInfos, aliases map[s } sort.Strings(bidderNames) - - return json.Marshal(bidderNames) + return jsonutil.Marshal(bidderNames) } func prepareBiddersResponseEnabledOnlyBaseOnly(bidders config.BidderInfos) ([]byte, error) { @@ -149,7 +148,7 @@ func prepareBiddersResponseEnabledOnlyBaseOnly(bidders config.BidderInfos) ([]by } sort.Strings(bidderNames) - return json.Marshal(bidderNames) + return jsonutil.Marshal(bidderNames) } func writeBadRequest(w http.ResponseWriter, data []byte) { diff --git a/endpoints/info/bidders_detail.go b/endpoints/info/bidders_detail.go index 1446e3ac22a..fbc9ab43486 100644 --- a/endpoints/info/bidders_detail.go +++ b/endpoints/info/bidders_detail.go @@ -8,8 +8,9 @@ import ( "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) const ( @@ -27,7 +28,11 @@ func NewBiddersDetailEndpoint(bidders config.BidderInfos, aliases map[string]str return func(w http.ResponseWriter, _ *http.Request, ps httprouter.Params) { bidder := ps.ByName("bidderName") - if response, ok := responses[bidder]; ok { + coreBidderName, found := getNormalisedBidderName(bidder, aliases) + if !found { + w.WriteHeader(http.StatusNotFound) + } + if response, ok := responses[coreBidderName]; ok { w.Header().Set("Content-Type", "application/json") if _, err := w.Write(response); err != nil { glog.Errorf("error writing response to /info/bidders/%s: %v", bidder, err) @@ -38,6 +43,20 @@ func NewBiddersDetailEndpoint(bidders config.BidderInfos, aliases map[string]str } } +func getNormalisedBidderName(bidderName string, aliases map[string]string) (string, bool) { + if strings.ToLower(bidderName) == "all" { + return "all", true + } + coreBidderName, ok := openrtb_ext.NormalizeBidderName(bidderName) + if !ok { //check default aliases if not found in coreBidders + if _, isDefaultAlias := aliases[bidderName]; isDefaultAlias { + return bidderName, true + } + return "", false + } + return coreBidderName.String(), true +} + func prepareBiddersDetailResponse(bidders config.BidderInfos, aliases map[string]string) (map[string][]byte, error) { details, err := mapDetails(bidders, aliases) if err != nil { @@ -83,7 +102,7 @@ func marshalDetailsResponse(details map[string]bidderDetail) (map[string][]byte, responses := map[string][]byte{} for bidder, detail := range details { - json, err := json.Marshal(detail) + json, err := jsonutil.Marshal(detail) if err != nil { return nil, fmt.Errorf("unable to marshal info for bidder %s: %v", bidder, err) } @@ -100,7 +119,7 @@ func marshalAllResponse(responses map[string][]byte) ([]byte, error) { responsesJSON[k] = json.RawMessage(v) } - json, err := json.Marshal(responsesJSON) + json, err := jsonutil.Marshal(responsesJSON) if err != nil { return nil, fmt.Errorf("unable to marshal info for bidder all: %v", err) } @@ -122,6 +141,7 @@ type maintainer struct { type capabilities struct { App *platform `json:"app,omitempty"` Site *platform `json:"site,omitempty"` + DOOH *platform `json:"dooh,omitempty"` } type platform struct { @@ -157,6 +177,12 @@ func mapDetailFromConfig(c config.BidderInfo) bidderDetail { MediaTypes: mapMediaTypes(c.Capabilities.Site.MediaTypes), } } + + if c.Capabilities.DOOH != nil { + bidderDetail.Capabilities.DOOH = &platform{ + MediaTypes: mapMediaTypes(c.Capabilities.DOOH.MediaTypes), + } + } } } else { bidderDetail.Status = statusDisabled diff --git a/endpoints/info/bidders_detail_test.go b/endpoints/info/bidders_detail_test.go index 47e5a0688a8..4965decef40 100644 --- a/endpoints/info/bidders_detail_test.go +++ b/endpoints/info/bidders_detail_test.go @@ -2,14 +2,15 @@ package info import ( "bytes" + "fmt" "io" "net/http" "net/http/httptest" "testing" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -225,6 +226,7 @@ func TestMapDetailFromConfig(t *testing.T) { Capabilities: &config.CapabilitiesInfo{ App: &config.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeBanner}}, Site: &config.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeVideo}}, + DOOH: &config.PlatformInfo{MediaTypes: []openrtb_ext.BidType{openrtb_ext.BidTypeNative}}, }, }, expected: bidderDetail{ @@ -236,6 +238,7 @@ func TestMapDetailFromConfig(t *testing.T) { Capabilities: &capabilities{ App: &platform{MediaTypes: []string{"banner"}}, Site: &platform{MediaTypes: []string{"video"}}, + DOOH: &platform{MediaTypes: []string{"native"}}, }, AliasOf: "", }, @@ -363,22 +366,22 @@ func TestMapMediaTypes(t *testing.T) { func TestBiddersDetailHandler(t *testing.T) { bidderAInfo := config.BidderInfo{Endpoint: "https://secureEndpoint.com", Disabled: false, Maintainer: &config.MaintainerInfo{Email: "bidderA"}} bidderAResponse := []byte(`{"status":"ACTIVE","usesHttps":true,"maintainer":{"email":"bidderA"}}`) - aliasAResponse := []byte(`{"status":"ACTIVE","usesHttps":true,"maintainer":{"email":"bidderA"},"aliasOf":"a"}`) + aliasAResponse := []byte(`{"status":"ACTIVE","usesHttps":true,"maintainer":{"email":"bidderA"},"aliasOf":"appnexus"}`) bidderBInfo := config.BidderInfo{Endpoint: "http://unsecureEndpoint.com", Disabled: false, Maintainer: &config.MaintainerInfo{Email: "bidderB"}} bidderBResponse := []byte(`{"status":"ACTIVE","usesHttps":false,"maintainer":{"email":"bidderB"}}`) allResponse := bytes.Buffer{} - allResponse.WriteString(`{"a":`) - allResponse.Write(bidderAResponse) - allResponse.WriteString(`,"aAlias":`) + allResponse.WriteString(`{"aAlias":`) allResponse.Write(aliasAResponse) - allResponse.WriteString(`,"b":`) + allResponse.WriteString(`,"appnexus":`) + allResponse.Write(bidderAResponse) + allResponse.WriteString(`,"rubicon":`) allResponse.Write(bidderBResponse) allResponse.WriteString(`}`) - bidders := config.BidderInfos{"a": bidderAInfo, "b": bidderBInfo} - aliases := map[string]string{"aAlias": "a"} + bidders := config.BidderInfos{"appnexus": bidderAInfo, "rubicon": bidderBInfo} + aliases := map[string]string{"aAlias": "appnexus"} handler := NewBiddersDetailEndpoint(bidders, aliases) @@ -391,14 +394,21 @@ func TestBiddersDetailHandler(t *testing.T) { }{ { description: "Bidder A", - givenBidder: "a", + givenBidder: "appnexus", expectedStatus: http.StatusOK, expectedHeaders: http.Header{"Content-Type": []string{"application/json"}}, expectedResponse: bidderAResponse, }, { description: "Bidder B", - givenBidder: "b", + givenBidder: "rubicon", + expectedStatus: http.StatusOK, + expectedHeaders: http.Header{"Content-Type": []string{"application/json"}}, + expectedResponse: bidderBResponse, + }, + { + description: "Bidder B - case insensitive", + givenBidder: "RUBICON", expectedStatus: http.StatusOK, expectedHeaders: http.Header{"Content-Type": []string{"application/json"}}, expectedResponse: bidderBResponse, @@ -410,6 +420,13 @@ func TestBiddersDetailHandler(t *testing.T) { expectedHeaders: http.Header{"Content-Type": []string{"application/json"}}, expectedResponse: aliasAResponse, }, + { + description: "Bidder A Alias - case insensitive", + givenBidder: "aAlias", + expectedStatus: http.StatusOK, + expectedHeaders: http.Header{"Content-Type": []string{"application/json"}}, + expectedResponse: aliasAResponse, + }, { description: "All Bidders", givenBidder: "all", @@ -418,11 +435,11 @@ func TestBiddersDetailHandler(t *testing.T) { expectedResponse: allResponse.Bytes(), }, { - description: "All Bidders - Wrong Case", - givenBidder: "ALL", - expectedStatus: http.StatusNotFound, - expectedHeaders: http.Header{}, - expectedResponse: []byte{}, + description: "All Bidders - Case insensitive", + givenBidder: "All", + expectedStatus: http.StatusOK, + expectedHeaders: http.Header{"Content-Type": []string{"application/json"}}, + expectedResponse: allResponse.Bytes(), }, { description: "Invalid Bidder", @@ -434,16 +451,22 @@ func TestBiddersDetailHandler(t *testing.T) { } for _, test := range testCases { - responseRecorder := httptest.NewRecorder() - handler(responseRecorder, nil, httprouter.Params{{"bidderName", test.givenBidder}}) - - result := responseRecorder.Result() - assert.Equal(t, result.StatusCode, test.expectedStatus, test.description+":statuscode") - - resultBody, _ := io.ReadAll(result.Body) - assert.Equal(t, test.expectedResponse, resultBody, test.description+":body") - - resultHeaders := result.Header - assert.Equal(t, test.expectedHeaders, resultHeaders, test.description+":headers") + t.Run(test.description, func(t *testing.T) { + responseRecorder := httptest.NewRecorder() + handler(responseRecorder, nil, httprouter.Params{{ + Key: "bidderName", + Value: test.givenBidder, + }}) + + result := responseRecorder.Result() + assert.Equal(t, result.StatusCode, test.expectedStatus, test.description+":statuscode") + + resultBody, _ := io.ReadAll(result.Body) + fmt.Println(string(test.expectedResponse)) + assert.Equal(t, test.expectedResponse, resultBody, test.description+":body") + + resultHeaders := result.Header + assert.Equal(t, test.expectedHeaders, resultHeaders, test.description+":headers") + }) } } diff --git a/endpoints/info/bidders_test.go b/endpoints/info/bidders_test.go index 1f483e5de27..189eb865551 100644 --- a/endpoints/info/bidders_test.go +++ b/endpoints/info/bidders_test.go @@ -6,7 +6,7 @@ import ( "net/http/httptest" "testing" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index 3b52e5ef404..bf8661c0a8c 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -11,34 +11,34 @@ import ( "strings" "time" - "github.com/prebid/prebid-server/privacy" - "github.com/buger/jsonparser" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/ortb" - "github.com/prebid/prebid-server/util/uuidutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/ortb" + "github.com/prebid/prebid-server/v2/util/uuidutil" jsonpatch "gopkg.in/evanphx/json-patch.v4" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/amp" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/stored_responses" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/iputil" - "github.com/prebid/prebid-server/version" + accountService "github.com/prebid/prebid-server/v2/account" + "github.com/prebid/prebid-server/v2/amp" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/v2/stored_responses" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/iputil" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/version" ) const defaultAmpRequestTimeoutMillis = 900 @@ -64,7 +64,7 @@ func NewAmpEndpoint( accounts stored_requests.AccountFetcher, cfg *config.Configuration, metricsEngine metrics.MetricsEngine, - pbsAnalytics analytics.PBSAnalyticsModule, + analyticsRunner analytics.Runner, disabledBidders map[string]string, defReqJSON []byte, bidderMap map[string]openrtb_ext.BidderName, @@ -77,7 +77,7 @@ func NewAmpEndpoint( return nil, errors.New("NewAmpEndpoint requires non-nil arguments.") } - defRequest := defReqJSON != nil && len(defReqJSON) > 0 + defRequest := len(defReqJSON) > 0 ipValidator := iputil.PublicNetworkIPValidator{ IPv4PrivateNetworks: cfg.RequestValidation.IPv4PrivateNetworksParsed, @@ -93,7 +93,7 @@ func NewAmpEndpoint( accounts, cfg, metricsEngine, - pbsAnalytics, + analyticsRunner, disabledBidders, defRequest, defReqJSON, @@ -104,6 +104,7 @@ func NewAmpEndpoint( storedRespFetcher, hookExecutionPlanBuilder, tmaxAdjustments, + openrtb_ext.NormalizeBidderName, }).AmpAuction), nil } @@ -133,11 +134,12 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h CookieFlag: metrics.CookieFlagUnknown, RequestStatus: metrics.RequestStatusOK, } + activityControl := privacy.ActivityControl{} defer func() { deps.metricsEngine.RecordRequest(labels) deps.metricsEngine.RecordRequestTime(labels, time.Since(start)) - deps.analytics.LogAmpObject(&ao) + deps.analytics.LogAmpObject(&ao, activityControl) }() // Add AMP headers @@ -207,7 +209,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h metricsStatus := metrics.RequestStatusBadInput for _, er := range errL { errCode := errortypes.ReadCode(er) - if errCode == errortypes.BlacklistedAppErrorCode || errCode == errortypes.BlacklistedAcctErrorCode { + if errCode == errortypes.BlacklistedAppErrorCode || errCode == errortypes.AccountDisabledErrorCode { httpStatus = http.StatusServiceUnavailable metricsStatus = metrics.RequestStatusBlacklisted break @@ -227,9 +229,25 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h return } + hasStoredResponses := len(storedAuctionResponses) > 0 + errs := deps.validateRequest(account, r, reqWrapper, true, hasStoredResponses, storedBidResponses, false) + errL = append(errL, errs...) + ao.Errors = append(ao.Errors, errs...) + if errortypes.ContainsFatalError(errs) { + w.WriteHeader(http.StatusBadRequest) + for _, err := range errortypes.FatalOnly(errs) { + w.Write([]byte(fmt.Sprintf("Invalid request: %s\n", err.Error()))) + } + labels.RequestStatus = metrics.RequestStatusBadInput + return + } + tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR) - activityControl := privacy.NewActivityControl(&account.Privacy) + activityControl = privacy.NewActivityControl(&account.Privacy) + + hookExecutor.SetActivityControl(activityControl) + hookExecutor.SetAccount(account) secGPC := r.Header.Get("Sec-GPC") @@ -339,7 +357,7 @@ func sendAmpResponse( // but this is a very unlikely corner case. Doing this so we can catch "hb_cache_id" // and "hb_cache_id_{deal}", which allows for deal support in AMP. bidExt := &openrtb_ext.ExtBid{} - err := json.Unmarshal(bid.Ext, bidExt) + err := jsonutil.Unmarshal(bid.Ext, bidExt) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintf(w, "Critical error while unpacking AMP targets: %v", err) @@ -358,7 +376,7 @@ func sendAmpResponse( // Extract global targeting var extResponse openrtb_ext.ExtBidResponse - eRErr := json.Unmarshal(response.Ext, &extResponse) + eRErr := jsonutil.Unmarshal(response.Ext, &extResponse) if eRErr != nil { ao.Errors = append(ao.Errors, fmt.Errorf("AMP response: failed to unpack OpenRTB response.ext, debug info cannot be forwarded: %v", eRErr)) } @@ -407,7 +425,7 @@ func getExtBidResponse( } // Extract any errors var extResponse openrtb_ext.ExtBidResponse - eRErr := json.Unmarshal(response.Ext, &extResponse) + eRErr := jsonutil.Unmarshal(response.Ext, &extResponse) if eRErr != nil { ao.Errors = append(ao.Errors, fmt.Errorf("AMP response: failed to unpack OpenRTB response.ext, debug info cannot be forwarded: %v", eRErr)) } @@ -491,10 +509,6 @@ func (deps *endpointDeps) parseAmpRequest(httpRequest *http.Request) (req *openr return } - hasStoredResponses := len(storedAuctionResponses) > 0 - e = deps.validateRequest(req, true, hasStoredResponses, storedBidResponses, false) - errs = append(errs, e...) - return } @@ -522,12 +536,12 @@ func (deps *endpointDeps) loadRequestJSONForAmp(httpRequest *http.Request) (req // The fetched config becomes the entire OpenRTB request requestJSON := storedRequests[ampParams.StoredRequestID] - if err := json.Unmarshal(requestJSON, req); err != nil { + if err := jsonutil.UnmarshalValid(requestJSON, req); err != nil { errs = []error{err} return } - storedAuctionResponses, storedBidResponses, bidderImpReplaceImp, errs = stored_responses.ProcessStoredResponses(ctx, requestJSON, deps.storedRespFetcher, deps.bidderMap) + storedAuctionResponses, storedBidResponses, bidderImpReplaceImp, errs = stored_responses.ProcessStoredResponses(ctx, &openrtb_ext.RequestWrapper{BidRequest: req}, deps.storedRespFetcher) if err != nil { errs = []error{err} return @@ -822,7 +836,7 @@ func setTrace(req *openrtb2.BidRequest, value string) error { return nil } - ext, err := json.Marshal(map[string]map[string]string{"prebid": {"trace": value}}) + ext, err := jsonutil.Marshal(map[string]map[string]string{"prebid": {"trace": value}}) if err != nil { return err } diff --git a/endpoints/openrtb2/amp_auction_test.go b/endpoints/openrtb2/amp_auction_test.go index 3a98f336228..340f1f0d999 100644 --- a/endpoints/openrtb2/amp_auction_test.go +++ b/endpoints/openrtb2/amp_auction_test.go @@ -14,23 +14,25 @@ import ( "time" "github.com/julienschmidt/httprouter" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/prebid/prebid-server/amp" - "github.com/prebid/prebid-server/analytics" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/metrics" - metricsConfig "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/v2/amp" + "github.com/prebid/prebid-server/v2/analytics" + analyticsBuild "github.com/prebid/prebid-server/v2/analytics/build" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/metrics" + metricsConfig "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) // TestGoodRequests makes sure that the auction runs properly-formatted stored bids correctly. @@ -49,6 +51,9 @@ func TestGoodAmpRequests(t *testing.T) { "imp-with-stored-resp.json", "gdpr-no-consentstring.json", "gdpr.json", + "buyeruids-case-insensitive.json", + "buyeruids-camel-case.json", + "aliased-buyeruids-case-insensitive.json", }, }, { @@ -73,7 +78,7 @@ func TestGoodAmpRequests(t *testing.T) { } test := testCase{} - if !assert.NoError(t, json.Unmarshal(fileJsonData, &test), "Failed to unmarshal data from file: %s. Error: %v", filename, err) { + if !assert.NoError(t, jsonutil.UnmarshalValid(fileJsonData, &test), "Failed to unmarshal data from file: %s. Error: %v", filename, err) { continue } @@ -98,8 +103,6 @@ func TestGoodAmpRequests(t *testing.T) { if test.Config != nil { cfg.BlacklistedApps = test.Config.BlacklistedApps cfg.BlacklistedAppMap = test.Config.getBlacklistedAppMap() - cfg.BlacklistedAccts = test.Config.BlacklistedAccounts - cfg.BlacklistedAcctMap = test.Config.getBlackListedAccountMap() cfg.AccountRequired = test.Config.AccountRequired } @@ -121,14 +124,14 @@ func TestGoodAmpRequests(t *testing.T) { // Assertions if assert.Equal(t, test.ExpectedReturnCode, recorder.Code, "Expected status %d. Got %d. Amp test file: %s", http.StatusOK, recorder.Code, filename) { if test.ExpectedReturnCode == http.StatusOK { - assert.JSONEq(t, string(test.ExpectedAmpResponse), string(recorder.Body.Bytes()), "Not the expected response. Test file: %s", filename) + assert.JSONEq(t, string(test.ExpectedAmpResponse), recorder.Body.String(), "Not the expected response. Test file: %s", filename) } else { assert.Equal(t, test.ExpectedErrorMessage, recorder.Body.String(), filename) } } if test.ExpectedValidatedBidReq != nil { // compare as json to ignore whitespace and ext field ordering - actualJson, err := json.Marshal(ex.actualValidatedBidReq) + actualJson, err := jsonutil.Marshal(ex.actualValidatedBidReq) if assert.NoError(t, err, "Error converting actual bid request to json. Test file: %s", filename) { assert.JSONEq(t, string(test.ExpectedValidatedBidReq), string(actualJson), "Not the expected validated request. Test file: %s", filename) } @@ -148,11 +151,6 @@ func TestAccountErrors(t *testing.T) { storedReqID: "1", filename: "account-malformed/malformed-acct.json", }, - { - description: "Blocked account", - storedReqID: "1", - filename: "blacklisted/blacklisted-site-publisher.json", - }, } for _, tt := range tests { @@ -162,16 +160,14 @@ func TestAccountErrors(t *testing.T) { } test := testCase{} - if !assert.NoError(t, json.Unmarshal(fileJsonData, &test), "Failed to unmarshal data from file: %s. Error: %v", tt.filename, err) { + if !assert.NoError(t, jsonutil.UnmarshalValid(fileJsonData, &test), "Failed to unmarshal data from file: %s. Error: %v", tt.filename, err) { continue } test.StoredRequest = map[string]json.RawMessage{tt.storedReqID: test.BidRequest} test.endpointType = AMP_ENDPOINT cfg := &config.Configuration{ - BlacklistedAccts: []string{"bad_acct"}, - BlacklistedAcctMap: map[string]bool{"bad_acct": true}, - MaxRequestSize: maxSize, + MaxRequestSize: maxSize, } cfg.MarshalAccountDefaults() @@ -210,7 +206,7 @@ func TestAMPPageInfo(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -314,7 +310,7 @@ func TestGDPRConsent(t *testing.T) { GDPR: config.GDPR{Enabled: true}, }, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -330,7 +326,7 @@ func TestGDPRConsent(t *testing.T) { // Parse Response var response AmpResponse - if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { + if err := jsonutil.UnmarshalValid(responseRecorder.Body.Bytes(), &response); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) } @@ -346,7 +342,7 @@ func TestGDPRConsent(t *testing.T) { return } var ue openrtb_ext.ExtUser - err = json.Unmarshal(result.User.Ext, &ue) + err = jsonutil.UnmarshalValid(result.User.Ext, &ue) if !assert.NoError(t, err, test.description+":deserialize") { return } @@ -361,7 +357,7 @@ func TestGDPRConsent(t *testing.T) { // Parse Resonse var responseLegacy AmpResponse - if err := json.Unmarshal(responseRecorderLegacy.Body.Bytes(), &responseLegacy); err != nil { + if err := jsonutil.UnmarshalValid(responseRecorderLegacy.Body.Bytes(), &responseLegacy); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) } @@ -377,7 +373,7 @@ func TestGDPRConsent(t *testing.T) { return } var ueLegacy openrtb_ext.ExtUser - err = json.Unmarshal(resultLegacy.User.Ext, &ueLegacy) + err = jsonutil.UnmarshalValid(resultLegacy.User.Ext, &ueLegacy) if !assert.NoError(t, err, test.description+":legacy:deserialize") { return } @@ -524,7 +520,7 @@ func TestOverrideWithParams(t *testing.T) { Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}, User: &openrtb2.User{Ext: json.RawMessage(`malformed`)}, }, - errorMsgs: []string{"invalid character 'm' looking for beginning of value"}, + errorMsgs: []string{"expect { or n, but found m"}, expectFatalErrors: true, }, }, @@ -572,7 +568,7 @@ func TestOverrideWithParams(t *testing.T) { User: &openrtb2.User{Ext: json.RawMessage(`{"prebid":{malformed}}`)}, Site: &openrtb2.Site{Ext: json.RawMessage(`{"amp":1}`)}, }, - errorMsgs: []string{"invalid character 'm' looking for beginning of object key string"}, + errorMsgs: []string{"expect \" after {, but found m"}, expectFatalErrors: true, }, }, @@ -738,7 +734,7 @@ func TestCCPAConsent(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -754,7 +750,7 @@ func TestCCPAConsent(t *testing.T) { // Parse Response var response AmpResponse - if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { + if err := jsonutil.UnmarshalValid(responseRecorder.Body.Bytes(), &response); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) } @@ -770,7 +766,7 @@ func TestCCPAConsent(t *testing.T) { return } var re openrtb_ext.ExtRegs - err = json.Unmarshal(result.Regs.Ext, &re) + err = jsonutil.UnmarshalValid(result.Regs.Ext, &re) if !assert.NoError(t, err, test.description+":deserialize") { return } @@ -852,7 +848,7 @@ func TestConsentWarnings(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -876,7 +872,7 @@ func TestConsentWarnings(t *testing.T) { // Parse Response var response AmpResponse - if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { + if err := jsonutil.UnmarshalValid(responseRecorder.Body.Bytes(), &response); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) } @@ -951,7 +947,7 @@ func TestNewAndLegacyConsentBothProvided(t *testing.T) { GDPR: config.GDPR{Enabled: true}, }, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -967,7 +963,7 @@ func TestNewAndLegacyConsentBothProvided(t *testing.T) { // Parse Response var response AmpResponse - if err := json.Unmarshal(responseRecorder.Body.Bytes(), &response); err != nil { + if err := jsonutil.UnmarshalValid(responseRecorder.Body.Bytes(), &response); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) } @@ -983,7 +979,7 @@ func TestNewAndLegacyConsentBothProvided(t *testing.T) { return } var ue openrtb_ext.ExtUser - err = json.Unmarshal(result.User.Ext, &ue) + err = jsonutil.UnmarshalValid(result.User.Ext, &ue) if !assert.NoError(t, err, test.description+":deserialize") { return } @@ -1006,7 +1002,7 @@ func TestAMPSiteExt(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), nil, nil, openrtb_ext.BuildBidderMap(), @@ -1049,7 +1045,7 @@ func TestAmpBadRequests(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -1083,7 +1079,7 @@ func TestAmpDebug(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -1104,7 +1100,7 @@ func TestAmpDebug(t *testing.T) { } var response AmpResponse - if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil { + if err := jsonutil.UnmarshalValid(recorder.Body.Bytes(), &response); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) } @@ -1139,7 +1135,7 @@ func TestInitAmpTargetingAndCache(t *testing.T) { { name: "malformed", request: &openrtb2.BidRequest{Ext: json.RawMessage("malformed")}, - expectedErrs: []string{"invalid character 'm' looking for beginning of value"}, + expectedErrs: []string{"expect { or n, but found m"}, }, { name: "nil", @@ -1219,7 +1215,7 @@ func TestQueryParamOverrides(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -1245,12 +1241,12 @@ func TestQueryParamOverrides(t *testing.T) { } var response AmpResponse - if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil { + if err := jsonutil.UnmarshalValid(recorder.Body.Bytes(), &response); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) } var resolvedRequest openrtb2.BidRequest - err := json.Unmarshal(response.ORTB2.Ext.Debug.ResolvedRequest, &resolvedRequest) + err := jsonutil.UnmarshalValid(response.ORTB2.Ext.Debug.ResolvedRequest, &resolvedRequest) assert.NoError(t, err, "resolved request should have a correct format") if resolvedRequest.TMax != timeout { t.Errorf("Expected TMax to equal timeout (%d), got: %d", timeout, resolvedRequest.TMax) @@ -1377,7 +1373,7 @@ func (s formatOverrideSpec) execute(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -1396,11 +1392,11 @@ func (s formatOverrideSpec) execute(t *testing.T) { t.Errorf("Request was: %s", string(requests["1"])) } var response AmpResponse - if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil { + if err := jsonutil.UnmarshalValid(recorder.Body.Bytes(), &response); err != nil { t.Fatalf("Error unmarshalling response: %s", err.Error()) } var resolvedRequest openrtb2.BidRequest - err := json.Unmarshal(response.ORTB2.Ext.Debug.ResolvedRequest, &resolvedRequest) + err := jsonutil.UnmarshalValid(response.ORTB2.Ext.Debug.ResolvedRequest, &resolvedRequest) assert.NoError(t, err, "resolved request should have the correct format") formats := resolvedRequest.Imp[0].Banner.Format if len(formats) != len(s.expect) { @@ -1450,14 +1446,14 @@ func (m *mockAmpExchange) HoldAuction(ctx context.Context, auctionRequest *excha if len(auctionRequest.StoredAuctionResponses) > 0 { var seatBids []openrtb2.SeatBid - if err := json.Unmarshal(auctionRequest.StoredAuctionResponses[r.BidRequest.Imp[0].ID], &seatBids); err != nil { + if err := jsonutil.UnmarshalValid(auctionRequest.StoredAuctionResponses[r.BidRequest.Imp[0].ID], &seatBids); err != nil { return nil, err } response.SeatBid = seatBids } if r.BidRequest.Test == 1 { - resolvedRequest, err := json.Marshal(r.BidRequest) + resolvedRequest, err := jsonutil.Marshal(r.BidRequest) if err != nil { resolvedRequest = json.RawMessage("{}") } @@ -1516,7 +1512,7 @@ func getTestBidRequest(nilUser bool, userExt *openrtb_ext.ExtUser, nilRegs bool, var userExtData []byte if userExt != nil { var err error - userExtData, err = json.Marshal(userExt) + userExtData, err = jsonutil.Marshal(userExt) if err != nil { return nil, err } @@ -1533,7 +1529,7 @@ func getTestBidRequest(nilUser bool, userExt *openrtb_ext.ExtUser, nilRegs bool, var regsExtData []byte if regsExt != nil { var err error - regsExtData, err = json.Marshal(regsExt) + regsExtData, err = jsonutil.Marshal(regsExt) if err != nil { return nil, err } @@ -1545,7 +1541,7 @@ func getTestBidRequest(nilUser bool, userExt *openrtb_ext.ExtUser, nilRegs bool, Ext: regsExtData, } } - return json.Marshal(bidRequest) + return jsonutil.Marshal(bidRequest) } func TestSetEffectiveAmpPubID(t *testing.T) { @@ -1640,25 +1636,25 @@ type mockLogger struct { auctionObject *analytics.AuctionObject } -func newMockLogger(ao *analytics.AmpObject, aucObj *analytics.AuctionObject) analytics.PBSAnalyticsModule { +func newMockLogger(ao *analytics.AmpObject, aucObj *analytics.AuctionObject) analytics.Runner { return &mockLogger{ ampObject: ao, auctionObject: aucObj, } } -func (logger mockLogger) LogAuctionObject(ao *analytics.AuctionObject) { +func (logger mockLogger) LogAuctionObject(ao *analytics.AuctionObject, _ privacy.ActivityControl) { *logger.auctionObject = *ao } -func (logger mockLogger) LogVideoObject(vo *analytics.VideoObject) { +func (logger mockLogger) LogVideoObject(vo *analytics.VideoObject, _ privacy.ActivityControl) { } func (logger mockLogger) LogCookieSyncObject(cookieObject *analytics.CookieSyncObject) { } func (logger mockLogger) LogSetUIDObject(uuidObj *analytics.SetUIDObject) { } -func (logger mockLogger) LogNotificationEventObject(uuidObj *analytics.NotificationEvent) { +func (logger mockLogger) LogNotificationEventObject(uuidObj *analytics.NotificationEvent, _ privacy.ActivityControl) { } -func (logger mockLogger) LogAmpObject(ao *analytics.AmpObject) { +func (logger mockLogger) LogAmpObject(ao *analytics.AmpObject, _ privacy.ActivityControl) { *logger.ampObject = *ao } @@ -1971,7 +1967,7 @@ func TestAmpAuctionResponseHeaders(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -2007,7 +2003,7 @@ func TestRequestWithTargeting(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), nil, nil, openrtb_ext.BuildBidderMap(), @@ -2196,7 +2192,7 @@ func TestValidAmpResponseWhenRequestRejected(t *testing.T) { assert.NoError(t, err, "Failed to read test file.") test := testCase{} - assert.NoError(t, json.Unmarshal(fileData, &test), "Failed to parse test file.") + assert.NoError(t, jsonutil.UnmarshalValid(fileData, &test), "Failed to parse test file.") request := httptest.NewRequest("GET", fmt.Sprintf("/openrtb2/auction/amp?%s", test.Query), nil) recorder := httptest.NewRecorder() @@ -2216,8 +2212,8 @@ func TestValidAmpResponseWhenRequestRejected(t *testing.T) { var actualAmpResp AmpResponse var expectedAmpResp AmpResponse - assert.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &actualAmpResp), "Unable to unmarshal actual AmpResponse.") - assert.NoError(t, json.Unmarshal(test.ExpectedAmpResponse, &expectedAmpResp), "Unable to unmarshal expected AmpResponse.") + assert.NoError(t, jsonutil.UnmarshalValid(recorder.Body.Bytes(), &actualAmpResp), "Unable to unmarshal actual AmpResponse.") + assert.NoError(t, jsonutil.UnmarshalValid(test.ExpectedAmpResponse, &expectedAmpResp), "Unable to unmarshal expected AmpResponse.") // validate modules data separately, because it has dynamic data if expectedAmpResp.ORTB2.Ext.Prebid == nil { @@ -2253,7 +2249,7 @@ func TestSendAmpResponse_LogsErrors(t *testing.T) { { description: "Error logged when bid.ext unmarshal fails", expectedErrors: []error{ - errors.New("Critical error while unpacking AMP targets: unexpected end of JSON input"), + errors.New("Critical error while unpacking AMP targets: expect { or n, but found \""), }, expectedStatus: http.StatusInternalServerError, writer: httptest.NewRecorder(), @@ -2328,9 +2324,9 @@ func TestSendAmpResponse_LogsErrors(t *testing.T) { account := &config.Account{DebugAllow: true} reqWrapper := openrtb_ext.RequestWrapper{BidRequest: test.request} - labels, ao = sendAmpResponse(test.writer, test.hookExecutor, &exchange.AuctionResponse{BidResponse: test.response}, &reqWrapper, account, labels, ao, nil) + _, ao = sendAmpResponse(test.writer, test.hookExecutor, &exchange.AuctionResponse{BidResponse: test.response}, &reqWrapper, account, labels, ao, nil) - assert.Equal(t, ao.Errors, test.expectedErrors, "Invalid errors.") + assert.Equal(t, test.expectedErrors, ao.Errors, "Invalid errors.") assert.Equal(t, test.expectedStatus, ao.Status, "Invalid HTTP response status.") }) } diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index aaa8c64a011..c94f3e50575 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -14,52 +14,53 @@ import ( "strings" "time" - "github.com/prebid/prebid-server/privacy" - "github.com/buger/jsonparser" "github.com/gofrs/uuid" "github.com/golang/glog" "github.com/julienschmidt/httprouter" gpplib "github.com/prebid/go-gpp" "github.com/prebid/go-gpp/constants" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/native1" - nativeRequests "github.com/prebid/openrtb/v19/native1/request" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/bidadjustment" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/ortb" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/native1" + nativeRequests "github.com/prebid/openrtb/v20/native1/request" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/bidadjustment" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/ortb" + "github.com/prebid/prebid-server/v2/privacy" "golang.org/x/net/publicsuffix" jsonpatch "gopkg.in/evanphx/json-patch.v4" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/lmt" - "github.com/prebid/prebid-server/schain" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/stored_responses" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/httputil" - "github.com/prebid/prebid-server/util/iputil" - "github.com/prebid/prebid-server/util/uuidutil" - "github.com/prebid/prebid-server/version" + accountService "github.com/prebid/prebid-server/v2/account" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/prebid_cache_client" + "github.com/prebid/prebid-server/v2/privacy/ccpa" + "github.com/prebid/prebid-server/v2/privacy/lmt" + "github.com/prebid/prebid-server/v2/schain" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/v2/stored_responses" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/httputil" + "github.com/prebid/prebid-server/v2/util/iputil" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/uuidutil" + "github.com/prebid/prebid-server/v2/version" ) const storedRequestTimeoutMillis = 50 const ampChannel = "amp" const appChannel = "app" +const secCookieDeprecation = "Sec-Cookie-Deprecation" var ( dntKey string = http.CanonicalHeaderKey("DNT") @@ -69,13 +70,16 @@ var ( ) var accountIdSearchPath = [...]struct { - isApp bool - key []string + isApp bool + isDOOH bool + key []string }{ - {true, []string{"app", "publisher", "ext", openrtb_ext.PrebidExtKey, "parentAccount"}}, - {true, []string{"app", "publisher", "id"}}, - {false, []string{"site", "publisher", "ext", openrtb_ext.PrebidExtKey, "parentAccount"}}, - {false, []string{"site", "publisher", "id"}}, + {true, false, []string{"app", "publisher", "ext", openrtb_ext.PrebidExtKey, "parentAccount"}}, + {true, false, []string{"app", "publisher", "id"}}, + {false, false, []string{"site", "publisher", "ext", openrtb_ext.PrebidExtKey, "parentAccount"}}, + {false, false, []string{"site", "publisher", "id"}}, + {false, true, []string{"dooh", "publisher", "ext", openrtb_ext.PrebidExtKey, "parentAccount"}}, + {false, true, []string{"dooh", "publisher", "id"}}, } func NewEndpoint( @@ -86,7 +90,7 @@ func NewEndpoint( accounts stored_requests.AccountFetcher, cfg *config.Configuration, metricsEngine metrics.MetricsEngine, - pbsAnalytics analytics.PBSAnalyticsModule, + analyticsRunner analytics.Runner, disabledBidders map[string]string, defReqJSON []byte, bidderMap map[string]openrtb_ext.BidderName, @@ -98,7 +102,7 @@ func NewEndpoint( return nil, errors.New("NewEndpoint requires non-nil arguments.") } - defRequest := defReqJSON != nil && len(defReqJSON) > 0 + defRequest := len(defReqJSON) > 0 ipValidator := iputil.PublicNetworkIPValidator{ IPv4PrivateNetworks: cfg.RequestValidation.IPv4PrivateNetworksParsed, @@ -114,7 +118,7 @@ func NewEndpoint( accounts, cfg, metricsEngine, - pbsAnalytics, + analyticsRunner, disabledBidders, defRequest, defReqJSON, @@ -124,9 +128,12 @@ func NewEndpoint( ipValidator, storedRespFetcher, hookExecutionPlanBuilder, - tmaxAdjustments}).Auction), nil + tmaxAdjustments, + openrtb_ext.NormalizeBidderName}).Auction), nil } +type normalizeBidderName func(name string) (openrtb_ext.BidderName, bool) + type endpointDeps struct { uuidGenerator uuidutil.UUIDGenerator ex exchange.Exchange @@ -136,7 +143,7 @@ type endpointDeps struct { accounts stored_requests.AccountFetcher cfg *config.Configuration metricsEngine metrics.MetricsEngine - analytics analytics.PBSAnalyticsModule + analytics analytics.Runner disabledBidders map[string]string defaultRequest bool defReqJSON []byte @@ -147,11 +154,10 @@ type endpointDeps struct { storedRespFetcher stored_requests.Fetcher hookExecutionPlanBuilder hooks.ExecutionPlanBuilder tmaxAdjustments *exchange.TmaxAdjustmentsPreprocessed + normalizeBidderName normalizeBidderName } func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { - deps.metricsEngine.RecordHttpCounter() - // Prebid Server interprets request.tmax to be the maximum amount of time that a caller is willing // to wait for bids. However, tmax may be defined in the Stored Request data. // @@ -175,11 +181,13 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http CookieFlag: metrics.CookieFlagUnknown, RequestStatus: metrics.RequestStatusOK, } + + activityControl := privacy.ActivityControl{} defer func() { deps.metricsEngine.RecordRequest(labels) recordRejectedBids(labels.PubID, ao.SeatNonBid, deps.metricsEngine) deps.metricsEngine.RecordRequestTime(labels, time.Since(start)) - deps.analytics.LogAuctionObject(&ao) + deps.analytics.LogAuctionObject(&ao, activityControl) }() w.Header().Set("X-Prebid", version.BuildXPrebidHeader(version.Ver)) @@ -197,7 +205,9 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR) - activityControl := privacy.NewActivityControl(&account.Privacy) + activityControl = privacy.NewActivityControl(&account.Privacy) + + hookExecutor.SetActivityControl(activityControl) ctx := r.Context() @@ -303,7 +313,7 @@ func setSeatNonBidRaw(request *openrtb_ext.RequestWrapper, auctionResponse *exch // by HoldAuction response := auctionResponse.BidResponse respExt := &openrtb_ext.ExtBidResponse{} - if err := json.Unmarshal(response.Ext, &respExt); err != nil { + if err := jsonutil.Unmarshal(response.Ext, &respExt); err != nil { return err } if setSeatNonBid(respExt, request, auctionResponse) { @@ -446,7 +456,7 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric requestJson, rejectErr := hookExecutor.ExecuteEntrypointStage(httpRequest, requestJson) if rejectErr != nil { errs = []error{rejectErr} - if err = json.Unmarshal(requestJson, req.BidRequest); err != nil { + if err = jsonutil.UnmarshalValid(requestJson, req.BidRequest); err != nil { glog.Errorf("Failed to unmarshal BidRequest during entrypoint rejection: %s", err) } return @@ -465,12 +475,16 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric return } - accountId, isAppReq, errs := getAccountIdFromRawRequest(hasStoredBidRequest, storedRequests[storedBidRequestId], requestJson) + accountId, isAppReq, isDOOHReq, errs := getAccountIdFromRawRequest(hasStoredBidRequest, storedRequests[storedBidRequestId], requestJson) // fill labels here in order to pass correct metrics in case of errors if isAppReq { labels.Source = metrics.DemandApp labels.RType = metrics.ReqTypeORTB2App labels.PubID = accountId + } else if isDOOHReq { + labels.Source = metrics.DemandDOOH + labels.RType = metrics.ReqTypeORTB2DOOH + labels.PubID = accountId } else { // is Site request labels.Source = metrics.DemandWeb labels.PubID = accountId @@ -489,7 +503,7 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric requestJson, rejectErr = hookExecutor.ExecuteRawAuctionStage(requestJson) if rejectErr != nil { errs = []error{rejectErr} - if err = json.Unmarshal(requestJson, req.BidRequest); err != nil { + if err = jsonutil.UnmarshalValid(requestJson, req.BidRequest); err != nil { glog.Errorf("Failed to unmarshal BidRequest during raw auction stage rejection: %s", err) } return @@ -512,13 +526,7 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric return } - //Stored auction responses should be processed after stored requests due to possible impression modification - storedAuctionResponses, storedBidResponses, bidderImpReplaceImpId, errs = stored_responses.ProcessStoredResponses(ctx, requestJson, deps.storedRespFetcher, deps.bidderMap) - if len(errs) > 0 { - return nil, nil, nil, nil, nil, nil, errs - } - - if err := json.Unmarshal(requestJson, req.BidRequest); err != nil { + if err := jsonutil.UnmarshalValid(requestJson, req.BidRequest); err != nil { errs = []error{err} return } @@ -549,8 +557,14 @@ func (deps *endpointDeps) parseRequest(httpRequest *http.Request, labels *metric lmt.ModifyForIOS(req.BidRequest) + //Stored auction responses should be processed after stored requests due to possible impression modification + storedAuctionResponses, storedBidResponses, bidderImpReplaceImpId, errs = stored_responses.ProcessStoredResponses(ctx, req, deps.storedRespFetcher) + if len(errs) > 0 { + return nil, nil, nil, nil, nil, nil, errs + } + hasStoredResponses := len(storedAuctionResponses) > 0 - errL := deps.validateRequest(req, false, hasStoredResponses, storedBidResponses, hasStoredBidRequest) + errL := deps.validateRequest(account, httpRequest, req, false, hasStoredResponses, storedBidResponses, hasStoredBidRequest) if len(errL) > 0 { errs = append(errs, errL...) } @@ -623,7 +637,7 @@ func mergeBidderParams(req *openrtb_ext.RequestWrapper) error { } bidderParams := map[string]map[string]json.RawMessage{} - if err := json.Unmarshal(bidderParamsJson, &bidderParams); err != nil { + if err := jsonutil.Unmarshal(bidderParamsJson, &bidderParams); err != nil { return nil } @@ -666,7 +680,7 @@ func mergeBidderParamsImpExt(impExt *openrtb_ext.ImpExt, reqExtParams map[string impExtBidderMap := map[string]json.RawMessage{} if len(impExtBidder) > 0 { - if err := json.Unmarshal(impExtBidder, &impExtBidderMap); err != nil { + if err := jsonutil.Unmarshal(impExtBidder, &impExtBidderMap); err != nil { continue } } @@ -680,7 +694,7 @@ func mergeBidderParamsImpExt(impExt *openrtb_ext.ImpExt, reqExtParams map[string } if modified { - impExtBidderJson, err := json.Marshal(impExtBidderMap) + impExtBidderJson, err := jsonutil.Marshal(impExtBidderMap) if err != nil { return fmt.Errorf("error marshalling ext.BIDDER: %s", err.Error()) } @@ -714,7 +728,7 @@ func mergeBidderParamsImpExtPrebid(impExt *openrtb_ext.ImpExt, reqExtParams map[ impExtPrebidBidderMap := map[string]json.RawMessage{} if len(impExtPrebidBidder) > 0 { - if err := json.Unmarshal(impExtPrebidBidder, &impExtPrebidBidderMap); err != nil { + if err := jsonutil.Unmarshal(impExtPrebidBidder, &impExtPrebidBidderMap); err != nil { continue } } @@ -728,7 +742,7 @@ func mergeBidderParamsImpExtPrebid(impExt *openrtb_ext.ImpExt, reqExtParams map[ } if modified { - impExtPrebidBidderJson, err := json.Marshal(impExtPrebidBidderMap) + impExtPrebidBidderJson, err := jsonutil.Marshal(impExtPrebidBidderMap) if err != nil { return fmt.Errorf("error marshalling ext.prebid.bidder.BIDDER: %s", err.Error()) } @@ -744,7 +758,7 @@ func mergeBidderParamsImpExtPrebid(impExt *openrtb_ext.ImpExt, reqExtParams map[ return nil } -func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp bool, hasStoredResponses bool, storedBidResp stored_responses.ImpBidderStoredResp, hasStoredBidRequest bool) []error { +func (deps *endpointDeps) validateRequest(account *config.Account, httpReq *http.Request, req *openrtb_ext.RequestWrapper, isAmp bool, hasStoredResponses bool, storedBidResp stored_responses.ImpBidderStoredResp, hasStoredBidRequest bool) []error { errL := []error{} if req.ID == "" { return []error{errors.New("request missing required field: \"id\"")} @@ -771,7 +785,7 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp } } - var aliases map[string]string + var requestAliases map[string]string reqExt, err := req.GetRequestExt() if err != nil { return []error{fmt.Errorf("request.ext is invalid: %v", err)} @@ -783,17 +797,17 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp } if reqPrebid != nil { - aliases = reqPrebid.Aliases + requestAliases = reqPrebid.Aliases - if err := deps.validateAliases(aliases); err != nil { + if err := deps.validateAliases(requestAliases); err != nil { return []error{err} } - if err := deps.validateAliasesGVLIDs(reqPrebid.AliasGVLIDs, aliases); err != nil { + if err := deps.validateAliasesGVLIDs(reqPrebid.AliasGVLIDs, requestAliases); err != nil { return []error{err} } - if err := deps.validateBidAdjustmentFactors(reqPrebid.BidAdjustmentFactors, aliases); err != nil { + if err := deps.validateBidAdjustmentFactors(reqPrebid.BidAdjustmentFactors, requestAliases); err != nil { return []error{err} } @@ -801,7 +815,7 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp return []error{err} } - if err := deps.validateEidPermissions(reqPrebid.Data, aliases); err != nil { + if err := deps.validateEidPermissions(reqPrebid.Data, requestAliases); err != nil { return []error{err} } @@ -818,8 +832,8 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp return []error{err} } - if (req.Site == nil && req.App == nil) || (req.Site != nil && req.App != nil) { - return append(errL, errors.New("request.site or request.app must be defined, but not both.")) + if err := validateExactlyOneInventoryType(req); err != nil { + return []error{err} } if errs := validateRequestExt(req); len(errs) != 0 { @@ -837,17 +851,21 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp return append(errL, err) } + if err := deps.validateDOOH(req); err != nil { + return append(errL, err) + } var gpp gpplib.GppContainer if req.BidRequest.Regs != nil && len(req.BidRequest.Regs.GPP) > 0 { - gpp, err = gpplib.Parse(req.BidRequest.Regs.GPP) - if err != nil { + var errs []error + gpp, errs = gpplib.Parse(req.BidRequest.Regs.GPP) + if len(errs) > 0 { errL = append(errL, &errortypes.Warning{ - Message: fmt.Sprintf("GPP consent string is invalid and will be ignored. (%v)", err), + Message: fmt.Sprintf("GPP consent string is invalid and will be ignored. (%v)", errs[0]), WarningCode: errortypes.InvalidPrivacyConsentWarningCode}) } } - if errs := deps.validateUser(req, aliases, gpp); errs != nil { + if errs := deps.validateUser(req, requestAliases, gpp); errs != nil { if len(errs) > 0 { errL = append(errL, errs...) } @@ -869,12 +887,16 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp return append(errL, err) } + if err := validateOrFillCDep(httpReq, req, account); err != nil { + errL = append(errL, err) + } + if ccpaPolicy, err := ccpa.ReadFromRequestWrapper(req, gpp); err != nil { errL = append(errL, err) if errortypes.ContainsFatalError([]error{err}) { return errL } - } else if _, err := ccpaPolicy.Parse(exchange.GetValidBidders(aliases)); err != nil { + } else if _, err := ccpaPolicy.Parse(exchange.GetValidBidders(requestAliases)); err != nil { if _, invalidConsent := err.(*errortypes.Warning); invalidConsent { errL = append(errL, &errortypes.Warning{ Message: fmt.Sprintf("CCPA consent is invalid and will be ignored. (%v)", err), @@ -897,7 +919,7 @@ func (deps *endpointDeps) validateRequest(req *openrtb_ext.RequestWrapper, isAmp } impIDs[imp.ID] = i - errs := deps.validateImp(imp, aliases, i, hasStoredResponses, storedBidResp) + errs := deps.validateImp(imp, requestAliases, i, hasStoredResponses, storedBidResp) if len(errs) > 0 { errL = append(errL, errs...) } @@ -956,7 +978,7 @@ func validateAndFillSourceTID(req *openrtb_ext.RequestWrapper, generateRequestID return errors.New("imp.ext.tid missing in the imp and error creating a random UID") } ie.SetTid(rawUUID.String()) - impWrapper.RebuildImpressionExt() + impWrapper.RebuildImp() } } @@ -964,11 +986,25 @@ func validateAndFillSourceTID(req *openrtb_ext.RequestWrapper, generateRequestID } func (deps *endpointDeps) validateBidAdjustmentFactors(adjustmentFactors map[string]float64, aliases map[string]string) error { + uniqueBidders := make(map[string]struct{}) for bidderToAdjust, adjustmentFactor := range adjustmentFactors { if adjustmentFactor <= 0 { return fmt.Errorf("request.ext.prebid.bidadjustmentfactors.%s must be a positive number. Got %f", bidderToAdjust, adjustmentFactor) } - if _, isBidder := deps.bidderMap[bidderToAdjust]; !isBidder { + + bidderName := bidderToAdjust + normalizedCoreBidder, ok := openrtb_ext.NormalizeBidderName(bidderToAdjust) + if ok { + bidderName = normalizedCoreBidder.String() + } + + if _, exists := uniqueBidders[bidderName]; exists { + return fmt.Errorf("cannot have multiple bidders that differ only in case style") + } else { + uniqueBidders[bidderName] = struct{}{} + } + + if _, isBidder := deps.bidderMap[bidderName]; !isBidder { if _, isAlias := aliases[bidderToAdjust]; !isAlias { return fmt.Errorf("request.ext.prebid.bidadjustmentfactors.%s is not a known bidder or alias", bidderToAdjust) } @@ -982,7 +1018,7 @@ func validateSChains(sChains []*openrtb_ext.ExtRequestPrebidSChain) error { return err } -func (deps *endpointDeps) validateEidPermissions(prebid *openrtb_ext.ExtRequestPrebidData, aliases map[string]string) error { +func (deps *endpointDeps) validateEidPermissions(prebid *openrtb_ext.ExtRequestPrebidData, requestAliases map[string]string) error { if prebid == nil { return nil } @@ -1002,7 +1038,7 @@ func (deps *endpointDeps) validateEidPermissions(prebid *openrtb_ext.ExtRequestP return fmt.Errorf(`request.ext.prebid.data.eidpermissions[%d] missing or empty required field: "bidders"`, i) } - if err := validateBidders(eid.Bidders, deps.bidderMap, aliases); err != nil { + if err := deps.validateBidders(eid.Bidders, deps.bidderMap, requestAliases); err != nil { return fmt.Errorf(`request.ext.prebid.data.eidpermissions[%d] contains %v`, i, err) } } @@ -1010,15 +1046,16 @@ func (deps *endpointDeps) validateEidPermissions(prebid *openrtb_ext.ExtRequestP return nil } -func validateBidders(bidders []string, knownBidders map[string]openrtb_ext.BidderName, knownAliases map[string]string) error { +func (deps *endpointDeps) validateBidders(bidders []string, knownBidders map[string]openrtb_ext.BidderName, knownRequestAliases map[string]string) error { for _, bidder := range bidders { if bidder == "*" { if len(bidders) > 1 { return errors.New(`bidder wildcard "*" mixed with specific bidders`) } } else { - _, isCoreBidder := knownBidders[bidder] - _, isAlias := knownAliases[bidder] + bidderNormalized, _ := deps.normalizeBidderName(bidder) + _, isCoreBidder := knownBidders[bidderNormalized.String()] + _, isAlias := knownRequestAliases[bidder] if !isCoreBidder && !isAlias { return fmt.Errorf(`unrecognized bidder "%v"`, bidder) } @@ -1126,10 +1163,10 @@ func validateVideo(video *openrtb2.Video, impIndex int) error { // The following fields were previously uints in the OpenRTB library we use, but have // since been changed to ints. We decided to maintain the non-negative check. - if video.W < 0 { + if video.W != nil && *video.W < 0 { return fmt.Errorf("request.imp[%d].video.w must be a positive number", impIndex) } - if video.H < 0 { + if video.H != nil && *video.H < 0 { return fmt.Errorf("request.imp[%d].video.h must be a positive number", impIndex) } if video.MinBitRate < 0 { @@ -1179,7 +1216,7 @@ func fillAndValidateNative(n *openrtb2.Native, impIndex int) error { return fmt.Errorf("request.imp[%d].native missing required property \"request\"", impIndex) } var nativePayload nativeRequests.Request - if err := json.Unmarshal(json.RawMessage(n.Request), &nativePayload); err != nil { + if err := jsonutil.UnmarshalValid(json.RawMessage(n.Request), &nativePayload); err != nil { return err } @@ -1196,7 +1233,7 @@ func fillAndValidateNative(n *openrtb2.Native, impIndex int) error { return err } - serialized, err := json.Marshal(nativePayload) + serialized, err := jsonutil.Marshal(nativePayload) if err != nil { return err } @@ -1515,10 +1552,8 @@ func (deps *endpointDeps) validateImpExt(imp *openrtb_ext.ImpWrapper, aliases ma return []error{fmt.Errorf("request validation failed. The StoredAuctionResponse.ID field must be completely present with, or completely absent from, all impressions in request. No StoredAuctionResponse data found for request.imp[%d].ext.prebid \n", impIndex)} } - if len(storedBidResp) > 0 { - if err := validateStoredBidRespAndImpExtBidders(prebid.Bidder, storedBidResp, imp.ID); err != nil { - return []error{err} - } + if err := deps.validateStoredBidRespAndImpExtBidders(prebid, storedBidResp, imp.ID); err != nil { + return []error{err} } errL := []error{} @@ -1768,6 +1803,18 @@ func (deps *endpointDeps) validateApp(req *openrtb_ext.RequestWrapper) error { return err } +func (deps *endpointDeps) validateDOOH(req *openrtb_ext.RequestWrapper) error { + if req.DOOH == nil { + return nil + } + + if req.DOOH.ID == "" && len(req.DOOH.VenueType) == 0 { + return errors.New("request.dooh should include at least one of request.dooh.id or request.dooh.venuetype.") + } + + return nil +} + func (deps *endpointDeps) validateUser(req *openrtb_ext.RequestWrapper, aliases map[string]string, gpp gpplib.GppContainer) []error { var errL []error @@ -1800,7 +1847,9 @@ func (deps *endpointDeps) validateUser(req *openrtb_ext.RequestWrapper, aliases return append(errL, errors.New(`request.user.ext.prebid requires a "buyeruids" property with at least one ID defined. If none exist, then request.user.ext.prebid should not be defined.`)) } for bidderName := range prebid.BuyerUIDs { - if _, ok := deps.bidderMap[bidderName]; !ok { + normalizedCoreBidder, _ := deps.normalizeBidderName(bidderName) + coreBidder := normalizedCoreBidder.String() + if _, ok := deps.bidderMap[coreBidder]; !ok { if _, ok := aliases[bidderName]; !ok { return append(errL, fmt.Errorf("request.user.ext.%s is neither a known bidder name nor an alias in request.ext.prebid.aliases", bidderName)) } @@ -1893,6 +1942,59 @@ func validateDevice(device *openrtb2.Device) error { return nil } +func validateOrFillCDep(httpReq *http.Request, req *openrtb_ext.RequestWrapper, account *config.Account) error { + if account == nil || !account.Privacy.PrivacySandbox.CookieDeprecation.Enabled { + return nil + } + + deviceExt, err := req.GetDeviceExt() + if err != nil { + return err + } + + if deviceExt.GetCDep() != "" { + return nil + } + + secCookieDeprecation := httpReq.Header.Get(secCookieDeprecation) + if secCookieDeprecation == "" { + return nil + } + if len(secCookieDeprecation) > 100 { + return &errortypes.Warning{ + Message: "request.device.ext.cdep must not exceed 100 characters", + WarningCode: errortypes.SecCookieDeprecationLenWarningCode, + } + } + + deviceExt.SetCDep(secCookieDeprecation) + return nil +} + +func validateExactlyOneInventoryType(reqWrapper *openrtb_ext.RequestWrapper) error { + + // Prep for mutual exclusion check + invTypeNumMatches := 0 + if reqWrapper.Site != nil { + invTypeNumMatches++ + } + if reqWrapper.App != nil { + invTypeNumMatches++ + } + if reqWrapper.DOOH != nil { + invTypeNumMatches++ + } + + if invTypeNumMatches == 0 { + return errors.New("One of request.site or request.app or request.dooh must be defined") + } else if invTypeNumMatches >= 2 { + return errors.New("No more than one of request.site or request.app or request.dooh can be defined") + } else { + return nil + } + +} + func validateOrFillChannel(reqWrapper *openrtb_ext.RequestWrapper, isAmp bool) error { requestExt, err := reqWrapper.GetRequestExt() if err != nil { @@ -1955,9 +2057,9 @@ func (deps *endpointDeps) setFieldsImplicitly(httpReq *http.Request, r *openrtb_ setDeviceImplicitly(httpReq, r, deps.privateNetworkIPValidator) - // Per the OpenRTB spec: A bid request must not contain both a Site and an App object. If neither are - // present, we'll assume it's a site request. - if r.App == nil { + // Per the OpenRTB spec: A bid request must not contain more than one of Site|App|DOOH + // Assume it's a site request if it's not declared as one of the other values + if r.App == nil && r.DOOH == nil { setSiteImplicitly(httpReq, r) } @@ -2036,7 +2138,7 @@ func getJsonSyntaxError(testJSON []byte) (bool, string) { } type jNode map[string]*JsonNode docErrdoc := &jNode{} - docErr := json.Unmarshal(testJSON, docErrdoc) + docErr := jsonutil.UnmarshalValid(testJSON, docErrdoc) if uerror, ok := docErr.(*json.SyntaxError); ok { err := fmt.Sprintf("%s at offset %v", uerror.Error(), uerror.Offset) return true, err @@ -2185,7 +2287,7 @@ func (deps *endpointDeps) processStoredRequests(requestJson []byte, impInfo []Im } } if len(resolvedImps) > 0 { - newImpJson, err := json.Marshal(resolvedImps) + newImpJson, err := jsonutil.Marshal(resolvedImps) if err != nil { return nil, nil, []error{err} } @@ -2205,7 +2307,7 @@ func parseImpInfo(requestJson []byte) (impData []ImpExtPrebidData, errs []error) impExtData, _, _, err := jsonparser.Get(imp, "ext", "prebid") var impExtPrebid openrtb_ext.ExtImpPrebid if impExtData != nil { - if err := json.Unmarshal(impExtData, &impExtPrebid); err != nil { + if err := jsonutil.Unmarshal(impExtData, &impExtPrebid); err != nil { errs = append(errs, err) } } @@ -2309,7 +2411,7 @@ func writeError(errs []error, w http.ResponseWriter, labels *metrics.Labels) boo metricsStatus := metrics.RequestStatusBadInput for _, err := range errs { erVal := errortypes.ReadCode(err) - if erVal == errortypes.BlacklistedAppErrorCode || erVal == errortypes.BlacklistedAcctErrorCode { + if erVal == errortypes.BlacklistedAppErrorCode || erVal == errortypes.AccountDisabledErrorCode { httpStatus = http.StatusServiceUnavailable metricsStatus = metrics.RequestStatusBlacklisted break @@ -2334,7 +2436,7 @@ func getAccountID(pub *openrtb2.Publisher) string { if pub != nil { if pub.Ext != nil { var pubExt openrtb_ext.ExtPublisher - err := json.Unmarshal(pub.Ext, &pubExt) + err := jsonutil.Unmarshal(pub.Ext, &pubExt) if err == nil && pubExt.Prebid != nil && pubExt.Prebid.ParentAccount != nil && *pubExt.Prebid.ParentAccount != "" { return *pubExt.Prebid.ParentAccount } @@ -2346,43 +2448,43 @@ func getAccountID(pub *openrtb2.Publisher) string { return metrics.PublisherUnknown } -func getAccountIdFromRawRequest(hasStoredRequest bool, storedRequest json.RawMessage, originalRequest []byte) (string, bool, []error) { +func getAccountIdFromRawRequest(hasStoredRequest bool, storedRequest json.RawMessage, originalRequest []byte) (string, bool, bool, []error) { request := originalRequest if hasStoredRequest { request = storedRequest } - accountId, isAppReq, err := searchAccountId(request) + accountId, isAppReq, isDOOHReq, err := searchAccountId(request) if err != nil { - return "", isAppReq, []error{err} + return "", isAppReq, isDOOHReq, []error{err} } // In case the stored request did not have account data we specifically search it in the original request if accountId == "" && hasStoredRequest { - accountId, _, err = searchAccountId(originalRequest) + accountId, _, _, err = searchAccountId(originalRequest) if err != nil { - return "", isAppReq, []error{err} + return "", isAppReq, isDOOHReq, []error{err} } } if accountId == "" { - return metrics.PublisherUnknown, isAppReq, nil + return metrics.PublisherUnknown, isAppReq, isDOOHReq, nil } - return accountId, isAppReq, nil + return accountId, isAppReq, isDOOHReq, nil } -func searchAccountId(request []byte) (string, bool, error) { +func searchAccountId(request []byte) (string, bool, bool, error) { for _, path := range accountIdSearchPath { accountId, exists, err := getStringValueFromRequest(request, path.key) if err != nil { - return "", path.isApp, err + return "", path.isApp, path.isDOOH, err } if exists { - return accountId, path.isApp, nil + return accountId, path.isApp, path.isDOOH, nil } } - return "", false, nil + return "", false, false, nil } func getStringValueFromRequest(request []byte, key []string) (string, bool, error) { @@ -2451,14 +2553,24 @@ func checkIfAppRequest(request []byte) (bool, error) { return false, nil } -func validateStoredBidRespAndImpExtBidders(bidderExts map[string]json.RawMessage, storedBidResp stored_responses.ImpBidderStoredResp, impId string) error { +func (deps *endpointDeps) validateStoredBidRespAndImpExtBidders(prebid *openrtb_ext.ExtImpPrebid, storedBidResp stored_responses.ImpBidderStoredResp, impId string) error { + if storedBidResp == nil && len(prebid.StoredBidResponse) == 0 { + return nil + } + + if storedBidResp == nil { + return generateStoredBidResponseValidationError(impId) + } if bidResponses, ok := storedBidResp[impId]; ok { - if len(bidResponses) != len(bidderExts) { + if len(bidResponses) != len(prebid.Bidder) { return generateStoredBidResponseValidationError(impId) } for bidderName := range bidResponses { - if _, present := bidderExts[bidderName]; !present { + if _, bidderNameOk := deps.normalizeBidderName(bidderName); !bidderNameOk { + return fmt.Errorf(`unrecognized bidder "%v"`, bidderName) + } + if _, present := prebid.Bidder[bidderName]; !present { return generateStoredBidResponseValidationError(impId) } } diff --git a/endpoints/openrtb2/auction_benchmark_test.go b/endpoints/openrtb2/auction_benchmark_test.go index 6750f0d693e..835d2920af4 100644 --- a/endpoints/openrtb2/auction_benchmark_test.go +++ b/endpoints/openrtb2/auction_benchmark_test.go @@ -10,19 +10,17 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/floors" - - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/experiment/adscert" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/macros" - metricsConfig "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/usersync" + analyticsBuild "github.com/prebid/prebid-server/v2/analytics/build" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/experiment/adscert" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/macros" + metricsConfig "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/v2/usersync" ) // benchmarkTestServer returns the header bidding test ad. This response was scraped from a real appnexus server response. @@ -97,7 +95,7 @@ func BenchmarkOpenrtbEndpoint(b *testing.B) { empty_fetcher.EmptyFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), - &floors.PriceFloorFetcher{}, + nil, ) endpoint, _ := NewEndpoint( @@ -108,7 +106,7 @@ func BenchmarkOpenrtbEndpoint(b *testing.B) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, nilMetrics, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, nil, @@ -150,12 +148,10 @@ func BenchmarkValidWholeExemplary(b *testing.B) { test.endpointType = OPENRTB_ENDPOINT cfg := &config.Configuration{ - MaxRequestSize: maxSize, - BlacklistedApps: test.Config.BlacklistedApps, - BlacklistedAppMap: test.Config.getBlacklistedAppMap(), - BlacklistedAccts: test.Config.BlacklistedAccounts, - BlacklistedAcctMap: test.Config.getBlackListedAccountMap(), - AccountRequired: test.Config.AccountRequired, + MaxRequestSize: maxSize, + BlacklistedApps: test.Config.BlacklistedApps, + BlacklistedAppMap: test.Config.getBlacklistedAppMap(), + AccountRequired: test.Config.AccountRequired, } auctionEndpointHandler, _, mockBidServers, mockCurrencyRatesServer, err := buildTestEndpoint(test, cfg) diff --git a/endpoints/openrtb2/auction_ow.go b/endpoints/openrtb2/auction_ow.go index 27fd86bb3f3..f9678dfeda0 100644 --- a/endpoints/openrtb2/auction_ow.go +++ b/endpoints/openrtb2/auction_ow.go @@ -6,12 +6,12 @@ import ( "strconv" "github.com/golang/glog" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/analytics/pubmatic" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/analytics/pubmatic" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // recordRejectedBids records the rejected bids and respective rejection reason code @@ -64,7 +64,7 @@ func UpdateResponseExtOW(bidResponse *openrtb2.BidResponse, ao analytics.Auction extBidResponse.OwLogInfo.Logger, _ = pubmatic.GetLogAuctionObjectAsURL(ao, rCtx, true, true) } - // TODO: uncomment after seatnonbid PR is merged https://github.com/prebid/prebid-server/pull/2505 + // TODO: uncomment after seatnonbid PR is merged https://github.com/prebid/prebid-server/v2/pull/2505 // if seatNonBids := updateSeatNoBid(rCtx, ao); len(seatNonBids) != 0 { // if extBidResponse.Prebid == nil { // extBidResponse.Prebid = &openrtb_ext.ExtResponsePrebid{} @@ -79,7 +79,7 @@ func UpdateResponseExtOW(bidResponse *openrtb2.BidResponse, ao analytics.Auction bidResponse.Ext, _ = json.Marshal(extBidResponse) } -// TODO: uncomment after seatnonbid PR is merged https://github.com/prebid/prebid-server/pull/2505 +// TODO: uncomment after seatnonbid PR is merged https://github.com/prebid/prebid-server/v2/pull/2505 // TODO: Move this to module once it gets []analytics.RejectedBid as param (submit it in vanilla) // func updateSeatNoBid(rCtx *models.RequestCtx, ao analytics.AuctionObject) []openrtb_ext.SeatNonBid { // seatNonBids := make([]openrtb_ext.SeatNonBid, 0, len(ao.RejectedBids)) diff --git a/endpoints/openrtb2/auction_ow_test.go b/endpoints/openrtb2/auction_ow_test.go index bbcfbae0a59..936c16969f1 100644 --- a/endpoints/openrtb2/auction_ow_test.go +++ b/endpoints/openrtb2/auction_ow_test.go @@ -6,16 +6,16 @@ import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/metrics" - metricsConfig "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" + "github.com/prebid/openrtb/v20/openrtb2" + analyticsBuild "github.com/prebid/prebid-server/v2/analytics/build" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/metrics" + metricsConfig "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/stored_requests/backends/empty_fetcher" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -70,7 +70,6 @@ func TestValidateImpExtOW(t *testing.T) { }, }, } - deps := &endpointDeps{ fakeUUIDGenerator{}, &nobidExchange{}, @@ -80,7 +79,7 @@ func TestValidateImpExtOW(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(8096)}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{"disabledbidder": "The bidder 'disabledbidder' has been disabled."}, false, []byte{}, @@ -91,6 +90,7 @@ func TestValidateImpExtOW(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, &exchange.TmaxAdjustmentsPreprocessed{}, + openrtb_ext.NormalizeBidderName, } for _, group := range testGroups { diff --git a/endpoints/openrtb2/auction_test.go b/endpoints/openrtb2/auction_test.go index 6206ac02ad4..c008a819512 100644 --- a/endpoints/openrtb2/auction_test.go +++ b/endpoints/openrtb2/auction_test.go @@ -18,30 +18,37 @@ import ( "time" "github.com/buger/jsonparser" + jsoniter "github.com/json-iterator/go" "github.com/julienschmidt/httprouter" - "github.com/prebid/openrtb/v19/native1" - nativeRequests "github.com/prebid/openrtb/v19/native1/request" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/analytics" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/metrics" - metricsConfig "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/stored_responses" - "github.com/prebid/prebid-server/util/iputil" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/native1" + nativeRequests "github.com/prebid/openrtb/v20/native1/request" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/analytics" + analyticsBuild "github.com/prebid/prebid-server/v2/analytics/build" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/metrics" + metricsConfig "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/v2/stored_responses" + "github.com/prebid/prebid-server/v2/util/iputil" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) const jsonFileExtension string = ".json" +func TestMain(m *testing.M) { + jsoniter.RegisterExtension(&jsonutil.RawMessageExtension{}) + os.Exit(m.Run()) +} + func TestJsonSampleRequests(t *testing.T) { testSuites := []struct { description string @@ -160,8 +167,6 @@ func runJsonBasedTest(t *testing.T, filename, desc string) { if test.Config != nil { cfg.BlacklistedApps = test.Config.BlacklistedApps cfg.BlacklistedAppMap = test.Config.getBlacklistedAppMap() - cfg.BlacklistedAccts = test.Config.BlacklistedAccounts - cfg.BlacklistedAcctMap = test.Config.getBlackListedAccountMap() cfg.AccountRequired = test.Config.AccountRequired } cfg.MarshalAccountDefaults() @@ -194,7 +199,7 @@ func runEndToEndTest(t *testing.T, auctionEndpointHandler httprouter.Handle, tes // Either assert bid response or expected error if len(test.ExpectedErrorMessage) > 0 { - assert.True(t, strings.HasPrefix(actualJsonBidResponse, test.ExpectedErrorMessage), "Actual: %s \nExpected: %s. Filename: %s \n", actualJsonBidResponse, test.ExpectedErrorMessage, testFile) + assert.Contains(t, actualJsonBidResponse, test.ExpectedErrorMessage, "Actual: %s \nExpected: %s. Filename: %s \n", actualJsonBidResponse, test.ExpectedErrorMessage, testFile) } if len(test.ExpectedBidResponse) > 0 { @@ -202,10 +207,10 @@ func runEndToEndTest(t *testing.T, auctionEndpointHandler httprouter.Handle, tes var actualBidResponse openrtb2.BidResponse var err error - err = json.Unmarshal(test.ExpectedBidResponse, &expectedBidResponse) + err = jsonutil.Unmarshal(test.ExpectedBidResponse, &expectedBidResponse) if assert.NoError(t, err, "Could not unmarshal expected bidResponse taken from test file.\n Test file: %s\n Error:%s\n", testFile, err) { - err = json.Unmarshal([]byte(actualJsonBidResponse), &actualBidResponse) - if assert.NoError(t, err, "Could not unmarshal actual bidResponse from auction.\n Test file: %s\n Error:%s\n", testFile, err) { + err = jsonutil.UnmarshalValid([]byte(actualJsonBidResponse), &actualBidResponse) + if assert.NoError(t, err, "Could not unmarshal actual bidResponse from auction.\n Test file: %s\n Error:%s\n actualJsonBidResponse: %s", testFile, err, actualJsonBidResponse) { assertBidResponseEqual(t, test, testFile, expectedBidResponse, actualBidResponse) } } @@ -224,13 +229,13 @@ func compareWarnings(t *testing.T, expectedBidResponseExt, actualBidResponseExt } var expectedWarn []openrtb_ext.ExtBidderMessage - err = json.Unmarshal(expectedWarnings, &expectedWarn) + err = jsonutil.UnmarshalValid(expectedWarnings, &expectedWarn) if err != nil { assert.Fail(t, "error unmarshalling expected warnings data from response extension") } var actualWarn []openrtb_ext.ExtBidderMessage - err = json.Unmarshal(actualWarnings, &actualWarn) + err = jsonutil.UnmarshalValid(actualWarnings, &actualWarn) if err != nil { assert.Fail(t, "error unmarshalling actual warnings data from response extension") } @@ -449,7 +454,7 @@ func TestExplicitUserId(t *testing.T) { empty_fetcher.EmptyFetcher{}, cfg, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -478,8 +483,8 @@ func TestExplicitUserId(t *testing.T) { // processes aliases before it processes stored imps. Changing that order // would probably cause this test to fail. func TestBadAliasRequests(t *testing.T) { - doBadAliasRequest(t, "sample-requests/invalid-stored/bad_stored_imp.json", "Invalid request: Invalid JSON in Default Request Settings: invalid character '\"' after object key:value pair at offset 51\n") - doBadAliasRequest(t, "sample-requests/invalid-stored/bad_incoming_imp.json", "Invalid request: Invalid JSON in Incoming Request: invalid character '\"' after object key:value pair at offset 230\n") + doBadAliasRequest(t, "sample-requests/invalid-stored/bad_stored_imp.json", "Invalid request: Invalid JSON Document\n") + doBadAliasRequest(t, "sample-requests/invalid-stored/bad_incoming_imp.json", "Invalid request: Invalid JSON Document\n") } // doBadAliasRequest() is a customized variation of doRequest(), above @@ -507,7 +512,7 @@ func doBadAliasRequest(t *testing.T, filename string, expectMsg string) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), disabledBidders, aliasJSON, bidderMap, @@ -562,7 +567,7 @@ func TestNilExchange(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), map[string]string{}, + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), empty_fetcher.EmptyFetcher{}, @@ -587,7 +592,7 @@ func TestNilValidator(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -613,7 +618,7 @@ func TestExchangeError(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -740,7 +745,7 @@ func TestImplicitIPsEndToEnd(t *testing.T) { empty_fetcher.EmptyFetcher{}, cfg, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -940,7 +945,7 @@ func TestImplicitDNTEndToEnd(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -1177,7 +1182,7 @@ func TestStoredRequests(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -1188,6 +1193,7 @@ func TestStoredRequests(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } testStoreVideoAttr := []bool{true, true, false, false, false} @@ -1541,6 +1547,81 @@ func TestMergeBidderParamsImpExtPrebid(t *testing.T) { } } +func TestValidateExactlyOneInventoryType(t *testing.T) { + + testCases := []struct { + description string + givenRequestWrapper *openrtb_ext.RequestWrapper + expectedError error + }{ + { + description: "None provided - invalid", + givenRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{}}, + expectedError: errors.New("One of request.site or request.app or request.dooh must be defined"), + }, + { + description: "Only site provided", + givenRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + Site: &openrtb2.Site{}, + }}, + expectedError: nil, + }, + { + description: "Only app provided", + givenRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + App: &openrtb2.App{}, + }}, + expectedError: nil, + }, + { + description: "Only dooh provided", + givenRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + DOOH: &openrtb2.DOOH{}, + }}, + expectedError: nil, + }, + { + description: "Two provided (site+app) - invalid", + givenRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + Site: &openrtb2.Site{}, + App: &openrtb2.App{}, + }}, + expectedError: errors.New("No more than one of request.site or request.app or request.dooh can be defined"), + }, + { + description: "Two provided (site+dooh) - invalid", + givenRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + Site: &openrtb2.Site{}, + DOOH: &openrtb2.DOOH{}, + }}, + expectedError: errors.New("No more than one of request.site or request.app or request.dooh can be defined"), + }, + { + description: "Two provided (app+dooh) - invalid", + givenRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + App: &openrtb2.App{}, + DOOH: &openrtb2.DOOH{}, + }}, + expectedError: errors.New("No more than one of request.site or request.app or request.dooh can be defined"), + }, + { + description: "Three provided - invalid", + givenRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + Site: &openrtb2.Site{}, + App: &openrtb2.App{}, + DOOH: &openrtb2.DOOH{}, + }}, + expectedError: errors.New("No more than one of request.site or request.app or request.dooh can be defined"), + }, + } + + for _, test := range testCases { + error := validateExactlyOneInventoryType(test.givenRequestWrapper) + assert.Equalf(t, test.expectedError, error, "Error doesn't match: %s\n", test.description) + } + +} + func TestValidateRequest(t *testing.T) { deps := &endpointDeps{ fakeUUIDGenerator{}, @@ -1551,7 +1632,7 @@ func TestValidateRequest(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -1562,12 +1643,15 @@ func TestValidateRequest(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } testCases := []struct { description string givenIsAmp bool givenRequestWrapper *openrtb_ext.RequestWrapper + givenHttpRequest *http.Request + givenAccount *config.Account expectedErrorList []error expectedChannelObject *openrtb_ext.ExtRequestPrebidChannel }{ @@ -1725,10 +1809,70 @@ func TestValidateRequest(t *testing.T) { expectedErrorList: []error{}, expectedChannelObject: &openrtb_ext.ExtRequestPrebidChannel{Name: appChannel, Version: ""}, }, + { + description: "Minimum required site attributes missing", + givenRequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "Some-ID", + Site: &openrtb2.Site{}, + Imp: []openrtb2.Imp{ + { + ID: "Some-Imp-ID", + Banner: &openrtb2.Banner{ + Format: []openrtb2.Format{ + { + W: 600, + H: 500, + }, + { + W: 300, + H: 600, + }, + }, + }, + Ext: []byte(`{"appnexus":{"placementId": 12345678}}`), + }, + }, + }, + }, + expectedErrorList: []error{ + errors.New("request.site should include at least one of request.site.id or request.site.page."), + }, + }, + { + description: "Minimum required DOOH attributes missing", + givenRequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "Some-ID", + DOOH: &openrtb2.DOOH{}, + Imp: []openrtb2.Imp{ + { + ID: "Some-Imp-ID", + Banner: &openrtb2.Banner{ + Format: []openrtb2.Format{ + { + W: 600, + H: 500, + }, + { + W: 300, + H: 600, + }, + }, + }, + Ext: []byte(`{"appnexus":{"placementId": 12345678}}`), + }, + }, + }, + }, + expectedErrorList: []error{ + errors.New("request.dooh should include at least one of request.dooh.id or request.dooh.venuetype."), + }, + }, } for _, test := range testCases { - errorList := deps.validateRequest(test.givenRequestWrapper, test.givenIsAmp, false, nil, false) + errorList := deps.validateRequest(test.givenAccount, test.givenHttpRequest, test.givenRequestWrapper, test.givenIsAmp, false, nil, false) assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description) if len(errorList) == 0 { @@ -1772,7 +1916,7 @@ func TestValidateRequestExt(t *testing.T) { { description: "prebid cache - bids - wrong type", givenRequestExt: json.RawMessage(`{"prebid":{"cache":{"bids":true}}}`), - expectedErrors: []string{`json: cannot unmarshal bool into Go struct field ExtRequestPrebidCache.cache.bids of type openrtb_ext.ExtRequestPrebidCacheBids`}, + expectedErrors: []string{"cannot unmarshal openrtb_ext.ExtRequestPrebidCache.Bids: expect { or n, but found t"}, }, { description: "prebid cache - bids - provided", @@ -1786,7 +1930,7 @@ func TestValidateRequestExt(t *testing.T) { { description: "prebid cache - vastxml - wrong type", givenRequestExt: json.RawMessage(`{"prebid":{"cache":{"vastxml":true}}}`), - expectedErrors: []string{`json: cannot unmarshal bool into Go struct field ExtRequestPrebidCache.cache.vastxml of type openrtb_ext.ExtRequestPrebidCacheVAST`}, + expectedErrors: []string{"cannot unmarshal openrtb_ext.ExtRequestPrebidCache.VastXML: expect { or n, but found t"}, }, { description: "prebid cache - vastxml - provided", @@ -2270,7 +2414,7 @@ func TestSetIntegrationType(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -2281,6 +2425,7 @@ func TestSetIntegrationType(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } testCases := []struct { @@ -2336,7 +2481,7 @@ func TestStoredRequestGenerateUuid(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -2347,6 +2492,7 @@ func TestStoredRequestGenerateUuid(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } req := &openrtb2.BidRequest{} @@ -2418,7 +2564,7 @@ func TestStoredRequestGenerateUuid(t *testing.T) { newRequest, _, errList := deps.processStoredRequests(json.RawMessage(test.givenRawData), impInfo, storedRequests, storedImps, storedBidRequestId, hasStoredBidRequest) assert.Empty(t, errList, test.description) - if err := json.Unmarshal(newRequest, req); err != nil { + if err := jsonutil.UnmarshalValid(newRequest, req); err != nil { t.Errorf("processStoredRequests Error: %s", err.Error()) } if test.expectedCur != "" { @@ -2440,7 +2586,7 @@ func TestOversizedRequest(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(len(reqBody) - 1)}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -2451,6 +2597,7 @@ func TestOversizedRequest(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody)) @@ -2479,7 +2626,7 @@ func TestRequestSizeEdgeCase(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(len(reqBody))}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -2490,6 +2637,7 @@ func TestRequestSizeEdgeCase(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(reqBody)) @@ -2516,7 +2664,7 @@ func TestNoEncoding(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -2601,7 +2749,7 @@ func TestContentType(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -2818,7 +2966,7 @@ func TestValidateImpExt(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(8096)}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{"disabledbidder": "The bidder 'disabledbidder' has been disabled."}, false, []byte{}, @@ -2829,6 +2977,7 @@ func TestValidateImpExt(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } for _, group := range testGroups { @@ -2839,7 +2988,7 @@ func TestValidateImpExt(t *testing.T) { errs := deps.validateImpExt(impWrapper, nil, 0, false, nil) - assert.NoError(t, impWrapper.RebuildImpressionExt(), test.description+":rebuild_imp") + assert.NoError(t, impWrapper.RebuildImp(), test.description+":rebuild_imp") if len(test.expectedImpExt) > 0 { assert.JSONEq(t, test.expectedImpExt, string(imp.Ext), "imp.ext JSON does not match expected. Test: %s. %s\n", group.description, test.description) @@ -2873,7 +3022,7 @@ func TestCurrencyTrunc(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -2884,6 +3033,7 @@ func TestCurrencyTrunc(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } ui := int64(1) @@ -2905,7 +3055,7 @@ func TestCurrencyTrunc(t *testing.T) { Cur: []string{"USD", "EUR"}, } - errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) + errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) expectedError := errortypes.Warning{Message: "A prebid request can only process one currency. Taking the first currency in the list, USD, as the active currency"} assert.ElementsMatch(t, errL, []error{&expectedError}) @@ -2921,7 +3071,7 @@ func TestCCPAInvalid(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -2932,6 +3082,7 @@ func TestCCPAInvalid(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } ui := int64(1) @@ -2955,7 +3106,7 @@ func TestCCPAInvalid(t *testing.T) { }, } - errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) + errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) expectedWarning := errortypes.Warning{ Message: "CCPA consent is invalid and will be ignored. (request.regs.ext.us_privacy must contain 4 characters)", @@ -2973,7 +3124,7 @@ func TestNoSaleInvalid(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -2984,6 +3135,7 @@ func TestNoSaleInvalid(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } ui := int64(1) @@ -3008,7 +3160,7 @@ func TestNoSaleInvalid(t *testing.T) { Ext: json.RawMessage(`{"prebid": {"nosale": ["*", "appnexus"]} }`), } - errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) + errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) expectedError := errors.New("request.ext.prebid.nosale is invalid: can only specify all bidders if no other bidders are provided") assert.ElementsMatch(t, errL, []error{expectedError}) @@ -3028,7 +3180,7 @@ func TestValidateSourceTID(t *testing.T) { empty_fetcher.EmptyFetcher{}, cfg, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -3039,6 +3191,7 @@ func TestValidateSourceTID(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } ui := int64(1) @@ -3059,7 +3212,7 @@ func TestValidateSourceTID(t *testing.T) { }, } - deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) + deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) assert.NotEmpty(t, req.Source.TID, "Expected req.Source.TID to be filled with a randomly generated UID") } @@ -3073,7 +3226,7 @@ func TestSChainInvalid(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -3084,6 +3237,7 @@ func TestSChainInvalid(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } ui := int64(1) @@ -3105,7 +3259,7 @@ func TestSChainInvalid(t *testing.T) { Ext: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller1.com","sid":"00001","rid":"BidRequest1","hp":1}],"ver":"1.0"}}, {"bidders":["appnexus"],"schain":{"complete":1,"nodes":[{"asi":"directseller2.com","sid":"00002","rid":"BidRequest2","hp":1}],"ver":"1.0"}}]}}`), } - errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) + errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) expectedError := errors.New("request.ext.prebid.schains contains multiple schains for bidder appnexus; it must contain no more than one per bidder.") assert.ElementsMatch(t, errL, []error{expectedError}) @@ -3239,6 +3393,78 @@ func TestMapSChains(t *testing.T) { } } +func TestSearchAccountID(t *testing.T) { + // Correctness for lookup within Publisher object left to TestGetAccountID + // This however tests the expected lookup paths in outer site, app and dooh + testCases := []struct { + description string + request []byte + expectedAccID string + expectedError error + expectedIsAppReq bool + expectedIsSiteReq bool + expectedIsDOOHReq bool + }{ + { + description: "No publisher available", + request: []byte(`{}`), + expectedAccID: "", + expectedError: nil, + expectedIsAppReq: false, + expectedIsDOOHReq: false, + }, + { + description: "Publisher.ID doesn't exist", + request: []byte(`{"site":{"publisher":{}}}`), + expectedAccID: "", + expectedError: nil, + expectedIsAppReq: false, + expectedIsDOOHReq: false, + }, + { + description: "Publisher.ID not a string", + request: []byte(`{"site":{"publisher":{"id":42}}}`), + expectedAccID: "", + expectedError: errors.New("site.publisher.id must be a string"), + expectedIsAppReq: false, + expectedIsDOOHReq: false, + }, + { + description: "Publisher available in request.site", + request: []byte(`{"site":{"publisher":{"id":"42"}}}`), + expectedAccID: "42", + expectedError: nil, + expectedIsAppReq: false, + expectedIsDOOHReq: false, + }, + { + description: "Publisher available in request.app", + request: []byte(`{"app":{"publisher":{"id":"42"}}}`), + expectedAccID: "42", + expectedError: nil, + expectedIsAppReq: true, + expectedIsDOOHReq: false, + }, + { + description: "Publisher available in request.dooh", + request: []byte(`{"dooh":{"publisher":{"id":"42"}}}`), + expectedAccID: "42", + expectedError: nil, + expectedIsAppReq: false, + expectedIsDOOHReq: true, + }, + } + + for _, test := range testCases { + accountId, isAppReq, isDOOHReq, err := searchAccountId(test.request) + assert.Equal(t, test.expectedAccID, accountId, "searchAccountID should return expected account ID for test case: %s", test.description) + assert.Equal(t, test.expectedIsAppReq, isAppReq, "searchAccountID should return expected isAppReq for test case: %s", test.description) + assert.Equal(t, test.expectedIsDOOHReq, isDOOHReq, "searchAccountID should return expected isDOOHReq for test case: %s", test.description) + assert.Equal(t, test.expectedError, err, "searchAccountID should return expected error for test case: %s", test.description) + } + +} + func TestGetAccountID(t *testing.T) { testPubID := "test-pub" testParentAccount := "test-account" @@ -3247,7 +3473,7 @@ func TestGetAccountID(t *testing.T) { ParentAccount: &testParentAccount, }, } - testPubExtJSON, err := json.Marshal(testPubExt) + testPubExtJSON, err := jsonutil.Marshal(testPubExt) assert.NoError(t, err) testCases := []struct { @@ -3569,7 +3795,7 @@ func TestEidPermissionsInvalid(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -3580,6 +3806,7 @@ func TestEidPermissionsInvalid(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } ui := int64(1) @@ -3601,7 +3828,7 @@ func TestEidPermissionsInvalid(t *testing.T) { Ext: json.RawMessage(`{"prebid": {"data": {"eidpermissions": [{"source":"a", "bidders":[]}]} } }`), } - errL := deps.validateRequest(&openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) + errL := deps.validateRequest(nil, nil, &openrtb_ext.RequestWrapper{BidRequest: &req}, false, false, nil, false) expectedError := errors.New(`request.ext.prebid.data.eidpermissions[0] missing or empty required field: "bidders"`) assert.ElementsMatch(t, errL, []error{expectedError}) @@ -3648,6 +3875,13 @@ func TestValidateEidPermissions(t *testing.T) { }}}}, expectedError: nil, }, + { + description: "Valid - One - Case Insensitive", + request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{ + {Source: "sourceA", Bidders: []string{"A"}}, + }}}}, + expectedError: nil, + }, { description: "Valid - Many", request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{ @@ -3696,9 +3930,16 @@ func TestValidateEidPermissions(t *testing.T) { }}}}, expectedError: errors.New(`request.ext.prebid.data.eidpermissions[1] contains unrecognized bidder "z"`), }, + { + description: "Valid - Alias Case Sensitive", + request: &openrtb_ext.ExtRequest{Prebid: openrtb_ext.ExtRequestPrebid{Data: &openrtb_ext.ExtRequestPrebidData{EidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{ + {Source: "sourceA", Bidders: []string{"B"}}, + }}}}, + expectedError: errors.New(`request.ext.prebid.data.eidpermissions[0] contains unrecognized bidder "B"`), + }, } - endpoint := &endpointDeps{bidderMap: knownBidders} + endpoint := &endpointDeps{bidderMap: knownBidders, normalizeBidderName: fakeNormalizeBidderName} for _, test := range testCases { result := endpoint.validateEidPermissions(test.request.Prebid.Data, knownAliases) assert.Equal(t, test.expectedError, result, test.description) @@ -3734,6 +3975,13 @@ func TestValidateBidders(t *testing.T) { knownAliases: map[string]string{"c": "c"}, expectedError: nil, }, + { + description: "Valid - One Core Bidder - Case Insensitive", + bidders: []string{"A"}, + knownBidders: map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")}, + knownAliases: map[string]string{"c": "c"}, + expectedError: nil, + }, { description: "Valid - Many Core Bidders", bidders: []string{"a", "b"}, @@ -3748,6 +3996,13 @@ func TestValidateBidders(t *testing.T) { knownAliases: map[string]string{"c": "c"}, expectedError: nil, }, + { + description: "Valid - One Alias Bidder - Case Sensitive", + bidders: []string{"C"}, + knownBidders: map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")}, + knownAliases: map[string]string{"c": "c"}, + expectedError: errors.New(`unrecognized bidder "C"`), + }, { description: "Valid - Many Alias Bidders", bidders: []string{"c", "d"}, @@ -3769,13 +4024,6 @@ func TestValidateBidders(t *testing.T) { knownAliases: map[string]string{"c": "c"}, expectedError: errors.New(`unrecognized bidder "z"`), }, - { - description: "Invalid - Unknown Bidder Case Sensitive", - bidders: []string{"A"}, - knownBidders: map[string]openrtb_ext.BidderName{"a": openrtb_ext.BidderName("a")}, - knownAliases: map[string]string{"c": "c"}, - expectedError: errors.New(`unrecognized bidder "A"`), - }, { description: "Invalid - Unknown Bidder With Known Bidders", bidders: []string{"a", "c", "z"}, @@ -3806,8 +4054,9 @@ func TestValidateBidders(t *testing.T) { }, } + endpoint := &endpointDeps{normalizeBidderName: fakeNormalizeBidderName} for _, test := range testCases { - result := validateBidders(test.bidders, test.knownBidders, test.knownAliases) + result := endpoint.validateBidders(test.bidders, test.knownBidders, test.knownAliases) assert.Equal(t, test.expectedError, result, test.description) } } @@ -3823,7 +4072,7 @@ func TestIOS14EndToEnd(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -3886,7 +4135,7 @@ func TestAuctionWarnings(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -3897,6 +4146,7 @@ func TestAuctionWarnings(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } for _, test := range testCases { t.Run(test.name, func(t *testing.T) { @@ -3932,7 +4182,7 @@ func TestParseRequestParseImpInfoError(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(len(reqBody))}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -3943,6 +4193,7 @@ func TestParseRequestParseImpInfoError(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine) @@ -3954,7 +4205,7 @@ func TestParseRequestParseImpInfoError(t *testing.T) { assert.Nil(t, resReq, "Result request should be nil due to incorrect imp") assert.Nil(t, impExtInfoMap, "Impression info map should be nil due to incorrect imp") assert.Len(t, errL, 1, "One error should be returned") - assert.Contains(t, errL[0].Error(), "echovideoattrs of type bool", "Incorrect error message") + assert.Contains(t, errL[0].Error(), "cannot unmarshal openrtb_ext.Options.EchoVideoAttrs", "Incorrect error message") } func TestParseGzipedRequest(t *testing.T) { @@ -4012,7 +4263,7 @@ func TestParseGzipedRequest(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(50), Compression: config.Compression{Request: config.CompressionInfo{GZIP: false}}}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -4023,6 +4274,7 @@ func TestParseGzipedRequest(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine) @@ -4511,7 +4763,7 @@ func TestAuctionResponseHeaders(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, []byte{}, openrtb_ext.BuildBidderMap(), @@ -4612,7 +4864,7 @@ func TestParseRequestMergeBidderParams(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(len(test.givenRequestBody))}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -4623,6 +4875,7 @@ func TestParseRequestMergeBidderParams(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine) @@ -4634,20 +4887,20 @@ func TestParseRequestMergeBidderParams(t *testing.T) { assert.NoError(t, resReq.RebuildRequest()) var expIExt, iExt map[string]interface{} - err := json.Unmarshal(test.expectedImpExt, &expIExt) + err := jsonutil.UnmarshalValid(test.expectedImpExt, &expIExt) assert.Nil(t, err, "unmarshal() should return nil error") assert.NotNil(t, resReq.BidRequest.Imp[0].Ext, "imp[0].Ext should not be nil") - err = json.Unmarshal(resReq.BidRequest.Imp[0].Ext, &iExt) + err = jsonutil.UnmarshalValid(resReq.BidRequest.Imp[0].Ext, &iExt) assert.Nil(t, err, "unmarshal() should return nil error") assert.Equal(t, expIExt, iExt, "bidderparams in imp[].Ext should match") var eReqE, reqE map[string]interface{} - err = json.Unmarshal(test.expectedReqExt, &eReqE) + err = jsonutil.UnmarshalValid(test.expectedReqExt, &eReqE) assert.Nil(t, err, "unmarshal() should return nil error") - err = json.Unmarshal(resReq.BidRequest.Ext, &reqE) + err = jsonutil.UnmarshalValid(resReq.BidRequest.Ext, &reqE) assert.Nil(t, err, "unmarshal() should return nil error") assert.Equal(t, eReqE, reqE, "req.Ext should match") @@ -4715,7 +4968,7 @@ func TestParseRequestStoredResponses(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(len(test.givenRequestBody))}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -4726,6 +4979,7 @@ func TestParseRequestStoredResponses(t *testing.T) { &mockStoredResponseFetcher{mockStoredResponses}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine) @@ -4745,11 +4999,13 @@ func TestParseRequestStoredResponses(t *testing.T) { } func TestParseRequestStoredBidResponses(t *testing.T) { - bidRespId1 := json.RawMessage(`{"id": "resp_id1", "seatbid": [{"bid": [{"id": "bid_id1"}], "seat": "testBidder1"}], "bidid": "123", "cur": "USD"}`) - bidRespId2 := json.RawMessage(`{"id": "resp_id2", "seatbid": [{"bid": [{"id": "bid_id2"}], "seat": "testBidder2"}], "bidid": "124", "cur": "USD"}`) + bidRespId1 := json.RawMessage(`{"id": "resp_id1", "seatbid": [{"bid": [{"id": "bid_id1"}], "seat": "telaria"}], "bidid": "123", "cur": "USD"}`) + bidRespId2 := json.RawMessage(`{"id": "resp_id2", "seatbid": [{"bid": [{"id": "bid_id2"}], "seat": "amx"}], "bidid": "124", "cur": "USD"}`) + bidRespId3 := json.RawMessage(`{"id": "resp_id3", "seatbid": [{"bid": [{"id": "bid_id3"}], "seat": "APPNEXUS"}], "bidid": "125", "cur": "USD"}`) mockStoredBidResponses := map[string]json.RawMessage{ "bidResponseId1": bidRespId1, "bidResponseId2": bidRespId2, + "bidResponseId3": bidRespId3, } tests := []struct { @@ -4763,7 +5019,23 @@ func TestParseRequestStoredBidResponses(t *testing.T) { name: "req imp has valid stored bid response", givenRequestBody: validRequest(t, "imp-with-stored-bid-resp.json"), expectedStoredBidResponses: map[string]map[string]json.RawMessage{ - "imp-id1": {"testBidder1": bidRespId1}, + "imp-id1": {"telaria": bidRespId1}, + }, + expectedErrorCount: 0, + }, + { + name: "req imp has valid stored bid response with case not-matching bidder name", + givenRequestBody: validRequest(t, "imp-with-stored-bid-resp-case-not-matching-bidder-name.json"), + expectedStoredBidResponses: map[string]map[string]json.RawMessage{ + "imp-id3": {"appnexus": bidRespId3}, + }, + expectedErrorCount: 0, + }, + { + name: "req imp has valid stored bid response with case matching bidder name", + givenRequestBody: validRequest(t, "imp-with-stored-bid-resp-case-matching-bidder-name.json"), + expectedStoredBidResponses: map[string]map[string]json.RawMessage{ + "imp-id3": {"appnexus": bidRespId3}, }, expectedErrorCount: 0, }, @@ -4771,8 +5043,8 @@ func TestParseRequestStoredBidResponses(t *testing.T) { name: "req has two imps with valid stored bid responses", givenRequestBody: validRequest(t, "req-two-imps-stored-bid-responses.json"), expectedStoredBidResponses: map[string]map[string]json.RawMessage{ - "imp-id1": {"testBidder1": bidRespId1}, - "imp-id2": {"testBidder2": bidRespId2}, + "imp-id1": {"telaria": bidRespId1}, + "imp-id2": {"amx": bidRespId2}, }, expectedErrorCount: 0, }, @@ -4780,7 +5052,7 @@ func TestParseRequestStoredBidResponses(t *testing.T) { name: "req has two imps one with valid stored bid responses and another one without stored bid responses", givenRequestBody: validRequest(t, "req-two-imps-with-and-without-stored-bid-responses.json"), expectedStoredBidResponses: map[string]map[string]json.RawMessage{ - "imp-id2": {"testBidder2": bidRespId2}, + "imp-id2": {"amx": bidRespId2}, }, expectedErrorCount: 0, }, @@ -4788,7 +5060,13 @@ func TestParseRequestStoredBidResponses(t *testing.T) { name: "req has two imps with missing stored bid responses", givenRequestBody: validRequest(t, "req-two-imps-missing-stored-bid-response.json"), expectedStoredBidResponses: nil, - expectedErrorCount: 2, + expectedErrorCount: 1, + }, + { + name: "req imp has valid stored bid response with non existing bidder name", + givenRequestBody: validRequest(t, "imp-with-stored-bid-resp-non-existing-bidder-name.json"), + expectedStoredBidResponses: nil, + expectedErrorCount: 1, }, } for _, test := range tests { @@ -4803,25 +5081,26 @@ func TestParseRequestStoredBidResponses(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(len(test.givenRequestBody))}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, - map[string]openrtb_ext.BidderName{"testBidder1": "testBidder1", "testBidder2": "testBidder2"}, + map[string]openrtb_ext.BidderName{"telaria": "telaria", "amx": "amx", "appnexus": "appnexus"}, nil, nil, hardcodedResponseIPValidator{response: true}, &mockStoredResponseFetcher{mockStoredBidResponses}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine) req := httptest.NewRequest("POST", "/openrtb2/auction", strings.NewReader(test.givenRequestBody)) _, _, _, storedBidResponses, _, _, errL := deps.parseRequest(req, &metrics.Labels{}, hookExecutor) - if test.expectedErrorCount == 0 { + assert.Empty(t, errL) assert.Equal(t, test.expectedStoredBidResponses, storedBidResponses, "stored responses should match") } else { assert.Contains(t, errL[0].Error(), test.expectedError, "error should match") @@ -4840,7 +5119,7 @@ func TestValidateStoredResp(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -4851,11 +5130,14 @@ func TestValidateStoredResp(t *testing.T) { &mockStoredResponseFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } testCases := []struct { description string givenRequestWrapper *openrtb_ext.RequestWrapper + givenHttpRequest *http.Request + givenAccount *config.Account expectedErrorList []error hasStoredAuctionResponses bool storedBidResponses stored_responses.ImpBidderStoredResp @@ -5396,8 +5678,10 @@ func TestValidateStoredResp(t *testing.T) { } for _, test := range testCases { - errorList := deps.validateRequest(test.givenRequestWrapper, false, test.hasStoredAuctionResponses, test.storedBidResponses, false) - assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description) + t.Run(test.description, func(t *testing.T) { + errorList := deps.validateRequest(test.givenAccount, test.givenHttpRequest, test.givenRequestWrapper, false, test.hasStoredAuctionResponses, test.storedBidResponses, false) + assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description) + }) } } @@ -5497,11 +5781,11 @@ func TestValidResponseAfterExecutingStages(t *testing.T) { var actualExt openrtb_ext.ExtBidResponse var expectedExt openrtb_ext.ExtBidResponse - assert.NoError(t, json.Unmarshal(test.ExpectedBidResponse, &expectedResp), "Unable to unmarshal expected BidResponse.") - assert.NoError(t, json.Unmarshal(recorder.Body.Bytes(), &actualResp), "Unable to unmarshal actual BidResponse.") + assert.NoError(t, jsonutil.UnmarshalValid(test.ExpectedBidResponse, &expectedResp), "Unable to unmarshal expected BidResponse.") + assert.NoError(t, jsonutil.UnmarshalValid(recorder.Body.Bytes(), &actualResp), "Unable to unmarshal actual BidResponse.") if expectedResp.Ext != nil { - assert.NoError(t, json.Unmarshal(expectedResp.Ext, &expectedExt), "Unable to unmarshal expected ExtBidResponse.") - assert.NoError(t, json.Unmarshal(actualResp.Ext, &actualExt), "Unable to unmarshal actual ExtBidResponse.") + assert.NoError(t, jsonutil.UnmarshalValid(expectedResp.Ext, &expectedExt), "Unable to unmarshal expected ExtBidResponse.") + assert.NoError(t, jsonutil.UnmarshalValid(actualResp.Ext, &actualExt), "Unable to unmarshal actual ExtBidResponse.") } assertBidResponseEqual(t, test, tc.file, expectedResp, actualResp) @@ -5587,7 +5871,7 @@ func TestSendAuctionResponse_LogsErrors(t *testing.T) { ao := analytics.AuctionObject{} account := &config.Account{DebugAllow: true} - labels, ao = sendAuctionResponse(writer, test.hookExecutor, test.response, test.request, account, labels, ao) + _, ao = sendAuctionResponse(writer, test.hookExecutor, test.response, test.request, account, labels, ao) assert.Equal(t, ao.Errors, test.expectedErrors, "Invalid errors.") assert.Equal(t, test.expectedStatus, ao.Status, "Invalid HTTP response status.") @@ -5653,7 +5937,7 @@ func TestParseRequestMultiBid(t *testing.T) { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: int64(len(test.givenRequestBody))}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -5664,6 +5948,7 @@ func TestParseRequestMultiBid(t *testing.T) { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointAuction, deps.metricsEngine) @@ -5702,7 +5987,7 @@ func getObject(t *testing.T, filename, key string) json.RawMessage { assert.NoError(t, err, "Error jsonparsing root.mockBidRequest from file %s. Desc: %v.", filename, err) var obj json.RawMessage - err = json.Unmarshal(testBidRequest, &obj) + err = jsonutil.UnmarshalValid(testBidRequest, &obj) if err != nil { t.Fatalf("Failed to fetch object with key '%s' ... got error: %v", key, err) } @@ -5829,3 +6114,396 @@ func TestValidateAliases(t *testing.T) { }) } } + +func fakeNormalizeBidderName(name string) (openrtb_ext.BidderName, bool) { + return openrtb_ext.BidderName(strings.ToLower(name)), true +} + +func TestValidateOrFillCDep(t *testing.T) { + type args struct { + httpReq *http.Request + req *openrtb_ext.RequestWrapper + account config.Account + } + tests := []struct { + name string + args args + wantDeviceExt json.RawMessage + wantErr error + }{ + { + name: "account-nil", + wantDeviceExt: nil, + wantErr: nil, + }, + { + name: "cookie-deprecation-not-enabled", + args: args{ + httpReq: &http.Request{ + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, + }, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + }, + wantDeviceExt: nil, + wantErr: nil, + }, + { + name: "cookie-deprecation-disabled-explicitly", + args: args{ + httpReq: &http.Request{ + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, + }, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + account: config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: false, + }, + }, + }, + }, + }, + wantDeviceExt: nil, + wantErr: nil, + }, + { + name: "cookie-deprecation-enabled-header-not-present-in-request", + args: args{ + httpReq: &http.Request{}, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + account: config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, + }, + wantDeviceExt: nil, + wantErr: nil, + }, + { + name: "header-present-request-device-nil", + args: args{ + httpReq: &http.Request{ + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, + }, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{}, + }, + account: config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, + }, + wantDeviceExt: json.RawMessage(`{"cdep":"example_label_1"}`), + wantErr: nil, + }, + { + name: "header-present-request-device-ext-nil", + args: args{ + httpReq: &http.Request{ + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, + }, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: nil, + }, + }, + }, + account: config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, + }, + wantDeviceExt: json.RawMessage(`{"cdep":"example_label_1"}`), + wantErr: nil, + }, + { + name: "header-present-request-device-ext-not-nil", + args: args{ + httpReq: &http.Request{ + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, + }, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{"foo":"bar"}`), + }, + }, + }, + account: config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, + }, + wantDeviceExt: json.RawMessage(`{"cdep":"example_label_1","foo":"bar"}`), + wantErr: nil, + }, + { + name: "header-present-with-length-more-than-100", + args: args{ + httpReq: &http.Request{ + Header: http.Header{secCookieDeprecation: []string{"zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa"}}, + }, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{"foo":"bar"}`), + }, + }, + }, + account: config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, + }, + wantDeviceExt: json.RawMessage(`{"foo":"bar"}`), + wantErr: &errortypes.Warning{ + Message: "request.device.ext.cdep must not exceed 100 characters", + WarningCode: errortypes.SecCookieDeprecationLenWarningCode, + }, + }, + { + name: "header-present-request-device-ext-cdep-present", + args: args{ + httpReq: &http.Request{ + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, + }, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{"foo":"bar","cdep":"example_label_2"}`), + }, + }, + }, + account: config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, + }, + wantDeviceExt: json.RawMessage(`{"foo":"bar","cdep":"example_label_2"}`), + wantErr: nil, + }, + { + name: "header-present-request-device-ext-invalid", + args: args{ + httpReq: &http.Request{ + Header: http.Header{secCookieDeprecation: []string{"example_label_1"}}, + }, + req: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{ + Ext: json.RawMessage(`{`), + }, + }, + }, + account: config.Account{ + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + }, + }, + }, + }, + }, + wantDeviceExt: json.RawMessage(`{`), + wantErr: &errortypes.FailedToUnmarshal{ + Message: "expects \" or n, but found \x00", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateOrFillCDep(tt.args.httpReq, tt.args.req, &tt.args.account) + assert.Equal(t, tt.wantErr, err) + if tt.args.req != nil { + err := tt.args.req.RebuildRequest() + assert.NoError(t, err) + } + if tt.wantDeviceExt == nil { + if tt.args.req != nil && tt.args.req.Device != nil { + assert.Nil(t, tt.args.req.Device.Ext) + } + } else { + assert.Equal(t, string(tt.wantDeviceExt), string(tt.args.req.Device.Ext)) + } + }) + } +} + +func TestValidateRequestCookieDeprecation(t *testing.T) { + testCases := + []struct { + name string + givenAccount *config.Account + httpReq *http.Request + reqWrapper *openrtb_ext.RequestWrapper + wantErrs []error + wantCDep string + }{ + { + name: "header-with-length-less-than-100", + httpReq: func() *http.Request { + req := httptest.NewRequest("POST", "/openrtb2/auction", nil) + req.Header.Set(secCookieDeprecation, "sample-value") + return req + }(), + givenAccount: &config.Account{ + ID: "1", + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, + }, + }, + }, + }, + reqWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "Some-ID", + App: &openrtb2.App{}, + Imp: []openrtb2.Imp{ + { + ID: "Some-Imp-ID", + Banner: &openrtb2.Banner{ + Format: []openrtb2.Format{ + { + W: 600, + H: 500, + }, + { + W: 300, + H: 600, + }, + }, + }, + Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`), + }, + }, + }, + }, + wantErrs: []error{}, + wantCDep: "sample-value", + }, + { + name: "header-with-length-more-than-100", + httpReq: func() *http.Request { + req := httptest.NewRequest("POST", "/openrtb2/auction", nil) + req.Header.Set(secCookieDeprecation, "zjfXqGxXFI8yura8AhQl1DK2EMMmryrC8haEpAlwjoerrFfEo2MQTXUq6cSmLohI8gjsnkGU4oAzvXd4TTAESzEKsoYjRJ2zKxmEa") + return req + }(), + givenAccount: &config.Account{ + ID: "1", + Privacy: config.AccountPrivacy{ + PrivacySandbox: config.PrivacySandbox{ + CookieDeprecation: config.CookieDeprecation{ + Enabled: true, + TTLSec: 86400, + }, + }, + }, + }, + reqWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "Some-ID", + App: &openrtb2.App{}, + Imp: []openrtb2.Imp{ + { + ID: "Some-Imp-ID", + Banner: &openrtb2.Banner{ + Format: []openrtb2.Format{ + { + W: 600, + H: 500, + }, + { + W: 300, + H: 600, + }, + }, + }, + Ext: []byte(`{"pubmatic":{"publisherId": 12345678}}`), + }, + }, + }, + }, + wantErrs: []error{ + &errortypes.Warning{ + Message: "request.device.ext.cdep must not exceed 100 characters", + WarningCode: errortypes.SecCookieDeprecationLenWarningCode, + }, + }, + wantCDep: "", + }, + } + + deps := &endpointDeps{ + fakeUUIDGenerator{}, + &warningsCheckExchange{}, + mockBidderParamValidator{}, + &mockStoredReqFetcher{}, + empty_fetcher.EmptyFetcher{}, + &mockAccountFetcher{}, + &config.Configuration{}, + &metricsConfig.NilMetricsEngine{}, + analyticsBuild.New(&config.Analytics{}), + map[string]string{}, + false, + []byte{}, + openrtb_ext.BuildBidderMap(), + nil, + nil, + hardcodedResponseIPValidator{response: true}, + empty_fetcher.EmptyFetcher{}, + hooks.EmptyPlanBuilder{}, + nil, + openrtb_ext.NormalizeBidderName, + } + + for _, test := range testCases { + errs := deps.validateRequest(test.givenAccount, test.httpReq, test.reqWrapper, false, false, stored_responses.ImpBidderStoredResp{}, false) + assert.Equal(t, test.wantErrs, errs) + test.reqWrapper.RebuildRequest() + deviceExt, err := test.reqWrapper.GetDeviceExt() + assert.NoError(t, err) + assert.Equal(t, test.wantCDep, deviceExt.GetCDep()) + } +} diff --git a/endpoints/openrtb2/ctv/combination/adslot_combination_generator.go b/endpoints/openrtb2/ctv/combination/adslot_combination_generator.go index a80a76f8fcb..c3565553488 100644 --- a/endpoints/openrtb2/ctv/combination/adslot_combination_generator.go +++ b/endpoints/openrtb2/ctv/combination/adslot_combination_generator.go @@ -1,7 +1,7 @@ package combination import ( - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" "math/big" ) diff --git a/endpoints/openrtb2/ctv/combination/adslot_combination_generator_test.go b/endpoints/openrtb2/ctv/combination/adslot_combination_generator_test.go index fd9a6a33bed..b6533797d59 100644 --- a/endpoints/openrtb2/ctv/combination/adslot_combination_generator_test.go +++ b/endpoints/openrtb2/ctv/combination/adslot_combination_generator_test.go @@ -6,8 +6,8 @@ import ( "os" "testing" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/util" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/util" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/openrtb2/ctv/combination/combination.go b/endpoints/openrtb2/ctv/combination/combination.go index f14a0157e43..dc83e95417f 100644 --- a/endpoints/openrtb2/ctv/combination/combination.go +++ b/endpoints/openrtb2/ctv/combination/combination.go @@ -8,8 +8,8 @@ package combination import ( - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/types" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/types" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // ICombination ... diff --git a/endpoints/openrtb2/ctv/combination/combination_test.go b/endpoints/openrtb2/ctv/combination/combination_test.go index 14f32eddcb6..d22f5518224 100644 --- a/endpoints/openrtb2/ctv/combination/combination_test.go +++ b/endpoints/openrtb2/ctv/combination/combination_test.go @@ -3,8 +3,8 @@ package combination import ( "testing" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/types" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/types" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/openrtb2/ctv/impressions/by_duration_range_test.go b/endpoints/openrtb2/ctv/impressions/by_duration_range_test.go index 0bc0b2ff1bc..43cbe5675ce 100644 --- a/endpoints/openrtb2/ctv/impressions/by_duration_range_test.go +++ b/endpoints/openrtb2/ctv/impressions/by_duration_range_test.go @@ -3,7 +3,7 @@ package impressions import ( "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/openrtb2/ctv/impressions/by_duration_ranges.go b/endpoints/openrtb2/ctv/impressions/by_duration_ranges.go index 6aa129b5ef7..8cd5d9139ee 100644 --- a/endpoints/openrtb2/ctv/impressions/by_duration_ranges.go +++ b/endpoints/openrtb2/ctv/impressions/by_duration_ranges.go @@ -1,8 +1,8 @@ package impressions import ( - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/util" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/util" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // byDurRangeConfig struct will be used for creating impressions object based on list of duration ranges diff --git a/endpoints/openrtb2/ctv/impressions/helper.go b/endpoints/openrtb2/ctv/impressions/helper.go index 0cfc0a76f7b..52eaa9b93e6 100644 --- a/endpoints/openrtb2/ctv/impressions/helper.go +++ b/endpoints/openrtb2/ctv/impressions/helper.go @@ -3,8 +3,8 @@ package impressions import ( "math" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/util" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/util" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // newConfig initializes the generator instance diff --git a/endpoints/openrtb2/ctv/impressions/impression_generator.go b/endpoints/openrtb2/ctv/impressions/impression_generator.go index 7c828ab65a4..f530932cd67 100644 --- a/endpoints/openrtb2/ctv/impressions/impression_generator.go +++ b/endpoints/openrtb2/ctv/impressions/impression_generator.go @@ -1,7 +1,7 @@ package impressions import ( - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/util" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/util" ) // generator contains Pod Minimum Duration, Pod Maximum Duration, Slot Minimum Duration and Slot Maximum Duration diff --git a/endpoints/openrtb2/ctv/impressions/impressions.go b/endpoints/openrtb2/ctv/impressions/impressions.go index 5260968dc9f..bfb3f6b687b 100644 --- a/endpoints/openrtb2/ctv/impressions/impressions.go +++ b/endpoints/openrtb2/ctv/impressions/impressions.go @@ -4,8 +4,8 @@ package impressions import ( - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/util" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/util" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // Algorithm indicates type of algorithms supported diff --git a/endpoints/openrtb2/ctv/impressions/impressions_test.go b/endpoints/openrtb2/ctv/impressions/impressions_test.go index 32dc0432640..6b185d11312 100644 --- a/endpoints/openrtb2/ctv/impressions/impressions_test.go +++ b/endpoints/openrtb2/ctv/impressions/impressions_test.go @@ -6,7 +6,7 @@ package impressions import ( "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/openrtb2/ctv/impressions/maximize_for_duration.go b/endpoints/openrtb2/ctv/impressions/maximize_for_duration.go index b51116744b6..08c14cbe2b9 100644 --- a/endpoints/openrtb2/ctv/impressions/maximize_for_duration.go +++ b/endpoints/openrtb2/ctv/impressions/maximize_for_duration.go @@ -1,8 +1,8 @@ package impressions import ( - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/util" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/util" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // newMaximizeForDuration Constucts the generator object from openrtb_ext.VideoAdPod diff --git a/endpoints/openrtb2/ctv/impressions/maximize_for_duration_test.go b/endpoints/openrtb2/ctv/impressions/maximize_for_duration_test.go index c252573cf68..9bcad7cf1d6 100644 --- a/endpoints/openrtb2/ctv/impressions/maximize_for_duration_test.go +++ b/endpoints/openrtb2/ctv/impressions/maximize_for_duration_test.go @@ -3,8 +3,8 @@ package impressions import ( "testing" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/impressions/testdata" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/impressions/testdata" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/openrtb2/ctv/impressions/min_max_algorithm.go b/endpoints/openrtb2/ctv/impressions/min_max_algorithm.go index 3cfd1bde252..a6bc043fa5e 100644 --- a/endpoints/openrtb2/ctv/impressions/min_max_algorithm.go +++ b/endpoints/openrtb2/ctv/impressions/min_max_algorithm.go @@ -7,8 +7,8 @@ import ( "strings" "sync" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/util" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/util" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // keyDelim used as separator in forming key of maxExpectedDurationMap diff --git a/endpoints/openrtb2/ctv/impressions/min_max_algorithm_test.go b/endpoints/openrtb2/ctv/impressions/min_max_algorithm_test.go index a1af101626f..f6d5f234a9b 100644 --- a/endpoints/openrtb2/ctv/impressions/min_max_algorithm_test.go +++ b/endpoints/openrtb2/ctv/impressions/min_max_algorithm_test.go @@ -4,7 +4,7 @@ import ( "sort" "testing" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/impressions/testdata" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/impressions/testdata" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/openrtb2/ctv/response/adpod_generator copy.go.bak b/endpoints/openrtb2/ctv/response/adpod_generator copy.go.bak index df5b7e36be0..c74437e4a18 100644 --- a/endpoints/openrtb2/ctv/response/adpod_generator copy.go.bak +++ b/endpoints/openrtb2/ctv/response/adpod_generator copy.go.bak @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) /********************* AdPodGenerator Functions *********************/ diff --git a/endpoints/openrtb2/ctv/response/adpod_generator.go b/endpoints/openrtb2/ctv/response/adpod_generator.go index 284e5ea6091..67301776936 100644 --- a/endpoints/openrtb2/ctv/response/adpod_generator.go +++ b/endpoints/openrtb2/ctv/response/adpod_generator.go @@ -5,13 +5,13 @@ import ( "sync" "time" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/combination" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/constant" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/types" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/util" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/combination" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/constant" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/types" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/util" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) /********************* AdPodGenerator Functions *********************/ diff --git a/endpoints/openrtb2/ctv/response/adpod_generator_test.go b/endpoints/openrtb2/ctv/response/adpod_generator_test.go index 54cdce7c3f9..9cbf3619dee 100644 --- a/endpoints/openrtb2/ctv/response/adpod_generator_test.go +++ b/endpoints/openrtb2/ctv/response/adpod_generator_test.go @@ -6,9 +6,9 @@ import ( "github.com/stretchr/testify/assert" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/constant" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/types" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/constant" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/types" ) func Test_findUniqueCombinations(t *testing.T) { diff --git a/endpoints/openrtb2/ctv/types/adpod_types.go b/endpoints/openrtb2/ctv/types/adpod_types.go index 1949dcdf49c..7d4435f182c 100644 --- a/endpoints/openrtb2/ctv/types/adpod_types.go +++ b/endpoints/openrtb2/ctv/types/adpod_types.go @@ -1,9 +1,9 @@ package types import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/constant" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/constant" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // Bid openrtb bid object with extra parameters diff --git a/endpoints/openrtb2/ctv/util/util.go b/endpoints/openrtb2/ctv/util/util.go index f0ea886cf05..1c0aac8523b 100644 --- a/endpoints/openrtb2/ctv/util/util.go +++ b/endpoints/openrtb2/ctv/util/util.go @@ -11,11 +11,11 @@ import ( "github.com/buger/jsonparser" "github.com/golang/glog" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/constant" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/types" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/constant" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/types" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) var ( diff --git a/endpoints/openrtb2/ctv/util/util_test.go b/endpoints/openrtb2/ctv/util/util_test.go index b1c4cae5533..136f1b3c9a1 100644 --- a/endpoints/openrtb2/ctv/util/util_test.go +++ b/endpoints/openrtb2/ctv/util/util_test.go @@ -4,10 +4,10 @@ import ( "fmt" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/types" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/types" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/openrtb2/ctv_auction.go b/endpoints/openrtb2/ctv_auction.go index 94902811a4b..06bd47cda7f 100644 --- a/endpoints/openrtb2/ctv_auction.go +++ b/endpoints/openrtb2/ctv_auction.go @@ -18,30 +18,31 @@ import ( uuid "github.com/gofrs/uuid" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/endpoints/events" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/combination" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/constant" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/impressions" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/response" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/types" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/util" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/iputil" - "github.com/prebid/prebid-server/util/uuidutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + accountService "github.com/prebid/prebid-server/v2/account" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/endpoints/events" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/combination" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/constant" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/impressions" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/response" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/types" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/util" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/iputil" + "github.com/prebid/prebid-server/v2/util/uuidutil" ) // CTV Specific Endpoint @@ -69,7 +70,7 @@ func NewCTVEndpoint( //categories stored_requests.CategoryFetcher, cfg *config.Configuration, met metrics.MetricsEngine, - pbsAnalytics analytics.PBSAnalyticsModule, + analyticsRunner analytics.Runner, disabledBidders map[string]string, defReqJSON []byte, bidderMap map[string]openrtb_ext.BidderName, @@ -96,7 +97,7 @@ func NewCTVEndpoint( accounts, cfg, met, - pbsAnalytics, + analyticsRunner, disabledBidders, defRequest, defReqJSON, @@ -107,6 +108,7 @@ func NewCTVEndpoint( nil, planBuilder, tmaxAdjustments, + openrtb_ext.NormalizeBidderName, }, }).CTVAuctionEndpoint), nil } @@ -124,6 +126,7 @@ func (deps *ctvEndpointDeps) CTVAuctionEndpoint(w http.ResponseWriter, r *http.R Status: http.StatusOK, Errors: make([]error, 0), } + activityControl := privacy.ActivityControl{} // Prebid Server interprets request.tmax to be the maximum amount of time that a caller is willing // to wait for bids. However, tmax may be defined in the Stored Request data. @@ -144,7 +147,7 @@ func (deps *ctvEndpointDeps) CTVAuctionEndpoint(w http.ResponseWriter, r *http.R deps.metricsEngine.RecordRequest(deps.labels) recordRejectedBids(deps.labels.PubID, ao.SeatNonBid, deps.metricsEngine) deps.metricsEngine.RecordRequestTime(deps.labels, time.Since(start)) - deps.analytics.LogAuctionObject(&ao) + deps.analytics.LogAuctionObject(&ao, activityControl) }() hookExecutor := hookexecution.NewHookExecutor(deps.hookExecutionPlanBuilder, hookexecution.EndpointCtv, deps.metricsEngine) @@ -943,13 +946,13 @@ func (deps *ctvEndpointDeps) getAdPodBid(adpod *types.AdPodBid) *types.Bid { bid.Price = adpod.Price bid.ADomain = adpod.ADomain[:] bid.Cat = adpod.Cat[:] - bid.AdM = *getAdPodBidCreative(deps.request.Imp[deps.impIndices[adpod.OriginalImpID]].Video, adpod, deps.cfg.GenerateBidID) + bid.AdM = *getAdPodBidCreative(adpod, deps.cfg.GenerateBidID) bid.Ext = getAdPodBidExtension(adpod) return &bid } // getAdPodBidCreative get commulative adpod bid details -func getAdPodBidCreative(video *openrtb2.Video, adpod *types.AdPodBid, generatedBidID bool) *string { +func getAdPodBidCreative(adpod *types.AdPodBid, generatedBidID bool) *string { doc := etree.NewDocument() vast := doc.CreateElement(constant.VASTElement) sequenceNumber := 1 @@ -981,6 +984,10 @@ func getAdPodBidCreative(video *openrtb2.Video, adpod *types.AdPodBid, generated } vastTag := adDoc.SelectElement(constant.VASTElement) + if vastTag == nil { + util.Logf("error:[vast_element_missing_in_adm] seat:[%s] adm:[%s]", bid.Seat, bid.AdM) + continue + } //Get Actual VAST Version bidVASTVersion, _ := strconv.ParseFloat(vastTag.SelectAttrValue(constant.VASTVersionAttribute, constant.VASTDefaultVersionStr), 64) diff --git a/endpoints/openrtb2/ctv_auction_test.go b/endpoints/openrtb2/ctv_auction_test.go index b1e9c8aeeae..01a161a87e9 100644 --- a/endpoints/openrtb2/ctv_auction_test.go +++ b/endpoints/openrtb2/ctv_auction_test.go @@ -4,11 +4,11 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/constant" - "github.com/prebid/prebid-server/endpoints/openrtb2/ctv/types" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/constant" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2/ctv/types" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -834,3 +834,54 @@ func TestRecordAdPodRejectedBids(t *testing.T) { me.AssertNumberOfCalls(t, "RecordRejectedBids", test.want.expectedCalls) } } + +func TestGetAdPodBidCreative(t *testing.T) { + type args struct { + adpod *types.AdPodBid + generatedBidID bool + } + tests := []struct { + name string + args args + want string + }{ + { + name: "VAST_element_missing_in_adm", + args: args{ + adpod: &types.AdPodBid{ + Bids: []*types.Bid{ + { + Bid: &openrtb2.Bid{ + AdM: "any_creative_without_vast", + }, + }, + }, + }, + generatedBidID: false, + }, + want: "", + }, + { + name: "VAST_element_present_in_adm", + args: args{ + adpod: &types.AdPodBid{ + Bids: []*types.Bid{ + { + Bid: &openrtb2.Bid{ + AdM: "url_creative", + }, + }, + }, + }, + generatedBidID: false, + }, + want: "", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := getAdPodBidCreative(tt.args.adpod, tt.args.generatedBidID) + assert.Equalf(t, tt.want, *got, "found incorrect creative") + }) + } +} diff --git a/endpoints/openrtb2/interstitial.go b/endpoints/openrtb2/interstitial.go index 46dc7a61510..a1af3e4721f 100644 --- a/endpoints/openrtb2/interstitial.go +++ b/endpoints/openrtb2/interstitial.go @@ -3,10 +3,10 @@ package openrtb2 import ( "fmt" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func processInterstitials(req *openrtb_ext.RequestWrapper) error { diff --git a/endpoints/openrtb2/interstitial_test.go b/endpoints/openrtb2/interstitial_test.go index 947817803b2..6373d18407b 100644 --- a/endpoints/openrtb2/interstitial_test.go +++ b/endpoints/openrtb2/interstitial_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/endpoints/openrtb2/sample-requests/account-required/with-account/required-blacklisted-acct.json b/endpoints/openrtb2/sample-requests/account-required/with-account/required-blacklisted-acct.json deleted file mode 100644 index 894a92fdb27..00000000000 --- a/endpoints/openrtb2/sample-requests/account-required/with-account/required-blacklisted-acct.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "description": "Account is required but request comes with a blacklisted account id", - "config": { - "accountRequired": true, - "blacklistedAccts": ["bad_acct"] - }, - "mockBidRequest": { - "id": "some-request-id", - "user": { - "ext": { - "consent": "gdpr-consent-string", - "prebid": { - "buyeruids": { - "appnexus": "override-appnexus-id-in-cookie" - } - } - } - }, - "app": { - "id": "cool_app", - "publisher": { - "id": "bad_acct" - } - }, - "regs": { - "ext": { - "gdpr": 1 - } - }, - "imp": [ - { - "id": "some-impression-id", - "banner": { - "format": [ - { - "w": 300, - "h": 250 - }, - { - "w": 300, - "h": 600 - } - ] - }, - "ext": { - "appnexus": { - "placementId": 12883451 - }, - "districtm": { - "placementId": 105 - }, - "rubicon": { - "accountId": 1001, - "siteId": 113932, - "zoneId": 535510 - } - } - } - ], - "tmax": 500, - "ext": { - "prebid": { - "aliases": { - "districtm": "appnexus" - }, - "bidadjustmentfactors": { - "appnexus": 1.01, - "districtm": 0.98, - "rubicon": 0.99 - }, - "cache": { - "bids": {} - }, - "targeting": { - "includewinners": false, - "pricegranularity": { - "precision": 2, - "ranges": [ - { - "max": 20, - "increment": 0.10 - } - ] - } - } - } - } - }, - "expectedReturnCode": 503, - "expectedErrorMessage": "Invalid request: Prebid-server has disabled Account ID: bad_acct, please reach out to the prebid server host.\n" -} diff --git a/endpoints/openrtb2/sample-requests/blacklisted/blacklisted-app-publisher.json b/endpoints/openrtb2/sample-requests/account-required/with-account/required-disabled-acct.json similarity index 86% rename from endpoints/openrtb2/sample-requests/blacklisted/blacklisted-app-publisher.json rename to endpoints/openrtb2/sample-requests/account-required/with-account/required-disabled-acct.json index ef7a93b8bad..31a0021b7c1 100644 --- a/endpoints/openrtb2/sample-requests/blacklisted/blacklisted-app-publisher.json +++ b/endpoints/openrtb2/sample-requests/account-required/with-account/required-disabled-acct.json @@ -1,7 +1,7 @@ { - "description": "This is a perfectly valid request except that it comes with a blacklisted app publisher account", + "description": "Account is required but request comes with a disabled account id", "config": { - "blacklistedAccts": ["bad_acct"] + "accountRequired": true }, "mockBidRequest": { "id": "some-request-id", @@ -18,7 +18,7 @@ "app": { "id": "cool_app", "publisher": { - "id": "bad_acct" + "id": "disabled_acct" } }, "regs": { @@ -86,5 +86,5 @@ } }, "expectedReturnCode": 503, - "expectedErrorMessage": "Invalid request: Prebid-server has disabled Account ID: bad_acct, please reach out to the prebid server host.\n" + "expectedErrorMessage": "Invalid request: Prebid-server has disabled Account ID: disabled_acct, please reach out to the prebid server host.\n" } diff --git a/endpoints/openrtb2/sample-requests/amp/valid-supplementary/aliased-buyeruids-case-insensitive.json b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/aliased-buyeruids-case-insensitive.json new file mode 100644 index 00000000000..1d2462c98b8 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/aliased-buyeruids-case-insensitive.json @@ -0,0 +1,84 @@ +{ + "description": "Amp request where root.user.ext.prebid.buyeruids field makes use of alias defined in root.ext.prebid.aliases and request makes use of case sensitive bidder name", + "query": "tag_id=101", + "config": { + "mockBidders": [ + { + "bidderName": "appnexus", + "currency": "USD", + "price": 2 + } + ] + }, + "mockBidRequest": { + "id": "request-with-user-ext-obj", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "prebid": { + "bidder": { + "Appnexus": { + "placementId": 12883451 + } + } + } + } + } + ], + "user": { + "ext": { + "prebid": { + "buyeruids": { + "unknown": "123" + } + } + } + }, + "ext": { + "prebid": { + "aliases": { + "unknown": "Appnexus" + } + } + } + }, + "expectedAmpResponse": { + "targeting": { + "hb_bidder": "Appnexus", + "hb_bidder_Appnexus": "Appnexus", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_Appnex": "www.pbcserver.com", + "hb_cache_id": "0", + "hb_cache_id_Appnexus": "0", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_Appnex": "/pbcache/endpoint", + "hb_pb": "2.00", + "hb_pb_Appnexus": "2.00" + }, + "ortb2": { + "ext": { + "warnings": { + "general": [ + { + "code": 10002, + "message": "debug turned off for account" + } + ] + } + } + } + }, + "expectedReturnCode": 200 +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/amp/valid-supplementary/buyeruids-camel-case.json b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/buyeruids-camel-case.json new file mode 100644 index 00000000000..4597fe7786a --- /dev/null +++ b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/buyeruids-camel-case.json @@ -0,0 +1,77 @@ +{ + "description": "Amp request where root.user.ext.prebid.buyeruids field has camel case bidder name", + "query": "tag_id=101", + "config": { + "mockBidders": [ + { + "bidderName": "yahooAds", + "currency": "USD", + "price": 2 + } + ] + }, + "mockBidRequest": { + "id": "request-with-user-ext-obj", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "prebid": { + "bidder": { + "YahooAds": { + "placementId": 12883451 + } + } + } + } + } + ], + "user": { + "ext": { + "prebid": { + "buyeruids": { + "YahooAds": "123" + } + } + } + } + }, + "expectedAmpResponse": { + "targeting": { + "hb_bidder": "YahooAds", + "hb_bidder_YahooAds": "YahooAds", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_YahooA": "www.pbcserver.com", + "hb_cache_id": "0", + "hb_cache_id_YahooAds": "0", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_YahooA": "/pbcache/endpoint", + "hb_pb": "2.00", + "hb_pb_YahooAds": "2.00" + }, + "ortb2": { + "ext": { + "warnings": { + "general": [ + { + "code": 10002, + "message": "debug turned off for account" + } + ] + } + } + } + }, + "expectedReturnCode": 200 +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/amp/valid-supplementary/buyeruids-case-insensitive.json b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/buyeruids-case-insensitive.json new file mode 100644 index 00000000000..976fcb3cf7f --- /dev/null +++ b/endpoints/openrtb2/sample-requests/amp/valid-supplementary/buyeruids-case-insensitive.json @@ -0,0 +1,77 @@ +{ + "description": "Amp request where root.user.ext.prebid.buyeruids field has case insensitive bidder name", + "query": "tag_id=101", + "config": { + "mockBidders": [ + { + "bidderName": "appnexus", + "currency": "USD", + "price": 2 + } + ] + }, + "mockBidRequest": { + "id": "request-with-user-ext-obj", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "prebid": { + "bidder": { + "Appnexus": { + "placementId": 12883451 + } + } + } + } + } + ], + "user": { + "ext": { + "prebid": { + "buyeruids": { + "Appnexus": "123" + } + } + } + } + }, + "expectedAmpResponse": { + "targeting": { + "hb_bidder": "Appnexus", + "hb_bidder_Appnexus": "Appnexus", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_Appnex": "www.pbcserver.com", + "hb_cache_id": "0", + "hb_cache_id_Appnexus": "0", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_Appnex": "/pbcache/endpoint", + "hb_pb": "2.00", + "hb_pb_Appnexus": "2.00" + }, + "ortb2": { + "ext": { + "warnings": { + "general": [ + { + "code": 10002, + "message": "debug turned off for account" + } + ] + } + } + } + }, + "expectedReturnCode": 200 +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/blacklisted/blacklisted-site-publisher.json b/endpoints/openrtb2/sample-requests/blacklisted/blacklisted-site-publisher.json deleted file mode 100644 index cdec20d22ba..00000000000 --- a/endpoints/openrtb2/sample-requests/blacklisted/blacklisted-site-publisher.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "description": "This is a perfectly valid request except that it comes with a blacklisted site publisher account", - "config": { - "blacklistedAccts": ["bad_acct"] - }, - "mockBidRequest": { - "id": "some-request-id", - "user": { - "ext": { - "consent": "gdpr-consent-string", - "prebid": { - "buyeruids": { - "appnexus": "override-appnexus-id-in-cookie" - } - } - } - }, - "site": { - "id": "cool_site", - "publisher": { - "id": "bad_acct" - } - }, - "regs": { - "ext": { - "gdpr": 1 - } - }, - "imp": [ - { - "id": "some-impression-id", - "banner": { - "format": [ - { - "w": 300, - "h": 250 - }, - { - "w": 300, - "h": 600 - } - ] - }, - "ext": { - "appnexus": { - "placementId": 12883451 - }, - "districtm": { - "placementId": 105 - }, - "rubicon": { - "accountId": 1001, - "siteId": 113932, - "zoneId": 535510 - } - } - } - ], - "tmax": 500, - "ext": { - "prebid": { - "aliases": { - "districtm": "appnexus" - }, - "bidadjustmentfactors": { - "appnexus": 1.01, - "districtm": 0.98, - "rubicon": 0.99 - }, - "cache": { - "bids": {} - }, - "targeting": { - "includewinners": false, - "pricegranularity": { - "precision": 2, - "ranges": [ - { - "max": 20, - "increment": 0.10 - } - ] - } - } - } - } - }, - "expectedReturnCode": 503, - "expectedErrorMessage": "Invalid request: Prebid-server has disabled Account ID: bad_acct, please reach out to the prebid server host.\n" -} diff --git a/endpoints/openrtb2/sample-requests/hooks/amp_bidder_reject.json b/endpoints/openrtb2/sample-requests/hooks/amp_bidder_reject.json index c3ce226ff9c..7fc1e7d8721 100644 --- a/endpoints/openrtb2/sample-requests/hooks/amp_bidder_reject.json +++ b/endpoints/openrtb2/sample-requests/hooks/amp_bidder_reject.json @@ -4,7 +4,7 @@ "config": { "mockBidders": [ {"bidderName": "appnexus", "currency": "USD", "price": 2}, - {"bidderName": "applogy", "currency": "USD", "price": 1} + {"bidderName": "rubicon", "currency": "USD", "price": 1} ] }, "mockBidRequest": { @@ -29,7 +29,7 @@ "appnexus": { "placementId": 12883451 }, - "applogy": { + "rubicon": { "placementId": 12883451 } } @@ -42,23 +42,23 @@ "debug": true, "aliases": { "unknown": "appnexus", - "foo": "applogy" + "foo": "rubicon" } } } }, "expectedAmpResponse": { "targeting": { - "hb_bidder": "applogy", - "hb_bidder_applogy": "applogy", + "hb_bidder": "rubicon", + "hb_bidder_rubicon": "rubicon", "hb_cache_host": "www.pbcserver.com", - "hb_cache_host_applog": "www.pbcserver.com", + "hb_cache_host_rubico": "www.pbcserver.com", "hb_cache_id": "0", - "hb_cache_id_applogy": "0", + "hb_cache_id_rubicon": "0", "hb_cache_path": "/pbcache/endpoint", - "hb_cache_path_applog": "/pbcache/endpoint", + "hb_cache_path_rubico": "/pbcache/endpoint", "hb_pb": "1.00", - "hb_pb_applogy": "1.00" + "hb_pb_rubicon": "1.00" }, "ortb2": { "ext": { @@ -103,7 +103,7 @@ ] }, { - "entity": "applogy", + "entity": "rubicon", "groups": [ { "invocation_results": [ diff --git a/endpoints/openrtb2/sample-requests/hooks/amp_bidder_response_reject.json b/endpoints/openrtb2/sample-requests/hooks/amp_bidder_response_reject.json index fe654304c4f..4e38b5a2f2d 100644 --- a/endpoints/openrtb2/sample-requests/hooks/amp_bidder_response_reject.json +++ b/endpoints/openrtb2/sample-requests/hooks/amp_bidder_response_reject.json @@ -4,7 +4,7 @@ "config": { "mockBidders": [ {"bidderName": "appnexus", "currency": "USD", "price": 2}, - {"bidderName": "applogy", "currency": "USD", "price": 1} + {"bidderName": "rubicon", "currency": "USD", "price": 1} ] }, "mockBidRequest": { @@ -29,7 +29,7 @@ "appnexus": { "placementId": 12883451 }, - "applogy": { + "rubicon": { "placementId": 12883451 } } @@ -42,23 +42,23 @@ "debug": true, "aliases": { "unknown": "appnexus", - "foo": "applogy" + "foo": "rubicon" } } } }, "expectedAmpResponse": { "targeting": { - "hb_bidder": "applogy", - "hb_bidder_applogy": "applogy", + "hb_bidder": "rubicon", + "hb_bidder_rubicon": "rubicon", "hb_cache_host": "www.pbcserver.com", - "hb_cache_host_applog": "www.pbcserver.com", + "hb_cache_host_rubico": "www.pbcserver.com", "hb_cache_id": "0", - "hb_cache_id_applogy": "0", + "hb_cache_id_rubicon": "0", "hb_cache_path": "/pbcache/endpoint", - "hb_cache_path_applog": "/pbcache/endpoint", + "hb_cache_path_rubico": "/pbcache/endpoint", "hb_pb": "1.00", - "hb_pb_applogy": "1.00" + "hb_pb_rubicon": "1.00" }, "ortb2": { "ext": { @@ -103,7 +103,7 @@ ] }, { - "entity": "applogy", + "entity": "rubicon", "groups": [ { "invocation_results": [ diff --git a/endpoints/openrtb2/sample-requests/hooks/auction_bidder_reject.json b/endpoints/openrtb2/sample-requests/hooks/auction_bidder_reject.json index bece42277ef..0bc86e4c4a5 100644 --- a/endpoints/openrtb2/sample-requests/hooks/auction_bidder_reject.json +++ b/endpoints/openrtb2/sample-requests/hooks/auction_bidder_reject.json @@ -3,7 +3,7 @@ "config": { "mockBidders": [ {"bidderName": "appnexus", "currency": "USD", "price": 0.00}, - {"bidderName": "applogy", "currency": "USD", "price": 0.00} + {"bidderName": "rubicon", "currency": "USD", "price": 0.00} ] }, "mockBidRequest": { @@ -26,13 +26,13 @@ "appnexus": { "placementId": 12883451 }, - "applogy": { + "rubicon": { "placementId": 12883451 } } } ], - "tmax": 50, + "tmax": 500, "test": 1, "ext": { "prebid": { @@ -49,12 +49,12 @@ { "bid": [ { - "id": "applogy-bid", + "id": "rubicon-bid", "impid": "some-impression-id", "price": 0 } ], - "seat": "applogy" + "seat": "rubicon" } ], "ext": { @@ -99,7 +99,7 @@ ] }, { - "entity": "applogy", + "entity": "rubicon", "groups": [ { "invocation_results": [ diff --git a/endpoints/openrtb2/sample-requests/hooks/auction_bidder_response_reject.json b/endpoints/openrtb2/sample-requests/hooks/auction_bidder_response_reject.json index 32743d67b75..16a7f43c87a 100644 --- a/endpoints/openrtb2/sample-requests/hooks/auction_bidder_response_reject.json +++ b/endpoints/openrtb2/sample-requests/hooks/auction_bidder_response_reject.json @@ -3,7 +3,7 @@ "config": { "mockBidders": [ {"bidderName": "appnexus", "currency": "USD", "price": 0.00}, - {"bidderName": "applogy", "currency": "USD", "price": 0.00} + {"bidderName": "rubicon", "currency": "USD", "price": 0.00} ] }, "mockBidRequest": { @@ -26,13 +26,13 @@ "appnexus": { "placementId": 12883451 }, - "applogy": { + "rubicon": { "placementId": 12883451 } } } ], - "tmax": 50, + "tmax": 500, "test": 1, "ext": { "prebid": { @@ -49,12 +49,12 @@ { "bid": [ { - "id": "applogy-bid", + "id": "rubicon-bid", "impid": "some-impression-id", "price": 0 } ], - "seat": "applogy" + "seat": "rubicon" } ], "ext": { @@ -99,7 +99,7 @@ ] }, { - "entity": "applogy", + "entity": "rubicon", "groups": [ { "invocation_results": [ diff --git a/endpoints/openrtb2/sample-requests/hooks/auction_entrypoint_reject.json b/endpoints/openrtb2/sample-requests/hooks/auction_entrypoint_reject.json index c67b8d4b490..3481b986bc3 100644 --- a/endpoints/openrtb2/sample-requests/hooks/auction_entrypoint_reject.json +++ b/endpoints/openrtb2/sample-requests/hooks/auction_entrypoint_reject.json @@ -28,7 +28,7 @@ } } ], - "tmax": 50, + "tmax": 500, "ext": { "prebid": { "trace": "verbose" diff --git a/endpoints/openrtb2/sample-requests/hooks/auction_processed_auction_request_reject.json b/endpoints/openrtb2/sample-requests/hooks/auction_processed_auction_request_reject.json index eca136cf75f..4c160dec007 100644 --- a/endpoints/openrtb2/sample-requests/hooks/auction_processed_auction_request_reject.json +++ b/endpoints/openrtb2/sample-requests/hooks/auction_processed_auction_request_reject.json @@ -28,7 +28,7 @@ } } ], - "tmax": 50, + "tmax": 500, "ext": { "prebid": { "trace": "verbose" diff --git a/endpoints/openrtb2/sample-requests/hooks/auction_raw_auction_request_reject.json b/endpoints/openrtb2/sample-requests/hooks/auction_raw_auction_request_reject.json index dc192baffee..ed284e6687d 100644 --- a/endpoints/openrtb2/sample-requests/hooks/auction_raw_auction_request_reject.json +++ b/endpoints/openrtb2/sample-requests/hooks/auction_raw_auction_request_reject.json @@ -28,7 +28,7 @@ } } ], - "tmax": 50, + "tmax": 500, "ext": { "prebid": { "trace": "verbose" diff --git a/endpoints/openrtb2/sample-requests/invalid-stored/bad_incoming_1.json b/endpoints/openrtb2/sample-requests/invalid-stored/bad_incoming_1.json index 812f0664fbb..cf699fea0c9 100644 --- a/endpoints/openrtb2/sample-requests/invalid-stored/bad_incoming_1.json +++ b/endpoints/openrtb2/sample-requests/invalid-stored/bad_incoming_1.json @@ -11,5 +11,5 @@ } }, "expectedReturnCode": 400, - "expectedErrorMessage": "Invalid request: Invalid JSON in Incoming Request: invalid character ']' looking for beginning of value at offset" + "expectedErrorMessage": "Invalid request: expect { or n, but found" } diff --git a/endpoints/openrtb2/sample-requests/invalid-stored/bad_incoming_imp.json b/endpoints/openrtb2/sample-requests/invalid-stored/bad_incoming_imp.json index e3960b17399..fb0dc866d10 100644 --- a/endpoints/openrtb2/sample-requests/invalid-stored/bad_incoming_imp.json +++ b/endpoints/openrtb2/sample-requests/invalid-stored/bad_incoming_imp.json @@ -37,5 +37,5 @@ "tmax": 500 }, "expectedReturnCode": 400, - "expectedErrorMessage": "Invalid request: Invalid JSON in Imp[0] of Incoming Request: invalid character '\"' after object key:value pair at offset 132\n" + "expectedErrorMessage": "Invalid request: Invalid JSON Document" } diff --git a/endpoints/openrtb2/sample-requests/invalid-stored/bad_stored_imp.json b/endpoints/openrtb2/sample-requests/invalid-stored/bad_stored_imp.json index 0fed6c32adf..ea19855e75a 100644 --- a/endpoints/openrtb2/sample-requests/invalid-stored/bad_stored_imp.json +++ b/endpoints/openrtb2/sample-requests/invalid-stored/bad_stored_imp.json @@ -33,5 +33,5 @@ "tmax": 500 }, "expectedReturnCode": 400, - "expectedErrorMessage": "Invalid request: imp.ext.prebid.storedrequest.id 7: Stored Imp has Invalid JSON" + "expectedErrorMessage": "Invalid request: Invalid JSON Document" } diff --git a/endpoints/openrtb2/sample-requests/invalid-stored/bad_stored_req.json b/endpoints/openrtb2/sample-requests/invalid-stored/bad_stored_req.json index 4e9d7f03352..235eb26179b 100644 --- a/endpoints/openrtb2/sample-requests/invalid-stored/bad_stored_req.json +++ b/endpoints/openrtb2/sample-requests/invalid-stored/bad_stored_req.json @@ -23,5 +23,5 @@ } }, "expectedReturnCode": 400, - "expectedErrorMessage": "Invalid request: ext.prebid.storedrequest.id refers to Stored Request 3 which contains Invalid JSON" + "expectedErrorMessage": "Invalid request: expect { or n, but found" } diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/bid-adj-factors-case-invalid-same-bidder-names.json b/endpoints/openrtb2/sample-requests/invalid-whole/bid-adj-factors-case-invalid-same-bidder-names.json new file mode 100644 index 00000000000..d484f129ba3 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/invalid-whole/bid-adj-factors-case-invalid-same-bidder-names.json @@ -0,0 +1,81 @@ +{ + "description": "This demonstrates a request with bidadjustmentfactors that have multiple of the same bidder is invalid request", + "config": { + "mockBidders": [ + { + "bidderName": "appnexus", + "currency": "USD", + "price": 1.00 + } + ] + }, + "mockBidRequest": { + "id": "some-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "ext": { + "consent": "gdpr-consent-string" + } + }, + "regs": { + "ext": { + "gdpr": 1, + "us_privacy": "1NYN" + } + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "Appnexus": { + "placementId": 12883451 + } + } + } + ], + "tmax": 500, + "ext": { + "prebid": { + "bidadjustmentfactors": { + "APPNEXUS": 1.01, + "Appnexus": 2.00 + }, + "cache": { + "bids": {} + }, + "channel": { + "name": "video", + "version": "1.0" + }, + "targeting": { + "includewinners": false, + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.10 + } + ] + } + } + } + } + }, + "expectedReturnCode": 400, + "expectedErrorMessage": "Invalid request: cannot have multiple bidders that differ only in case style" +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/interstital-bad-perc.json b/endpoints/openrtb2/sample-requests/invalid-whole/interstital-bad-perc.json index 302372b5e5d..e5031d4597d 100644 --- a/endpoints/openrtb2/sample-requests/invalid-whole/interstital-bad-perc.json +++ b/endpoints/openrtb2/sample-requests/invalid-whole/interstital-bad-perc.json @@ -49,5 +49,5 @@ } }, "expectedReturnCode": 400, - "expectedErrorMessage": "Invalid request: request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100\n" + "expectedErrorMessage": "Invalid request: request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100" } diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/invalid-source.json b/endpoints/openrtb2/sample-requests/invalid-whole/invalid-source.json index 5aa7fd4dea1..8db15b82a75 100644 --- a/endpoints/openrtb2/sample-requests/invalid-whole/invalid-source.json +++ b/endpoints/openrtb2/sample-requests/invalid-whole/invalid-source.json @@ -39,5 +39,5 @@ ] }, "expectedReturnCode": 400, - "expectedErrorMessage": "Invalid request: json: cannot unmarshal object into Go struct field ExtAppPrebid.source of type string" + "expectedErrorMessage": "Invalid request: cannot unmarshal openrtb_ext.ExtAppPrebid.Source: expects \" or n, but found {" } diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/malformed-bid-request.json b/endpoints/openrtb2/sample-requests/invalid-whole/malformed-bid-request.json index c6fd52304a7..482af257d62 100644 --- a/endpoints/openrtb2/sample-requests/invalid-whole/malformed-bid-request.json +++ b/endpoints/openrtb2/sample-requests/invalid-whole/malformed-bid-request.json @@ -2,5 +2,5 @@ "description": "Malformed bid request throws an error", "mockBidRequest": "malformed", "expectedReturnCode": 400, - "expectedErrorMessage": "Invalid request: invalid character 'm' looking for beginning of value\n" + "expectedErrorMessage": "Invalid request: expect { or n, but found m" } diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json b/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app-or-dooh.json similarity index 85% rename from endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json rename to endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app-or-dooh.json index 8ec2273ebec..c883979c98d 100644 --- a/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app.json +++ b/endpoints/openrtb2/sample-requests/invalid-whole/no-site-or-app-or-dooh.json @@ -1,5 +1,5 @@ { - "description": "Request does not come with site field nor app field", + "description": "Request does not come with site nor app nor dooh field", "mockBidRequest": { "id": "req-id", "imp": [ diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/regs-ext-malformed.json b/endpoints/openrtb2/sample-requests/invalid-whole/regs-ext-malformed.json index ab44e3e2428..7ab2631b701 100644 --- a/endpoints/openrtb2/sample-requests/invalid-whole/regs-ext-malformed.json +++ b/endpoints/openrtb2/sample-requests/invalid-whole/regs-ext-malformed.json @@ -42,5 +42,5 @@ } }, "expectedReturnCode": 400, - "expectedErrorMessage": "Invalid request: request.regs.ext is invalid: json: cannot unmarshal string into Go value of type map[string]json.RawMessage\n" + "expectedErrorMessage": "Invalid request: request.regs.ext is invalid: expect { or n, but found " } diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/site-app-both.json b/endpoints/openrtb2/sample-requests/invalid-whole/site-app-both.json index 5bc3054c356..255ea59e498 100644 --- a/endpoints/openrtb2/sample-requests/invalid-whole/site-app-both.json +++ b/endpoints/openrtb2/sample-requests/invalid-whole/site-app-both.json @@ -1,5 +1,5 @@ { - "description": "Bid request comes with both site and app fields, it should only come with one or the other", + "description": "Bid request comes with both site and app fields, it should only come with one", "mockBidRequest": { "id": "req-id", "site": { @@ -23,5 +23,5 @@ ] }, "expectedReturnCode": 400, - "expectedErrorMessage": "Invalid request: request.site or request.app must be defined, but not both.\n" + "expectedErrorMessage": "Invalid request: No more than one of request.site or request.app or request.dooh can be defined\n" } diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/site-dooh-both.json b/endpoints/openrtb2/sample-requests/invalid-whole/site-dooh-both.json new file mode 100644 index 00000000000..d4760d12a99 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/invalid-whole/site-dooh-both.json @@ -0,0 +1,27 @@ +{ + "description": "Bid request comes with both site and dooh fields, it should only come with one", + "mockBidRequest": { + "id": "req-id", + "site": { + "page": "test.mysite.com" + }, + "dooh": {}, + "imp": [ + { + "id": "imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "appnexus": { + "placementId": 12883451 + } + } + } + ] + }, + "expectedReturnCode": 400, + "expectedErrorMessage": "Invalid request: No more than one of request.site or request.app or request.dooh can be defined\n" +} diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/user-ext-consent-int.json b/endpoints/openrtb2/sample-requests/invalid-whole/user-ext-consent-int.json index a26db8a5695..af04627c3a9 100644 --- a/endpoints/openrtb2/sample-requests/invalid-whole/user-ext-consent-int.json +++ b/endpoints/openrtb2/sample-requests/invalid-whole/user-ext-consent-int.json @@ -46,5 +46,5 @@ } }, "expectedReturnCode": 400, - "expectedErrorMessage": "Invalid request: request.user.ext object is not valid: json: cannot unmarshal number into Go value of type string\n" + "expectedErrorMessage": "Invalid request: request.user.ext object is not valid: expects \" or n, but found 1" } diff --git a/endpoints/openrtb2/sample-requests/invalid-whole/user-gdpr-consent-invalid.json b/endpoints/openrtb2/sample-requests/invalid-whole/user-gdpr-consent-invalid.json index c4646550dd2..b710d589ea5 100644 --- a/endpoints/openrtb2/sample-requests/invalid-whole/user-gdpr-consent-invalid.json +++ b/endpoints/openrtb2/sample-requests/invalid-whole/user-gdpr-consent-invalid.json @@ -41,5 +41,5 @@ } }, "expectedReturnCode": 400, - "expectedErrorMessage": "Invalid request: request.user.ext object is not valid: json: cannot unmarshal number into Go value of type string" + "expectedErrorMessage": "Invalid request: request.user.ext object is not valid: expects \" or n, but found 2" } diff --git a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/all-ext-case-insensitive.json b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/all-ext-case-insensitive.json index 20997076af2..67633ab8e1a 100644 --- a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/all-ext-case-insensitive.json +++ b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/all-ext-case-insensitive.json @@ -51,7 +51,7 @@ ] }, "ext": { - "appnexus": { + "APPnexus": { "placementId": 12883451 }, "districtm": { @@ -109,7 +109,7 @@ "price": 1.01 } ], - "seat": "appnexus" + "seat": "APPnexus" }, { "bid": [ @@ -137,4 +137,4 @@ "nbr": 0 }, "expectedReturnCode": 200 -} \ No newline at end of file +} diff --git a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/bid-adj-factors-case-insensitive-different-casing.json b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/bid-adj-factors-case-insensitive-different-casing.json new file mode 100644 index 00000000000..a231af657cd --- /dev/null +++ b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/bid-adj-factors-case-insensitive-different-casing.json @@ -0,0 +1,97 @@ +{ + "description": "This demonstrates bid adjustment factors with bidder names that have different casing in imp vs. in bidadjustmentfactors", + "config": { + "mockBidders": [ + { + "bidderName": "appnexus", + "currency": "USD", + "price": 1.00 + } + ] + }, + "mockBidRequest": { + "id": "some-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "ext": { + "consent": "gdpr-consent-string" + } + }, + "regs": { + "ext": { + "gdpr": 1, + "us_privacy": "1NYN" + } + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "Appnexus": { + "placementId": 12883451 + } + } + } + ], + "tmax": 500, + "ext": { + "prebid": { + "bidadjustmentfactors": { + "APPNEXUS": 1.01 + }, + "cache": { + "bids": {} + }, + "channel": { + "name": "video", + "version": "1.0" + }, + "targeting": { + "includewinners": false, + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.10 + } + ] + } + } + } + } + }, + "expectedBidResponse": { + "id": "some-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "appnexus-bid", + "impid": "some-impression-id", + "price": 1.01 + } + ], + "seat": "Appnexus" + } + ], + "bidid": "test bid id", + "cur": "USD", + "nbr": 0 + }, + "expectedReturnCode": 200 +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/bid-adj-factors-case-insensitive.json b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/bid-adj-factors-case-insensitive.json new file mode 100644 index 00000000000..2fa4f37cc88 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/bid-adj-factors-case-insensitive.json @@ -0,0 +1,119 @@ +{ + "description": "This demonstrates bid adjustment factors with case insensitive bidder names", + "config": { + "mockBidders": [ + { + "bidderName": "appnexus", + "currency": "USD", + "price": 1.00 + }, + { + "bidderName": "rubicon", + "currency": "USD", + "price": 1.00 + } + ] + }, + "mockBidRequest": { + "id": "some-request-id", + "site": { + "page": "prebid.org" + }, + "user": { + "ext": { + "consent": "gdpr-consent-string" + } + }, + "regs": { + "ext": { + "gdpr": 1, + "us_privacy": "1NYN" + } + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "APPNEXUS": { + "placementId": 12883451 + }, + "districtm": { + "placementId": 105 + } + } + } + ], + "tmax": 500, + "ext": { + "prebid": { + "aliases": { + "districtm": "APPNEXUS" + }, + "bidadjustmentfactors": { + "APPNEXUS": 1.01, + "districtm": 0.98 + }, + "cache": { + "bids": {} + }, + "channel": { + "name": "video", + "version": "1.0" + }, + "targeting": { + "includewinners": false, + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "max": 20, + "increment": 0.10 + } + ] + } + } + } + } + }, + "expectedBidResponse": { + "id": "some-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "appnexus-bid", + "impid": "some-impression-id", + "price": 1.01 + } + ], + "seat": "APPNEXUS" + }, + { + "bid": [ + { + "id": "appnexus-bid", + "impid": "some-impression-id", + "price": 0.98 + } + ], + "seat": "districtm" + } + ], + "bidid": "test bid id", + "cur": "USD", + "nbr": 0 + }, + "expectedReturnCode": 200 +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/valid-whole/exemplary/dooh.json b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/dooh.json new file mode 100644 index 00000000000..faa9a7f6646 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/valid-whole/exemplary/dooh.json @@ -0,0 +1,53 @@ +{ + "description": "Simple DOOH request", + "config": { + "mockBidders": [ + {"bidderName": "appnexus", "currency": "USD", "price": 0.00} + ] + }, + "mockBidRequest": { + "id": "some-request-id", + "dooh": { + "id": "12345" + }, + "imp": [ + { + "id": "some-impression-id", + "banner": { + "format": [ + { + "w": 1920, + "h": 1080 + } + ] + }, + "ext": { + "appnexus": { + "placementId": 12883451 + } + } + } + ], + "tmax": 500, + "ext": {} + }, + "expectedBidResponse": { + "id": "some-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "appnexus-bid", + "impid": "some-impression-id", + "price": 0 + } + ], + "seat": "appnexus" + } + ], + "bidid": "test bid id", + "cur": "USD", + "nbr": 0 + }, + "expectedReturnCode": 200 +} diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp-case-matching-bidder-name.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp-case-matching-bidder-name.json new file mode 100644 index 00000000000..49576023b04 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp-case-matching-bidder-name.json @@ -0,0 +1,39 @@ +{ + "description": "request with impression with stored bid response with case matching bidder name", + "mockBidRequest": { + "id": "request-with-stored-resp", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "imp-id3", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "appnexus": { + "placementId": 12883451 + }, + "prebid": { + "storedbidresponse": [ + { + "bidder": "appnexus", + "id": "bidResponseId3" + } + ] + } + } + } + ], + "user": { + "yob": 1989 + } + }, + "expectedReturnCode": 200 +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp-case-not-matching-bidder-name.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp-case-not-matching-bidder-name.json new file mode 100644 index 00000000000..bcebe81472d --- /dev/null +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp-case-not-matching-bidder-name.json @@ -0,0 +1,39 @@ +{ + "description": "request with impression with stored bid response with case not matching bidder name", + "mockBidRequest": { + "id": "request-with-stored-resp", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "imp-id3", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "appnexus": { + "placementId": 12883451 + }, + "prebid": { + "storedbidresponse": [ + { + "bidder": "APPNEXUS", + "id": "bidResponseId3" + } + ] + } + } + } + ], + "user": { + "yob": 1989 + } + }, + "expectedReturnCode": 200 +} \ No newline at end of file diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp-non-existing-bidder-name.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp-non-existing-bidder-name.json new file mode 100644 index 00000000000..d8685307e20 --- /dev/null +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp-non-existing-bidder-name.json @@ -0,0 +1,34 @@ +{ + "description": "request with impression with stored bid response", + "mockBidRequest": { + "id": "request-with-stored-resp", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "imp-id1", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidderABC": {}, + "prebid": { + "storedbidresponse": [ + {"bidder":"bidderABC", "id": "bidResponseId1"} + ] + } + } + } + ], + "user": { + "yob": 1989 + } + }, + "expectedReturnCode": 200 +} diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp.json index e72cce49355..692a34c4f41 100644 --- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp.json +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/imp-with-stored-bid-resp.json @@ -17,12 +17,10 @@ ] }, "ext": { - "appnexus": { - "placementId": 12883451 - }, + "telaria": {}, "prebid": { "storedbidresponse": [ - {"bidder":"testBidder1", "id": "bidResponseId1"} + {"bidder":"telaria", "id": "bidResponseId1"} ] } } diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-two-imps-stored-bid-responses.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-two-imps-stored-bid-responses.json index 5906eb9149c..09b4bc57746 100644 --- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-two-imps-stored-bid-responses.json +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-two-imps-stored-bid-responses.json @@ -17,12 +17,12 @@ ] }, "ext": { - "appnexus": { + "telaria": { "placementId": 12883451 }, "prebid": { "storedbidresponse": [ - {"bidder":"testBidder1", "id": "bidResponseId1"} + {"bidder":"telaria", "id": "bidResponseId1"} ] } } @@ -38,12 +38,10 @@ ] }, "ext": { - "appnexus": { - "placementId": 12883451 - }, + "amx": {}, "prebid": { "storedbidresponse": [ - {"bidder":"testBidder2", "id": "bidResponseId2"} + {"bidder":"amx", "id": "bidResponseId2"} ] } } diff --git a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-two-imps-with-and-without-stored-bid-responses.json b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-two-imps-with-and-without-stored-bid-responses.json index 6122e4df066..5387a9d61ae 100644 --- a/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-two-imps-with-and-without-stored-bid-responses.json +++ b/endpoints/openrtb2/sample-requests/valid-whole/supplementary/req-two-imps-with-and-without-stored-bid-responses.json @@ -33,12 +33,12 @@ ] }, "ext": { - "appnexus": { + "amx": { "placementId": 12883451 }, "prebid": { "storedbidresponse": [ - {"bidder":"testBidder2", "id": "bidResponseId2"} + {"bidder":"amx", "id": "bidResponseId2"} ] } } diff --git a/endpoints/openrtb2/test_utils.go b/endpoints/openrtb2/test_utils.go index a1db828e90b..9ad0cf83fe2 100644 --- a/endpoints/openrtb2/test_utils.go +++ b/endpoints/openrtb2/test_utils.go @@ -16,30 +16,30 @@ import ( "github.com/buger/jsonparser" "github.com/julienschmidt/httprouter" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/analytics" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/experiment/adscert" - "github.com/prebid/prebid-server/floors" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/metrics" - metricsConfig "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - pbc "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/util/iputil" - "github.com/prebid/prebid-server/util/uuidutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/analytics" + analyticsBuild "github.com/prebid/prebid-server/v2/analytics/build" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/experiment/adscert" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/metrics" + metricsConfig "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + pbc "github.com/prebid/prebid-server/v2/prebid_cache_client" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/v2/util/iputil" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/uuidutil" jsonpatch "gopkg.in/evanphx/json-patch.v4" ) @@ -85,7 +85,6 @@ type testCase struct { type testConfigValues struct { AccountRequired bool `json:"accountRequired"` AliasJSON string `json:"aliases"` - BlacklistedAccounts []string `json:"blacklistedAccts"` BlacklistedApps []string `json:"blacklistedApps"` DisabledAdapters []string `json:"disabledAdapters"` CurrencyRates map[string]map[string]float64 `json:"currencyRates"` @@ -928,13 +927,12 @@ func (s mockCurrencyRatesClient) handle(w http.ResponseWriter, req *http.Request s.data.DataAsOfRaw = "2018-09-12" // Marshal the response and http write it - currencyServerJsonResponse, err := json.Marshal(&s.data) + currencyServerJsonResponse, err := jsonutil.Marshal(&s.data) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Write(currencyServerJsonResponse) - return } // mockBidderHandler carries mock bidder server information that will be read from JSON test files @@ -953,13 +951,13 @@ func (b mockBidderHandler) bid(w http.ResponseWriter, req *http.Request) { // Unmarshal exit if error var openrtb2Request openrtb2.BidRequest - if err := json.Unmarshal(buf.Bytes(), &openrtb2Request); err != nil { + if err := jsonutil.UnmarshalValid(buf.Bytes(), &openrtb2Request); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } var openrtb2ImpExt map[string]json.RawMessage - if err := json.Unmarshal(openrtb2Request.Imp[0].Ext, &openrtb2ImpExt); err != nil { + if err := jsonutil.UnmarshalValid(openrtb2Request.Imp[0].Ext, &openrtb2ImpExt); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } @@ -990,13 +988,12 @@ func (b mockBidderHandler) bid(w http.ResponseWriter, req *http.Request) { } // Marshal the response and http write it - serverJsonResponse, err := json.Marshal(&serverResponseObject) + serverJsonResponse, err := jsonutil.Marshal(&serverResponseObject) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Write(serverJsonResponse) - return } // mockAdapter is a mock impression-splitting adapter @@ -1021,7 +1018,7 @@ func (a mockAdapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *ada for _, imp := range request.Imp { requestCopy.Imp = []openrtb2.Imp{imp} - requestJSON, err := json.Marshal(request) + requestJSON, err := jsonutil.Marshal(request) if err != nil { errors = append(errors, err) continue @@ -1054,7 +1051,7 @@ func (a mockAdapter) MakeBids(request *openrtb2.BidRequest, requestData *adapter } var publisherResponse openrtb2.BidResponse - if err := json.Unmarshal(responseData.Body, &publisherResponse); err != nil { + if err := jsonutil.UnmarshalValid(responseData.Body, &publisherResponse); err != nil { return nil, []error{err} } @@ -1118,7 +1115,7 @@ func parseTestData(fileData []byte, testFile string) (testCase, error) { jsonTestConfig, _, _, err = jsonparser.Get(fileData, "config") if err == nil { - if err = json.Unmarshal(jsonTestConfig, parsedTestData.Config); err != nil { + if err = jsonutil.UnmarshalValid(jsonTestConfig, parsedTestData.Config); err != nil { return parsedTestData, fmt.Errorf("Error unmarshaling root.config from file %s. Desc: %v.", testFile, err) } } @@ -1156,18 +1153,6 @@ func (tc *testConfigValues) getBlacklistedAppMap() map[string]bool { return blacklistedAppMap } -func (tc *testConfigValues) getBlackListedAccountMap() map[string]bool { - var blacklistedAccountMap map[string]bool - - if len(tc.BlacklistedAccounts) > 0 { - blacklistedAccountMap = make(map[string]bool, len(tc.BlacklistedAccounts)) - for _, account := range tc.BlacklistedAccounts { - blacklistedAccountMap[account] = true - } - } - return blacklistedAccountMap -} - // exchangeTestWrapper is a wrapper that asserts the openrtb2 bid request just before the HoldAuction call type exchangeTestWrapper struct { ex exchange.Exchange @@ -1220,7 +1205,7 @@ func buildTestExchange(testCfg *testConfigValues, adapterMap map[openrtb_ext.Bid mockFetcher, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), - &floors.PriceFloorFetcher{}, + nil, ) testExchange = &exchangeTestWrapper{ @@ -1281,10 +1266,10 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han storedResponseFetcher = empty_fetcher.EmptyFetcher{} } - var accountFetcher stored_requests.AccountFetcher - accountFetcher = &mockAccountFetcher{ + accountFetcher := &mockAccountFetcher{ data: map[string]json.RawMessage{ "malformed_acct": json.RawMessage(`{"disabled":"invalid type"}`), + "disabled_acct": json.RawMessage(`{"disabled":true}`), }, } @@ -1293,7 +1278,7 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han planBuilder = hooks.EmptyPlanBuilder{} } - var endpointBuilder func(uuidutil.UUIDGenerator, exchange.Exchange, openrtb_ext.BidderParamValidator, stored_requests.Fetcher, stored_requests.AccountFetcher, *config.Configuration, metrics.MetricsEngine, analytics.PBSAnalyticsModule, map[string]string, []byte, map[string]openrtb_ext.BidderName, stored_requests.Fetcher, hooks.ExecutionPlanBuilder, *exchange.TmaxAdjustmentsPreprocessed) (httprouter.Handle, error) + var endpointBuilder func(uuidutil.UUIDGenerator, exchange.Exchange, openrtb_ext.BidderParamValidator, stored_requests.Fetcher, stored_requests.AccountFetcher, *config.Configuration, metrics.MetricsEngine, analytics.Runner, map[string]string, []byte, map[string]openrtb_ext.BidderName, stored_requests.Fetcher, hooks.ExecutionPlanBuilder, *exchange.TmaxAdjustmentsPreprocessed) (httprouter.Handle, error) switch test.endpointType { case AMP_ENDPOINT: @@ -1310,7 +1295,7 @@ func buildTestEndpoint(test testCase, cfg *config.Configuration) (httprouter.Han accountFetcher, cfg, met, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), disabledBidders, []byte(test.Config.AliasJSON), bidderMap, @@ -1608,7 +1593,7 @@ var entryPointHookUpdate = hooks.HookWrapper[hookstage.Entrypoint]{ ch := hookstage.ChangeSet[hookstage.EntrypointPayload]{} ch.AddMutation(func(payload hookstage.EntrypointPayload) (hookstage.EntrypointPayload, error) { - body, err := jsonpatch.MergePatch(payload.Body, []byte(`{"tmax":50}`)) + body, err := jsonpatch.MergePatch(payload.Body, []byte(`{"tmax":600}`)) if err == nil { payload.Body = body } diff --git a/endpoints/openrtb2/video_auction.go b/endpoints/openrtb2/video_auction.go index b074d67eedf..fed79dd4a5d 100644 --- a/endpoints/openrtb2/video_auction.go +++ b/endpoints/openrtb2/video_auction.go @@ -2,7 +2,6 @@ package openrtb2 import ( "context" - "encoding/json" "errors" "fmt" "io" @@ -17,28 +16,29 @@ import ( "github.com/gofrs/uuid" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/ortb" - "github.com/prebid/prebid-server/privacy" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/ortb" + "github.com/prebid/prebid-server/v2/privacy" jsonpatch "gopkg.in/evanphx/json-patch.v4" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/iputil" - "github.com/prebid/prebid-server/util/ptrutil" - "github.com/prebid/prebid-server/util/uuidutil" - "github.com/prebid/prebid-server/version" + accountService "github.com/prebid/prebid-server/v2/account" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/prebid_cache_client" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/iputil" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/prebid/prebid-server/v2/util/uuidutil" + "github.com/prebid/prebid-server/v2/version" ) var defaultRequestTimeout int64 = 5000 @@ -52,7 +52,7 @@ func NewVideoEndpoint( accounts stored_requests.AccountFetcher, cfg *config.Configuration, met metrics.MetricsEngine, - pbsAnalytics analytics.PBSAnalyticsModule, + analyticsRunner analytics.Runner, disabledBidders map[string]string, defReqJSON []byte, bidderMap map[string]openrtb_ext.BidderName, @@ -82,7 +82,7 @@ func NewVideoEndpoint( accounts, cfg, met, - pbsAnalytics, + analyticsRunner, disabledBidders, defRequest, defReqJSON, @@ -92,7 +92,8 @@ func NewVideoEndpoint( ipValidator, empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, - tmaxAdjustments}).VideoAuctionEndpoint), nil + tmaxAdjustments, + openrtb_ext.NormalizeBidderName}).VideoAuctionEndpoint), nil } /* @@ -149,6 +150,8 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re } debugLog.DebugEnabledOrOverridden = debugLog.Enabled || debugLog.DebugOverride + activityControl := privacy.ActivityControl{} + defer func() { if len(debugLog.CacheKey) > 0 && vo.VideoResponse == nil { err := debugLog.PutDebugLogError(deps.cache, deps.cfg.CacheURL.ExpectedTimeMillis, vo.Errors) @@ -158,7 +161,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re } deps.metricsEngine.RecordRequest(labels) deps.metricsEngine.RecordRequestTime(labels, time.Since(start)) - deps.analytics.LogVideoObject(&vo) + deps.analytics.LogVideoObject(&vo, activityControl) }() w.Header().Set("X-Prebid", version.BuildXPrebidHeader(version.Ver)) @@ -176,7 +179,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re resolvedRequest := requestJson if debugLog.DebugEnabledOrOverridden { debugLog.Data.Request = string(requestJson) - if headerBytes, err := json.Marshal(r.Header); err == nil { + if headerBytes, err := jsonutil.Marshal(r.Header); err == nil { debugLog.Data.Headers = string(headerBytes) } else { debugLog.Data.Headers = fmt.Sprintf("Unable to marshal headers data: %s", err.Error()) @@ -216,7 +219,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re var bidReq = &openrtb2.BidRequest{} if deps.defaultRequest { - if err := json.Unmarshal(deps.defReqJSON, bidReq); err != nil { + if err := jsonutil.UnmarshalValid(deps.defReqJSON, bidReq); err != nil { err = fmt.Errorf("Invalid JSON in Default Request Settings: %s", err) handleError(&labels, w, []error{err}, &vo, &debugLog) return @@ -244,7 +247,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re for _, podEr := range podErrors { resPodErr = append(resPodErr, strings.Join(podEr.ErrMsgs, ", ")) } - err := errors.New(fmt.Sprintf("all pods are incorrect: %s", strings.Join(resPodErr, "; "))) + err := fmt.Errorf("all pods are incorrect: %s", strings.Join(resPodErr, "; ")) errL = append(errL, err) handleError(&labels, w, errL, &vo, &debugLog) return @@ -264,12 +267,6 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re return } - errL = deps.validateRequest(bidReqWrapper, false, false, nil, false) - if errortypes.ContainsFatalError(errL) { - handleError(&labels, w, errL, &vo, &debugLog) - return - } - ctx := context.Background() timeout := deps.cfg.AuctionTimeouts.LimitAuctionTimeout(time.Duration(bidReqWrapper.TMax) * time.Millisecond) if timeout > 0 { @@ -303,7 +300,13 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re return } - activityControl := privacy.NewActivityControl(&account.Privacy) + errL = deps.validateRequest(account, r, bidReqWrapper, false, false, nil, false) + if errortypes.ContainsFatalError(errL) { + handleError(&labels, w, errL, &vo, &debugLog) + return + } + + activityControl = privacy.NewActivityControl(&account.Privacy) secGPC := r.Header.Get("Sec-GPC") auctionRequest := &exchange.AuctionRequest{ @@ -371,8 +374,7 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re vo.VideoResponse = bidResp - resp, err := json.Marshal(bidResp) - //resp, err := json.Marshal(response) + resp, err := jsonutil.Marshal(bidResp) if err != nil { errL := []error{err} handleError(&labels, w, errL, &vo, &debugLog) @@ -381,7 +383,6 @@ func (deps *endpointDeps) VideoAuctionEndpoint(w http.ResponseWriter, r *http.Re w.Header().Set("Content-Type", "application/json") w.Write(resp) - } func cleanupVideoBidRequest(videoReq *openrtb_ext.BidRequestVideo, podErrors []PodError) *openrtb_ext.BidRequestVideo { @@ -403,7 +404,7 @@ func handleError(labels *metrics.Labels, w http.ResponseWriter, errL []error, vo var status int = http.StatusInternalServerError for _, er := range errL { erVal := errortypes.ReadCode(er) - if erVal == errortypes.BlacklistedAppErrorCode || erVal == errortypes.BlacklistedAcctErrorCode { + if erVal == errortypes.BlacklistedAppErrorCode || erVal == errortypes.AccountDisabledErrorCode { status = http.StatusServiceUnavailable labels.RequestStatus = metrics.RequestStatusBlacklisted break @@ -504,7 +505,7 @@ func (deps *endpointDeps) loadStoredImp(storedImpId string) (openrtb2.Imp, []err return impr, err } - if err := json.Unmarshal(imp[storedImpId], &impr); err != nil { + if err := jsonutil.UnmarshalValid(imp[storedImpId], &impr); err != nil { return impr, []error{err} } return impr, nil @@ -533,7 +534,7 @@ func buildVideoResponse(bidresponse *openrtb2.BidResponse, podErrors []PodError) anyBidsReturned = true var tempRespBidExt openrtb_ext.ExtBid - if err := json.Unmarshal(bid.Ext, &tempRespBidExt); err != nil { + if err := jsonutil.UnmarshalValid(bid.Ext, &tempRespBidExt); err != nil { return nil, err } if tempRespBidExt.Prebid.Targeting[formatTargetingKey(openrtb_ext.HbVastCacheKey, seatBid.Seat)] == "" { @@ -555,7 +556,7 @@ func buildVideoResponse(bidresponse *openrtb2.BidResponse, podErrors []PodError) if adPod == nil { adPod = &openrtb_ext.AdPod{ PodId: podId, - Targeting: make([]openrtb_ext.VideoTargeting, 0, 0), + Targeting: make([]openrtb_ext.VideoTargeting, 0), } adPods = append(adPods, adPod) } @@ -620,22 +621,15 @@ func getVideoStoredRequestId(request []byte) (string, error) { func mergeData(videoRequest *openrtb_ext.BidRequestVideo, bidRequest *openrtb2.BidRequest) error { if videoRequest.Site != nil { bidRequest.Site = videoRequest.Site - if &videoRequest.Content != nil { - bidRequest.Site.Content = &videoRequest.Content - } + bidRequest.Site.Content = &videoRequest.Content } if videoRequest.App != nil { bidRequest.App = videoRequest.App - if &videoRequest.Content != nil { - bidRequest.App.Content = &videoRequest.Content - } - } - - if &videoRequest.Device != nil { - bidRequest.Device = &videoRequest.Device + bidRequest.App.Content = &videoRequest.Content } + bidRequest.Device = &videoRequest.Device bidRequest.User = videoRequest.User if len(videoRequest.BCat) != 0 { @@ -709,13 +703,13 @@ func createBidExtension(videoRequest *openrtb_ext.BidRequestVideo) ([]byte, erro } extReq := openrtb_ext.ExtRequest{Prebid: prebid} - return json.Marshal(extReq) + return jsonutil.Marshal(extReq) } func (deps *endpointDeps) parseVideoRequest(request []byte, headers http.Header) (req *openrtb_ext.BidRequestVideo, errs []error, podErrors []PodError) { req = &openrtb_ext.BidRequestVideo{} - if err := json.Unmarshal(request, &req); err != nil { + if err := jsonutil.UnmarshalValid(request, &req); err != nil { errs = []error{err} return } @@ -768,7 +762,7 @@ func (deps *endpointDeps) validateVideoRequest(req *openrtb_ext.BidRequestVideo) err := errors.New("request missing required field: PodConfig.Pods") errL = append(errL, err) } - podErrors := make([]PodError, 0, 0) + podErrors := make([]PodError, 0) podIdsSet := make(map[int]bool) for ind, pod := range req.PodConfig.Pods { podErr := PodError{} diff --git a/endpoints/openrtb2/video_auction_test.go b/endpoints/openrtb2/video_auction_test.go index e49e8b689a2..caa152c1f99 100644 --- a/endpoints/openrtb2/video_auction_test.go +++ b/endpoints/openrtb2/video_auction_test.go @@ -11,21 +11,23 @@ import ( "strings" "testing" - "github.com/prebid/prebid-server/analytics" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/metrics" - metricsConfig "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/util/ptrutil" - - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/v2/analytics" + analyticsBuild "github.com/prebid/prebid-server/v2/analytics/build" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/metrics" + metricsConfig "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/prebid_cache_client" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" + + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" gometrics "github.com/rcrowley/go-metrics" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -46,13 +48,13 @@ func TestVideoEndpointImpressionsNumber(t *testing.T) { respBytes := recorder.Body.Bytes() resp := &openrtb_ext.BidResponseVideo{} - if err := json.Unmarshal(respBytes, resp); err != nil { + if err := jsonutil.UnmarshalValid(respBytes, resp); err != nil { t.Fatalf("Unable to unmarshal response.") } assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request") - assert.Equal(t, string(ex.lastRequest.Site.Page), "prebid.com", "Incorrect site page in request") - assert.Equal(t, ex.lastRequest.Site.Content.Series, "TvName", "Incorrect site content series in request") + assert.Equal(t, "prebid.com", string(ex.lastRequest.Site.Page), "Incorrect site page in request") + assert.Equal(t, "TvName", ex.lastRequest.Site.Content.Series, "Incorrect site content series in request") assert.Len(t, resp.AdPods, 5, "Incorrect number of Ad Pods in response") assert.Len(t, resp.AdPods[0].Targeting, 4, "Incorrect Targeting data in response") @@ -61,8 +63,8 @@ func TestVideoEndpointImpressionsNumber(t *testing.T) { assert.Len(t, resp.AdPods[3].Targeting, 1, "Incorrect Targeting data in response") assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response") - assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s", "Incorrect number of Ad Pods in response") - assert.Equal(t, resp.AdPods[0].Targeting[0].HbDeal, "ABC_123", "If DealID exists in bid response, hb_deal targeting needs to be added to resp") + assert.Equal(t, "20.00_395_30s", resp.AdPods[4].Targeting[0].HbPbCatDur, "Incorrect number of Ad Pods in response") + assert.Equal(t, "ABC_123", resp.AdPods[0].Targeting[0].HbDeal, "If DealID exists in bid response, hb_deal targeting needs to be added to resp") } func TestVideoEndpointImpressionsDuration(t *testing.T) { @@ -79,26 +81,26 @@ func TestVideoEndpointImpressionsDuration(t *testing.T) { } var extData openrtb_ext.ExtRequest - json.Unmarshal(ex.lastRequest.Ext, &extData) + jsonutil.UnmarshalValid(ex.lastRequest.Ext, &extData) assert.NotNil(t, extData.Prebid.Targeting.IncludeBidderKeys, "Request ext incorrect: IncludeBidderKeys should be true ") assert.True(t, *extData.Prebid.Targeting.IncludeBidderKeys, "Request ext incorrect: IncludeBidderKeys should be true ") assert.Len(t, ex.lastRequest.Imp, 22, "Incorrect number of impressions in request") - assert.Equal(t, ex.lastRequest.Imp[0].ID, "1_0", "Incorrect impression id in request") - assert.Equal(t, ex.lastRequest.Imp[0].Video.MaxDuration, int64(15), "Incorrect impression max duration in request") - assert.Equal(t, ex.lastRequest.Imp[0].Video.MinDuration, int64(15), "Incorrect impression min duration in request") + assert.Equal(t, "1_0", ex.lastRequest.Imp[0].ID, "Incorrect impression id in request") + assert.Equal(t, int64(15), ex.lastRequest.Imp[0].Video.MaxDuration, "Incorrect impression max duration in request") + assert.Equal(t, int64(15), ex.lastRequest.Imp[0].Video.MinDuration, "Incorrect impression min duration in request") - assert.Equal(t, ex.lastRequest.Imp[6].ID, "1_6", "Incorrect impression id in request") - assert.Equal(t, ex.lastRequest.Imp[6].Video.MaxDuration, int64(30), "Incorrect impression max duration in request") - assert.Equal(t, ex.lastRequest.Imp[6].Video.MinDuration, int64(30), "Incorrect impression min duration in request") + assert.Equal(t, "1_6", ex.lastRequest.Imp[6].ID, "Incorrect impression id in request") + assert.Equal(t, int64(30), ex.lastRequest.Imp[6].Video.MaxDuration, "Incorrect impression max duration in request") + assert.Equal(t, int64(30), ex.lastRequest.Imp[6].Video.MinDuration, "Incorrect impression min duration in request") - assert.Equal(t, ex.lastRequest.Imp[12].ID, "2_0", "Incorrect impression id in request") - assert.Equal(t, ex.lastRequest.Imp[12].Video.MaxDuration, int64(15), "Incorrect impression max duration in request") - assert.Equal(t, ex.lastRequest.Imp[12].Video.MinDuration, int64(15), "Incorrect impression min duration in request") + assert.Equal(t, "2_0", ex.lastRequest.Imp[12].ID, "Incorrect impression id in request") + assert.Equal(t, int64(15), ex.lastRequest.Imp[12].Video.MaxDuration, "Incorrect impression max duration in request") + assert.Equal(t, int64(15), ex.lastRequest.Imp[12].Video.MinDuration, "Incorrect impression min duration in request") - assert.Equal(t, ex.lastRequest.Imp[17].ID, "2_5", "Incorrect impression id in request") - assert.Equal(t, ex.lastRequest.Imp[17].Video.MaxDuration, int64(30), "Incorrect impression max duration in request") - assert.Equal(t, ex.lastRequest.Imp[17].Video.MinDuration, int64(30), "Incorrect impression min duration in request") + assert.Equal(t, "2_5", ex.lastRequest.Imp[17].ID, "Incorrect impression id in request") + assert.Equal(t, int64(30), ex.lastRequest.Imp[17].Video.MaxDuration, "Incorrect impression max duration in request") + assert.Equal(t, int64(30), ex.lastRequest.Imp[17].Video.MinDuration, "Incorrect impression min duration in request") } func TestCreateBidExtension(t *testing.T) { @@ -134,11 +136,11 @@ func TestCreateBidExtension(t *testing.T) { resExt := &openrtb_ext.ExtRequest{} - if err := json.Unmarshal(res, &resExt); err != nil { + if err := jsonutil.UnmarshalValid(res, &resExt); err != nil { assert.Fail(t, "Unable to unmarshal bid extension") } - assert.Equal(t, resExt.Prebid.Targeting.DurationRangeSec, durationRange, "Duration range seconds is incorrect") - assert.Equal(t, resExt.Prebid.Targeting.PriceGranularity.Ranges, priceGranRanges, "Price granularity is incorrect") + assert.Equal(t, durationRange, resExt.Prebid.Targeting.DurationRangeSec, "Duration range seconds is incorrect") + assert.Equal(t, priceGranRanges, resExt.Prebid.Targeting.PriceGranularity.Ranges, "Price granularity is incorrect") } func TestCreateBidExtensionTargeting(t *testing.T) { @@ -177,13 +179,13 @@ func TestVideoEndpointDebugQueryTrue(t *testing.T) { respBytes := recorder.Body.Bytes() resp := &openrtb_ext.BidResponseVideo{} - if err := json.Unmarshal(respBytes, resp); err != nil { + if err := jsonutil.UnmarshalValid(respBytes, resp); err != nil { t.Fatalf("Unable to unmarshal response.") } assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request") - assert.Equal(t, string(ex.lastRequest.Site.Page), "prebid.com", "Incorrect site page in request") - assert.Equal(t, ex.lastRequest.Site.Content.Series, "TvName", "Incorrect site content series in request") + assert.Equal(t, "prebid.com", string(ex.lastRequest.Site.Page), "Incorrect site page in request") + assert.Equal(t, "TvName", ex.lastRequest.Site.Content.Series, "Incorrect site content series in request") assert.Len(t, resp.AdPods, 5, "Incorrect number of Ad Pods in response") assert.Len(t, resp.AdPods[0].Targeting, 4, "Incorrect Targeting data in response") @@ -192,7 +194,7 @@ func TestVideoEndpointDebugQueryTrue(t *testing.T) { assert.Len(t, resp.AdPods[3].Targeting, 1, "Incorrect Targeting data in response") assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response") - assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s", "Incorrect number of Ad Pods in response") + assert.Equal(t, "20.00_395_30s", resp.AdPods[4].Targeting[0].HbPbCatDur, "Incorrect number of Ad Pods in response") } func TestVideoEndpointDebugQueryFalse(t *testing.T) { @@ -215,13 +217,13 @@ func TestVideoEndpointDebugQueryFalse(t *testing.T) { respBytes := recorder.Body.Bytes() resp := &openrtb_ext.BidResponseVideo{} - if err := json.Unmarshal(respBytes, resp); err != nil { + if err := jsonutil.UnmarshalValid(respBytes, resp); err != nil { t.Fatalf("Unable to unmarshal response.") } assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request") - assert.Equal(t, string(ex.lastRequest.Site.Page), "prebid.com", "Incorrect site page in request") - assert.Equal(t, ex.lastRequest.Site.Content.Series, "TvName", "Incorrect site content series in request") + assert.Equal(t, "prebid.com", string(ex.lastRequest.Site.Page), "Incorrect site page in request") + assert.Equal(t, "TvName", ex.lastRequest.Site.Content.Series, "Incorrect site content series in request") assert.Len(t, resp.AdPods, 5, "Incorrect number of Ad Pods in response") assert.Len(t, resp.AdPods[0].Targeting, 4, "Incorrect Targeting data in response") @@ -230,7 +232,7 @@ func TestVideoEndpointDebugQueryFalse(t *testing.T) { assert.Len(t, resp.AdPods[3].Targeting, 1, "Incorrect Targeting data in response") assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response") - assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s", "Incorrect number of Ad Pods in response") + assert.Equal(t, "20.00_395_30s", resp.AdPods[4].Targeting[0].HbPbCatDur, "Incorrect number of Ad Pods in response") } func TestVideoEndpointDebugError(t *testing.T) { @@ -248,7 +250,7 @@ func TestVideoEndpointDebugError(t *testing.T) { t.Fatalf("Cache was not called when it should have been") } - assert.Equal(t, recorder.Code, 500, "Should catch error in request") + assert.Equal(t, 500, recorder.Code, "Should catch error in request") } func TestVideoEndpointDebugNoAdPods(t *testing.T) { @@ -271,7 +273,7 @@ func TestVideoEndpointDebugNoAdPods(t *testing.T) { respBytes := recorder.Body.Bytes() resp := &openrtb_ext.BidResponseVideo{} - if err := json.Unmarshal(respBytes, resp); err != nil { + if err := jsonutil.UnmarshalValid(respBytes, resp); err != nil { t.Fatalf("Unable to unmarshal response.") } @@ -292,9 +294,9 @@ func TestVideoEndpointNoPods(t *testing.T) { deps := mockDeps(t, ex) deps.VideoAuctionEndpoint(recorder, req, nil) - errorMessage := string(recorder.Body.Bytes()) + errorMessage := recorder.Body.String() - assert.Equal(t, recorder.Code, 500, "Should catch error in request") + assert.Equal(t, 500, recorder.Code, "Should catch error in request") assert.Equal(t, "Critical error while running the video endpoint: request missing required field: PodConfig.DurationRangeSec request missing required field: PodConfig.Pods", errorMessage, "Incorrect request validation message") } @@ -739,7 +741,7 @@ func TestVideoBuildVideoResponsePodErrors(t *testing.T) { func TestVideoBuildVideoResponseNoBids(t *testing.T) { openRtbBidResp := openrtb2.BidResponse{} - podErrors := make([]PodError, 0, 0) + podErrors := make([]PodError, 0) openRtbBidResp.SeatBid = make([]openrtb2.SeatBid, 0) bidRespVideo, err := buildVideoResponse(&openRtbBidResp, podErrors) assert.NoError(t, err, "Error should be nil") @@ -808,7 +810,7 @@ func TestHandleError(t *testing.T) { { description: "Blocked account - return 503 with blocked metrics status", giveErrors: []error{ - &errortypes.BlacklistedAcct{}, + &errortypes.AccountDisabled{}, }, wantCode: 503, wantMetricsStatus: metrics.RequestStatusBlacklisted, @@ -1024,28 +1026,27 @@ func TestHandleErrorDebugLog(t *testing.T) { } func TestCreateImpressionTemplate(t *testing.T) { - imp := openrtb2.Imp{} imp.Video = &openrtb2.Video{} imp.Video.Protocols = []adcom1.MediaCreativeSubtype{1, 2} imp.Video.MIMEs = []string{"video/mp4"} - imp.Video.H = 200 - imp.Video.W = 400 + imp.Video.H = ptrutil.ToPtr[int64](200) + imp.Video.W = ptrutil.ToPtr[int64](400) imp.Video.PlaybackMethod = []adcom1.PlaybackMethod{5, 6} video := openrtb2.Video{} video.Protocols = []adcom1.MediaCreativeSubtype{3, 4} video.MIMEs = []string{"video/flv"} - video.H = 300 - video.W = 0 + video.H = ptrutil.ToPtr[int64](300) + video.W = ptrutil.ToPtr[int64](0) video.PlaybackMethod = []adcom1.PlaybackMethod{7, 8} res := createImpressionTemplate(imp, &video) - assert.Equal(t, res.Video.Protocols, []adcom1.MediaCreativeSubtype{3, 4}, "Incorrect video protocols") - assert.Equal(t, res.Video.MIMEs, []string{"video/flv"}, "Incorrect video MIMEs") - assert.Equal(t, int(res.Video.H), 300, "Incorrect video height") - assert.Equal(t, int(res.Video.W), 0, "Incorrect video width") - assert.Equal(t, res.Video.PlaybackMethod, []adcom1.PlaybackMethod{7, 8}, "Incorrect video playback method") + assert.Equal(t, []adcom1.MediaCreativeSubtype{3, 4}, res.Video.Protocols, "Incorrect video protocols") + assert.Equal(t, []string{"video/flv"}, res.Video.MIMEs, "Incorrect video MIMEs") + assert.Equal(t, ptrutil.ToPtr[int64](300), res.Video.H, "Incorrect video height") + assert.Equal(t, ptrutil.ToPtr[int64](0), res.Video.W, "Incorrect video width") + assert.Equal(t, []adcom1.PlaybackMethod{7, 8}, res.Video.PlaybackMethod, "Incorrect video playback method") } func TestCCPA(t *testing.T) { @@ -1090,7 +1091,7 @@ func TestCCPA(t *testing.T) { t.Fatalf("%s: The request never made it into the exchange.", test.description) } extRegs := &openrtb_ext.ExtRegs{} - if err := json.Unmarshal(ex.lastRequest.Regs.Ext, extRegs); err != nil { + if err := jsonutil.UnmarshalValid(ex.lastRequest.Regs.Ext, extRegs); err != nil { t.Fatalf("%s: Failed to unmarshal reg.ext in request to the exchange: %v", test.description, err) } if test.expectConsentString { @@ -1102,7 +1103,7 @@ func TestCCPA(t *testing.T) { // Validate HTTP Response responseBytes := httpResponseRecorder.Body.Bytes() response := &openrtb_ext.BidResponseVideo{} - if err := json.Unmarshal(responseBytes, response); err != nil { + if err := jsonutil.UnmarshalValid(responseBytes, response); err != nil { t.Fatalf("%s: Unable to unmarshal response.", test.description) } assert.Len(t, ex.lastRequest.Imp, 11, test.description+":imps") @@ -1124,18 +1125,18 @@ func TestVideoEndpointAppendBidderNames(t *testing.T) { } var extData openrtb_ext.ExtRequest - json.Unmarshal(ex.lastRequest.Ext, &extData) + jsonutil.UnmarshalValid(ex.lastRequest.Ext, &extData) assert.True(t, extData.Prebid.Targeting.AppendBidderNames, "Request ext incorrect: AppendBidderNames should be true ") respBytes := recorder.Body.Bytes() resp := &openrtb_ext.BidResponseVideo{} - if err := json.Unmarshal(respBytes, resp); err != nil { + if err := jsonutil.UnmarshalValid(respBytes, resp); err != nil { t.Fatalf("Unable to unmarshal response.") } assert.Len(t, ex.lastRequest.Imp, 11, "Incorrect number of impressions in request") - assert.Equal(t, string(ex.lastRequest.Site.Page), "prebid.com", "Incorrect site page in request") - assert.Equal(t, ex.lastRequest.Site.Content.Series, "TvName", "Incorrect site content series in request") + assert.Equal(t, "prebid.com", string(ex.lastRequest.Site.Page), "Incorrect site page in request") + assert.Equal(t, "TvName", ex.lastRequest.Site.Content.Series, "Incorrect site content series in request") assert.Len(t, resp.AdPods, 5, "Incorrect number of Ad Pods in response") assert.Len(t, resp.AdPods[0].Targeting, 4, "Incorrect Targeting data in response") @@ -1144,7 +1145,7 @@ func TestVideoEndpointAppendBidderNames(t *testing.T) { assert.Len(t, resp.AdPods[3].Targeting, 1, "Incorrect Targeting data in response") assert.Len(t, resp.AdPods[4].Targeting, 3, "Incorrect Targeting data in response") - assert.Equal(t, resp.AdPods[4].Targeting[0].HbPbCatDur, "20.00_395_30s_appnexus", "Incorrect number of Ad Pods in response") + assert.Equal(t, "20.00_395_30s_appnexus", resp.AdPods[4].Targeting[0].HbPbCatDur, "Incorrect number of Ad Pods in response") } func TestFormatTargetingKey(t *testing.T) { @@ -1226,6 +1227,7 @@ func mockDepsWithMetrics(t *testing.T, ex *mockExchangeVideo) (*endpointDeps, *m empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } return deps, metrics, mockModule } @@ -1235,11 +1237,11 @@ type mockAnalyticsModule struct { videoObjects []*analytics.VideoObject } -func (m *mockAnalyticsModule) LogAuctionObject(ao *analytics.AuctionObject) { +func (m *mockAnalyticsModule) LogAuctionObject(ao *analytics.AuctionObject, _ privacy.ActivityControl) { m.auctionObjects = append(m.auctionObjects, ao) } -func (m *mockAnalyticsModule) LogVideoObject(vo *analytics.VideoObject) { +func (m *mockAnalyticsModule) LogVideoObject(vo *analytics.VideoObject, _ privacy.ActivityControl) { m.videoObjects = append(m.videoObjects, vo) } @@ -1247,9 +1249,11 @@ func (m *mockAnalyticsModule) LogCookieSyncObject(cso *analytics.CookieSyncObjec func (m *mockAnalyticsModule) LogSetUIDObject(so *analytics.SetUIDObject) {} -func (m *mockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject) {} +func (m *mockAnalyticsModule) LogAmpObject(ao *analytics.AmpObject, _ privacy.ActivityControl) { +} -func (m *mockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent) {} +func (m *mockAnalyticsModule) LogNotificationEventObject(ne *analytics.NotificationEvent, _ privacy.ActivityControl) { +} func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps { return &endpointDeps{ @@ -1261,41 +1265,7 @@ func mockDeps(t *testing.T, ex *mockExchangeVideo) *endpointDeps { &mockAccountFetcher{data: mockVideoAccountData}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), - map[string]string{}, - false, - []byte{}, - openrtb_ext.BuildBidderMap(), - ex.cache, - regexp.MustCompile(`[<>]`), - hardcodedResponseIPValidator{response: true}, - empty_fetcher.EmptyFetcher{}, - hooks.EmptyPlanBuilder{}, - nil, - } -} - -func mockDepsInvalidPrivacy(t *testing.T, ex *mockExchangeVideo) *endpointDeps { - return &endpointDeps{ - fakeUUIDGenerator{}, - ex, - mockBidderParamValidator{}, - &mockVideoStoredReqFetcher{}, - &mockVideoStoredReqFetcher{}, - &mockAccountFetcher{data: mockVideoAccountData}, - &config.Configuration{MaxRequestSize: maxSize, - AccountDefaults: config.Account{ - Privacy: config.AccountPrivacy{ - AllowActivities: &config.AllowActivities{ - TransmitPreciseGeo: config.Activity{Rules: []config.ActivityRule{ - {Condition: config.ActivityCondition{ComponentName: []string{"bidderA.BidderB.bidderC"}}}, - }}, - }, - }, - }, - }, - &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -1306,6 +1276,7 @@ func mockDepsInvalidPrivacy(t *testing.T, ex *mockExchangeVideo) *endpointDeps { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } } @@ -1319,7 +1290,7 @@ func mockDepsAppendBidderNames(t *testing.T, ex *mockExchangeAppendBidderNames) empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -1330,6 +1301,7 @@ func mockDepsAppendBidderNames(t *testing.T, ex *mockExchangeAppendBidderNames) empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } return deps @@ -1345,7 +1317,7 @@ func mockDepsNoBids(t *testing.T, ex *mockExchangeVideoNoBids) *endpointDeps { empty_fetcher.EmptyFetcher{}, &config.Configuration{MaxRequestSize: maxSize}, &metricsConfig.NilMetricsEngine{}, - analyticsConf.NewPBSAnalytics(&config.Analytics{}), + analyticsBuild.New(&config.Analytics{}), map[string]string{}, false, []byte{}, @@ -1356,6 +1328,7 @@ func mockDepsNoBids(t *testing.T, ex *mockExchangeVideoNoBids) *endpointDeps { empty_fetcher.EmptyFetcher{}, hooks.EmptyPlanBuilder{}, nil, + openrtb_ext.NormalizeBidderName, } return edep diff --git a/endpoints/setuid.go b/endpoints/setuid.go index 520a133b51e..5d91b3c8165 100644 --- a/endpoints/setuid.go +++ b/endpoints/setuid.go @@ -12,18 +12,19 @@ import ( "github.com/julienschmidt/httprouter" gpplib "github.com/prebid/go-gpp" gppConstants "github.com/prebid/go-gpp/constants" - accountService "github.com/prebid/prebid-server/account" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/privacy" - gppPrivacy "github.com/prebid/prebid-server/privacy/gpp" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/httputil" - stringutil "github.com/prebid/prebid-server/util/stringutil" + accountService "github.com/prebid/prebid-server/v2/account" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/privacy" + gppPrivacy "github.com/prebid/prebid-server/v2/privacy/gpp" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/httputil" + stringutil "github.com/prebid/prebid-server/v2/util/stringutil" ) const ( @@ -36,7 +37,7 @@ const ( const uidCookieName = "uids" -func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]usersync.Syncer, gdprPermsBuilder gdpr.PermissionsBuilder, tcf2CfgBuilder gdpr.TCF2ConfigBuilder, pbsanalytics analytics.PBSAnalyticsModule, accountsFetcher stored_requests.AccountFetcher, metricsEngine metrics.MetricsEngine) httprouter.Handle { +func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]usersync.Syncer, gdprPermsBuilder gdpr.PermissionsBuilder, tcf2CfgBuilder gdpr.TCF2ConfigBuilder, analyticsRunner analytics.Runner, accountsFetcher stored_requests.AccountFetcher, metricsEngine metrics.MetricsEngine) httprouter.Handle { encoder := usersync.Base64Encoder{} decoder := usersync.Base64Decoder{} @@ -46,7 +47,7 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use Errors: make([]error, 0), } - defer pbsanalytics.LogSetUIDObject(&so) + defer analyticsRunner.LogSetUIDObject(&so) cookie := usersync.ReadCookie(r, decoder, &cfg.HostCookie) if !cookie.AllowSyncs() { @@ -125,7 +126,6 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use handleBadStatus(w, http.StatusBadRequest, metrics.SetUidBadRequest, err, metricsEngine, &so) return } - w.Write([]byte("Warning: " + err.Error())) } tcf2Cfg := tcf2CfgBuilder(cfg.GDPR.TCF2, account.GDPR) @@ -222,18 +222,14 @@ func extractGDPRInfo(query url.Values) (reqInfo gdpr.RequestInfo, err error) { // parseGDPRFromGPP parses and validates the "gpp_sid" and "gpp" query fields. func parseGDPRFromGPP(query url.Values) (gdpr.RequestInfo, error) { - var gdprSignal gdpr.Signal = gdpr.SignalAmbiguous - var gdprConsent string = "" - var err error - - gdprSignal, err = parseSignalFromGppSidStr(query.Get("gpp_sid")) + gdprSignal, err := parseSignalFromGppSidStr(query.Get("gpp_sid")) if err != nil { return gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous}, err } - gdprConsent, err = parseConsentFromGppStr(query.Get("gpp")) - if err != nil { - return gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous}, err + gdprConsent, errs := parseConsentFromGppStr(query.Get("gpp")) + if len(errs) > 0 { + return gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous}, errs[0] } return gdpr.RequestInfo{ @@ -305,13 +301,13 @@ func parseSignalFromGppSidStr(strSID string) (gdpr.Signal, error) { return gdprSignal, nil } -func parseConsentFromGppStr(gppQueryValue string) (string, error) { +func parseConsentFromGppStr(gppQueryValue string) (string, []error) { var gdprConsent string if len(gppQueryValue) > 0 { - gpp, err := gpplib.Parse(gppQueryValue) - if err != nil { - return "", err + gpp, errs := gpplib.Parse(gppQueryValue) + if len(errs) > 0 { + return "", errs } if i := gppPrivacy.IndexOfSID(gpp, gppConstants.SectionTCFEU2); i >= 0 { @@ -329,7 +325,13 @@ func getSyncer(query url.Values, syncersByBidder map[string]usersync.Syncer) (us return nil, "", errors.New(`"bidder" query param is required`) } - syncer, syncerExists := syncersByBidder[bidder] + // case insensitive comparison + bidderNormalized, bidderFound := openrtb_ext.NormalizeBidderName(bidder) + if !bidderFound { + return nil, "", errors.New("The bidder name provided is not supported by Prebid Server") + } + + syncer, syncerExists := syncersByBidder[bidderNormalized.String()] if !syncerExists { return nil, "", errors.New("The bidder name provided is not supported by Prebid Server") } @@ -340,7 +342,7 @@ func getSyncer(query url.Values, syncersByBidder map[string]usersync.Syncer) (us func isSyncerPriority(bidderNameFromSyncerQuery string, priorityGroups [][]string) bool { for _, group := range priorityGroups { for _, bidder := range group { - if bidderNameFromSyncerQuery == bidder { + if strings.EqualFold(bidderNameFromSyncerQuery, bidder) { return true } } @@ -356,7 +358,7 @@ func getResponseFormat(query url.Values, syncer usersync.Syncer) (string, error) formatEmpty := len(format) == 0 || format[0] == "" if !formatProvided || formatEmpty { - switch syncer.DefaultSyncType() { + switch syncer.DefaultResponseFormat() { case usersync.SyncTypeIFrame: return "b", nil case usersync.SyncTypeRedirect: diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go index 897538c35e5..d576b8a0093 100644 --- a/endpoints/setuid_test.go +++ b/endpoints/setuid_test.go @@ -12,18 +12,18 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/usersync" + "github.com/prebid/prebid-server/v2/analytics" + analyticsBuild "github.com/prebid/prebid-server/v2/analytics/build" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/usersync" "github.com/stretchr/testify/assert" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - metricsConf "github.com/prebid/prebid-server/metrics/config" + metricsConf "github.com/prebid/prebid-server/v2/metrics/config" ) func TestSetUIDEndpoint(t *testing.T) { @@ -34,6 +34,7 @@ func TestSetUIDEndpoint(t *testing.T) { gdprAllowsHostCookies bool gdprReturnsError bool gdprMalformed bool + formatOverride string expectedSyncs map[string]string expectedBody string expectedStatusCode int @@ -50,6 +51,16 @@ func TestSetUIDEndpoint(t *testing.T) { expectedHeaders: map[string]string{"Content-Type": "text/html", "Content-Length": "0"}, description: "Set uid for valid bidder", }, + { + uri: "/setuid?bidder=PUBMATIC&uid=123", + syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"}, + existingSyncs: nil, + gdprAllowsHostCookies: true, + expectedSyncs: map[string]string{"pubmatic": "123"}, + expectedStatusCode: http.StatusOK, + expectedHeaders: map[string]string{"Content-Type": "text/html", "Content-Length": "0"}, + description: "Set uid for valid bidder case insensitive", + }, { uri: "/setuid?bidder=appnexus&uid=123", syncersBidderNameToKey: map[string]string{"appnexus": "adnxs"}, @@ -207,15 +218,25 @@ func TestSetUIDEndpoint(t *testing.T) { }, { uri: "/setuid?bidder=pubmatic&uid=123&gpp_sid=2,4&gpp=DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA" + - "gdpr=1&gdpr_consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw", + "&gdpr=1&gdpr_consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw", + syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"}, + gdprAllowsHostCookies: true, + existingSyncs: nil, + expectedSyncs: map[string]string{"pubmatic": "123"}, + expectedStatusCode: http.StatusOK, + expectedHeaders: map[string]string{"Content-Type": "text/html", "Content-Length": "0"}, + description: "GPP value will be used over the one found in the deprecated GDPR consent field for iframe format", + }, + { + uri: "/setuid?f=i&bidder=pubmatic&uid=123&gpp_sid=2,4&gpp=DBABMA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA" + + "&gdpr=1&gdpr_consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw", syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"}, gdprAllowsHostCookies: true, existingSyncs: nil, expectedSyncs: map[string]string{"pubmatic": "123"}, expectedStatusCode: http.StatusOK, - expectedBody: "Warning: 'gpp' value will be used over the one found in the deprecated 'gdpr_consent' field.", - expectedHeaders: map[string]string{"Content-Type": "text/plain; charset=utf-8"}, - description: "Sets uid for a bidder allowed by GDPR in GPP, throws warning because GDPR legacy values weren't used", + expectedHeaders: map[string]string{"Content-Type": "image/png", "Content-Length": "86"}, + description: "GPP value will be used over the one found in the deprecated GDPR consent field for redirect format", }, { uri: "/setuid?bidder=pubmatic&uid=123&gdpr=1&gdpr_consent=malformed", @@ -326,14 +347,25 @@ func TestSetUIDEndpoint(t *testing.T) { expectedStatusCode: http.StatusBadRequest, expectedBody: "invalid gpp_sid encoding, must be a csv list of integers", }, + { + uri: "/setuid?bidder=pubmatic&uid=123", + syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"}, + existingSyncs: nil, + gdprAllowsHostCookies: true, + formatOverride: "i", + expectedSyncs: map[string]string{"pubmatic": "123"}, + expectedStatusCode: http.StatusOK, + expectedHeaders: map[string]string{"Content-Length": "86", "Content-Type": "image/png"}, + description: "Format not provided in URL, but formatOverride is defined", + }, } - analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{}) + analytics := analyticsBuild.New(&config.Analytics{}) metrics := &metricsConf.NilMetricsEngine{} for _, test := range testCases { response := doRequest(makeRequest(test.uri, test.existingSyncs), analytics, metrics, - test.syncersBidderNameToKey, test.gdprAllowsHostCookies, test.gdprReturnsError, test.gdprMalformed, false, 0, nil) + test.syncersBidderNameToKey, test.gdprAllowsHostCookies, test.gdprReturnsError, test.gdprMalformed, false, 0, nil, test.formatOverride) assert.Equal(t, test.expectedStatusCode, response.Code, "Test Case: %s. /setuid returned unexpected error code", test.description) if test.expectedSyncs != nil { @@ -362,7 +394,7 @@ func TestSetUIDEndpoint(t *testing.T) { func TestSetUIDPriorityEjection(t *testing.T) { decoder := usersync.Base64Decoder{} - analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{}) + analytics := analyticsBuild.New(&config.Analytics{}) syncersByBidder := map[string]string{ "pubmatic": "pubmatic", "syncer1": "syncer1", @@ -457,7 +489,7 @@ func TestSetUIDPriorityEjection(t *testing.T) { request.AddCookie(httpCookie) // Make Request to /setuid - response := doRequest(request, analytics, &metricsConf.NilMetricsEngine{}, syncersByBidder, true, false, false, false, test.givenMaxCookieSize, test.givenPriorityGroups) + response := doRequest(request, analytics, &metricsConf.NilMetricsEngine{}, syncersByBidder, true, false, false, false, test.givenMaxCookieSize, test.givenPriorityGroups, "") if test.expectedWarning != "" { assert.Equal(t, test.expectedWarning, response.Body.String(), test.description) @@ -543,7 +575,7 @@ func TestParseSignalFromGPPSID(t *testing.T) { func TestParseConsentFromGppStr(t *testing.T) { type testOutput struct { gdprConsent string - err error + err []error } testCases := []struct { desc string @@ -563,7 +595,7 @@ func TestParseConsentFromGppStr(t *testing.T) { inGppQuery: "malformed", expected: testOutput{ gdprConsent: "", - err: errors.New(`error parsing GPP header, base64 decoding: illegal base64 data at input byte 8`), + err: []error{errors.New(`error parsing GPP header, header must have type=3`)}, }, }, { @@ -587,7 +619,7 @@ func TestParseConsentFromGppStr(t *testing.T) { outConsent, outErr := parseConsentFromGppStr(tc.inGppQuery) assert.Equal(t, tc.expected.gdprConsent, outConsent, tc.desc) - assert.Equal(t, tc.expected.err, outErr, tc.desc) + assert.ElementsMatch(t, tc.expected.err, outErr, tc.desc) } } @@ -626,7 +658,7 @@ func TestParseGDPRFromGPP(t *testing.T) { inUri: "/setuid?gpp=malformed", expected: testOutput{ reqInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous}, - err: errors.New("error parsing GPP header, base64 decoding: illegal base64 data at input byte 8"), + err: errors.New("error parsing GPP header, header must have type=3"), }, }, { @@ -901,7 +933,7 @@ func TestExtractGDPRInfo(t *testing.T) { inUri: "/setuid?gpp=malformed&gpp_sid=2", expected: testOutput{ requestInfo: gdpr.RequestInfo{GDPRSignal: gdpr.SignalAmbiguous}, - err: errors.New("error parsing GPP header, base64 decoding: illegal base64 data at input byte 8"), + err: errors.New("error parsing GPP header, header must have type=3"), }, }, { @@ -1093,7 +1125,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) { cfgAccountRequired bool expectedResponseCode int expectedMetrics func(*metrics.MetricsEngineMock) - expectedAnalytics func(*MockAnalytics) + expectedAnalytics func(*MockAnalyticsRunner) }{ { description: "Success - Sync", @@ -1106,7 +1138,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) { m.On("RecordSetUid", metrics.SetUidOK).Once() m.On("RecordSyncerSet", "pubmatic", metrics.SyncerSetUidOK).Once() }, - expectedAnalytics: func(a *MockAnalytics) { + expectedAnalytics: func(a *MockAnalyticsRunner) { expected := analytics.SetUIDObject{ Status: 200, Bidder: "pubmatic", @@ -1128,7 +1160,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) { m.On("RecordSetUid", metrics.SetUidOK).Once() m.On("RecordSyncerSet", "pubmatic", metrics.SyncerSetUidCleared).Once() }, - expectedAnalytics: func(a *MockAnalytics) { + expectedAnalytics: func(a *MockAnalyticsRunner) { expected := analytics.SetUIDObject{ Status: 200, Bidder: "pubmatic", @@ -1149,7 +1181,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) { expectedMetrics: func(m *metrics.MetricsEngineMock) { m.On("RecordSetUid", metrics.SetUidOptOut).Once() }, - expectedAnalytics: func(a *MockAnalytics) { + expectedAnalytics: func(a *MockAnalyticsRunner) { expected := analytics.SetUIDObject{ Status: 401, Bidder: "", @@ -1170,7 +1202,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) { expectedMetrics: func(m *metrics.MetricsEngineMock) { m.On("RecordSetUid", metrics.SetUidSyncerUnknown).Once() }, - expectedAnalytics: func(a *MockAnalytics) { + expectedAnalytics: func(a *MockAnalyticsRunner) { expected := analytics.SetUIDObject{ Status: 400, Bidder: "", @@ -1191,7 +1223,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) { expectedMetrics: func(m *metrics.MetricsEngineMock) { m.On("RecordSetUid", metrics.SetUidBadRequest).Once() }, - expectedAnalytics: func(a *MockAnalytics) { + expectedAnalytics: func(a *MockAnalyticsRunner) { expected := analytics.SetUIDObject{ Status: 400, Bidder: "pubmatic", @@ -1212,7 +1244,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) { expectedMetrics: func(m *metrics.MetricsEngineMock) { m.On("RecordSetUid", metrics.SetUidBadRequest).Once() }, - expectedAnalytics: func(a *MockAnalytics) { + expectedAnalytics: func(a *MockAnalyticsRunner) { expected := analytics.SetUIDObject{ Status: 400, Bidder: "pubmatic", @@ -1233,7 +1265,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) { expectedMetrics: func(m *metrics.MetricsEngineMock) { m.On("RecordSetUid", metrics.SetUidGDPRHostCookieBlocked).Once() }, - expectedAnalytics: func(a *MockAnalytics) { + expectedAnalytics: func(a *MockAnalyticsRunner) { expected := analytics.SetUIDObject{ Status: 451, Bidder: "pubmatic", @@ -1244,27 +1276,6 @@ func TestSetUIDEndpointMetrics(t *testing.T) { a.On("LogSetUIDObject", &expected).Once() }, }, - { - description: "Blocked account", - uri: "/setuid?bidder=pubmatic&uid=123&account=blocked_acct", - cookies: []*usersync.Cookie{}, - syncersBidderNameToKey: map[string]string{"pubmatic": "pubmatic"}, - gdprAllowsHostCookies: true, - expectedResponseCode: 400, - expectedMetrics: func(m *metrics.MetricsEngineMock) { - m.On("RecordSetUid", metrics.SetUidAccountBlocked).Once() - }, - expectedAnalytics: func(a *MockAnalytics) { - expected := analytics.SetUIDObject{ - Status: 400, - Bidder: "pubmatic", - UID: "", - Errors: []error{errCookieSyncAccountBlocked}, - Success: false, - } - a.On("LogSetUIDObject", &expected).Once() - }, - }, { description: "Invalid account", uri: "/setuid?bidder=pubmatic&uid=123&account=unknown", @@ -1276,7 +1287,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) { expectedMetrics: func(m *metrics.MetricsEngineMock) { m.On("RecordSetUid", metrics.SetUidAccountInvalid).Once() }, - expectedAnalytics: func(a *MockAnalytics) { + expectedAnalytics: func(a *MockAnalyticsRunner) { expected := analytics.SetUIDObject{ Status: 400, Bidder: "pubmatic", @@ -1298,7 +1309,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) { expectedMetrics: func(m *metrics.MetricsEngineMock) { m.On("RecordSetUid", metrics.SetUidAccountConfigMalformed).Once() }, - expectedAnalytics: func(a *MockAnalytics) { + expectedAnalytics: func(a *MockAnalyticsRunner) { expected := analytics.SetUIDObject{ Status: 400, Bidder: "pubmatic", @@ -1318,14 +1329,14 @@ func TestSetUIDEndpointMetrics(t *testing.T) { cfgAccountRequired: true, expectedResponseCode: 400, expectedMetrics: func(m *metrics.MetricsEngineMock) { - m.On("RecordSetUid", metrics.SetUidBadRequest).Once() + m.On("RecordSetUid", metrics.SetUidAccountConfigMalformed).Once() }, - expectedAnalytics: func(a *MockAnalytics) { + expectedAnalytics: func(a *MockAnalyticsRunner) { expected := analytics.SetUIDObject{ Status: 400, Bidder: "pubmatic", UID: "", - Errors: []error{errors.New("unexpected end of JSON input")}, + Errors: []error{errCookieSyncAccountConfigMalformed}, Success: false, } a.On("LogSetUIDObject", &expected).Once() @@ -1334,7 +1345,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) { } for _, test := range testCases { - analyticsEngine := &MockAnalytics{} + analyticsEngine := &MockAnalyticsRunner{} test.expectedAnalytics(analyticsEngine) metricsEngine := &metrics.MetricsEngineMock{} @@ -1344,7 +1355,7 @@ func TestSetUIDEndpointMetrics(t *testing.T) { for _, v := range test.cookies { addCookie(req, v) } - response := doRequest(req, analyticsEngine, metricsEngine, test.syncersBidderNameToKey, test.gdprAllowsHostCookies, false, false, test.cfgAccountRequired, 0, nil) + response := doRequest(req, analyticsEngine, metricsEngine, test.syncersBidderNameToKey, test.gdprAllowsHostCookies, false, false, test.cfgAccountRequired, 0, nil, "") assert.Equal(t, test.expectedResponseCode, response.Code, test.description) analyticsEngine.AssertExpectations(t) @@ -1358,9 +1369,9 @@ func TestOptedOut(t *testing.T) { cookie.SetOptOut(true) addCookie(request, cookie) syncersBidderNameToKey := map[string]string{"pubmatic": "pubmatic"} - analytics := analyticsConf.NewPBSAnalytics(&config.Analytics{}) + analytics := analyticsBuild.New(&config.Analytics{}) metrics := &metricsConf.NilMetricsEngine{} - response := doRequest(request, analytics, metrics, syncersBidderNameToKey, true, false, false, false, 0, nil) + response := doRequest(request, analytics, metrics, syncersBidderNameToKey, true, false, false, false, 0, nil, "") assert.Equal(t, http.StatusUnauthorized, response.Code) } @@ -1456,6 +1467,30 @@ func TestGetResponseFormat(t *testing.T) { expectedFormat: "i", description: "parameter given is empty (by empty item), use default sync type redirect", }, + { + urlValues: url.Values{"f": []string{""}}, + syncer: fakeSyncer{key: "a", defaultSyncType: usersync.SyncTypeRedirect}, + expectedFormat: "i", + description: "parameter given is empty (by empty item), use default sync type redirect", + }, + { + urlValues: url.Values{"f": []string{}}, + syncer: fakeSyncer{key: "a", formatOverride: "i"}, + expectedFormat: "i", + description: "format not provided, but formatOverride is defined, expect i", + }, + { + urlValues: url.Values{"f": []string{}}, + syncer: fakeSyncer{key: "a", formatOverride: "b"}, + expectedFormat: "b", + description: "format not provided, but formatOverride is defined, expect b", + }, + { + urlValues: url.Values{"f": []string{}}, + syncer: fakeSyncer{key: "a", formatOverride: "b", defaultSyncType: usersync.SyncTypeRedirect}, + expectedFormat: "b", + description: "format not provided, default is defined but formatOverride is defined as well, expect b", + }, } for _, test := range testCases { @@ -1479,35 +1514,44 @@ func TestIsSyncerPriority(t *testing.T) { expected bool }{ { - name: "bidder-name-is-priority", - givenBidderNameFromSyncerQuery: "priorityBidder", - givenPriorityGroups: [][]string{ - {"priorityBidder"}, - {"2", "3"}, - }, - expected: true, + name: "priority-tier-1", + givenBidderNameFromSyncerQuery: "a", + givenPriorityGroups: [][]string{{"a"}}, + expected: true, }, { - name: "bidder-name-is-not-priority", - givenBidderNameFromSyncerQuery: "notPriorityBidderName", - givenPriorityGroups: [][]string{ - {"1"}, - {"2", "3"}, - }, - expected: false, + name: "priority-tier-other", + givenBidderNameFromSyncerQuery: "c", + givenPriorityGroups: [][]string{{"a"}, {"b", "c"}}, + expected: true, + }, + { + name: "priority-case-insensitive", + givenBidderNameFromSyncerQuery: "A", + givenPriorityGroups: [][]string{{"a"}}, + expected: true, }, { - name: "no-bidder-name-given", + name: "not-priority-empty", + givenBidderNameFromSyncerQuery: "a", + givenPriorityGroups: [][]string{}, + expected: false, + }, + { + name: "not-priority-not-defined", + givenBidderNameFromSyncerQuery: "a", + givenPriorityGroups: [][]string{{"b"}}, + expected: false, + }, + { + name: "no-bidder", givenBidderNameFromSyncerQuery: "", - givenPriorityGroups: [][]string{ - {"1"}, - {"2", "3"}, - }, - expected: false, + givenPriorityGroups: [][]string{{"b"}}, + expected: false, }, { - name: "no-priority-groups-given", - givenBidderNameFromSyncerQuery: "bidderName", + name: "no-priority-groups", + givenBidderNameFromSyncerQuery: "a", givenPriorityGroups: [][]string{}, expected: false, }, @@ -1546,12 +1590,9 @@ func makeRequest(uri string, existingSyncs map[string]string) *http.Request { return request } -func doRequest(req *http.Request, analytics analytics.PBSAnalyticsModule, metrics metrics.MetricsEngine, syncersBidderNameToKey map[string]string, gdprAllowsHostCookies, gdprReturnsError, gdprReturnsMalformedError, cfgAccountRequired bool, maxCookieSize int, priorityGroups [][]string) *httptest.ResponseRecorder { +func doRequest(req *http.Request, analytics analytics.Runner, metrics metrics.MetricsEngine, syncersBidderNameToKey map[string]string, gdprAllowsHostCookies, gdprReturnsError, gdprReturnsMalformedError, cfgAccountRequired bool, maxCookieSize int, priorityGroups [][]string, formatOverride string) *httptest.ResponseRecorder { cfg := config.Configuration{ AccountRequired: cfgAccountRequired, - BlacklistedAcctMap: map[string]bool{ - "blocked_acct": true, - }, AccountDefaults: config.Account{}, UserSync: config.UserSync{ PriorityGroups: priorityGroups, @@ -1580,7 +1621,7 @@ func doRequest(req *http.Request, analytics analytics.PBSAnalyticsModule, metric syncersByBidder := make(map[string]usersync.Syncer) for bidderName, syncerKey := range syncersBidderNameToKey { - syncersByBidder[bidderName] = fakeSyncer{key: syncerKey, defaultSyncType: usersync.SyncTypeIFrame} + syncersByBidder[bidderName] = fakeSyncer{key: syncerKey, defaultSyncType: usersync.SyncTypeIFrame, formatOverride: formatOverride} if priorityGroups == nil { cfg.UserSync.PriorityGroups = [][]string{{}} cfg.UserSync.PriorityGroups[0] = append(cfg.UserSync.PriorityGroups[0], bidderName) @@ -1671,14 +1712,22 @@ func (g *fakePermsSetUID) AuctionActivitiesAllowed(ctx context.Context, bidderCo type fakeSyncer struct { key string defaultSyncType usersync.SyncType + formatOverride string } func (s fakeSyncer) Key() string { return s.key } -func (s fakeSyncer) DefaultSyncType() usersync.SyncType { - return s.defaultSyncType +func (s fakeSyncer) DefaultResponseFormat() usersync.SyncType { + switch s.formatOverride { + case "b": + return usersync.SyncTypeIFrame + case "i": + return usersync.SyncTypeRedirect + default: + return s.defaultSyncType + } } func (s fakeSyncer) SupportsType(syncTypes []usersync.SyncType) bool { diff --git a/endpoints/version.go b/endpoints/version.go index 00d894963e6..f9e07da9a0d 100644 --- a/endpoints/version.go +++ b/endpoints/version.go @@ -5,6 +5,7 @@ import ( "net/http" "github.com/golang/glog" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) const versionEndpointValueNotSet = "not-set" @@ -29,7 +30,7 @@ func prepareVersionEndpointResponse(version, revision string) (json.RawMessage, revision = versionEndpointValueNotSet } - return json.Marshal(struct { + return jsonutil.Marshal(struct { Revision string `json:"revision"` Version string `json:"version"` }{ diff --git a/errortypes/code.go b/errortypes/code.go index 8a6a07c6773..813d2377b91 100644 --- a/errortypes/code.go +++ b/errortypes/code.go @@ -9,11 +9,14 @@ const ( BadServerResponseErrorCode FailedToRequestBidsErrorCode BidderTemporarilyDisabledErrorCode - BlacklistedAcctErrorCode + AccountDisabledErrorCode AcctRequiredErrorCode NoConversionRateErrorCode MalformedAcctErrorCode ModuleRejectionErrorCode + TmaxTimeoutErrorCode + FailedToMarshalErrorCode + FailedToUnmarshalErrorCode // NYC: shall we have different range for OW error codes to avoid change in codes with introduction of new PBS error codes. NoBidPriceErrorCode @@ -34,6 +37,8 @@ const ( AdServerTargetingWarningCode BidAdjustmentWarningCode FloorBidRejectionWarningCode + InvalidBidResponseDSAWarningCode + SecCookieDeprecationLenWarningCode AdpodPostFilteringWarningCode ) diff --git a/errortypes/errortypes.go b/errortypes/errortypes.go index f88b08f30c9..7019e9d04e2 100644 --- a/errortypes/errortypes.go +++ b/errortypes/errortypes.go @@ -20,6 +20,25 @@ func (err *Timeout) Severity() Severity { return SeverityFatal } +// TmaxTimeout should be used to flag that remaining tmax duration is not enough to get response from bidder +// +// TmaxTimeout will not be written to the app log, since it's not an actionable item for the Prebid Server hosts. +type TmaxTimeout struct { + Message string +} + +func (err *TmaxTimeout) Error() string { + return err.Message +} + +func (err *TmaxTimeout) Code() int { + return TmaxTimeoutErrorCode +} + +func (err *TmaxTimeout) Severity() Severity { + return SeverityFatal +} + // BadInput should be used when returning errors which are caused by bad input. // It should _not_ be used if the error is a server-side issue (e.g. failed to send the external request). // @@ -60,23 +79,20 @@ func (err *BlacklistedApp) Severity() Severity { return SeverityFatal } -// BlacklistedAcct should be used when a request account ID matches an entry in the BlacklistedAccts -// environment variable array -// -// These errors will be written to http.ResponseWriter before canceling execution -type BlacklistedAcct struct { +// AccountDisabled should be used when a request an account is specifically disabled in account config. +type AccountDisabled struct { Message string } -func (err *BlacklistedAcct) Error() string { +func (err *AccountDisabled) Error() string { return err.Message } -func (err *BlacklistedAcct) Code() int { - return BlacklistedAcctErrorCode +func (err *AccountDisabled) Code() int { + return AccountDisabledErrorCode } -func (err *BlacklistedAcct) Severity() Severity { +func (err *AccountDisabled) Severity() Severity { return SeverityFatal } @@ -202,6 +218,40 @@ func (err *Warning) Severity() Severity { return SeverityWarning } +// FailedToUnmarshal should be used to represent errors that occur when unmarshaling raw json. +type FailedToUnmarshal struct { + Message string +} + +func (err *FailedToUnmarshal) Error() string { + return err.Message +} + +func (err *FailedToUnmarshal) Code() int { + return FailedToUnmarshalErrorCode +} + +func (err *FailedToUnmarshal) Severity() Severity { + return SeverityFatal +} + +// FailedToMarshal should be used to represent errors that occur when marshaling to a byte slice. +type FailedToMarshal struct { + Message string +} + +func (err *FailedToMarshal) Error() string { + return err.Message +} + +func (err *FailedToMarshal) Code() int { + return FailedToMarshalErrorCode +} + +func (err *FailedToMarshal) Severity() Severity { + return SeverityFatal +} + // BidderFailedSchemaValidation is used at the request validation step, // when the bidder parameters fail the schema validation, we want to // continue processing the request and still return an error message. diff --git a/errortypes/errortypes_test.go b/errortypes/errortypes_test.go index a17f4d0f6d0..cbe35ed3c6c 100644 --- a/errortypes/errortypes_test.go +++ b/errortypes/errortypes_test.go @@ -65,17 +65,6 @@ func TestErrors(t *testing.T) { severity: SeverityFatal, }, }, - { - name: `BlacklistedAcct`, - args: args{ - err: &BlacklistedAcct{Message: `BlacklistedAcct_ErrorMessage`}, - }, - want: want{ - errorMessage: `BlacklistedAcct_ErrorMessage`, - code: BlacklistedAcctErrorCode, - severity: SeverityFatal, - }, - }, { name: `AcctRequired`, args: args{ diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 6c6c2e106ce..fe72226cf1e 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -1,196 +1,204 @@ package exchange import ( - "github.com/prebid/prebid-server/adapters" - ttx "github.com/prebid/prebid-server/adapters/33across" - "github.com/prebid/prebid-server/adapters/aax" - "github.com/prebid/prebid-server/adapters/aceex" - "github.com/prebid/prebid-server/adapters/acuityads" - "github.com/prebid/prebid-server/adapters/adf" - "github.com/prebid/prebid-server/adapters/adgeneration" - "github.com/prebid/prebid-server/adapters/adhese" - "github.com/prebid/prebid-server/adapters/adkernel" - "github.com/prebid/prebid-server/adapters/adkernelAdn" - "github.com/prebid/prebid-server/adapters/adman" - "github.com/prebid/prebid-server/adapters/admixer" - "github.com/prebid/prebid-server/adapters/adnuntius" - "github.com/prebid/prebid-server/adapters/adocean" - "github.com/prebid/prebid-server/adapters/adoppler" - "github.com/prebid/prebid-server/adapters/adot" - "github.com/prebid/prebid-server/adapters/adpone" - "github.com/prebid/prebid-server/adapters/adprime" - "github.com/prebid/prebid-server/adapters/adquery" - "github.com/prebid/prebid-server/adapters/adrino" - "github.com/prebid/prebid-server/adapters/adsinteractive" - "github.com/prebid/prebid-server/adapters/adtarget" - "github.com/prebid/prebid-server/adapters/adtelligent" - "github.com/prebid/prebid-server/adapters/adtrgtme" - "github.com/prebid/prebid-server/adapters/advangelists" - "github.com/prebid/prebid-server/adapters/adview" - "github.com/prebid/prebid-server/adapters/adxcg" - "github.com/prebid/prebid-server/adapters/adyoulike" - "github.com/prebid/prebid-server/adapters/aidem" - "github.com/prebid/prebid-server/adapters/aja" - "github.com/prebid/prebid-server/adapters/algorix" - "github.com/prebid/prebid-server/adapters/amx" - "github.com/prebid/prebid-server/adapters/apacdex" - "github.com/prebid/prebid-server/adapters/applogy" - "github.com/prebid/prebid-server/adapters/appnexus" - "github.com/prebid/prebid-server/adapters/appush" - "github.com/prebid/prebid-server/adapters/audienceNetwork" - "github.com/prebid/prebid-server/adapters/automatad" - "github.com/prebid/prebid-server/adapters/avocet" - "github.com/prebid/prebid-server/adapters/axis" - "github.com/prebid/prebid-server/adapters/axonix" - "github.com/prebid/prebid-server/adapters/beachfront" - "github.com/prebid/prebid-server/adapters/beintoo" - "github.com/prebid/prebid-server/adapters/bematterfull" - "github.com/prebid/prebid-server/adapters/between" - "github.com/prebid/prebid-server/adapters/beyondmedia" - "github.com/prebid/prebid-server/adapters/bidmachine" - "github.com/prebid/prebid-server/adapters/bidmyadz" - "github.com/prebid/prebid-server/adapters/bidscube" - "github.com/prebid/prebid-server/adapters/bidstack" - "github.com/prebid/prebid-server/adapters/bizzclick" - "github.com/prebid/prebid-server/adapters/bliink" - "github.com/prebid/prebid-server/adapters/blue" - "github.com/prebid/prebid-server/adapters/bluesea" - "github.com/prebid/prebid-server/adapters/bmtm" - "github.com/prebid/prebid-server/adapters/boldwin" - "github.com/prebid/prebid-server/adapters/brave" - cadentaperturemx "github.com/prebid/prebid-server/adapters/cadent_aperture_mx" - "github.com/prebid/prebid-server/adapters/ccx" - "github.com/prebid/prebid-server/adapters/coinzilla" - "github.com/prebid/prebid-server/adapters/colossus" - "github.com/prebid/prebid-server/adapters/compass" - "github.com/prebid/prebid-server/adapters/connectad" - "github.com/prebid/prebid-server/adapters/consumable" - "github.com/prebid/prebid-server/adapters/conversant" - "github.com/prebid/prebid-server/adapters/cpmstar" - "github.com/prebid/prebid-server/adapters/criteo" - "github.com/prebid/prebid-server/adapters/cwire" - "github.com/prebid/prebid-server/adapters/datablocks" - "github.com/prebid/prebid-server/adapters/decenterads" - "github.com/prebid/prebid-server/adapters/deepintent" - "github.com/prebid/prebid-server/adapters/definemedia" - "github.com/prebid/prebid-server/adapters/dianomi" - "github.com/prebid/prebid-server/adapters/dmx" - evolution "github.com/prebid/prebid-server/adapters/e_volution" - "github.com/prebid/prebid-server/adapters/emtv" - "github.com/prebid/prebid-server/adapters/engagebdr" - "github.com/prebid/prebid-server/adapters/eplanning" - "github.com/prebid/prebid-server/adapters/epom" - "github.com/prebid/prebid-server/adapters/flipp" - "github.com/prebid/prebid-server/adapters/freewheelssp" - "github.com/prebid/prebid-server/adapters/frvradn" - "github.com/prebid/prebid-server/adapters/gamma" - "github.com/prebid/prebid-server/adapters/gamoshi" - "github.com/prebid/prebid-server/adapters/globalsun" - "github.com/prebid/prebid-server/adapters/gothamads" - "github.com/prebid/prebid-server/adapters/grid" - "github.com/prebid/prebid-server/adapters/gumgum" - "github.com/prebid/prebid-server/adapters/huaweiads" - "github.com/prebid/prebid-server/adapters/imds" - "github.com/prebid/prebid-server/adapters/impactify" - "github.com/prebid/prebid-server/adapters/improvedigital" - "github.com/prebid/prebid-server/adapters/infytv" - "github.com/prebid/prebid-server/adapters/inmobi" - "github.com/prebid/prebid-server/adapters/interactiveoffers" - "github.com/prebid/prebid-server/adapters/invibes" - "github.com/prebid/prebid-server/adapters/iqzone" - "github.com/prebid/prebid-server/adapters/ix" - "github.com/prebid/prebid-server/adapters/jixie" - "github.com/prebid/prebid-server/adapters/kargo" - "github.com/prebid/prebid-server/adapters/kayzen" - "github.com/prebid/prebid-server/adapters/kidoz" - "github.com/prebid/prebid-server/adapters/kiviads" - "github.com/prebid/prebid-server/adapters/krushmedia" - "github.com/prebid/prebid-server/adapters/kubient" - "github.com/prebid/prebid-server/adapters/liftoff" - "github.com/prebid/prebid-server/adapters/limelightDigital" - lmkiviads "github.com/prebid/prebid-server/adapters/lm_kiviads" - "github.com/prebid/prebid-server/adapters/lockerdome" - "github.com/prebid/prebid-server/adapters/logan" - "github.com/prebid/prebid-server/adapters/logicad" - "github.com/prebid/prebid-server/adapters/lunamedia" - mabidder "github.com/prebid/prebid-server/adapters/mabidder" - "github.com/prebid/prebid-server/adapters/madvertise" - "github.com/prebid/prebid-server/adapters/marsmedia" - "github.com/prebid/prebid-server/adapters/medianet" - "github.com/prebid/prebid-server/adapters/mgid" - "github.com/prebid/prebid-server/adapters/mgidX" - "github.com/prebid/prebid-server/adapters/mobfoxpb" - "github.com/prebid/prebid-server/adapters/mobilefuse" - "github.com/prebid/prebid-server/adapters/motorik" - "github.com/prebid/prebid-server/adapters/nanointeractive" - "github.com/prebid/prebid-server/adapters/nextmillennium" - "github.com/prebid/prebid-server/adapters/ninthdecimal" - "github.com/prebid/prebid-server/adapters/nobid" - "github.com/prebid/prebid-server/adapters/onetag" - "github.com/prebid/prebid-server/adapters/openweb" - "github.com/prebid/prebid-server/adapters/openx" - "github.com/prebid/prebid-server/adapters/operaads" - "github.com/prebid/prebid-server/adapters/orbidder" - "github.com/prebid/prebid-server/adapters/outbrain" - "github.com/prebid/prebid-server/adapters/ownadx" - "github.com/prebid/prebid-server/adapters/pangle" - "github.com/prebid/prebid-server/adapters/pgamssp" - "github.com/prebid/prebid-server/adapters/pubmatic" - "github.com/prebid/prebid-server/adapters/pubnative" - "github.com/prebid/prebid-server/adapters/pulsepoint" - "github.com/prebid/prebid-server/adapters/pwbid" - "github.com/prebid/prebid-server/adapters/revcontent" - "github.com/prebid/prebid-server/adapters/rhythmone" - "github.com/prebid/prebid-server/adapters/richaudience" - "github.com/prebid/prebid-server/adapters/rise" - "github.com/prebid/prebid-server/adapters/rtbhouse" - "github.com/prebid/prebid-server/adapters/rubicon" - salunamedia "github.com/prebid/prebid-server/adapters/sa_lunamedia" - "github.com/prebid/prebid-server/adapters/screencore" - "github.com/prebid/prebid-server/adapters/seedingAlliance" - "github.com/prebid/prebid-server/adapters/sharethrough" - "github.com/prebid/prebid-server/adapters/silvermob" - "github.com/prebid/prebid-server/adapters/silverpush" - "github.com/prebid/prebid-server/adapters/smaato" - "github.com/prebid/prebid-server/adapters/smartadserver" - "github.com/prebid/prebid-server/adapters/smarthub" - "github.com/prebid/prebid-server/adapters/smartrtb" - "github.com/prebid/prebid-server/adapters/smartyads" - "github.com/prebid/prebid-server/adapters/smilewanted" - "github.com/prebid/prebid-server/adapters/sonobi" - "github.com/prebid/prebid-server/adapters/sovrn" - "github.com/prebid/prebid-server/adapters/spotx" - "github.com/prebid/prebid-server/adapters/sspBC" - "github.com/prebid/prebid-server/adapters/stroeerCore" - "github.com/prebid/prebid-server/adapters/suntContent" - "github.com/prebid/prebid-server/adapters/taboola" - "github.com/prebid/prebid-server/adapters/tappx" - "github.com/prebid/prebid-server/adapters/telaria" - "github.com/prebid/prebid-server/adapters/tpmn" - "github.com/prebid/prebid-server/adapters/trafficgate" - "github.com/prebid/prebid-server/adapters/triplelift" - "github.com/prebid/prebid-server/adapters/triplelift_native" - "github.com/prebid/prebid-server/adapters/ucfunnel" - "github.com/prebid/prebid-server/adapters/undertone" - "github.com/prebid/prebid-server/adapters/unicorn" - "github.com/prebid/prebid-server/adapters/unruly" - "github.com/prebid/prebid-server/adapters/vastbidder" - "github.com/prebid/prebid-server/adapters/videobyte" - "github.com/prebid/prebid-server/adapters/videoheroes" - "github.com/prebid/prebid-server/adapters/vidoomy" - "github.com/prebid/prebid-server/adapters/visiblemeasures" - "github.com/prebid/prebid-server/adapters/visx" - "github.com/prebid/prebid-server/adapters/vox" - "github.com/prebid/prebid-server/adapters/vrtcal" - "github.com/prebid/prebid-server/adapters/xeworks" - "github.com/prebid/prebid-server/adapters/yahooAds" - "github.com/prebid/prebid-server/adapters/yeahmobi" - "github.com/prebid/prebid-server/adapters/yieldlab" - "github.com/prebid/prebid-server/adapters/yieldmo" - "github.com/prebid/prebid-server/adapters/yieldone" - "github.com/prebid/prebid-server/adapters/zeroclickfraud" - "github.com/prebid/prebid-server/adapters/zeta_global_ssp" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters" + ttx "github.com/prebid/prebid-server/v2/adapters/33across" + "github.com/prebid/prebid-server/v2/adapters/aax" + "github.com/prebid/prebid-server/v2/adapters/aceex" + "github.com/prebid/prebid-server/v2/adapters/acuityads" + "github.com/prebid/prebid-server/v2/adapters/adelement" + "github.com/prebid/prebid-server/v2/adapters/adf" + "github.com/prebid/prebid-server/v2/adapters/adgeneration" + "github.com/prebid/prebid-server/v2/adapters/adhese" + "github.com/prebid/prebid-server/v2/adapters/adkernel" + "github.com/prebid/prebid-server/v2/adapters/adkernelAdn" + "github.com/prebid/prebid-server/v2/adapters/adman" + "github.com/prebid/prebid-server/v2/adapters/admixer" + "github.com/prebid/prebid-server/v2/adapters/adnuntius" + "github.com/prebid/prebid-server/v2/adapters/adocean" + "github.com/prebid/prebid-server/v2/adapters/adoppler" + "github.com/prebid/prebid-server/v2/adapters/adot" + "github.com/prebid/prebid-server/v2/adapters/adpone" + "github.com/prebid/prebid-server/v2/adapters/adprime" + "github.com/prebid/prebid-server/v2/adapters/adquery" + "github.com/prebid/prebid-server/v2/adapters/adrino" + "github.com/prebid/prebid-server/v2/adapters/adsinteractive" + "github.com/prebid/prebid-server/v2/adapters/adtarget" + "github.com/prebid/prebid-server/v2/adapters/adtelligent" + "github.com/prebid/prebid-server/v2/adapters/adtrgtme" + "github.com/prebid/prebid-server/v2/adapters/advangelists" + "github.com/prebid/prebid-server/v2/adapters/adview" + "github.com/prebid/prebid-server/v2/adapters/adxcg" + "github.com/prebid/prebid-server/v2/adapters/adyoulike" + "github.com/prebid/prebid-server/v2/adapters/aidem" + "github.com/prebid/prebid-server/v2/adapters/aja" + "github.com/prebid/prebid-server/v2/adapters/algorix" + "github.com/prebid/prebid-server/v2/adapters/alkimi" + "github.com/prebid/prebid-server/v2/adapters/amx" + "github.com/prebid/prebid-server/v2/adapters/apacdex" + "github.com/prebid/prebid-server/v2/adapters/appnexus" + "github.com/prebid/prebid-server/v2/adapters/appush" + "github.com/prebid/prebid-server/v2/adapters/audienceNetwork" + "github.com/prebid/prebid-server/v2/adapters/automatad" + "github.com/prebid/prebid-server/v2/adapters/avocet" + "github.com/prebid/prebid-server/v2/adapters/axis" + "github.com/prebid/prebid-server/v2/adapters/axonix" + "github.com/prebid/prebid-server/v2/adapters/beachfront" + "github.com/prebid/prebid-server/v2/adapters/beintoo" + "github.com/prebid/prebid-server/v2/adapters/bematterfull" + "github.com/prebid/prebid-server/v2/adapters/between" + "github.com/prebid/prebid-server/v2/adapters/beyondmedia" + "github.com/prebid/prebid-server/v2/adapters/bidmachine" + "github.com/prebid/prebid-server/v2/adapters/bidmyadz" + "github.com/prebid/prebid-server/v2/adapters/bidscube" + "github.com/prebid/prebid-server/v2/adapters/bidstack" + "github.com/prebid/prebid-server/v2/adapters/bizzclick" + "github.com/prebid/prebid-server/v2/adapters/bliink" + "github.com/prebid/prebid-server/v2/adapters/blue" + "github.com/prebid/prebid-server/v2/adapters/bluesea" + "github.com/prebid/prebid-server/v2/adapters/bmtm" + "github.com/prebid/prebid-server/v2/adapters/boldwin" + "github.com/prebid/prebid-server/v2/adapters/brave" + "github.com/prebid/prebid-server/v2/adapters/bwx" + cadentaperturemx "github.com/prebid/prebid-server/v2/adapters/cadent_aperture_mx" + "github.com/prebid/prebid-server/v2/adapters/ccx" + "github.com/prebid/prebid-server/v2/adapters/coinzilla" + "github.com/prebid/prebid-server/v2/adapters/colossus" + "github.com/prebid/prebid-server/v2/adapters/compass" + "github.com/prebid/prebid-server/v2/adapters/connectad" + "github.com/prebid/prebid-server/v2/adapters/consumable" + "github.com/prebid/prebid-server/v2/adapters/conversant" + "github.com/prebid/prebid-server/v2/adapters/cpmstar" + "github.com/prebid/prebid-server/v2/adapters/criteo" + "github.com/prebid/prebid-server/v2/adapters/cwire" + "github.com/prebid/prebid-server/v2/adapters/datablocks" + "github.com/prebid/prebid-server/v2/adapters/decenterads" + "github.com/prebid/prebid-server/v2/adapters/deepintent" + "github.com/prebid/prebid-server/v2/adapters/definemedia" + "github.com/prebid/prebid-server/v2/adapters/dianomi" + "github.com/prebid/prebid-server/v2/adapters/dmx" + "github.com/prebid/prebid-server/v2/adapters/dxkulture" + evolution "github.com/prebid/prebid-server/v2/adapters/e_volution" + "github.com/prebid/prebid-server/v2/adapters/edge226" + "github.com/prebid/prebid-server/v2/adapters/emtv" + "github.com/prebid/prebid-server/v2/adapters/eplanning" + "github.com/prebid/prebid-server/v2/adapters/epom" + "github.com/prebid/prebid-server/v2/adapters/flipp" + "github.com/prebid/prebid-server/v2/adapters/freewheelssp" + "github.com/prebid/prebid-server/v2/adapters/frvradn" + "github.com/prebid/prebid-server/v2/adapters/gamma" + "github.com/prebid/prebid-server/v2/adapters/gamoshi" + "github.com/prebid/prebid-server/v2/adapters/globalsun" + "github.com/prebid/prebid-server/v2/adapters/gothamads" + "github.com/prebid/prebid-server/v2/adapters/grid" + "github.com/prebid/prebid-server/v2/adapters/gumgum" + "github.com/prebid/prebid-server/v2/adapters/huaweiads" + "github.com/prebid/prebid-server/v2/adapters/imds" + "github.com/prebid/prebid-server/v2/adapters/impactify" + "github.com/prebid/prebid-server/v2/adapters/improvedigital" + "github.com/prebid/prebid-server/v2/adapters/infytv" + "github.com/prebid/prebid-server/v2/adapters/inmobi" + "github.com/prebid/prebid-server/v2/adapters/interactiveoffers" + "github.com/prebid/prebid-server/v2/adapters/invibes" + "github.com/prebid/prebid-server/v2/adapters/iqx" + "github.com/prebid/prebid-server/v2/adapters/iqzone" + "github.com/prebid/prebid-server/v2/adapters/ix" + "github.com/prebid/prebid-server/v2/adapters/jixie" + "github.com/prebid/prebid-server/v2/adapters/kargo" + "github.com/prebid/prebid-server/v2/adapters/kayzen" + "github.com/prebid/prebid-server/v2/adapters/kidoz" + "github.com/prebid/prebid-server/v2/adapters/kiviads" + "github.com/prebid/prebid-server/v2/adapters/krushmedia" + "github.com/prebid/prebid-server/v2/adapters/lemmadigital" + "github.com/prebid/prebid-server/v2/adapters/liftoff" + "github.com/prebid/prebid-server/v2/adapters/limelightDigital" + lmkiviads "github.com/prebid/prebid-server/v2/adapters/lm_kiviads" + "github.com/prebid/prebid-server/v2/adapters/lockerdome" + "github.com/prebid/prebid-server/v2/adapters/logan" + "github.com/prebid/prebid-server/v2/adapters/logicad" + "github.com/prebid/prebid-server/v2/adapters/lunamedia" + "github.com/prebid/prebid-server/v2/adapters/mabidder" + "github.com/prebid/prebid-server/v2/adapters/madvertise" + "github.com/prebid/prebid-server/v2/adapters/marsmedia" + "github.com/prebid/prebid-server/v2/adapters/medianet" + "github.com/prebid/prebid-server/v2/adapters/mgid" + "github.com/prebid/prebid-server/v2/adapters/mgidX" + "github.com/prebid/prebid-server/v2/adapters/minutemedia" + "github.com/prebid/prebid-server/v2/adapters/mobfoxpb" + "github.com/prebid/prebid-server/v2/adapters/mobilefuse" + "github.com/prebid/prebid-server/v2/adapters/motorik" + "github.com/prebid/prebid-server/v2/adapters/nextmillennium" + "github.com/prebid/prebid-server/v2/adapters/nobid" + "github.com/prebid/prebid-server/v2/adapters/oms" + "github.com/prebid/prebid-server/v2/adapters/onetag" + "github.com/prebid/prebid-server/v2/adapters/openweb" + "github.com/prebid/prebid-server/v2/adapters/openx" + "github.com/prebid/prebid-server/v2/adapters/operaads" + "github.com/prebid/prebid-server/v2/adapters/orbidder" + "github.com/prebid/prebid-server/v2/adapters/outbrain" + "github.com/prebid/prebid-server/v2/adapters/ownadx" + "github.com/prebid/prebid-server/v2/adapters/pangle" + "github.com/prebid/prebid-server/v2/adapters/pgamssp" + "github.com/prebid/prebid-server/v2/adapters/pubmatic" + "github.com/prebid/prebid-server/v2/adapters/pubnative" + "github.com/prebid/prebid-server/v2/adapters/pulsepoint" + "github.com/prebid/prebid-server/v2/adapters/pwbid" + "github.com/prebid/prebid-server/v2/adapters/relevantdigital" + "github.com/prebid/prebid-server/v2/adapters/revcontent" + "github.com/prebid/prebid-server/v2/adapters/richaudience" + "github.com/prebid/prebid-server/v2/adapters/rise" + "github.com/prebid/prebid-server/v2/adapters/rtbhouse" + "github.com/prebid/prebid-server/v2/adapters/rubicon" + salunamedia "github.com/prebid/prebid-server/v2/adapters/sa_lunamedia" + "github.com/prebid/prebid-server/v2/adapters/screencore" + "github.com/prebid/prebid-server/v2/adapters/seedingAlliance" + "github.com/prebid/prebid-server/v2/adapters/sharethrough" + "github.com/prebid/prebid-server/v2/adapters/silvermob" + "github.com/prebid/prebid-server/v2/adapters/silverpush" + "github.com/prebid/prebid-server/v2/adapters/smaato" + "github.com/prebid/prebid-server/v2/adapters/smartadserver" + "github.com/prebid/prebid-server/v2/adapters/smarthub" + "github.com/prebid/prebid-server/v2/adapters/smartrtb" + "github.com/prebid/prebid-server/v2/adapters/smartx" + "github.com/prebid/prebid-server/v2/adapters/smartyads" + "github.com/prebid/prebid-server/v2/adapters/smilewanted" + "github.com/prebid/prebid-server/v2/adapters/sonobi" + "github.com/prebid/prebid-server/v2/adapters/sovrn" + "github.com/prebid/prebid-server/v2/adapters/sovrnXsp" + "github.com/prebid/prebid-server/v2/adapters/spotx" + "github.com/prebid/prebid-server/v2/adapters/sspBC" + "github.com/prebid/prebid-server/v2/adapters/stroeerCore" + "github.com/prebid/prebid-server/v2/adapters/taboola" + "github.com/prebid/prebid-server/v2/adapters/tappx" + "github.com/prebid/prebid-server/v2/adapters/teads" + "github.com/prebid/prebid-server/v2/adapters/telaria" + "github.com/prebid/prebid-server/v2/adapters/tpmn" + "github.com/prebid/prebid-server/v2/adapters/trafficgate" + "github.com/prebid/prebid-server/v2/adapters/triplelift" + "github.com/prebid/prebid-server/v2/adapters/triplelift_native" + "github.com/prebid/prebid-server/v2/adapters/ucfunnel" + "github.com/prebid/prebid-server/v2/adapters/undertone" + "github.com/prebid/prebid-server/v2/adapters/unicorn" + "github.com/prebid/prebid-server/v2/adapters/unruly" + "github.com/prebid/prebid-server/v2/adapters/vastbidder" + "github.com/prebid/prebid-server/v2/adapters/videobyte" + "github.com/prebid/prebid-server/v2/adapters/videoheroes" + "github.com/prebid/prebid-server/v2/adapters/vidoomy" + "github.com/prebid/prebid-server/v2/adapters/visiblemeasures" + "github.com/prebid/prebid-server/v2/adapters/visx" + "github.com/prebid/prebid-server/v2/adapters/vox" + "github.com/prebid/prebid-server/v2/adapters/vrtcal" + "github.com/prebid/prebid-server/v2/adapters/xeworks" + "github.com/prebid/prebid-server/v2/adapters/yahooAds" + "github.com/prebid/prebid-server/v2/adapters/yandex" + "github.com/prebid/prebid-server/v2/adapters/yeahmobi" + "github.com/prebid/prebid-server/v2/adapters/yieldlab" + "github.com/prebid/prebid-server/v2/adapters/yieldmo" + "github.com/prebid/prebid-server/v2/adapters/yieldone" + "github.com/prebid/prebid-server/v2/adapters/zeroclickfraud" + "github.com/prebid/prebid-server/v2/adapters/zeta_global_ssp" + "github.com/prebid/prebid-server/v2/adapters/zmaticoo" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // Adapter registration is kept in this separate file for ease of use and to aid @@ -202,8 +210,8 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderAax: aax.Builder, openrtb_ext.BidderAceex: aceex.Builder, openrtb_ext.BidderAcuityAds: acuityads.Builder, + openrtb_ext.BidderAdelement: adelement.Builder, openrtb_ext.BidderAdf: adf.Builder, - openrtb_ext.BidderAdform: adf.Builder, openrtb_ext.BidderAdgeneration: adgeneration.Builder, openrtb_ext.BidderAdhese: adhese.Builder, openrtb_ext.BidderAdkernel: adkernel.Builder, @@ -219,7 +227,6 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderAdquery: adquery.Builder, openrtb_ext.BidderAdrino: adrino.Builder, openrtb_ext.BidderAdsinteractive: adsinteractive.Builder, - openrtb_ext.BidderAdsyield: limelightDigital.Builder, openrtb_ext.BidderAdtarget: adtarget.Builder, openrtb_ext.BidderAdtrgtme: adtrgtme.Builder, openrtb_ext.BidderAdtelligent: adtelligent.Builder, @@ -230,11 +237,10 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderAidem: aidem.Builder, openrtb_ext.BidderAJA: aja.Builder, openrtb_ext.BidderAlgorix: algorix.Builder, + openrtb_ext.BidderAlkimi: alkimi.Builder, openrtb_ext.BidderAMX: amx.Builder, openrtb_ext.BidderApacdex: apacdex.Builder, - openrtb_ext.BidderApplogy: applogy.Builder, openrtb_ext.BidderAppnexus: appnexus.Builder, - openrtb_ext.BidderAppstock: limelightDigital.Builder, openrtb_ext.BidderAppush: appush.Builder, openrtb_ext.BidderAudienceNetwork: audienceNetwork.Builder, openrtb_ext.BidderAutomatad: automatad.Builder, @@ -257,6 +263,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderBmtm: bmtm.Builder, openrtb_ext.BidderBoldwin: boldwin.Builder, openrtb_ext.BidderBrave: brave.Builder, + openrtb_ext.BidderBWX: bwx.Builder, openrtb_ext.BidderCadentApertureMX: cadentaperturemx.Builder, openrtb_ext.BidderCcx: ccx.Builder, openrtb_ext.BidderCoinzilla: coinzilla.Builder, @@ -265,7 +272,6 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderConnectAd: connectad.Builder, openrtb_ext.BidderConsumable: consumable.Builder, openrtb_ext.BidderConversant: conversant.Builder, - openrtb_ext.BidderCopper6: adtelligent.Builder, openrtb_ext.BidderCpmstar: cpmstar.Builder, openrtb_ext.BidderCriteo: criteo.Builder, openrtb_ext.BidderCWire: cwire.Builder, @@ -274,28 +280,24 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderDeepintent: deepintent.Builder, openrtb_ext.BidderDefinemedia: definemedia.Builder, openrtb_ext.BidderDianomi: dianomi.Builder, + openrtb_ext.BidderEdge226: edge226.Builder, openrtb_ext.BidderDmx: dmx.Builder, + openrtb_ext.BidderDXKulture: dxkulture.Builder, openrtb_ext.BidderEmtv: emtv.Builder, openrtb_ext.BidderEmxDigital: cadentaperturemx.Builder, - openrtb_ext.BidderEngageBDR: engagebdr.Builder, openrtb_ext.BidderEPlanning: eplanning.Builder, openrtb_ext.BidderEpom: epom.Builder, - openrtb_ext.BidderEpsilon: conversant.Builder, openrtb_ext.BidderEVolution: evolution.Builder, - openrtb_ext.BidderEvtech: limelightDigital.Builder, openrtb_ext.BidderFlipp: flipp.Builder, openrtb_ext.BidderFreewheelSSP: freewheelssp.Builder, - openrtb_ext.BidderFreewheelSSPOld: freewheelssp.Builder, openrtb_ext.BidderFRVRAdNetwork: frvradn.Builder, openrtb_ext.BidderGamma: gamma.Builder, openrtb_ext.BidderGamoshi: gamoshi.Builder, openrtb_ext.BidderGlobalsun: globalsun.Builder, openrtb_ext.BidderGothamads: gothamads.Builder, - openrtb_ext.BidderGreedygame: limelightDigital.Builder, openrtb_ext.BidderGrid: grid.Builder, openrtb_ext.BidderGumGum: gumgum.Builder, openrtb_ext.BidderHuaweiAds: huaweiads.Builder, - openrtb_ext.BidderIionads: limelightDigital.Builder, openrtb_ext.BidderImds: imds.Builder, openrtb_ext.BidderImpactify: impactify.Builder, openrtb_ext.BidderImprovedigital: improvedigital.Builder, @@ -303,9 +305,9 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderInMobi: inmobi.Builder, openrtb_ext.BidderInteractiveoffers: interactiveoffers.Builder, openrtb_ext.BidderInvibes: invibes.Builder, + openrtb_ext.BidderIQX: iqx.Builder, openrtb_ext.BidderIQZone: iqzone.Builder, openrtb_ext.BidderIx: ix.Builder, - openrtb_ext.BidderJANet: adtelligent.Builder, openrtb_ext.BidderJixie: jixie.Builder, openrtb_ext.BidderKargo: kargo.Builder, openrtb_ext.BidderKayzen: kayzen.Builder, @@ -313,7 +315,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderKiviads: kiviads.Builder, openrtb_ext.BidderLmKiviads: lmkiviads.Builder, openrtb_ext.BidderKrushmedia: krushmedia.Builder, - openrtb_ext.BidderKubient: kubient.Builder, + openrtb_ext.BidderLemmadigital: lemmadigital.Builder, openrtb_ext.BidderLiftoff: liftoff.Builder, openrtb_ext.BidderLimelightDigital: limelightDigital.Builder, openrtb_ext.BidderLockerDome: lockerdome.Builder, @@ -327,13 +329,13 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderMedianet: medianet.Builder, openrtb_ext.BidderMgid: mgid.Builder, openrtb_ext.BidderMgidX: mgidX.Builder, + openrtb_ext.BidderMinuteMedia: minutemedia.Builder, openrtb_ext.BidderMobfoxpb: mobfoxpb.Builder, openrtb_ext.BidderMobileFuse: mobilefuse.Builder, openrtb_ext.BidderMotorik: motorik.Builder, - openrtb_ext.BidderNanoInteractive: nanointeractive.Builder, openrtb_ext.BidderNextMillennium: nextmillennium.Builder, - openrtb_ext.BidderNinthDecimal: ninthdecimal.Builder, openrtb_ext.BidderNoBid: nobid.Builder, + openrtb_ext.BidderOms: oms.Builder, openrtb_ext.BidderOneTag: onetag.Builder, openrtb_ext.BidderOpenWeb: openweb.Builder, openrtb_ext.BidderOpenx: openx.Builder, @@ -342,15 +344,13 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderOutbrain: outbrain.Builder, openrtb_ext.BidderOwnAdx: ownadx.Builder, openrtb_ext.BidderPangle: pangle.Builder, - openrtb_ext.BidderPGAM: adtelligent.Builder, openrtb_ext.BidderPGAMSsp: pgamssp.Builder, openrtb_ext.BidderPubmatic: pubmatic.Builder, openrtb_ext.BidderPubnative: pubnative.Builder, openrtb_ext.BidderPulsepoint: pulsepoint.Builder, openrtb_ext.BidderPWBid: pwbid.Builder, - openrtb_ext.BidderQuantumdex: apacdex.Builder, + openrtb_ext.BidderRelevantDigital: relevantdigital.Builder, openrtb_ext.BidderRevcontent: revcontent.Builder, - openrtb_ext.BidderRhythmone: rhythmone.Builder, openrtb_ext.BidderRichaudience: richaudience.Builder, openrtb_ext.BidderRise: rise.Builder, openrtb_ext.BidderRTBHouse: rtbhouse.Builder, @@ -365,24 +365,24 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderSmartAdserver: smartadserver.Builder, openrtb_ext.BidderSmartHub: smarthub.Builder, openrtb_ext.BidderSmartRTB: smartrtb.Builder, + openrtb_ext.BidderSmartx: smartx.Builder, openrtb_ext.BidderSmartyAds: smartyads.Builder, openrtb_ext.BidderSmileWanted: smilewanted.Builder, openrtb_ext.BidderSonobi: sonobi.Builder, openrtb_ext.BidderSovrn: sovrn.Builder, + openrtb_ext.BidderSovrnXsp: sovrnXsp.Builder, openrtb_ext.BidderSspBC: sspBC.Builder, openrtb_ext.BidderSpotX: spotx.Builder, openrtb_ext.BidderStreamkey: adtelligent.Builder, - openrtb_ext.BidderSuntContent: suntContent.Builder, openrtb_ext.BidderStroeerCore: stroeerCore.Builder, - openrtb_ext.BidderSynacormedia: imds.Builder, openrtb_ext.BidderTaboola: taboola.Builder, openrtb_ext.BidderTappx: tappx.Builder, + openrtb_ext.BidderTeads: teads.Builder, openrtb_ext.BidderTelaria: telaria.Builder, openrtb_ext.BidderTpmn: tpmn.Builder, openrtb_ext.BidderTrafficGate: trafficgate.Builder, openrtb_ext.BidderTriplelift: triplelift.Builder, openrtb_ext.BidderTripleliftNative: triplelift_native.Builder, - openrtb_ext.BidderTrustX: grid.Builder, openrtb_ext.BidderUcfunnel: ucfunnel.Builder, openrtb_ext.BidderUndertone: undertone.Builder, openrtb_ext.BidderUnicorn: unicorn.Builder, @@ -392,21 +392,19 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderVideoByte: videobyte.Builder, openrtb_ext.BidderVideoHeroes: videoheroes.Builder, openrtb_ext.BidderVidoomy: vidoomy.Builder, - openrtb_ext.BidderViewdeos: adtelligent.Builder, openrtb_ext.BidderVisibleMeasures: visiblemeasures.Builder, openrtb_ext.BidderVisx: visx.Builder, openrtb_ext.BidderVox: vox.Builder, openrtb_ext.BidderVrtcal: vrtcal.Builder, openrtb_ext.BidderXeworks: xeworks.Builder, - openrtb_ext.BidderXtrmqb: limelightDigital.Builder, openrtb_ext.BidderYahooAds: yahooAds.Builder, - openrtb_ext.BidderYahooAdvertising: yahooAds.Builder, - openrtb_ext.BidderYahooSSP: yahooAds.Builder, + openrtb_ext.BidderYandex: yandex.Builder, openrtb_ext.BidderYeahmobi: yeahmobi.Builder, openrtb_ext.BidderYieldlab: yieldlab.Builder, openrtb_ext.BidderYieldmo: yieldmo.Builder, openrtb_ext.BidderYieldone: yieldone.Builder, openrtb_ext.BidderZeroClickFraud: zeroclickfraud.Builder, openrtb_ext.BidderZetaGlobalSsp: zeta_global_ssp.Builder, + openrtb_ext.BidderZmaticoo: zmaticoo.Builder, } } diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go index ee9a066aa58..cd70530bfc3 100644 --- a/exchange/adapter_util.go +++ b/exchange/adapter_util.go @@ -4,10 +4,10 @@ import ( "fmt" "net/http" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func BuildAdapters(client *http.Client, cfg *config.Configuration, infos config.BidderInfos, me metrics.MetricsEngine) (map[openrtb_ext.BidderName]AdaptedBidder, []error) { @@ -33,10 +33,6 @@ func buildBidders(infos config.BidderInfos, builders map[openrtb_ext.BidderName] var errs []error for bidder, info := range infos { - if len(info.AliasOf) > 0 { - errs = append(errs, fmt.Errorf("This feature is currently under development")) - continue - } bidderName, bidderNameFound := openrtb_ext.NormalizeBidderName(bidder) if !bidderNameFound { errs = append(errs, fmt.Errorf("%v: unknown bidder", bidder)) @@ -109,15 +105,21 @@ func GetActiveBidders(infos config.BidderInfos) map[string]openrtb_ext.BidderNam func GetDisabledBidderWarningMessages(infos config.BidderInfos) map[string]string { removed := map[string]string{ - "lifestreet": `Bidder "lifestreet" is no longer available in Prebid Server. Please update your configuration.`, - "adagio": `Bidder "adagio" is no longer available in Prebid Server. Please update your configuration.`, - "somoaudience": `Bidder "somoaudience" is no longer available in Prebid Server. Please update your configuration.`, - "yssp": `Bidder "yssp" is no longer available in Prebid Server. If you're looking to use the Yahoo SSP adapter, please rename it to "yahooAds" in your configuration.`, - "andbeyondmedia": `Bidder "andbeyondmedia" is no longer available in Prebid Server. If you're looking to use the AndBeyond.Media SSP adapter, please rename it to "beyondmedia" in your configuration.`, - "oftmedia": `Bidder "oftmedia" is no longer available in Prebid Server. Please update your configuration.`, - "groupm": `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`, - "verizonmedia": `Bidder "verizonmedia" is no longer available in Prebid Server. Please update your configuration.`, - "brightroll": `Bidder "brightroll" is no longer available in Prebid Server. Please update your configuration.`, + "lifestreet": `Bidder "lifestreet" is no longer available in Prebid Server. Please update your configuration.`, + "adagio": `Bidder "adagio" is no longer available in Prebid Server. Please update your configuration.`, + "somoaudience": `Bidder "somoaudience" is no longer available in Prebid Server. Please update your configuration.`, + "yssp": `Bidder "yssp" is no longer available in Prebid Server. If you're looking to use the Yahoo SSP adapter, please rename it to "yahooAds" in your configuration.`, + "andbeyondmedia": `Bidder "andbeyondmedia" is no longer available in Prebid Server. If you're looking to use the AndBeyond.Media SSP adapter, please rename it to "beyondmedia" in your configuration.`, + "oftmedia": `Bidder "oftmedia" is no longer available in Prebid Server. Please update your configuration.`, + "groupm": `Bidder "groupm" is no longer available in Prebid Server. Please update your configuration.`, + "verizonmedia": `Bidder "verizonmedia" is no longer available in Prebid Server. Please update your configuration.`, + "brightroll": `Bidder "brightroll" is no longer available in Prebid Server. Please update your configuration.`, + "engagebdr": `Bidder "engagebdr" is no longer available in Prebid Server. Please update your configuration.`, + "ninthdecimal": `Bidder "ninthdecimal" is no longer available in Prebid Server. Please update your configuration.`, + "kubient": `Bidder "kubient" is no longer available in Prebid Server. Please update your configuration.`, + "applogy": `Bidder "applogy" is no longer available in Prebid Server. Please update your configuration.`, + "rhythmone": `Bidder "rhythmone" is no longer available in Prebid Server. Please update your configuration.`, + "nanointeractive": `Bidder "nanointeractive" is no longer available in Prebid Server. Please update your configuration.`, } return mergeRemovedAndDisabledBidderWarningMessages(removed, infos) diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go index 611498fea0c..053b669961d 100644 --- a/exchange/adapter_util_test.go +++ b/exchange/adapter_util_test.go @@ -5,13 +5,13 @@ import ( "net/http" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/appnexus" - "github.com/prebid/prebid-server/adapters/rubicon" - "github.com/prebid/prebid-server/config" - metrics "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/adapters/appnexus" + "github.com/prebid/prebid-server/v2/adapters/rubicon" + "github.com/prebid/prebid-server/v2/config" + metrics "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -68,13 +68,6 @@ func TestBuildAdapters(t *testing.T) { errors.New("unknown: unknown bidder"), }, }, - { - description: "Alias feature disabled", - bidderInfos: map[string]config.BidderInfo{"appNexus": {AliasOf: "rubicon"}}, - expectedErrors: []error{ - errors.New("This feature is currently under development"), - }, - }, } cfg := &config.Configuration{} diff --git a/exchange/auction.go b/exchange/auction.go index b68fd06113e..79f6d10cba2 100644 --- a/exchange/auction.go +++ b/exchange/auction.go @@ -2,7 +2,6 @@ package exchange import ( "context" - "encoding/json" "encoding/xml" "errors" "fmt" @@ -12,11 +11,12 @@ import ( "time" uuid "github.com/gofrs/uuid" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/prebid_cache_client" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/prebid_cache_client" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) const ( @@ -83,7 +83,7 @@ func (d *DebugLog) PutDebugLogError(cache prebid_cache_client.Client, timeout in d.CacheKey = rawUUID.String() } - data, err := json.Marshal(d.CacheString) + data, err := jsonutil.Marshal(d.CacheString) if err != nil { return err } @@ -108,7 +108,7 @@ func (d *DebugLog) PutDebugLogError(cache prebid_cache_client.Client, timeout in func newAuction(seatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, numImps int, preferDeals bool) *auction { winningBids := make(map[string]*entities.PbsOrtbBid, numImps) - winningBidsByBidder := make(map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid, numImps) + allBidsByBidder := make(map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid, numImps) for bidderName, seatBid := range seatBids { if seatBid != nil { @@ -118,10 +118,10 @@ func newAuction(seatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, nu winningBids[bid.Bid.ImpID] = bid } - if bidMap, ok := winningBidsByBidder[bid.Bid.ImpID]; ok { + if bidMap, ok := allBidsByBidder[bid.Bid.ImpID]; ok { bidMap[bidderName] = append(bidMap[bidderName], bid) } else { - winningBidsByBidder[bid.Bid.ImpID] = map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder[bid.Bid.ImpID] = map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ bidderName: {bid}, } } @@ -130,8 +130,8 @@ func newAuction(seatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, nu } return &auction{ - winningBids: winningBids, - winningBidsByBidder: winningBidsByBidder, + winningBids: winningBids, + allBidsByBidder: allBidsByBidder, } } @@ -151,7 +151,7 @@ func isNewWinningBid(bid, wbid *openrtb2.Bid, preferDeals bool) bool { func (a *auction) validateAndUpdateMultiBid(adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, preferDeals bool, accountDefaultBidLimit int) { bidsSnipped := false // sort bids for multibid targeting - for _, topBidsPerBidder := range a.winningBidsByBidder { + for _, topBidsPerBidder := range a.allBidsByBidder { for bidder, topBids := range topBidsPerBidder { sort.Slice(topBids, func(i, j int) bool { return isNewWinningBid(topBids[i].Bid, topBids[j].Bid, preferDeals) @@ -187,7 +187,7 @@ func (a *auction) validateAndUpdateMultiBid(adapterBids map[openrtb_ext.BidderNa func (a *auction) setRoundedPrices(targetingData targetData) { roundedPrices := make(map[*entities.PbsOrtbBid]string, 5*len(a.winningBids)) - for _, topBidsPerImp := range a.winningBidsByBidder { + for _, topBidsPerImp := range a.allBidsByBidder { for _, topBidsPerBidder := range topBidsPerImp { for _, topBid := range topBidsPerBidder { roundedPrices[topBid] = GetPriceBucket(*topBid.Bid, targetingData) @@ -225,7 +225,7 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, for _, imp := range bidRequest.Imp { expByImp[imp.ID] = imp.Exp } - for impID, topBidsPerImp := range a.winningBidsByBidder { + for impID, topBidsPerImp := range a.allBidsByBidder { for bidderName, topBidsPerBidder := range topBidsPerImp { for _, topBid := range topBidsPerBidder { isOverallWinner := a.winningBids[impID] == topBid @@ -244,7 +244,7 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, } } if bids { - if jsonBytes, err := json.Marshal(topBid.Bid); err == nil { + if jsonBytes, err := jsonutil.Marshal(topBid.Bid); err == nil { jsonBytes, err = evTracking.modifyBidJSON(topBid, bidderName, jsonBytes) if err != nil { errs = append(errs, err) @@ -265,7 +265,7 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, } if vast && topBid.BidType == openrtb_ext.BidTypeVideo { vastXML := makeVAST(topBid.Bid) - if jsonBytes, err := json.Marshal(vastXML); err == nil { + if jsonBytes, err := jsonutil.Marshal(vastXML); err == nil { if useCustomCacheKey { toCache = append(toCache, prebid_cache_client.Cacheable{ Type: prebid_cache_client.TypeXML, @@ -292,7 +292,7 @@ func (a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, if len(toCache) > 0 && debugLog != nil && debugLog.DebugEnabledOrOverridden { debugLog.CacheKey = hbCacheID debugLog.BuildCacheString() - if jsonBytes, err := json.Marshal(debugLog.CacheString); err == nil { + if jsonBytes, err := jsonutil.Marshal(debugLog.CacheString); err == nil { toCache = append(toCache, prebid_cache_client.Cacheable{ Type: debugLog.CacheType, Data: jsonBytes, @@ -400,8 +400,8 @@ func defTTL(bidType openrtb_ext.BidType, defaultTTLs *config.DefaultTTLs) (ttl i type auction struct { // winningBids is a map from imp.id to the highest overall CPM bid in that imp. winningBids map[string]*entities.PbsOrtbBid - // winningBidsByBidder stores the highest bid on each imp by each bidder. - winningBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid + // allBidsByBidder is map from ImpID to another map that maps bidderName to all bids from that bidder. + allBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid // roundedPrices stores the price strings rounded for each bid according to the price granularity. roundedPrices map[*entities.PbsOrtbBid]string // cacheIds stores the UUIDs from Prebid Cache for fetching the full bid JSON. diff --git a/exchange/auction_response.go b/exchange/auction_response.go index 3b85a4472c2..1d0747b0fe5 100644 --- a/exchange/auction_response.go +++ b/exchange/auction_response.go @@ -1,8 +1,8 @@ package exchange import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // AuctionResponse contains OpenRTB Bid Response object and its extension (un-marshalled) object diff --git a/exchange/auction_test.go b/exchange/auction_test.go index 8d8e248ec83..8e464aeca86 100644 --- a/exchange/auction_test.go +++ b/exchange/auction_test.go @@ -2,7 +2,6 @@ package exchange import ( "context" - "encoding/json" "encoding/xml" "fmt" "os" @@ -12,12 +11,13 @@ import ( "strconv" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/prebid_cache_client" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -193,7 +193,7 @@ func loadCacheSpec(filename string) (*cacheSpec, error) { } var spec cacheSpec - if err := json.Unmarshal(specData, &spec); err != nil { + if err := jsonutil.UnmarshalValid(specData, &spec); err != nil { return nil, fmt.Errorf("Failed to unmarshal JSON from file: %v", err) } @@ -205,7 +205,7 @@ func loadCacheSpec(filename string) (*cacheSpec, error) { func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) { var bid *entities.PbsOrtbBid winningBidsByImp := make(map[string]*entities.PbsOrtbBid) - winningBidsByBidder := make(map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid) + allBidsByBidder := make(map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid) roundedPrices := make(map[*entities.PbsOrtbBid]string) bidCategory := make(map[string]string) @@ -224,15 +224,15 @@ func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) { } // Map this bid if it's the highest we've seen from this bidder so far - if bidMap, ok := winningBidsByBidder[bid.Bid.ImpID]; ok { + if bidMap, ok := allBidsByBidder[bid.Bid.ImpID]; ok { bidMap[pbsBid.Bidder] = append(bidMap[pbsBid.Bidder], bid) } else { - winningBidsByBidder[bid.Bid.ImpID] = map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder[bid.Bid.ImpID] = map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ pbsBid.Bidder: {bid}, } } - for _, topBidsPerBidder := range winningBidsByBidder { + for _, topBidsPerBidder := range allBidsByBidder { for _, topBids := range topBidsPerBidder { sort.Slice(topBids, func(i, j int) bool { return isNewWinningBid(topBids[i].Bid, topBids[j].Bid, true) @@ -277,9 +277,9 @@ func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) { } testAuction := &auction{ - winningBids: winningBidsByImp, - winningBidsByBidder: winningBidsByBidder, - roundedPrices: roundedPrices, + winningBids: winningBidsByImp, + allBidsByBidder: allBidsByBidder, + roundedPrices: roundedPrices, } evTracking := &eventTracking{ accountID: "TEST_ACC_ID", @@ -300,18 +300,18 @@ func runCacheSpec(t *testing.T, fileDisplayName string, specData *cacheSpec) { for i, expectedCacheable := range specData.ExpectedCacheables { found := false var expectedData interface{} - if err := json.Unmarshal(expectedCacheable.Data, &expectedData); err != nil { + if err := jsonutil.UnmarshalValid(expectedCacheable.Data, &expectedData); err != nil { t.Fatalf("Failed to decode expectedCacheables[%d].value: %v", i, err) } if s, ok := expectedData.(string); ok && expectedCacheable.Type == prebid_cache_client.TypeJSON { // decode again if we have pre-encoded json string values - if err := json.Unmarshal([]byte(s), &expectedData); err != nil { + if err := jsonutil.UnmarshalValid([]byte(s), &expectedData); err != nil { t.Fatalf("Failed to re-decode expectedCacheables[%d].value :%v", i, err) } } for j, cachedItem := range cache.items { var actualData interface{} - if err := json.Unmarshal(cachedItem.Data, &actualData); err != nil { + if err := jsonutil.UnmarshalValid(cachedItem.Data, &actualData); err != nil { t.Fatalf("Failed to decode actual cache[%d].value: %s", j, err) } if assert.ObjectsAreEqual(expectedData, actualData) && @@ -419,7 +419,7 @@ func TestNewAuction(t *testing.T) { winningBids: map[string]*entities.PbsOrtbBid{ "imp1": &bid1p230, }, - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p123}, "rubicon": []*entities.PbsOrtbBid{&bid1p230}, @@ -447,7 +447,7 @@ func TestNewAuction(t *testing.T) { "imp1": &bid1p230, "imp2": &bid2p144, }, - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p230}, "rubicon": []*entities.PbsOrtbBid{&bid1p077}, @@ -476,7 +476,7 @@ func TestNewAuction(t *testing.T) { winningBids: map[string]*entities.PbsOrtbBid{ "imp1": &bid1p123, }, - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p123}, "rubicon": []*entities.PbsOrtbBid{&bid1p088d}, @@ -500,7 +500,7 @@ func TestNewAuction(t *testing.T) { winningBids: map[string]*entities.PbsOrtbBid{ "imp1": &bid1p088d, }, - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p123}, "rubicon": []*entities.PbsOrtbBid{&bid1p088d}, @@ -524,7 +524,7 @@ func TestNewAuction(t *testing.T) { winningBids: map[string]*entities.PbsOrtbBid{ "imp1": &bid1p166d, }, - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p166d}, "rubicon": []*entities.PbsOrtbBid{&bid1p088d}, @@ -551,7 +551,7 @@ func TestNewAuction(t *testing.T) { winningBids: map[string]*entities.PbsOrtbBid{ "imp1": &bid1p166d, }, - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p166d}, "rubicon": []*entities.PbsOrtbBid{&bid1p088d}, @@ -577,7 +577,7 @@ func TestNewAuction(t *testing.T) { "imp1": &bid1p166d, "imp2": &bid2p166, }, - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p166d, &bid1p077}, "pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123}, @@ -659,11 +659,11 @@ func TestValidateAndUpdateMultiBid(t *testing.T) { } type fields struct { - winningBids map[string]*entities.PbsOrtbBid - winningBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid - roundedPrices map[*entities.PbsOrtbBid]string - cacheIds map[*openrtb2.Bid]string - vastCacheIds map[*openrtb2.Bid]string + winningBids map[string]*entities.PbsOrtbBid + allBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid + roundedPrices map[*entities.PbsOrtbBid]string + cacheIds map[*openrtb2.Bid]string + vastCacheIds map[*openrtb2.Bid]string } type args struct { adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid @@ -671,8 +671,8 @@ func TestValidateAndUpdateMultiBid(t *testing.T) { accountDefaultBidLimit int } type want struct { - winningBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid - adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid + allBidsByBidder map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid + adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid } tests := []struct { description string @@ -687,7 +687,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) { "imp1": &bid1p166d, "imp2": &bid2p166, }, - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p001, &bid1p166d, &bid1p077}, "pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123}, @@ -711,7 +711,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) { preferDeals: true, }, want: want{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p166d, &bid1p077, &bid1p001}, "pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123}, @@ -738,7 +738,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) { "imp1": &bid1p166d, "imp2": &bid2p166, }, - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p001, &bid1p166d, &bid1p077}, "pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123}, @@ -762,7 +762,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) { preferDeals: true, }, want: want{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p166d, &bid1p077, &bid1p001}, "pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123}, @@ -789,7 +789,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) { "imp1": &bid1p166d, "imp2": &bid2p166, }, - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p001, &bid1p166d, &bid1p077}, "pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123}, @@ -813,7 +813,7 @@ func TestValidateAndUpdateMultiBid(t *testing.T) { preferDeals: true, }, want: want{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp1": { "appnexus": []*entities.PbsOrtbBid{&bid1p166d, &bid1p077}, "pubmatic": []*entities.PbsOrtbBid{&bid1p088d, &bid1p123}, @@ -837,14 +837,14 @@ func TestValidateAndUpdateMultiBid(t *testing.T) { for _, tt := range tests { t.Run(tt.description, func(t *testing.T) { a := &auction{ - winningBids: tt.fields.winningBids, - winningBidsByBidder: tt.fields.winningBidsByBidder, - roundedPrices: tt.fields.roundedPrices, - cacheIds: tt.fields.cacheIds, - vastCacheIds: tt.fields.vastCacheIds, + winningBids: tt.fields.winningBids, + allBidsByBidder: tt.fields.allBidsByBidder, + roundedPrices: tt.fields.roundedPrices, + cacheIds: tt.fields.cacheIds, + vastCacheIds: tt.fields.vastCacheIds, } a.validateAndUpdateMultiBid(tt.args.adapterBids, tt.args.preferDeals, tt.args.accountDefaultBidLimit) - assert.Equal(t, tt.want.winningBidsByBidder, tt.fields.winningBidsByBidder, tt.description) + assert.Equal(t, tt.want.allBidsByBidder, tt.fields.allBidsByBidder, tt.description) assert.Equal(t, tt.want.adapterBids, tt.args.adapterBids, tt.description) }) } diff --git a/exchange/bidder.go b/exchange/bidder.go index 2ac15c10fe5..bb29810dd3b 100644 --- a/exchange/bidder.go +++ b/exchange/bidder.go @@ -13,26 +13,28 @@ import ( "net/http/httptrace" "regexp" "strings" + "sync" "time" "github.com/golang/glog" - "github.com/prebid/prebid-server/bidadjustment" - "github.com/prebid/prebid-server/config/util" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/experiment/adscert" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/version" - - "github.com/prebid/openrtb/v19/adcom1" - nativeRequests "github.com/prebid/openrtb/v19/native1/request" - nativeResponse "github.com/prebid/openrtb/v19/native1/response" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/bidadjustment" + "github.com/prebid/prebid-server/v2/config/util" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/experiment/adscert" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/version" + + "github.com/prebid/openrtb/v20/adcom1" + nativeRequests "github.com/prebid/openrtb/v20/native1/request" + nativeResponse "github.com/prebid/openrtb/v20/native1/response" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" "golang.org/x/net/context/ctxhttp" ) @@ -69,6 +71,7 @@ type bidRequestOptions struct { bidAdjustments map[string]float64 tmaxAdjustments *TmaxAdjustmentsPreprocessed bidderRequestStartTime time.Time + responseDebugAllowed bool } type extraBidderRespInfo struct { @@ -92,8 +95,6 @@ const ( Gzip string = "GZIP" ) -var errTmaxTimeout = errors.New("exceeded tmax duration") - // AdaptBidder converts an adapters.Bidder into an exchange.AdaptedBidder. // // The name refers to the "Adapter" architecture pattern, and should not be confused with a Prebid "Adapter" @@ -136,7 +137,8 @@ type bidderAdapterConfig struct { } func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest BidderRequest, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, adsCertSigner adscert.Signer, bidRequestOptions bidRequestOptions, alternateBidderCodes openrtb_ext.ExtAlternateBidderCodes, hookExecutor hookexecution.StageExecutor, ruleToAdjustments openrtb_ext.AdjustmentsByDealID) ([]*entities.PbsOrtbSeatBid, extraBidderRespInfo, []error) { - reject := hookExecutor.ExecuteBidderRequestStage(bidderRequest.BidRequest, string(bidderRequest.BidderName)) + request := openrtb_ext.RequestWrapper{BidRequest: bidderRequest.BidRequest} + reject := hookExecutor.ExecuteBidderRequestStage(&request, string(bidderRequest.BidderName)) if reject != nil { return nil, extraBidderRespInfo{}, []error{reject} } @@ -148,6 +150,10 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde extraRespInfo extraBidderRespInfo ) + // rebuild request after modules execution + request.RebuildRequest() + bidderRequest.BidRequest = request.BidRequest + //check if real request exists for this bidder or it only has stored responses dataLen := 0 if len(bidderRequest.BidRequest.Imp) > 0 { @@ -313,7 +319,7 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde errs = append(errs, moreErrs...) if nativeMarkup != nil { - markup, err := json.Marshal(*nativeMarkup) + markup, err := jsonutil.Marshal(*nativeMarkup) if err != nil { errs = append(errs, err) } else { @@ -352,10 +358,6 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde if err == nil { // Conversion rate found, using it for conversion for i := 0; i < len(bidResponse.Bids); i++ { - if bidResponse.Bids[i].BidMeta == nil { - bidResponse.Bids[i].BidMeta = &openrtb_ext.ExtBidPrebidMeta{} - } - bidResponse.Bids[i].BidMeta.AdapterCode = bidderRequest.BidderName.String() bidderName := bidderRequest.BidderName if bidResponse.Bids[i].Seat != "" { @@ -376,9 +378,9 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde adjustmentFactor := 1.0 if bidderName != bidderGroupM { - if givenAdjustment, ok := bidRequestOptions.bidAdjustments[bidderName.String()]; ok { + if givenAdjustment, ok := bidRequestOptions.bidAdjustments[(strings.ToLower(bidderName.String()))]; ok { adjustmentFactor = givenAdjustment - } else if givenAdjustment, ok := bidRequestOptions.bidAdjustments[bidderRequest.BidderName.String()]; ok { + } else if givenAdjustment, ok := bidRequestOptions.bidAdjustments[(strings.ToLower(bidderRequest.BidderName.String()))]; ok { adjustmentFactor = givenAdjustment } } @@ -404,18 +406,22 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde Seat: bidderName.String(), } } + alternateBidderCode := "" + if !strings.EqualFold(bidderRequest.BidderName.String(), bidderName.String()) { + alternateBidderCode = bidderRequest.BidderName.String() + } seatBidMap[bidderName].Bids = append(seatBidMap[bidderName].Bids, &entities.PbsOrtbBid{ - Bid: bidResponse.Bids[i].Bid, - BidMeta: bidResponse.Bids[i].BidMeta, - BidType: bidResponse.Bids[i].BidType, - BidVideo: bidResponse.Bids[i].BidVideo, - DealPriority: bidResponse.Bids[i].DealPriority, - OriginalBidCPM: originalBidCpm, - OriginalBidCur: bidResponse.Currency, - BidTargets: bidResponse.Bids[i].BidTargets, - - OriginalBidCPMUSD: originalBidCPMUSD, + Bid: bidResponse.Bids[i].Bid, + BidMeta: bidResponse.Bids[i].BidMeta, + BidType: bidResponse.Bids[i].BidType, + BidVideo: bidResponse.Bids[i].BidVideo, + DealPriority: bidResponse.Bids[i].DealPriority, + OriginalBidCPM: originalBidCpm, + OriginalBidCur: bidResponse.Currency, + BidTargets: bidResponse.Bids[i].BidTargets, + OriginalBidCPMUSD: originalBidCPMUSD, + AlternateBidderCode: alternateBidderCode, }) seatBidMap[bidderName].Currency = currencyAfterAdjustments } @@ -441,8 +447,8 @@ func (bidder *bidderAdapter) requestBid(ctx context.Context, bidderRequest Bidde func addNativeTypes(bid *openrtb2.Bid, request *openrtb2.BidRequest) (*nativeResponse.Response, []error) { var errs []error - var nativeMarkup *nativeResponse.Response - if err := json.Unmarshal(json.RawMessage(bid.AdM), &nativeMarkup); err != nil || len(nativeMarkup.Assets) == 0 { + var nativeMarkup nativeResponse.Response + if err := jsonutil.UnmarshalValid(json.RawMessage(bid.AdM), &nativeMarkup); err != nil || len(nativeMarkup.Assets) == 0 { // Some bidders are returning non-IAB compliant native markup. In this case Prebid server will not be able to add types. E.g Facebook return nil, errs } @@ -454,7 +460,7 @@ func addNativeTypes(bid *openrtb2.Bid, request *openrtb2.BidRequest) (*nativeRes } var nativePayload nativeRequests.Request - if err := json.Unmarshal(json.RawMessage((*nativeImp).Request), &nativePayload); err != nil { + if err := jsonutil.UnmarshalValid(json.RawMessage((*nativeImp).Request), &nativePayload); err != nil { errs = append(errs, err) } @@ -464,7 +470,7 @@ func addNativeTypes(bid *openrtb2.Bid, request *openrtb2.BidRequest) (*nativeRes } } - return nativeMarkup, errs + return &nativeMarkup, errs } func setAssetTypes(asset nativeResponse.Asset, nativePayload nativeRequests.Request) error { @@ -561,16 +567,14 @@ func (bidder *bidderAdapter) doRequest(ctx context.Context, req *adapters.Reques } func (bidder *bidderAdapter) doRequestImpl(ctx context.Context, req *adapters.RequestData, logger util.LogMsg, bidderRequestStartTime time.Time, tmaxAdjustments *TmaxAdjustmentsPreprocessed) *httpCallInfo { - var requestBody []byte - - switch strings.ToUpper(bidder.config.EndpointCompression) { - case Gzip: - requestBody = compressToGZIP(req.Body) - req.Headers.Set("Content-Encoding", "gzip") - default: - requestBody = req.Body + requestBody, err := getRequestBody(req, bidder.config.EndpointCompression) + if err != nil { + return &httpCallInfo{ + request: req, + err: err, + } } - httpReq, err := http.NewRequest(req.Method, req.Uri, bytes.NewBuffer(requestBody)) + httpReq, err := http.NewRequest(req.Method, req.Uri, requestBody) if err != nil { return &httpCallInfo{ request: req, @@ -591,7 +595,7 @@ func (bidder *bidderAdapter) doRequestImpl(ctx context.Context, req *adapters.Re bidder.me.RecordTMaxTimeout() return &httpCallInfo{ request: req, - err: errTmaxTimeout, + err: &errortypes.TmaxTimeout{Message: "exceeded tmax duration"}, } } } @@ -614,6 +618,7 @@ func (bidder *bidderAdapter) doRequestImpl(ctx context.Context, req *adapters.Re // a loop of trying to report timeouts to the timeout notifications. go bidder.doTimeoutNotification(tb, req, logger) } + } return &httpCallInfo{ request: req, @@ -790,3 +795,37 @@ func hasShorterDurationThanTmax(ctx bidderTmaxContext, tmaxAdjustments TmaxAdjus } return false } + +func getRequestBody(req *adapters.RequestData, endpointCompression string) (*bytes.Buffer, error) { + switch strings.ToUpper(endpointCompression) { + case Gzip: + // Compress to GZIP + b := bytes.NewBuffer(make([]byte, 0, len(req.Body))) + + w := gzipWriterPool.Get().(*gzip.Writer) + defer gzipWriterPool.Put(w) + + w.Reset(b) + _, err := w.Write(req.Body) + if err != nil { + return nil, err + } + err = w.Close() + if err != nil { + return nil, err + } + + // Set Header + req.Headers.Set("Content-Encoding", "gzip") + + return b, nil + default: + return bytes.NewBuffer(req.Body), nil + } +} + +var gzipWriterPool = sync.Pool{ + New: func() interface{} { + return gzip.NewWriter(nil) + }, +} diff --git a/exchange/bidder_test.go b/exchange/bidder_test.go index d75283783bc..1e4a59aeedb 100644 --- a/exchange/bidder_test.go +++ b/exchange/bidder_test.go @@ -2,6 +2,7 @@ package exchange import ( "bytes" + "compress/gzip" "context" "crypto/tls" "encoding/json" @@ -17,21 +18,23 @@ import ( "time" "github.com/golang/glog" - "github.com/prebid/openrtb/v19/adcom1" - nativeRequests "github.com/prebid/openrtb/v19/native1/request" - nativeResponse "github.com/prebid/openrtb/v19/native1/response" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/experiment/adscert" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/metrics" - metricsConfig "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/version" + "github.com/prebid/openrtb/v20/adcom1" + nativeRequests "github.com/prebid/openrtb/v20/native1/request" + nativeResponse "github.com/prebid/openrtb/v20/native1/response" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/experiment/adscert" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/metrics" + metricsConfig "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/prebid/prebid-server/v2/version" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -856,7 +859,7 @@ func TestMultiCurrencies(t *testing.T) { mockedHTTPServer := httptest.NewServer(http.HandlerFunc( func(rw http.ResponseWriter, req *http.Request) { - b, err := json.Marshal(tc.rates) + b, err := jsonutil.Marshal(tc.rates) if err == nil { rw.WriteHeader(http.StatusOK) rw.Write(b) @@ -1177,7 +1180,7 @@ func TestMultiCurrencies_RequestCurrencyPick(t *testing.T) { mockedHTTPServer := httptest.NewServer(http.HandlerFunc( func(rw http.ResponseWriter, req *http.Request) { - b, err := json.Marshal(tc.rates) + b, err := jsonutil.Marshal(tc.rates) if err == nil { rw.WriteHeader(http.StatusOK) rw.Write(b) @@ -1567,6 +1570,46 @@ func TestMobileNativeTypes(t *testing.T) { } } +func TestAddNativeTypes(t *testing.T) { + testCases := []struct { + description string + bidderRequest *openrtb2.BidRequest + bid *openrtb2.Bid + expectedResponse *nativeResponse.Response + expectedErrors []error + }{ + { + description: "Null in bid.Adm in response", + bidderRequest: &openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + { + ID: "some-imp-id", + Native: &openrtb2.Native{ + Request: "{\"ver\":\"1.1\",\"context\":1,\"contextsubtype\":11,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":500}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":1,\"hmin\":1}},{\"id\":3,\"required\":0,\"data\":{\"type\":1,\"len\":200}},{\"id\":4,\"required\":0,\"data\":{\"type\":2,\"len\":15000}},{\"id\":5,\"required\":0,\"data\":{\"type\":6,\"len\":40}}]}", + }, + }, + }, + App: &openrtb2.App{}, + }, + bid: &openrtb2.Bid{ + ImpID: "some-imp-id", + AdM: "null", + Price: 10, + }, + expectedResponse: nil, + expectedErrors: nil, + }, + } + + for _, tt := range testCases { + t.Run(tt.description, func(t *testing.T) { + resp, errs := addNativeTypes(tt.bid, tt.bidderRequest) + assert.Equal(t, tt.expectedResponse, resp, "response") + assert.Equal(t, tt.expectedErrors, errs, "errors") + }) + } +} + func TestRequestBidsStoredBidResponses(t *testing.T) { respBody := "{\"bid\":false}" respStatus := 200 @@ -1864,7 +1907,7 @@ func TestSetAssetTypes(t *testing.T) { }, { respAsset: nativeResponse.Asset{ - ID: openrtb2.Int64Ptr(2), + ID: ptrutil.ToPtr[int64](2), Data: &nativeResponse.Data{ Label: "some label", }, @@ -2344,7 +2387,7 @@ func (bidder *goodSingleBidderWithStoredBidResp) MakeRequests(request *openrtb2. func (bidder *goodSingleBidderWithStoredBidResp) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { var bidResp openrtb2.BidResponse - if err := json.Unmarshal(response.Body, &bidResp); err != nil { + if err := jsonutil.UnmarshalValid(response.Body, &bidResp); err != nil { return nil, []error{err} } bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) @@ -2475,12 +2518,11 @@ func TestExtraBid(t *testing.T) { { HttpCalls: []*openrtb_ext.ExtHttpCall{}, Bids: []*entities.PbsOrtbBid{{ - Bid: &openrtb2.Bid{ID: "groupmImp1"}, - DealPriority: 5, - BidType: openrtb_ext.BidTypeVideo, - OriginalBidCur: "USD", - BidMeta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: string(openrtb_ext.BidderPubmatic)}, - }}, + Bid: &openrtb2.Bid{ID: "groupmImp1"}, + DealPriority: 5, + BidType: openrtb_ext.BidTypeVideo, + OriginalBidCur: "USD", + AlternateBidderCode: string(openrtb_ext.BidderPubmatic)}}, Seat: "groupm", Currency: "USD", }, @@ -2491,7 +2533,6 @@ func TestExtraBid(t *testing.T) { DealPriority: 4, BidType: openrtb_ext.BidTypeBanner, OriginalBidCur: "USD", - BidMeta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: string(openrtb_ext.BidderPubmatic)}, }}, Seat: string(openrtb_ext.BidderPubmatic), Currency: "USD", @@ -2585,11 +2626,11 @@ func TestExtraBidWithAlternateBidderCodeDisabled(t *testing.T) { { HttpCalls: []*openrtb_ext.ExtHttpCall{}, Bids: []*entities.PbsOrtbBid{{ - Bid: &openrtb2.Bid{ID: "groupmImp2"}, - DealPriority: 5, - BidType: openrtb_ext.BidTypeVideo, - OriginalBidCur: "USD", - BidMeta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: string(openrtb_ext.BidderPubmatic)}, + Bid: &openrtb2.Bid{ID: "groupmImp2"}, + DealPriority: 5, + BidType: openrtb_ext.BidTypeVideo, + OriginalBidCur: "USD", + AlternateBidderCode: string(openrtb_ext.BidderPubmatic), }}, Seat: "groupm-allowed", Currency: "USD", @@ -2601,7 +2642,6 @@ func TestExtraBidWithAlternateBidderCodeDisabled(t *testing.T) { DealPriority: 4, BidType: openrtb_ext.BidTypeBanner, OriginalBidCur: "USD", - BidMeta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: string(openrtb_ext.BidderPubmatic)}, }}, Seat: string(openrtb_ext.BidderPubmatic), Currency: "USD", @@ -2672,7 +2712,7 @@ func TestExtraBidWithBidAdjustments(t *testing.T) { }, BidType: openrtb_ext.BidTypeBanner, DealPriority: 4, - Seat: "pubmatic", + Seat: "PUBMATIC", }, { Bid: &openrtb2.Bid{ @@ -2695,11 +2735,11 @@ func TestExtraBidWithBidAdjustments(t *testing.T) { ID: "groupmImp1", Price: 7, }, - DealPriority: 5, - BidType: openrtb_ext.BidTypeVideo, - OriginalBidCPM: 7, - OriginalBidCur: "USD", - BidMeta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: string(openrtb_ext.BidderPubmatic)}, + DealPriority: 5, + BidType: openrtb_ext.BidTypeVideo, + OriginalBidCPM: 7, + OriginalBidCur: "USD", + AlternateBidderCode: "PUBMATIC", }}, Seat: "groupm", Currency: "USD", @@ -2715,9 +2755,8 @@ func TestExtraBidWithBidAdjustments(t *testing.T) { BidType: openrtb_ext.BidTypeBanner, OriginalBidCur: "USD", OriginalBidCPM: 3, - BidMeta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: string(openrtb_ext.BidderPubmatic)}, }}, - Seat: string(openrtb_ext.BidderPubmatic), + Seat: "PUBMATIC", Currency: "USD", }, } @@ -2727,10 +2766,10 @@ func TestExtraBidWithBidAdjustments(t *testing.T) { bidderReq := BidderRequest{ BidRequest: &openrtb2.BidRequest{Imp: []openrtb2.Imp{{ID: "impId"}}}, - BidderName: openrtb_ext.BidderPubmatic, + BidderName: "PUBMATIC", } bidAdjustments := map[string]float64{ - string(openrtb_ext.BidderPubmatic): 2, + string(openrtb_ext.BidderPubmatic): 2, // All lowercase value in bid adjustments to simulate it being case insensitive "groupm": 3, } @@ -2745,7 +2784,7 @@ func TestExtraBidWithBidAdjustments(t *testing.T) { openrtb_ext.ExtAlternateBidderCodes{ Enabled: true, Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ - string(openrtb_ext.BidderPubmatic): { + "PUBMATIC": { Enabled: true, AllowedBidderCodes: []string{"groupm"}, }, @@ -2810,11 +2849,11 @@ func TestExtraBidWithBidAdjustmentsUsingAdapterCode(t *testing.T) { ID: "groupmImp1", Price: 7, }, - DealPriority: 5, - BidType: openrtb_ext.BidTypeVideo, - OriginalBidCPM: 7, - OriginalBidCur: "USD", - BidMeta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: string(openrtb_ext.BidderPubmatic)}, + DealPriority: 5, + BidType: openrtb_ext.BidTypeVideo, + OriginalBidCPM: 7, + OriginalBidCur: "USD", + AlternateBidderCode: string(openrtb_ext.BidderPubmatic), }}, Seat: "groupm", Currency: "USD", @@ -2830,7 +2869,6 @@ func TestExtraBidWithBidAdjustmentsUsingAdapterCode(t *testing.T) { BidType: openrtb_ext.BidTypeBanner, OriginalBidCur: "USD", OriginalBidCPM: 3, - BidMeta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: string(openrtb_ext.BidderPubmatic)}, }}, Seat: string(openrtb_ext.BidderPubmatic), Currency: "USD", @@ -2924,12 +2962,12 @@ func TestExtraBidWithMultiCurrencies(t *testing.T) { ID: "groupmImp1", Price: 571.5994430039375, }, - DealPriority: 5, - BidType: openrtb_ext.BidTypeVideo, - OriginalBidCPM: 7, - OriginalBidCur: "USD", - OriginalBidCPMUSD: 7, - BidMeta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: string(openrtb_ext.BidderPubmatic)}, + DealPriority: 5, + BidType: openrtb_ext.BidTypeVideo, + OriginalBidCPM: 7, + OriginalBidCur: "USD", + OriginalBidCPMUSD: 7, + AlternateBidderCode: string(openrtb_ext.BidderPubmatic), }}, Seat: "groupm", Currency: "INR", @@ -2946,7 +2984,6 @@ func TestExtraBidWithMultiCurrencies(t *testing.T) { OriginalBidCPM: 3, OriginalBidCur: "USD", OriginalBidCPMUSD: 3, - BidMeta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: string(openrtb_ext.BidderPubmatic)}, }}, Seat: string(openrtb_ext.BidderPubmatic), Currency: "INR", @@ -3223,7 +3260,7 @@ func TestDoRequestImplWithTmax(t *testing.T) { ctxDeadline: time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC), description: "returns-tmax-timeout-error", tmaxAdjustments: &TmaxAdjustmentsPreprocessed{IsEnforced: true, PBSResponsePreparationDuration: 100, BidderNetworkLatencyBuffer: 10, BidderResponseDurationMin: 5000}, - assertFn: func(err error) { assert.Equal(t, errTmaxTimeout, err) }, + assertFn: func(err error) { assert.Equal(t, &errortypes.TmaxTimeout{Message: "exceeded tmax duration"}, err) }, }, { ctxDeadline: time.Now().Add(5 * time.Second), @@ -3298,7 +3335,7 @@ func TestDoRequestImplWithTmaxTimeout(t *testing.T) { ctxDeadline: time.Date(2023, 1, 1, 0, 0, 0, 0, time.UTC), description: "returns-tmax-timeout-error", tmaxAdjustments: &TmaxAdjustmentsPreprocessed{IsEnforced: true, PBSResponsePreparationDuration: 100, BidderNetworkLatencyBuffer: 10, BidderResponseDurationMin: 5000}, - assertFn: func(err error) { assert.Equal(t, errTmaxTimeout, err) }, + assertFn: func(err error) { assert.Equal(t, &errortypes.TmaxTimeout{Message: "exceeded tmax duration"}, err) }, }, } for _, test := range tests { @@ -3318,3 +3355,77 @@ func TestDoRequestImplWithTmaxTimeout(t *testing.T) { test.assertFn(httpCallInfo.err) } } + +func TestGetRequestBody(t *testing.T) { + tests := []struct { + name string + endpointCompression string + givenReqBody []byte + }{ + { + name: "No-Compression", + endpointCompression: "", + givenReqBody: []byte("test body"), + }, + { + name: "GZIP-Compression", + endpointCompression: "GZIP", + givenReqBody: []byte("test body"), + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + req := &adapters.RequestData{Body: test.givenReqBody, Headers: http.Header{}} + requestBody, err := getRequestBody(req, test.endpointCompression) + assert.NoError(t, err) + + if test.endpointCompression == "GZIP" { + assert.Equal(t, "gzip", req.Headers.Get("Content-Encoding")) + + decompressedReqBody, err := decompressGzip(requestBody.Bytes()) + assert.NoError(t, err) + assert.Equal(t, test.givenReqBody, decompressedReqBody) + } else { + assert.Equal(t, test.givenReqBody, requestBody.Bytes()) + } + }) + } +} + +func decompressGzip(input []byte) ([]byte, error) { + r, err := gzip.NewReader(bytes.NewReader(input)) + if err != nil { + return nil, err + } + defer r.Close() + + decompressed, err := io.ReadAll(r) + if err != nil { + return nil, err + } + + return decompressed, nil +} + +func BenchmarkCompressToGZIPOptimized(b *testing.B) { + // Setup the mock server + respBody := "{\"bid\":false}" + respStatus := 200 + server := httptest.NewServer(mockHandler(respStatus, "getBody", respBody)) + defer server.Close() + + // Prepare the request data + req := &adapters.RequestData{ + Method: "POST", + Uri: server.URL, + Body: []byte("{\"key\":\"val\"}"), + Headers: http.Header{}, + } + + // Run the benchmark + b.ResetTimer() + for i := 0; i < b.N; i++ { + getRequestBody(req, "GZIP") + } +} diff --git a/exchange/bidder_validate_bids.go b/exchange/bidder_validate_bids.go index 9b5771a3497..0afa32a3321 100644 --- a/exchange/bidder_validate_bids.go +++ b/exchange/bidder_validate_bids.go @@ -6,13 +6,13 @@ import ( "fmt" "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/experiment/adscert" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/experiment/adscert" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/openrtb_ext" goCurrency "golang.org/x/text/currency" ) @@ -34,7 +34,7 @@ type validatedBidder struct { func (v *validatedBidder) requestBid(ctx context.Context, bidderRequest BidderRequest, conversions currency.Conversions, reqInfo *adapters.ExtraRequestInfo, adsCertSigner adscert.Signer, bidRequestOptions bidRequestOptions, alternateBidderCodes openrtb_ext.ExtAlternateBidderCodes, hookExecutor hookexecution.StageExecutor, ruleToAdjustments openrtb_ext.AdjustmentsByDealID) ([]*entities.PbsOrtbSeatBid, extraBidderRespInfo, []error) { seatBids, extraBidderRespInfo, errs := v.bidder.requestBid(ctx, bidderRequest, conversions, reqInfo, adsCertSigner, bidRequestOptions, alternateBidderCodes, hookExecutor, ruleToAdjustments) for _, seatBid := range seatBids { - if validationErrors := removeInvalidBids(bidderRequest.BidRequest, seatBid); len(validationErrors) > 0 { + if validationErrors := removeInvalidBids(bidderRequest.BidRequest, seatBid, bidRequestOptions.responseDebugAllowed); len(validationErrors) > 0 { errs = append(errs, validationErrors...) } } @@ -42,7 +42,7 @@ func (v *validatedBidder) requestBid(ctx context.Context, bidderRequest BidderRe } // validateBids will run some validation checks on the returned bids and excise any invalid bids -func removeInvalidBids(request *openrtb2.BidRequest, seatBid *entities.PbsOrtbSeatBid) []error { +func removeInvalidBids(request *openrtb2.BidRequest, seatBid *entities.PbsOrtbSeatBid, debug bool) []error { // Exit early if there is nothing to do. if seatBid == nil || len(seatBid.Bids) == 0 { return nil @@ -57,10 +57,10 @@ func removeInvalidBids(request *openrtb2.BidRequest, seatBid *entities.PbsOrtbSe errs := make([]error, 0, len(seatBid.Bids)) validBids := make([]*entities.PbsOrtbBid, 0, len(seatBid.Bids)) for _, bid := range seatBid.Bids { - if ok, berr := validateBid(bid); ok { + if ok, err := validateBid(bid, debug); ok { validBids = append(validBids, bid) - } else { - errs = append(errs, berr) + } else if err != nil { + errs = append(errs, err) } } seatBid.Bids = validBids @@ -104,7 +104,7 @@ func validateCurrency(requestAllowedCurrencies []string, bidCurrency string) err } // validateBid will run the supplied bid through validation checks and return true if it passes, false otherwise. -func validateBid(bid *entities.PbsOrtbBid) (bool, error) { +func validateBid(bid *entities.PbsOrtbBid, debug bool) (bool, error) { if bid.Bid == nil { return false, errors.New("Empty bid object submitted.") } @@ -116,10 +116,16 @@ func validateBid(bid *entities.PbsOrtbBid) (bool, error) { return false, fmt.Errorf("Bid \"%s\" missing required field 'impid'", bid.Bid.ID) } if bid.Bid.Price < 0.0 { - return false, fmt.Errorf("Bid \"%s\" does not contain a positive (or zero if there is a deal) 'price'", bid.Bid.ID) + if debug { + return false, fmt.Errorf("Bid \"%s\" does not contain a positive (or zero if there is a deal) 'price'", bid.Bid.ID) + } + return false, nil } if bid.Bid.Price == 0.0 && bid.Bid.DealID == "" { - return false, fmt.Errorf("Bid \"%s\" does not contain positive 'price' which is required since there is no deal set for this bid", bid.Bid.ID) + if debug { + return false, fmt.Errorf("Bid \"%s\" does not contain positive 'price' which is required since there is no deal set for this bid", bid.Bid.ID) + } + return false, nil } if bid.Bid.CrID == "" { return false, fmt.Errorf("Bid \"%s\" missing creative ID", bid.Bid.ID) diff --git a/exchange/bidder_validate_bids_test.go b/exchange/bidder_validate_bids_test.go index ed6173b64ad..a488bf81150 100644 --- a/exchange/bidder_validate_bids_test.go +++ b/exchange/bidder_validate_bids_test.go @@ -4,13 +4,13 @@ import ( "context" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/experiment/adscert" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/experiment/adscert" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -130,10 +130,11 @@ func TestAllBadBids(t *testing.T) { } bidAdjustments := map[string]float64{string(openrtb_ext.BidderAppnexus): 1.0} bidReqOptions := bidRequestOptions{ - accountDebugAllowed: true, - headerDebugAllowed: false, - addCallSignHeader: false, - bidAdjustments: bidAdjustments, + accountDebugAllowed: true, + headerDebugAllowed: false, + addCallSignHeader: false, + bidAdjustments: bidAdjustments, + responseDebugAllowed: true, } seatBids, _, errs := bidder.requestBid(context.Background(), bidderReq, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, &adscert.NilSigner{}, bidReqOptions, openrtb_ext.ExtAlternateBidderCodes{}, &hookexecution.EmptyHookExecutor{}, nil) assert.Len(t, seatBids, 1) @@ -211,15 +212,16 @@ func TestMixedBids(t *testing.T) { } bidAdjustments := map[string]float64{string(openrtb_ext.BidderAppnexus): 1.0} bidReqOptions := bidRequestOptions{ - accountDebugAllowed: true, - headerDebugAllowed: false, - addCallSignHeader: false, - bidAdjustments: bidAdjustments, + accountDebugAllowed: true, + headerDebugAllowed: false, + addCallSignHeader: false, + bidAdjustments: bidAdjustments, + responseDebugAllowed: false, } seatBids, _, errs := bidder.requestBid(context.Background(), bidderReq, currency.NewConstantRates(), &adapters.ExtraRequestInfo{}, &adscert.NilSigner{}, bidReqOptions, openrtb_ext.ExtAlternateBidderCodes{}, &hookexecution.EmptyHookExecutor{}, nil) assert.Len(t, seatBids, 1) assert.Len(t, seatBids[0].Bids, 3) - assert.Len(t, errs, 5) + assert.Len(t, errs, 2) } func TestCurrencyBids(t *testing.T) { diff --git a/exchange/entities/entities.go b/exchange/entities/entities.go index 1295c565ead..a7c149a8b34 100644 --- a/exchange/entities/entities.go +++ b/exchange/entities/entities.go @@ -1,8 +1,8 @@ package entities import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // PbsOrtbSeatBid is a SeatBid returned by an AdaptedBidder. @@ -40,18 +40,19 @@ type PbsOrtbSeatBid struct { // PbsOrtbBid.GeneratedBidID is unique Bid id generated by prebid server if generate Bid id option is enabled in config // pbsOrtbBid.originalBidCPMUSD is USD rate of the bid for WL and WTK as they only accepts USD type PbsOrtbBid struct { - Bid *openrtb2.Bid - BidMeta *openrtb_ext.ExtBidPrebidMeta - BidType openrtb_ext.BidType - BidTargets map[string]string - BidVideo *openrtb_ext.ExtBidPrebidVideo - BidEvents *openrtb_ext.ExtBidPrebidEvents - BidFloors *openrtb_ext.ExtBidPrebidFloors - DealPriority int - DealTierSatisfied bool - GeneratedBidID string - OriginalBidCPM float64 - OriginalBidCur string - TargetBidderCode string - OriginalBidCPMUSD float64 + Bid *openrtb2.Bid + BidMeta *openrtb_ext.ExtBidPrebidMeta + BidType openrtb_ext.BidType + BidTargets map[string]string + BidVideo *openrtb_ext.ExtBidPrebidVideo + BidEvents *openrtb_ext.ExtBidPrebidEvents + BidFloors *openrtb_ext.ExtBidPrebidFloors + DealPriority int + DealTierSatisfied bool + GeneratedBidID string + OriginalBidCPM float64 + OriginalBidCur string + TargetBidderCode string + OriginalBidCPMUSD float64 + AlternateBidderCode string } diff --git a/exchange/events.go b/exchange/events.go index cb08c052f16..20b5a1827d4 100644 --- a/exchange/events.go +++ b/exchange/events.go @@ -1,17 +1,17 @@ package exchange import ( - "encoding/json" "time" - "github.com/prebid/prebid-server/exchange/entities" - - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/endpoints/events" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/exchange/entities" jsonpatch "gopkg.in/evanphx/json-patch.v4" + + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/endpoints/events" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) // eventTracking has configuration fields needed for adding event tracking to an auction response @@ -29,7 +29,7 @@ type eventTracking struct { func getEventTracking(requestExtPrebid *openrtb_ext.ExtRequestPrebid, ts time.Time, account *config.Account, bidderInfos config.BidderInfos, externalURL string) *eventTracking { return &eventTracking{ accountID: account.ID, - enabledForAccount: account.Events.IsEnabled(), + enabledForAccount: account.Events.Enabled, enabledForRequest: requestExtPrebid != nil && requestExtPrebid.Events != nil, auctionTimestampMs: ts.UnixNano() / 1e+6, integrationType: getIntegrationType(requestExtPrebid), @@ -97,7 +97,7 @@ func (ev *eventTracking) modifyBidJSON(pbsBid *entities.PbsOrtbBid, bidderName o winEventURL = ev.makeEventURL(analytics.Win, pbsBid, bidderName) } // wurl attribute is not in the schema, so we have to patch - patch, err := json.Marshal(map[string]string{"wurl": winEventURL}) + patch, err := jsonutil.Marshal(map[string]string{"wurl": winEventURL}) if err != nil { return jsonBytes, err } @@ -136,7 +136,7 @@ func (ev *eventTracking) makeEventURL(evType analytics.EventType, pbsBid *entiti }) } -// isEnabled checks if events are enabled by default or on account/request level +// isEventAllowed checks if events are enabled by default or on account/request level func (ev *eventTracking) isEventAllowed() bool { return ev.enabledForAccount || ev.enabledForRequest } diff --git a/exchange/events_test.go b/exchange/events_test.go index 61f29bcacac..144e62f19f3 100644 --- a/exchange/events_test.go +++ b/exchange/events_test.go @@ -4,11 +4,10 @@ import ( "strings" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/exchange/entities" - - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/exchange/exchange.go b/exchange/exchange.go index 5ff5c25aea7..7ba55544945 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -14,34 +14,36 @@ import ( "strings" "time" - "github.com/prebid/prebid-server/privacy" - - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adservertargeting" - "github.com/prebid/prebid-server/bidadjustment" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/experiment/adscert" - "github.com/prebid/prebid-server/firstpartydata" - "github.com/prebid/prebid-server/floors" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_responses" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/maputil" + "github.com/prebid/prebid-server/v2/privacy" + + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/adservertargeting" + "github.com/prebid/prebid-server/v2/bidadjustment" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/dsa" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/experiment/adscert" + "github.com/prebid/prebid-server/v2/firstpartydata" + "github.com/prebid/prebid-server/v2/floors" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/prebid_cache_client" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/stored_responses" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/maputil" "github.com/buger/jsonparser" "github.com/gofrs/uuid" "github.com/golang/glog" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" ) type extCacheInstructions struct { @@ -80,9 +82,10 @@ type exchange struct { bidValidationEnforcement config.Validations requestSplitter requestSplitter macroReplacer macros.Replacer + priceFloorEnabled bool + priceFloorFetcher floors.FloorFetcher floor config.PriceFloors trakerURL string - priceFloorFetcher *floors.PriceFloorFetcher } // Container to pass out response ext data from the GetAllBids goroutines back into the main thread @@ -104,7 +107,7 @@ type bidResponseWrapper struct { } type BidIDGenerator interface { - New() (string, error) + New(bidder string) (string, error) Enabled() bool } @@ -116,7 +119,7 @@ func (big *bidIDGenerator) Enabled() bool { return big.enabled } -func (big *bidIDGenerator) New() (string, error) { +func (big *bidIDGenerator) New(bidder string) (string, error) { rawUuid, err := uuid.NewV4() return rawUuid.String(), err } @@ -131,7 +134,7 @@ func (randomDeduplicateBidBooleanGenerator) Generate() bool { return rand.Intn(100) < 50 } -func NewExchange(adapters map[openrtb_ext.BidderName]AdaptedBidder, cache prebid_cache_client.Client, cfg *config.Configuration, syncersByBidder map[string]usersync.Syncer, metricsEngine metrics.MetricsEngine, infos config.BidderInfos, gdprPermsBuilder gdpr.PermissionsBuilder, currencyConverter *currency.RateConverter, categoriesFetcher stored_requests.CategoryFetcher, adsCertSigner adscert.Signer, macroReplacer macros.Replacer, floorFetcher *floors.PriceFloorFetcher) Exchange { +func NewExchange(adapters map[openrtb_ext.BidderName]AdaptedBidder, cache prebid_cache_client.Client, cfg *config.Configuration, syncersByBidder map[string]usersync.Syncer, metricsEngine metrics.MetricsEngine, infos config.BidderInfos, gdprPermsBuilder gdpr.PermissionsBuilder, currencyConverter *currency.RateConverter, categoriesFetcher stored_requests.CategoryFetcher, adsCertSigner adscert.Signer, macroReplacer macros.Replacer, priceFloorFetcher floors.FloorFetcher) Exchange { bidderToSyncerKey := map[string]string{} for bidder, syncer := range syncersByBidder { bidderToSyncerKey[bidder] = syncer.Key() @@ -176,10 +179,10 @@ func NewExchange(adapters map[openrtb_ext.BidderName]AdaptedBidder, cache prebid bidValidationEnforcement: cfg.Validations, requestSplitter: requestSplitter, macroReplacer: macroReplacer, + priceFloorEnabled: cfg.PriceFloors.Enabled, + priceFloorFetcher: priceFloorFetcher, floor: cfg.PriceFloors, - - trakerURL: cfg.TrackerURL, - priceFloorFetcher: floorFetcher, + trakerURL: cfg.TrackerURL, } } @@ -228,6 +231,7 @@ type BidderRequest struct { BidderCoreName openrtb_ext.BidderName BidderLabels metrics.AdapterLabels BidderStoredResponses map[string]json.RawMessage + IsRequestAlias bool ImpReplaceImpId map[string]bool } @@ -236,7 +240,6 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog return nil, nil } - var floorErrs []error err := r.HookExecutor.ExecuteProcessedAuctionStage(r.BidRequestWrapper) if err != nil { return nil, err @@ -269,9 +272,10 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog } // Get currency rates conversions for the auction - conversions := e.getAuctionCurrencyRates(requestExtPrebid.CurrencyConversions) + conversions := currency.GetAuctionCurrencyRates(e.currencyConverter, requestExtPrebid.CurrencyConversions) - if e.floor.Enabled { + var floorErrs []error + if e.priceFloorEnabled { floorErrs = floors.EnrichWithPriceFloors(r.BidRequestWrapper, r.Account, conversions, e.priceFloorFetcher) if floors.RequestHasFloors(r.BidRequestWrapper.BidRequest) { // Record request count with non-zero imp.bidfloor value @@ -286,7 +290,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog if err := r.BidRequestWrapper.RebuildRequest(); err != nil { return nil, err } - resolvedBidReq, err := json.Marshal(r.BidRequestWrapper.BidRequest) + resolvedBidReq, err := jsonutil.Marshal(r.BidRequestWrapper.BidRequest) if err != nil { return nil, err } @@ -379,7 +383,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog alternateBidderCodes = *r.Account.AlternateBidderCodes } var extraRespInfo extraAuctionResponseInfo - adapterBids, adapterExtra, extraRespInfo = e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions, accountDebugAllow, r.GlobalPrivacyControlHeader, debugLog.DebugOverride, alternateBidderCodes, requestExtLegacy.Prebid.Experiment, r.HookExecutor, r.StartTime, bidAdjustmentRules, r.Account.PriceFloors.AdjustForBidAdjustment, r.TmaxAdjustments, r.Account) + adapterBids, adapterExtra, extraRespInfo = e.getAllBids(auctionCtx, bidderRequests, bidAdjustmentFactors, conversions, accountDebugAllow, r.GlobalPrivacyControlHeader, debugLog.DebugOverride, alternateBidderCodes, requestExtLegacy.Prebid.Experiment, r.HookExecutor, r.StartTime, bidAdjustmentRules, r.TmaxAdjustments, responseDebugAllow, r.Account) fledge = extraRespInfo.fledge anyBidsReturned = extraRespInfo.bidsFound r.BidderResponseStartTime = extraRespInfo.bidderResponseStartTime @@ -404,7 +408,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog recordBids(ctx, e.me, r.PubID, adapterBids) recordVastVersion(e.me, adapterBids) - if e.floor.Enabled { + if e.priceFloorEnabled { var rejectedBids []*entities.PbsOrtbSeatBid var enforceErrs []error @@ -454,11 +458,11 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog } if e.bidIDGenerator.Enabled() { - for _, seatBid := range adapterBids { - for _, pbsBid := range seatBid.Bids { - pbsBid.GeneratedBidID, err = e.bidIDGenerator.New() - glog.V(3).Infof("Original BidID = %s Generated BidID = %s", pbsBid.Bid.ID, pbsBid.GeneratedBidID) - if err != nil { + for bidder, seatBid := range adapterBids { + for i := range seatBid.Bids { + if bidID, err := e.bidIDGenerator.New(bidder.String()); err == nil { + seatBid.Bids[i].GeneratedBidID = bidID + } else { errs = append(errs, errors.New("Error generating bid.ext.prebid.bidid")) } } @@ -485,7 +489,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog bidResponseExt = e.makeExtBidResponse(adapterBids, adapterExtra, *r, responseDebugAllow, requestExtPrebid.Passthrough, fledge, errs) if debugLog.DebugEnabledOrOverridden { - if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil { + if bidRespExtBytes, err := jsonutil.Marshal(bidResponseExt); err == nil { debugLog.Data.Response = string(bidRespExtBytes) } else { debugLog.Data.Response = "Unable to marshal response ext for debugging" @@ -506,7 +510,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog if debugLog.DebugEnabledOrOverridden { - if bidRespExtBytes, err := json.Marshal(bidResponseExt); err == nil { + if bidRespExtBytes, err := jsonutil.Marshal(bidResponseExt); err == nil { debugLog.Data.Response = string(bidRespExtBytes) } else { debugLog.Data.Response = "Unable to marshal response ext for debugging" @@ -541,7 +545,7 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog e.bidValidationEnforcement.SetBannerCreativeMaxSize(r.Account.Validations) // Build the response - bidResponse := e.buildBidResponse(ctx, liveAdapters, adapterBids, r.BidRequestWrapper.BidRequest, adapterExtra, auc, bidResponseExt, cacheInstructions.returnCreative, r.ImpExtInfoMap, r.PubID, errs) + bidResponse := e.buildBidResponse(ctx, liveAdapters, adapterBids, r.BidRequestWrapper, adapterExtra, auc, bidResponseExt, cacheInstructions.returnCreative, r.ImpExtInfoMap, r.PubID, errs, &seatNonBids) bidResponse = adservertargeting.Apply(r.BidRequestWrapper, r.ResolvedBidRequest, bidResponse, r.QueryParams, bidResponseExt, r.Account.TruncateTargetAttribute) bidResponse.Ext, err = encodeBidResponseExt(bidResponseExt) @@ -565,10 +569,14 @@ func buildMultiBidMap(prebid *openrtb_ext.ExtRequestPrebid) map[string]openrtb_e multiBidMap := make(map[string]openrtb_ext.ExtMultiBid) for _, multiBid := range prebid.MultiBid { if multiBid.Bidder != "" { - multiBidMap[multiBid.Bidder] = *multiBid + if bidderNormalized, bidderFound := openrtb_ext.NormalizeBidderName(multiBid.Bidder); bidderFound { + multiBidMap[string(bidderNormalized)] = *multiBid + } } else { for _, bidder := range multiBid.Bidders { - multiBidMap[bidder] = *multiBid + if bidderNormalized, bidderFound := openrtb_ext.NormalizeBidderName(bidder); bidderFound { + multiBidMap[string(bidderNormalized)] = *multiBid + } } } } @@ -617,18 +625,22 @@ func applyDealSupport(bidRequest *openrtb2.BidRequest, auc *auction, bidCategory errs := []error{} impDealMap := getDealTiers(bidRequest) - for impID, topBidsPerImp := range auc.winningBidsByBidder { + for impID, topBidsPerImp := range auc.allBidsByBidder { impDeal := impDealMap[impID] for bidder, topBidsPerBidder := range topBidsPerImp { - maxBid := bidsToUpdate(multiBid, bidder.String()) + bidderNormalized, bidderFound := openrtb_ext.NormalizeBidderName(bidder.String()) + if !bidderFound { + bidderNormalized = bidder + } + maxBid := bidsToUpdate(multiBid, bidderNormalized.String()) for i, topBid := range topBidsPerBidder { if i == maxBid { break } if topBid.DealPriority > 0 { - if validateDealTier(impDeal[bidder]) { - updateHbPbCatDur(topBid, impDeal[bidder], bidCategory) + if validateDealTier(impDeal[bidderNormalized]) { + updateHbPbCatDur(topBid, impDeal[bidderNormalized], bidCategory) } else { errs = append(errs, fmt.Errorf("dealTier configuration invalid for bidder '%s', imp ID '%s'", string(bidder), impID)) } @@ -711,8 +723,8 @@ func (e *exchange) getAllBids( hookExecutor hookexecution.StageExecutor, pbsRequestStartTime time.Time, bidAdjustmentRules map[string][]openrtb_ext.Adjustment, - bidFloorAdjustment bool, tmaxAdjustments *TmaxAdjustmentsPreprocessed, + responseDebugAllowed bool, account config.Account) ( map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, @@ -754,6 +766,7 @@ func (e *exchange) getAllBids( bidAdjustments: bidAdjustments, tmaxAdjustments: tmaxAdjustments, bidderRequestStartTime: start, + responseDebugAllowed: responseDebugAllowed, } seatBids, extraBidderRespInfo, err := e.adapterMap[bidderRequest.BidderCoreName].requestBid(ctx, bidderRequest, conversions, &reqInfo, e.adsCertSigner, bidReqOptions, alternateBidderCodes, hookExecutor, bidAdjustmentRules) brw.bidderResponseStartTime = extraBidderRespInfo.respProcessingStartTime @@ -827,6 +840,7 @@ func (e *exchange) getAllBids( } else { adapterBids[bidderName] = seatBid } + extraRespInfo.bidsFound = true } // collect fledgeAuctionConfigs separately from bids, as empty seatBids may be discarded extraRespInfo.fledge = collectFledgeFromSeatBid(extraRespInfo.fledge, bidderName, brw.adapter, seatBid) @@ -936,6 +950,8 @@ func errorsToMetric(errs []error) map[metrics.AdapterError]struct{} { ret[metrics.AdapterErrorFailedToRequestBids] = s case errortypes.AlternateBidderCodeWarningCode: ret[metrics.AdapterErrorValidation] = s + case errortypes.TmaxTimeoutErrorCode: + ret[metrics.AdapterErrorTmaxTimeout] = s default: ret[metrics.AdapterErrorUnknown] = s } @@ -969,7 +985,7 @@ func errsToBidderWarnings(errs []error) []openrtb_ext.ExtBidderMessage { } // This piece takes all the bids supplied by the adapters and crafts an openRTB response to send back to the requester -func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ext.BidderName, adapterSeatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, bidRequest *openrtb2.BidRequest, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, bidResponseExt *openrtb_ext.ExtBidResponse, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, pubID string, errList []error) *openrtb2.BidResponse { +func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ext.BidderName, adapterSeatBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid, bidRequest *openrtb_ext.RequestWrapper, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, bidResponseExt *openrtb_ext.ExtBidResponse, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, pubID string, errList []error, seatNonBids *nonBids) *openrtb2.BidResponse { bidResponse := new(openrtb2.BidResponse) bidResponse.ID = bidRequest.ID @@ -984,7 +1000,7 @@ func (e *exchange) buildBidResponse(ctx context.Context, liveAdapters []openrtb_ for a, adapterSeatBids := range adapterSeatBids { //while processing every single bib, do we need to handle categories here? if adapterSeatBids != nil && len(adapterSeatBids.Bids) > 0 { - sb := e.makeSeatBid(adapterSeatBids, a, adapterExtra, auc, returnCreative, impExtInfoMap, bidResponseExt, pubID) + sb := e.makeSeatBid(adapterSeatBids, a, adapterExtra, auc, returnCreative, impExtInfoMap, bidRequest, bidResponseExt, pubID, seatNonBids) seatBids = append(seatBids, *sb) bidResponse.Cur = adapterSeatBids.Currency } @@ -1328,14 +1344,14 @@ func (e *exchange) makeExtBidResponse(adapterBids map[openrtb_ext.BidderName]*en // Return an openrtb seatBid for a bidder // buildBidResponse is responsible for ensuring nil bid seatbids are not included -func (e *exchange) makeSeatBid(adapterBid *entities.PbsOrtbSeatBid, adapter openrtb_ext.BidderName, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, bidResponseExt *openrtb_ext.ExtBidResponse, pubID string) *openrtb2.SeatBid { +func (e *exchange) makeSeatBid(adapterBid *entities.PbsOrtbSeatBid, adapter openrtb_ext.BidderName, adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, auc *auction, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, bidRequest *openrtb_ext.RequestWrapper, bidResponseExt *openrtb_ext.ExtBidResponse, pubID string, seatNonBids *nonBids) *openrtb2.SeatBid { seatBid := &openrtb2.SeatBid{ Seat: adapter.String(), Group: 0, // Prebid cannot support roadblocking } var errList []error - seatBid.Bid, errList = e.makeBid(adapterBid.Bids, auc, returnCreative, impExtInfoMap, bidResponseExt, adapter, pubID) + seatBid.Bid, errList = e.makeBid(adapterBid.Bids, auc, returnCreative, impExtInfoMap, bidRequest, bidResponseExt, adapter, pubID, seatNonBids) if len(errList) > 0 { adapterExtra[adapter].Errors = append(adapterExtra[adapter].Errors, errsToBidderErrors(errList)...) } @@ -1343,13 +1359,24 @@ func (e *exchange) makeSeatBid(adapterBid *entities.PbsOrtbSeatBid, adapter open return seatBid } -func (e *exchange) makeBid(bids []*entities.PbsOrtbBid, auc *auction, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, bidResponseExt *openrtb_ext.ExtBidResponse, adapter openrtb_ext.BidderName, pubID string) ([]openrtb2.Bid, []error) { +func (e *exchange) makeBid(bids []*entities.PbsOrtbBid, auc *auction, returnCreative bool, impExtInfoMap map[string]ImpExtInfo, bidRequest *openrtb_ext.RequestWrapper, bidResponseExt *openrtb_ext.ExtBidResponse, adapter openrtb_ext.BidderName, pubID string, seatNonBids *nonBids) ([]openrtb2.Bid, []error) { result := make([]openrtb2.Bid, 0, len(bids)) errs := make([]error, 0, 1) for _, bid := range bids { + if err := dsa.Validate(bidRequest, bid); err != nil { + dsaMessage := openrtb_ext.ExtBidderMessage{ + Code: errortypes.InvalidBidResponseDSAWarningCode, + Message: fmt.Sprintf("bid rejected: %s", err.Error()), + } + bidResponseExt.Warnings[adapter] = append(bidResponseExt.Warnings[adapter], dsaMessage) + + seatNonBids.addBid(bid, int(ResponseRejectedGeneral), adapter.String()) + continue // Don't add bid to result + } if e.bidValidationEnforcement.BannerCreativeMaxSize == config.ValidationEnforce && bid.BidType == openrtb_ext.BidTypeBanner { if !e.validateBannerCreativeSize(bid, bidResponseExt, adapter, pubID, e.bidValidationEnforcement.BannerCreativeMaxSize) { + seatNonBids.addBid(bid, int(ResponseRejectedCreativeSizeNotAllowed), adapter.String()) continue // Don't add bid to result } } else if e.bidValidationEnforcement.BannerCreativeMaxSize == config.ValidationWarn && bid.BidType == openrtb_ext.BidTypeBanner { @@ -1358,6 +1385,7 @@ func (e *exchange) makeBid(bids []*entities.PbsOrtbBid, auc *auction, returnCrea if _, ok := impExtInfoMap[bid.Bid.ImpID]; ok { if e.bidValidationEnforcement.SecureMarkup == config.ValidationEnforce && (bid.BidType == openrtb_ext.BidTypeBanner || bid.BidType == openrtb_ext.BidTypeVideo) { if !e.validateBidAdM(bid, bidResponseExt, adapter, pubID, e.bidValidationEnforcement.SecureMarkup) { + seatNonBids.addBid(bid, int(ResponseRejectedCreativeNotSecure), adapter.String()) continue // Don't add bid to result } } else if e.bidValidationEnforcement.SecureMarkup == config.ValidationWarn && (bid.BidType == openrtb_ext.BidTypeBanner || bid.BidType == openrtb_ext.BidTypeVideo) { @@ -1383,8 +1411,11 @@ func (e *exchange) makeBid(bids []*entities.PbsOrtbBid, auc *auction, returnCrea Bids: &cacheInfo, } } - - if bidExtJSON, err := makeBidExtJSON(bid.Bid.Ext, bidExtPrebid, impExtInfoMap, bid.Bid.ImpID, bid.OriginalBidCPM, bid.OriginalBidCur, bid.OriginalBidCPMUSD); err != nil { + adapterCode := adapter + if bid.AlternateBidderCode != "" { + adapterCode = openrtb_ext.BidderName(bid.AlternateBidderCode) + } + if bidExtJSON, err := makeBidExtJSON(bid.Bid.Ext, bidExtPrebid, impExtInfoMap, bid.Bid.ImpID, bid.OriginalBidCPM, bid.OriginalBidCur, bid.OriginalBidCPMUSD, adapterCode); err != nil { errs = append(errs, err) } else { result = append(result, *bid.Bid) @@ -1398,11 +1429,11 @@ func (e *exchange) makeBid(bids []*entities.PbsOrtbBid, auc *auction, returnCrea return result, errs } -func makeBidExtJSON(ext json.RawMessage, prebid *openrtb_ext.ExtBidPrebid, impExtInfoMap map[string]ImpExtInfo, impId string, originalBidCpm float64, originalBidCur string, originalBidCpmUSD float64) (json.RawMessage, error) { +func makeBidExtJSON(ext json.RawMessage, prebid *openrtb_ext.ExtBidPrebid, impExtInfoMap map[string]ImpExtInfo, impId string, originalBidCpm float64, originalBidCur string, originalBidCpmUSD float64, adapter openrtb_ext.BidderName) (json.RawMessage, error) { var extMap map[string]interface{} if len(ext) != 0 { - if err := json.Unmarshal(ext, &extMap); err != nil { + if err := jsonutil.Unmarshal(ext, &extMap); err != nil { return nil, err } } else { @@ -1431,11 +1462,16 @@ func makeBidExtJSON(ext json.RawMessage, prebid *openrtb_ext.ExtBidPrebid, impEx Meta openrtb_ext.ExtBidPrebidMeta `json:"meta"` } `json:"prebid"` }{} - if err := json.Unmarshal(ext, &metaContainer); err != nil { - return nil, fmt.Errorf("error validaing response from server, %s", err) + if err := jsonutil.Unmarshal(ext, &metaContainer); err != nil { + return nil, fmt.Errorf("error validating response from server, %s", err) } prebid.Meta = &metaContainer.Prebid.Meta } + if prebid.Meta == nil { + prebid.Meta = &openrtb_ext.ExtBidPrebidMeta{} + } + + prebid.Meta.AdapterCode = adapter.String() // ext.prebid.storedrequestattributes and ext.prebid.passthrough if impExtInfo, ok := impExtInfoMap[impId]; ok { @@ -1452,7 +1488,7 @@ func makeBidExtJSON(ext json.RawMessage, prebid *openrtb_ext.ExtBidPrebid, impEx } } extMap[openrtb_ext.PrebidExtKey] = prebid - return json.Marshal(extMap) + return jsonutil.Marshal(extMap) } // If bid got cached inside `(a *auction) doCache(ctx context.Context, cache prebid_cache_client.Client, targData *targetData, bidRequest *openrtb2.BidRequest, ttlBuffer int64, defaultTTLs *config.DefaultTTLs, bidCategory map[string]string)`, @@ -1468,34 +1504,6 @@ func (e *exchange) getBidCacheInfo(bid *entities.PbsOrtbBid, auction *auction) ( return } -func (e *exchange) getAuctionCurrencyRates(requestRates *openrtb_ext.ExtRequestCurrency) currency.Conversions { - if requestRates == nil { - // No bidRequest.ext.currency field was found, use PBS rates as usual - return e.currencyConverter.Rates() - } - - // If bidRequest.ext.currency.usepbsrates is nil, we understand its value as true. It will be false - // only if it's explicitly set to false - usePbsRates := requestRates.UsePBSRates == nil || *requestRates.UsePBSRates - - if !usePbsRates { - // At this point, we can safely assume the ConversionRates map is not empty because - // validateCustomRates(bidReqCurrencyRates *openrtb_ext.ExtRequestCurrency) would have - // thrown an error under such conditions. - return currency.NewRates(requestRates.ConversionRates) - } - - // Both PBS and custom rates can be used, check if ConversionRates is not empty - if len(requestRates.ConversionRates) == 0 { - // Custom rates map is empty, use PBS rates only - return e.currencyConverter.Rates() - } - - // Return an AggregateConversions object that includes both custom and PBS currency rates but will - // prioritize custom rates over PBS rates whenever a currency rate is found in both - return currency.NewAggregateConversions(currency.NewRates(requestRates.ConversionRates), e.currencyConverter.Rates()) -} - func findCacheID(bid *entities.PbsOrtbBid, auction *auction) (string, bool) { if bid != nil && bid.Bid != nil && auction != nil { if id, found := auction.cacheIds[bid.Bid]; found { @@ -1556,7 +1564,7 @@ func buildStoredAuctionResponse(storedAuctionResponses map[string]json.RawMessag for impId, storedResp := range storedAuctionResponses { var seatBids []openrtb2.SeatBid - if err := json.Unmarshal(storedResp, &seatBids); err != nil { + if err := jsonutil.UnmarshalValid(storedResp, &seatBids); err != nil { return nil, nil, nil, err } for _, seat := range seatBids { @@ -1575,7 +1583,7 @@ func buildStoredAuctionResponse(storedAuctionResponses map[string]json.RawMessag if seat.Ext != nil { var seatExt openrtb_ext.ExtBidResponse - if err := json.Unmarshal(seat.Ext, &seatExt); err != nil { + if err := jsonutil.Unmarshal(seat.Ext, &seatExt); err != nil { return nil, nil, nil, err } // add in FLEDGE response with impId substituted diff --git a/exchange/exchange_ow.go b/exchange/exchange_ow.go index e7a6503a0a6..d316b1b8de3 100644 --- a/exchange/exchange_ow.go +++ b/exchange/exchange_ow.go @@ -10,16 +10,16 @@ import ( "strings" "github.com/golang/glog" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/metrics" - pubmaticstats "github.com/prebid/prebid-server/metrics/pubmatic_stats" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/ortb" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/metrics" + pubmaticstats "github.com/prebid/prebid-server/v2/metrics/pubmatic_stats" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/ortb" + "github.com/prebid/prebid-server/v2/util/ptrutil" "golang.org/x/net/publicsuffix" ) diff --git a/exchange/exchange_ow_test.go b/exchange/exchange_ow_test.go index 3d2966ac852..3ec5637ba1b 100644 --- a/exchange/exchange_ow_test.go +++ b/exchange/exchange_ow_test.go @@ -10,15 +10,15 @@ import ( "strings" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/adapters/vastbidder" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/metrics" - metricsConf "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/adapters/vastbidder" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/metrics" + metricsConf "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -99,6 +99,7 @@ func TestApplyAdvertiserBlocking(t *testing.T) { Bid: openrtb_ext.NonBidObject{ ID: "reject_b.a.com.a.com.b.c.d.a.com", ADomain: []string{"b.a.com.a.com.b.c.d.a.com"}, + Meta: &openrtb_ext.ExtBidPrebidMeta{}, }, }, }, @@ -110,6 +111,7 @@ func TestApplyAdvertiserBlocking(t *testing.T) { Bid: openrtb_ext.NonBidObject{ ID: "a.com_bid", ADomain: []string{"a.com"}, + Meta: &openrtb_ext.ExtBidPrebidMeta{}, }, }, }, @@ -561,6 +563,7 @@ func TestApplyAdvertiserBlocking(t *testing.T) { seatNonBids := nonBids{} // applyAdvertiserBlocking internally uses tagBidders from (adapter_map.go) // not testing alias here + seatBids, rejections := applyAdvertiserBlocking(tt.args.advBlockReq, seatBids, &seatNonBids) re := regexp.MustCompile("bid rejected \\[bid ID:(.*?)\\] reason") for bidder, sBid := range seatBids { @@ -758,7 +761,7 @@ func TestMakeBidExtJSONOW(t *testing.T) { impExtInfo: map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}}, origbidcpm: 10.0000, origbidcur: "USD", - expectedBidExt: `{"prebid":{"meta": {"brandName": "foo"}, "passthrough":{"imp_passthrough_val":1}, "type":"video"}, "storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, + expectedBidExt: `{"prebid":{"meta": {"adaptercode": "adapter","brandName": "foo"}, "passthrough":{"imp_passthrough_val":1}, "type":"video"}, "storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -769,13 +772,14 @@ func TestMakeBidExtJSONOW(t *testing.T) { origbidcpm: 10.0000, origbidcur: "USD", origbidcpmusd: 10.0000, - expectedBidExt: `{"prebid":{"meta": {"brandName": "foo"}, "passthrough":{"imp_passthrough_val":1}, "type":"video"}, "storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD", "origbidcpmusd": 10}`, + expectedBidExt: `{"prebid":{"meta": {"adaptercode": "adapter", "brandName": "foo"}, "passthrough":{"imp_passthrough_val":1}, "type":"video"}, "storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD", "origbidcpmusd": 10}`, expectedErrMessage: "", }, } for _, test := range testCases { - result, err := makeBidExtJSON(test.ext, &test.extBidPrebid, test.impExtInfo, "test_imp_id", test.origbidcpm, test.origbidcur, test.origbidcpmusd) + var adapter openrtb_ext.BidderName = "adapter" + result, err := makeBidExtJSON(test.ext, &test.extBidPrebid, test.impExtInfo, "test_imp_id", test.origbidcpm, test.origbidcur, test.origbidcpmusd, adapter) if test.expectedErrMessage == "" { assert.JSONEq(t, test.expectedBidExt, string(result), "Incorrect result") @@ -1496,7 +1500,8 @@ func Test_updateSeatNonBidsFloors(t *testing.T) { Ext: openrtb_ext.NonBidExt{ Prebid: openrtb_ext.ExtResponseNonBidPrebid{ Bid: openrtb_ext.NonBidObject{ - ID: "bid1", + ID: "bid1", + Meta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: "pubmatic"}, }, }, }, @@ -1508,6 +1513,7 @@ func Test_updateSeatNonBidsFloors(t *testing.T) { Bid: openrtb_ext.NonBidObject{ ID: "bid2", DealID: "deal1", + Meta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: "pubmatic"}, }, }, }, @@ -1563,7 +1569,8 @@ func Test_updateSeatNonBidsFloors(t *testing.T) { Ext: openrtb_ext.NonBidExt{ Prebid: openrtb_ext.ExtResponseNonBidPrebid{ Bid: openrtb_ext.NonBidObject{ - ID: "bid1", + ID: "bid1", + Meta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: "pubmatic"}, }, }, }, @@ -1575,6 +1582,7 @@ func Test_updateSeatNonBidsFloors(t *testing.T) { Bid: openrtb_ext.NonBidObject{ ID: "bid2", DealID: "deal1", + Meta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: "pubmatic"}, }, }, }, @@ -1586,7 +1594,8 @@ func Test_updateSeatNonBidsFloors(t *testing.T) { Ext: openrtb_ext.NonBidExt{ Prebid: openrtb_ext.ExtResponseNonBidPrebid{ Bid: openrtb_ext.NonBidObject{ - ID: "bid1", + ID: "bid1", + Meta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: "appnexus"}, }, }, }, @@ -1598,6 +1607,7 @@ func Test_updateSeatNonBidsFloors(t *testing.T) { Bid: openrtb_ext.NonBidObject{ ID: "bid2", DealID: "deal1", + Meta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: "appnexus"}, }, }, }, diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 747735f5284..5449086936a 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -6,7 +6,6 @@ import ( "encoding/json" "errors" "fmt" - "io" "math" "net/http" "net/http/httptest" @@ -20,29 +19,29 @@ import ( "time" "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/experiment/adscert" - "github.com/prebid/prebid-server/floors" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/metrics" - metricsConf "github.com/prebid/prebid-server/metrics/config" - metricsConfig "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - pbc "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/file_fetcher" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/experiment/adscert" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/metrics" + metricsConf "github.com/prebid/prebid-server/v2/metrics/config" + metricsConfig "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + pbc "github.com/prebid/prebid-server/v2/prebid_cache_client" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests/backends/file_fetcher" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" jsonpatch "gopkg.in/evanphx/json-patch.v4" @@ -83,7 +82,7 @@ func TestNewExchange(t *testing.T) { }, }.Builder - e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), &floors.PriceFloorFetcher{}).(*exchange) + e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), nil).(*exchange) for _, bidderName := range knownAdapters { if _, ok := e.adapterMap[bidderName]; !ok { if biddersInfo[string(bidderName)].IsEnabled() { @@ -133,7 +132,7 @@ func TestCharacterEscape(t *testing.T) { }, }.Builder - e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), &floors.PriceFloorFetcher{}).(*exchange) + e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), nil).(*exchange) // 3) Build all the parameters e.buildBidResponse(ctx.Background(), liveA... ) needs //liveAdapters []openrtb_ext.BidderName, @@ -145,18 +144,20 @@ func TestCharacterEscape(t *testing.T) { adapterBids["appnexus"] = &entities.PbsOrtbSeatBid{Currency: "USD"} //An openrtb2.BidRequest struct as specified in https://github.com/prebid/prebid-server/issues/465 - bidRequest := &openrtb2.BidRequest{ - ID: "some-request-id", - Imp: []openrtb2.Imp{{ - ID: "some-impression-id", - Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}, {W: 300, H: 600}}}, - Ext: json.RawMessage(`{"appnexus": {"placementId": 1}}`), - }}, - Site: &openrtb2.Site{Page: "prebid.org", Ext: json.RawMessage(`{"amp":0}`)}, - Device: &openrtb2.Device{UA: "curl/7.54.0", IP: "::1"}, - AT: 1, - TMax: 500, - Ext: json.RawMessage(`{"id": "some-request-id","site": {"page": "prebid.org"},"imp": [{"id": "some-impression-id","banner": {"format": [{"w": 300,"h": 250},{"w": 300,"h": 600}]},"ext": {"appnexus": {"placementId": 1}}}],"tmax": 500}`), + bidRequest := &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "some-request-id", + Imp: []openrtb2.Imp{{ + ID: "some-impression-id", + Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}, {W: 300, H: 600}}}, + Ext: json.RawMessage(`{"appnexus": {"placementId": 1}}`), + }}, + Site: &openrtb2.Site{Page: "prebid.org", Ext: json.RawMessage(`{"amp":0}`)}, + Device: &openrtb2.Device{UA: "curl/7.54.0", IP: "::1"}, + AT: 1, + TMax: 500, + Ext: json.RawMessage(`{"id": "some-request-id","site": {"page": "prebid.org"},"imp": [{"id": "some-impression-id","banner": {"format": [{"w": 300,"h": 250},{"w": 300,"h": 600}]},"ext": {"appnexus": {"placementId": 1}}}],"tmax": 500}`), + }, } //adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra, @@ -169,7 +170,7 @@ func TestCharacterEscape(t *testing.T) { var errList []error // 4) Build bid response - bidResp := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, nil, nil, true, nil, "", errList) + bidResp := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, nil, nil, true, nil, "", errList, &nonBids{}) // 5) Assert we have no errors and one '&' character as we are supposed to if len(errList) > 0 { @@ -380,7 +381,7 @@ func TestDebugBehaviour(t *testing.T) { assert.NotNilf(t, outBidResponse.Ext, "%s. outBidResponse.Ext should not be nil \n", test.desc) assert.False(t, auctionRequest.BidderResponseStartTime.IsZero()) actualExt := &openrtb_ext.ExtBidResponse{} - err = json.Unmarshal(outBidResponse.Ext, actualExt) + err = jsonutil.UnmarshalValid(outBidResponse.Ext, actualExt) assert.NoErrorf(t, err, "%s. \"ext\" JSON field could not be unmarshaled. err: \"%v\" \n outBidResponse.Ext: \"%s\" \n", test.desc, err, outBidResponse.Ext) assert.NotEmpty(t, actualExt.Prebid, "%s. ext.prebid should not be empty") @@ -543,7 +544,7 @@ func TestTwoBiddersDebugDisabledAndEnabled(t *testing.T) { assert.False(t, auctionRequest.BidderResponseStartTime.IsZero()) actualExt := &openrtb_ext.ExtBidResponse{} - err = json.Unmarshal(outBidResponse.Ext, actualExt) + err = jsonutil.UnmarshalValid(outBidResponse.Ext, actualExt) assert.NoErrorf(t, err, "JSON field unmarshaling err. ") assert.NotEmpty(t, actualExt.Prebid, "ext.prebid should not be empty") @@ -574,9 +575,8 @@ func TestTwoBiddersDebugDisabledAndEnabled(t *testing.T) { } func TestOverrideWithCustomCurrency(t *testing.T) { - - mockCurrencyClient := &fakeCurrencyRatesHttpClient{ - responseBody: `{"dataAsOf":"2018-09-12","conversions":{"USD":{"MXN":10.00}}}`, + mockCurrencyClient := ¤cy.MockCurrencyRatesHttpClient{ + ResponseBody: `{"dataAsOf":"2018-09-12","conversions":{"USD":{"MXN":10.00}}}`, } mockCurrencyConverter := currency.NewRateConverter( mockCurrencyClient, @@ -664,7 +664,7 @@ func TestOverrideWithCustomCurrency(t *testing.T) { }.Builder e.currencyConverter = mockCurrencyConverter e.categoriesFetcher = categoriesFetcher - e.bidIDGenerator = &mockBidIDGenerator{false, false} + e.bidIDGenerator = &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false} e.requestSplitter = requestSplitter{ me: e.me, gdprPermsBuilder: e.gdprPermsBuilder, @@ -742,11 +742,11 @@ func TestOverrideWithCustomCurrency(t *testing.T) { } func TestAdapterCurrency(t *testing.T) { - fakeCurrencyClient := &fakeCurrencyRatesHttpClient{ - responseBody: `{"dataAsOf":"2018-09-12","conversions":{"USD":{"MXN":10.00}}}`, + mockCurrencyClient := ¤cy.MockCurrencyRatesHttpClient{ + ResponseBody: `{"dataAsOf":"2018-09-12","conversions":{"USD":{"MXN":10.00}}}`, } currencyConverter := currency.NewRateConverter( - fakeCurrencyClient, + mockCurrencyClient, "currency.fake.com", 24*time.Hour, ) @@ -769,7 +769,7 @@ func TestAdapterCurrency(t *testing.T) { }.Builder, currencyConverter: currencyConverter, categoriesFetcher: nilCategoryFetcher{}, - bidIDGenerator: &mockBidIDGenerator{false, false}, + bidIDGenerator: &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false}, adapterMap: map[openrtb_ext.BidderName]AdaptedBidder{ openrtb_ext.BidderName("appnexus"): AdaptBidder(mockBidder, nil, &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderName("appnexus"), nil, ""), }, @@ -818,13 +818,20 @@ func TestAdapterCurrency(t *testing.T) { } } -func TestFloorsSignalling(t *testing.T) { +type mockPriceFloorFetcher struct{} + +func (mpf *mockPriceFloorFetcher) Fetch(configs config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) { + return nil, openrtb_ext.FetchNone +} + +func (mpf *mockPriceFloorFetcher) Stop() {} - fakeCurrencyClient := &fakeCurrencyRatesHttpClient{ - responseBody: `{"dataAsOf":"2023-04-10","conversions":{"USD":{"MXN":10.00}}}`, +func TestFloorsSignalling(t *testing.T) { + mockCurrencyClient := ¤cy.MockCurrencyRatesHttpClient{ + ResponseBody: `{"dataAsOf":"2023-04-10","conversions":{"USD":{"MXN":10.00}}}`, } currencyConverter := currency.NewRateConverter( - fakeCurrencyClient, + mockCurrencyClient, "currency.com", 24*time.Hour, ) @@ -841,8 +848,9 @@ func TestFloorsSignalling(t *testing.T) { }.Builder, currencyConverter: currencyConverter, categoriesFetcher: nilCategoryFetcher{}, - bidIDGenerator: &mockBidIDGenerator{false, false}, - floor: config.PriceFloors{Enabled: true}, + bidIDGenerator: &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false}, + priceFloorEnabled: true, + priceFloorFetcher: &mockPriceFloorFetcher{}, } e.requestSplitter = requestSplitter{ me: e.me, @@ -961,7 +969,7 @@ func TestFloorsSignalling(t *testing.T) { expected: testResults{ bidFloor: 11.00, bidFloorCur: "USD", - resolvedReq: `{"id":"some-request-id","imp":[{"id":"some-impression-id","banner":{"format":[{"w":300,"h":250}]},"bidfloor":11,"bidfloorcur":"USD","ext":{"prebid":{"floors":{"floorrule":"banner|300x250|www.website.com","floorrulevalue":11,"floorvalue":11}}}}],"site":{"domain":"www.website.com","page":"prebid.org","ext":{"amp":0}},"test":1,"cur":["USD"],"ext":{"prebid":{"floors":{"floormin":1,"floormincur":"USD","data":{"currency":"USD","modelgroups":[{"modelversion":"model 1 from req","schema":{"fields":["mediaType","size","domain"],"delimiter":"|"},"values":{"*|*|*":20,"*|*|www.test.com":15,"banner|300x250|www.website.com":11},"default":50}]},"enabled":true,"skipped":false,"fetchstatus":"none","location":"request"}}}}`, + resolvedReq: `{"id":"some-request-id","imp":[{"id":"some-impression-id","banner":{"format":[{"w":300,"h":250}]},"bidfloor":11,"bidfloorcur":"USD","ext":{"prebid":{"floors":{"floorrule":"banner|300x250|www.website.com","floorrulevalue":11,"floorvalue":11}}}}],"site":{"domain":"www.website.com","page":"prebid.org","ext":{"amp":0}},"test":1,"cur":["USD"],"ext":{"prebid":{"floors":{"floormin":1,"floormincur":"USD","data":{"currency":"USD","modelgroups":[{"modelversion":"model 1 from req","schema":{"fields":["mediaType","size","domain"],"delimiter":"|"},"values":{"*|*|*":20,"*|*|www.test.com":15,"banner|300x250|www.website.com":11},"default":50}]},"enabled":true,"skipped":false,"location":"request"}}}}`, }, }, } @@ -987,208 +995,6 @@ func TestFloorsSignalling(t *testing.T) { } } -func TestGetAuctionCurrencyRates(t *testing.T) { - - pbsRates := map[string]map[string]float64{ - "MXN": { - "USD": 20.13, - "EUR": 27.82, - "JPY": 5.09, // "MXN" to "JPY" rate not found in customRates - }, - } - - customRates := map[string]map[string]float64{ - "MXN": { - "USD": 25.00, // different rate than in pbsRates - "EUR": 27.82, // same as in pbsRates - "GBP": 31.12, // not found in pbsRates at all - }, - } - - expectedRateEngineRates := map[string]map[string]float64{ - "MXN": { - "USD": 25.00, // rates engine will prioritize the value found in custom rates - "EUR": 27.82, // same value in both the engine reads the custom entry first - "JPY": 5.09, // the engine will find it in the pbsRates conversions - "GBP": 31.12, // the engine will find it in the custom conversions - }, - } - - boolTrue := true - boolFalse := false - - type testInput struct { - pbsRates map[string]map[string]float64 - bidExtCurrency *openrtb_ext.ExtRequestCurrency - } - type testOutput struct { - constantRates bool - resultingRates map[string]map[string]float64 - } - testCases := []struct { - desc string - given testInput - expected testOutput - }{ - { - "valid pbsRates, valid ConversionRates, false UsePBSRates. Resulting rates identical to customRates", - testInput{ - pbsRates: pbsRates, - bidExtCurrency: &openrtb_ext.ExtRequestCurrency{ - ConversionRates: customRates, - UsePBSRates: &boolFalse, - }, - }, - testOutput{ - resultingRates: customRates, - }, - }, - { - "valid pbsRates, valid ConversionRates, true UsePBSRates. Resulting rates are a mix but customRates gets priority", - testInput{ - pbsRates: pbsRates, - bidExtCurrency: &openrtb_ext.ExtRequestCurrency{ - ConversionRates: customRates, - UsePBSRates: &boolTrue, - }, - }, - testOutput{ - resultingRates: expectedRateEngineRates, - }, - }, - { - "nil pbsRates, valid ConversionRates, false UsePBSRates. Resulting rates identical to customRates", - testInput{ - pbsRates: nil, - bidExtCurrency: &openrtb_ext.ExtRequestCurrency{ - ConversionRates: customRates, - UsePBSRates: &boolFalse, - }, - }, - testOutput{ - resultingRates: customRates, - }, - }, - { - "nil pbsRates, valid ConversionRates, true UsePBSRates. Resulting rates identical to customRates", - testInput{ - pbsRates: nil, - bidExtCurrency: &openrtb_ext.ExtRequestCurrency{ - ConversionRates: customRates, - UsePBSRates: &boolTrue, - }, - }, - testOutput{ - resultingRates: customRates, - }, - }, - { - "valid pbsRates, empty ConversionRates, false UsePBSRates. Because pbsRates cannot be used, default to constant rates", - testInput{ - pbsRates: pbsRates, - bidExtCurrency: &openrtb_ext.ExtRequestCurrency{ - // ConversionRates inCustomRates not initialized makes for a zero-length map - UsePBSRates: &boolFalse, - }, - }, - testOutput{ - constantRates: true, - }, - }, - { - "valid pbsRates, nil ConversionRates, UsePBSRates defaults to true. Resulting rates will be identical to pbsRates", - testInput{ - pbsRates: pbsRates, - bidExtCurrency: nil, - }, - testOutput{ - resultingRates: pbsRates, - }, - }, - { - "nil pbsRates, empty ConversionRates, false UsePBSRates. Default to constant rates", - testInput{ - pbsRates: nil, - bidExtCurrency: &openrtb_ext.ExtRequestCurrency{ - // ConversionRates inCustomRates not initialized makes for a zero-length map - UsePBSRates: &boolFalse, - }, - }, - testOutput{ - constantRates: true, - }, - }, - { - "customRates empty, UsePBSRates set to true, pbsRates are nil. Return default constant rates converter", - testInput{ - pbsRates: nil, - bidExtCurrency: &openrtb_ext.ExtRequestCurrency{ - // ConversionRates inCustomRates not initialized makes for a zero-length map - UsePBSRates: &boolTrue, - }, - }, - testOutput{ - constantRates: true, - }, - }, - { - "nil customRates, nil pbsRates, UsePBSRates defaults to true. Return default constant rates converter", - testInput{ - pbsRates: nil, - bidExtCurrency: nil, - }, - testOutput{ - constantRates: true, - }, - }, - } - - for _, tc := range testCases { - - // Test setup: - jsonPbsRates, err := json.Marshal(tc.given.pbsRates) - if err != nil { - t.Fatalf("Failed to marshal PBS rates: %v", err) - } - - // Init mock currency conversion service - mockCurrencyClient := &fakeCurrencyRatesHttpClient{ - responseBody: `{"dataAsOf":"2018-09-12","conversions":` + string(jsonPbsRates) + `}`, - } - mockCurrencyConverter := currency.NewRateConverter( - mockCurrencyClient, - "currency.fake.com", - 24*time.Hour, - ) - mockCurrencyConverter.Run() - - e := new(exchange) - e.currencyConverter = mockCurrencyConverter - - // Run test - auctionRates := e.getAuctionCurrencyRates(tc.given.bidExtCurrency) - - // When fromCurrency and toCurrency are the same, a rate of 1.00 is always expected - rate, err := auctionRates.GetRate("USD", "USD") - assert.NoError(t, err, tc.desc) - assert.Equal(t, float64(1), rate, tc.desc) - - // If we expect an error, assert we have one along with a conversion rate of zero - if tc.expected.constantRates { - rate, err := auctionRates.GetRate("USD", "MXN") - assert.Error(t, err, tc.desc) - assert.Equal(t, float64(0), rate, tc.desc) - } else { - for fromCurrency, rates := range tc.expected.resultingRates { - for toCurrency, expectedRate := range rates { - actualRate, err := auctionRates.GetRate(fromCurrency, toCurrency) - assert.NoError(t, err, tc.desc) - assert.Equal(t, expectedRate, actualRate, tc.desc) - } - } - } - } -} func TestReturnCreativeEndToEnd(t *testing.T) { sampleAd := "" @@ -1325,7 +1131,7 @@ func TestReturnCreativeEndToEnd(t *testing.T) { }.Builder e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) e.categoriesFetcher = categoriesFetcher - e.bidIDGenerator = &mockBidIDGenerator{false, false} + e.bidIDGenerator = &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false} e.requestSplitter = requestSplitter{ me: e.me, gdprPermsBuilder: e.gdprPermsBuilder, @@ -1431,7 +1237,7 @@ func TestGetBidCacheInfoEndToEnd(t *testing.T) { }, }.Builder - e := NewExchange(adapters, pbc, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), &floors.PriceFloorFetcher{}).(*exchange) + e := NewExchange(adapters, pbc, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), nil).(*exchange) // 3) Build all the parameters e.buildBidResponse(ctx.Background(), liveA... ) needs liveAdapters := []openrtb_ext.BidderName{bidderName} @@ -1502,40 +1308,42 @@ func TestGetBidCacheInfoEndToEnd(t *testing.T) { }, }, } - bidRequest := &openrtb2.BidRequest{ - ID: "some-request-id", - TMax: 1000, - Imp: []openrtb2.Imp{ - { - ID: "test-div", - Secure: openrtb2.Int8Ptr(0), - Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}}}, - Ext: json.RawMessage(` { - "rubicon": { - "accountId": 1001, - "siteId": 113932, - "zoneId": 535510 - }, - "appnexus": { "placementId": 1 }, - "pubmatic": { "publisherId": "156209", "adSlot": "pubmatic_test2@300x250" }, - "pulsepoint": { "cf": "300X250", "cp": 512379, "ct": 486653 }, - "conversant": { "site_id": "108060" }, - "ix": { "siteId": "287415" } -}`), + bidRequest := &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "some-request-id", + TMax: 1000, + Imp: []openrtb2.Imp{ + { + ID: "test-div", + Secure: openrtb2.Int8Ptr(0), + Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}}}, + Ext: json.RawMessage(` { + "rubicon": { + "accountId": 1001, + "siteId": 113932, + "zoneId": 535510 + }, + "appnexus": { "placementId": 1 }, + "pubmatic": { "publisherId": "156209", "adSlot": "pubmatic_test2@300x250" }, + "pulsepoint": { "cf": "300X250", "cp": 512379, "ct": 486653 }, + "conversant": { "site_id": "108060" }, + "ix": { "siteId": "287415" } + }`), + }, }, + Site: &openrtb2.Site{ + Page: "http://rubitest.com/index.html", + Publisher: &openrtb2.Publisher{ID: "1001"}, + }, + Test: 1, + Ext: json.RawMessage(`{"prebid": { "cache": { "bids": {}, "vastxml": {} }, "targeting": { "pricegranularity": "med", "includewinners": true, "includebidderkeys": false } }}`), }, - Site: &openrtb2.Site{ - Page: "http://rubitest.com/index.html", - Publisher: &openrtb2.Publisher{ID: "1001"}, - }, - Test: 1, - Ext: json.RawMessage(`{"prebid": { "cache": { "bids": {}, "vastxml": {} }, "targeting": { "pricegranularity": "med", "includewinners": true, "includebidderkeys": false } }}`), } var errList []error // 4) Build bid response - bid_resp := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, auc, nil, true, nil, "", errList) + bid_resp := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, adapterExtra, auc, nil, true, nil, "", errList, &nonBids{}) expectedBidResponse := &openrtb2.BidResponse{ SeatBid: []openrtb2.SeatBid{ @@ -1625,13 +1433,13 @@ func TestBidReturnsCreative(t *testing.T) { //Run tests for _, test := range testCases { - resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, test.inReturnCreative, nil, nil, "", "") + resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, test.inReturnCreative, nil, &openrtb_ext.RequestWrapper{}, nil, "", "", &nonBids{}) assert.Equal(t, 0, len(resultingErrs), "%s. Test should not return errors \n", test.description) assert.Equal(t, test.expectedCreativeMarkup, resultingBids[0].AdM, "%s. Ad markup string doesn't match expected \n", test.description) var bidExt openrtb_ext.ExtBid - json.Unmarshal(resultingBids[0].Ext, &bidExt) + jsonutil.UnmarshalValid(resultingBids[0].Ext, &bidExt) assert.Equal(t, 0, bidExt.Prebid.DealPriority, "%s. Test should have DealPriority set to 0", test.description) assert.Equal(t, false, bidExt.Prebid.DealTierSatisfied, "%s. Test should have DealTierSatisfied set to false", test.description) } @@ -1788,23 +1596,25 @@ func TestBidResponseCurrency(t *testing.T) { }, }.Builder - e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), &floors.PriceFloorFetcher{}).(*exchange) + e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), nil).(*exchange) liveAdapters := make([]openrtb_ext.BidderName, 1) liveAdapters[0] = "appnexus" - bidRequest := &openrtb2.BidRequest{ - ID: "some-request-id", - Imp: []openrtb2.Imp{{ - ID: "some-impression-id", - Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}, {W: 300, H: 600}}}, - Ext: json.RawMessage(`{"appnexus": {"placementId": 10433394}}`), - }}, - Site: &openrtb2.Site{Page: "prebid.org", Ext: json.RawMessage(`{"amp":0}`)}, - Device: &openrtb2.Device{UA: "curl/7.54.0", IP: "::1"}, - AT: 1, - TMax: 500, - Ext: json.RawMessage(`{"id": "some-request-id","site": {"page": "prebid.org"},"imp": [{"id": "some-impression-id","banner": {"format": [{"w": 300,"h": 250},{"w": 300,"h": 600}]},"ext": {"appnexus": {"placementId": 10433394}}}],"tmax": 500}`), + bidRequest := &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "some-request-id", + Imp: []openrtb2.Imp{{ + ID: "some-impression-id", + Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}, {W: 300, H: 600}}}, + Ext: json.RawMessage(`{"appnexus": {"placementId": 10433394}}`), + }}, + Site: &openrtb2.Site{Page: "prebid.org", Ext: json.RawMessage(`{"amp":0}`)}, + Device: &openrtb2.Device{UA: "curl/7.54.0", IP: "::1"}, + AT: 1, + TMax: 500, + Ext: json.RawMessage(`{"id": "some-request-id","site": {"page": "prebid.org"},"imp": [{"id": "some-impression-id","banner": {"format": [{"w": 300,"h": 250},{"w": 300,"h": 600}]},"ext": {"appnexus": {"placementId": 10433394}}}],"tmax": 500}`), + }, } adapterExtra := map[openrtb_ext.BidderName]*seatResponseExtra{ @@ -1830,7 +1640,7 @@ func TestBidResponseCurrency(t *testing.T) { Price: 9.517803, W: 300, H: 250, - Ext: json.RawMessage(`{"origbidcpm":9.517803,"prebid":{"type":"banner"}}`), + Ext: json.RawMessage(`{"origbidcpm":9.517803,"prebid":{"meta":{"adaptercode":"appnexus"},"type":"banner"}}`), }, }, }, @@ -1908,7 +1718,7 @@ func TestBidResponseCurrency(t *testing.T) { } // Run tests for i := range testCases { - actualBidResp := e.buildBidResponse(context.Background(), liveAdapters, testCases[i].adapterBids, bidRequest, adapterExtra, nil, bidResponseExt, true, nil, "", errList) + actualBidResp := e.buildBidResponse(context.Background(), liveAdapters, testCases[i].adapterBids, bidRequest, adapterExtra, nil, bidResponseExt, true, nil, "", errList, &nonBids{}) assert.Equalf(t, testCases[i].expectedBidResponse, actualBidResp, fmt.Sprintf("[TEST_FAILED] Objects must be equal for test: %s \n Expected: >>%s<< \n Actual: >>%s<< ", testCases[i].description, testCases[i].expectedBidResponse.Ext, actualBidResp.Ext)) } } @@ -1934,19 +1744,21 @@ func TestBidResponseImpExtInfo(t *testing.T) { t.Fatalf("Error intializing adapters: %v", adaptersErr) } - e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, nil, gdprPermsBuilder, nil, nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), &floors.PriceFloorFetcher{}).(*exchange) + e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, nil, gdprPermsBuilder, nil, nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), nil).(*exchange) liveAdapters := make([]openrtb_ext.BidderName, 1) liveAdapters[0] = "appnexus" - bidRequest := &openrtb2.BidRequest{ - ID: "some-request-id", - Imp: []openrtb2.Imp{{ - ID: "some-impression-id", - Video: &openrtb2.Video{}, - Ext: json.RawMessage(`{"appnexus": {"placementId": 10433394}}`), - }}, - Ext: json.RawMessage(``), + bidRequest := &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + ID: "some-request-id", + Imp: []openrtb2.Imp{{ + ID: "some-impression-id", + Video: &openrtb2.Video{}, + Ext: json.RawMessage(`{"appnexus": {"placementId": 10433394}}`), + }}, + Ext: json.RawMessage(``), + }, } var errList []error @@ -1970,11 +1782,10 @@ func TestBidResponseImpExtInfo(t *testing.T) { impExtInfo["some-impression-id"] = ImpExtInfo{ true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), - json.RawMessage(`{"imp_passthrough_val": 1}`)} + json.RawMessage(`{"imp_passthrough_val":1}`)} + expectedBidResponseExt := `{"origbidcpm":0,"prebid":{"meta":{"adaptercode":"appnexus"},"type":"video","passthrough":{"imp_passthrough_val":1}},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]}}` - expectedBidResponseExt := `{"origbidcpm":0,"prebid":{"type":"video","passthrough":{"imp_passthrough_val":1}},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]}}` - - actualBidResp := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, nil, nil, nil, true, impExtInfo, "", errList) + actualBidResp := e.buildBidResponse(context.Background(), liveAdapters, adapterBids, bidRequest, nil, nil, nil, true, impExtInfo, "", errList, &nonBids{}) resBidExt := string(actualBidResp.SeatBid[0].Bid[0].Ext) assert.Equalf(t, expectedBidResponseExt, resBidExt, "Expected bid response extension is incorrect") @@ -2026,7 +1837,7 @@ func TestRaceIntegration(t *testing.T) { }, }.Builder - ex := NewExchange(adapters, &wellBehavedCache{}, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, &nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), &floors.PriceFloorFetcher{}).(*exchange) + ex := NewExchange(adapters, &wellBehavedCache{}, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, &nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), nil).(*exchange) _, err = ex.HoldAuction(context.Background(), auctionRequest, &debugLog) if err != nil { t.Errorf("HoldAuction returned unexpected error: %v", err) @@ -2091,8 +1902,8 @@ func getTestBuildRequest(t *testing.T) *openrtb2.BidRequest { MIMEs: []string{"video/mp4"}, MinDuration: 1, MaxDuration: 300, - W: 300, - H: 600, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](600), }, Ext: json.RawMessage(`{"prebid":{"bidder":{"appnexus": {"placementId": 1}}}}`), }}, @@ -2124,7 +1935,7 @@ func TestPanicRecovery(t *testing.T) { }, }.Builder - e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), &floors.PriceFloorFetcher{}).(*exchange) + e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, nilCategoryFetcher{}, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), nil).(*exchange) chBids := make(chan *bidResponseWrapper, 1) panicker := func(bidderRequest BidderRequest, conversions currency.Conversions) { @@ -2194,7 +2005,7 @@ func TestPanicRecoveryHighLevel(t *testing.T) { allowAllBidders: true, }, }.Builder - e := NewExchange(adapters, &mockCache{}, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, categoriesFetcher, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), &floors.PriceFloorFetcher{}).(*exchange) + e := NewExchange(adapters, &mockCache{}, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, categoriesFetcher, &adscert.NilSigner{}, macros.NewStringIndexBasedReplacer(), nil).(*exchange) e.adapterMap[openrtb_ext.BidderBeachfront] = panicingAdapter{} e.adapterMap[openrtb_ext.BidderAppnexus] = panicingAdapter{} @@ -2264,8 +2075,13 @@ func TestTimeoutComputation(t *testing.T) { func TestExchangeJSON(t *testing.T) { if specFiles, err := os.ReadDir("./exchangetest"); err == nil { for _, specFile := range specFiles { + if !strings.HasSuffix(specFile.Name(), ".json") { + continue + } + fileName := "./exchangetest/" + specFile.Name() fileDisplayName := "exchange/exchangetest/" + specFile.Name() + t.Run(fileDisplayName, func(t *testing.T) { specData, err := loadFile(fileName) if assert.NoError(t, err, "Failed to load contents of file %s: %v", fileDisplayName, err) { @@ -2284,7 +2100,7 @@ func loadFile(filename string) (*exchangeSpec, error) { } var spec exchangeSpec - if err := json.Unmarshal(specData, &spec); err != nil { + if err := jsonutil.UnmarshalValid(specData, &spec); err != nil { return nil, fmt.Errorf("Failed to unmarshal JSON from file: %v", err) } @@ -2326,9 +2142,9 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { }, }, } - bidIdGenerator := &mockBidIDGenerator{} + bidIdGenerator := &fakeBidIDGenerator{} if spec.BidIDGenerator != nil { - *bidIdGenerator = *spec.BidIDGenerator + bidIdGenerator = spec.BidIDGenerator } ex := newExchangeForTests(t, filename, spec.OutgoingRequests, aliases, privacyConfig, bidIdGenerator, spec.HostSChainFlag, spec.FloorsEnabled, spec.HostConfigBidValidation, spec.Server) biddersInAuction := findBiddersInAuction(t, filename, &spec.IncomingRequest.OrtbRequest) @@ -2364,7 +2180,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { Account: config.Account{ ID: "testaccount", Events: config.Events{ - Enabled: &spec.EventsEnabled, + Enabled: spec.EventsEnabled, }, DebugAllow: true, PriceFloors: config.AccountPriceFloors{Enabled: spec.AccountFloorsEnabled}, @@ -2381,7 +2197,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { auctionRequest.Account.DefaultBidLimit = spec.MultiBid.AccountMaxBid requestExt := &openrtb_ext.ExtRequest{} - err := json.Unmarshal(spec.IncomingRequest.OrtbRequest.Ext, requestExt) + err := jsonutil.UnmarshalValid(spec.IncomingRequest.OrtbRequest.Ext, requestExt) assert.NoError(t, err, "invalid request ext") validatedMultiBids, errs := openrtb_ext.ValidateAndBuildExtMultiBid(&requestExt.Prebid) for _, err := range errs { // same as in validateRequestExt(). @@ -2392,7 +2208,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { } requestExt.Prebid.MultiBid = validatedMultiBids - updateReqExt, err := json.Marshal(requestExt) + updateReqExt, err := jsonutil.Marshal(requestExt) assert.NoError(t, err, "invalid request ext") auctionRequest.BidRequestWrapper.Ext = updateReqExt } @@ -2407,8 +2223,10 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { aucResponse, err := ex.HoldAuction(ctx, auctionRequest, debugLog) var bid *openrtb2.BidResponse + var bidExt *openrtb_ext.ExtBidResponse if aucResponse != nil { bid = aucResponse.BidResponse + bidExt = aucResponse.ExtBidResponse } if len(spec.Response.Error) > 0 && spec.Response.Bids == nil { if err.Error() != spec.Response.Error { @@ -2455,7 +2273,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { actualPassthrough := "" actualBidRespExt := &openrtb_ext.ExtBidResponse{} if bid.Ext != nil { - if err := json.Unmarshal(bid.Ext, actualBidRespExt); err != nil { + if err := jsonutil.UnmarshalValid(bid.Ext, actualBidRespExt); err != nil { assert.NoError(t, err, fmt.Sprintf("Error when unmarshalling: %s", err)) } if actualBidRespExt.Prebid != nil { @@ -2464,7 +2282,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { } expectedBidRespExt := &openrtb_ext.ExtBidResponse{} if spec.Response.Ext != nil { - if err := json.Unmarshal(spec.Response.Ext, expectedBidRespExt); err != nil { + if err := jsonutil.UnmarshalValid(spec.Response.Ext, expectedBidRespExt); err != nil { assert.NoError(t, err, fmt.Sprintf("Error when unmarshalling: %s", err)) } if expectedBidRespExt.Prebid != nil { @@ -2491,20 +2309,24 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) { assert.JSONEq(t, string(spec.Response.Ext), string(bid.Ext), "ext mismatch") } + expectedBidRespExt := &openrtb_ext.ExtBidResponse{} + if spec.Response.Ext != nil { + if err := jsonutil.UnmarshalValid(spec.Response.Ext, expectedBidRespExt); err != nil { + assert.NoError(t, err, fmt.Sprintf("Error when unmarshalling: %s", err)) + } + } if spec.HostConfigBidValidation.BannerCreativeMaxSize == config.ValidationEnforce || spec.HostConfigBidValidation.SecureMarkup == config.ValidationEnforce { actualBidRespExt := &openrtb_ext.ExtBidResponse{} - expectedBidRespExt := &openrtb_ext.ExtBidResponse{} if bid.Ext != nil { - if err := json.Unmarshal(bid.Ext, actualBidRespExt); err != nil { + if err := jsonutil.UnmarshalValid(bid.Ext, actualBidRespExt); err != nil { assert.NoError(t, err, fmt.Sprintf("Error when unmarshalling: %s", err)) } } - if err := json.Unmarshal(spec.Response.Ext, expectedBidRespExt); err != nil { - assert.NoError(t, err, fmt.Sprintf("Error when unmarshalling: %s", err)) - } - assert.Equal(t, expectedBidRespExt.Errors, actualBidRespExt.Errors, "Expected errors from response ext do not match") } + if expectedBidRespExt.Prebid != nil { + assert.ElementsMatch(t, expectedBidRespExt.Prebid.SeatNonBid, bidExt.Prebid.SeatNonBid, "Expected seatNonBids from response ext do not match") + } } func findBiddersInAuction(t *testing.T, context string, req *openrtb2.BidRequest) []string { @@ -2529,7 +2351,7 @@ func extractResponseTimes(t *testing.T, context string, bid *openrtb2.BidRespons return nil } else { responseTimes := make(map[string]int) - if err := json.Unmarshal(data, &responseTimes); err != nil { + if err := jsonutil.UnmarshalValid(data, &responseTimes); err != nil { t.Errorf("%s: Failed to unmarshal ext.responsetimemillis into map[string]int: %v", context, err) return nil } @@ -2633,35 +2455,40 @@ func newExchangeForTests(t *testing.T, filename string, expectations map[string] server: config.Server{ExternalUrl: server.ExternalUrl, GvlID: server.GvlID, DataCenter: server.DataCenter}, bidValidationEnforcement: hostBidValidation, requestSplitter: requestSplitter, - floor: config.PriceFloors{Enabled: floorsFlag}, + priceFloorEnabled: floorsFlag, + priceFloorFetcher: &mockPriceFloorFetcher{}, } } -type mockBidIDGenerator struct { +type fakeBidIDGenerator struct { GenerateBidID bool `json:"generateBidID"` ReturnError bool `json:"returnError"` + bidCount map[string]int } -func (big *mockBidIDGenerator) Enabled() bool { - return big.GenerateBidID +func (f *fakeBidIDGenerator) Enabled() bool { + return f.GenerateBidID } -func (big *mockBidIDGenerator) New() (string, error) { +func (f *fakeBidIDGenerator) New(bidder string) (string, error) { + if f.ReturnError { + return "", errors.New("Test error generating bid.ext.prebid.bidid") + } - if big.ReturnError { - err := errors.New("Test error generating bid.ext.prebid.bidid") - return "", err + if f.bidCount == nil { + f.bidCount = make(map[string]int) } - return "mock_uuid", nil + f.bidCount[bidder] += 1 + return fmt.Sprintf("bid-%v-%v", bidder, f.bidCount[bidder]), nil } -type fakeRandomDeduplicateBidBooleanGenerator struct { - returnValue bool +type fakeBooleanGenerator struct { + value bool } -func (m *fakeRandomDeduplicateBidBooleanGenerator) Generate() bool { - return m.returnValue +func (f *fakeBooleanGenerator) Generate() bool { + return f.value } func newExtRequest() openrtb_ext.ExtRequest { @@ -2751,10 +2578,10 @@ func TestCategoryMapping(t *testing.T) { bid3 := openrtb2.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1} bid4 := openrtb2.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 40.0000, Cat: cats4, W: 1, H: 1} - bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} - bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, nil, nil, 0, false, "", 20.0000, "USD", "", 20.0000} - bid1_3 := entities.PbsOrtbBid{&bid3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, nil, nil, 0, false, "", 30.0000, "USD", "", 30.0000} - bid1_4 := entities.PbsOrtbBid{&bid4, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 40.0000, "USD", "", 40.0000} + bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: "", OriginalBidCPMUSD: 10.0000} + bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: "", OriginalBidCPMUSD: 20.0000} + bid1_3 := entities.PbsOrtbBid{Bid: &bid3, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 30.0000, OriginalBidCur: "USD", TargetBidderCode: "", OriginalBidCPMUSD: 30.0000} + bid1_4 := entities.PbsOrtbBid{Bid: &bid4, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 40.0000, OriginalBidCur: "USD", TargetBidderCode: "", OriginalBidCPMUSD: 40.0000} innerBids := []*entities.PbsOrtbBid{ &bid1_1, @@ -2789,6 +2616,7 @@ func TestCategoryMapping(t *testing.T) { Video: &openrtb_ext.ExtBidPrebidVideo{ Duration: 30, }, + Meta: &openrtb_ext.ExtBidPrebidMeta{}, }, }, }, @@ -2841,10 +2669,10 @@ func TestCategoryMappingNoIncludeBrandCategory(t *testing.T) { bid3 := openrtb2.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1} bid4 := openrtb2.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 40.0000, Cat: cats4, W: 1, H: 1} - bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} - bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, nil, nil, 0, false, "", 20.0000, "USD", "", 20.0000} - bid1_3 := entities.PbsOrtbBid{&bid3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, nil, nil, 0, false, "", 30.0000, "USD", "", 30.0000} - bid1_4 := entities.PbsOrtbBid{&bid4, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, nil, nil, 0, false, "", 40.0000, "USD", "", 40.0000} + bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_3 := entities.PbsOrtbBid{Bid: &bid3, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30, PrimaryCategory: "AdapterOverride"}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 30.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_4 := entities.PbsOrtbBid{Bid: &bid4, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 40.0000, OriginalBidCur: "USD", TargetBidderCode: ""} innerBids := []*entities.PbsOrtbBid{ &bid1_1, @@ -2852,7 +2680,6 @@ func TestCategoryMappingNoIncludeBrandCategory(t *testing.T) { &bid1_3, &bid1_4, } - seatBid := entities.PbsOrtbSeatBid{Bids: innerBids, Currency: "USD"} bidderName1 := openrtb_ext.BidderName("appnexus") @@ -2903,9 +2730,9 @@ func TestCategoryMappingTranslateCategoriesNil(t *testing.T) { bid2 := openrtb2.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 20.0000, Cat: cats2, W: 1, H: 1} bid3 := openrtb2.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1} - bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} - bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, nil, nil, 0, false, "", 20.0000, "USD", "", 20.0000} - bid1_3 := entities.PbsOrtbBid{&bid3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 30.0000, "USD", "", 30.0000} + bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: "", OriginalBidCPMUSD: 10.0000} + bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: "", OriginalBidCPMUSD: 20.0000} + bid1_3 := entities.PbsOrtbBid{Bid: &bid3, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 30.0000, OriginalBidCur: "USD", TargetBidderCode: "", OriginalBidCPMUSD: 30.0000} innerBids := []*entities.PbsOrtbBid{ &bid1_1, @@ -2940,6 +2767,7 @@ func TestCategoryMappingTranslateCategoriesNil(t *testing.T) { Video: &openrtb_ext.ExtBidPrebidVideo{ Duration: 30, }, + Meta: &openrtb_ext.ExtBidPrebidMeta{}, }, }, }, @@ -3023,16 +2851,14 @@ func TestCategoryMappingTranslateCategoriesFalse(t *testing.T) { bid2 := openrtb2.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 20.0000, Cat: cats2, W: 1, H: 1} bid3 := openrtb2.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 30.0000, Cat: cats3, W: 1, H: 1} - bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} - bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, nil, nil, 0, false, "", 20.0000, "USD", "", 20.0000} - bid1_3 := entities.PbsOrtbBid{&bid3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 30.0000, "USD", "", 30.0000} - + bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 40}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_3 := entities.PbsOrtbBid{Bid: &bid3, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 30.0000, OriginalBidCur: "USD", TargetBidderCode: ""} innerBids := []*entities.PbsOrtbBid{ &bid1_1, &bid1_2, &bid1_3, } - seatBid := entities.PbsOrtbSeatBid{Bids: innerBids, Currency: "USD"} bidderName1 := openrtb_ext.BidderName("appnexus") @@ -3049,92 +2875,88 @@ func TestCategoryMappingTranslateCategoriesFalse(t *testing.T) { assert.Equal(t, 3, len(adapterBids[bidderName1].Bids), "Bidders number doesn't match") assert.Equal(t, 3, len(bidCategory), "Bidders category mapping doesn't match") assert.Equal(t, expectedseatNonBids, seatNonbids, "SeatNonBids not matching") - } func TestCategoryDedupe(t *testing.T) { - categoriesFetcher, error := newCategoryFetcher("./test/category-mapping") if error != nil { t.Errorf("Failed to create a category Fetcher: %v", error) } - r := &AuctionRequest{ BidRequestWrapper: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{}, }, } requestExt := newExtRequest() - targData := &targetData{ priceGranularity: *requestExt.Prebid.Targeting.PriceGranularity, includeWinners: true, } - adapterBids := make(map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid) + // bid3 and bid5 will be same price, category, and duration so one of them should be removed based on the dedupe generator + bid1 := openrtb2.Bid{ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: []string{"IAB1-3"}, W: 1, H: 1} + bid2 := openrtb2.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 15.0000, Cat: []string{"IAB1-4"}, W: 1, H: 1} + bid3 := openrtb2.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 20.0000, Cat: []string{"IAB1-3"}, W: 1, H: 1} + bid4 := openrtb2.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 20.0000, Cat: []string{"IAB1-INVALID"}, W: 1, H: 1} + bid5 := openrtb2.Bid{ID: "bid_id5", ImpID: "imp_id5", Price: 20.0000, Cat: []string{"IAB1-3"}, W: 1, H: 1} - cats1 := []string{"IAB1-3"} - cats2 := []string{"IAB1-4"} - // bid3 will be same price, category, and duration as bid1 so one of them should get removed - cats4 := []string{"IAB1-2000"} - bid1 := openrtb2.Bid{ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: cats1, W: 1, H: 1} - bid2 := openrtb2.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 15.0000, Cat: cats2, W: 1, H: 1} - bid3 := openrtb2.Bid{ID: "bid_id3", ImpID: "imp_id3", Price: 20.0000, Cat: cats1, W: 1, H: 1} - bid4 := openrtb2.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 20.0000, Cat: cats4, W: 1, H: 1} - bid5 := openrtb2.Bid{ID: "bid_id5", ImpID: "imp_id5", Price: 20.0000, Cat: cats1, W: 1, H: 1} + bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidType: "video", BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, OriginalBidCPM: 10.0000, OriginalBidCur: "USD"} + bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidType: "video", BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, OriginalBidCPM: 15.0000, OriginalBidCur: "USD"} + bid1_3 := entities.PbsOrtbBid{Bid: &bid3, BidType: "video", BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, OriginalBidCPM: 20.0000, OriginalBidCur: "USD"} + bid1_4 := entities.PbsOrtbBid{Bid: &bid4, BidType: "video", BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, OriginalBidCPM: 20.0000, OriginalBidCur: "USD"} + bid1_5 := entities.PbsOrtbBid{Bid: &bid5, BidType: "video", BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, OriginalBidCPM: 20.0000, OriginalBidCur: "USD"} - bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} - bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 50}, nil, nil, 0, false, "", 15.0000, "USD", "", 15.0000} - bid1_3 := entities.PbsOrtbBid{&bid3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", "", 20.0000} - bid1_4 := entities.PbsOrtbBid{&bid4, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", "", 20.0000} - bid1_5 := entities.PbsOrtbBid{&bid5, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", "", 20.0000} + bidderName1 := openrtb_ext.BidderName("appnexus") - selectedBids := make(map[string]int) - expectedCategories := map[string]string{ - "bid_id1": "10.00_Electronics_30s", - "bid_id2": "14.00_Sports_50s", - "bid_id3": "20.00_Electronics_30s", - "bid_id5": "20.00_Electronics_30s", + tests := []struct { + name string + dedupeGeneratorValue bool + expectedBids []*entities.PbsOrtbBid + expectedCategories map[string]string + }{ + { + name: "bid_id5_selected_over_bid_id3", + dedupeGeneratorValue: true, + expectedBids: []*entities.PbsOrtbBid{&bid1_2, &bid1_5}, + expectedCategories: map[string]string{ + "bid_id2": "14.00_Sports_50s", + "bid_id5": "20.00_Electronics_30s", + }, + }, + { + name: "bid_id3_selected_over_bid_id5", + dedupeGeneratorValue: false, + expectedBids: []*entities.PbsOrtbBid{&bid1_2, &bid1_3}, + expectedCategories: map[string]string{ + "bid_id2": "14.00_Sports_50s", + "bid_id3": "20.00_Electronics_30s", + }, + }, } - numIterations := 10 - - // Run the function many times, this should be enough for the 50% chance of which bid to remove to remove bid1 sometimes - // and bid3 others. It's conceivably possible (but highly unlikely) that the same bid get chosen every single time, but - // if you notice false fails from this test increase numIterations to make it even less likely to happen. - for i := 0; i < numIterations; i++ { - innerBids := []*entities.PbsOrtbBid{ - &bid1_1, - &bid1_2, - &bid1_3, - &bid1_4, - &bid1_5, - } - - seatBid := entities.PbsOrtbSeatBid{Bids: innerBids, Currency: "USD"} - bidderName1 := openrtb_ext.BidderName("appnexus") - - adapterBids[bidderName1] = &seatBid - - bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, r, *requestExt.Prebid.Targeting, adapterBids, categoriesFetcher, targData, &randomDeduplicateBidBooleanGenerator{}, &nonBids{}) - - assert.Equal(t, nil, err, "Category mapping error should be empty") - assert.Equal(t, 3, len(rejections), "There should be 2 bid rejection messages") - assert.Regexpf(t, regexp.MustCompile(`bid rejected \[bid ID: bid_id(1|3)\] reason: Bid was deduplicated`), rejections[0], "Rejection message did not match expected") - assert.Equal(t, "bid rejected [bid ID: bid_id4] reason: Category mapping file for primary ad server: 'freewheel', publisher: '' not found", rejections[1], "Rejection message did not match expected") - assert.Equal(t, 2, len(adapterBids[bidderName1].Bids), "Bidders number doesn't match") - assert.Equal(t, 2, len(bidCategory), "Bidders category mapping doesn't match") + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + adapterBids := map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{ + bidderName1: { + Bids: []*entities.PbsOrtbBid{ + &bid1_1, + &bid1_2, + &bid1_3, + &bid1_4, + &bid1_5, + }, + Currency: "USD", + }, + } + deduplicateGenerator := fakeBooleanGenerator{value: tt.dedupeGeneratorValue} + bidCategory, adapterBids, rejections, err := applyCategoryMapping(nil, r, *requestExt.Prebid.Targeting, adapterBids, categoriesFetcher, targData, &deduplicateGenerator, &nonBids{}) - for bidId, bidCat := range bidCategory { - assert.Equal(t, expectedCategories[bidId], bidCat, "Category mapping doesn't match") - selectedBids[bidId]++ - } + assert.Nil(t, err) + assert.Equal(t, 3, len(rejections)) + assert.Equal(t, adapterBids[bidderName1].Bids, tt.expectedBids) + assert.Equal(t, bidCategory, tt.expectedCategories) + }) } - - assert.Equal(t, numIterations, selectedBids["bid_id2"], "Bid 2 did not make it through every time") - assert.Equal(t, 0, selectedBids["bid_id1"], "Bid 1 should be rejected on every iteration due to lower price") - assert.NotEqual(t, 0, selectedBids["bid_id3"], "Bid 3 should be accepted at least once") - assert.NotEqual(t, 0, selectedBids["bid_id5"], "Bid 5 should be accepted at least once") } func TestNoCategoryDedupe(t *testing.T) { @@ -3167,11 +2989,11 @@ func TestNoCategoryDedupe(t *testing.T) { bid4 := openrtb2.Bid{ID: "bid_id4", ImpID: "imp_id4", Price: 20.0000, Cat: cats4, W: 1, H: 1} bid5 := openrtb2.Bid{ID: "bid_id5", ImpID: "imp_id5", Price: 10.0000, Cat: cats1, W: 1, H: 1} - bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 14.0000, "USD", "", 14.0000} - bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 14.0000, "USD", "", 14.0000} - bid1_3 := entities.PbsOrtbBid{&bid3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", "", 20.0000} - bid1_4 := entities.PbsOrtbBid{&bid4, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", "", 20.0000} - bid1_5 := entities.PbsOrtbBid{&bid5, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} + bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 14.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 14.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_3 := entities.PbsOrtbBid{Bid: &bid3, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_4 := entities.PbsOrtbBid{Bid: &bid4, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_5 := entities.PbsOrtbBid{Bid: &bid5, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""} selectedBids := make(map[string]int) expectedCategories := map[string]string{ @@ -3252,8 +3074,8 @@ func TestCategoryMappingBidderName(t *testing.T) { bid1 := openrtb2.Bid{ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: cats1, W: 1, H: 1} bid2 := openrtb2.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 10.0000, Cat: cats2, W: 1, H: 1} - bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} - bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} + bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: "", OriginalBidCPMUSD: 10.0000} + bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: "", OriginalBidCPMUSD: 10.0000} innerBids1 := []*entities.PbsOrtbBid{ &bid1_1, @@ -3261,7 +3083,6 @@ func TestCategoryMappingBidderName(t *testing.T) { innerBids2 := []*entities.PbsOrtbBid{ &bid1_2, } - seatBid1 := entities.PbsOrtbSeatBid{Bids: innerBids1, Currency: "USD"} bidderName1 := openrtb_ext.BidderName("bidder1") @@ -3314,8 +3135,8 @@ func TestCategoryMappingBidderNameNoCategories(t *testing.T) { bid1 := openrtb2.Bid{ID: "bid_id1", ImpID: "imp_id1", Price: 10.0000, Cat: cats1, W: 1, H: 1} bid2 := openrtb2.Bid{ID: "bid_id2", ImpID: "imp_id2", Price: 12.0000, Cat: cats2, W: 1, H: 1} - bid1_1 := entities.PbsOrtbBid{&bid1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 17}, nil, nil, 0, false, "", 10.0000, "USD", "", 10} - bid1_2 := entities.PbsOrtbBid{&bid2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 8}, nil, nil, 0, false, "", 12.0000, "USD", "", 12} + bid1_1 := entities.PbsOrtbBid{Bid: &bid1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 17}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: "", OriginalBidCPMUSD: 10.0000} + bid1_2 := entities.PbsOrtbBid{Bid: &bid2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 8}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 12.0000, OriginalBidCur: "USD", TargetBidderCode: "", OriginalBidCPMUSD: 12.0000} innerBids1 := []*entities.PbsOrtbBid{ &bid1_1, @@ -3410,6 +3231,7 @@ func TestBidRejectionErrors(t *testing.T) { Video: &openrtb_ext.ExtBidPrebidVideo{ Duration: 30, }, + Meta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: "appnexus"}, }, }, }, @@ -3450,6 +3272,7 @@ func TestBidRejectionErrors(t *testing.T) { Video: &openrtb_ext.ExtBidPrebidVideo{ Duration: 30, }, + Meta: &openrtb_ext.ExtBidPrebidMeta{}, }, }, }, @@ -3490,6 +3313,7 @@ func TestBidRejectionErrors(t *testing.T) { Video: &openrtb_ext.ExtBidPrebidVideo{ Duration: 70, }, + Meta: &openrtb_ext.ExtBidPrebidMeta{}, }, }, }, @@ -3532,6 +3356,7 @@ func TestBidRejectionErrors(t *testing.T) { Video: &openrtb_ext.ExtBidPrebidVideo{ Duration: 30, }, + Meta: &openrtb_ext.ExtBidPrebidMeta{}, }, }, }, @@ -3546,7 +3371,7 @@ func TestBidRejectionErrors(t *testing.T) { innerBids := []*entities.PbsOrtbBid{} for _, bid := range test.bids { currentBid := entities.PbsOrtbBid{ - bid, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: test.duration}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} + Bid: bid, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: test.duration}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: "", OriginalBidCPMUSD: 10.0000} innerBids = append(innerBids, ¤tBid) } @@ -3606,8 +3431,8 @@ func TestCategoryMappingTwoBiddersOneBidEachNoCategorySamePrice(t *testing.T) { bidApn1 := openrtb2.Bid{ID: "bid_idApn1", ImpID: "imp_idApn1", Price: 10.0000, Cat: cats1, W: 1, H: 1} bidApn2 := openrtb2.Bid{ID: "bid_idApn2", ImpID: "imp_idApn2", Price: 10.0000, Cat: cats2, W: 1, H: 1} - bid1_Apn1 := entities.PbsOrtbBid{&bidApn1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} - bid1_Apn2 := entities.PbsOrtbBid{&bidApn2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} + bid1_Apn1 := entities.PbsOrtbBid{Bid: &bidApn1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_Apn2 := entities.PbsOrtbBid{Bid: &bidApn2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""} innerBidsApn1 := []*entities.PbsOrtbBid{ &bid1_Apn1, @@ -3691,11 +3516,11 @@ func TestCategoryMappingTwoBiddersManyBidsEachNoCategorySamePrice(t *testing.T) bidApn2_1 := openrtb2.Bid{ID: "bid_idApn2_1", ImpID: "imp_idApn2_1", Price: 10.0000, Cat: cats2, W: 1, H: 1} bidApn2_2 := openrtb2.Bid{ID: "bid_idApn2_2", ImpID: "imp_idApn2_2", Price: 20.0000, Cat: cats2, W: 1, H: 1} - bid1_Apn1_1 := entities.PbsOrtbBid{&bidApn1_1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} - bid1_Apn1_2 := entities.PbsOrtbBid{&bidApn1_2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", "", 20.0000} + bid1_Apn1_1 := entities.PbsOrtbBid{Bid: &bidApn1_1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_Apn1_2 := entities.PbsOrtbBid{Bid: &bidApn1_2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""} - bid1_Apn2_1 := entities.PbsOrtbBid{&bidApn2_1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} - bid1_Apn2_2 := entities.PbsOrtbBid{&bidApn2_2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", "", 20.0000} + bid1_Apn2_1 := entities.PbsOrtbBid{Bid: &bidApn2_1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_Apn2_2 := entities.PbsOrtbBid{Bid: &bidApn2_2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""} innerBidsApn1 := []*entities.PbsOrtbBid{ &bid1_Apn1_1, @@ -3720,7 +3545,7 @@ func TestCategoryMappingTwoBiddersManyBidsEachNoCategorySamePrice(t *testing.T) expectedseatNonBids := nonBids{} seatNonbids := nonBids{} - _, adapterBids, rejections, err := applyCategoryMapping(nil, r, *requestExt.Prebid.Targeting, adapterBids, categoriesFetcher, targData, &fakeRandomDeduplicateBidBooleanGenerator{true}, &seatNonbids) + _, adapterBids, rejections, err := applyCategoryMapping(nil, r, *requestExt.Prebid.Targeting, adapterBids, categoriesFetcher, targData, &fakeBooleanGenerator{value: true}, &seatNonbids) assert.NoError(t, err, "Category mapping error should be empty") @@ -3775,9 +3600,9 @@ func TestRemoveBidById(t *testing.T) { bidApn1_2 := openrtb2.Bid{ID: "bid_idApn1_2", ImpID: "imp_idApn1_2", Price: 20.0000, Cat: cats1, W: 1, H: 1} bidApn1_3 := openrtb2.Bid{ID: "bid_idApn1_3", ImpID: "imp_idApn1_3", Price: 10.0000, Cat: cats1, W: 1, H: 1} - bid1_Apn1_1 := entities.PbsOrtbBid{&bidApn1_1, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} - bid1_Apn1_2 := entities.PbsOrtbBid{&bidApn1_2, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 20.0000, "USD", "", 20.0000} - bid1_Apn1_3 := entities.PbsOrtbBid{&bidApn1_3, nil, "video", nil, &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, nil, nil, 0, false, "", 10.0000, "USD", "", 10.0000} + bid1_Apn1_1 := entities.PbsOrtbBid{Bid: &bidApn1_1, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_Apn1_2 := entities.PbsOrtbBid{Bid: &bidApn1_2, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 20.0000, OriginalBidCur: "USD", TargetBidderCode: ""} + bid1_Apn1_3 := entities.PbsOrtbBid{Bid: &bidApn1_3, BidMeta: nil, BidType: "video", BidTargets: nil, BidVideo: &openrtb_ext.ExtBidPrebidVideo{Duration: 30}, BidEvents: nil, BidFloors: nil, DealPriority: 0, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 10.0000, OriginalBidCur: "USD", TargetBidderCode: ""} type aTest struct { desc string @@ -3785,11 +3610,6 @@ func TestRemoveBidById(t *testing.T) { outBids []*entities.PbsOrtbBid } testCases := []aTest{ - { - desc: "remove element from the middle", - inBidName: "bid_idApn1_2", - outBids: []*entities.PbsOrtbBid{&bid1_Apn1_1, &bid1_Apn1_3}, - }, { desc: "remove element from the end", inBidName: "bid_idApn1_3", @@ -3835,92 +3655,168 @@ func TestUpdateRejections(t *testing.T) { } func TestApplyDealSupport(t *testing.T) { + type testInput struct { + dealPriority int + impExt json.RawMessage + targ map[string]string + bidderName openrtb_ext.BidderName + } + + type testOutput struct { + hbPbCatDur string + dealErr string + dealTierSatisfied bool + } + testCases := []struct { - description string - dealPriority int - impExt json.RawMessage - targ map[string]string - expectedHbPbCatDur string - expectedDealErr string - expectedDealTierSatisfied bool + description string + in testInput + expected testOutput }{ { - description: "hb_pb_cat_dur should be modified", - dealPriority: 5, - impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}}}`), - targ: map[string]string{ - "hb_pb_cat_dur": "12.00_movies_30s", + description: "hb_pb_cat_dur should be modified", + in: testInput{ + dealPriority: 5, + impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}}}`), + targ: map[string]string{ + "hb_pb_cat_dur": "12.00_movies_30s", + }, + bidderName: openrtb_ext.BidderName("appnexus"), + }, + expected: testOutput{ + hbPbCatDur: "tier5_movies_30s", + dealErr: "", + dealTierSatisfied: true, }, - expectedHbPbCatDur: "tier5_movies_30s", - expectedDealErr: "", - expectedDealTierSatisfied: true, }, { - description: "hb_pb_cat_dur should not be modified due to priority not exceeding min", - dealPriority: 9, - impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 10, "prefix": "tier"}, "placementId": 10433394}}}}`), - targ: map[string]string{ - "hb_pb_cat_dur": "12.00_medicine_30s", + description: "hb_pb_cat_dur should be modified even with a mixed case bidder in the impExt", + in: testInput{ + dealPriority: 5, + impExt: json.RawMessage(`{"prebid": {"bidder": {"APPnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}}}`), + targ: map[string]string{ + "hb_pb_cat_dur": "12.00_movies_30s", + }, + bidderName: openrtb_ext.BidderName("appnexus"), + }, + expected: testOutput{ + hbPbCatDur: "tier5_movies_30s", + dealErr: "", + dealTierSatisfied: true, }, - expectedHbPbCatDur: "12.00_medicine_30s", - expectedDealErr: "", - expectedDealTierSatisfied: false, }, { - description: "hb_pb_cat_dur should not be modified due to invalid config", - dealPriority: 5, - impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": ""}, "placementId": 10433394}}}}`), - targ: map[string]string{ - "hb_pb_cat_dur": "12.00_games_30s", + description: "hb_pb_cat_dur should be modified even with a mixed case bidder in the winningBidsByBidder map", + in: testInput{ + dealPriority: 5, + impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}}}`), + targ: map[string]string{ + "hb_pb_cat_dur": "12.00_movies_30s", + }, + bidderName: openrtb_ext.BidderName("APPnexus"), + }, + expected: testOutput{ + hbPbCatDur: "tier5_movies_30s", + dealErr: "", + dealTierSatisfied: true, }, - expectedHbPbCatDur: "12.00_games_30s", - expectedDealErr: "dealTier configuration invalid for bidder 'appnexus', imp ID 'imp_id1'", - expectedDealTierSatisfied: false, }, { - description: "hb_pb_cat_dur should not be modified due to deal priority of 0", - dealPriority: 0, - impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}}}`), - targ: map[string]string{ - "hb_pb_cat_dur": "12.00_auto_30s", + description: "hb_pb_cat_dur should not be modified due to unknown bidder in the winningBidsByBidder map", + in: testInput{ + dealPriority: 9, + impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 10, "prefix": "tier"}, "placementId": 10433394}}}}`), + targ: map[string]string{ + "hb_pb_cat_dur": "12.00_medicine_30s", + }, + bidderName: openrtb_ext.BidderName("unknown"), + }, + expected: testOutput{ + hbPbCatDur: "12.00_medicine_30s", + dealErr: "", + dealTierSatisfied: false, + }, + }, + { + description: "hb_pb_cat_dur should not be modified due to priority not exceeding min", + in: testInput{ + dealPriority: 9, + impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 10, "prefix": "tier"}, "placementId": 10433394}}}}`), + targ: map[string]string{ + "hb_pb_cat_dur": "12.00_medicine_30s", + }, + bidderName: openrtb_ext.BidderName("appnexus"), + }, + expected: testOutput{ + hbPbCatDur: "12.00_medicine_30s", + dealErr: "", + dealTierSatisfied: false, + }, + }, + { + description: "hb_pb_cat_dur should not be modified due to invalid config", + in: testInput{ + dealPriority: 5, + impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": ""}, "placementId": 10433394}}}}`), + targ: map[string]string{ + "hb_pb_cat_dur": "12.00_games_30s", + }, + bidderName: openrtb_ext.BidderName("appnexus"), + }, + expected: testOutput{ + hbPbCatDur: "12.00_games_30s", + dealErr: "dealTier configuration invalid for bidder 'appnexus', imp ID 'imp_id1'", + dealTierSatisfied: false, + }, + }, + { + description: "hb_pb_cat_dur should not be modified due to deal priority of 0", + in: testInput{ + dealPriority: 0, + impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}}}`), + targ: map[string]string{ + "hb_pb_cat_dur": "12.00_auto_30s", + }, + bidderName: openrtb_ext.BidderName("appnexus"), + }, + expected: testOutput{ + hbPbCatDur: "12.00_auto_30s", + dealErr: "", + dealTierSatisfied: false, }, - expectedHbPbCatDur: "12.00_auto_30s", - expectedDealErr: "", - expectedDealTierSatisfied: false, }, } - bidderName := openrtb_ext.BidderName("appnexus") for _, test := range testCases { bidRequest := &openrtb2.BidRequest{ ID: "some-request-id", Imp: []openrtb2.Imp{ { ID: "imp_id1", - Ext: test.impExt, + Ext: test.in.impExt, }, }, } - bid := entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, test.dealPriority, false, "", 0, "USD", "", 0} + bid := entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "123456"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: test.in.dealPriority, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""} bidCategory := map[string]string{ - bid.Bid.ID: test.targ["hb_pb_cat_dur"], + bid.Bid.ID: test.in.targ["hb_pb_cat_dur"], } auc := &auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp_id1": { - bidderName: {&bid}, + test.in.bidderName: {&bid}, }, }, } dealErrs := applyDealSupport(bidRequest, auc, bidCategory, nil) - assert.Equal(t, test.expectedHbPbCatDur, bidCategory[auc.winningBidsByBidder["imp_id1"][bidderName][0].Bid.ID], test.description) - assert.Equal(t, test.expectedDealTierSatisfied, auc.winningBidsByBidder["imp_id1"][bidderName][0].DealTierSatisfied, "expectedDealTierSatisfied=%v when %v", test.expectedDealTierSatisfied, test.description) - if len(test.expectedDealErr) > 0 { - assert.Containsf(t, dealErrs, errors.New(test.expectedDealErr), "Expected error message not found in deal errors") + assert.Equal(t, test.expected.hbPbCatDur, bidCategory[auc.allBidsByBidder["imp_id1"][test.in.bidderName][0].Bid.ID], test.description) + assert.Equal(t, test.expected.dealTierSatisfied, auc.allBidsByBidder["imp_id1"][test.in.bidderName][0].DealTierSatisfied, "expected.dealTierSatisfied=%v when %v", test.expected.dealTierSatisfied, test.description) + if len(test.expected.dealErr) > 0 { + assert.Containsf(t, dealErrs, errors.New(test.expected.dealErr), "Expected error message not found in deal errors") } } } @@ -3959,11 +3855,11 @@ func TestApplyDealSupportMultiBid(t *testing.T) { }, }, auc: &auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp_id1": { openrtb_ext.BidderName("appnexus"): { - &entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", "", 0}, - &entities.PbsOrtbBid{&openrtb2.Bid{ID: "789101"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", "", 0}, + &entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "123456"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: 5, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""}, + &entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "789101"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: 5, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""}, }, }, }, @@ -4005,11 +3901,11 @@ func TestApplyDealSupportMultiBid(t *testing.T) { }, }, auc: &auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp_id1": { openrtb_ext.BidderName("appnexus"): { - &entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", "", 0}, - &entities.PbsOrtbBid{&openrtb2.Bid{ID: "789101"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", "", 0}, + &entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "123456"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: 5, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""}, + &entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "789101"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: 5, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""}, }, }, }, @@ -4056,11 +3952,11 @@ func TestApplyDealSupportMultiBid(t *testing.T) { }, }, auc: &auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "imp_id1": { openrtb_ext.BidderName("appnexus"): { - &entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", "", 0}, - &entities.PbsOrtbBid{&openrtb2.Bid{ID: "789101"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, 5, false, "", 0, "USD", "", 0}, + &entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "123456"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: 5, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""}, + &entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "789101"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: 5, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""}, }, }, }, @@ -4095,7 +3991,7 @@ func TestApplyDealSupportMultiBid(t *testing.T) { errs := applyDealSupport(tt.args.bidRequest, tt.args.auc, tt.args.bidCategory, tt.args.multiBid) assert.Equal(t, tt.want.errs, errs) - for impID, topBidsPerImp := range tt.args.auc.winningBidsByBidder { + for impID, topBidsPerImp := range tt.args.auc.allBidsByBidder { for bidder, topBidsPerBidder := range topBidsPerImp { for i, topBid := range topBidsPerBidder { assert.Equal(t, tt.want.expectedHbPbCatDur[impID][bidder.String()][i], tt.args.bidCategory[topBid.Bid.ID], tt.name) @@ -4251,7 +4147,7 @@ func TestUpdateHbPbCatDur(t *testing.T) { } for _, test := range testCases { - bid := entities.PbsOrtbBid{&openrtb2.Bid{ID: "123456"}, nil, "video", map[string]string{}, &openrtb_ext.ExtBidPrebidVideo{}, nil, nil, test.dealPriority, false, "", 0, "USD", "", 0} + bid := entities.PbsOrtbBid{Bid: &openrtb2.Bid{ID: "123456"}, BidMeta: nil, BidType: "video", BidTargets: map[string]string{}, BidVideo: &openrtb_ext.ExtBidPrebidVideo{}, BidEvents: nil, BidFloors: nil, DealPriority: test.dealPriority, DealTierSatisfied: false, GeneratedBidID: "", OriginalBidCPM: 0, OriginalBidCur: "USD", TargetBidderCode: ""} bidCategory := map[string]string{ bid.Bid.ID: test.targ["hb_pb_cat_dur"], } @@ -4285,7 +4181,7 @@ func TestMakeBidExtJSON(t *testing.T) { impExtInfo: map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}}, origbidcpm: 10.0000, origbidcur: "USD", - expectedBidExt: `{"prebid":{"meta": {"brandName": "foo"}, "passthrough":{"imp_passthrough_val":1}, "type":"video"}, "storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, + expectedBidExt: `{"prebid":{"meta": {"brandName": "foo","adaptercode": "adapter"}, "passthrough":{"imp_passthrough_val":1}, "type":"video"}, "storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4295,7 +4191,7 @@ func TestMakeBidExtJSON(t *testing.T) { impExtInfo: map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), nil}}, origbidcpm: 10.0000, origbidcur: "USD", - expectedBidExt: `{"prebid":{"meta": {"brandName": "foo"}, "type":"video"},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, + expectedBidExt: `{"prebid":{"meta": {"brandName": "foo","adaptercode": "adapter"}, "type":"video"},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4304,7 +4200,7 @@ func TestMakeBidExtJSON(t *testing.T) { extBidPrebid: openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video")}, impExtInfo: map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}}, origbidcpm: 0, - expectedBidExt: `{"origbidcpm": 0,"prebid":{"passthrough":{"imp_passthrough_val":1}, "type":"video"},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]}}`, + expectedBidExt: `{"origbidcpm": 0,"prebid":{"meta":{"adaptercode": "adapter"},"passthrough":{"imp_passthrough_val":1}, "type":"video"},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]}}`, expectedErrMessage: "", }, { @@ -4314,7 +4210,7 @@ func TestMakeBidExtJSON(t *testing.T) { impExtInfo: map[string]ImpExtInfo{"another_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}}, origbidcpm: 10.0000, origbidcur: "USD", - expectedBidExt: `{"prebid":{"type":"video"},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, + expectedBidExt: `{"prebid":{"meta":{"adaptercode": "adapter"},"type":"video"},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4324,7 +4220,7 @@ func TestMakeBidExtJSON(t *testing.T) { origbidcpm: 10.0000, origbidcur: "USD", impExtInfo: map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}}, - expectedBidExt: `{"prebid":{"passthrough":{"imp_passthrough_val":1}},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, + expectedBidExt: `{"prebid":{"meta":{"adaptercode": "adapter"},"passthrough":{"imp_passthrough_val":1}},"storedrequestattributes":{"h":480,"mimes":["video/mp4"]},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4334,7 +4230,7 @@ func TestMakeBidExtJSON(t *testing.T) { origbidcpm: 10.0000, origbidcur: "USD", impExtInfo: nil, - expectedBidExt: `{"prebid":{"type":"video"},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, + expectedBidExt: `{"prebid":{"meta":{"adaptercode": "adapter"},"type":"video"},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4344,7 +4240,7 @@ func TestMakeBidExtJSON(t *testing.T) { origbidcpm: 10.0000, origbidcur: "USD", impExtInfo: map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"banner":{"h":480}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}}, - expectedBidExt: `{"prebid":{"passthrough":{"imp_passthrough_val":1}, "type":"video"},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, + expectedBidExt: `{"prebid":{"meta":{"adaptercode": "adapter"},"passthrough":{"imp_passthrough_val":1}, "type":"video"},"video":{"h":100}, "origbidcpm": 10, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4354,7 +4250,7 @@ func TestMakeBidExtJSON(t *testing.T) { origbidcpm: 10.0000, origbidcur: "USD", impExtInfo: map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"banner":{"h":480}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}}, - expectedBidExt: `{"prebid":{"passthrough":{"imp_passthrough_val":1}, "type":"video"}, "origbidcpm": 10, "origbidcur": "USD"}`, + expectedBidExt: `{"prebid":{"meta":{"adaptercode": "adapter"},"passthrough":{"imp_passthrough_val":1}, "type":"video"}, "origbidcpm": 10, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4364,7 +4260,7 @@ func TestMakeBidExtJSON(t *testing.T) { origbidcpm: 10.0000, origbidcur: "USD", impExtInfo: map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`{"imp_passthrough_val": 1}`)}}, - expectedBidExt: `{"prebid":{"passthrough":{"imp_passthrough_val":1}, "type":"video"}, "storedrequestattributes":{"h":480,"mimes":["video/mp4"]}, "origbidcpm": 10, "origbidcur": "USD"}`, + expectedBidExt: `{"prebid":{"meta":{"adaptercode": "adapter"},"passthrough":{"imp_passthrough_val":1}, "type":"video"}, "storedrequestattributes":{"h":480,"mimes":["video/mp4"]}, "origbidcpm": 10, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4374,7 +4270,7 @@ func TestMakeBidExtJSON(t *testing.T) { impExtInfo: map[string]ImpExtInfo{}, origbidcpm: 0, origbidcur: "USD", - expectedBidExt: `{"origbidcpm": 0,"prebid":{"meta":{"brandName":"foo"},"type":"banner"}, "origbidcur": "USD"}`, + expectedBidExt: `{"origbidcpm": 0,"prebid":{"meta":{"brandName":"foo","adaptercode": "adapter"},"type":"banner"}, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4384,7 +4280,7 @@ func TestMakeBidExtJSON(t *testing.T) { impExtInfo: nil, origbidcpm: 0, origbidcur: "USD", - expectedBidExt: `{"origbidcpm": 0,"prebid":{"meta":{"brandName":"foo"},"type":"banner"}, "origbidcur": "USD"}`, + expectedBidExt: `{"origbidcpm": 0,"prebid":{"meta":{"brandName":"foo","adaptercode": "adapter"},"type":"banner"}, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4394,7 +4290,7 @@ func TestMakeBidExtJSON(t *testing.T) { impExtInfo: nil, origbidcpm: 10.0000, origbidcur: "USD", - expectedBidExt: `{"prebid":{"meta":{"brandName":"foo"},"type":"banner"}, "origbidcpm": 10, "origbidcur": "USD"}`, + expectedBidExt: `{"prebid":{"meta":{"brandName":"foo","adaptercode": "adapter"},"type":"banner"}, "origbidcpm": 10, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4404,7 +4300,7 @@ func TestMakeBidExtJSON(t *testing.T) { impExtInfo: nil, origbidcpm: 10.0000, origbidcur: "USD", - expectedBidExt: `{"prebid":{"meta":{"brandName":"foo"},"type":"banner"}, "origbidcpm": 10, "origbidcur": "USD"}`, + expectedBidExt: `{"prebid":{"meta":{"brandName":"foo","adaptercode": "adapter"},"type":"banner"}, "origbidcpm": 10, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4414,7 +4310,7 @@ func TestMakeBidExtJSON(t *testing.T) { impExtInfo: nil, origbidcpm: -1, origbidcur: "USD", - expectedBidExt: `{"prebid":{"meta":{"brandName":"foo"},"type":"banner"}, "origbidcur": "USD"}`, + expectedBidExt: `{"prebid":{"meta":{"brandName":"foo","adaptercode":"adapter"},"type":"banner"}, "origbidcur": "USD"}`, expectedErrMessage: "", }, { @@ -4424,7 +4320,7 @@ func TestMakeBidExtJSON(t *testing.T) { impExtInfo: nil, origbidcpm: 0, origbidcur: "USD", - expectedBidExt: `{"origbidcpm": 0,"prebid":{"type":"banner"}, "origbidcur": "USD"}`, + expectedBidExt: `{"origbidcpm": 0,"prebid":{"type":"banner","meta":{"adaptercode":"adapter"}}, "origbidcur": "USD"}`, expectedErrMessage: "", }, //Error cases @@ -4432,37 +4328,30 @@ func TestMakeBidExtJSON(t *testing.T) { description: "Invalid extension, valid extBidPrebid and valid imp ext info", ext: json.RawMessage(`{invalid json}`), extBidPrebid: openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("video")}, - impExtInfo: map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{"h":480,"mimes":["video/mp4"]}}`), json.RawMessage(`"prebid": {"passthrough": {"imp_passthrough_val": some_val}}"`)}}, - expectedBidExt: ``, - expectedErrMessage: "invalid character", - }, - { - description: "Valid extension, empty extBidPrebid and invalid imp ext info", - ext: json.RawMessage(`{"video":{"h":100}}`), - extBidPrebid: openrtb_ext.ExtBidPrebid{}, - impExtInfo: map[string]ImpExtInfo{"test_imp_id": {true, []byte(`{"video":{!}}`), nil}}, expectedBidExt: ``, - expectedErrMessage: "invalid character", + expectedErrMessage: "expects \" or n, but found i", }, { description: "Meta - Invalid", ext: json.RawMessage(`{"prebid":{"meta":{"brandId":"foo"}}}`), // brandId should be an int, but is a string in this test case extBidPrebid: openrtb_ext.ExtBidPrebid{Type: openrtb_ext.BidType("banner")}, impExtInfo: nil, - expectedErrMessage: "error validaing response from server, json: cannot unmarshal string into Go struct field ExtBidPrebidMeta.prebid.meta.brandId of type int", + expectedErrMessage: "error validating response from server, cannot unmarshal openrtb_ext.ExtBidPrebidMeta.BrandID: unexpected character: \xff", }, - // add invalid } for _, test := range testCases { - result, err := makeBidExtJSON(test.ext, &test.extBidPrebid, test.impExtInfo, "test_imp_id", test.origbidcpm, test.origbidcur, test.origbidcpmusd) + t.Run(test.description, func(t *testing.T) { + var adapter openrtb_ext.BidderName = "adapter" + result, err := makeBidExtJSON(test.ext, &test.extBidPrebid, test.impExtInfo, "test_imp_id", test.origbidcpm, test.origbidcur, test.origbidcpmusd, adapter) - if test.expectedErrMessage == "" { - assert.JSONEq(t, test.expectedBidExt, string(result), "Incorrect result") - assert.NoError(t, err, "Error should not be returned") - } else { - assert.Contains(t, err.Error(), test.expectedErrMessage, "incorrect error message") - } + if test.expectedErrMessage == "" { + assert.JSONEq(t, test.expectedBidExt, string(result), "Incorrect result") + assert.NoError(t, err, "Error should not be returned") + } else { + assert.Contains(t, err.Error(), test.expectedErrMessage, "incorrect error message") + } + }) } } @@ -4476,7 +4365,7 @@ func TestStoredAuctionResponses(t *testing.T) { e.cache = &wellBehavedCache{} e.me = &metricsConf.NilMetricsEngine{} e.categoriesFetcher = categoriesFetcher - e.bidIDGenerator = &mockBidIDGenerator{false, false} + e.bidIDGenerator = &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false} e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) e.gdprPermsBuilder = fakePermissionsBuilder{ permissions: &permissionsMock{ @@ -4489,7 +4378,7 @@ func TestStoredAuctionResponses(t *testing.T) { ID: "request-id", Imp: []openrtb2.Imp{{ ID: "impression-id", - Video: &openrtb2.Video{W: 400, H: 300}, + Video: &openrtb2.Video{W: ptrutil.ToPtr[int64](400), H: ptrutil.ToPtr[int64](300)}, }}, } @@ -4498,7 +4387,7 @@ func TestStoredAuctionResponses(t *testing.T) { SeatBid: []openrtb2.SeatBid{ { Bid: []openrtb2.Bid{ - {ID: "bid_id", ImpID: "impression-id", Ext: json.RawMessage(`{"origbidcpm":0,"prebid":{"type":"video"}}`)}, + {ID: "bid_id", ImpID: "impression-id", Ext: json.RawMessage(`{"origbidcpm":0,"prebid":{"meta":{"adaptercode":"appnexus"},"type":"video"}}`)}, }, Seat: "appnexus", }, @@ -4843,7 +4732,7 @@ func TestAuctionDebugEnabled(t *testing.T) { e.cache = &wellBehavedCache{} e.me = &metricsConf.NilMetricsEngine{} e.categoriesFetcher = categoriesFetcher - e.bidIDGenerator = &mockBidIDGenerator{false, false} + e.bidIDGenerator = &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false} e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) e.gdprPermsBuilder = fakePermissionsBuilder{ permissions: &permissionsMock{ @@ -4921,7 +4810,7 @@ func TestPassExperimentConfigsToHoldAuction(t *testing.T) { }, }.Builder - e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, nilCategoryFetcher{}, &signer, macros.NewStringIndexBasedReplacer(), &floors.PriceFloorFetcher{}).(*exchange) + e := NewExchange(adapters, nil, cfg, map[string]usersync.Syncer{}, &metricsConf.NilMetricsEngine{}, biddersInfo, gdprPermsBuilder, currencyConverter, nilCategoryFetcher{}, &signer, macros.NewStringIndexBasedReplacer(), nil).(*exchange) // Define mock incoming bid requeset mockBidRequest := &openrtb2.BidRequest{ @@ -5112,48 +5001,128 @@ func TestMakeBidWithValidation(t *testing.T) { sampleAd := "" sampleOpenrtbBid := &openrtb2.Bid{ID: "some-bid-id", AdM: sampleAd} - // Define test cases testCases := []struct { - description string - givenValidations config.Validations - givenBids []*entities.PbsOrtbBid - expectedNumOfBids int + name string + givenBidRequestExt json.RawMessage + givenValidations config.Validations + givenBids []*entities.PbsOrtbBid + givenSeat openrtb_ext.BidderName + expectedNumOfBids int + expectedNonBids *nonBids + expectedNumDebugErrors int + expectedNumDebugWarnings int }{ { - description: "Validation is enforced, and one bid out of the two is invalid based on dimensions", + name: "One_of_two_bids_is_invalid_based_on_DSA_object_presence", + givenBidRequestExt: json.RawMessage(`{"dsa": {"dsarequired": 2}}`), + givenValidations: config.Validations{}, + givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{Ext: json.RawMessage(`{"dsa": {"adrender":1}}`)}}, {Bid: &openrtb2.Bid{}}}, + givenSeat: "pubmatic", + expectedNumOfBids: 1, + expectedNonBids: &nonBids{ + seatNonBidsMap: map[string][]openrtb_ext.NonBid{ + "pubmatic": { + { + StatusCode: 300, + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + Meta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: "pubmatic"}, + }, + }, + }, + }, + }, + }, + }, + expectedNumDebugWarnings: 1, + }, + { + name: "Creative_size_validation_enforced,_one_of_two_bids_has_invalid_dimensions", givenValidations: config.Validations{BannerCreativeMaxSize: config.ValidationEnforce, MaxCreativeWidth: 100, MaxCreativeHeight: 100}, givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{W: 200, H: 200}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{W: 50, H: 50}, BidType: openrtb_ext.BidTypeBanner}}, + givenSeat: "pubmatic", expectedNumOfBids: 1, + expectedNonBids: &nonBids{ + seatNonBidsMap: map[string][]openrtb_ext.NonBid{ + "pubmatic": { + { + StatusCode: 351, + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + W: 200, + H: 200, + Type: "banner", + Meta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: "pubmatic"}, + }, + }, + }, + }, + }, + }, + }, + expectedNumDebugErrors: 1, }, { - description: "Validation is warned, so no bids should be removed (Validating CreativeMaxSize) ", - givenValidations: config.Validations{BannerCreativeMaxSize: config.ValidationWarn, MaxCreativeWidth: 100, MaxCreativeHeight: 100}, - givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{W: 200, H: 200}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{W: 50, H: 50}, BidType: openrtb_ext.BidTypeBanner}}, - expectedNumOfBids: 2, + name: "Creative_size_validation_warned,_one_of_two_bids_has_invalid_dimensions", + givenValidations: config.Validations{BannerCreativeMaxSize: config.ValidationWarn, MaxCreativeWidth: 100, MaxCreativeHeight: 100}, + givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{W: 200, H: 200}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{W: 50, H: 50}, BidType: openrtb_ext.BidTypeBanner}}, + givenSeat: "pubmatic", + expectedNumOfBids: 2, + expectedNonBids: &nonBids{}, + expectedNumDebugErrors: 1, }, { - description: "Validation is enforced, and one bid out of the two is invalid based on AdM", + name: "AdM_validation_enforced,_one_of_two_bids_has_invalid_AdM", givenValidations: config.Validations{SecureMarkup: config.ValidationEnforce}, givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{AdM: "http://domain.com/invalid", ImpID: "1"}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{AdM: "https://domain.com/valid", ImpID: "2"}, BidType: openrtb_ext.BidTypeBanner}}, + givenSeat: "pubmatic", expectedNumOfBids: 1, + expectedNonBids: &nonBids{ + seatNonBidsMap: map[string][]openrtb_ext.NonBid{ + "pubmatic": { + { + ImpId: "1", + StatusCode: 352, + Ext: openrtb_ext.NonBidExt{ + Prebid: openrtb_ext.ExtResponseNonBidPrebid{ + Bid: openrtb_ext.NonBidObject{ + Type: "banner", + Meta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: "pubmatic"}, + }, + }, + }, + }, + }, + }, + }, + expectedNumDebugErrors: 1, }, { - description: "Validation is warned so no bids should be removed (Validating SecureMarkup)", - givenValidations: config.Validations{SecureMarkup: config.ValidationWarn}, - givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{AdM: "http://domain.com/invalid", ImpID: "1"}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{AdM: "https://domain.com/valid", ImpID: "2"}, BidType: openrtb_ext.BidTypeBanner}}, - expectedNumOfBids: 2, + name: "AdM_validation_warned,_one_of_two_bids_has_invalid_AdM", + givenValidations: config.Validations{SecureMarkup: config.ValidationWarn}, + givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{AdM: "http://domain.com/invalid", ImpID: "1"}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{AdM: "https://domain.com/valid", ImpID: "2"}, BidType: openrtb_ext.BidTypeBanner}}, + givenSeat: "pubmatic", + expectedNumOfBids: 2, + expectedNonBids: &nonBids{}, + expectedNumDebugErrors: 1, }, { - description: "Adm validation is skipped, creative size validation is enforced, one Adm is invalid, but because we skip, no bids should be removed", + name: "Adm_validation_skipped,_creative_size_validation_enforced,_one_of_two_bids_has_invalid_AdM", givenValidations: config.Validations{SecureMarkup: config.ValidationSkip, BannerCreativeMaxSize: config.ValidationEnforce}, givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{AdM: "http://domain.com/invalid"}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{AdM: "https://domain.com/valid"}, BidType: openrtb_ext.BidTypeBanner}}, + givenSeat: "pubmatic", expectedNumOfBids: 2, + expectedNonBids: &nonBids{}, }, { - description: "Creative Size Validation is skipped, Adm Validation is enforced, one Creative Size is invalid, but because we skip, no bids should be removed", - givenValidations: config.Validations{BannerCreativeMaxSize: config.ValidationWarn, MaxCreativeWidth: 100, MaxCreativeHeight: 100}, + name: "Creative_size_validation_skipped,_Adm_Validation_enforced,_one_of_two_bids_has_invalid_dimensions", + givenValidations: config.Validations{BannerCreativeMaxSize: config.ValidationSkip, MaxCreativeWidth: 100, MaxCreativeHeight: 100}, givenBids: []*entities.PbsOrtbBid{{Bid: &openrtb2.Bid{W: 200, H: 200}, BidType: openrtb_ext.BidTypeBanner}, {Bid: &openrtb2.Bid{W: 50, H: 50}, BidType: openrtb_ext.BidTypeBanner}}, + givenSeat: "pubmatic", expectedNumOfBids: 2, + expectedNonBids: &nonBids{}, }, } @@ -5182,20 +5151,35 @@ func TestMakeBidWithValidation(t *testing.T) { e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) - bidExtResponse := &openrtb_ext.ExtBidResponse{Errors: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage)} - ImpExtInfoMap := make(map[string]ImpExtInfo) ImpExtInfoMap["1"] = ImpExtInfo{} ImpExtInfoMap["2"] = ImpExtInfo{} //Run tests for _, test := range testCases { - e.bidValidationEnforcement = test.givenValidations - sampleBids := test.givenBids - resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, true, ImpExtInfoMap, bidExtResponse, "", "") - - assert.Equal(t, 0, len(resultingErrs), "%s. Test should not return errors \n", test.description) - assert.Equal(t, test.expectedNumOfBids, len(resultingBids), "%s. Test returns more valid bids than expected\n", test.description) + t.Run(test.name, func(t *testing.T) { + bidExtResponse := &openrtb_ext.ExtBidResponse{ + Errors: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage), + Warnings: make(map[openrtb_ext.BidderName][]openrtb_ext.ExtBidderMessage), + } + bidRequest := &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: test.givenBidRequestExt, + }, + }, + } + e.bidValidationEnforcement = test.givenValidations + sampleBids := test.givenBids + nonBids := &nonBids{} + resultingBids, resultingErrs := e.makeBid(sampleBids, sampleAuction, true, ImpExtInfoMap, bidRequest, bidExtResponse, test.givenSeat, "", nonBids) + + assert.Equal(t, 0, len(resultingErrs)) + assert.Equal(t, test.expectedNumOfBids, len(resultingBids)) + assert.Equal(t, test.expectedNonBids, nonBids) + assert.Equal(t, test.expectedNumDebugErrors, len(bidExtResponse.Errors)) + assert.Equal(t, test.expectedNumDebugWarnings, len(bidExtResponse.Warnings)) + }) } } @@ -5363,7 +5347,7 @@ func TestOverrideConfigAlternateBidderCodesWithRequestValues(t *testing.T) { }.Builder e.currencyConverter = currency.NewRateConverter(&http.Client{}, "", time.Duration(0)) e.categoriesFetcher = categoriesFetcher - e.bidIDGenerator = &mockBidIDGenerator{false, false} + e.bidIDGenerator = &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false} e.requestSplitter = requestSplitter{ me: e.me, gdprPermsBuilder: e.gdprPermsBuilder, @@ -5420,6 +5404,333 @@ func TestOverrideConfigAlternateBidderCodesWithRequestValues(t *testing.T) { } } +func TestGetAllBids(t *testing.T) { + noBidServer := func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(200) } + server := httptest.NewServer(http.HandlerFunc(noBidServer)) + defer server.Close() + + type testIn struct { + bidderRequests []BidderRequest + bidAdjustments map[string]float64 + conversions currency.Conversions + accountDebugAllowed bool + globalPrivacyControlHeader string + headerDebugAllowed bool + alternateBidderCodes openrtb_ext.ExtAlternateBidderCodes + experiment *openrtb_ext.Experiment + hookExecutor hookexecution.StageExecutor + pbsRequestStartTime time.Time + bidAdjustmentRules map[string][]openrtb_ext.Adjustment + tmaxAdjustments *TmaxAdjustmentsPreprocessed + adapterMap map[openrtb_ext.BidderName]AdaptedBidder + account config.Account + } + type testResults struct { + adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid + adapterExtra map[openrtb_ext.BidderName]*seatResponseExtra + extraRespInfo extraAuctionResponseInfo + } + testCases := []struct { + desc string + in testIn + expected testResults + }{ + { + desc: "alternateBidderCodes-config-absent: pubmatic bidder returns bids with 'pubmatic' and 'groupm' seats", + in: testIn{ + bidderRequests: []BidderRequest{ + { + BidderName: "pubmatic", + BidderCoreName: "pubmatic", + BidRequest: &openrtb2.BidRequest{ + ID: "some-request-id", + Imp: []openrtb2.Imp{{ + ID: "some-impression-id", + }}, + }, + }, + }, + conversions: ¤cy.ConstantRates{}, + hookExecutor: &hookexecution.EmptyHookExecutor{}, + pbsRequestStartTime: time.Now(), + adapterMap: map[openrtb_ext.BidderName]AdaptedBidder{ + openrtb_ext.BidderPubmatic: AdaptBidder(&goodSingleBidder{ + httpRequest: &adapters.RequestData{ + Method: "POST", + Uri: server.URL, + }, + bidResponse: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{ + {Bid: &openrtb2.Bid{ID: "1"}, Seat: "pubmatic"}, + {Bid: &openrtb2.Bid{ID: "2"}, Seat: "groupm"}, + }, + }, + }, server.Client(), &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderPubmatic, nil, ""), + }, + account: config.Account{BidPriceThreshold: 10.00}, + }, + expected: testResults{ + extraRespInfo: extraAuctionResponseInfo{ + bidsFound: true, + }, + adapterExtra: map[openrtb_ext.BidderName]*seatResponseExtra{ + "pubmatic": { + Warnings: []openrtb_ext.ExtBidderMessage{ + { + Code: errortypes.AlternateBidderCodeWarningCode, + Message: `alternateBidderCodes disabled for "pubmatic", rejecting bids for "groupm"`, + }, + }, + }, + }, + adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{ + "pubmatic": { + Bids: []*entities.PbsOrtbBid{ + { + Bid: &openrtb2.Bid{ + ID: "1", + }, + OriginalBidCur: "USD", + }, + }, + Currency: "USD", + Seat: "pubmatic", + BidderCoreName: "pubmatic", + HttpCalls: []*openrtb_ext.ExtHttpCall{}, + }, + }, + }, + }, + { + desc: "alternateBidderCodes-enabled: pubmatic bidder returns bids with 'pubmatic' and 'groupm' seats", + in: testIn{ + bidderRequests: []BidderRequest{ + { + BidderName: "pubmatic", + BidderCoreName: "pubmatic", + BidRequest: &openrtb2.BidRequest{ + ID: "some-request-id", + Imp: []openrtb2.Imp{{ + ID: "some-impression-id", + }}, + }, + }, + }, + conversions: ¤cy.ConstantRates{}, + alternateBidderCodes: openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "pubmatic": { + Enabled: true, + AllowedBidderCodes: []string{"groupm"}, + }, + }, + }, + hookExecutor: &hookexecution.EmptyHookExecutor{}, + pbsRequestStartTime: time.Now(), + adapterMap: map[openrtb_ext.BidderName]AdaptedBidder{ + openrtb_ext.BidderPubmatic: AdaptBidder(&goodSingleBidder{ + httpRequest: &adapters.RequestData{ + Method: "POST", + Uri: server.URL, + }, + bidResponse: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{ + {Bid: &openrtb2.Bid{ID: "1"}, Seat: "pubmatic"}, + {Bid: &openrtb2.Bid{ID: "2"}, Seat: "groupm"}, + }, + }, + }, server.Client(), &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderPubmatic, nil, ""), + }, + }, + expected: testResults{ + extraRespInfo: extraAuctionResponseInfo{ + bidsFound: true, + }, + adapterExtra: map[openrtb_ext.BidderName]*seatResponseExtra{ + "pubmatic": { + Warnings: []openrtb_ext.ExtBidderMessage{}, + }, + }, + adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{ + "pubmatic": { + Bids: []*entities.PbsOrtbBid{ + { + Bid: &openrtb2.Bid{ + ID: "1", + }, + OriginalBidCur: "USD", + }, + }, + Currency: "USD", + Seat: "pubmatic", + BidderCoreName: "pubmatic", + HttpCalls: []*openrtb_ext.ExtHttpCall{}, + }, + "groupm": { + Bids: []*entities.PbsOrtbBid{ + { + Bid: &openrtb2.Bid{ + ID: "2", + }, + OriginalBidCur: "USD", + AlternateBidderCode: string(openrtb_ext.BidderPubmatic), + }, + }, + Currency: "USD", + Seat: "groupm", + BidderCoreName: "pubmatic", + HttpCalls: []*openrtb_ext.ExtHttpCall{}, + }, + }, + }, + }, + { + desc: "alternateBidderCodes-enabled: pubmatic bidder returns bids with only 'groupm' seat", + in: testIn{ + bidderRequests: []BidderRequest{ + { + BidderName: "pubmatic", + BidderCoreName: "pubmatic", + BidRequest: &openrtb2.BidRequest{ + ID: "some-request-id", + Imp: []openrtb2.Imp{{ + ID: "some-impression-id", + }}, + }, + }, + }, + conversions: ¤cy.ConstantRates{}, + alternateBidderCodes: openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "pubmatic": { + Enabled: true, + AllowedBidderCodes: []string{"groupm"}, + }, + }, + }, + hookExecutor: &hookexecution.EmptyHookExecutor{}, + pbsRequestStartTime: time.Now(), + adapterMap: map[openrtb_ext.BidderName]AdaptedBidder{ + openrtb_ext.BidderPubmatic: AdaptBidder(&goodSingleBidder{ + httpRequest: &adapters.RequestData{ + Method: "POST", + Uri: server.URL, + }, + bidResponse: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{ + {Bid: &openrtb2.Bid{ID: "2"}, Seat: "groupm"}, + }, + }, + }, server.Client(), &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderPubmatic, nil, ""), + }, + }, + expected: testResults{ + extraRespInfo: extraAuctionResponseInfo{ + bidsFound: true, + }, + adapterExtra: map[openrtb_ext.BidderName]*seatResponseExtra{ + "pubmatic": { + Warnings: []openrtb_ext.ExtBidderMessage{}, + }, + }, + adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{ + "groupm": { + Bids: []*entities.PbsOrtbBid{ + { + Bid: &openrtb2.Bid{ + ID: "2", + }, + OriginalBidCur: "USD", + AlternateBidderCode: string(openrtb_ext.BidderPubmatic), + }, + }, + Currency: "USD", + Seat: "groupm", + BidderCoreName: "pubmatic", + HttpCalls: []*openrtb_ext.ExtHttpCall{}, + }, + }, + }, + }, + { + desc: "bidder responded with empty bid", + in: testIn{ + bidderRequests: []BidderRequest{ + { + BidderName: "pubmatic", + BidderCoreName: "pubmatic", + BidRequest: &openrtb2.BidRequest{ + ID: "some-request-id", + Imp: []openrtb2.Imp{{ + ID: "some-impression-id", + }}, + }, + }, + }, + conversions: ¤cy.ConstantRates{}, + alternateBidderCodes: openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "pubmatic": { + Enabled: true, + AllowedBidderCodes: []string{"groupm"}, + }, + }, + }, + hookExecutor: &hookexecution.EmptyHookExecutor{}, + pbsRequestStartTime: time.Now(), + adapterMap: map[openrtb_ext.BidderName]AdaptedBidder{ + openrtb_ext.BidderPubmatic: AdaptBidder(&goodSingleBidder{ + httpRequest: &adapters.RequestData{ + Method: "POST", + Uri: server.URL, + }, + bidResponse: &adapters.BidderResponse{}, + }, server.Client(), &config.Configuration{}, &metricsConfig.NilMetricsEngine{}, openrtb_ext.BidderPubmatic, nil, ""), + }, + }, + expected: testResults{ + extraRespInfo: extraAuctionResponseInfo{ + bidsFound: false, + }, + adapterExtra: map[openrtb_ext.BidderName]*seatResponseExtra{ + "pubmatic": { + Warnings: []openrtb_ext.ExtBidderMessage{}, + }, + }, + adapterBids: map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{}, + }, + }, + } + for _, test := range testCases { + t.Run(test.desc, func(t *testing.T) { + e := exchange{ + cache: &wellBehavedCache{}, + me: &metricsConf.NilMetricsEngine{}, + gdprPermsBuilder: fakePermissionsBuilder{ + permissions: &permissionsMock{ + allowAllBidders: true, + }, + }.Builder, + adapterMap: test.in.adapterMap, + } + + adapterBids, adapterExtra, extraRespInfo := e.getAllBids(context.Background(), test.in.bidderRequests, test.in.bidAdjustments, + test.in.conversions, test.in.accountDebugAllowed, test.in.globalPrivacyControlHeader, test.in.headerDebugAllowed, test.in.alternateBidderCodes, test.in.experiment, + test.in.hookExecutor, test.in.pbsRequestStartTime, test.in.bidAdjustmentRules, test.in.tmaxAdjustments, false, test.in.account) + + assert.Equalf(t, test.expected.extraRespInfo.bidsFound, extraRespInfo.bidsFound, "extraRespInfo.bidsFound mismatch") + assert.Equalf(t, test.expected.adapterBids, adapterBids, "adapterBids mismatch") + assert.Equalf(t, len(test.expected.adapterExtra), len(adapterExtra), "adapterExtra length mismatch") + for adapter, extra := range test.expected.adapterExtra { + assert.Equalf(t, extra.Warnings, adapterExtra[adapter].Warnings, "adapterExtra.Warnings mismatch for adapter [%s]", adapter) + } + }) + } +} + type MockSigner struct { data string } @@ -5441,7 +5752,7 @@ type exchangeSpec struct { DebugLog *DebugLog `json:"debuglog,omitempty"` EventsEnabled bool `json:"events_enabled,omitempty"` StartTime int64 `json:"start_time_ms,omitempty"` - BidIDGenerator *mockBidIDGenerator `json:"bidIDGenerator,omitempty"` + BidIDGenerator *fakeBidIDGenerator `json:"bidIDGenerator,omitempty"` RequestType *metrics.RequestType `json:"requestType,omitempty"` PassthroughFlag bool `json:"passthrough_flag,omitempty"` HostSChainFlag bool `json:"host_schain_flag,omitempty"` @@ -5598,12 +5909,12 @@ func (b *capturingRequestBidder) requestBid(ctx context.Context, bidderRequest B func diffOrtbRequests(t *testing.T, description string, expected *openrtb2.BidRequest, actual *openrtb2.BidRequest) { t.Helper() - actualJSON, err := json.Marshal(actual) + actualJSON, err := jsonutil.Marshal(actual) if err != nil { t.Fatalf("%s failed to marshal actual BidRequest into JSON. %v", description, err) } - expectedJSON, err := json.Marshal(expected) + expectedJSON, err := jsonutil.Marshal(expected) if err != nil { t.Fatalf("%s failed to marshal expected BidRequest into JSON. %v", description, err) } @@ -5621,12 +5932,12 @@ func diffOrtbResponses(t *testing.T, description string, expected *openrtb2.BidR // this implementation detail, I'm cutting a corner and ignoring it here. actualSeats := mapifySeatBids(t, description, actual.SeatBid) expectedSeats := mapifySeatBids(t, description, expected.SeatBid) - actualJSON, err := json.Marshal(actualSeats) + actualJSON, err := jsonutil.Marshal(actualSeats) if err != nil { t.Fatalf("%s failed to marshal actual BidResponse into JSON. %v", description, err) } - expectedJSON, err := json.Marshal(expectedSeats) + expectedJSON, err := jsonutil.Marshal(expectedSeats) if err != nil { t.Fatalf("%s failed to marshal expected BidResponse into JSON. %v", description, err) } @@ -5720,19 +6031,6 @@ func (nilCategoryFetcher) FetchCategories(ctx context.Context, primaryAdServer, return "", nil } -// fakeCurrencyRatesHttpClient is a simple http client mock returning a constant response body -type fakeCurrencyRatesHttpClient struct { - responseBody string -} - -func (m *fakeCurrencyRatesHttpClient) Do(req *http.Request) (*http.Response, error) { - return &http.Response{ - Status: "200 OK", - StatusCode: http.StatusOK, - Body: io.NopCloser(strings.NewReader(m.responseBody)), - }, nil -} - type mockBidder struct { mock.Mock lastExtraRequestInfo *adapters.ExtraRequestInfo @@ -5756,13 +6054,13 @@ func getInfoFromImp(req *openrtb_ext.RequestWrapper) (json.RawMessage, string, e impID := imp.ID var bidderExts map[string]json.RawMessage - if err := json.Unmarshal(imp.Ext, &bidderExts); err != nil { + if err := jsonutil.UnmarshalValid(imp.Ext, &bidderExts); err != nil { return nil, "", err } var extPrebid openrtb_ext.ExtImpPrebid if bidderExts[openrtb_ext.PrebidExtKey] != nil { - if err := json.Unmarshal(bidderExts[openrtb_ext.PrebidExtKey], &extPrebid); err != nil { + if err := jsonutil.UnmarshalValid(bidderExts[openrtb_ext.PrebidExtKey], &extPrebid); err != nil { return nil, "", err } } @@ -5882,12 +6180,12 @@ func (e mockUpdateBidRequestHook) HandleBidderRequestHook(_ context.Context, mct c := hookstage.ChangeSet[hookstage.BidderRequestPayload]{} c.AddMutation( func(payload hookstage.BidderRequestPayload) (hookstage.BidderRequestPayload, error) { - payload.BidRequest.Site.Name = "test" + payload.Request.Site.Name = "test" return payload, nil }, hookstage.MutationUpdate, "bidRequest", "site.name", ).AddMutation( func(payload hookstage.BidderRequestPayload) (hookstage.BidderRequestPayload, error) { - payload.BidRequest.Site.Domain = "test.com" + payload.Request.Site.Domain = "test.com" return payload, nil }, hookstage.MutationUpdate, "bidRequest", "site.domain", ) @@ -6010,3 +6308,257 @@ func TestSetSeatNonBid(t *testing.T) { }) } } + +func TestBuildMultiBidMap(t *testing.T) { + type testCase struct { + desc string + inPrebid *openrtb_ext.ExtRequestPrebid + expected map[string]openrtb_ext.ExtMultiBid + } + testGroups := []struct { + groupDesc string + tests []testCase + }{ + { + groupDesc: "Nil or empty tests", + tests: []testCase{ + { + desc: "prebid nil, expect nil map", + inPrebid: nil, + expected: nil, + }, + { + desc: "prebid.MultiBid nil, expect nil map", + inPrebid: &openrtb_ext.ExtRequestPrebid{}, + expected: nil, + }, + { + desc: "not-nil prebid.MultiBid is empty, expect empty map", + inPrebid: &openrtb_ext.ExtRequestPrebid{ + MultiBid: []*openrtb_ext.ExtMultiBid{}, + }, + expected: map[string]openrtb_ext.ExtMultiBid{}, + }, + }, + }, + { + groupDesc: "prebid.MultiBid.Bidder tests", + tests: []testCase{ + { + desc: "Lowercase prebid.MultiBid.Bidder is found in the BidderName list, entry is mapped", + inPrebid: &openrtb_ext.ExtRequestPrebid{ + MultiBid: []*openrtb_ext.ExtMultiBid{ + {Bidder: "appnexus"}, + }, + }, + expected: map[string]openrtb_ext.ExtMultiBid{ + "appnexus": {Bidder: "appnexus"}, + }, + }, + { + desc: "Uppercase prebid.MultiBid.Bidder is found in the BidderName list, entry is mapped", + inPrebid: &openrtb_ext.ExtRequestPrebid{ + MultiBid: []*openrtb_ext.ExtMultiBid{ + {Bidder: "APPNEXUS"}, + }, + }, + expected: map[string]openrtb_ext.ExtMultiBid{ + "appnexus": {Bidder: "APPNEXUS"}, + }, + }, + { + desc: "Lowercase prebid.MultiBid.Bidder is not found in the BidderName list, expect empty map", + inPrebid: &openrtb_ext.ExtRequestPrebid{ + MultiBid: []*openrtb_ext.ExtMultiBid{ + {Bidder: "unknown"}, + }, + }, + expected: map[string]openrtb_ext.ExtMultiBid{}, + }, + { + desc: "Mixed-case prebid.MultiBid.Bidder is not found in the BidderName list, expect empty map", + inPrebid: &openrtb_ext.ExtRequestPrebid{ + MultiBid: []*openrtb_ext.ExtMultiBid{ + {Bidder: "UnknownBidder"}, + }, + }, + expected: map[string]openrtb_ext.ExtMultiBid{}, + }, + { + desc: "Different-cased prebid.MultiBid.Bidder entries that refer to the same adapter are found in the BidderName list are mapped once", + inPrebid: &openrtb_ext.ExtRequestPrebid{ + MultiBid: []*openrtb_ext.ExtMultiBid{ + {Bidder: "AppNexus"}, + {Bidder: "appnexus"}, + }, + }, + expected: map[string]openrtb_ext.ExtMultiBid{ + "appnexus": {Bidder: "appnexus"}, + }, + }, + }, + }, + { + groupDesc: "prebid.MultiBid.Bidders tests", + tests: []testCase{ + { + desc: "Lowercase prebid.MultiBid.Bidder is found in the BidderName list, entry is mapped", + inPrebid: &openrtb_ext.ExtRequestPrebid{ + MultiBid: []*openrtb_ext.ExtMultiBid{ + {Bidders: []string{"appnexus"}}, + }, + }, + expected: map[string]openrtb_ext.ExtMultiBid{ + "appnexus": { + Bidders: []string{"appnexus"}, + }, + }, + }, + { + desc: "Lowercase prebid.MultiBid.Bidder is not found in the BidderName list, expect empty map", + inPrebid: &openrtb_ext.ExtRequestPrebid{ + MultiBid: []*openrtb_ext.ExtMultiBid{ + {Bidders: []string{"unknown"}}, + }, + }, + expected: map[string]openrtb_ext.ExtMultiBid{}, + }, + { + desc: "Mixed-case prebid.MultiBid.Bidder is not found in the BidderName list, expect empty map", + inPrebid: &openrtb_ext.ExtRequestPrebid{ + MultiBid: []*openrtb_ext.ExtMultiBid{ + {Bidders: []string{"UnknownBidder"}}, + }, + }, + expected: map[string]openrtb_ext.ExtMultiBid{}, + }, + { + desc: "Different-cased prebid.MultiBid.Bidder entries that refer to the same adapter are found in the BidderName list are mapped once", + inPrebid: &openrtb_ext.ExtRequestPrebid{ + MultiBid: []*openrtb_ext.ExtMultiBid{ + {Bidders: []string{"AppNexus", "appnexus", "UnknownBidder"}}, + }, + }, + expected: map[string]openrtb_ext.ExtMultiBid{ + "appnexus": { + Bidders: []string{"AppNexus", "appnexus", "UnknownBidder"}, + }, + }, + }, + }, + }, + { + groupDesc: "prebid.MultiBid.Bidder and prebid.MultiBid.Bidders entries in tests", + tests: []testCase{ + { + desc: "prebid.MultiBid.Bidder found, ignore entries in prebid.MultiBid.Bidders, even if its unknown", + inPrebid: &openrtb_ext.ExtRequestPrebid{ + MultiBid: []*openrtb_ext.ExtMultiBid{ + { + Bidder: "UnknownBidder", + Bidders: []string{"appnexus", "rubicon", "pubmatic"}, + }, + }, + }, + expected: map[string]openrtb_ext.ExtMultiBid{}, + }, + { + desc: "prebid.MultiBid.Bidder found in one entry, prebid.MultiBid.Bidders in another. Add all to map", + inPrebid: &openrtb_ext.ExtRequestPrebid{ + MultiBid: []*openrtb_ext.ExtMultiBid{ + { + Bidder: "pubmatic", + Bidders: []string{"appnexus", "rubicon", "UnknownBidder"}, + }, + { + Bidders: []string{"UnknownBidder", "appnexus", "rubicon"}, + }, + }, + }, + expected: map[string]openrtb_ext.ExtMultiBid{ + "pubmatic": { + Bidder: "pubmatic", + Bidders: []string{"appnexus", "rubicon", "UnknownBidder"}, + }, + "appnexus": { + Bidders: []string{"UnknownBidder", "appnexus", "rubicon"}, + }, + "rubicon": { + Bidders: []string{"UnknownBidder", "appnexus", "rubicon"}, + }, + }, + }, + }, + }, + } + for _, group := range testGroups { + for _, tc := range group.tests { + t.Run(group.groupDesc+tc.desc, func(t *testing.T) { + multiBidMap := buildMultiBidMap(tc.inPrebid) + assert.Equal(t, tc.expected, multiBidMap, tc.desc) + }) + } + } +} + +func TestBidsToUpdate(t *testing.T) { + type testInput struct { + multiBid map[string]openrtb_ext.ExtMultiBid + bidder string + } + testCases := []struct { + desc string + in testInput + expected int + }{ + { + desc: "Empty multibid map. Expect openrtb_ext.DefaultBidLimit", + in: testInput{}, + expected: openrtb_ext.DefaultBidLimit, + }, + { + desc: "Empty bidder. Expect openrtb_ext.DefaultBidLimit", + in: testInput{ + multiBid: map[string]openrtb_ext.ExtMultiBid{ + "appnexus": { + Bidder: "appnexus", + MaxBids: ptrutil.ToPtr(2), + }, + }, + }, + expected: openrtb_ext.DefaultBidLimit, + }, + { + desc: "bidder finds a match in multibid map but TargetBidderCodePrefix is empty. Expect openrtb_ext.DefaultBidLimit", + in: testInput{ + multiBid: map[string]openrtb_ext.ExtMultiBid{ + "appnexus": { + Bidder: "appnexus", + MaxBids: ptrutil.ToPtr(2), + }, + }, + bidder: "appnexus", + }, + expected: openrtb_ext.DefaultBidLimit, + }, + { + desc: "multibid element with non-empty TargetBidderCodePrefix matches bidder. Expect MaxBids value", + in: testInput{ + multiBid: map[string]openrtb_ext.ExtMultiBid{ + "appnexus": { + Bidder: "appnexus", + MaxBids: ptrutil.ToPtr(2), + TargetBidderCodePrefix: "aPrefix", + }, + }, + bidder: "appnexus", + }, + expected: 2, + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + assert.Equal(t, tc.expected, bidsToUpdate(tc.in.multiBid, tc.in.bidder), tc.desc) + }) + } +} diff --git a/exchange/exchangetest/alternate-bidder-codes.json b/exchange/exchangetest/alternate-bidder-codes.json new file mode 100644 index 00000000000..c2e6f95a5a7 --- /dev/null +++ b/exchange/exchangetest/alternate-bidder-codes.json @@ -0,0 +1,272 @@ +{ + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "imp-id-1", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "pubmatic": { + "publisherId": 5890 + }, + "appnexus": { + "placementId": 1 + } + } + } + } + } + ], + "ext": { + "prebid": { + "alternatebiddercodes": { + "enabled": true, + "bidders": { + "PUBmatic": { + "enabled": true, + "allowedbiddercodes": [ + "groupm" + ] + } + } + } + } + } + } + }, + "outgoingRequests": { + "pubmatic": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "imp-id-1", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "publisherId": 5890 + } + } + } + ], + "ext": { + "prebid": { + "alternatebiddercodes": { + "enabled": true, + "bidders": { + "pubmatic": { + "enabled": true, + "allowedbiddercodes": [ + "groupm" + ] + } + } + } + } + } + } + }, + "mockResponse": { + "pbsSeatBids": [ + { + "pbsBids": [ + { + "ortbBid": { + "id": "pubmatic-bid-1", + "impid": "imp-id-1", + "price": 0.71 + }, + "bidType": "video", + "bidMeta": { + "adaptercode": "pubmatic" + } + } + ], + "seat": "pubmatic" + }, + { + "pbsBids": [ + { + "ortbBid": { + "id": "pubmatic-bid-2", + "impid": "imp-id-1", + "price": 0.51 + }, + "bidType": "video", + "bidMeta": { + "adaptercode": "pubmatic" + } + } + ], + "seat": "groupm" + } + ] + } + }, + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "imp-id-1", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + } + ], + "ext": { + "prebid": { + "alternatebiddercodes": { + "enabled": true, + "bidders": null + } + } + } + } + }, + "mockResponse": { + "pbsSeatBids": [ + { + "pbsBids": [ + { + "ortbBid": { + "id": "appnexus-bid-1", + "impid": "imp-id-1", + "price": 0.3 + }, + "bidType": "banner", + "bidMeta": { + "adaptercode": "appnexus" + } + } + ], + "seat": "appnexus" + }, + { + "pbsBids": [ + { + "ortbBid": { + "id": "appnexus-bid-2", + "impid": "imp-id-1", + "price": 0.3 + }, + "bidType": "banner", + "bidMeta": { + "adaptercode": "appnexus" + } + } + ], + "seat": "groupm" + } + ] + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [ + { + "seat": "groupm", + "bid": [ + { + "id": "pubmatic-bid-2", + "impid": "imp-id-1", + "price": 0.51, + "ext": { + "origbidcpm": 0.51, + "prebid": { + "meta": { + "adaptercode": "groupm" + }, + "type": "video" + } + } + }, + { + "id": "appnexus-bid-2", + "impid": "imp-id-1", + "price": 0.3, + "ext": { + "origbidcpm": 0.3, + "prebid": { + "meta": { + "adaptercode": "groupm" + }, + "type": "banner" + } + } + } + ] + }, + { + "seat": "pubmatic", + "bid": [ + { + "id": "pubmatic-bid-1", + "impid": "imp-id-1", + "price": 0.71, + "ext": { + "origbidcpm": 0.71, + "prebid": { + "meta": { + "adaptercode": "pubmatic" + }, + "type": "video" + } + } + } + ] + }, + { + "seat": "appnexus", + "bid": [ + { + "id": "appnexus-bid-1", + "impid": "imp-id-1", + "price": 0.3, + "ext": { + "origbidcpm": 0.3, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "type": "banner" + } + } + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/append-bidder-names.json b/exchange/exchangetest/append-bidder-names.json index 659045cd588..14f1181e96a 100644 --- a/exchange/exchangetest/append-bidder-names.json +++ b/exchange/exchangetest/append-bidder-names.json @@ -137,6 +137,9 @@ "origbidcpm": 0.3, "prebid": { "type": "video", + "meta": { + "adaptercode":"appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_bidder_appnexus": "appnexus", @@ -162,6 +165,9 @@ "ext": { "origbidcpm": 0.6, "prebid": { + "meta": { + "adaptercode":"appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_bidder_appnexus": "appnexus", diff --git a/exchange/exchangetest/bid-consolidation-test.json b/exchange/exchangetest/bid-consolidation-test.json index b38e9d69603..61dbae5e045 100644 --- a/exchange/exchangetest/bid-consolidation-test.json +++ b/exchange/exchangetest/bid-consolidation-test.json @@ -127,6 +127,9 @@ "ext": { "origbidcpm": 0.4, "prebid": { + "meta": { + "adaptercode":"rubicon" + }, "type": "video" } } @@ -146,6 +149,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode":"appnexus" + }, "type": "video" } } @@ -160,6 +166,9 @@ "ext": { "origbidcpm": 0.6, "prebid": { + "meta": { + "adaptercode":"appnexus" + }, "type": "video" } } diff --git a/exchange/exchangetest/bid-ext-prebid-collision.json b/exchange/exchangetest/bid-ext-prebid-collision.json index 49e4f8a8f22..f6a2a065485 100644 --- a/exchange/exchangetest/bid-ext-prebid-collision.json +++ b/exchange/exchangetest/bid-ext-prebid-collision.json @@ -99,6 +99,9 @@ "someField": "someValue", "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode":"appnexus" + }, "type": "video" } } diff --git a/exchange/exchangetest/bid-ext.json b/exchange/exchangetest/bid-ext.json index 69e7aba9f0c..f2416d6f2de 100644 --- a/exchange/exchangetest/bid-ext.json +++ b/exchange/exchangetest/bid-ext.json @@ -96,6 +96,9 @@ "origbidcpm": 0.3, "someField": "someValue", "prebid": { + "meta": { + "adaptercode":"appnexus" + }, "type": "video" } } diff --git a/exchange/exchangetest/bid-id-invalid.json b/exchange/exchangetest/bid-id-invalid.json deleted file mode 100644 index 9c5cb84c310..00000000000 --- a/exchange/exchangetest/bid-id-invalid.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "bidIDGenerator": { - "generateBidID": true, - "returnError": true - }, - "incomingRequest": { - "ortbRequest": { - "id": "some-request-id", - "site": { - "page": "test.somepage.com" - }, - "imp": [ - { - "id": "my-imp-id", - "video": { - "mimes": [ - "video/mp4" - ] - }, - "ext": { - "prebid": { - "bidder": { - "appnexus": { - "placementId": 1 - } - } - } - } - } - ], - "test": 1, - "ext": { - "prebid": { - "targeting": { - "includebrandcategory": { - "primaryadserver": 1, - "publisher": "", - "withcategory": true - }, - "pricegranularity": { - "precision": 2, - "ranges": [ - { - "min": 0, - "max": 20, - "increment": 0.1 - } - ] - }, - "includewinners": true, - "includebidderkeys": true, - "appendbiddernames": true - } - } - } - }, - "usersyncs": { - "appnexus": "123" - } - }, - "outgoingRequests": { - "appnexus": { - "mockResponse": { - "pbsSeatBids": [ - { - "pbsBids": [ - { - "ortbBid": { - "id": "apn-bid", - "impid": "my-imp-id", - "price": 0.3, - "w": 200, - "h": 250, - "crid": "creative-1", - "cat": [ - "IAB1-1" - ] - }, - "bidType": "video", - "bidVideo": { - "duration": 30, - "PrimaryCategory": "" - } - } - ], - "seat": "appnexus" - } - ] - } - } - }, - "response": { - "bids": { - "id": "some-request-id", - "seatbid": [ - { - "seat": "appnexus", - "bid": [ - { - "id": "apn-bid", - "impid": "my-imp-id", - "price": 0.3, - "w": 200, - "h": 250, - "crid": "creative-1", - "cat": [ - "IAB1-1" - ], - "ext": { - "origbidcpm": 0.3, - "prebid": { - "type": "video", - "targeting": { - "hb_bidder": "appnexus", - "hb_bidder_appnexus": "appnexus", - "hb_cache_host": "www.pbcserver.com", - "hb_cache_host_appnex": "www.pbcserver.com", - "hb_cache_path": "/pbcache/endpoint", - "hb_cache_path_appnex": "/pbcache/endpoint", - "hb_pb": "0.20", - "hb_pb_appnexus": "0.20", - "hb_pb_cat_dur": "0.20_VideoGames_0s_appnexus", - "hb_pb_cat_dur_appnex": "0.20_VideoGames_0s_appnexus", - "hb_size": "200x250", - "hb_size_appnexus": "200x250" - } - } - } - } - ] - } - ] - }, - "ext": { - "debug": { - "resolvedrequest": { - "id": "some-request-id", - "imp": [ - { - "id": "my-imp-id", - "video": { - "mimes": [ - "video/mp4" - ] - }, - "ext": { - "prebid": { - "bidder": { - "appnexus": { - "placementId": 1 - } - } - } - } - } - ], - "site": { - "page": "test.somepage.com" - }, - "test": 1, - "ext": { - "prebid": { - "targeting": { - "includebrandcategory": { - "primaryadserver": 1, - "publisher": "", - "withcategory": true - }, - "pricegranularity": { - "precision": 2, - "ranges": [ - { - "min": 0, - "max": 20, - "increment": 0.1 - } - ] - }, - "includewinners": true, - "includebidderkeys": true, - "appendbiddernames": true - } - } - } - } - }, - "errors": { - "prebid": [ - { - "code": 999, - "message": "Error generating bid.ext.prebid.bidid" - } - ] - } - } - } -} \ No newline at end of file diff --git a/exchange/exchangetest/bid-id-valid.json b/exchange/exchangetest/bid-id-valid.json deleted file mode 100644 index b4a30640f77..00000000000 --- a/exchange/exchangetest/bid-id-valid.json +++ /dev/null @@ -1,190 +0,0 @@ -{ - "bidIDGenerator": { - "generateBidID": true, - "returnError": false - }, - "incomingRequest": { - "ortbRequest": { - "id": "some-request-id", - "site": { - "page": "test.somepage.com" - }, - "imp": [ - { - "id": "my-imp-id", - "video": { - "mimes": [ - "video/mp4" - ] - }, - "ext": { - "prebid": { - "bidder": { - "appnexus": { - "placementId": 1 - } - } - } - } - } - ], - "test": 1, - "ext": { - "prebid": { - "targeting": { - "includebrandcategory": { - "primaryadserver": 1, - "publisher": "", - "withcategory": true - }, - "pricegranularity": { - "precision": 2, - "ranges": [ - { - "min": 0, - "max": 20, - "increment": 0.1 - } - ] - }, - "includewinners": true, - "includebidderkeys": true, - "appendbiddernames": true - } - } - } - }, - "usersyncs": { - "appnexus": "123" - } - }, - "outgoingRequests": { - "appnexus": { - "mockResponse": { - "pbsSeatBids": [ - { - "pbsBids": [ - { - "ortbBid": { - "id": "apn-bid", - "impid": "my-imp-id", - "price": 0.3, - "w": 200, - "h": 250, - "crid": "creative-1", - "cat": [ - "IAB1-1" - ] - }, - "bidType": "video", - "bidVideo": { - "duration": 30, - "PrimaryCategory": "" - } - } - ], - "seat": "appnexus" - } - ] - } - } - }, - "response": { - "bids": { - "id": "some-request-id", - "seatbid": [ - { - "seat": "appnexus", - "bid": [ - { - "id": "apn-bid", - "impid": "my-imp-id", - "price": 0.3, - "w": 200, - "h": 250, - "crid": "creative-1", - "cat": [ - "IAB1-1" - ], - "ext": { - "origbidcpm": 0.3, - "prebid": { - "bidid": "mock_uuid", - "type": "video", - "targeting": { - "hb_bidder": "appnexus", - "hb_bidder_appnexus": "appnexus", - "hb_cache_host": "www.pbcserver.com", - "hb_cache_host_appnex": "www.pbcserver.com", - "hb_cache_path": "/pbcache/endpoint", - "hb_cache_path_appnex": "/pbcache/endpoint", - "hb_pb": "0.20", - "hb_pb_appnexus": "0.20", - "hb_pb_cat_dur": "0.20_VideoGames_0s_appnexus", - "hb_pb_cat_dur_appnex": "0.20_VideoGames_0s_appnexus", - "hb_size": "200x250", - "hb_size_appnexus": "200x250" - } - } - } - } - ] - } - ] - }, - "ext": { - "debug": { - "resolvedrequest": { - "id": "some-request-id", - "imp": [ - { - "id": "my-imp-id", - "video": { - "mimes": [ - "video/mp4" - ] - }, - "ext": { - "prebid": { - "bidder": { - "appnexus": { - "placementId": 1 - } - } - } - } - } - ], - "site": { - "page": "test.somepage.com" - }, - "test": 1, - "ext": { - "prebid": { - "targeting": { - "pricegranularity": { - "precision": 2, - "ranges": [ - { - "min": 0, - "max": 20, - "increment": 0.1 - } - ] - }, - "includewinners": true, - "includebidderkeys": true, - "includebrandcategory": { - "primaryadserver": 1, - "publisher": "", - "withcategory": true - }, - "appendbiddernames": true - } - } - } - } - } - } - } -} \ No newline at end of file diff --git a/exchange/exchangetest/bid_response_validation_enforce_one_bid_rejected.json b/exchange/exchangetest/bid_response_validation_enforce_one_bid_rejected.json index ea48f3a966f..f82b7a56c0f 100644 --- a/exchange/exchangetest/bid_response_validation_enforce_one_bid_rejected.json +++ b/exchange/exchangetest/bid_response_validation_enforce_one_bid_rejected.json @@ -173,6 +173,9 @@ "ext": { "origbidcpm": 0.4, "prebid": { + "meta": { + "adaptercode":"rubicon" + }, "type": "banner" } } @@ -189,6 +192,35 @@ "message": "bidResponse rejected: size WxH" } ] + }, + "prebid": { + "seatnonbid": [ + { + "nonbid": [ + { + "impid": "some-imp-id", + "statuscode": 351, + "ext": { + "prebid": { + "bid": { + "price": 0.3, + "w": 200, + "h": 500, + "origbidcpm": 0.3, + "id": "apn-bid", + "type": "banner", + "meta": { + "adaptercode":"appnexus" + } + } + } + } + } + ], + "seat": "appnexus", + "ext": null + } + ] } } } diff --git a/exchange/exchangetest/bid_response_validation_enforce_secure.json b/exchange/exchangetest/bid_response_validation_enforce_secure.json index e036f82a1e3..773f9b9881c 100644 --- a/exchange/exchangetest/bid_response_validation_enforce_secure.json +++ b/exchange/exchangetest/bid_response_validation_enforce_secure.json @@ -183,6 +183,50 @@ } ] } + }, + "prebid": { + "seatnonbid": [ + { + "nonbid": [ + { + "impid": "some-imp-id", + "statuscode": 352, + "ext": { + "prebid": { + "bid": { + "price": 0.3, + "w": 20, + "h": 50, + "origbidcpm": 0.3 + } + } + } + } + ], + "seat": "appnexus", + "ext": null + }, + { + "nonbid": [ + { + "impid": "some-imp-id", + "statuscode": 352, + "ext": { + "prebid": { + "bid": { + "price": 0.4, + "w": 20, + "h": 50, + "origbidcpm": 0.4 + } + } + } + } + ], + "seat": "rubicon", + "ext": null + } + ] } } } \ No newline at end of file diff --git a/exchange/exchangetest/bid_response_validation_warn_creative.json b/exchange/exchangetest/bid_response_validation_warn_creative.json index a170eac778e..47546683f05 100644 --- a/exchange/exchangetest/bid_response_validation_warn_creative.json +++ b/exchange/exchangetest/bid_response_validation_warn_creative.json @@ -169,6 +169,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode":"appnexus" + }, "type": "banner" } } @@ -188,6 +191,9 @@ "ext": { "origbidcpm": 0.4, "prebid": { + "meta": { + "adaptercode":"rubicon" + }, "type": "banner" } } diff --git a/exchange/exchangetest/bid_response_validation_warn_secure.json b/exchange/exchangetest/bid_response_validation_warn_secure.json index a6b208d2f0d..3c06f695970 100644 --- a/exchange/exchangetest/bid_response_validation_warn_secure.json +++ b/exchange/exchangetest/bid_response_validation_warn_secure.json @@ -172,6 +172,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode":"appnexus" + }, "type": "banner" } } @@ -192,6 +195,9 @@ "ext": { "origbidcpm": 0.4, "prebid": { + "meta": { + "adaptercode":"rubicon" + }, "type": "banner" } } diff --git a/exchange/exchangetest/buyeruid_case_insensitive.json b/exchange/exchangetest/buyeruid_case_insensitive.json new file mode 100644 index 00000000000..6999e8c9515 --- /dev/null +++ b/exchange/exchangetest/buyeruid_case_insensitive.json @@ -0,0 +1,144 @@ +{ + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "user": { + "id": "userId", + "ext": { + "prebid": { + "buyeruids": { + "APPnexUS": "12345" + } + } + } + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "APPnexus": { + "placementId": 1 + }, + "appNEXUS": { + "placementId": 2 + }, + "amx": {} + } + } + } + } + ], + "ext": { + "prebid": { + "aliases": { + "APPnexus": "appnexus", + "appNEXUS": "appnexus" + } + } + } + } + }, + "outgoingRequests": { + "APPnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "user": { + "id": "userId", + "buyeruid": "12345" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + } + ] + } + } + }, + "appNEXUS": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "user": { + "id": "userId", + "buyeruid": "12345" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "placementId": 2 + } + } + } + ] + } + } + }, + "amx": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "user": { + "id": "userId" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + } + } + } + ] + } + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "ext": {} + } + } +} diff --git a/exchange/exchangetest/debuglog_disabled.json b/exchange/exchangetest/debuglog_disabled.json index d803759d1d9..56beef34542 100644 --- a/exchange/exchangetest/debuglog_disabled.json +++ b/exchange/exchangetest/debuglog_disabled.json @@ -145,6 +145,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "targeting": { "hb_bidder": "appnexus", @@ -171,6 +174,9 @@ "ext": { "origbidcpm": 0.6, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_bidder_appnexus": "appnexus", @@ -269,4 +275,4 @@ } } } -} +} \ No newline at end of file diff --git a/exchange/exchangetest/debuglog_enabled.json b/exchange/exchangetest/debuglog_enabled.json index 054737e653e..8bcf4bab757 100644 --- a/exchange/exchangetest/debuglog_enabled.json +++ b/exchange/exchangetest/debuglog_enabled.json @@ -147,6 +147,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "targeting": { "hb_bidder": "appnexus", @@ -173,6 +176,9 @@ "ext": { "origbidcpm": 0.6, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_bidder_appnexus": "appnexus", @@ -271,4 +277,4 @@ } } } -} +} \ No newline at end of file diff --git a/exchange/exchangetest/dsa-not-required.json b/exchange/exchangetest/dsa-not-required.json new file mode 100644 index 00000000000..f2a1f821e2f --- /dev/null +++ b/exchange/exchangetest/dsa-not-required.json @@ -0,0 +1,134 @@ +{ + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 1 + } + } + } + } + }], + "regs": { + "ext": { + "dsa": { + "dsarequired": 1, + "pubrender": 0, + "datatopub": 2, + "transparency": [{ + "domain": "platform1domain.com", + "dsaparams": [1] + }, + { + "domain": "SSP2domain.com", + "dsaparams": [1, 2] + } + ] + } + } + } + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }], + "regs": { + "ext": { + "dsa": { + "dsarequired": 1, + "pubrender": 0, + "datatopub": 2, + "transparency": [{ + "domain": "platform1domain.com", + "dsaparams": [1] + }, + { + "domain": "SSP2domain.com", + "dsaparams": [1, 2] + } + ] + } + } + } + } + }, + "mockResponse": { + "pbsSeatBids": [{ + "pbsBids": [{ + "ortbBid": { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "ext": { + "someField": "someValue", + "origbidcpm": 0.3 + } + }, + "bidType": "video" + }], + "seat": "appnexus" + }] + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [{ + "seat": "appnexus", + "bid": [{ + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "ext": { + "someField": "someValue", + "origbidcpm": 0.3, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "type": "video" + } + } + }] + }] + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/dsa-required.json b/exchange/exchangetest/dsa-required.json new file mode 100644 index 00000000000..f2a1f821e2f --- /dev/null +++ b/exchange/exchangetest/dsa-required.json @@ -0,0 +1,134 @@ +{ + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 1 + } + } + } + } + }], + "regs": { + "ext": { + "dsa": { + "dsarequired": 1, + "pubrender": 0, + "datatopub": 2, + "transparency": [{ + "domain": "platform1domain.com", + "dsaparams": [1] + }, + { + "domain": "SSP2domain.com", + "dsaparams": [1, 2] + } + ] + } + } + } + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [{ + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }], + "regs": { + "ext": { + "dsa": { + "dsarequired": 1, + "pubrender": 0, + "datatopub": 2, + "transparency": [{ + "domain": "platform1domain.com", + "dsaparams": [1] + }, + { + "domain": "SSP2domain.com", + "dsaparams": [1, 2] + } + ] + } + } + } + } + }, + "mockResponse": { + "pbsSeatBids": [{ + "pbsBids": [{ + "ortbBid": { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "ext": { + "someField": "someValue", + "origbidcpm": 0.3 + } + }, + "bidType": "video" + }], + "seat": "appnexus" + }] + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [{ + "seat": "appnexus", + "bid": [{ + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "ext": { + "someField": "someValue", + "origbidcpm": 0.3, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "type": "video" + } + } + }] + }] + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/eidpermissions-allowed-alias.json b/exchange/exchangetest/eidpermissions-allowed-alias.json index f10bdcf86c0..3dfe441567e 100644 --- a/exchange/exchangetest/eidpermissions-allowed-alias.json +++ b/exchange/exchangetest/eidpermissions-allowed-alias.json @@ -10,7 +10,11 @@ "eids": [ { "source": "source1", - "id": "anyId" + "uids": [ + { + "id": "id1" + } + ] } ] } @@ -66,7 +70,11 @@ "eids": [ { "source": "source1", - "id": "anyId" + "uids": [ + { + "id": "id1" + } + ] } ] } @@ -127,6 +135,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "foo" + }, "type": "video" } } diff --git a/exchange/exchangetest/eidpermissions-allowed-case-insensitive.json b/exchange/exchangetest/eidpermissions-allowed-case-insensitive.json new file mode 100644 index 00000000000..459bb015e25 --- /dev/null +++ b/exchange/exchangetest/eidpermissions-allowed-case-insensitive.json @@ -0,0 +1,147 @@ +{ + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "user": { + "ext": { + "eids": [ + { + "source": "source1", + "uids": [ + { + "id": "id1" + } + ] + } + ] + } + }, + "ext": { + "prebid": { + "data": { + "eidpermissions": [ + { + "source": "source1", + "bidders": [ + "APPNEXUS" + ] + } + ] + } + } + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 1 + } + } + } + } + } + ] + } + }, + "outgoingRequests": { + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "user": { + "ext": { + "eids": [ + { + "source": "source1", + "uids": [ + { + "id": "id1" + } + ] + } + ] + } + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + } + ] + } + }, + "mockResponse": { + "pbsSeatBids": [ + { + "pbsBids": [ + { + "ortbBid": { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1" + }, + "bidType": "video" + } + ], + "seat": "appnexus" + } + ] + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [ + { + "seat": "appnexus", + "bid": [ + { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "ext": { + "origbidcpm": 0.3, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "type": "video" + } + } + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/eidpermissions-allowed.json b/exchange/exchangetest/eidpermissions-allowed.json index 6b27212cbcf..06b2d184bf1 100644 --- a/exchange/exchangetest/eidpermissions-allowed.json +++ b/exchange/exchangetest/eidpermissions-allowed.json @@ -10,7 +10,11 @@ "eids": [ { "source": "source1", - "id": "anyId" + "uids": [ + { + "id": "id1" + } + ] } ] } @@ -63,7 +67,11 @@ "eids": [ { "source": "source1", - "id": "anyId" + "uids": [ + { + "id": "id1" + } + ] } ] } @@ -124,6 +132,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video" } } diff --git a/exchange/exchangetest/eidpermissions-denied.json b/exchange/exchangetest/eidpermissions-denied.json index df112905235..72431595fe3 100644 --- a/exchange/exchangetest/eidpermissions-denied.json +++ b/exchange/exchangetest/eidpermissions-denied.json @@ -10,7 +10,11 @@ "eids": [ { "source": "source1", - "id": "anyId" + "uids": [ + { + "id": "id1" + } + ] } ] } @@ -115,6 +119,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video" } } diff --git a/exchange/exchangetest/events-bid-account-off-request-off.json b/exchange/exchangetest/events-bid-account-off-request-off.json index b15b33a8653..4ae1881b865 100644 --- a/exchange/exchangetest/events-bid-account-off-request-off.json +++ b/exchange/exchangetest/events-bid-account-off-request-off.json @@ -79,6 +79,9 @@ "ext": { "origbidcpm": 0.71, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "banner" } } @@ -93,6 +96,9 @@ "ext": { "origbidcpm": 0.21, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "banner" } } diff --git a/exchange/exchangetest/events-bid-account-off-request-on.json b/exchange/exchangetest/events-bid-account-off-request-on.json index 8a6b8aa7e14..20606002619 100644 --- a/exchange/exchangetest/events-bid-account-off-request-on.json +++ b/exchange/exchangetest/events-bid-account-off-request-on.json @@ -82,6 +82,9 @@ "ext": { "origbidcpm": 0.71, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "banner", "events": { "imp": "http://localhost/event?t=imp&b=winning-bid&a=testaccount&bidder=appnexus&int=testIntegrationType&ts=1234567890", @@ -100,6 +103,9 @@ "ext": { "origbidcpm": 0.21, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "banner", "events": { "imp": "http://localhost/event?t=imp&b=losing-bid&a=testaccount&bidder=appnexus&int=testIntegrationType&ts=1234567890", diff --git a/exchange/exchangetest/events-bid-account-on-request-off.json b/exchange/exchangetest/events-bid-account-on-request-off.json index 5a2fa6ae4d2..0447a2240e3 100644 --- a/exchange/exchangetest/events-bid-account-on-request-off.json +++ b/exchange/exchangetest/events-bid-account-on-request-off.json @@ -81,6 +81,9 @@ "ext": { "origbidcpm": 0.71, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "banner", "events": { "imp": "http://localhost/event?t=imp&b=winning-bid&a=testaccount&bidder=appnexus&int=testIntegrationType&ts=1234567890", @@ -99,6 +102,9 @@ "ext": { "origbidcpm": 0.21, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "banner", "events": { "imp": "http://localhost/event?t=imp&b=losing-bid&a=testaccount&bidder=appnexus&int=testIntegrationType&ts=1234567890", diff --git a/exchange/exchangetest/events-vast-account-off-request-off.json b/exchange/exchangetest/events-vast-account-off-request-off.json index a94546d2aa0..af07abd1e70 100644 --- a/exchange/exchangetest/events-vast-account-off-request-off.json +++ b/exchange/exchangetest/events-vast-account-off-request-off.json @@ -125,6 +125,9 @@ "ext": { "origbidcpm": 0.51, "prebid": { + "meta": { + "adaptercode": "audienceNetwork" + }, "type": "video" } } @@ -145,6 +148,9 @@ "ext": { "origbidcpm": 0.71, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "targeting": { "hb_bidder": "appnexus", @@ -168,6 +174,9 @@ "ext": { "origbidcpm": 0.21, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video" } } @@ -177,4 +186,4 @@ ] } } -} +} \ No newline at end of file diff --git a/exchange/exchangetest/events-vast-account-off-request-on.json b/exchange/exchangetest/events-vast-account-off-request-on.json index 275b8aef2ce..af2832474cb 100644 --- a/exchange/exchangetest/events-vast-account-off-request-on.json +++ b/exchange/exchangetest/events-vast-account-off-request-on.json @@ -131,7 +131,10 @@ "ext": { "origbidcpm": 0.51, "prebid": { - "bidid": "mock_uuid", + "meta": { + "adaptercode": "audienceNetwork" + }, + "bidid": "bid-audienceNetwork-1", "type": "video" } } @@ -143,7 +146,7 @@ "bid": [ { "id": "winning-bid", - "adm": "prebid.org wrapper", + "adm": "prebid.org wrapper", "nurl": "http://domain.com/winning-bid", "impid": "my-imp-id", "price": 0.71, @@ -153,7 +156,10 @@ "ext": { "origbidcpm": 0.71, "prebid": { - "bidid": "mock_uuid", + "meta": { + "adaptercode": "appnexus" + }, + "bidid": "bid-appnexus-1", "type": "video", "targeting": { "hb_bidder": "appnexus", @@ -168,7 +174,7 @@ }, { "id": "losing-bid", - "adm": "prebid.org wrapper", + "adm": "prebid.org wrapper", "nurl": "http://domain.com/losing-bid", "impid": "my-imp-id", "price": 0.21, @@ -178,7 +184,10 @@ "ext": { "origbidcpm": 0.21, "prebid": { - "bidid": "mock_uuid", + "meta": { + "adaptercode": "appnexus" + }, + "bidid": "bid-appnexus-2", "type": "video" } } @@ -188,4 +197,4 @@ ] } } -} +} \ No newline at end of file diff --git a/exchange/exchangetest/events-vast-account-on-request-off.json b/exchange/exchangetest/events-vast-account-on-request-off.json index e271ecf5c8f..d6f813364e4 100644 --- a/exchange/exchangetest/events-vast-account-on-request-off.json +++ b/exchange/exchangetest/events-vast-account-on-request-off.json @@ -126,6 +126,9 @@ "ext": { "origbidcpm": 0.51, "prebid": { + "meta": { + "adaptercode": "audienceNetwork" + }, "type": "video" } } @@ -147,6 +150,9 @@ "ext": { "origbidcpm": 0.71, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "targeting": { "hb_bidder": "appnexus", @@ -171,6 +177,9 @@ "ext": { "origbidcpm": 0.21, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video" } } @@ -180,4 +189,4 @@ ] } } -} +} \ No newline at end of file diff --git a/exchange/exchangetest/extra-bids-with-aliases-adaptercode.json b/exchange/exchangetest/extra-bids-with-aliases-adaptercode.json index b46f54c6408..8da9a29095a 100644 --- a/exchange/exchangetest/extra-bids-with-aliases-adaptercode.json +++ b/exchange/exchangetest/extra-bids-with-aliases-adaptercode.json @@ -185,7 +185,7 @@ "origbidcpm": 0.71, "prebid": { "meta": { - "adaptercode": "pmbidder" + "adaptercode": "groupm" }, "type": "video", "targeting": { @@ -214,7 +214,7 @@ "origbidcpm": 0.21, "prebid": { "meta": { - "adaptercode": "pmbidder" + "adaptercode": "groupm" }, "type": "video" } @@ -231,7 +231,7 @@ "origbidcpm": 0.61, "prebid": { "meta": { - "adaptercode": "pmbidder" + "adaptercode": "groupm" }, "type": "video", "targeting": { diff --git a/exchange/exchangetest/extra-bids.json b/exchange/exchangetest/extra-bids.json index 23e122593b5..edf2e60b124 100644 --- a/exchange/exchangetest/extra-bids.json +++ b/exchange/exchangetest/extra-bids.json @@ -309,7 +309,7 @@ "origbidcpm": 0.51, "prebid": { "meta": { - "adaptercode": "pubmatic" + "adaptercode": "groupm" }, "type": "video", "targeting": { @@ -333,7 +333,7 @@ "origbidcpm": 0.3, "prebid": { "meta": { - "adaptercode": "appnexus" + "adaptercode": "groupm" }, "type": "banner" } diff --git a/exchange/exchangetest/firstpartydata-amp-imp-ext-one-prebid-bidder.json b/exchange/exchangetest/firstpartydata-amp-imp-ext-one-prebid-bidder.json index 627c95f9d54..22e0fb8229f 100644 --- a/exchange/exchangetest/firstpartydata-amp-imp-ext-one-prebid-bidder.json +++ b/exchange/exchangetest/firstpartydata-amp-imp-ext-one-prebid-bidder.json @@ -121,6 +121,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "banner" } } diff --git a/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json b/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json index f15fea59d87..37d5ade5c45 100644 --- a/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json +++ b/exchange/exchangetest/firstpartydata-imp-ext-multiple-prebid-bidders.json @@ -188,6 +188,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "banner" } } @@ -207,6 +210,9 @@ "ext": { "origbidcpm": 0.4, "prebid": { + "meta": { + "adaptercode": "rubicon" + }, "type": "banner" } } diff --git a/exchange/exchangetest/firstpartydata-imp-ext-one-prebid-bidder.json b/exchange/exchangetest/firstpartydata-imp-ext-one-prebid-bidder.json index 9eb6a77eed5..5a42a37baee 100644 --- a/exchange/exchangetest/firstpartydata-imp-ext-one-prebid-bidder.json +++ b/exchange/exchangetest/firstpartydata-imp-ext-one-prebid-bidder.json @@ -121,6 +121,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "banner" } } diff --git a/exchange/exchangetest/fledge-with-bids.json b/exchange/exchangetest/fledge-with-bids.json index 0f998e69ee0..df59c0d28c7 100644 --- a/exchange/exchangetest/fledge-with-bids.json +++ b/exchange/exchangetest/fledge-with-bids.json @@ -114,6 +114,9 @@ "origbidcpm": 0.3, "someField": "someValue", "prebid": { + "meta": { + "adaptercode": "openx" + }, "type": "video" } } diff --git a/exchange/exchangetest/floors_enforcement.json b/exchange/exchangetest/floors_enforcement.json index 90d25301e65..2124bcf2aac 100644 --- a/exchange/exchangetest/floors_enforcement.json +++ b/exchange/exchangetest/floors_enforcement.json @@ -136,6 +136,9 @@ "ext": { "origbidcpm": 12, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "floors": { "floorCurrency": "USD", "floorRule": "*|*", diff --git a/exchange/exchangetest/generate-bid-id-error.json b/exchange/exchangetest/generate-bid-id-error.json new file mode 100644 index 00000000000..9c13ef38895 --- /dev/null +++ b/exchange/exchangetest/generate-bid-id-error.json @@ -0,0 +1,200 @@ +{ + "bidIDGenerator": { + "generateBidID": true, + "returnError": true + }, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 1 + } + } + } + } + } + ], + "test": 1, + "ext": { + "prebid": { + "targeting": { + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "", + "withcategory": true + }, + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "min": 0, + "max": 20, + "increment": 0.1 + } + ] + }, + "includewinners": true, + "includebidderkeys": true, + "appendbiddernames": true + } + } + } + }, + "usersyncs": { + "appnexus": "123" + } + }, + "outgoingRequests": { + "appnexus": { + "mockResponse": { + "pbsSeatBids": [ + { + "pbsBids": [ + { + "ortbBid": { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": [ + "IAB1-1" + ] + }, + "bidType": "video", + "bidVideo": { + "duration": 30, + "PrimaryCategory": "" + } + } + ], + "seat": "appnexus" + } + ] + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [ + { + "seat": "appnexus", + "bid": [ + { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": [ + "IAB1-1" + ], + "ext": { + "origbidcpm": 0.3, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "type": "video", + "targeting": { + "hb_bidder": "appnexus", + "hb_bidder_appnexus": "appnexus", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_appnex": "www.pbcserver.com", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_appnex": "/pbcache/endpoint", + "hb_pb": "0.20", + "hb_pb_appnexus": "0.20", + "hb_pb_cat_dur": "0.20_VideoGames_0s_appnexus", + "hb_pb_cat_dur_appnex": "0.20_VideoGames_0s_appnexus", + "hb_size": "200x250", + "hb_size_appnexus": "200x250" + } + } + } + } + ] + } + ] + }, + "ext": { + "debug": { + "resolvedrequest": { + "id": "some-request-id", + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 1 + } + } + } + } + } + ], + "site": { + "page": "test.somepage.com" + }, + "test": 1, + "ext": { + "prebid": { + "targeting": { + "includebrandcategory": { + "primaryadserver": 1, + "publisher": "", + "withcategory": true + }, + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "min": 0, + "max": 20, + "increment": 0.1 + } + ] + }, + "includewinners": true, + "includebidderkeys": true, + "appendbiddernames": true + } + } + } + } + }, + "errors": { + "prebid": [ + { + "code": 999, + "message": "Error generating bid.ext.prebid.bidid" + } + ] + } + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/generate-bid-id-many.json b/exchange/exchangetest/generate-bid-id-many.json new file mode 100644 index 00000000000..4c6a18ab174 --- /dev/null +++ b/exchange/exchangetest/generate-bid-id-many.json @@ -0,0 +1,163 @@ +{ + "bidIDGenerator": { + "generateBidID": true, + "returnError": false + }, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "imp-id-1", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 1 + } + } + } + } + } + ] + } + }, + "outgoingRequests": { + "appnexus": { + "mockResponse": { + "pbsSeatBids": [ + { + "pbsBids": [ + { + "ortbBid": { + "id": "apn-bid-1", + "impid": "imp-id-1", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": [ + "IAB1-1" + ] + }, + "bidType": "video", + "bidVideo": { + "duration": 30 + } + }, + { + "ortbBid": { + "id": "apn-bid-2", + "impid": "imp-id-1", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": [ + "IAB1-1" + ] + }, + "bidType": "video", + "bidVideo": { + "duration": 30 + } + } + ], + "seat": "appnexus" + } + ] + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [ + { + "seat": "appnexus", + "bid": [ + { + "id": "apn-bid-1", + "impid": "imp-id-1", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": [ + "IAB1-1" + ], + "ext": { + "origbidcpm": 0.3, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "bidid": "bid-appnexus-1", + "type": "video" + } + } + }, + { + "id": "apn-bid-2", + "impid": "imp-id-1", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": [ + "IAB1-1" + ], + "ext": { + "origbidcpm": 0.3, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "bidid": "bid-appnexus-2", + "type": "video" + } + } + } + ] + } + ] + }, + "ext": { + "debug": { + "resolvedrequest": { + "id": "some-request-id", + "imp": [ + { + "id": "imp-id-1", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 1 + } + } + } + } + } + ], + "site": { + "page": "test.somepage.com" + } + } + } + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/generate-bid-id-one.json b/exchange/exchangetest/generate-bid-id-one.json new file mode 100644 index 00000000000..7d4169d635d --- /dev/null +++ b/exchange/exchangetest/generate-bid-id-one.json @@ -0,0 +1,125 @@ +{ + "bidIDGenerator": { + "generateBidID": true, + "returnError": false + }, + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "imp-id-1", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 1 + } + } + } + } + } + ] + } + }, + "outgoingRequests": { + "appnexus": { + "mockResponse": { + "pbsSeatBids": [ + { + "pbsBids": [ + { + "ortbBid": { + "id": "apn-bid", + "impid": "imp-id-1", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": [ + "IAB1-1" + ] + }, + "bidType": "video", + "bidVideo": { + "duration": 30 + } + } + ], + "seat": "appnexus" + } + ] + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [ + { + "seat": "appnexus", + "bid": [ + { + "id": "apn-bid", + "impid": "imp-id-1", + "price": 0.3, + "w": 200, + "h": 250, + "crid": "creative-1", + "cat": [ + "IAB1-1" + ], + "ext": { + "origbidcpm": 0.3, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "bidid": "bid-appnexus-1", + "type": "video" + } + } + } + ] + } + ] + }, + "ext": { + "debug": { + "resolvedrequest": { + "id": "some-request-id", + "imp": [ + { + "id": "imp-id-1", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 1 + } + } + } + } + } + ], + "site": { + "page": "test.somepage.com" + } + } + } + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/include-brand-category.json b/exchange/exchangetest/include-brand-category.json index 9d537f74902..4031e4cf45a 100644 --- a/exchange/exchangetest/include-brand-category.json +++ b/exchange/exchangetest/include-brand-category.json @@ -135,6 +135,9 @@ "origbidcpm": 0.3, "prebid": { "type": "video", + "meta": { + "adaptercode": "appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_bidder_appnexus": "appnexus", @@ -160,6 +163,9 @@ "ext": { "origbidcpm": 0.6, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_bidder_appnexus": "appnexus", diff --git a/exchange/exchangetest/mediatypepricegranularity-banner-video-native.json b/exchange/exchangetest/mediatypepricegranularity-banner-video-native.json index 7d119b23044..218030d77f6 100644 --- a/exchange/exchangetest/mediatypepricegranularity-banner-video-native.json +++ b/exchange/exchangetest/mediatypepricegranularity-banner-video-native.json @@ -197,6 +197,9 @@ "origbidcpm": 15, "prebid": { "type": "banner", + "meta": { + "adaptercode": "appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_bidder_appnexus": "appnexus", @@ -223,6 +226,9 @@ "ext": { "origbidcpm": 18, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_bidder_appnexus": "appnexus", @@ -255,6 +261,9 @@ "ext": { "origbidcpm": 29, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_bidder_appnexus": "appnexus", diff --git a/exchange/exchangetest/mediatypepricegranularity-native.json b/exchange/exchangetest/mediatypepricegranularity-native.json index 57a318e1fcf..e454b500722 100644 --- a/exchange/exchangetest/mediatypepricegranularity-native.json +++ b/exchange/exchangetest/mediatypepricegranularity-native.json @@ -164,6 +164,9 @@ "ext": { "origbidcpm": 24, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "native", "targeting": { "hb_bidder": "appnexus", @@ -191,6 +194,9 @@ "ext": { "origbidcpm": 29, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_bidder_appnexus": "appnexus", diff --git a/exchange/exchangetest/multi-bids-mixed-case.json b/exchange/exchangetest/multi-bids-mixed-case.json new file mode 100644 index 00000000000..44630b3068c --- /dev/null +++ b/exchange/exchangetest/multi-bids-mixed-case.json @@ -0,0 +1,449 @@ +{ + "description": "incoming req.ext.prebid.multibid comes with a mixed case bidder name. Expect the same bidder name casing in the outgoing ext.prebid.multibid field", + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "pubmatic": { + "publisherId": "5890" + }, + "appnexus": { + "placementId": 1 + } + } + } + } + }, + { + "id": "imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "pubmatic": { + "publisherId": "5890" + }, + "appnexus": { + "placementId": 1 + } + } + } + } + } + ], + "ext": { + "prebid": { + "targeting": { + "includewinners": true, + "includebidderkeys": true, + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "min": 0, + "max": 20, + "increment": 0.1 + } + ] + } + }, + "multibid": [ + { + "bidder": "PUBmatic", + "maxbids": 2, + "targetbiddercodeprefix": "pubm" + }, + { + "bidders": [ + "appnexus", + "someBidder" + ], + "maxbids": 2 + } + ] + } + } + } + }, + "outgoingRequests": { + "pubmatic": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "publisherId": "5890" + } + } + }, + { + "id": "imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "publisherId": "5890" + } + } + } + ], + "ext": { + "prebid": { + "multibid": [ + { + "bidder": "PUBmatic", + "maxbids": 2, + "targetbiddercodeprefix": "pubm" + } + ] + } + } + } + }, + "mockResponse": { + "pbsSeatBids": [ + { + "pbsBids": [ + { + "ortbBid": { + "id": "winning-bid", + "impid": "my-imp-id", + "price": 0.71, + "w": 200, + "h": 250, + "crid": "creative-1" + }, + "bidType": "video", + "bidMeta": { + "adaptercode": "pubmatic" + } + }, + { + "ortbBid": { + "id": "losing-bid", + "impid": "my-imp-id", + "price": 0.21, + "w": 200, + "h": 250, + "crid": "creative-2" + }, + "bidType": "video", + "bidMeta": { + "adaptercode": "pubmatic" + } + }, + { + "ortbBid": { + "id": "other-bid", + "impid": "imp-id-2", + "price": 0.61, + "w": 300, + "h": 500, + "crid": "creative-3" + }, + "bidType": "video", + "bidMeta": { + "adaptercode": "pubmatic" + } + }, + { + "ortbBid": { + "id": "contending-bid", + "impid": "my-imp-id", + "price": 0.51, + "w": 200, + "h": 250, + "crid": "creative-4" + }, + "bidType": "video", + "bidMeta": { + "adaptercode": "pubmatic" + } + } + ], + "seat": "pubmatic" + } + ] + } + }, + "appnexus": { + "expectRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + }, + { + "id": "imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "placementId": 1 + } + } + } + ], + "ext": { + "prebid": { + "multibid": [ + { + "bidders": [ + "appnexus" + ], + "maxbids": 2 + } + ] + } + } + } + }, + "mockResponse": { + "pbsSeatBids": [ + { + "pbsBids": [ + { + "ortbBid": { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 500, + "crid": "creative-a-1" + }, + "bidType": "banner", + "bidMeta": { + "adaptercode": "appnexus" + } + }, + { + "ortbBid": { + "id": "apn-bid-2", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 500, + "crid": "creative-a-2" + }, + "bidType": "banner", + "bidMeta": { + "adaptercode": "appnexus" + } + } + ], + "seat": "appnexus" + } + ] + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [ + { + "seat": "pubmatic", + "bid": [ + { + "id": "winning-bid", + "impid": "my-imp-id", + "price": 0.71, + "w": 200, + "h": 250, + "crid": "creative-1", + "ext": { + "origbidcpm": 0.71, + "prebid": { + "meta": { + "adaptercode": "pubmatic" + }, + "type": "video", + "targeting": { + "hb_bidder": "pubmatic", + "hb_bidder_pubmatic": "pubmatic", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_pubmat": "www.pbcserver.com", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_pubmat": "/pbcache/endpoint", + "hb_pb": "0.70", + "hb_pb_pubmatic": "0.70", + "hb_size": "200x250", + "hb_size_pubmatic": "200x250" + }, + "targetbiddercode": "pubmatic" + } + } + }, + { + "id": "losing-bid", + "impid": "my-imp-id", + "price": 0.21, + "w": 200, + "h": 250, + "crid": "creative-2", + "ext": { + "origbidcpm": 0.21, + "prebid": { + "meta": { + "adaptercode": "pubmatic" + }, + "type": "video" + } + } + }, + { + "id": "other-bid", + "impid": "imp-id-2", + "price": 0.61, + "w": 300, + "h": 500, + "crid": "creative-3", + "ext": { + "origbidcpm": 0.61, + "prebid": { + "meta": { + "adaptercode": "pubmatic" + }, + "type": "video", + "targeting": { + "hb_bidder": "pubmatic", + "hb_bidder_pubmatic": "pubmatic", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_pubmat": "www.pbcserver.com", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_pubmat": "/pbcache/endpoint", + "hb_pb": "0.60", + "hb_pb_pubmatic": "0.60", + "hb_size": "300x500", + "hb_size_pubmatic": "300x500" + }, + "targetbiddercode": "pubmatic" + } + } + }, + { + "id": "contending-bid", + "impid": "my-imp-id", + "price": 0.51, + "w": 200, + "h": 250, + "crid": "creative-4", + "ext": { + "origbidcpm": 0.51, + "prebid": { + "meta": { + "adaptercode": "pubmatic" + }, + "type": "video", + "targeting": { + "hb_bidder_pubm2": "pubm2", + "hb_cache_host_pubm2": "www.pbcserver.com", + "hb_cache_path_pubm2": "/pbcache/endpoint", + "hb_pb_pubm2": "0.50", + "hb_size_pubm2": "200x250" + }, + "targetbiddercode": "pubm2" + } + } + } + ] + }, + { + "seat": "appnexus", + "bid": [ + { + "id": "apn-bid", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 500, + "crid": "creative-a-1", + "ext": { + "origbidcpm": 0.3, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "targetbiddercode": "appnexus", + "type": "banner", + "targeting": { + "hb_bidder_appnexus": "appnexus", + "hb_cache_host_appnex": "www.pbcserver.com", + "hb_cache_path_appnex": "/pbcache/endpoint", + "hb_pb_appnexus": "0.20", + "hb_size_appnexus": "200x500" + } + } + } + }, + { + "id": "apn-bid-2", + "impid": "my-imp-id", + "price": 0.3, + "w": 200, + "h": 500, + "crid": "creative-a-2", + "ext": { + "origbidcpm": 0.3, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "type": "banner" + } + } + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/passthrough_imp_only.json b/exchange/exchangetest/passthrough_imp_only.json index b7b2270270b..bc3d8dd22de 100644 --- a/exchange/exchangetest/passthrough_imp_only.json +++ b/exchange/exchangetest/passthrough_imp_only.json @@ -144,6 +144,9 @@ "someField": "someValue", "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "passthrough": { "imp_passthrough_val": 20 @@ -162,6 +165,9 @@ "someField": "someValue", "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video" } } diff --git a/exchange/exchangetest/passthrough_root_and_imp.json b/exchange/exchangetest/passthrough_root_and_imp.json index 7d4fe89b6d2..959b934f857 100644 --- a/exchange/exchangetest/passthrough_root_and_imp.json +++ b/exchange/exchangetest/passthrough_root_and_imp.json @@ -106,6 +106,9 @@ "someField": "someValue", "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "passthrough": { "imp_passthrough_val": 20 diff --git a/exchange/exchangetest/passthrough_root_only.json b/exchange/exchangetest/passthrough_root_only.json index 0b444535348..254b09adc0e 100644 --- a/exchange/exchangetest/passthrough_root_only.json +++ b/exchange/exchangetest/passthrough_root_only.json @@ -101,6 +101,9 @@ "crid": "creative-1", "ext": { "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video" }, "someField": "someValue", diff --git a/exchange/exchangetest/request-ext-prebid-filtering.json b/exchange/exchangetest/request-ext-prebid-filtering.json index 8e85d8edfdc..8a82f4cb8ab 100644 --- a/exchange/exchangetest/request-ext-prebid-filtering.json +++ b/exchange/exchangetest/request-ext-prebid-filtering.json @@ -174,6 +174,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video" } } diff --git a/exchange/exchangetest/request-imp-ext-prebid-filtering.json b/exchange/exchangetest/request-imp-ext-prebid-filtering.json index fb8e0892aa5..94ac6ec057f 100644 --- a/exchange/exchangetest/request-imp-ext-prebid-filtering.json +++ b/exchange/exchangetest/request-imp-ext-prebid-filtering.json @@ -124,6 +124,9 @@ "ext": { "origbidcpm": 0.3, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video" } } diff --git a/exchange/exchangetest/request-multi-bidders-debug-info.json b/exchange/exchangetest/request-multi-bidders-debug-info.json index 8f41286b3eb..b1ba42896d7 100644 --- a/exchange/exchangetest/request-multi-bidders-debug-info.json +++ b/exchange/exchangetest/request-multi-bidders-debug-info.json @@ -154,6 +154,9 @@ "ext": { "origbidcpm": 12.00, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_bidder_appnexus": "appnexus", diff --git a/exchange/exchangetest/request-multi-bidders-one-no-resp.json b/exchange/exchangetest/request-multi-bidders-one-no-resp.json index c1fe595ad5f..bfc310f3e55 100644 --- a/exchange/exchangetest/request-multi-bidders-one-no-resp.json +++ b/exchange/exchangetest/request-multi-bidders-one-no-resp.json @@ -126,6 +126,9 @@ "ext": { "origbidcpm": 12.00, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_bidder_appnexus": "appnexus", diff --git a/exchange/exchangetest/targeting-always-include-deals.json b/exchange/exchangetest/targeting-always-include-deals.json new file mode 100644 index 00000000000..b45f595849b --- /dev/null +++ b/exchange/exchangetest/targeting-always-include-deals.json @@ -0,0 +1,296 @@ +{ + "incomingRequest": { + "ortbRequest": { + "id": "some-request-id", + "site": { + "page": "test.somepage.com" + }, + "imp": [ + { + "id": "my-imp-id", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 1 + }, + "audienceNetwork": { + "placementId": "some-placement" + } + } + } + } + }, + { + "id": "imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "prebid": { + "bidder": { + "appnexus": { + "placementId": 2 + }, + "audienceNetwork": { + "placementId": "some-other-placement" + } + } + } + } + } + ], + "ext": { + "prebid": { + "targeting": { + "pricegranularity": { + "precision": 2, + "ranges": [ + { + "min": 0, + "max": 20, + "increment": 0.1 + } + ] + }, + "includewinners": true, + "includebidderkeys": false, + "alwaysincludedeals": true + } + } + } + } + }, + "outgoingRequests": { + "appnexus": { + "mockResponse": { + "pbsSeatBids": [ + { + "pbsBids": [ + { + "ortbBid": { + "id": "winning-bid", + "impid": "my-imp-id", + "price": 0.71, + "w": 200, + "h": 250, + "crid": "creative-1", + "dealid": "deal-1" + }, + "bidType": "video" + }, + { + "ortbBid": { + "id": "losing-bid", + "impid": "my-imp-id", + "price": 0.21, + "w": 200, + "h": 250, + "crid": "creative-2", + "dealid": "deal-2" + }, + "bidType": "video" + }, + { + "ortbBid": { + "id": "other-bid", + "impid": "imp-id-2", + "price": 0.61, + "w": 300, + "h": 500, + "crid": "creative-3", + "dealid": "deal-3" + }, + "bidType": "video" + } + ], + "seat": "appnexus" + } + ] + } + }, + "audienceNetwork": { + "mockResponse": { + "pbsSeatBids": [ + { + "pbsBids": [ + { + "ortbBid": { + "id": "contending-bid", + "impid": "my-imp-id", + "price": 0.51, + "w": 200, + "h": 250, + "crid": "creative-4", + "dealid": "deal-4" + }, + "bidType": "video" + }, + { + "ortbBid": { + "id": "losing-bid-aN", + "impid": "imp-id-2", + "price": 0.40, + "w": 200, + "h": 250, + "crid": "creative-5" + }, + "bidType": "video" + } + ], + "seat": "audienceNetwork" + } + ] + } + } + }, + "response": { + "bids": { + "id": "some-request-id", + "seatbid": [ + { + "seat": "audienceNetwork", + "bid": [ + { + "id": "contending-bid", + "impid": "my-imp-id", + "price": 0.51, + "w": 200, + "h": 250, + "crid": "creative-4", + "dealid": "deal-4", + "ext": { + "origbidcpm": 0.51, + "prebid": { + "meta": { + "adaptercode": "audienceNetwork" + }, + "type": "video", + "targeting": { + "hb_bidder_audienceNe": "audienceNetwork", + "hb_cache_host_audien": "www.pbcserver.com", + "hb_cache_path_audien": "/pbcache/endpoint", + "hb_deal_audienceNetw": "deal-4", + "hb_pb_audienceNetwor": "0.50", + "hb_size_audienceNetw": "200x250" + } + } + } + }, + { + "id": "losing-bid-aN", + "impid": "imp-id-2", + "price": 0.40, + "w": 200, + "h": 250, + "crid": "creative-5", + "ext": { + "origbidcpm": 0.40, + "prebid": { + "meta": { + "adaptercode": "audienceNetwork" + }, + "type": "video" + } + } + } + ] + }, + { + "seat": "appnexus", + "bid": [ + { + "id": "winning-bid", + "impid": "my-imp-id", + "price": 0.71, + "w": 200, + "h": 250, + "crid": "creative-1", + "dealid": "deal-1", + "ext": { + "origbidcpm": 0.71, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "type": "video", + "targeting": { + "hb_bidder": "appnexus", + "hb_bidder_appnexus": "appnexus", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_appnex": "www.pbcserver.com", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_appnex": "/pbcache/endpoint", + "hb_pb": "0.70", + "hb_pb_appnexus": "0.70", + "hb_deal":"deal-1", + "hb_deal_appnexus":"deal-1", + "hb_size": "200x250", + "hb_size_appnexus": "200x250" + } + } + } + }, + { + "id": "losing-bid", + "impid": "my-imp-id", + "price": 0.21, + "w": 200, + "h": 250, + "crid": "creative-2", + "dealid": "deal-2", + "ext": { + "origbidcpm": 0.21, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "type": "video" + } + } + }, + { + "id": "other-bid", + "impid": "imp-id-2", + "price": 0.61, + "w": 300, + "h": 500, + "crid": "creative-3", + "dealid": "deal-3", + "ext": { + "origbidcpm": 0.61, + "prebid": { + "meta": { + "adaptercode": "appnexus" + }, + "type": "video", + "targeting": { + "hb_bidder": "appnexus", + "hb_bidder_appnexus": "appnexus", + "hb_cache_host": "www.pbcserver.com", + "hb_cache_host_appnex": "www.pbcserver.com", + "hb_cache_path": "/pbcache/endpoint", + "hb_cache_path_appnex": "/pbcache/endpoint", + "hb_deal":"deal-3", + "hb_deal_appnexus":"deal-3", + "hb_pb": "0.60", + "hb_pb_appnexus": "0.60", + "hb_size": "300x500", + "hb_size_appnexus": "300x500" + } + } + } + } + ] + } + ] + } + } +} \ No newline at end of file diff --git a/exchange/exchangetest/targeting-cache-vast-banner.json b/exchange/exchangetest/targeting-cache-vast-banner.json index 43b65e59c12..aafd35fce35 100644 --- a/exchange/exchangetest/targeting-cache-vast-banner.json +++ b/exchange/exchangetest/targeting-cache-vast-banner.json @@ -87,6 +87,9 @@ "ext": { "origbidcpm": 0.01, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "banner", "targeting": { "hb_bidder": "appnexus", diff --git a/exchange/exchangetest/targeting-cache-vast.json b/exchange/exchangetest/targeting-cache-vast.json index 2f59ba30878..f196dc93895 100644 --- a/exchange/exchangetest/targeting-cache-vast.json +++ b/exchange/exchangetest/targeting-cache-vast.json @@ -88,6 +88,9 @@ "ext": { "origbidcpm": 0.01, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "cache": { "bids": { "cacheId": "0", diff --git a/exchange/exchangetest/targeting-cache-zero.json b/exchange/exchangetest/targeting-cache-zero.json index ea062fbe277..1bff4440933 100644 --- a/exchange/exchangetest/targeting-cache-zero.json +++ b/exchange/exchangetest/targeting-cache-zero.json @@ -90,6 +90,9 @@ "ext": { "origbidcpm": 0.01, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "cache": { "bids": { "cacheId": "0", diff --git a/exchange/exchangetest/targeting-mobile.json b/exchange/exchangetest/targeting-mobile.json index 914a557bca9..229a1bb14e0 100644 --- a/exchange/exchangetest/targeting-mobile.json +++ b/exchange/exchangetest/targeting-mobile.json @@ -152,6 +152,9 @@ "ext": { "origbidcpm": 0.51, "prebid": { + "meta": { + "adaptercode": "audienceNetwork" + }, "type": "video", "targeting": { "hb_bidder_audienceNe": "audienceNetwork", @@ -179,6 +182,9 @@ "ext": { "origbidcpm": 0.71, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "targeting": { "hb_bidder": "appnexus", @@ -207,6 +213,9 @@ "ext": { "origbidcpm": 0.21, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video" } } @@ -222,6 +231,9 @@ "origbidcpm": 0.61, "prebid": { "type": "video", + "meta": { + "adaptercode": "appnexus" + }, "targeting": { "hb_bidder": "appnexus", "hb_cache_host": "www.pbcserver.com", diff --git a/exchange/exchangetest/targeting-no-winners.json b/exchange/exchangetest/targeting-no-winners.json index 6ab69fb4773..92526022f38 100644 --- a/exchange/exchangetest/targeting-no-winners.json +++ b/exchange/exchangetest/targeting-no-winners.json @@ -152,6 +152,9 @@ "ext": { "origbidcpm": 0.51, "prebid": { + "meta": { + "adaptercode": "audienceNetwork" + }, "type": "video", "targeting": { "hb_bidder_audienceNe": "audienceNetwork", @@ -178,6 +181,9 @@ "ext": { "origbidcpm": 0.71, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "targeting": { "hb_bidder_appnexus": "appnexus", @@ -199,6 +205,9 @@ "ext": { "origbidcpm": 0.21, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video" } } @@ -213,6 +222,9 @@ "ext": { "origbidcpm": 0.61, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "targeting": { "hb_bidder_appnexus": "appnexus", diff --git a/exchange/exchangetest/targeting-only-winners.json b/exchange/exchangetest/targeting-only-winners.json index f9f96515452..5e6d43ce236 100644 --- a/exchange/exchangetest/targeting-only-winners.json +++ b/exchange/exchangetest/targeting-only-winners.json @@ -152,6 +152,9 @@ "ext": { "origbidcpm": 0.51, "prebid": { + "meta": { + "adaptercode": "audienceNetwork" + }, "type": "video" } } @@ -171,6 +174,9 @@ "ext": { "origbidcpm": 0.71, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "targeting": { "hb_bidder": "appnexus", @@ -192,6 +198,9 @@ "ext": { "origbidcpm": 0.21, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video" } } @@ -206,6 +215,9 @@ "ext": { "origbidcpm": 0.61, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "targeting": { "hb_bidder": "appnexus", diff --git a/exchange/exchangetest/targeting-with-winners.json b/exchange/exchangetest/targeting-with-winners.json index d1a0f49f862..14ad8ef4b4e 100644 --- a/exchange/exchangetest/targeting-with-winners.json +++ b/exchange/exchangetest/targeting-with-winners.json @@ -152,6 +152,9 @@ "ext": { "origbidcpm": 0.51, "prebid": { + "meta": { + "adaptercode": "audienceNetwork" + }, "type": "video", "targeting": { "hb_bidder_audienceNe": "audienceNetwork", @@ -178,6 +181,9 @@ "ext": { "origbidcpm": 0.71, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "targeting": { "hb_bidder": "appnexus", @@ -204,6 +210,9 @@ "ext": { "origbidcpm": 0.21, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video" } } @@ -218,6 +227,9 @@ "ext": { "origbidcpm": 0.61, "prebid": { + "meta": { + "adaptercode": "appnexus" + }, "type": "video", "targeting": { "hb_bidder": "appnexus", diff --git a/exchange/floors_ow.go b/exchange/floors_ow.go index 92ba516d82e..d4c12c71d27 100644 --- a/exchange/floors_ow.go +++ b/exchange/floors_ow.go @@ -1,8 +1,8 @@ package exchange import ( - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // floorsEnabled will return true if floors are enabled in both account and request level diff --git a/exchange/floors_ow_test.go b/exchange/floors_ow_test.go index 6e247c70a7e..8b20b31dc80 100644 --- a/exchange/floors_ow_test.go +++ b/exchange/floors_ow_test.go @@ -4,10 +4,10 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/boolutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/boolutil" "github.com/stretchr/testify/assert" ) diff --git a/exchange/gdpr.go b/exchange/gdpr.go index d503eb5da27..52fb860f5df 100644 --- a/exchange/gdpr.go +++ b/exchange/gdpr.go @@ -3,9 +3,9 @@ package exchange import ( gpplib "github.com/prebid/go-gpp" gppConstants "github.com/prebid/go-gpp/constants" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/openrtb_ext" - gppPolicy "github.com/prebid/prebid-server/privacy/gpp" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/openrtb_ext" + gppPolicy "github.com/prebid/prebid-server/v2/privacy/gpp" ) // getGDPR will pull the gdpr flag from an openrtb request diff --git a/exchange/gdpr_test.go b/exchange/gdpr_test.go index 44573b59167..c73112be6f3 100644 --- a/exchange/gdpr_test.go +++ b/exchange/gdpr_test.go @@ -6,9 +6,9 @@ import ( gpplib "github.com/prebid/go-gpp" gppConstants "github.com/prebid/go-gpp/constants" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -185,3 +185,7 @@ func (ms mockGPPSection) GetID() gppConstants.SectionID { func (ms mockGPPSection) GetValue() string { return ms.value } + +func (ms mockGPPSection) Encode(bool) []byte { + return nil +} diff --git a/exchange/non_bid_reason.go b/exchange/non_bid_reason.go index e405cec52ee..d817c2bca55 100644 --- a/exchange/non_bid_reason.go +++ b/exchange/non_bid_reason.go @@ -1,15 +1,17 @@ package exchange -import "github.com/prebid/openrtb/v19/openrtb3" +import "github.com/prebid/openrtb/v20/openrtb3" // SeatNonBid list the reasons why bid was not resulted in positive bid // reason could be either No bid, Error, Request rejection or Response rejection // Reference: https://github.com/InteractiveAdvertisingBureau/openrtb/blob/master/extensions/community_extensions/seat-non-bid.md const ( - ErrorGeneral openrtb3.NoBidReason = 100 // Error - General - ErrorTimeout openrtb3.NoBidReason = 101 // Error - Timeout - ErrorInvalidBidResponse openrtb3.NoBidReason = 102 // Error - Invalid Bid Response - ErrorBidderUnreachable openrtb3.NoBidReason = 103 // Error - Bidder Unreachable + NoBidUnknownError openrtb3.NoBidReason = 0 // No Bid - General + ResponseRejectedCategoryMappingInvalid openrtb3.NoBidReason = 303 // Response Rejected - Category Mapping Invalid + ErrorGeneral openrtb3.NoBidReason = 100 // Error - General + ErrorTimeout openrtb3.NoBidReason = 101 // Error - Timeout + ErrorInvalidBidResponse openrtb3.NoBidReason = 102 // Error - Invalid Bid Response + ErrorBidderUnreachable openrtb3.NoBidReason = 103 // Error - Bidder Unreachable ) const ( diff --git a/exchange/price_granularity.go b/exchange/price_granularity.go index c2ea9389575..267867a53c4 100644 --- a/exchange/price_granularity.go +++ b/exchange/price_granularity.go @@ -1,10 +1,11 @@ package exchange import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" "math" "strconv" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // GetPriceBucket is the externally facing function for computing CPM buckets diff --git a/exchange/price_granularity_test.go b/exchange/price_granularity_test.go index 6fc3c628f92..66d210f492e 100644 --- a/exchange/price_granularity_test.go +++ b/exchange/price_granularity_test.go @@ -5,9 +5,9 @@ import ( "math" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/exchange/seat_non_bids.go b/exchange/seat_non_bids.go index ed675d9706b..ee990105c1d 100644 --- a/exchange/seat_non_bids.go +++ b/exchange/seat_non_bids.go @@ -1,8 +1,8 @@ package exchange import ( - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type nonBids struct { @@ -17,6 +17,15 @@ func (snb *nonBids) addBid(bid *entities.PbsOrtbBid, nonBidReason int, seat stri if snb.seatNonBidsMap == nil { snb.seatNonBidsMap = make(map[string][]openrtb_ext.NonBid) } + if bid.BidMeta == nil { + bid.BidMeta = &openrtb_ext.ExtBidPrebidMeta{} + } + adapterCode := seat + if bid.AlternateBidderCode != "" { + adapterCode = string(openrtb_ext.BidderName(bid.AlternateBidderCode)) + } + bid.BidMeta.AdapterCode = adapterCode + nonBid := openrtb_ext.NonBid{ ImpId: bid.Bid.ImpID, StatusCode: nonBidReason, diff --git a/exchange/seat_non_bids_test.go b/exchange/seat_non_bids_test.go index d9f7aa88ca0..4436716ce42 100644 --- a/exchange/seat_non_bids_test.go +++ b/exchange/seat_non_bids_test.go @@ -3,9 +3,9 @@ package exchange import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -42,6 +42,12 @@ func TestSeatNonBidsAdd(t *testing.T) { args: args{bid: &entities.PbsOrtbBid{Bid: &openrtb2.Bid{}}, seat: "bidder2"}, want: sampleSeatNonBidMap("bidder2", 2), }, + { + name: "multiple-nonbids-without-meta", + fields: fields{seatNonBidsMap: sampleSeatNonBidMap("bidder2", 1)}, + args: args{bid: &entities.PbsOrtbBid{Bid: &openrtb2.Bid{}}, seat: "bidder2"}, + want: sampleSeatNonBidMap("bidder2", 2), + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -86,7 +92,7 @@ var sampleSeatNonBidMap = func(seat string, nonBidCount int) map[string][]openrt nonBids := make([]openrtb_ext.NonBid, 0) for i := 0; i < nonBidCount; i++ { nonBids = append(nonBids, openrtb_ext.NonBid{ - Ext: openrtb_ext.NonBidExt{Prebid: openrtb_ext.ExtResponseNonBidPrebid{Bid: openrtb_ext.NonBidObject{}}}, + Ext: openrtb_ext.NonBidExt{Prebid: openrtb_ext.ExtResponseNonBidPrebid{Bid: openrtb_ext.NonBidObject{Meta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: seat}}}}, }) } return map[string][]openrtb_ext.NonBid{ @@ -102,7 +108,7 @@ var sampleSeatBids = func(seat string, nonBidCount int) []openrtb_ext.SeatNonBid } for i := 0; i < nonBidCount; i++ { seatNonBid.NonBid = append(seatNonBid.NonBid, openrtb_ext.NonBid{ - Ext: openrtb_ext.NonBidExt{Prebid: openrtb_ext.ExtResponseNonBidPrebid{Bid: openrtb_ext.NonBidObject{}}}, + Ext: openrtb_ext.NonBidExt{Prebid: openrtb_ext.ExtResponseNonBidPrebid{Bid: openrtb_ext.NonBidObject{Meta: &openrtb_ext.ExtBidPrebidMeta{AdapterCode: seat}}}}, }) } seatNonBids = append(seatNonBids, seatNonBid) diff --git a/exchange/targeting.go b/exchange/targeting.go index a76c7a458b3..3d904f6a573 100644 --- a/exchange/targeting.go +++ b/exchange/targeting.go @@ -4,8 +4,8 @@ import ( "fmt" "strconv" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const MaxKeyLength = 20 @@ -26,6 +26,7 @@ type targetData struct { includeCacheVast bool includeFormat bool preferDeals bool + alwaysIncludeDeals bool // cacheHost and cachePath exist to supply cache host and path as targeting parameters cacheHost string cachePath string @@ -38,7 +39,7 @@ type targetData struct { // it's ok if those stay in the auction. For now, this method implements a very naive cache strategy. // In the future, we should implement a more clever retry & backoff strategy to balance the success rate & performance. func (targData *targetData) setTargeting(auc *auction, isApp bool, categoryMapping map[string]string, truncateTargetAttr *int, multiBidMap map[string]openrtb_ext.ExtMultiBid) { - for impId, topBidsPerImp := range auc.winningBidsByBidder { + for impId, topBidsPerImp := range auc.allBidsByBidder { overallWinner := auc.winningBids[impId] for originalBidderName, topBidsPerBidder := range topBidsPerImp { targetingBidderCode := originalBidderName @@ -61,40 +62,42 @@ func (targData *targetData) setTargeting(auc *auction, isApp bool, categoryMappi isOverallWinner := overallWinner == topBid + bidHasDeal := len(topBid.Bid.DealID) > 0 + targets := make(map[string]string, 10) if cpm, ok := auc.roundedPrices[topBid]; ok { - targData.addKeys(targets, openrtb_ext.HbpbConstantKey, cpm, targetingBidderCode, isOverallWinner, truncateTargetAttr) + targData.addKeys(targets, openrtb_ext.HbpbConstantKey, cpm, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal) } - targData.addKeys(targets, openrtb_ext.HbBidderConstantKey, string(targetingBidderCode), targetingBidderCode, isOverallWinner, truncateTargetAttr) + targData.addKeys(targets, openrtb_ext.HbBidderConstantKey, string(targetingBidderCode), targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal) if hbSize := makeHbSize(topBid.Bid); hbSize != "" { - targData.addKeys(targets, openrtb_ext.HbSizeConstantKey, hbSize, targetingBidderCode, isOverallWinner, truncateTargetAttr) + targData.addKeys(targets, openrtb_ext.HbSizeConstantKey, hbSize, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal) } if cacheID, ok := auc.cacheIds[topBid.Bid]; ok { - targData.addKeys(targets, openrtb_ext.HbCacheKey, cacheID, targetingBidderCode, isOverallWinner, truncateTargetAttr) + targData.addKeys(targets, openrtb_ext.HbCacheKey, cacheID, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal) } if vastID, ok := auc.vastCacheIds[topBid.Bid]; ok { - targData.addKeys(targets, openrtb_ext.HbVastCacheKey, vastID, targetingBidderCode, isOverallWinner, truncateTargetAttr) + targData.addKeys(targets, openrtb_ext.HbVastCacheKey, vastID, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal) } if targData.includeFormat { - targData.addKeys(targets, openrtb_ext.HbFormatKey, string(topBid.BidType), targetingBidderCode, isOverallWinner, truncateTargetAttr) + targData.addKeys(targets, openrtb_ext.HbFormatKey, string(topBid.BidType), targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal) } if targData.cacheHost != "" { - targData.addKeys(targets, openrtb_ext.HbConstantCacheHostKey, targData.cacheHost, targetingBidderCode, isOverallWinner, truncateTargetAttr) + targData.addKeys(targets, openrtb_ext.HbConstantCacheHostKey, targData.cacheHost, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal) } if targData.cachePath != "" { - targData.addKeys(targets, openrtb_ext.HbConstantCachePathKey, targData.cachePath, targetingBidderCode, isOverallWinner, truncateTargetAttr) + targData.addKeys(targets, openrtb_ext.HbConstantCachePathKey, targData.cachePath, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal) } - if deal := topBid.Bid.DealID; len(deal) > 0 { - targData.addKeys(targets, openrtb_ext.HbDealIDConstantKey, deal, targetingBidderCode, isOverallWinner, truncateTargetAttr) + if bidHasDeal { + targData.addKeys(targets, openrtb_ext.HbDealIDConstantKey, topBid.Bid.DealID, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal) } if isApp { - targData.addKeys(targets, openrtb_ext.HbEnvKey, openrtb_ext.HbEnvKeyApp, targetingBidderCode, isOverallWinner, truncateTargetAttr) + targData.addKeys(targets, openrtb_ext.HbEnvKey, openrtb_ext.HbEnvKeyApp, targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal) } if len(categoryMapping) > 0 { - targData.addKeys(targets, openrtb_ext.HbCategoryDurationKey, categoryMapping[topBid.Bid.ID], targetingBidderCode, isOverallWinner, truncateTargetAttr) + targData.addKeys(targets, openrtb_ext.HbCategoryDurationKey, categoryMapping[topBid.Bid.ID], targetingBidderCode, isOverallWinner, truncateTargetAttr, bidHasDeal) } targData.addBidderKeys(targets, topBid.BidTargets) topBid.BidTargets = targets @@ -103,7 +106,7 @@ func (targData *targetData) setTargeting(auc *auction, isApp bool, categoryMappi } } -func (targData *targetData) addKeys(keys map[string]string, key openrtb_ext.TargetingKey, value string, bidderName openrtb_ext.BidderName, overallWinner bool, truncateTargetAttr *int) { +func (targData *targetData) addKeys(keys map[string]string, key openrtb_ext.TargetingKey, value string, bidderName openrtb_ext.BidderName, overallWinner bool, truncateTargetAttr *int, bidHasDeal bool) { var maxLength int if truncateTargetAttr != nil { maxLength = *truncateTargetAttr @@ -113,7 +116,7 @@ func (targData *targetData) addKeys(keys map[string]string, key openrtb_ext.Targ } else { maxLength = MaxKeyLength } - if targData.includeBidderKeys { + if targData.includeBidderKeys || (targData.alwaysIncludeDeals && bidHasDeal) { keys[key.BidderKey(bidderName, maxLength)] = value } if targData.includeWinners && overallWinner { diff --git a/exchange/targeting_test.go b/exchange/targeting_test.go index ee8d8b47878..47ec692c88c 100644 --- a/exchange/targeting_test.go +++ b/exchange/targeting_test.go @@ -8,17 +8,18 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/hooks/hookexecution" - metricsConfig "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" - - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + metricsConfig "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" + + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" ) @@ -101,7 +102,7 @@ func runTargetingAuction(t *testing.T, mockBids map[openrtb_ext.BidderName][]*op currencyConverter: currency.NewRateConverter(&http.Client{}, "", time.Duration(0)), gdprDefaultValue: gdpr.SignalYes, categoriesFetcher: categoriesFetcher, - bidIDGenerator: &mockBidIDGenerator{false, false}, + bidIDGenerator: &fakeBidIDGenerator{GenerateBidID: false, ReturnError: false}, } ex.requestSplitter = requestSplitter{ me: ex.me, @@ -183,7 +184,7 @@ func buildParams(t *testing.T, mockBids map[openrtb_ext.BidderName][]*openrtb2.B paramsPrebid["bidder"] = paramsPrebidBidders params["prebid"] = paramsPrebid - ext, err := json.Marshal(params) + ext, err := jsonutil.Marshal(params) if err != nil { t.Fatalf("Failed to make imp exts: %v", err) } @@ -225,7 +226,7 @@ func buildBidMap(seatBids []openrtb2.SeatBid, numBids int) map[string]*openrtb2. func parseTargets(t *testing.T, bid *openrtb2.Bid) map[string]string { t.Helper() var parsed openrtb_ext.ExtBid - if err := json.Unmarshal(bid.Ext, &parsed); err != nil { + if err := jsonutil.UnmarshalValid(bid.Ext, &parsed); err != nil { t.Fatalf("Unexpected error parsing targeting params: %v", err) } return parsed.Prebid.Targeting @@ -319,6 +320,11 @@ var bid2p166 *openrtb2.Bid = &openrtb2.Bid{ Price: 1.66, } +var bid175 *openrtb2.Bid = &openrtb2.Bid{ + Price: 1.75, + DealID: "mydeal2", +} + var ( truncateTargetAttrValue10 int = 10 truncateTargetAttrValue5 int = 5 @@ -339,7 +345,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ includeWinners: true, }, Auction: auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "ImpId-1": { openrtb_ext.BidderAppnexus: {{ Bid: bid123, @@ -374,7 +380,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ includeBidderKeys: true, }, Auction: auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "ImpId-1": { openrtb_ext.BidderAppnexus: {{ Bid: bid123, @@ -409,6 +415,54 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ }, TruncateTargetAttr: nil, }, + { + Description: "Targeting with alwaysIncludeDeals", + TargetData: targetData{ + priceGranularity: lookupPriceGranularity("med"), + alwaysIncludeDeals: true, + }, + Auction: auction{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + "ImpId-1": { + openrtb_ext.BidderAppnexus: {{ + Bid: bid111, + BidType: openrtb_ext.BidTypeBanner, + }}, + openrtb_ext.BidderRubicon: {{ + Bid: bid175, + BidType: openrtb_ext.BidTypeBanner, + }}, + openrtb_ext.BidderPubmatic: {{ + Bid: bid123, + BidType: openrtb_ext.BidTypeBanner, + }}, + }, + }, + }, + ExpectedPbsBids: map[string]map[openrtb_ext.BidderName][]ExpectedPbsBid{ + "ImpId-1": { + openrtb_ext.BidderAppnexus: []ExpectedPbsBid{ + { + BidTargets: map[string]string{ + "hb_bidder_appnexus": "appnexus", + "hb_pb_appnexus": "1.10", + "hb_deal_appnexus": "mydeal", + }, + }, + }, + openrtb_ext.BidderRubicon: []ExpectedPbsBid{ + { + BidTargets: map[string]string{ + "hb_bidder_rubicon": "rubicon", + "hb_pb_rubicon": "1.70", + "hb_deal_rubicon": "mydeal2", + }, + }, + }, + }, + }, + TruncateTargetAttr: nil, + }, { Description: "Full basic targeting with hd_format", TargetData: targetData{ @@ -418,7 +472,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ includeFormat: true, }, Auction: auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "ImpId-1": { openrtb_ext.BidderAppnexus: {{ Bid: bid123, @@ -467,7 +521,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ cachePath: "cache", }, Auction: auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "ImpId-1": { openrtb_ext.BidderAppnexus: {{ Bid: bid123, @@ -520,7 +574,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ includeBidderKeys: true, }, Auction: auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "ImpId-1": { openrtb_ext.BidderAppnexus: {{ Bid: bid123, @@ -550,7 +604,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ includeBidderKeys: true, }, Auction: auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "ImpId-1": { openrtb_ext.BidderAppnexus: {{ Bid: bid123, @@ -592,7 +646,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ includeBidderKeys: true, }, Auction: auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "ImpId-1": { openrtb_ext.BidderAppnexus: {{ Bid: bid123, @@ -634,7 +688,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ includeBidderKeys: true, }, Auction: auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "ImpId-1": { openrtb_ext.BidderAppnexus: {{ Bid: bid123, @@ -676,7 +730,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ includeWinners: true, }, Auction: auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "ImpId-1": { openrtb_ext.BidderAppnexus: {{ Bid: bid123, @@ -711,7 +765,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ includeWinners: true, }, Auction: auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "ImpId-1": { openrtb_ext.BidderAppnexus: {{ Bid: bid123, @@ -746,7 +800,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ includeWinners: true, }, Auction: auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "ImpId-1": { openrtb_ext.BidderAppnexus: {{ Bid: bid123, @@ -783,7 +837,7 @@ var TargetingTests []TargetingTestData = []TargetingTestData{ includeFormat: true, }, Auction: auction{ - winningBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ + allBidsByBidder: map[string]map[openrtb_ext.BidderName][]*entities.PbsOrtbBid{ "ImpId-1": { openrtb_ext.BidderAppnexus: { { @@ -918,7 +972,7 @@ func TestSetTargeting(t *testing.T) { auc.setRoundedPrices(test.TargetData) winningBids := make(map[string]*entities.PbsOrtbBid) // Set winning bids from the auction data - for imp, bidsByBidder := range auc.winningBidsByBidder { + for imp, bidsByBidder := range auc.allBidsByBidder { for _, bids := range bidsByBidder { for _, bid := range bids { if winningBid, ok := winningBids[imp]; ok { @@ -939,12 +993,12 @@ func TestSetTargeting(t *testing.T) { for i, expected := range expectedTargets { assert.Equal(t, expected.BidTargets, - auc.winningBidsByBidder[imp][bidder][i].BidTargets, + auc.allBidsByBidder[imp][bidder][i].BidTargets, "Test: %s\nTargeting failed for bidder %s on imp %s.", test.Description, string(bidder), imp) - assert.Equal(t, expected.TargetBidderCode, auc.winningBidsByBidder[imp][bidder][i].TargetBidderCode) + assert.Equal(t, expected.TargetBidderCode, auc.allBidsByBidder[imp][bidder][i].TargetBidderCode) } } } diff --git a/exchange/tmax_adjustments.go b/exchange/tmax_adjustments.go index 29e732995af..55e2b18ad01 100644 --- a/exchange/tmax_adjustments.go +++ b/exchange/tmax_adjustments.go @@ -4,7 +4,7 @@ import ( "context" "time" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" ) type TmaxAdjustmentsPreprocessed struct { diff --git a/exchange/tmax_adjustments_test.go b/exchange/tmax_adjustments_test.go index 7e6a02ab81e..ce6f1736adf 100644 --- a/exchange/tmax_adjustments_test.go +++ b/exchange/tmax_adjustments_test.go @@ -4,7 +4,7 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" "github.com/stretchr/testify/assert" ) diff --git a/exchange/utils.go b/exchange/utils.go index 1df406528ce..48bac2caa3f 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -7,32 +7,37 @@ import ( "errors" "fmt" "math/rand" + "strings" + + "github.com/prebid/prebid-server/v2/ortb" "github.com/buger/jsonparser" "github.com/prebid/go-gdpr/vendorconsent" gpplib "github.com/prebid/go-gpp" gppConstants "github.com/prebid/go-gpp/constants" - "github.com/prebid/openrtb/v19/openrtb2" - - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/firstpartydata" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/privacy/ccpa" - "github.com/prebid/prebid-server/privacy/lmt" - "github.com/prebid/prebid-server/schain" - "github.com/prebid/prebid-server/stored_responses" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/firstpartydata" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/privacy/ccpa" + "github.com/prebid/prebid-server/v2/privacy/lmt" + "github.com/prebid/prebid-server/v2/schain" + "github.com/prebid/prebid-server/v2/stored_responses" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) var channelTypeMap = map[metrics.RequestType]config.ChannelType{ - metrics.ReqTypeAMP: config.ChannelAMP, - metrics.ReqTypeORTB2App: config.ChannelApp, - metrics.ReqTypeVideo: config.ChannelVideo, - metrics.ReqTypeORTB2Web: config.ChannelWeb, + metrics.ReqTypeAMP: config.ChannelAMP, + metrics.ReqTypeORTB2App: config.ChannelApp, + metrics.ReqTypeVideo: config.ChannelVideo, + metrics.ReqTypeORTB2Web: config.ChannelWeb, + metrics.ReqTypeORTB2DOOH: config.ChannelDOOH, } const unknownBidder string = "" @@ -87,9 +92,10 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, var gpp gpplib.GppContainer if req.BidRequest.Regs != nil && len(req.BidRequest.Regs.GPP) > 0 { - gpp, err = gpplib.Parse(req.BidRequest.Regs.GPP) - if err != nil { - errs = append(errs, err) + var gppErrs []error + gpp, gppErrs = gpplib.Parse(req.BidRequest.Regs.GPP) + if len(gppErrs) > 0 { + errs = append(errs, gppErrs[0]) } } updateContentObjectForBidder(allBidderRequests, requestExt) @@ -152,11 +158,6 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, // bidder level privacy policies for _, bidderRequest := range allBidderRequests { - privacyEnforcement := privacy.Enforcement{ - COPPA: coppa, - LMT: lmt, - } - // fetchBids activity scopedName := privacy.Component{Type: privacy.ComponentTypeBidder, Name: bidderRequest.BidderName.String()} fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids, scopedName, privacy.NewRequestFromBidRequest(*req)) @@ -179,48 +180,58 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, } } + ipConf := privacy.IPConf{IPV6: auctionReq.Account.Privacy.IPv6Config, IPV4: auctionReq.Account.Privacy.IPv4Config} + + // FPD should be applied before policies, otherwise it overrides policies and activities restricted data + applyFPD(auctionReq.FirstPartyData, bidderRequest) + + reqWrapper := &openrtb_ext.RequestWrapper{ + BidRequest: ortb.CloneBidRequestPartial(bidderRequest.BidRequest), + } + passIDActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitUserFPD, scopedName, privacy.NewRequestFromBidRequest(*req)) if !passIDActivityAllowed { - privacyEnforcement.UFPD = true + //UFPD + privacy.ScrubUserFPD(reqWrapper) } else { // run existing policies (GDPR, CCPA, COPPA, LMT) // potentially block passing IDs based on GDPR - if gdprEnforced { - if gdprErr == nil { - privacyEnforcement.GDPRID = !auctionPermissions.PassID - } else { - privacyEnforcement.GDPRID = true - } + if gdprEnforced && (gdprErr != nil || !auctionPermissions.PassID) { + privacy.ScrubGdprID(reqWrapper) } // potentially block passing IDs based on CCPA - privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String()) + if ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String()) { + privacy.ScrubDeviceIDsIPsUserDemoExt(reqWrapper, ipConf, "eids", false) + } } passGeoActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitPreciseGeo, scopedName, privacy.NewRequestFromBidRequest(*req)) if !passGeoActivityAllowed { - privacyEnforcement.PreciseGeo = true + privacy.ScrubGeoAndDeviceIP(reqWrapper, ipConf) } else { // run existing policies (GDPR, CCPA, COPPA, LMT) // potentially block passing geo based on GDPR - if gdprEnforced { - if gdprErr == nil { - privacyEnforcement.GDPRGeo = !auctionPermissions.PassGeo - } else { - privacyEnforcement.GDPRGeo = true - } + if gdprEnforced && (gdprErr != nil || !auctionPermissions.PassGeo) { + privacy.ScrubGeoAndDeviceIP(reqWrapper, ipConf) } // potentially block passing geo based on CCPA - privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String()) + if ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String()) { + privacy.ScrubDeviceIDsIPsUserDemoExt(reqWrapper, ipConf, "eids", false) + } + } + if lmt || coppa { + privacy.ScrubDeviceIDsIPsUserDemoExt(reqWrapper, ipConf, "eids", coppa) } - if auctionReq.FirstPartyData != nil && auctionReq.FirstPartyData[bidderRequest.BidderName] != nil { - applyFPD(auctionReq.FirstPartyData[bidderRequest.BidderName], bidderRequest.BidRequest) + passTIDAllowed := auctionReq.Activities.Allow(privacy.ActivityTransmitTIDs, scopedName, privacy.NewRequestFromBidRequest(*req)) + if !passTIDAllowed { + privacy.ScrubTID(reqWrapper) } - privacyEnforcement.TID = !auctionReq.Activities.Allow(privacy.ActivityTransmitTIDs, scopedName, privacy.NewRequestFromBidRequest(*req)) + reqWrapper.RebuildRequest() + bidderRequest.BidRequest = reqWrapper.BidRequest - privacyEnforcement.Apply(bidderRequest.BidRequest, auctionReq.Account.Privacy) allowedBidderRequests = append(allowedBidderRequests, bidderRequest) // GPP downgrade: always downgrade unless we can confirm GPP is supported @@ -284,7 +295,7 @@ func ExtractReqExtBidderParamsMap(bidRequest *openrtb2.BidRequest) (map[string]j reqExt := &openrtb_ext.ExtRequest{} if len(bidRequest.Ext) > 0 { - err := json.Unmarshal(bidRequest.Ext, &reqExt) + err := jsonutil.Unmarshal(bidRequest.Ext, &reqExt) if err != nil { return nil, fmt.Errorf("error decoding Request.ext : %s", err.Error()) } @@ -295,7 +306,7 @@ func ExtractReqExtBidderParamsMap(bidRequest *openrtb2.BidRequest) (map[string]j } var bidderParams map[string]json.RawMessage - err := json.Unmarshal(reqExt.Prebid.BidderParams, &bidderParams) + err := jsonutil.Unmarshal(reqExt.Prebid.BidderParams, &bidderParams) if err != nil { return nil, err } @@ -327,9 +338,15 @@ func getAuctionBidderRequests(auctionRequest AuctionRequest, return nil, []error{err} } + lowerCaseExplicitBuyerUIDs := make(map[string]string) + for bidder, uid := range explicitBuyerUIDs { + lowerKey := strings.ToLower(bidder) + lowerCaseExplicitBuyerUIDs[lowerKey] = uid + } + var errs []error for bidder, imps := range impsByBidder { - coreBidder := resolveBidder(bidder, aliases) + coreBidder, isRequestAlias := resolveBidder(bidder, aliases) reqCopy := *req.BidRequest reqCopy.Imp = imps @@ -349,6 +366,7 @@ func getAuctionBidderRequests(auctionRequest AuctionRequest, bidderRequest := BidderRequest{ BidderName: openrtb_ext.BidderName(bidder), BidderCoreName: coreBidder, + IsRequestAlias: isRequestAlias, BidRequest: &reqCopy, BidderLabels: metrics.AdapterLabels{ Source: auctionRequest.LegacyLabels.Source, @@ -361,7 +379,7 @@ func getAuctionBidderRequests(auctionRequest AuctionRequest, } syncerKey := bidderToSyncerKey[string(coreBidder)] - if hadSync := prepareUser(&reqCopy, bidder, syncerKey, explicitBuyerUIDs, auctionRequest.UserSyncs); !hadSync && req.BidRequest.App == nil { + if hadSync := prepareUser(&reqCopy, bidder, syncerKey, lowerCaseExplicitBuyerUIDs, auctionRequest.UserSyncs); !hadSync && req.BidRequest.App == nil { bidderRequest.BidderLabels.CookieFlag = metrics.CookieFlagNo } else { bidderRequest.BidderLabels.CookieFlag = metrics.CookieFlagYes @@ -409,7 +427,7 @@ func buildRequestExtForBidder(bidder string, requestExt json.RawMessage, request } // Marshal New Prebid Object - prebidJson, err := json.Marshal(prebid) + prebidJson, err := jsonutil.Marshal(prebid) if err != nil { return nil, err } @@ -417,7 +435,7 @@ func buildRequestExtForBidder(bidder string, requestExt json.RawMessage, request // Parse Existing Ext extMap := make(map[string]json.RawMessage) if len(requestExt) != 0 { - if err := json.Unmarshal(requestExt, &extMap); err != nil { + if err := jsonutil.Unmarshal(requestExt, &extMap); err != nil { return nil, err } } @@ -430,37 +448,39 @@ func buildRequestExtForBidder(bidder string, requestExt json.RawMessage, request } if len(extMap) > 0 { - return json.Marshal(extMap) + return jsonutil.Marshal(extMap) } else { return nil, nil } } func buildRequestExtAlternateBidderCodes(bidder string, accABC *openrtb_ext.ExtAlternateBidderCodes, reqABC *openrtb_ext.ExtAlternateBidderCodes) *openrtb_ext.ExtAlternateBidderCodes { - if reqABC != nil { - alternateBidderCodes := &openrtb_ext.ExtAlternateBidderCodes{ - Enabled: reqABC.Enabled, - } - if bidderCodes, ok := reqABC.Bidders[bidder]; ok { - alternateBidderCodes.Bidders = map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ - bidder: bidderCodes, - } - } - return alternateBidderCodes + + if altBidderCodes := copyExtAlternateBidderCodes(bidder, reqABC); altBidderCodes != nil { + return altBidderCodes + } + + if altBidderCodes := copyExtAlternateBidderCodes(bidder, accABC); altBidderCodes != nil { + return altBidderCodes } - if accABC != nil { + return nil +} + +func copyExtAlternateBidderCodes(bidder string, altBidderCodes *openrtb_ext.ExtAlternateBidderCodes) *openrtb_ext.ExtAlternateBidderCodes { + if altBidderCodes != nil { alternateBidderCodes := &openrtb_ext.ExtAlternateBidderCodes{ - Enabled: accABC.Enabled, + Enabled: altBidderCodes.Enabled, } - if bidderCodes, ok := accABC.Bidders[bidder]; ok { + + if bidderCodes, ok := altBidderCodes.IsBidderInAlternateBidderCodes(bidder); ok { alternateBidderCodes.Bidders = map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ bidder: bidderCodes, } } + return alternateBidderCodes } - return nil } @@ -468,12 +488,12 @@ func buildRequestExtMultiBid(adapter string, reqMultiBid []*openrtb_ext.ExtMulti adapterMultiBid := make([]*openrtb_ext.ExtMultiBid, 0) for _, multiBid := range reqMultiBid { if multiBid.Bidder != "" { - if multiBid.Bidder == adapter || isBidderInExtAlternateBidderCodes(adapter, multiBid.Bidder, adapterABC) { + if strings.ToLower(multiBid.Bidder) == adapter || isBidderInExtAlternateBidderCodes(adapter, strings.ToLower(multiBid.Bidder), adapterABC) { adapterMultiBid = append(adapterMultiBid, multiBid) } } else { for _, bidder := range multiBid.Bidders { - if bidder == adapter || isBidderInExtAlternateBidderCodes(adapter, bidder, adapterABC) { + if strings.ToLower(bidder) == adapter || isBidderInExtAlternateBidderCodes(adapter, strings.ToLower(bidder), adapterABC) { adapterMultiBid = append(adapterMultiBid, &openrtb_ext.ExtMultiBid{ Bidders: []string{bidder}, MaxBids: multiBid.MaxBids, @@ -514,7 +534,7 @@ func extractBuyerUIDs(user *openrtb2.User) (map[string]string, error) { } var userExt openrtb_ext.ExtUser - if err := json.Unmarshal(user.Ext, &userExt); err != nil { + if err := jsonutil.Unmarshal(user.Ext, &userExt); err != nil { return nil, err } if userExt.Prebid == nil { @@ -528,7 +548,7 @@ func extractBuyerUIDs(user *openrtb2.User) (map[string]string, error) { // Remarshal (instead of removing) if the ext has other known fields if userExt.Consent != "" || len(userExt.Eids) > 0 { - if newUserExtBytes, err := json.Marshal(userExt); err != nil { + if newUserExtBytes, err := jsonutil.Marshal(userExt); err != nil { return nil, err } else { user.Ext = newUserExtBytes @@ -552,20 +572,20 @@ func splitImps(imps []openrtb2.Imp) (map[string][]openrtb2.Imp, error) { for i, imp := range imps { var impExt map[string]json.RawMessage - if err := json.Unmarshal(imp.Ext, &impExt); err != nil { + if err := jsonutil.UnmarshalValid(imp.Ext, &impExt); err != nil { return nil, fmt.Errorf("invalid json for imp[%d]: %v", i, err) } var impExtPrebid map[string]json.RawMessage if impExtPrebidJSON, exists := impExt[openrtb_ext.PrebidExtKey]; exists { // validation already performed by impExt unmarshal. no error is possible here, proven by tests. - json.Unmarshal(impExtPrebidJSON, &impExtPrebid) + jsonutil.Unmarshal(impExtPrebidJSON, &impExtPrebid) } var impExtPrebidBidder map[string]json.RawMessage if impExtPrebidBidderJSON, exists := impExtPrebid[openrtb_ext.PrebidExtBidderKey]; exists { // validation already performed by impExt unmarshal. no error is possible here, proven by tests. - json.Unmarshal(impExtPrebidBidderJSON, &impExtPrebidBidder) + jsonutil.Unmarshal(impExtPrebidBidderJSON, &impExtPrebidBidder) } sanitizedImpExt, err := createSanitizedImpExt(impExt, impExtPrebid) @@ -578,7 +598,7 @@ func splitImps(imps []openrtb2.Imp) (map[string][]openrtb2.Imp, error) { sanitizedImpExt[openrtb_ext.PrebidExtBidderKey] = bidderExt - impExtJSON, err := json.Marshal(sanitizedImpExt) + impExtJSON, err := jsonutil.Marshal(sanitizedImpExt) if err != nil { return nil, fmt.Errorf("unable to remove other bidder fields for imp[%d]: cannot marshal ext: %v", i, err) } @@ -623,7 +643,7 @@ func createSanitizedImpExt(impExt, impExtPrebid map[string]json.RawMessage) (map // marshal sanitized imp[].ext.prebid if len(sanitizedImpPrebidExt) > 0 { - if impExtPrebidJSON, err := json.Marshal(sanitizedImpPrebidExt); err == nil { + if impExtPrebidJSON, err := jsonutil.Marshal(sanitizedImpPrebidExt); err == nil { sanitizedImpExt[openrtb_ext.PrebidExtKey] = impExtPrebidJSON } else { return nil, fmt.Errorf("cannot marshal ext.prebid: %v", err) @@ -648,7 +668,7 @@ func createSanitizedImpExt(impExt, impExtPrebid map[string]json.RawMessage) (map func prepareUser(req *openrtb2.BidRequest, givenBidder, syncerKey string, explicitBuyerUIDs map[string]string, usersyncs IdFetcher) bool { cookieId, hadCookie, _ := usersyncs.GetUID(syncerKey) - if id, ok := explicitBuyerUIDs[givenBidder]; ok { + if id, ok := explicitBuyerUIDs[strings.ToLower(givenBidder)]; ok { req.User = copyWithBuyerUID(req.User, id) } else if hadCookie { req.User = copyWithBuyerUID(req.User, cookieId) @@ -687,7 +707,7 @@ func removeUnpermissionedEids(request *openrtb2.BidRequest, bidder string, reque // low level unmarshal to preserve other request.user.ext values. prebid server is non-destructive. var userExt map[string]json.RawMessage - if err := json.Unmarshal(request.User.Ext, &userExt); err != nil { + if err := jsonutil.Unmarshal(request.User.Ext, &userExt); err != nil { return err } @@ -697,7 +717,7 @@ func removeUnpermissionedEids(request *openrtb2.BidRequest, bidder string, reque } var eids []openrtb2.EID - if err := json.Unmarshal(eidsJSON, &eids); err != nil { + if err := jsonutil.Unmarshal(eidsJSON, &eids); err != nil { return err } @@ -717,7 +737,7 @@ func removeUnpermissionedEids(request *openrtb2.BidRequest, bidder string, reque allowed := false if rule, hasRule := eidRules[eid.Source]; hasRule { for _, ruleBidder := range rule { - if ruleBidder == "*" || ruleBidder == bidder { + if ruleBidder == "*" || strings.EqualFold(ruleBidder, bidder) { allowed = true break } @@ -740,7 +760,7 @@ func removeUnpermissionedEids(request *openrtb2.BidRequest, bidder string, reque if len(eidsAllowed) == 0 { delete(userExt, "eids") } else { - eidsRaw, err := json.Marshal(eidsAllowed) + eidsRaw, err := jsonutil.Marshal(eidsAllowed) if err != nil { return err } @@ -753,7 +773,7 @@ func removeUnpermissionedEids(request *openrtb2.BidRequest, bidder string, reque return nil } - userExtJSON, err := json.Marshal(userExt) + userExtJSON, err := jsonutil.Marshal(userExt) if err != nil { return err } @@ -768,19 +788,21 @@ func setUserExtWithCopy(request *openrtb2.BidRequest, userExtJSON json.RawMessag } // resolveBidder returns the known BidderName associated with bidder, if bidder is an alias. If it's not an alias, the bidder is returned. -func resolveBidder(bidder string, aliases map[string]string) openrtb_ext.BidderName { +func resolveBidder(bidder string, requestAliases map[string]string) (openrtb_ext.BidderName, bool) { normalisedBidderName, _ := openrtb_ext.NormalizeBidderName(bidder) - if coreBidder, ok := aliases[bidder]; ok { - return openrtb_ext.BidderName(coreBidder) + + if coreBidder, ok := requestAliases[bidder]; ok { + return openrtb_ext.BidderName(coreBidder), true } - return normalisedBidderName + + return normalisedBidderName, false } // parseAliases parses the aliases from the BidRequest func parseAliases(orig *openrtb2.BidRequest) (map[string]string, []error) { var aliases map[string]string if value, dataType, _, err := jsonparser.Get(orig.Ext, openrtb_ext.PrebidExtKey, "aliases"); dataType == jsonparser.Object && err == nil { - if err := json.Unmarshal(value, &aliases); err != nil { + if err := jsonutil.Unmarshal(value, &aliases); err != nil { return nil, []error{err} } } else if dataType != jsonparser.NotExist && err != jsonparser.KeyPathNotFoundError { @@ -793,7 +815,7 @@ func parseAliases(orig *openrtb2.BidRequest) (map[string]string, []error) { func parseAliasesGVLIDs(orig *openrtb2.BidRequest) (map[string]uint16, []error) { var aliasesGVLIDs map[string]uint16 if value, dataType, _, err := jsonparser.Get(orig.Ext, openrtb_ext.PrebidExtKey, "aliasgvlids"); dataType == jsonparser.Object && err == nil { - if err := json.Unmarshal(value, &aliasesGVLIDs); err != nil { + if err := jsonutil.Unmarshal(value, &aliasesGVLIDs); err != nil { return nil, []error{err} } } else if dataType != jsonparser.NotExist && err != jsonparser.KeyPathNotFoundError { @@ -865,6 +887,7 @@ func getExtTargetData(requestExtPrebid *openrtb_ext.ExtRequestPrebid, cacheInstr priceGranularity: *requestExtPrebid.Targeting.PriceGranularity, mediaTypePriceGranularity: requestExtPrebid.Targeting.MediaTypePriceGranularity, preferDeals: requestExtPrebid.Targeting.PreferDeals, + alwaysIncludeDeals: requestExtPrebid.Targeting.AlwaysIncludeDeals, } } @@ -899,26 +922,46 @@ func parseRequestDebugValues(test int8, requestExtPrebid *openrtb_ext.ExtRequest } func getExtBidAdjustmentFactors(requestExtPrebid *openrtb_ext.ExtRequestPrebid) map[string]float64 { - if requestExtPrebid != nil { - return requestExtPrebid.BidAdjustmentFactors + if requestExtPrebid != nil && requestExtPrebid.BidAdjustmentFactors != nil { + caseInsensitiveMap := make(map[string]float64, len(requestExtPrebid.BidAdjustmentFactors)) + for bidder, bidAdjFactor := range requestExtPrebid.BidAdjustmentFactors { + caseInsensitiveMap[strings.ToLower(bidder)] = bidAdjFactor + } + return caseInsensitiveMap } return nil } -func applyFPD(fpd *firstpartydata.ResolvedFirstPartyData, bidReq *openrtb2.BidRequest) { - if fpd.Site != nil { - bidReq.Site = fpd.Site +func applyFPD(fpd map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData, r BidderRequest) { + if fpd == nil { + return + } + + bidder := r.BidderCoreName + if r.IsRequestAlias { + bidder = r.BidderName } - if fpd.App != nil { - bidReq.App = fpd.App + + fpdToApply, exists := fpd[bidder] + if !exists || fpdToApply == nil { + return } - if fpd.User != nil { + + if fpdToApply.Site != nil { + r.BidRequest.Site = fpdToApply.Site + } + + if fpdToApply.App != nil { + r.BidRequest.App = fpdToApply.App + } + + if fpdToApply.User != nil { //BuyerUID is a value obtained between fpd extraction and fpd application. //BuyerUID needs to be set back to fpd before applying this fpd to final bidder request - if bidReq.User != nil && len(bidReq.User.BuyerUID) > 0 { - fpd.User.BuyerUID = bidReq.User.BuyerUID + if r.BidRequest.User != nil && len(r.BidRequest.User.BuyerUID) > 0 { + fpdToApply.User.BuyerUID = r.BidRequest.User.BuyerUID } - bidReq.User = fpd.User + r.BidRequest.User = fpdToApply.User } } @@ -930,13 +973,14 @@ func buildBidResponseRequest(req *openrtb2.BidRequest, bidderToBidderResponse := make(map[openrtb_ext.BidderName]BidderRequest) for bidderName, impResps := range bidderImpResponses { - resolvedBidder := resolveBidder(string(bidderName), aliases) + resolvedBidder, isRequestAlias := resolveBidder(string(bidderName), aliases) bidderToBidderResponse[bidderName] = BidderRequest{ BidRequest: req, BidderCoreName: resolvedBidder, BidderName: bidderName, BidderStoredResponses: impResps, - ImpReplaceImpId: bidderImpReplaceImpID[string(resolvedBidder)], + ImpReplaceImpId: bidderImpReplaceImpID[string(bidderName)], + IsRequestAlias: isRequestAlias, BidderLabels: metrics.AdapterLabels{Adapter: resolvedBidder}, } } @@ -1067,7 +1111,7 @@ func getPrebidMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { if bid.Ext != nil { var bidExt openrtb_ext.ExtBid - err = json.Unmarshal(bid.Ext, &bidExt) + err = jsonutil.Unmarshal(bid.Ext, &bidExt) if err == nil && bidExt.Prebid != nil { if bidType, err = openrtb_ext.ParseBidType(string(bidExt.Prebid.Type)); err == nil { return bidType, nil diff --git a/exchange/utils_ow.go b/exchange/utils_ow.go index b4a7c4f9c99..b1ae8eefab8 100644 --- a/exchange/utils_ow.go +++ b/exchange/utils_ow.go @@ -4,8 +4,9 @@ import ( "encoding/json" "github.com/golang/glog" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) func JLogf(msg string, obj interface{}) { @@ -168,15 +169,15 @@ func createNewContentObject(contentObject *openrtb2.Content, include bool, keys case "keywords": newContentObject.Keywords = "" case "livestream": - newContentObject.LiveStream = 0 + newContentObject.LiveStream = ptrutil.ToPtr[int8](0) case "sourcerelationship": - newContentObject.SourceRelationship = 0 + newContentObject.SourceRelationship = ptrutil.ToPtr[int8](0) case "len": newContentObject.Len = 0 case "language": newContentObject.Language = "" case "embeddable": - newContentObject.Embeddable = 0 + newContentObject.Embeddable = ptrutil.ToPtr[int8](0) case "data": newContentObject.Data = nil case "ext": diff --git a/exchange/utils_ow_test.go b/exchange/utils_ow_test.go index ad1d44ca072..76509efbd04 100644 --- a/exchange/utils_ow_test.go +++ b/exchange/utils_ow_test.go @@ -3,8 +3,8 @@ package exchange import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/exchange/utils_test.go b/exchange/utils_test.go index ee6dad1ad96..0c8153b6b11 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -8,21 +8,26 @@ import ( "sort" "testing" + "github.com/prebid/prebid-server/v2/stored_responses" + gpplib "github.com/prebid/go-gpp" "github.com/prebid/go-gpp/constants" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/firstpartydata" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/privacy" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/firstpartydata" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) +const deviceUA = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36" + // permissionsMock mocks the Permissions interface for tests type permissionsMock struct { allowAllBidders bool @@ -187,21 +192,21 @@ func TestSplitImps(t *testing.T) { givenImps: []openrtb2.Imp{ {ID: "imp1", Ext: json.RawMessage(`malformed`)}, }, - expectedError: "invalid json for imp[0]: invalid character 'm' looking for beginning of value", + expectedError: "invalid json for imp[0]: expect { or n, but found m", }, { description: "Malformed imp.ext.prebid", givenImps: []openrtb2.Imp{ {ID: "imp1", Ext: json.RawMessage(`{"prebid": malformed}`)}, }, - expectedError: "invalid json for imp[0]: invalid character 'm' looking for beginning of value", + expectedError: "invalid json for imp[0]: do not know how to skip: 109", }, { description: "Malformed imp.ext.prebid.bidder", givenImps: []openrtb2.Imp{ {ID: "imp1", Ext: json.RawMessage(`{"prebid": {"bidder": malformed}}`)}, }, - expectedError: "invalid json for imp[0]: invalid character 'm' looking for beginning of value", + expectedError: "invalid json for imp[0]: do not know how to skip: 109", }, } @@ -403,22 +408,6 @@ func TestCreateSanitizedImpExt(t *testing.T) { }, expectedError: "", }, - { - description: "Marshal Error - imp.ext.prebid", - givenImpExt: map[string]json.RawMessage{ - "prebid": json.RawMessage(`"ignoredInFavorOfSeparatelyUnmarshalledImpExtPrebid"`), - "data": json.RawMessage(`"anyData"`), - "context": json.RawMessage(`"anyContext"`), - "skadn": json.RawMessage(`"anySKAdNetwork"`), - "gpid": json.RawMessage(`"anyGPID"`), - "tid": json.RawMessage(`"anyTID"`), - }, - givenImpExtPrebid: map[string]json.RawMessage{ - "options": json.RawMessage(`malformed`), // String value without quotes. - }, - expected: nil, - expectedError: "cannot marshal ext.prebid: json: error calling MarshalJSON for type json.RawMessage: invalid character 'm' looking for beginning of value", - }, } for _, test := range testCases { @@ -592,7 +581,7 @@ func TestExtractAdapterReqBidderParamsMap(t *testing.T) { name: "malformed req.ext", givenBidRequest: &openrtb2.BidRequest{Ext: json.RawMessage("malformed")}, want: nil, - wantErr: errors.New("error decoding Request.ext : invalid character 'm' looking for beginning of value"), + wantErr: errors.New("error decoding Request.ext : expect { or n, but found m"), }, { name: "extract bidder params from req.Ext for input request in adapter code", @@ -629,8 +618,8 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) { { ID: "imp-id1", Video: &openrtb2.Video{ - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), }, Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`), }, @@ -653,8 +642,8 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) { { ID: "imp-id1", Video: &openrtb2.Video{ - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), }, Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`), }, @@ -683,8 +672,8 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) { { ID: "imp-id1", Video: &openrtb2.Video{ - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), }, Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`), }, @@ -714,8 +703,8 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) { { ID: "imp-id1", Video: &openrtb2.Video{ - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), }, Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`), }, @@ -754,8 +743,8 @@ func TestCleanOpenRTBRequestsWithBidResponses(t *testing.T) { { ID: "imp-id1", Video: &openrtb2.Video{ - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), }, Ext: json.RawMessage(`{"prebid":{"bidder":{"bidderA":{"placementId":"123"}}}}`), }, @@ -1081,7 +1070,7 @@ func TestCleanOpenRTBRequestsCCPAErrors(t *testing.T) { req.Regs = &openrtb2.Regs{Ext: test.reqRegsExt} var reqExtStruct openrtb_ext.ExtRequest - err := json.Unmarshal(req.Ext, &reqExtStruct) + err := jsonutil.UnmarshalValid(req.Ext, &reqExtStruct) assert.NoError(t, err, test.description+":marshal_ext") auctionReq := AuctionRequest{ @@ -1240,7 +1229,7 @@ func TestCleanOpenRTBRequestsSChain(t *testing.T) { if test.inExt != nil { req.Ext = test.inExt extRequest = &openrtb_ext.ExtRequest{} - err := json.Unmarshal(req.Ext, extRequest) + err := jsonutil.UnmarshalValid(req.Ext, extRequest) assert.NoErrorf(t, err, test.description+":Error unmarshaling inExt") } @@ -1311,7 +1300,7 @@ func TestCleanOpenRTBRequestsBidderParams(t *testing.T) { if test.inExt != nil { req.Ext = test.inExt extRequest = &openrtb_ext.ExtRequest{} - err := json.Unmarshal(req.Ext, extRequest) + err := jsonutil.UnmarshalValid(req.Ext, extRequest) assert.NoErrorf(t, err, test.description+":Error unmarshaling inExt") } @@ -1834,6 +1823,11 @@ func TestGetExtBidAdjustmentFactors(t *testing.T) { requestExtPrebid: &openrtb_ext.ExtRequestPrebid{BidAdjustmentFactors: map[string]float64{"bid-factor": 1.0}}, outBidAdjustmentFactors: map[string]float64{"bid-factor": 1.0}, }, + { + desc: "BidAdjustmentFactors contains uppercase bidders, expect case insensitve map returned", + requestExtPrebid: &openrtb_ext.ExtRequestPrebid{BidAdjustmentFactors: map[string]float64{"Bidder": 1.0, "APPNEXUS": 2.0}}, + outBidAdjustmentFactors: map[string]float64{"bidder": 1.0, "appnexus": 2.0}, + }, } for _, test := range testCases { actualBidAdjustmentFactors := getExtBidAdjustmentFactors(test.requestExtPrebid) @@ -2287,7 +2281,8 @@ func TestCleanOpenRTBRequestsWithOpenRTBDowngrade(t *testing.T) { bidReq.User.ID = "" bidReq.User.BuyerUID = "" bidReq.User.Yob = 0 - bidReq.User.Geo = &openrtb2.Geo{Lat: 123.46} + bidReq.User.Gender = "" + bidReq.User.Geo = &openrtb2.Geo{Lat: ptrutil.ToPtr(123.46)} downgradedRegs := *bidReq.Regs downgradedUser := *bidReq.User @@ -2487,7 +2482,7 @@ func TestBuildRequestExtForBidder(t *testing.T) { for _, test := range testCases { requestExtParsed := &openrtb_ext.ExtRequest{} if test.requestExt != nil { - err := json.Unmarshal(test.requestExt, requestExtParsed) + err := jsonutil.UnmarshalValid(test.requestExt, requestExtParsed) if !assert.NoError(t, err, test.description+":parse_ext") { continue } @@ -2528,7 +2523,7 @@ func TestBuildRequestExtForBidder_RequestExtMalformed(t *testing.T) { actualJson, actualErr := buildRequestExtForBidder(bidder, requestExt, requestExtParsed, bidderParams, alternateBidderCodes) assert.Equal(t, json.RawMessage(nil), actualJson) - assert.EqualError(t, actualErr, "invalid character 'm' looking for beginning of value") + assert.EqualError(t, actualErr, "expect { or n, but found m") } // newAdapterAliasBidRequest builds a BidRequest with aliases @@ -2544,7 +2539,7 @@ func newAdapterAliasBidRequest(t *testing.T) *openrtb2.BidRequest { }, Device: &openrtb2.Device{ DIDMD5: "some device ID hash", - UA: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36", + UA: deviceUA, IFA: "ifa", IP: "132.173.230.74", DNT: &dnt, @@ -2588,11 +2583,17 @@ func newBidRequest(t *testing.T) *openrtb2.BidRequest { }, }, Device: &openrtb2.Device{ - DIDMD5: "some device ID hash", - UA: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36", - IFA: "ifa", + UA: deviceUA, IP: "132.173.230.74", Language: "EN", + DIDMD5: "DIDMD5", + IFA: "IFA", + DIDSHA1: "DIDSHA1", + DPIDMD5: "DPIDMD5", + DPIDSHA1: "DPIDSHA1", + MACMD5: "MACMD5", + MACSHA1: "MACSHA1", + Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.456), Lon: ptrutil.ToPtr(11.278)}, }, Source: &openrtb2.Source{ TID: "testTID", @@ -2601,8 +2602,13 @@ func newBidRequest(t *testing.T) *openrtb2.BidRequest { ID: "our-id", BuyerUID: "their-id", Yob: 1982, - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{Lat: 123.456}, + Gender: "test", + Ext: json.RawMessage(`{"data": 1, "test": 2}`), + Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.456), Lon: ptrutil.ToPtr(11.278)}, + EIDs: []openrtb2.EID{ + {Source: "eids-source"}, + }, + Data: []openrtb2.Data{{ID: "data-id"}}, }, Imp: []openrtb2.Imp{{ BidFloor: 100, @@ -2632,7 +2638,7 @@ func newBidRequestWithBidderParams(t *testing.T) *openrtb2.BidRequest { }, Device: &openrtb2.Device{ DIDMD5: "some device ID hash", - UA: "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36", + UA: deviceUA, IFA: "ifa", IP: "132.173.230.74", Language: "EN", @@ -2768,6 +2774,14 @@ func TestRemoveUnpermissionedEids(t *testing.T) { }, expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`), }, + { + description: "Allowed By Specific Bidder - Case Insensitive", + userExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`), + eidPermissions: []openrtb_ext.ExtRequestPrebidDataEidPermission{ + {Source: "source1", Bidders: []string{"BIDDERA"}}, + }, + expectedUserExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`), + }, { description: "Allowed By All Bidders", userExt: json.RawMessage(`{"eids":[{"source":"source1","uids":[{"id":"anyID"}]}]}`), @@ -2851,17 +2865,17 @@ func TestRemoveUnpermissionedEidsUnmarshalErrors(t *testing.T) { { description: "Malformed Ext", userExt: json.RawMessage(`malformed`), - expectedErr: "invalid character 'm' looking for beginning of value", + expectedErr: "expect { or n, but found m", }, { description: "Malformed Eid Array Type", userExt: json.RawMessage(`{"eids":[42]}`), - expectedErr: "json: cannot unmarshal number into Go value of type openrtb2.EID", + expectedErr: "cannot unmarshal []openrtb2.EID: expect { or n, but found 4", }, { description: "Malformed Eid Item Type", userExt: json.RawMessage(`{"eids":[{"source":42,"id":"anyID"}]}`), - expectedErr: "json: cannot unmarshal number into Go struct field EID.source of type string", + expectedErr: "cannot unmarshal openrtb2.EID.Source: expects \" or n, but found 4", }, } @@ -3075,7 +3089,7 @@ func TestCleanOpenRTBRequestsSChainMultipleBidders(t *testing.T) { } extRequest := &openrtb_ext.ExtRequest{} - err := json.Unmarshal(req.Ext, extRequest) + err := jsonutil.UnmarshalValid(req.Ext, extRequest) assert.NoErrorf(t, err, "Error unmarshaling inExt") auctionReq := AuctionRequest{ @@ -3159,7 +3173,7 @@ func TestCleanOpenRTBRequestsBidAdjustment(t *testing.T) { H: 600, }}, }, - Ext: json.RawMessage(`{"bidder":{"placementId":1}}`), + Ext: json.RawMessage(`{"bidder":{"placementId": 1}}`), }}, }, { @@ -3187,7 +3201,7 @@ func TestCleanOpenRTBRequestsBidAdjustment(t *testing.T) { H: 600, }}, }, - Ext: json.RawMessage(`{"bidder":{"placementId":1}}`), + Ext: json.RawMessage(`{"bidder":{"placementId": 1}}`), }}, }, } @@ -3229,60 +3243,145 @@ func TestCleanOpenRTBRequestsBidAdjustment(t *testing.T) { } func TestApplyFPD(t *testing.T) { - testCases := []struct { - description string - inputFpd firstpartydata.ResolvedFirstPartyData - inputRequest openrtb2.BidRequest - expectedRequest openrtb2.BidRequest + description string + inputFpd map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData + inputBidderName string + inputBidderCoreName string + inputBidderIsRequestAlias bool + inputRequest openrtb2.BidRequest + expectedRequest openrtb2.BidRequest }{ { - description: "req.Site defined; bidderFPD.Site not defined; expect request.Site remains the same", - inputFpd: firstpartydata.ResolvedFirstPartyData{Site: nil, App: nil, User: nil}, - inputRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, - expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, + description: "fpd-nil", + inputFpd: nil, + inputBidderName: "bidderFromRequest", + inputBidderCoreName: "bidderNormalized", + inputBidderIsRequestAlias: false, + inputRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, + expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, + }, + { + description: "fpd-bidderdata-nil", + inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{ + "bidderNormalized": nil, + }, + inputBidderName: "bidderFromRequest", + inputBidderCoreName: "bidderNormalized", + inputBidderIsRequestAlias: false, + inputRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, + expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, + }, + { + description: "fpd-bidderdata-notdefined", + inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{ + "differentBidder": {App: &openrtb2.App{ID: "AppId"}}, + }, + inputBidderName: "bidderFromRequest", + inputBidderCoreName: "bidderNormalized", + inputBidderIsRequestAlias: false, + inputRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, + expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, + }, + { + description: "fpd-bidderdata-alias", + inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{ + "alias": {App: &openrtb2.App{ID: "AppId"}}, + }, + inputBidderName: "alias", + inputBidderCoreName: "bidder", + inputBidderIsRequestAlias: true, + inputRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, + expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}}, + }, + { + description: "req.Site defined; bidderFPD.Site not defined; expect request.Site remains the same", + inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{ + "bidderNormalized": {Site: nil, App: nil, User: nil}, + }, + inputBidderName: "bidderFromRequest", + inputBidderCoreName: "bidderNormalized", + inputBidderIsRequestAlias: false, + inputRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, + expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, }, { description: "req.Site, req.App, req.User are not defined; bidderFPD.App, bidderFPD.Site and bidderFPD.User defined; " + "expect req.Site, req.App, req.User to be overriden by bidderFPD.App, bidderFPD.Site and bidderFPD.User", - inputFpd: firstpartydata.ResolvedFirstPartyData{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId"}}, - inputRequest: openrtb2.BidRequest{}, - expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId"}}, + inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{ + "bidderNormalized": {Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId"}}, + }, + inputBidderName: "bidderFromRequest", + inputBidderCoreName: "bidderNormalized", + inputBidderIsRequestAlias: false, + inputRequest: openrtb2.BidRequest{}, + expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId"}}, }, { - description: "req.Site, defined; bidderFPD.App defined; expect request.App to be overriden by bidderFPD.App; expect req.Site remains the same", - inputFpd: firstpartydata.ResolvedFirstPartyData{App: &openrtb2.App{ID: "AppId"}}, - inputRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, - expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}}, + description: "req.Site, defined; bidderFPD.App defined; expect request.App to be overriden by bidderFPD.App; expect req.Site remains the same", + inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{ + "bidderNormalized": {App: &openrtb2.App{ID: "AppId"}}, + }, + inputBidderName: "bidderFromRequest", + inputBidderCoreName: "bidderNormalized", + inputBidderIsRequestAlias: false, + inputRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}}, + expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}}, }, { - description: "req.Site, req.App defined; bidderFPD.App defined; expect request.App to be overriden by bidderFPD.App", - inputFpd: firstpartydata.ResolvedFirstPartyData{App: &openrtb2.App{ID: "AppId"}}, - inputRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "TestAppId"}}, - expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}}, + description: "req.Site, req.App defined; bidderFPD.App defined; expect request.App to be overriden by bidderFPD.App", + inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{ + "bidderNormalized": {App: &openrtb2.App{ID: "AppId"}}, + }, + inputBidderName: "bidderFromRequest", + inputBidderCoreName: "bidderNormalized", + inputBidderIsRequestAlias: false, + inputRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "TestAppId"}}, + expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}}, }, { - description: "req.User is defined; bidderFPD.User defined; req.User has BuyerUID. Expect to see user.BuyerUID in result request", - inputFpd: firstpartydata.ResolvedFirstPartyData{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId"}}, - inputRequest: openrtb2.BidRequest{User: &openrtb2.User{ID: "UserIdIn", BuyerUID: "12345"}}, - expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{ID: "UserId", BuyerUID: "12345"}, Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}}, + description: "req.User is defined; bidderFPD.User defined; req.User has BuyerUID. Expect to see user.BuyerUID in result request", + inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{ + "bidderNormalized": {Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId"}}, + }, + inputBidderName: "bidderFromRequest", + inputBidderCoreName: "bidderNormalized", + inputBidderIsRequestAlias: false, + inputRequest: openrtb2.BidRequest{User: &openrtb2.User{ID: "UserIdIn", BuyerUID: "12345"}}, + expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{ID: "UserId", BuyerUID: "12345"}, Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}}, }, { - description: "req.User is defined; bidderFPD.User defined; req.User has BuyerUID with zero length. Expect to see empty user.BuyerUID in result request", - inputFpd: firstpartydata.ResolvedFirstPartyData{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId"}}, - inputRequest: openrtb2.BidRequest{User: &openrtb2.User{ID: "UserIdIn", BuyerUID: ""}}, - expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{ID: "UserId"}, Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}}, + description: "req.User is defined; bidderFPD.User defined; req.User has BuyerUID with zero length. Expect to see empty user.BuyerUID in result request", + inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{ + "bidderNormalized": {Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId"}}, + }, + inputBidderName: "bidderFromRequest", + inputBidderCoreName: "bidderNormalized", + inputBidderIsRequestAlias: false, + inputRequest: openrtb2.BidRequest{User: &openrtb2.User{ID: "UserIdIn", BuyerUID: ""}}, + expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{ID: "UserId"}, Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}}, }, { - description: "req.User is not defined; bidderFPD.User defined and has BuyerUID. Expect to see user.BuyerUID in result request", - inputFpd: firstpartydata.ResolvedFirstPartyData{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId", BuyerUID: "FPDBuyerUID"}}, - inputRequest: openrtb2.BidRequest{}, - expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId", BuyerUID: "FPDBuyerUID"}}, + description: "req.User is not defined; bidderFPD.User defined and has BuyerUID. Expect to see user.BuyerUID in result request", + inputFpd: map[openrtb_ext.BidderName]*firstpartydata.ResolvedFirstPartyData{ + "bidderNormalized": {Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId", BuyerUID: "FPDBuyerUID"}}, + }, + inputBidderName: "bidderFromRequest", + inputBidderCoreName: "bidderNormalized", + inputBidderIsRequestAlias: false, + inputRequest: openrtb2.BidRequest{}, + expectedRequest: openrtb2.BidRequest{Site: &openrtb2.Site{ID: "SiteId"}, App: &openrtb2.App{ID: "AppId"}, User: &openrtb2.User{ID: "UserId", BuyerUID: "FPDBuyerUID"}}, }, } for _, testCase := range testCases { - applyFPD(&testCase.inputFpd, &testCase.inputRequest) + bidderRequest := BidderRequest{ + BidderName: openrtb_ext.BidderName(testCase.inputBidderName), + BidderCoreName: openrtb_ext.BidderName(testCase.inputBidderCoreName), + IsRequestAlias: testCase.inputBidderIsRequestAlias, + BidRequest: &testCase.inputRequest, + } + applyFPD(testCase.inputFpd, bidderRequest) assert.Equal(t, testCase.expectedRequest, testCase.inputRequest, fmt.Sprintf("incorrect request after applying fpd, testcase %s", testCase.description)) } } @@ -3537,7 +3636,7 @@ func TestCleanOpenRTBRequestsFilterBidderRequestExt(t *testing.T) { if test.inExt != nil { req.Ext = test.inExt extRequest = &openrtb_ext.ExtRequest{} - err := json.Unmarshal(req.Ext, extRequest) + err := jsonutil.UnmarshalValid(req.Ext, extRequest) assert.NoErrorf(t, err, test.desc+":Error unmarshaling inExt") } @@ -3586,6 +3685,10 @@ func (gs GPPMockSection) GetValue() string { return gs.value } +func (gs GPPMockSection) Encode(bool) []byte { + return nil +} + func TestGdprFromGPP(t *testing.T) { testCases := []struct { name string @@ -4298,7 +4401,7 @@ func TestGetPrebidMediaTypeForBid(t *testing.T) { { description: "Invalid bid ext", inputBid: openrtb2.Bid{ID: "bidId", ImpID: "impId", Ext: json.RawMessage(`[true`)}, - expectedError: "Failed to parse bid mediatype for impression \"impId\", unexpected end of JSON input", + expectedError: "Failed to parse bid mediatype for impression \"impId\", expect { or n, but found [", }, { description: "Bid ext is nil", @@ -4339,7 +4442,7 @@ func TestGetMediaTypeForBid(t *testing.T) { { description: "invalid bid ext", inputBid: openrtb2.Bid{ID: "bidId", ImpID: "impId", Ext: json.RawMessage(`{"prebid"`)}, - expectedError: "Failed to parse bid mediatype for impression \"impId\", unexpected end of JSON input", + expectedError: "Failed to parse bid mediatype for impression \"impId\", expect :, but found \x00", }, { description: "Valid bid ext with mtype native", @@ -4381,126 +4484,210 @@ func TestGetMediaTypeForBid(t *testing.T) { } func TestCleanOpenRTBRequestsActivities(t *testing.T) { + expectedUserDefault := openrtb2.User{ + ID: "our-id", + BuyerUID: "their-id", + Yob: 1982, + Gender: "test", + Ext: json.RawMessage(`{"data": 1, "test": 2}`), + Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.456), Lon: ptrutil.ToPtr(11.278)}, + EIDs: []openrtb2.EID{ + {Source: "eids-source"}, + }, + Data: []openrtb2.Data{{ID: "data-id"}}, + } + expectedDeviceDefault := openrtb2.Device{ + UA: deviceUA, + IP: "132.173.230.74", + Language: "EN", + DIDMD5: "DIDMD5", + IFA: "IFA", + DIDSHA1: "DIDSHA1", + DPIDMD5: "DPIDMD5", + DPIDSHA1: "DPIDSHA1", + MACMD5: "MACMD5", + MACSHA1: "MACSHA1", + Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.456), Lon: ptrutil.ToPtr(11.278)}, + } + + expectedSourceDefault := openrtb2.Source{ + TID: "testTID", + } + testCases := []struct { - name string - req *openrtb2.BidRequest - privacyConfig config.AccountPrivacy - componentName string - allow bool - expectedReqNumber int - expectedUserYOB int64 - expectedUserLat float64 - expectedDeviceDIDMD5 string - expectedSourceTID string + name string + req *openrtb2.BidRequest + privacyConfig config.AccountPrivacy + componentName string + allow bool + expectedReqNumber int + expectedUser openrtb2.User + expectedDevice openrtb2.Device + expectedSource openrtb2.Source + expectedImpExt json.RawMessage }{ { - name: "fetch_bids_request_with_one_bidder_allowed", - req: newBidRequest(t), - privacyConfig: getFetchBidsActivityConfig("appnexus", true), - expectedReqNumber: 1, - expectedUserYOB: 1982, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "testTID", - }, - { - name: "fetch_bids_request_with_one_bidder_not_allowed", - req: newBidRequest(t), - privacyConfig: getFetchBidsActivityConfig("appnexus", false), - expectedReqNumber: 0, - expectedUserYOB: 1982, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "testTID", - }, - { - name: "transmit_ufpd_allowed", - req: newBidRequest(t), - privacyConfig: getTransmitUFPDActivityConfig("appnexus", true), - expectedReqNumber: 1, - expectedUserYOB: 1982, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "testTID", - }, - { - name: "transmit_ufpd_deny", - req: newBidRequest(t), - privacyConfig: getTransmitUFPDActivityConfig("appnexus", false), - expectedReqNumber: 1, - expectedUserYOB: 0, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "", - expectedSourceTID: "testTID", - }, - { - name: "transmit_precise_geo_allowed", - req: newBidRequest(t), - privacyConfig: getTransmitPreciseGeoActivityConfig("appnexus", true), - expectedReqNumber: 1, - expectedUserYOB: 1982, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "testTID", - }, - { - name: "transmit_precise_geo_deny", - req: newBidRequest(t), - privacyConfig: getTransmitPreciseGeoActivityConfig("appnexus", false), - expectedReqNumber: 1, - expectedUserYOB: 1982, - expectedUserLat: 123.46, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "testTID", - }, - { - name: "transmit_tid_allowed", - req: newBidRequest(t), - privacyConfig: getTransmitTIDActivityConfig("appnexus", true), - expectedReqNumber: 1, - expectedUserYOB: 1982, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "testTID", - }, - { - name: "transmit_tid_deny", - req: newBidRequest(t), - privacyConfig: getTransmitTIDActivityConfig("appnexus", false), - expectedReqNumber: 1, - expectedUserYOB: 1982, - expectedUserLat: 123.456, - expectedDeviceDIDMD5: "some device ID hash", - expectedSourceTID: "", + name: "fetch_bids_request_with_one_bidder_allowed", + req: newBidRequest(t), + privacyConfig: getFetchBidsActivityConfig("appnexus", true), + expectedReqNumber: 1, + expectedUser: expectedUserDefault, + expectedDevice: expectedDeviceDefault, + expectedSource: expectedSourceDefault, + }, + { + name: "fetch_bids_request_with_one_bidder_not_allowed", + req: newBidRequest(t), + privacyConfig: getFetchBidsActivityConfig("appnexus", false), + expectedReqNumber: 0, + expectedUser: expectedUserDefault, + expectedDevice: expectedDeviceDefault, + expectedSource: expectedSourceDefault, + }, + { + name: "transmit_ufpd_allowed", + req: newBidRequest(t), + privacyConfig: getTransmitUFPDActivityConfig("appnexus", true), + expectedReqNumber: 1, + expectedUser: expectedUserDefault, + expectedDevice: expectedDeviceDefault, + expectedSource: expectedSourceDefault, + }, + { + //remove user.eids, user.ext.data.*, user.data.*, user.{id, buyeruid, yob, gender} + //and device-specific IDs + name: "transmit_ufpd_deny", + req: newBidRequest(t), + privacyConfig: getTransmitUFPDActivityConfig("appnexus", false), + expectedReqNumber: 1, + expectedUser: openrtb2.User{ + ID: "", + BuyerUID: "", + Yob: 0, + Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.456), Lon: ptrutil.ToPtr(11.278)}, + EIDs: nil, + Ext: json.RawMessage(`{"test":2}`), + Data: nil, + }, + expectedDevice: openrtb2.Device{ + UA: deviceUA, + Language: "EN", + IP: "132.173.230.74", + DIDMD5: "", + IFA: "", + DIDSHA1: "", + DPIDMD5: "", + DPIDSHA1: "", + MACMD5: "", + MACSHA1: "", + Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.456), Lon: ptrutil.ToPtr(11.278)}, + }, + expectedSource: expectedSourceDefault, + }, + { + name: "transmit_precise_geo_allowed", + req: newBidRequest(t), + privacyConfig: getTransmitPreciseGeoActivityConfig("appnexus", true), + expectedReqNumber: 1, + expectedUser: expectedUserDefault, + expectedDevice: expectedDeviceDefault, + expectedSource: expectedSourceDefault, + }, + { + //round user's geographic location by rounding off IP address and lat/lng data. + //this applies to both device.geo and user.geo + name: "transmit_precise_geo_deny", + req: newBidRequest(t), + privacyConfig: getTransmitPreciseGeoActivityConfig("appnexus", false), + expectedReqNumber: 1, + expectedUser: openrtb2.User{ + ID: "our-id", + BuyerUID: "their-id", + Yob: 1982, + Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.46), Lon: ptrutil.ToPtr(11.28)}, + Gender: "test", + Ext: json.RawMessage(`{"data": 1, "test": 2}`), + EIDs: []openrtb2.EID{ + {Source: "eids-source"}, + }, + Data: []openrtb2.Data{{ID: "data-id"}}, + }, + expectedDevice: openrtb2.Device{ + UA: deviceUA, + IP: "132.173.0.0", + Language: "EN", + DIDMD5: "DIDMD5", + IFA: "IFA", + DIDSHA1: "DIDSHA1", + DPIDMD5: "DPIDMD5", + DPIDSHA1: "DPIDSHA1", + MACMD5: "MACMD5", + MACSHA1: "MACSHA1", + Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.46), Lon: ptrutil.ToPtr(11.28)}, + }, + expectedSource: expectedSourceDefault, + }, + { + name: "transmit_tid_allowed", + req: newBidRequest(t), + privacyConfig: getTransmitTIDActivityConfig("appnexus", true), + expectedReqNumber: 1, + expectedUser: expectedUserDefault, + expectedDevice: expectedDeviceDefault, + expectedSource: expectedSourceDefault, + }, + { + //remove source.tid and imp.ext.tid + name: "transmit_tid_deny", + req: newBidRequest(t), + privacyConfig: getTransmitTIDActivityConfig("appnexus", false), + expectedReqNumber: 1, + expectedUser: expectedUserDefault, + expectedDevice: expectedDeviceDefault, + expectedSource: openrtb2.Source{ + TID: "", + }, + expectedImpExt: json.RawMessage(`{"bidder": {"placementId": 1}}`), }, } for _, test := range testCases { - activities := privacy.NewActivityControl(&test.privacyConfig) - auctionReq := AuctionRequest{ - BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: test.req}, - UserSyncs: &emptyUsersync{}, - Activities: activities, - } + t.Run(test.name, func(t *testing.T) { + activities := privacy.NewActivityControl(&test.privacyConfig) + auctionReq := AuctionRequest{ + BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: test.req}, + UserSyncs: &emptyUsersync{}, + Activities: activities, + Account: config.Account{Privacy: config.AccountPrivacy{ + IPv6Config: config.IPv6{ + AnonKeepBits: 32, + }, + IPv4Config: config.IPv4{ + AnonKeepBits: 16, + }, + }}, + } - bidderToSyncerKey := map[string]string{} - reqSplitter := &requestSplitter{ - bidderToSyncerKey: bidderToSyncerKey, - me: &metrics.MetricsEngineMock{}, - hostSChainNode: nil, - bidderInfo: config.BidderInfos{}, - } + bidderToSyncerKey := map[string]string{} + reqSplitter := &requestSplitter{ + bidderToSyncerKey: bidderToSyncerKey, + me: &metrics.MetricsEngineMock{}, + hostSChainNode: nil, + bidderInfo: config.BidderInfos{}, + } - t.Run(test.name, func(t *testing.T) { bidderRequests, _, errs := reqSplitter.cleanOpenRTBRequests(context.Background(), auctionReq, nil, gdpr.SignalNo, map[string]float64{}) assert.Empty(t, errs) assert.Len(t, bidderRequests, test.expectedReqNumber) if test.expectedReqNumber == 1 { - assert.Equal(t, test.expectedUserYOB, bidderRequests[0].BidRequest.User.Yob) - assert.Equal(t, test.expectedUserLat, bidderRequests[0].BidRequest.User.Geo.Lat) - assert.Equal(t, test.expectedDeviceDIDMD5, bidderRequests[0].BidRequest.Device.DIDMD5) - assert.Equal(t, test.expectedSourceTID, bidderRequests[0].BidRequest.Source.TID) + assert.Equal(t, &test.expectedUser, bidderRequests[0].BidRequest.User) + assert.Equal(t, &test.expectedDevice, bidderRequests[0].BidRequest.Device) + assert.Equal(t, &test.expectedSource, bidderRequests[0].BidRequest.Source) + + if len(test.expectedImpExt) > 0 { + assert.JSONEq(t, string(test.expectedImpExt), string(bidderRequests[0].BidRequest.Imp[0].Ext)) + } } }) } @@ -4671,3 +4858,243 @@ func TestApplyBidAdjustmentToFloor(t *testing.T) { }) } } + +func TestBuildRequestExtAlternateBidderCodes(t *testing.T) { + type testInput struct { + bidderNameRaw string + accABC *openrtb_ext.ExtAlternateBidderCodes + reqABC *openrtb_ext.ExtAlternateBidderCodes + } + testCases := []struct { + desc string + in testInput + expected *openrtb_ext.ExtAlternateBidderCodes + }{ + { + desc: "No biddername, nil reqABC and accABC", + in: testInput{}, + expected: nil, + }, + { + desc: "No biddername, non-nil reqABC", + in: testInput{ + reqABC: &openrtb_ext.ExtAlternateBidderCodes{}, + }, + expected: &openrtb_ext.ExtAlternateBidderCodes{}, + }, + { + desc: "No biddername, non-nil accABC", + in: testInput{ + accABC: &openrtb_ext.ExtAlternateBidderCodes{}, + }, + expected: &openrtb_ext.ExtAlternateBidderCodes{}, + }, + { + desc: "No biddername, non-nil reqABC nor accABC", + in: testInput{ + reqABC: &openrtb_ext.ExtAlternateBidderCodes{}, + accABC: &openrtb_ext.ExtAlternateBidderCodes{}, + }, + expected: &openrtb_ext.ExtAlternateBidderCodes{}, + }, + { + desc: "non-nil reqABC", + in: testInput{ + bidderNameRaw: "pubmatic", + reqABC: &openrtb_ext.ExtAlternateBidderCodes{}, + }, + expected: &openrtb_ext.ExtAlternateBidderCodes{}, + }, + { + desc: "non-nil accABC", + in: testInput{ + bidderNameRaw: "pubmatic", + accABC: &openrtb_ext.ExtAlternateBidderCodes{}, + }, + expected: &openrtb_ext.ExtAlternateBidderCodes{}, + }, + { + desc: "both reqABC and accABC enabled and bidder matches elements in accABC but reqABC comes first", + in: testInput{ + bidderNameRaw: "PUBmatic", + reqABC: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "appnexus": { + AllowedBidderCodes: []string{"pubCode1"}, + }, + }, + }, + accABC: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "PubMatic": { + AllowedBidderCodes: []string{"pubCode2"}, + }, + }, + }, + }, + expected: &openrtb_ext.ExtAlternateBidderCodes{Enabled: true}, + }, + { + desc: "both reqABC and accABC enabled and bidder matches elements in both but we prioritize reqABC", + in: testInput{ + bidderNameRaw: "pubmatic", + reqABC: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "PubMatic": { + AllowedBidderCodes: []string{"pubCode"}, + }, + }, + }, + accABC: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "appnexus": { + AllowedBidderCodes: []string{"anxsCode"}, + }, + }, + }, + }, + expected: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "pubmatic": { + AllowedBidderCodes: []string{"pubCode"}, + }, + }, + }, + }, + { + desc: "nil reqABC non-nil accABC enabled and bidder matches elements in accABC", + in: testInput{ + bidderNameRaw: "APPnexus", + accABC: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "appnexus": { + AllowedBidderCodes: []string{"anxsCode"}, + }, + }, + }, + }, + expected: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "APPnexus": { + AllowedBidderCodes: []string{"anxsCode"}, + }, + }, + }, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + alternateBidderCodes := buildRequestExtAlternateBidderCodes(tc.in.bidderNameRaw, tc.in.accABC, tc.in.reqABC) + assert.Equal(t, tc.expected, alternateBidderCodes) + }) + } +} + +func TestCopyExtAlternateBidderCodes(t *testing.T) { + type testInput struct { + bidder string + alternateBidderCodes *openrtb_ext.ExtAlternateBidderCodes + } + testCases := []struct { + desc string + in testInput + expected *openrtb_ext.ExtAlternateBidderCodes + }{ + { + desc: "pass a nil alternateBidderCodes argument, expect nil output", + in: testInput{}, + expected: nil, + }, + { + desc: "non-nil alternateBidderCodes argument but bidder doesn't match", + in: testInput{ + alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + }, + }, + expected: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + }, + }, + { + desc: "non-nil alternateBidderCodes argument bidder is identical to one element in map", + in: testInput{ + bidder: "appnexus", + alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "appnexus": { + AllowedBidderCodes: []string{"adnxs"}, + }, + }, + }, + }, + expected: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "appnexus": { + AllowedBidderCodes: []string{"adnxs"}, + }, + }, + }, + }, + { + desc: "case insensitive match, keep bidder casing in output", + in: testInput{ + bidder: "AppNexus", + alternateBidderCodes: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "appnexus": { + AllowedBidderCodes: []string{"adnxs"}, + }, + }, + }, + }, + expected: &openrtb_ext.ExtAlternateBidderCodes{ + Enabled: true, + Bidders: map[string]openrtb_ext.ExtAdapterAlternateBidderCodes{ + "AppNexus": { + AllowedBidderCodes: []string{"adnxs"}, + }, + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + alternateBidderCodes := copyExtAlternateBidderCodes(tc.in.bidder, tc.in.alternateBidderCodes) + assert.Equal(t, tc.expected, alternateBidderCodes) + }) + } +} + +func TestBuildBidResponseRequestBidderName(t *testing.T) { + bidderImpResponses := stored_responses.BidderImpsWithBidResponses{ + openrtb_ext.BidderName("appnexus"): {"impId1": json.RawMessage(`{}`), "impId2": json.RawMessage(`{}`)}, + openrtb_ext.BidderName("appneXUS"): {"impId3": json.RawMessage(`{}`), "impId4": json.RawMessage(`{}`)}, + } + + bidderImpReplaceImpID := stored_responses.BidderImpReplaceImpID{ + "appnexus": {"impId1": true, "impId2": false}, + "appneXUS": {"impId3": true, "impId4": false}, + } + result := buildBidResponseRequest(nil, bidderImpResponses, nil, bidderImpReplaceImpID) + + resultAppnexus := result["appnexus"] + assert.Equal(t, resultAppnexus.BidderName, openrtb_ext.BidderName("appnexus")) + assert.Equal(t, resultAppnexus.ImpReplaceImpId, map[string]bool{"impId1": true, "impId2": false}) + + resultAppneXUS := result["appneXUS"] + assert.Equal(t, resultAppneXUS.BidderName, openrtb_ext.BidderName("appneXUS")) + assert.Equal(t, resultAppneXUS.ImpReplaceImpId, map[string]bool{"impId3": true, "impId4": false}) + +} diff --git a/experiment/adscert/inprocesssigner.go b/experiment/adscert/inprocesssigner.go index 604287f9ed6..eabd35ebb95 100644 --- a/experiment/adscert/inprocesssigner.go +++ b/experiment/adscert/inprocesssigner.go @@ -2,12 +2,13 @@ package adscert import ( "crypto/rand" + "time" + "github.com/IABTechLab/adscert/pkg/adscert/api" "github.com/IABTechLab/adscert/pkg/adscert/discovery" "github.com/IABTechLab/adscert/pkg/adscert/signatory" "github.com/benbjohnson/clock" - "github.com/prebid/prebid-server/config" - "time" + "github.com/prebid/prebid-server/v2/config" ) // inProcessSigner holds the signatory to add adsCert header to requests using in process go library diff --git a/experiment/adscert/remotesigner.go b/experiment/adscert/remotesigner.go index 3c9479560b2..d23dad201d3 100644 --- a/experiment/adscert/remotesigner.go +++ b/experiment/adscert/remotesigner.go @@ -2,12 +2,13 @@ package adscert import ( "fmt" + "time" + "github.com/IABTechLab/adscert/pkg/adscert/api" "github.com/IABTechLab/adscert/pkg/adscert/signatory" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" - "time" ) // remoteSigner holds the signatory to add adsCert header to requests using remote signing server diff --git a/experiment/adscert/signer.go b/experiment/adscert/signer.go index 08b3f655fa2..f060f957149 100644 --- a/experiment/adscert/signer.go +++ b/experiment/adscert/signer.go @@ -2,10 +2,11 @@ package adscert import ( "fmt" + "github.com/IABTechLab/adscert/pkg/adscert/api" "github.com/IABTechLab/adscert/pkg/adscert/logger" "github.com/IABTechLab/adscert/pkg/adscert/signatory" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" ) const SignHeader = "X-Ads-Cert-Auth" diff --git a/experiment/adscert/signer_test.go b/experiment/adscert/signer_test.go index d6d02175d95..fceb2e5c79c 100644 --- a/experiment/adscert/signer_test.go +++ b/experiment/adscert/signer_test.go @@ -2,10 +2,11 @@ package adscert import ( "errors" + "testing" + "github.com/IABTechLab/adscert/pkg/adscert/api" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" "github.com/stretchr/testify/assert" - "testing" ) func TestNilSigner(t *testing.T) { diff --git a/firstpartydata/extmerger.go b/firstpartydata/extmerger.go index 119fa8a4c3c..f3196bea996 100644 --- a/firstpartydata/extmerger.go +++ b/firstpartydata/extmerger.go @@ -5,7 +5,7 @@ import ( "errors" "fmt" - "github.com/prebid/prebid-server/util/sliceutil" + "github.com/prebid/prebid-server/v2/util/sliceutil" jsonpatch "gopkg.in/evanphx/json-patch.v4" ) diff --git a/firstpartydata/extmerger_test.go b/firstpartydata/extmerger_test.go index 784163ac313..4107b0d1144 100644 --- a/firstpartydata/extmerger_test.go +++ b/firstpartydata/extmerger_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/util/sliceutil" + "github.com/prebid/prebid-server/v2/util/sliceutil" "github.com/stretchr/testify/assert" ) diff --git a/firstpartydata/first_party_data.go b/firstpartydata/first_party_data.go index 0fde931d445..8c77f61a3d6 100644 --- a/firstpartydata/first_party_data.go +++ b/firstpartydata/first_party_data.go @@ -4,13 +4,14 @@ import ( "encoding/json" "fmt" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" jsonpatch "gopkg.in/evanphx/json-patch.v4" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/ortb" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/ortb" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) const ( @@ -120,7 +121,9 @@ func ResolveFPD(bidRequest *openrtb2.BidRequest, fpdBidderConfigData map[openrtb } } else { // only bidders in global bidder list will receive global data and bidder specific data - for _, bidderName := range biddersWithGlobalFPD { + for _, bidder := range biddersWithGlobalFPD { + bidderName := openrtb_ext.NormalizeBidderNameOrUnchanged(bidder) + if _, present := allBiddersTable[string(bidderName)]; !present { allBiddersTable[string(bidderName)] = struct{}{} } @@ -213,7 +216,7 @@ func mergeUser(v *openrtb2.User, overrideJSON json.RawMessage) error { } // Merge - if err := json.Unmarshal(overrideJSON, &v); err != nil { + if err := jsonutil.Unmarshal(overrideJSON, &v); err != nil { return err } @@ -307,7 +310,7 @@ func mergeSite(v *openrtb2.Site, overrideJSON json.RawMessage, bidderName string } // Merge - if err := json.Unmarshal(overrideJSON, &v); err != nil { + if err := jsonutil.Unmarshal(overrideJSON, &v); err != nil { return err } @@ -424,7 +427,7 @@ func mergeApp(v *openrtb2.App, overrideJSON json.RawMessage) error { } // Merge - if err := json.Unmarshal(overrideJSON, &v); err != nil { + if err := jsonutil.Unmarshal(overrideJSON, &v); err != nil { return err } @@ -462,12 +465,14 @@ func buildExtData(data []byte) []byte { // ExtractBidderConfigFPD extracts bidder specific configs from req.ext.prebid.bidderconfig func ExtractBidderConfigFPD(reqExt *openrtb_ext.RequestExt) (map[openrtb_ext.BidderName]*openrtb_ext.ORTB2, error) { fpd := make(map[openrtb_ext.BidderName]*openrtb_ext.ORTB2) + reqExtPrebid := reqExt.GetPrebid() if reqExtPrebid != nil { for _, bidderConfig := range reqExtPrebid.BidderConfigs { for _, bidder := range bidderConfig.Bidders { - if _, present := fpd[openrtb_ext.BidderName(bidder)]; present { - //if bidder has duplicated config - throw an error + bidderName := openrtb_ext.NormalizeBidderNameOrUnchanged(bidder) + + if _, duplicate := fpd[bidderName]; duplicate { return nil, &errortypes.BadInput{ Message: fmt.Sprintf("multiple First Party Data bidder configs provided for bidder: %s", bidder), } @@ -476,18 +481,12 @@ func ExtractBidderConfigFPD(reqExt *openrtb_ext.RequestExt) (map[openrtb_ext.Bid fpdBidderData := &openrtb_ext.ORTB2{} if bidderConfig.Config != nil && bidderConfig.Config.ORTB2 != nil { - if bidderConfig.Config.ORTB2.Site != nil { - fpdBidderData.Site = bidderConfig.Config.ORTB2.Site - } - if bidderConfig.Config.ORTB2.App != nil { - fpdBidderData.App = bidderConfig.Config.ORTB2.App - } - if bidderConfig.Config.ORTB2.User != nil { - fpdBidderData.User = bidderConfig.Config.ORTB2.User - } + fpdBidderData.Site = bidderConfig.Config.ORTB2.Site + fpdBidderData.App = bidderConfig.Config.ORTB2.App + fpdBidderData.User = bidderConfig.Config.ORTB2.User } - fpd[openrtb_ext.BidderName(bidder)] = fpdBidderData + fpd[bidderName] = fpdBidderData } } reqExtPrebid.BidderConfigs = nil diff --git a/firstpartydata/first_party_data_test.go b/firstpartydata/first_party_data_test.go index 4c9cd7ad5e8..f417a24d7e7 100644 --- a/firstpartydata/first_party_data_test.go +++ b/firstpartydata/first_party_data_test.go @@ -3,12 +3,15 @@ package firstpartydata import ( "encoding/json" "os" + "path/filepath" "reflect" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -474,13 +477,13 @@ func TestExtractBidderConfigFPD(t *testing.T) { for _, test := range tests { t.Run(test.Name(), func(t *testing.T) { - filePath := testPath + "/" + test.Name() + path := filepath.Join(testPath, test.Name()) - fpdFile, err := loadFpdFile(filePath) - require.NoError(t, err, "Cannot Load Test") + testFile, err := loadTestFile[fpdFile](path) + require.NoError(t, err, "Load Test File") givenRequestExtPrebid := &openrtb_ext.ExtRequestPrebid{} - err = json.Unmarshal(fpdFile.InputRequestData, givenRequestExtPrebid) + err = jsonutil.UnmarshalValid(testFile.InputRequestData, givenRequestExtPrebid) require.NoError(t, err, "Cannot Load Test Conditions") testRequest := &openrtb_ext.RequestExt{} @@ -490,15 +493,17 @@ func TestExtractBidderConfigFPD(t *testing.T) { results, err := ExtractBidderConfigFPD(testRequest) // assert errors - if len(fpdFile.ValidationErrors) > 0 { - require.EqualError(t, err, fpdFile.ValidationErrors[0].Message, "Expected Error Not Received") + if len(testFile.ValidationErrors) > 0 { + require.EqualError(t, err, testFile.ValidationErrors[0].Message, "Expected Error Not Received") } else { require.NoError(t, err, "Error Not Expected") assert.Nil(t, testRequest.GetPrebid().BidderConfigs, "Bidder specific FPD config should be removed from request") } // assert fpd (with normalization for nicer looking tests) - for bidderName, expectedFPD := range fpdFile.BidderConfigFPD { + for bidderName, expectedFPD := range testFile.BidderConfigFPD { + require.Contains(t, results, bidderName) + if expectedFPD.App != nil { assert.JSONEq(t, string(expectedFPD.App), string(results[bidderName].App), "app is incorrect") } else { @@ -520,7 +525,6 @@ func TestExtractBidderConfigFPD(t *testing.T) { }) } } - func TestResolveFPD(t *testing.T) { testPath := "tests/resolvefpd" @@ -529,141 +533,137 @@ func TestResolveFPD(t *testing.T) { for _, test := range tests { t.Run(test.Name(), func(t *testing.T) { - filePath := testPath + "/" + test.Name() + path := filepath.Join(testPath, test.Name()) - fpdFile, err := loadFpdFile(filePath) - require.NoError(t, err, "Cannot Load Test") + testFile, err := loadTestFile[fpdFileForResolveFPD](path) + require.NoError(t, err, "Load Test File") request := &openrtb2.BidRequest{} - err = json.Unmarshal(fpdFile.InputRequestData, &request) + err = jsonutil.UnmarshalValid(testFile.InputRequestData, &request) require.NoError(t, err, "Cannot Load Request") originalRequest := &openrtb2.BidRequest{} - err = json.Unmarshal(fpdFile.InputRequestData, &originalRequest) + err = jsonutil.UnmarshalValid(testFile.InputRequestData, &originalRequest) require.NoError(t, err, "Cannot Load Request") - outputReq := &openrtb2.BidRequest{} - err = json.Unmarshal(fpdFile.OutputRequestData, &outputReq) - require.NoError(t, err, "Cannot Load Output Request") - reqExtFPD := make(map[string][]byte) - reqExtFPD["site"] = fpdFile.GlobalFPD["site"] - reqExtFPD["app"] = fpdFile.GlobalFPD["app"] - reqExtFPD["user"] = fpdFile.GlobalFPD["user"] + reqExtFPD["site"] = testFile.GlobalFPD["site"] + reqExtFPD["app"] = testFile.GlobalFPD["app"] + reqExtFPD["user"] = testFile.GlobalFPD["user"] reqFPD := make(map[string][]openrtb2.Data, 3) - reqFPDSiteContentData := fpdFile.GlobalFPD[siteContentDataKey] + reqFPDSiteContentData := testFile.GlobalFPD[siteContentDataKey] if len(reqFPDSiteContentData) > 0 { var siteConData []openrtb2.Data - err = json.Unmarshal(reqFPDSiteContentData, &siteConData) + err = jsonutil.UnmarshalValid(reqFPDSiteContentData, &siteConData) if err != nil { t.Errorf("Unable to unmarshal site.content.data:") } reqFPD[siteContentDataKey] = siteConData } - reqFPDAppContentData := fpdFile.GlobalFPD[appContentDataKey] + reqFPDAppContentData := testFile.GlobalFPD[appContentDataKey] if len(reqFPDAppContentData) > 0 { var appConData []openrtb2.Data - err = json.Unmarshal(reqFPDAppContentData, &appConData) + err = jsonutil.UnmarshalValid(reqFPDAppContentData, &appConData) if err != nil { t.Errorf("Unable to unmarshal app.content.data: ") } reqFPD[appContentDataKey] = appConData } - reqFPDUserData := fpdFile.GlobalFPD[userDataKey] + reqFPDUserData := testFile.GlobalFPD[userDataKey] if len(reqFPDUserData) > 0 { var userData []openrtb2.Data - err = json.Unmarshal(reqFPDUserData, &userData) + err = jsonutil.UnmarshalValid(reqFPDUserData, &userData) if err != nil { t.Errorf("Unable to unmarshal app.content.data: ") } reqFPD[userDataKey] = userData } - if fpdFile.BidderConfigFPD == nil { - fpdFile.BidderConfigFPD = make(map[openrtb_ext.BidderName]*openrtb_ext.ORTB2) - fpdFile.BidderConfigFPD["appnexus"] = &openrtb_ext.ORTB2{} - } // run test - resultFPD, errL := ResolveFPD(request, fpdFile.BidderConfigFPD, reqExtFPD, reqFPD, []string{"appnexus"}) + resultFPD, errL := ResolveFPD(request, testFile.BidderConfigFPD, reqExtFPD, reqFPD, testFile.BiddersWithGlobalFPD) if len(errL) == 0 { assert.Equal(t, request, originalRequest, "Original request should not be modified") - bidderFPD := resultFPD["appnexus"] - - if outputReq.Site != nil && len(outputReq.Site.Ext) > 0 { - resSiteExt := bidderFPD.Site.Ext - expectedSiteExt := outputReq.Site.Ext - bidderFPD.Site.Ext = nil - outputReq.Site.Ext = nil - assert.JSONEq(t, string(expectedSiteExt), string(resSiteExt), "site.ext is incorrect") - - assert.Equal(t, outputReq.Site, bidderFPD.Site, "Site is incorrect") + expectedResultKeys := []string{} + for k := range testFile.OutputRequestData { + expectedResultKeys = append(expectedResultKeys, k.String()) } - if outputReq.App != nil && len(outputReq.App.Ext) > 0 { - resAppExt := bidderFPD.App.Ext - expectedAppExt := outputReq.App.Ext - bidderFPD.App.Ext = nil - outputReq.App.Ext = nil - - assert.JSONEq(t, string(expectedAppExt), string(resAppExt), "app.ext is incorrect") - - assert.Equal(t, outputReq.App, bidderFPD.App, "App is incorrect") + actualResultKeys := []string{} + for k := range resultFPD { + actualResultKeys = append(actualResultKeys, k.String()) } - if outputReq.User != nil && len(outputReq.User.Ext) > 0 { - resUserExt := bidderFPD.User.Ext - expectedUserExt := outputReq.User.Ext - bidderFPD.User.Ext = nil - outputReq.User.Ext = nil - assert.JSONEq(t, string(expectedUserExt), string(resUserExt), "user.ext is incorrect") - - assert.Equal(t, outputReq.User, bidderFPD.User, "User is incorrect") + require.ElementsMatch(t, expectedResultKeys, actualResultKeys) + + for k, outputReq := range testFile.OutputRequestData { + bidderFPD := resultFPD[k] + + if outputReq.Site != nil && len(outputReq.Site.Ext) > 0 { + resSiteExt := bidderFPD.Site.Ext + expectedSiteExt := outputReq.Site.Ext + bidderFPD.Site.Ext = nil + outputReq.Site.Ext = nil + assert.JSONEq(t, string(expectedSiteExt), string(resSiteExt), "site.ext is incorrect") + assert.Equal(t, outputReq.Site, bidderFPD.Site, "Site is incorrect") + } + if outputReq.App != nil && len(outputReq.App.Ext) > 0 { + resAppExt := bidderFPD.App.Ext + expectedAppExt := outputReq.App.Ext + bidderFPD.App.Ext = nil + outputReq.App.Ext = nil + assert.JSONEq(t, string(expectedAppExt), string(resAppExt), "app.ext is incorrect") + assert.Equal(t, outputReq.App, bidderFPD.App, "App is incorrect") + } + if outputReq.User != nil && len(outputReq.User.Ext) > 0 { + resUserExt := bidderFPD.User.Ext + expectedUserExt := outputReq.User.Ext + bidderFPD.User.Ext = nil + outputReq.User.Ext = nil + assert.JSONEq(t, string(expectedUserExt), string(resUserExt), "user.ext is incorrect") + assert.Equal(t, outputReq.User, bidderFPD.User, "User is incorrect") + } } } else { - assert.ElementsMatch(t, errL, fpdFile.ValidationErrors, "Incorrect first party data warning message") + assert.ElementsMatch(t, errL, testFile.ValidationErrors, "Incorrect first party data warning message") } }) } } - func TestExtractFPDForBidders(t *testing.T) { if specFiles, err := os.ReadDir("./tests/extractfpdforbidders"); err == nil { for _, specFile := range specFiles { - fileName := "./tests/extractfpdforbidders/" + specFile.Name() - - fpdFile, err := loadFpdFile(fileName) + path := filepath.Join("./tests/extractfpdforbidders/", specFile.Name()) - if err != nil { - t.Errorf("Unable to load file: %s", fileName) - } + testFile, err := loadTestFile[fpdFile](path) + require.NoError(t, err, "Load Test File") var expectedRequest openrtb2.BidRequest - err = json.Unmarshal(fpdFile.OutputRequestData, &expectedRequest) + err = jsonutil.UnmarshalValid(testFile.OutputRequestData, &expectedRequest) if err != nil { - t.Errorf("Unable to unmarshal input request: %s", fileName) + t.Errorf("Unable to unmarshal input request: %s", path) } resultRequest := &openrtb_ext.RequestWrapper{} resultRequest.BidRequest = &openrtb2.BidRequest{} - err = json.Unmarshal(fpdFile.InputRequestData, resultRequest.BidRequest) + err = jsonutil.UnmarshalValid(testFile.InputRequestData, resultRequest.BidRequest) assert.NoError(t, err, "Error should be nil") resultFPD, errL := ExtractFPDForBidders(resultRequest) - if len(fpdFile.ValidationErrors) > 0 { - assert.Equal(t, len(fpdFile.ValidationErrors), len(errL), "Incorrect number of errors was returned") - assert.ElementsMatch(t, errL, fpdFile.ValidationErrors, "Incorrect errors were returned") + if len(testFile.ValidationErrors) > 0 { + assert.Equal(t, len(testFile.ValidationErrors), len(errL), "Incorrect number of errors was returned") + assert.ElementsMatch(t, errL, testFile.ValidationErrors, "Incorrect errors were returned") //in case or error no further assertions needed continue } assert.Empty(t, errL, "Error should be empty") - assert.Equal(t, len(resultFPD), len(fpdFile.BiddersFPDResolved)) + assert.Equal(t, len(resultFPD), len(testFile.BiddersFPDResolved)) - for bidderName, expectedValue := range fpdFile.BiddersFPDResolved { + for bidderName, expectedValue := range testFile.BiddersFPDResolved { actualValue := resultFPD[bidderName] if expectedValue.Site != nil { if len(expectedValue.Site.Ext) > 0 { @@ -715,34 +715,10 @@ func TestExtractFPDForBidders(t *testing.T) { } assert.Equal(t, expectedRequest.User, resultRequest.BidRequest.User, "Incorrect user in request") } - } } } -func loadFpdFile(filename string) (fpdFile, error) { - var fileData fpdFile - fileContents, err := os.ReadFile(filename) - if err != nil { - return fileData, err - } - err = json.Unmarshal(fileContents, &fileData) - if err != nil { - return fileData, err - } - - return fileData, nil -} - -type fpdFile struct { - InputRequestData json.RawMessage `json:"inputRequestData,omitempty"` - OutputRequestData json.RawMessage `json:"outputRequestData,omitempty"` - BidderConfigFPD map[openrtb_ext.BidderName]*openrtb_ext.ORTB2 `json:"bidderConfigFPD,omitempty"` - BiddersFPDResolved map[openrtb_ext.BidderName]*ResolvedFirstPartyData `json:"biddersFPDResolved,omitempty"` - GlobalFPD map[string]json.RawMessage `json:"globalFPD,omitempty"` - ValidationErrors []*errortypes.BadInput `json:"validationErrors,omitempty"` -} - func TestResolveUser(t *testing.T) { testCases := []struct { description string @@ -751,7 +727,7 @@ func TestResolveUser(t *testing.T) { globalFPD map[string][]byte openRtbGlobalFPD map[string][]openrtb2.Data expectedUser *openrtb2.User - expectedError string + expectError bool }{ { description: "FPD config and bid request user are not specified", @@ -794,7 +770,7 @@ func TestResolveUser(t *testing.T) { fpdConfig: &openrtb_ext.ORTB2{User: json.RawMessage(`{"id": "test1"}`)}, bidRequestUser: &openrtb2.User{ID: "test2", Ext: json.RawMessage(`{"data":{"inputFPDUserData":"inputFPDUserDataValue"}}`)}, globalFPD: map[string][]byte{userKey: []byte(`malformed`)}, - expectedError: "Invalid JSON Patch", + expectError: true, }, { description: "bid request and openrtb global fpd user are specified, no input user ext", @@ -863,18 +839,18 @@ func TestResolveUser(t *testing.T) { }, Ext: json.RawMessage(`{"key":"value","test":1}`), }, - expectedError: "invalid character 'm' looking for beginning of object key string", + expectError: true, }, } for _, test := range testCases { t.Run(test.description, func(t *testing.T) { resultUser, err := resolveUser(test.fpdConfig, test.bidRequestUser, test.globalFPD, test.openRtbGlobalFPD, "bidderA") - if test.expectedError == "" { + if test.expectError { + assert.Error(t, err, "expected error incorrect") + } else { assert.NoError(t, err, "unexpected error returned") assert.Equal(t, test.expectedUser, resultUser, "Result user is incorrect") - } else { - assert.EqualError(t, err, test.expectedError, "expected error incorrect") } }) } @@ -888,16 +864,16 @@ func TestResolveSite(t *testing.T) { globalFPD map[string][]byte openRtbGlobalFPD map[string][]openrtb2.Data expectedSite *openrtb2.Site - expectedError string + expectError bool }{ { description: "FPD config and bid request site are not specified", expectedSite: nil, }, { - description: "FPD config site only is specified", - fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id": "test"}`)}, - expectedError: "incorrect First Party Data for bidder bidderA: Site object is not defined in request, but defined in FPD config", + description: "FPD config site only is specified", + fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id": "test"}`)}, + expectError: true, }, { description: "FPD config and bid request site are specified", @@ -931,7 +907,7 @@ func TestResolveSite(t *testing.T) { fpdConfig: &openrtb_ext.ORTB2{Site: json.RawMessage(`{"id": "test1"}`)}, bidRequestSite: &openrtb2.Site{ID: "test2", Ext: json.RawMessage(`{"data":{"inputFPDSiteData":"inputFPDSiteDataValue"}}`)}, globalFPD: map[string][]byte{siteKey: []byte(`malformed`)}, - expectedError: "Invalid JSON Patch", + expectError: true, }, { description: "bid request and openrtb global fpd site are specified, no input site ext", @@ -1023,18 +999,18 @@ func TestResolveSite(t *testing.T) { }}, Ext: json.RawMessage(`{"key":"value","test":1}`), }, - expectedError: "invalid character 'm' looking for beginning of object key string", + expectError: true, }, } for _, test := range testCases { t.Run(test.description, func(t *testing.T) { resultSite, err := resolveSite(test.fpdConfig, test.bidRequestSite, test.globalFPD, test.openRtbGlobalFPD, "bidderA") - if test.expectedError == "" { + if test.expectError { + assert.Error(t, err) + } else { assert.NoError(t, err, "unexpected error returned") assert.Equal(t, test.expectedSite, resultSite, "Result site is incorrect") - } else { - assert.EqualError(t, err, test.expectedError, "expected error incorrect") } }) } @@ -1048,16 +1024,16 @@ func TestResolveApp(t *testing.T) { globalFPD map[string][]byte openRtbGlobalFPD map[string][]openrtb2.Data expectedApp *openrtb2.App - expectedError string + expectError bool }{ { description: "FPD config and bid request app are not specified", expectedApp: nil, }, { - description: "FPD config app only is specified", - fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id": "test"}`)}, - expectedError: "incorrect First Party Data for bidder bidderA: App object is not defined in request, but defined in FPD config", + description: "FPD config app only is specified", + fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id": "test"}`)}, + expectError: true, }, { description: "FPD config and bid request app are specified", @@ -1091,7 +1067,7 @@ func TestResolveApp(t *testing.T) { fpdConfig: &openrtb_ext.ORTB2{App: json.RawMessage(`{"id": "test1"}`)}, bidRequestApp: &openrtb2.App{ID: "test2", Ext: json.RawMessage(`{"data":{"inputFPDAppData":"inputFPDAppDataValue"}}`)}, globalFPD: map[string][]byte{appKey: []byte(`malformed`)}, - expectedError: "Invalid JSON Patch", + expectError: true, }, { description: "bid request and openrtb global fpd app are specified, no input app ext", @@ -1183,18 +1159,18 @@ func TestResolveApp(t *testing.T) { }}, Ext: json.RawMessage(`{"key":"value","test":1}`), }, - expectedError: "invalid character 'm' looking for beginning of object key string", + expectError: true, }, } for _, test := range testCases { t.Run(test.description, func(t *testing.T) { resultApp, err := resolveApp(test.fpdConfig, test.bidRequestApp, test.globalFPD, test.openRtbGlobalFPD, "bidderA") - if test.expectedError == "" { - assert.NoError(t, err, "unexpected error returned") - assert.Equal(t, test.expectedApp, resultApp, "Result app is incorrect") + if test.expectError { + assert.Error(t, err) } else { - assert.EqualError(t, err, test.expectedError, "expected error incorrect") + assert.NoError(t, err) + assert.Equal(t, test.expectedApp, resultApp, "Result app is incorrect") } }) } @@ -1245,7 +1221,7 @@ func TestMergeUser(t *testing.T) { givenUser openrtb2.User givenFPD json.RawMessage expectedUser openrtb2.User - expectedErr string + expectError bool }{ { name: "empty", @@ -1269,13 +1245,13 @@ func TestMergeUser(t *testing.T) { name: "toplevel-ext-err", givenUser: openrtb2.User{ID: "1", Ext: []byte(`malformed`)}, givenFPD: []byte(`{"id":"2"}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "nested-geo", - givenUser: openrtb2.User{Geo: &openrtb2.Geo{Lat: 1}}, + givenUser: openrtb2.User{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(1.0)}}, givenFPD: []byte(`{"geo":{"lat": 2}}`), - expectedUser: openrtb2.User{Geo: &openrtb2.Geo{Lat: 2}}, + expectedUser: openrtb2.User{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(2.0)}}, }, { name: "nested-geo-ext", @@ -1293,13 +1269,13 @@ func TestMergeUser(t *testing.T) { name: "nested-geo-ext-err", givenUser: openrtb2.User{Geo: &openrtb2.Geo{Ext: []byte(`malformed`)}}, givenFPD: []byte(`{"geo":{"ext":{"b":100,"c":3}}}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "fpd-err", givenUser: openrtb2.User{ID: "1", Ext: []byte(`{"a":1}`)}, givenFPD: []byte(`malformed`), - expectedErr: "invalid character 'm' looking for beginning of value", + expectError: true, }, } @@ -1307,11 +1283,11 @@ func TestMergeUser(t *testing.T) { t.Run(test.name, func(t *testing.T) { err := mergeUser(&test.givenUser, test.givenFPD) - if test.expectedErr == "" { - assert.NoError(t, err, "unexpected error returned") - assert.Equal(t, test.expectedUser, test.givenUser, "result user is incorrect") + if test.expectError { + assert.Error(t, err) } else { - assert.EqualError(t, err, test.expectedErr, "expected error incorrect") + assert.NoError(t, err) + assert.Equal(t, test.expectedUser, test.givenUser, "result user is incorrect") } }) } @@ -1323,7 +1299,7 @@ func TestMergeApp(t *testing.T) { givenApp openrtb2.App givenFPD json.RawMessage expectedApp openrtb2.App - expectedErr string + expectError bool }{ { name: "empty", @@ -1347,7 +1323,7 @@ func TestMergeApp(t *testing.T) { name: "toplevel-ext-err", givenApp: openrtb2.App{ID: "1", Ext: []byte(`malformed`)}, givenFPD: []byte(`{"id":"2"}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "nested-publisher", @@ -1443,37 +1419,37 @@ func TestMergeApp(t *testing.T) { name: "nested-publisher-ext-err", givenApp: openrtb2.App{Publisher: &openrtb2.Publisher{Ext: []byte(`malformed`)}}, givenFPD: []byte(`{"publisher":{"ext":{"b":100,"c":3}}}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "nested-content-ext-err", givenApp: openrtb2.App{Content: &openrtb2.Content{Ext: []byte(`malformed`)}}, givenFPD: []byte(`{"content":{"ext":{"b":100,"c":3}}}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "nested-content-producer-ext-err", givenApp: openrtb2.App{Content: &openrtb2.Content{Producer: &openrtb2.Producer{Ext: []byte(`malformed`)}}}, givenFPD: []byte(`{"content":{"producer": {"ext":{"b":100,"c":3}}}}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "nested-content-network-ext-err", givenApp: openrtb2.App{Content: &openrtb2.Content{Network: &openrtb2.Network{Ext: []byte(`malformed`)}}}, givenFPD: []byte(`{"content":{"network": {"ext":{"b":100,"c":3}}}}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "nested-content-channel-ext-err", givenApp: openrtb2.App{Content: &openrtb2.Content{Channel: &openrtb2.Channel{Ext: []byte(`malformed`)}}}, givenFPD: []byte(`{"content":{"channelx": {"ext":{"b":100,"c":3}}}}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "fpd-err", givenApp: openrtb2.App{ID: "1", Ext: []byte(`{"a":1}`)}, givenFPD: []byte(`malformed`), - expectedErr: "invalid character 'm' looking for beginning of value", + expectError: true, }, } @@ -1481,11 +1457,11 @@ func TestMergeApp(t *testing.T) { t.Run(test.name, func(t *testing.T) { err := mergeApp(&test.givenApp, test.givenFPD) - if test.expectedErr == "" { - assert.NoError(t, err, "unexpected error returned") - assert.Equal(t, test.expectedApp, test.givenApp, " result app is incorrect") + if test.expectError { + assert.Error(t, err) } else { - assert.EqualError(t, err, test.expectedErr, "expected error incorrect") + assert.NoError(t, err) + assert.Equal(t, test.expectedApp, test.givenApp, " result app is incorrect") } }) } @@ -1497,13 +1473,13 @@ func TestMergeSite(t *testing.T) { givenSite openrtb2.Site givenFPD json.RawMessage expectedSite openrtb2.Site - expectedErr string + expectError bool }{ { name: "empty", givenSite: openrtb2.Site{}, givenFPD: []byte(`{}`), - expectedErr: "incorrect First Party Data for bidder BidderA: Site object cannot set empty page if req.site.id is empty", + expectError: true, }, { name: "toplevel", @@ -1521,7 +1497,7 @@ func TestMergeSite(t *testing.T) { name: "toplevel-ext-err", givenSite: openrtb2.Site{ID: "1", Ext: []byte(`malformed`)}, givenFPD: []byte(`{"id":"2"}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "nested-publisher", @@ -1617,37 +1593,37 @@ func TestMergeSite(t *testing.T) { name: "nested-publisher-ext-err", givenSite: openrtb2.Site{ID: "1", Publisher: &openrtb2.Publisher{Ext: []byte(`malformed`)}}, givenFPD: []byte(`{"publisher":{"ext":{"b":100,"c":3}}}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "nested-content-ext-err", givenSite: openrtb2.Site{ID: "1", Content: &openrtb2.Content{Ext: []byte(`malformed`)}}, givenFPD: []byte(`{"content":{"ext":{"b":100,"c":3}}}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "nested-content-producer-ext-err", givenSite: openrtb2.Site{ID: "1", Content: &openrtb2.Content{Producer: &openrtb2.Producer{Ext: []byte(`malformed`)}}}, givenFPD: []byte(`{"content":{"producer": {"ext":{"b":100,"c":3}}}}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "nested-content-network-ext-err", givenSite: openrtb2.Site{ID: "1", Content: &openrtb2.Content{Network: &openrtb2.Network{Ext: []byte(`malformed`)}}}, givenFPD: []byte(`{"content":{"network": {"ext":{"b":100,"c":3}}}}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "nested-content-channel-ext-err", givenSite: openrtb2.Site{ID: "1", Content: &openrtb2.Content{Channel: &openrtb2.Channel{Ext: []byte(`malformed`)}}}, givenFPD: []byte(`{"content":{"channelx": {"ext":{"b":100,"c":3}}}}`), - expectedErr: "invalid request ext", + expectError: true, }, { name: "fpd-err", givenSite: openrtb2.Site{ID: "1", Ext: []byte(`{"a":1}`)}, givenFPD: []byte(`malformed`), - expectedErr: "invalid character 'm' looking for beginning of value", + expectError: true, }, } @@ -1655,11 +1631,11 @@ func TestMergeSite(t *testing.T) { t.Run(test.name, func(t *testing.T) { err := mergeSite(&test.givenSite, test.givenFPD, "BidderA") - if test.expectedErr == "" { - assert.NoError(t, err, "unexpected error returned") - assert.Equal(t, test.expectedSite, test.givenSite, " result Site is incorrect") + if test.expectError { + assert.Error(t, err) } else { - assert.EqualError(t, err, test.expectedErr, "expected error incorrect") + assert.NoError(t, err) + assert.Equal(t, test.expectedSite, test.givenSite, " result Site is incorrect") } }) } @@ -1912,18 +1888,38 @@ var ( } } `) +) - user = []byte(` -{ - "id": "2", - "yob": 2000, - "geo": { - "city": "LA", - "ext": { - "b": 100, - "c": 3 - } - } +func loadTestFile[T any](filename string) (T, error) { + var testFile T + + b, err := os.ReadFile(filename) + if err != nil { + return testFile, err + } + + err = json.Unmarshal(b, &testFile) + if err != nil { + return testFile, err + } + + return testFile, nil +} + +type fpdFile struct { + InputRequestData json.RawMessage `json:"inputRequestData,omitempty"` + OutputRequestData json.RawMessage `json:"outputRequestData,omitempty"` + BidderConfigFPD map[openrtb_ext.BidderName]*openrtb_ext.ORTB2 `json:"bidderConfigFPD,omitempty"` + BiddersFPDResolved map[openrtb_ext.BidderName]*ResolvedFirstPartyData `json:"biddersFPDResolved,omitempty"` + GlobalFPD map[string]json.RawMessage `json:"globalFPD,omitempty"` + ValidationErrors []*errortypes.BadInput `json:"validationErrors,omitempty"` +} + +type fpdFileForResolveFPD struct { + InputRequestData json.RawMessage `json:"inputRequestData,omitempty"` + OutputRequestData map[openrtb_ext.BidderName]openrtb2.BidRequest `json:"outputRequestData,omitempty"` + BiddersWithGlobalFPD []string `json:"biddersWithGlobalFPD,omitempty"` + BidderConfigFPD map[openrtb_ext.BidderName]*openrtb_ext.ORTB2 `json:"bidderConfigFPD,omitempty"` + GlobalFPD map[string]json.RawMessage `json:"globalFPD,omitempty"` + ValidationErrors []*errortypes.BadInput `json:"validationErrors,omitempty"` } -`) -) diff --git a/firstpartydata/tests/extractbidderconfigfpd/bidder-config-case-normalize.json b/firstpartydata/tests/extractbidderconfigfpd/bidder-config-case-normalize.json new file mode 100644 index 00000000000..934afe47de0 --- /dev/null +++ b/firstpartydata/tests/extractbidderconfigfpd/bidder-config-case-normalize.json @@ -0,0 +1,28 @@ +{ + "description": "Extracts bidder configs for a bidder, normalizing the case for a known bidder", + "inputRequestData": { + "data": {}, + "bidderconfig": [ + { + "bidders": [ + "APPNexus" + ], + "config": { + "ortb2": { + "site": { + "id": "apnSiteId" + } + } + } + } + ] + }, + "outputRequestData": {}, + "bidderConfigFPD": { + "appnexus": { + "site": { + "id": "apnSiteId" + } + } + } +} \ No newline at end of file diff --git a/firstpartydata/tests/extractbidderconfigfpd/bidder-config-duplicated-case-insensitive.json b/firstpartydata/tests/extractbidderconfigfpd/bidder-config-duplicated-case-insensitive.json new file mode 100644 index 00000000000..ed9904579d1 --- /dev/null +++ b/firstpartydata/tests/extractbidderconfigfpd/bidder-config-duplicated-case-insensitive.json @@ -0,0 +1,63 @@ +{ + "description": "Verifies error presence in case more than one bidder config specified for the same bidder, case insensitive", + "inputRequestData": { + "data": {}, + "bidderconfig": [ + { + "bidders": [ + "appnexus" + ], + "config": { + "ortb2": { + "site": { + "id": "apnSiteId", + "ext": { + "data": { + "sitefpddata": "sitefpddata", + "moreFpd": { + "fpd": 123 + }, + "morefpdData": "morefpddata" + } + } + } + } + } + }, + { + "bidders": [ + "APPNEXUS", + "telaria", + "testBidder2" + ], + "config": { + "ortb2": { + "user": { + "id": "telariaUserData", + "ext": { + "data": { + "userdata": "fpduserdata" + } + } + }, + "app": { + "id": "telariaAppData", + "ext": { + "data": { + "appdata": "fpdappdata" + } + } + } + } + } + } + ] + }, + "outputRequestData": {}, + "bidderConfigFPD": {}, + "validationErrors": [ + { + "Message": "multiple First Party Data bidder configs provided for bidder: APPNEXUS" + } + ] +} \ No newline at end of file diff --git a/firstpartydata/tests/extractbidderconfigfpd/bidder-config-request-alias-case-sensitive.json b/firstpartydata/tests/extractbidderconfigfpd/bidder-config-request-alias-case-sensitive.json new file mode 100644 index 00000000000..1becf730ada --- /dev/null +++ b/firstpartydata/tests/extractbidderconfigfpd/bidder-config-request-alias-case-sensitive.json @@ -0,0 +1,28 @@ +{ + "description": "Extracts bidder configs for a bidder, normalizing the case", + "inputRequestData": { + "data": {}, + "bidderconfig": [ + { + "bidders": [ + "requestAlias" + ], + "config": { + "ortb2": { + "site": { + "id": "aliasSiteId" + } + } + } + } + ] + }, + "outputRequestData": {}, + "bidderConfigFPD": { + "requestAlias": { + "site": { + "id": "aliasSiteId" + } + } + } +} \ No newline at end of file diff --git a/firstpartydata/tests/extractfpdforbidders/global-fpd-defined-app-content-data.json b/firstpartydata/tests/extractfpdforbidders/global-fpd-defined-app-content-data.json index 797846ee7a6..6e199f4eb0f 100644 --- a/firstpartydata/tests/extractfpdforbidders/global-fpd-defined-app-content-data.json +++ b/firstpartydata/tests/extractfpdforbidders/global-fpd-defined-app-content-data.json @@ -84,4 +84,4 @@ } }, "validationErrors": [] -} +} \ No newline at end of file diff --git a/firstpartydata/tests/extractfpdforbidders/two-bidders-correct-case-insensitive-integration.json b/firstpartydata/tests/extractfpdforbidders/two-bidders-correct-case-insensitive-integration.json new file mode 100644 index 00000000000..074c8ddef4e --- /dev/null +++ b/firstpartydata/tests/extractfpdforbidders/two-bidders-correct-case-insensitive-integration.json @@ -0,0 +1,88 @@ +{ + "description": "case insensitive known bidder, case sensitive request alias", + "inputRequestData": { + "id": "bid_id", + "site": { + "id": "reqSiteId", + "page": "http://www.foobar.com/1234.html", + "publisher": { + "id": "1" + } + }, + "user": { + "id": "reqUserId" + }, + "test": 1, + "ext": { + "prebid": { + "data": { + "bidders": [ + "APPNEXUS", + "requestAlias" + ] + }, + "bidderconfig": [ + { + "bidders": [ + "appnexus" + ], + "config": { + "ortb2": { + "site": { + "id": "apnSiteId" + }, + "user": { + "id": "apnUserId" + } + } + } + }, + { + "bidders": [ + "requestAlias" + ], + "config": { + "ortb2": { + "user": { + "keywords": "aliasUserKeywords" + } + } + } + } + ] + } + } + }, + "outputRequestData": { + "id": "bid_id", + "site": { + "id": "reqSiteId", + "page": "http://www.foobar.com/1234.html", + "publisher": { + "id": "1" + } + }, + "test": 1 + }, + "biddersFPDResolved": { + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "publisher": { + "id": "1" + } + }, + "user": { + "id": "apnUserId" + } + }, + "requestAlias": { + "user": { + "id": "reqUserId", + "keywords": "aliasUserKeywords" + } + } + }, + "validationErrors": [] +} diff --git a/firstpartydata/tests/resolvefpd/bidder-fpd-alias-matches.json b/firstpartydata/tests/resolvefpd/bidder-fpd-alias-matches.json new file mode 100644 index 00000000000..a9062d7ac4b --- /dev/null +++ b/firstpartydata/tests/resolvefpd/bidder-fpd-alias-matches.json @@ -0,0 +1,25 @@ +{ + "description": "Bidder FPD defined with a case sensitive request alias, positive test", + "inputRequestData": { + "app": { + "id": "reqUserID" + } + }, + "biddersWithGlobalFPD": [ + "requestAlias" + ], + "bidderConfigFPD": { + "requestAlias": { + "app": { + "id": "apnAppId" + } + } + }, + "outputRequestData": { + "requestAlias": { + "app": { + "id": "apnAppId" + } + } + } +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/bidder-fpd-alias-not-matches.json b/firstpartydata/tests/resolvefpd/bidder-fpd-alias-not-matches.json new file mode 100644 index 00000000000..ffc549c3cef --- /dev/null +++ b/firstpartydata/tests/resolvefpd/bidder-fpd-alias-not-matches.json @@ -0,0 +1,21 @@ +{ + "description": "Bidder FPD defined with a case sensitive request alias, negative test", + "inputRequestData": { + "app": { + "id": "reqUserID" + } + }, + "biddersWithGlobalFPD": [ + "requestAlias" + ], + "bidderConfigFPD": { + "REQUESTALIAS": { + "app": { + "id": "apnAppId" + } + } + }, + "outputRequestData": { + "requestAlias": {} + } +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/bidder-fpd-case-normalize.json b/firstpartydata/tests/resolvefpd/bidder-fpd-case-normalize.json new file mode 100644 index 00000000000..1f3e93b153c --- /dev/null +++ b/firstpartydata/tests/resolvefpd/bidder-fpd-case-normalize.json @@ -0,0 +1,25 @@ +{ + "description": "Bidder FPD defined with a case insensitive bidder", + "inputRequestData": { + "app": { + "id": "reqUserID" + } + }, + "biddersWithGlobalFPD": [ + "APPNEXUS" + ], + "bidderConfigFPD": { + "appnexus": { + "app": { + "id": "apnAppId" + } + } + }, + "outputRequestData": { + "appnexus": { + "app": { + "id": "apnAppId" + } + } + } +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/bidder-fpd-only-app.json b/firstpartydata/tests/resolvefpd/bidder-fpd-only-app.json index 812db7b10b5..7dc9adee182 100644 --- a/firstpartydata/tests/resolvefpd/bidder-fpd-only-app.json +++ b/firstpartydata/tests/resolvefpd/bidder-fpd-only-app.json @@ -1,30 +1,35 @@ { - "description": "Bidder FPD defined only for app", - "inputRequestData": { - "app": { - "id": "reqUserID" - } - }, - "bidderConfigFPD": { - "appnexus": { - "app": { - "id": "apnAppId", - "ext": { - "data": { - "other": "data" - } - } - } + "description": "Bidder FPD defined only for app", + "inputRequestData": { + "app": { + "id": "reqUserID" + } + }, + "biddersWithGlobalFPD": [ + "appnexus" + ], + "bidderConfigFPD": { + "appnexus": { + "app": { + "id": "apnAppId", + "ext": { + "data": { + "other": "data" + } } - }, - "outputRequestData": { - "app": { - "id": "apnAppId", - "ext": { - "data": { - "other": "data" - } - } + } + } + }, + "outputRequestData": { + "appnexus": { + "app": { + "id": "apnAppId", + "ext": { + "data": { + "other": "data" + } } + } } + } } \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/bidder-fpd-only-site.json b/firstpartydata/tests/resolvefpd/bidder-fpd-only-site.json index 5accfc3b3a0..8ee3b40175b 100644 --- a/firstpartydata/tests/resolvefpd/bidder-fpd-only-site.json +++ b/firstpartydata/tests/resolvefpd/bidder-fpd-only-site.json @@ -1,30 +1,35 @@ { - "description": "Bidder FPD defined only for site", - "inputRequestData": { - "site": { - "id": "reqUserID" - } - }, - "bidderConfigFPD": { - "appnexus": { - "site": { - "id": "apnSiteId", - "ext": { - "data": { - "other": "data" - } - } - } + "description": "Bidder FPD defined only for site", + "inputRequestData": { + "site": { + "id": "reqUserID" + } + }, + "biddersWithGlobalFPD": [ + "appnexus" + ], + "bidderConfigFPD": { + "appnexus": { + "site": { + "id": "apnSiteId", + "ext": { + "data": { + "other": "data" + } } - }, - "outputRequestData": { - "site": { - "id": "apnSiteId", - "ext": { - "data": { - "other": "data" - } - } + } + } + }, + "outputRequestData": { + "appnexus": { + "site": { + "id": "apnSiteId", + "ext": { + "data": { + "other": "data" + } } + } } + } } \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/bidder-fpd-only-user.json b/firstpartydata/tests/resolvefpd/bidder-fpd-only-user.json index 606cee7dbe6..204cf3c8d3f 100644 --- a/firstpartydata/tests/resolvefpd/bidder-fpd-only-user.json +++ b/firstpartydata/tests/resolvefpd/bidder-fpd-only-user.json @@ -1,35 +1,40 @@ { - "description": "Bidder FPD defined only for user", - "inputRequestData": { - "user": { - "id": "reqUserID", - "yob": 1982, - "gender": "M" - } - }, - "bidderConfigFPD": { - "appnexus": { - "user": { - "id": "apnUserId", - "yob": 1982, - "ext": { - "data": { - "other": "data" - } - } - } + "description": "Bidder FPD defined only for user", + "inputRequestData": { + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M" + } + }, + "biddersWithGlobalFPD": [ + "appnexus" + ], + "bidderConfigFPD": { + "appnexus": { + "user": { + "id": "apnUserId", + "yob": 1982, + "ext": { + "data": { + "other": "data" + } } - }, - "outputRequestData": { - "user": { - "id": "apnUserId", - "yob": 1982, - "gender": "M", - "ext": { - "data": { - "other": "data" - } - } + } + } + }, + "outputRequestData": { + "appnexus": { + "user": { + "id": "apnUserId", + "yob": 1982, + "gender": "M", + "ext": { + "data": { + "other": "data" + } } + } } + } } \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/bidder-fpd-site-content-data-only.json b/firstpartydata/tests/resolvefpd/bidder-fpd-site-content-data-only.json index 541d51f42af..2008d46f265 100644 --- a/firstpartydata/tests/resolvefpd/bidder-fpd-site-content-data-only.json +++ b/firstpartydata/tests/resolvefpd/bidder-fpd-site-content-data-only.json @@ -28,6 +28,9 @@ "ifa": "123" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "site": { @@ -60,44 +63,46 @@ } }, "outputRequestData": { - "site": { - "id": "apnSiteId", - "name": "apnSiteName", - "domain": "apnSiteDomain", - "page": "http://www.foobar.com/1234.html", - "cat": [ - "books", - "novels" - ], - "search": "book search", - "publisher": { - "id": "1" - }, - "content": { - "episode": 7, - "title": "apnEpisodeName", - "series": "TvName", - "season": "season3", - "len": 600, - "data": [ - { - "id": "siteData3", - "name": "siteName3" + "appnexus": { + "site": { + "id": "apnSiteId", + "name": "apnSiteName", + "domain": "apnSiteDomain", + "page": "http://www.foobar.com/1234.html", + "cat": [ + "books", + "novels" + ], + "search": "book search", + "publisher": { + "id": "1" + }, + "content": { + "episode": 7, + "title": "apnEpisodeName", + "series": "TvName", + "season": "season3", + "len": 600, + "data": [ + { + "id": "siteData3", + "name": "siteName3" + } + ] + }, + "ext": { + "data": { + "other": "data", + "testSiteFpd": "testSite" } - ] - }, - "ext": { - "data": { - "other": "data", - "testSiteFpd": "testSite" } + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" } - }, - "device": { - "ua": "testDevice", - "ip": "123.145.167.10", - "devicetype": 1, - "ifa": "123" } } } \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-app-content-data-user-data.json b/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-app-content-data-user-data.json index 16b5332e942..7be277eb5ff 100644 --- a/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-app-content-data-user-data.json +++ b/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-app-content-data-user-data.json @@ -14,6 +14,9 @@ "gender": "reqUserGender" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "app": { @@ -55,48 +58,50 @@ ] }, "outputRequestData": { - "app": { - "id": "apnAppId", - "page": "http://www.foobar.com/1234.html", - "publisher": { - "id": "1" + "appnexus": { + "app": { + "id": "apnAppId", + "page": "http://www.foobar.com/1234.html", + "publisher": { + "id": "1" + }, + "content": { + "data": [ + { + "id": "appData1", + "name": "appName1" + }, + { + "id": "appData2", + "name": "appName2" + } + ] + }, + "ext": { + "data": { + "morefpdData": "morefpddata", + "appFpd": 123 + } + } }, - "content": { + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "reqUserGender", "data": [ { - "id": "appData1", - "name": "appName1" + "id": "userData1", + "name": "userName1" }, { - "id": "appData2", - "name": "appName2" + "id": "userData2", + "name": "userName2" + } + ], + "ext": { + "data": { + "testUserFpd": "testuser" } - ] - }, - "ext": { - "data": { - "morefpdData": "morefpddata", - "appFpd": 123 - } - } - }, - "user": { - "id": "reqUserID", - "yob": 1982, - "gender": "reqUserGender", - "data": [ - { - "id": "userData1", - "name": "userName1" - }, - { - "id": "userData2", - "name": "userName2" - } - ], - "ext": { - "data": { - "testUserFpd": "testuser" } } } diff --git a/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-app.json b/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-app.json index 609ede597cf..fb27d1631c1 100644 --- a/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-app.json +++ b/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-app.json @@ -22,6 +22,9 @@ "ifa": "123" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "app": { @@ -44,36 +47,37 @@ } }, "outputRequestData": { - "app": { - "id": "apnAppId", - "page": "http://www.foobar.com/1234.html", - "publisher": { - "id": "1" - }, - "content": { - "episode": 6, - "title": "episodeName", - "series": "TvName", - "season": "season3", - "len": 900 - }, - "ext": { - "data": { - "moreFpd": { - "fpd": 123 - }, - "morefpdData": "morefpddata", - "appFpd": 123, - "appFpddata": "appFpddata" + "appnexus": { + "app": { + "id": "apnAppId", + "page": "http://www.foobar.com/1234.html", + "publisher": { + "id": "1" + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900 + }, + "ext": { + "data": { + "moreFpd": { + "fpd": 123 + }, + "morefpdData": "morefpddata", + "appFpd": 123, + "appFpddata": "appFpddata" + } } + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" } - }, - "device": { - "ua": "testDevice", - "ip": "123.145.167.10", - "devicetype": 1, - "ifa": "123" } } -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-site-user.json b/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-site-user.json index f66a8722308..9563fffebcc 100644 --- a/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-site-user.json +++ b/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-site-user.json @@ -19,6 +19,9 @@ "id": "apnUserId" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "site": { @@ -51,40 +54,41 @@ } }, "outputRequestData": { - "site": { - "id": "apnSiteId", - "page": "http://www.foobar.com/1234.html", - "publisher": { - "id": "1" - }, - "content": { - "episode": 6, - "title": "episodeName", - "series": "TvName", - "season": "season3", - "len": 900 - }, - "ext": { - "data": { - "morefpdData": "morefpddata", - "siteFpddata": "siteFpddata", - "moreFpd": { - "fpd": 123 + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "publisher": { + "id": "1" + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900 + }, + "ext": { + "data": { + "morefpdData": "morefpddata", + "siteFpddata": "siteFpddata", + "moreFpd": { + "fpd": 123 + } } } - } - }, - "user": { - "id": "apnUserId", - "ext": { - "data": { - "moreFpd": { - "fpd": 567 - }, - "testUserFpd": "testuser" + }, + "user": { + "id": "apnUserId", + "ext": { + "data": { + "moreFpd": { + "fpd": 567 + }, + "testUserFpd": "testuser" + } } } } } -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-site.json b/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-site.json index be03f0cb9b0..4362362718a 100644 --- a/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-site.json +++ b/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-site.json @@ -16,6 +16,9 @@ } } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "site": { @@ -40,31 +43,32 @@ } }, "outputRequestData": { - "site": { - "id": "apnSiteId", - "page": "http://www.foobar.com/1234.html", - "ref": "fpdRef", - "publisher": { - "id": "1" - }, - "content": { - "episode": 6, - "title": "episodeName", - "series": "TvName", - "season": "season3", - "len": 900 - }, - "ext": { - "data": { - "moreFpd": { - "fpd": 123 - }, - "morefpdData": "morefpddata", - "siteFpd": 123, - "siteFpddata": "siteFpddata" + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "ref": "fpdRef", + "publisher": { + "id": "1" + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900 + }, + "ext": { + "data": { + "moreFpd": { + "fpd": 123 + }, + "morefpdData": "morefpddata", + "siteFpd": 123, + "siteFpddata": "siteFpddata" + } } } } } -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-user.json b/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-user.json index ef381d613e6..4d233e6c473 100644 --- a/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-user.json +++ b/firstpartydata/tests/resolvefpd/global-and-bidder-fpd-user.json @@ -7,6 +7,9 @@ "gender": "M" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "user": { @@ -30,21 +33,22 @@ } }, "outputRequestData": { - "user": { - "id": "apnUserId", - "yob": 1982, - "gender": "M", - "ext": { - "data": { - "testUserFpd": "testuser", - "morefpdData": "morefpddata", - "userFpddata": "siteFpddata", - "moreFpd": { - "fpd": 123 + "appnexus": { + "user": { + "id": "apnUserId", + "yob": 1982, + "gender": "M", + "ext": { + "data": { + "testUserFpd": "testuser", + "morefpdData": "morefpddata", + "userFpddata": "siteFpddata", + "moreFpd": { + "fpd": 123 + } } } } } } -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/global-and-bidder-site-content-data.json b/firstpartydata/tests/resolvefpd/global-and-bidder-site-content-data.json index c0f4723d62a..a0a1c461292 100644 --- a/firstpartydata/tests/resolvefpd/global-and-bidder-site-content-data.json +++ b/firstpartydata/tests/resolvefpd/global-and-bidder-site-content-data.json @@ -20,6 +20,9 @@ "gender": "M" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "site": { @@ -62,52 +65,53 @@ ] }, "outputRequestData": { - "site": { - "id": "apnSiteId", - "page": "http://www.foobar.com/1234.html", - "ref": "fpdRef", - "publisher": { - "id": "1" - }, - "content": { - "data": [ - { - "id": "siteData1", - "name": "siteName1" - }, - { - "id": "siteData2", - "name": "siteName2" + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "ref": "fpdRef", + "publisher": { + "id": "1" + }, + "content": { + "data": [ + { + "id": "siteData1", + "name": "siteName1" + }, + { + "id": "siteData2", + "name": "siteName2" + } + ] + }, + "ext": { + "data": { + "moreFpd": { + "fpd": 123 + }, + "morefpdData": "morefpddata", + "siteFpd": 123, + "siteFpddata": "siteFpddata" } - ] - }, - "ext": { - "data": { - "moreFpd": { - "fpd": 123 - }, - "morefpdData": "morefpddata", - "siteFpd": 123, - "siteFpddata": "siteFpddata" } - } - }, - "device": { - "ua": "testDevice", - "ip": "123.145.167.10", - "devicetype": 1, - "ifa": "123" - }, - "user": { - "id": "reqUserID", - "yob": 1982, - "gender": "M", - "ext": { - "data": { - "testUserFpd": "testuser" + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" + }, + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M", + "ext": { + "data": { + "testUserFpd": "testuser" + } } } } } -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/global-and-no-bidder-fpd-site-app-user.json b/firstpartydata/tests/resolvefpd/global-and-no-bidder-fpd-site-app-user.json index a705e8b2405..e8dace465e7 100644 --- a/firstpartydata/tests/resolvefpd/global-and-no-bidder-fpd-site-app-user.json +++ b/firstpartydata/tests/resolvefpd/global-and-no-bidder-fpd-site-app-user.json @@ -23,6 +23,9 @@ "gender": "M" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "site": { @@ -72,58 +75,59 @@ ] }, "outputRequestData": { - "site": { - "id": "apnSiteId", - "page": "http://www.foobar.com/1234.html", - "ref": "fpdRef", - "publisher": { - "id": "1" - }, - "content": { - "title": "episodeName8", - "series": "TvName", - "season": "season4", - "episode": 8, - "len": 900, - "data": [ - { - "id": "siteData1", - "name": "siteName1" - }, - { - "id": "siteData2", - "name": "siteName2" + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "ref": "fpdRef", + "publisher": { + "id": "1" + }, + "content": { + "title": "episodeName8", + "series": "TvName", + "season": "season4", + "episode": 8, + "len": 900, + "data": [ + { + "id": "siteData1", + "name": "siteName1" + }, + { + "id": "siteData2", + "name": "siteName2" + } + ] + }, + "ext": { + "testSiteExt": 123, + "data": { + "moreFpd": { + "fpd": 123 + }, + "morefpdData": "morefpddata", + "siteFpd": 123, + "siteFpddata": "siteFpddata" } - ] - }, - "ext": { - "testSiteExt": 123, - "data": { - "moreFpd": { - "fpd": 123 - }, - "morefpdData": "morefpddata", - "siteFpd": 123, - "siteFpddata": "siteFpddata" } - } - }, - "device": { - "ua": "testDevice", - "ip": "123.145.167.10", - "devicetype": 1, - "ifa": "123" - }, - "user": { - "id": "reqUserID", - "yob": 1982, - "gender": "M", - "ext": { - "data": { - "testUserFpd": "testuser" + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" + }, + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M", + "ext": { + "data": { + "testUserFpd": "testuser" + } } } } } -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/global-and-no-bidder-fpd-site-content-app-user.json b/firstpartydata/tests/resolvefpd/global-and-no-bidder-fpd-site-content-app-user.json index ccfa030cd8d..10fa471744f 100644 --- a/firstpartydata/tests/resolvefpd/global-and-no-bidder-fpd-site-content-app-user.json +++ b/firstpartydata/tests/resolvefpd/global-and-no-bidder-fpd-site-content-app-user.json @@ -23,6 +23,9 @@ "gender": "M" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "site": { @@ -65,53 +68,54 @@ ] }, "outputRequestData": { - "site": { - "id": "apnSiteId", - "page": "http://www.foobar.com/1234.html", - "ref": "fpdRef", - "publisher": { - "id": "1" - }, - "content": { - "data": [ - { - "id": "siteData1", - "name": "siteName1" - }, - { - "id": "siteData2", - "name": "siteName2" + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "ref": "fpdRef", + "publisher": { + "id": "1" + }, + "content": { + "data": [ + { + "id": "siteData1", + "name": "siteName1" + }, + { + "id": "siteData2", + "name": "siteName2" + } + ] + }, + "ext": { + "testSiteExt": 123, + "data": { + "moreFpd": { + "fpd": 123 + }, + "morefpdData": "morefpddata", + "siteFpd": 123, + "siteFpddata": "siteFpddata" } - ] - }, - "ext": { - "testSiteExt": 123, - "data": { - "moreFpd": { - "fpd": 123 - }, - "morefpdData": "morefpddata", - "siteFpd": 123, - "siteFpddata": "siteFpddata" } - } - }, - "device": { - "ua": "testDevice", - "ip": "123.145.167.10", - "devicetype": 1, - "ifa": "123" - }, - "user": { - "id": "reqUserID", - "yob": 1982, - "gender": "M", - "ext": { - "data": { - "testUserFpd": "testuser" + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" + }, + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M", + "ext": { + "data": { + "testUserFpd": "testuser" + } } } } } -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/global-fpd-only-app.json b/firstpartydata/tests/resolvefpd/global-fpd-only-app.json index e4d5e169986..55839983a5b 100644 --- a/firstpartydata/tests/resolvefpd/global-fpd-only-app.json +++ b/firstpartydata/tests/resolvefpd/global-fpd-only-app.json @@ -29,6 +29,9 @@ "gender": "M" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "app": { @@ -64,56 +67,57 @@ ] }, "outputRequestData": { - "app": { - "id": "apnAppId", - "publisher": { - "id": "1" - }, - "content": { - "episode": 6, - "title": "episodeName", - "series": "TvName", - "season": "season3", - "len": 900, - "data": [ - { - "id": "appData1", - "name": "appName1" - }, - { - "id": "appData2", - "name": "appName2" + "appnexus": { + "app": { + "id": "apnAppId", + "publisher": { + "id": "1" + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "data": [ + { + "id": "appData1", + "name": "appName1" + }, + { + "id": "appData2", + "name": "appName2" + } + ] + }, + "ext": { + "testAppExt": 123, + "data": { + "moreFpd": { + "fpd": 123 + }, + "morefpdData": "morefpddata", + "appFpd": 123, + "appFpddata": "appFpddata" } - ] - }, - "ext": { - "testAppExt": 123, - "data": { - "moreFpd": { - "fpd": 123 - }, - "morefpdData": "morefpddata", - "appFpd": 123, - "appFpddata": "appFpddata" } - } - }, - "device": { - "ua": "testDevice", - "ip": "123.145.167.10", - "devicetype": 1, - "ifa": "123" - }, - "user": { - "id": "reqUserID", - "yob": 1982, - "gender": "M", - "ext": { - "data": { - "testUserFpd": "testuser" + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" + }, + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M", + "ext": { + "data": { + "testUserFpd": "testuser" + } } } } } -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/global-fpd-only-excludes-some.json b/firstpartydata/tests/resolvefpd/global-fpd-only-excludes-some.json new file mode 100644 index 00000000000..b23a347cf9b --- /dev/null +++ b/firstpartydata/tests/resolvefpd/global-fpd-only-excludes-some.json @@ -0,0 +1,138 @@ +{ + "description": "Global and bidder FPD defined for site and user. Global FPD has site.content.data. Excludes rubicon since not listed in biddersWithGlobalFPD", + "inputRequestData": { + "site": { + "id": "reqSiteId", + "page": "http://www.foobar.com/1234.html", + "publisher": { + "id": "1" + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900 + }, + "ext": { + "testSiteExt": 123 + } + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" + }, + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M" + } + }, + "biddersWithGlobalFPD": [ + "appnexus" + ], + "bidderConfigFPD": { + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "ref": "fpdRef", + "ext": { + "data": { + "morefpdData": "morefpddata", + "siteFpddata": "siteFpddata", + "moreFpd": { + "fpd": 123 + } + } + } + } + }, + "rubicon": { + "site": { + "id": "rbcSiteId" + } + } + }, + "globalFPD": { + "site": { + "siteFpd": 123 + }, + "app": { + "appFpd": { + "testValue": true + } + }, + "user": { + "testUserFpd": "testuser" + }, + "siteContentData": [ + { + "id": "siteData1", + "name": "siteName1" + }, + { + "id": "siteData2", + "name": "siteName2" + } + ] + }, + "outputRequestData": { + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "ref": "fpdRef", + "publisher": { + "id": "1" + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "data": [ + { + "id": "siteData1", + "name": "siteName1" + }, + { + "id": "siteData2", + "name": "siteName2" + } + ] + }, + "ext": { + "testSiteExt": 123, + "data": { + "moreFpd": { + "fpd": 123 + }, + "morefpdData": "morefpddata", + "siteFpd": 123, + "siteFpddata": "siteFpddata" + } + } + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" + }, + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M", + "ext": { + "data": { + "testUserFpd": "testuser" + } + } + } + } + } +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/global-fpd-only-include-all-explicit.json b/firstpartydata/tests/resolvefpd/global-fpd-only-include-all-explicit.json new file mode 100644 index 00000000000..f959ebcde4f --- /dev/null +++ b/firstpartydata/tests/resolvefpd/global-fpd-only-include-all-explicit.json @@ -0,0 +1,144 @@ +{ + "description": "Global and bidder FPD defined for site and user. Global FPD has site.content.data. Bidders mentioned explicitly in biddersWithGlobalFPD", + "inputRequestData": { + "site": { + "id": "reqSiteId", + "page": "http://www.foobar.com/1234.html", + "publisher": { + "id": "1" + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900 + }, + "ext": { + "testSiteExt": 123 + } + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" + }, + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M" + } + }, + "biddersWithGlobalFPD": [ + "appnexus", + "rubicon" + ], + "bidderConfigFPD": { + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "ref": "fpdRef", + "ext": { + "data": { + "morefpdData": "morefpddata", + "siteFpddata": "siteFpddata", + "moreFpd": { + "fpd": 123 + } + } + } + } + }, + "rubicon": { + "site": { + "id": "rbcSiteId" + } + } + }, + "globalFPD": { + "site": { + "siteFpd": 123 + }, + "app": { + "appFpd": { + "testValue": true + } + }, + "user": { + "testUserFpd": "testuser" + }, + "siteContentData": [ + { + "id": "siteData1", + "name": "siteName1" + }, + { + "id": "siteData2", + "name": "siteName2" + } + ] + }, + "outputRequestData": { + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "ref": "fpdRef", + "publisher": { + "id": "1" + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "data": [ + { + "id": "siteData1", + "name": "siteName1" + }, + { + "id": "siteData2", + "name": "siteName2" + } + ] + }, + "ext": { + "testSiteExt": 123, + "data": { + "moreFpd": { + "fpd": 123 + }, + "morefpdData": "morefpddata", + "siteFpd": 123, + "siteFpddata": "siteFpddata" + } + } + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" + }, + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M", + "ext": { + "data": { + "testUserFpd": "testuser" + } + } + } + }, + "rubicon": { + "site": { + "id": "rbcSiteId" + } + } + } +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/global-fpd-only-include-all-implicit.json b/firstpartydata/tests/resolvefpd/global-fpd-only-include-all-implicit.json new file mode 100644 index 00000000000..7c1f0f10aff --- /dev/null +++ b/firstpartydata/tests/resolvefpd/global-fpd-only-include-all-implicit.json @@ -0,0 +1,141 @@ +{ + "description": "Global and bidder FPD defined for site and user. Global FPD has site.content.data. Bidders included implicitly with biddersWithGlobalFPD nil", + "inputRequestData": { + "site": { + "id": "reqSiteId", + "page": "http://www.foobar.com/1234.html", + "publisher": { + "id": "1" + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900 + }, + "ext": { + "testSiteExt": 123 + } + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" + }, + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M" + } + }, + "biddersWithGlobalFPD": null, + "bidderConfigFPD": { + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "ref": "fpdRef", + "ext": { + "data": { + "morefpdData": "morefpddata", + "siteFpddata": "siteFpddata", + "moreFpd": { + "fpd": 123 + } + } + } + } + }, + "rubicon": { + "site": { + "id": "rbcSiteId" + } + } + }, + "globalFPD": { + "site": { + "siteFpd": 123 + }, + "app": { + "appFpd": { + "testValue": true + } + }, + "user": { + "testUserFpd": "testuser" + }, + "siteContentData": [ + { + "id": "siteData1", + "name": "siteName1" + }, + { + "id": "siteData2", + "name": "siteName2" + } + ] + }, + "outputRequestData": { + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "ref": "fpdRef", + "publisher": { + "id": "1" + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "data": [ + { + "id": "siteData1", + "name": "siteName1" + }, + { + "id": "siteData2", + "name": "siteName2" + } + ] + }, + "ext": { + "testSiteExt": 123, + "data": { + "moreFpd": { + "fpd": 123 + }, + "morefpdData": "morefpddata", + "siteFpd": 123, + "siteFpddata": "siteFpddata" + } + } + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" + }, + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M", + "ext": { + "data": { + "testUserFpd": "testuser" + } + } + } + }, + "rubicon": { + "site": { + "id": "rbcSiteId" + } + } + } +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/global-fpd-only-site.json b/firstpartydata/tests/resolvefpd/global-fpd-only-site.json index 49b8f4dcefb..3f7e91adc63 100644 --- a/firstpartydata/tests/resolvefpd/global-fpd-only-site.json +++ b/firstpartydata/tests/resolvefpd/global-fpd-only-site.json @@ -30,6 +30,9 @@ "gender": "M" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "site": { @@ -72,58 +75,59 @@ ] }, "outputRequestData": { - "site": { - "id": "apnSiteId", - "page": "http://www.foobar.com/1234.html", - "ref": "fpdRef", - "publisher": { - "id": "1" - }, - "content": { - "episode": 6, - "title": "episodeName", - "series": "TvName", - "season": "season3", - "len": 900, - "data": [ - { - "id": "siteData1", - "name": "siteName1" - }, - { - "id": "siteData2", - "name": "siteName2" + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "ref": "fpdRef", + "publisher": { + "id": "1" + }, + "content": { + "episode": 6, + "title": "episodeName", + "series": "TvName", + "season": "season3", + "len": 900, + "data": [ + { + "id": "siteData1", + "name": "siteName1" + }, + { + "id": "siteData2", + "name": "siteName2" + } + ] + }, + "ext": { + "testSiteExt": 123, + "data": { + "moreFpd": { + "fpd": 123 + }, + "morefpdData": "morefpddata", + "siteFpd": 123, + "siteFpddata": "siteFpddata" } - ] - }, - "ext": { - "testSiteExt": 123, - "data": { - "moreFpd": { - "fpd": 123 - }, - "morefpdData": "morefpddata", - "siteFpd": 123, - "siteFpddata": "siteFpddata" } - } - }, - "device": { - "ua": "testDevice", - "ip": "123.145.167.10", - "devicetype": 1, - "ifa": "123" - }, - "user": { - "id": "reqUserID", - "yob": 1982, - "gender": "M", - "ext": { - "data": { - "testUserFpd": "testuser" + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" + }, + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M", + "ext": { + "data": { + "testUserFpd": "testuser" + } } } } } -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/req-and-bidder-fpd-site-content.json b/firstpartydata/tests/resolvefpd/req-and-bidder-fpd-site-content.json index 446e7151afa..da0d4357dd1 100644 --- a/firstpartydata/tests/resolvefpd/req-and-bidder-fpd-site-content.json +++ b/firstpartydata/tests/resolvefpd/req-and-bidder-fpd-site-content.json @@ -40,6 +40,9 @@ "gender": "M" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "site": { @@ -89,58 +92,59 @@ } }, "outputRequestData": { - "site": { - "id": "apnSiteId", - "page": "http://www.foobar.com/1234.html", - "ref": "fpdRef", - "publisher": { - "id": "1" - }, - "content": { - "episode": 8, - "title": "episodeName8", - "series": "TvName", - "season": "season4", - "len": 900, - "data": [ - { - "id": "siteData1", - "name": "siteName1" - }, - { - "id": "siteData2", - "name": "siteName2" + "appnexus": { + "site": { + "id": "apnSiteId", + "page": "http://www.foobar.com/1234.html", + "ref": "fpdRef", + "publisher": { + "id": "1" + }, + "content": { + "episode": 8, + "title": "episodeName8", + "series": "TvName", + "season": "season4", + "len": 900, + "data": [ + { + "id": "siteData1", + "name": "siteName1" + }, + { + "id": "siteData2", + "name": "siteName2" + } + ] + }, + "ext": { + "testSiteExt": 123, + "data": { + "moreFpd": { + "fpd": 123 + }, + "morefpdData": "morefpddata", + "siteFpd": 123, + "siteFpddata": "siteFpddata" } - ] - }, - "ext": { - "testSiteExt": 123, - "data": { - "moreFpd": { - "fpd": 123 - }, - "morefpdData": "morefpddata", - "siteFpd": 123, - "siteFpddata": "siteFpddata" } - } - }, - "device": { - "ua": "testDevice", - "ip": "123.145.167.10", - "devicetype": 1, - "ifa": "123" - }, - "user": { - "id": "reqUserID", - "yob": 1982, - "gender": "M", - "ext": { - "data": { - "testUserFpd": "testuser" + }, + "device": { + "ua": "testDevice", + "ip": "123.145.167.10", + "devicetype": 1, + "ifa": "123" + }, + "user": { + "id": "reqUserID", + "yob": 1982, + "gender": "M", + "ext": { + "data": { + "testUserFpd": "testuser" + } } } } } -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/req-app-not-defined.json b/firstpartydata/tests/resolvefpd/req-app-not-defined.json index f24a4d27db1..6ab9e076f43 100644 --- a/firstpartydata/tests/resolvefpd/req-app-not-defined.json +++ b/firstpartydata/tests/resolvefpd/req-app-not-defined.json @@ -20,6 +20,9 @@ "gender": "M" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "app": { @@ -42,5 +45,4 @@ "Message": "incorrect First Party Data for bidder appnexus: App object is not defined in request, but defined in FPD config" } ] -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/req-site-not-defined.json b/firstpartydata/tests/resolvefpd/req-site-not-defined.json index d078ad7804f..8e20a6e0e1d 100644 --- a/firstpartydata/tests/resolvefpd/req-site-not-defined.json +++ b/firstpartydata/tests/resolvefpd/req-site-not-defined.json @@ -7,6 +7,9 @@ "gender": "M" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "site": { @@ -29,5 +32,4 @@ "Message": "incorrect First Party Data for bidder appnexus: Site object is not defined in request, but defined in FPD config" } ] -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/req-user-not-defined.json b/firstpartydata/tests/resolvefpd/req-user-not-defined.json index 76ae3f827ca..85c41c23969 100644 --- a/firstpartydata/tests/resolvefpd/req-user-not-defined.json +++ b/firstpartydata/tests/resolvefpd/req-user-not-defined.json @@ -5,6 +5,9 @@ "at": 1, "tmax": 5000 }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "user": { @@ -21,11 +24,12 @@ } } }, - "outputRequestData": {}, + "outputRequestData": { + "appnexus": {} + }, "validationErrors": [ { "Message": "incorrect First Party Data for bidder appnexus: User object is not defined in request, but defined in FPD config" } ] -} - +} \ No newline at end of file diff --git a/firstpartydata/tests/resolvefpd/site-page-empty-conflict.json b/firstpartydata/tests/resolvefpd/site-page-empty-conflict.json index 8da3dc69329..260385c49be 100644 --- a/firstpartydata/tests/resolvefpd/site-page-empty-conflict.json +++ b/firstpartydata/tests/resolvefpd/site-page-empty-conflict.json @@ -19,6 +19,9 @@ "gender": "M" } }, + "biddersWithGlobalFPD": [ + "appnexus" + ], "bidderConfigFPD": { "appnexus": { "site": { @@ -41,5 +44,4 @@ "Message": "incorrect First Party Data for bidder appnexus: Site object cannot set empty page if req.site.id is empty" } ] -} - +} \ No newline at end of file diff --git a/floors/enforce.go b/floors/enforce.go index 57107895d61..84eb02f7b02 100644 --- a/floors/enforce.go +++ b/floors/enforce.go @@ -5,11 +5,11 @@ import ( "fmt" "math/rand" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // Enforce does floors enforcement for bids from all bidders based on floors provided in request, account level floors config diff --git a/floors/enforce_ow.go b/floors/enforce_ow.go index d2acdcf1190..9568ca44b1e 100644 --- a/floors/enforce_ow.go +++ b/floors/enforce_ow.go @@ -1,6 +1,6 @@ package floors -import "github.com/prebid/prebid-server/currency" +import "github.com/prebid/prebid-server/v2/currency" func getOriginalBidCpmUsd(price float64, from string, conversions currency.Conversions) float64 { rate, _ := getCurrencyConversionRate(from, "USD", conversions) diff --git a/floors/enforce_test.go b/floors/enforce_test.go index 64455303b30..8ae48016116 100644 --- a/floors/enforce_test.go +++ b/floors/enforce_test.go @@ -5,12 +5,12 @@ import ( "errors" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -768,7 +768,7 @@ func TestUpdateBidExtWithFloors(t *testing.T) { { name: "Valid prebid extension in imp.ext", args: args{ - reqImp: &openrtb_ext.ImpWrapper{Imp: &openrtb2.Imp{ID: "1234", Video: &openrtb2.Video{W: 300, H: 250}, Ext: []byte(`{"prebid":{"floors":{"floorrule":"test|123|xyz","floorrulevalue":5.5,"floorvalue":5.5}}}`)}}, + reqImp: &openrtb_ext.ImpWrapper{Imp: &openrtb2.Imp{ID: "1234", Video: &openrtb2.Video{W: ptrutil.ToPtr[int64](300), H: ptrutil.ToPtr[int64](250)}, Ext: []byte(`{"prebid":{"floors":{"floorrule":"test|123|xyz","floorrulevalue":5.5,"floorvalue":5.5}}}`)}}, bid: &entities.PbsOrtbBid{ Bid: &openrtb2.Bid{ Price: 10.10, diff --git a/floors/fetcher.go b/floors/fetcher.go index 1a0b7526193..5ed96b9ec36 100644 --- a/floors/fetcher.go +++ b/floors/fetcher.go @@ -5,7 +5,6 @@ import ( "context" "encoding/json" "errors" - "fmt" "io" "math" "net/http" @@ -14,15 +13,21 @@ import ( "github.com/alitto/pond" validator "github.com/asaskevich/govalidator" + "github.com/coocood/freecache" "github.com/golang/glog" - "github.com/patrickmn/go-cache" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/timeutil" ) -type FloorFetcher interface { - Fetch(configs config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) +var refetchCheckInterval = 300 + +type fetchInfo struct { + config.AccountFloorFetch + fetchTime int64 + refetchRequest bool + retryCount int } type WorkerPool interface { @@ -30,33 +35,32 @@ type WorkerPool interface { Stop() } -var refetchCheckInterval = 300 - -type PriceFloorFetcher struct { - pool WorkerPool // Goroutines worker pool - fetchQueue FetchQueue // Priority Queue to fetch floor data - fetchInprogress map[string]bool // Map of URL with fetch status - configReceiver chan FetchInfo // Channel which recieves URLs to be fetched - done chan struct{} // Channel to close fetcher - cache *cache.Cache // cache - cacheExpiry time.Duration // cache expiry time - metricEngine metrics.MetricsEngine +type FloorFetcher interface { + Fetch(configs config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) + Stop() } -type FetchInfo struct { - config.AccountFloorFetch - FetchTime int64 - RefetchRequest bool +type PriceFloorFetcher struct { + pool WorkerPool // Goroutines worker pool + fetchQueue FetchQueue // Priority Queue to fetch floor data + fetchInProgress map[string]bool // Map of URL with fetch status + configReceiver chan fetchInfo // Channel which recieves URLs to be fetched + done chan struct{} // Channel to close fetcher + cache *freecache.Cache // cache + httpClient *http.Client // http client to fetch data from url + time timeutil.Time // time interface to record request timings + metricEngine metrics.MetricsEngine // Records malfunctions in dynamic fetch + maxRetries int // Max number of retries for failing URLs } -type FetchQueue []*FetchInfo +type FetchQueue []*fetchInfo func (fq FetchQueue) Len() int { return len(fq) } func (fq FetchQueue) Less(i, j int) bool { - return fq[i].FetchTime < fq[j].FetchTime + return fq[i].fetchTime < fq[j].fetchTime } func (fq FetchQueue) Swap(i, j int) { @@ -64,7 +68,7 @@ func (fq FetchQueue) Swap(i, j int) { } func (fq *FetchQueue) Push(element interface{}) { - fetchInfo := element.(*FetchInfo) + fetchInfo := element.(*fetchInfo) *fq = append(*fq, fetchInfo) } @@ -72,12 +76,12 @@ func (fq *FetchQueue) Pop() interface{} { old := *fq n := len(old) fetchInfo := old[n-1] - old[n-1] = nil // avoid memory leak + old[n-1] = nil *fq = old[0 : n-1] return fetchInfo } -func (fq *FetchQueue) Top() *FetchInfo { +func (fq *FetchQueue) Top() *fetchInfo { old := *fq if len(old) == 0 { return nil @@ -89,17 +93,22 @@ func workerPanicHandler(p interface{}) { glog.Errorf("floor fetcher worker panicked: %v", p) } -func NewPriceFloorFetcher(maxWorkers, maxCapacity, cacheCleanUpInt, cacheExpiry int, metricEngine metrics.MetricsEngine) *PriceFloorFetcher { +func NewPriceFloorFetcher(config config.PriceFloors, httpClient *http.Client, metricEngine metrics.MetricsEngine) *PriceFloorFetcher { + if !config.Enabled { + return nil + } floorFetcher := PriceFloorFetcher{ - pool: pond.New(maxWorkers, maxCapacity, pond.PanicHandler(workerPanicHandler)), + pool: pond.New(config.Fetcher.Worker, config.Fetcher.Capacity, pond.PanicHandler(workerPanicHandler)), fetchQueue: make(FetchQueue, 0, 100), - fetchInprogress: make(map[string]bool), - configReceiver: make(chan FetchInfo, maxCapacity), + fetchInProgress: make(map[string]bool), + configReceiver: make(chan fetchInfo, config.Fetcher.Capacity), done: make(chan struct{}), - cacheExpiry: time.Duration(cacheExpiry) * time.Second, - cache: cache.New(time.Duration(cacheExpiry)*time.Second, time.Duration(cacheCleanUpInt)*time.Second), + cache: freecache.NewCache(config.Fetcher.CacheSize * 1024 * 1024), + httpClient: httpClient, + time: &timeutil.RealTime{}, metricEngine: metricEngine, + maxRetries: config.Fetcher.MaxRetries, } go floorFetcher.Fetcher() @@ -107,126 +116,141 @@ func NewPriceFloorFetcher(maxWorkers, maxCapacity, cacheCleanUpInt, cacheExpiry return &floorFetcher } -func (f *PriceFloorFetcher) SetWithExpiry(key string, value interface{}, cacheExpiry time.Duration) { - f.cache.Set(key, value, cacheExpiry) +func (f *PriceFloorFetcher) SetWithExpiry(key string, value json.RawMessage, cacheExpiry int) { + f.cache.Set([]byte(key), value, cacheExpiry) } -func (f *PriceFloorFetcher) Get(key string) (interface{}, bool) { - return f.cache.Get(key) -} +func (f *PriceFloorFetcher) Get(key string) (json.RawMessage, bool) { + data, err := f.cache.Get([]byte(key)) + if err != nil { + return nil, false + } -func (f *PriceFloorFetcher) Fetch(configs config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) { + return data, true +} - if !configs.UseDynamicData || len(configs.Fetch.URL) == 0 || !validator.IsURL(configs.Fetch.URL) { +func (f *PriceFloorFetcher) Fetch(config config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) { + if f == nil || !config.UseDynamicData || len(config.Fetcher.URL) == 0 || !validator.IsURL(config.Fetcher.URL) { return nil, openrtb_ext.FetchNone } // Check for floors JSON in cache - result, found := f.Get(configs.Fetch.URL) - if found { - fetcheRes, ok := result.(*openrtb_ext.PriceFloorRules) - if !ok || fetcheRes.Data == nil { + if result, found := f.Get(config.Fetcher.URL); found { + var fetchedFloorData openrtb_ext.PriceFloorRules + if err := json.Unmarshal(result, &fetchedFloorData); err != nil || fetchedFloorData.Data == nil { return nil, openrtb_ext.FetchError } - return fetcheRes, openrtb_ext.FetchSuccess + return &fetchedFloorData, openrtb_ext.FetchSuccess } //miss: push to channel to fetch and return empty response - if configs.Enabled && configs.Fetch.Enabled && configs.Fetch.Timeout > 0 { - fetchInfo := FetchInfo{AccountFloorFetch: configs.Fetch, FetchTime: time.Now().Unix(), RefetchRequest: false} - f.configReceiver <- fetchInfo + if config.Enabled && config.Fetcher.Enabled && config.Fetcher.Timeout > 0 { + fetchConfig := fetchInfo{AccountFloorFetch: config.Fetcher, fetchTime: f.time.Now().Unix(), refetchRequest: false, retryCount: 0} + f.configReceiver <- fetchConfig } return nil, openrtb_ext.FetchInprogress } -func (f *PriceFloorFetcher) worker(configs config.AccountFloorFetch) { - - floorData, fetchedMaxAge := fetchAndValidate(configs, f.metricEngine) +func (f *PriceFloorFetcher) worker(fetchConfig fetchInfo) { + floorData, fetchedMaxAge := f.fetchAndValidate(fetchConfig.AccountFloorFetch) if floorData != nil { + // Reset retry count when data is successfully fetched + fetchConfig.retryCount = 0 + // Update cache with new floor rules - glog.Infof("Updating Value in cache for URL %s", configs.URL) - cacheExpiry := f.cacheExpiry - if fetchedMaxAge != 0 && fetchedMaxAge > configs.Period && fetchedMaxAge < math.MaxInt32 { - cacheExpiry = time.Duration(fetchedMaxAge) * time.Second + cacheExpiry := fetchConfig.AccountFloorFetch.MaxAge + if fetchedMaxAge != 0 { + cacheExpiry = fetchedMaxAge } - f.SetWithExpiry(configs.URL, floorData, cacheExpiry) + floorData, err := json.Marshal(floorData) + if err != nil { + glog.Errorf("Error while marshaling fetched floor data for url %s", fetchConfig.AccountFloorFetch.URL) + } else { + f.SetWithExpiry(fetchConfig.AccountFloorFetch.URL, floorData, cacheExpiry) + } + } else { + fetchConfig.retryCount++ } // Send to refetch channel - f.configReceiver <- FetchInfo{AccountFloorFetch: configs, FetchTime: time.Now().Add(time.Duration(configs.Period) * time.Second).Unix(), RefetchRequest: true} - + if fetchConfig.retryCount < f.maxRetries { + fetchConfig.fetchTime = f.time.Now().Add(time.Duration(fetchConfig.AccountFloorFetch.Period) * time.Second).Unix() + fetchConfig.refetchRequest = true + f.configReceiver <- fetchConfig + } } +// Stop terminates price floor fetcher func (f *PriceFloorFetcher) Stop() { + if f == nil { + return + } + close(f.done) + f.pool.Stop() + close(f.configReceiver) } -func (f *PriceFloorFetcher) submit(fetchInfo *FetchInfo) { +func (f *PriceFloorFetcher) submit(fetchConfig *fetchInfo) { status := f.pool.TrySubmit(func() { - f.worker(fetchInfo.AccountFloorFetch) + f.worker(*fetchConfig) }) if !status { - heap.Push(&f.fetchQueue, fetchInfo) + heap.Push(&f.fetchQueue, fetchConfig) } } func (f *PriceFloorFetcher) Fetcher() { - //Create Ticker of 5 minutes ticker := time.NewTicker(time.Duration(refetchCheckInterval) * time.Second) for { select { - case fetchInfo := <-f.configReceiver: - if fetchInfo.RefetchRequest { - heap.Push(&f.fetchQueue, &fetchInfo) + case fetchConfig := <-f.configReceiver: + if fetchConfig.refetchRequest { + heap.Push(&f.fetchQueue, &fetchConfig) } else { - if _, ok := f.fetchInprogress[fetchInfo.URL]; !ok { - f.fetchInprogress[fetchInfo.URL] = true - f.submit(&fetchInfo) + if _, ok := f.fetchInProgress[fetchConfig.URL]; !ok { + f.fetchInProgress[fetchConfig.URL] = true + f.submit(&fetchConfig) } } case <-ticker.C: - currentTime := time.Now().Unix() - for top := f.fetchQueue.Top(); top != nil && top.FetchTime <= currentTime; top = f.fetchQueue.Top() { + currentTime := f.time.Now().Unix() + for top := f.fetchQueue.Top(); top != nil && top.fetchTime <= currentTime; top = f.fetchQueue.Top() { nextFetch := heap.Pop(&f.fetchQueue) - f.submit(nextFetch.(*FetchInfo)) + f.submit(nextFetch.(*fetchInfo)) } case <-f.done: - f.pool.Stop() + ticker.Stop() glog.Info("Price Floor fetcher terminated") return } } } -func fetchAndValidate(configs config.AccountFloorFetch, metricEngine metrics.MetricsEngine) (*openrtb_ext.PriceFloorRules, int) { - - floorResp, maxAge, err := fetchFloorRulesFromURL(configs) - if err != nil { - metricEngine.RecordDynamicFetchFailure(configs.AccountID, "1") - glog.Errorf("Error while fetching floor data from URL: %s, reason : %s", configs.URL, err.Error()) +func (f *PriceFloorFetcher) fetchAndValidate(config config.AccountFloorFetch) (*openrtb_ext.PriceFloorRules, int) { + floorResp, maxAge, err := f.fetchFloorRulesFromURL(config) + if floorResp == nil || err != nil { + glog.Errorf("Error while fetching floor data from URL: %s, reason : %s", config.URL, err.Error()) return nil, 0 } - if len(floorResp) > (configs.MaxFileSize * 1024) { - glog.Errorf("Recieved invalid floor data from URL: %s, reason : floor file size is greater than MaxFileSize", configs.URL) + if len(floorResp) > (config.MaxFileSizeKB * 1024) { + glog.Errorf("Recieved invalid floor data from URL: %s, reason : floor file size is greater than MaxFileSize", config.URL) return nil, 0 } var priceFloors openrtb_ext.PriceFloorRules if err = json.Unmarshal(floorResp, &priceFloors.Data); err != nil { - metricEngine.RecordDynamicFetchFailure(configs.AccountID, "2") - glog.Errorf("Recieved invalid price floor json from URL: %s", configs.URL) + glog.Errorf("Recieved invalid price floor json from URL: %s", config.URL) + return nil, 0 + } + + if err := validateRules(config, &priceFloors); err != nil { + glog.Errorf("Validation failed for floor JSON from URL: %s, reason: %s", config.URL, err.Error()) return nil, 0 - } else { - err := validateRules(configs, &priceFloors) - if err != nil { - metricEngine.RecordDynamicFetchFailure(configs.AccountID, "3") - glog.Errorf("Validation failed for floor JSON from URL: %s, reason: %s", configs.URL, err.Error()) - return nil, 0 - } } return &priceFloors, maxAge @@ -234,30 +258,32 @@ func fetchAndValidate(configs config.AccountFloorFetch, metricEngine metrics.Met // fetchFloorRulesFromURL returns a price floor JSON and time for which this JSON is valid // from provided URL with timeout constraints -func fetchFloorRulesFromURL(configs config.AccountFloorFetch) ([]byte, int, error) { - - ctx, cancel := context.WithTimeout(context.Background(), time.Duration(configs.Timeout)*time.Millisecond) +func (f *PriceFloorFetcher) fetchFloorRulesFromURL(config config.AccountFloorFetch) ([]byte, int, error) { + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(config.Timeout)*time.Millisecond) defer cancel() - httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, configs.URL, nil) + httpReq, err := http.NewRequestWithContext(ctx, http.MethodGet, config.URL, nil) if err != nil { return nil, 0, errors.New("error while forming http fetch request : " + err.Error()) } - httpResp, err := http.DefaultClient.Do(httpReq) + httpResp, err := f.httpClient.Do(httpReq) if err != nil { return nil, 0, errors.New("error while getting response from url : " + err.Error()) } - if httpResp.StatusCode != 200 { + if httpResp.StatusCode != http.StatusOK { return nil, 0, errors.New("no response from server") } var maxAge int if maxAgeStr := httpResp.Header.Get("max-age"); maxAgeStr != "" { - maxAge, _ = strconv.Atoi(maxAgeStr) - if maxAge <= configs.Period || maxAge > math.MaxInt32 { - glog.Errorf("Invalid max-age = %s provided, value should be valid integer and should be within (%v, %v)", maxAgeStr, configs.Period, math.MaxInt32) + maxAge, err = strconv.Atoi(maxAgeStr) + if err != nil { + glog.Errorf("max-age in header is malformed for url %s", config.URL) + } + if maxAge <= config.Period || maxAge > math.MaxInt32 { + glog.Errorf("Invalid max-age = %s provided, value should be valid integer and should be within (%v, %v)", maxAgeStr, config.Period, math.MaxInt32) } } @@ -270,8 +296,7 @@ func fetchFloorRulesFromURL(configs config.AccountFloorFetch) ([]byte, int, erro return respBody, maxAge, nil } -func validateRules(configs config.AccountFloorFetch, priceFloors *openrtb_ext.PriceFloorRules) error { - +func validateRules(config config.AccountFloorFetch, priceFloors *openrtb_ext.PriceFloorRules) error { if priceFloors.Data == nil { return errors.New("empty data in floor JSON") } @@ -280,16 +305,16 @@ func validateRules(configs config.AccountFloorFetch, priceFloors *openrtb_ext.Pr return errors.New("no model groups found in price floor data") } - if priceFloors.Data.SkipRate < skipRateMin || priceFloors.Data.SkipRate > skipRateMax { - return fmt.Errorf("skip rate should be greater than or equal to %d and less than %d", skipRateMin, skipRateMax) + if priceFloors.Data.SkipRate < 0 || priceFloors.Data.SkipRate > 100 { + return errors.New("skip rate should be greater than or equal to 0 and less than 100") } - if priceFloors.Data.UseFetchDataRate != nil && (*priceFloors.Data.UseFetchDataRate < dataRateMin || *priceFloors.Data.UseFetchDataRate > dataRateMax) { - return fmt.Errorf("useFetchDataRate should be greater than or equal to %d and less than or equal to %d", dataRateMin, dataRateMax) + if priceFloors.Data.FetchRate != nil && (*priceFloors.Data.FetchRate < dataRateMin || *priceFloors.Data.FetchRate > dataRateMax) { + return errors.New("FetchRate should be greater than or equal to 0 and less than or equal to 100") } for _, modelGroup := range priceFloors.Data.ModelGroups { - if len(modelGroup.Values) == 0 || len(modelGroup.Values) > configs.MaxRules { + if len(modelGroup.Values) == 0 || len(modelGroup.Values) > config.MaxRules { return errors.New("invalid number of floor rules, floor rules should be greater than zero and less than MaxRules specified in account config") } diff --git a/floors/fetcher_test.go b/floors/fetcher_test.go index 83b13cf53be..cdf0537c9ed 100644 --- a/floors/fetcher_test.go +++ b/floors/fetcher_test.go @@ -11,11 +11,13 @@ import ( "time" "github.com/alitto/pond" - "github.com/patrickmn/go-cache" - "github.com/prebid/prebid-server/config" - metricsConf "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/coocood/freecache" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + metricsConf "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/prebid/prebid-server/v2/util/timeutil" "github.com/stretchr/testify/assert" ) @@ -60,13 +62,13 @@ func TestFetchQueueLess(t *testing.T) { }{ { name: "first fetchperiod is less than second", - fq: FetchQueue{&FetchInfo{FetchTime: 10}, &FetchInfo{FetchTime: 20}}, + fq: FetchQueue{&fetchInfo{fetchTime: 10}, &fetchInfo{fetchTime: 20}}, args: args{i: 0, j: 1}, want: true, }, { name: "first fetchperiod is greater than second", - fq: FetchQueue{&FetchInfo{FetchTime: 30}, &FetchInfo{FetchTime: 10}}, + fq: FetchQueue{&fetchInfo{fetchTime: 30}, &fetchInfo{fetchTime: 10}}, args: args{i: 0, j: 1}, want: false, }, @@ -92,7 +94,7 @@ func TestFetchQueueSwap(t *testing.T) { }{ { name: "Swap two elements at index i and j", - fq: FetchQueue{&FetchInfo{FetchTime: 30}, &FetchInfo{FetchTime: 10}}, + fq: FetchQueue{&fetchInfo{fetchTime: 30}, &fetchInfo{fetchTime: 10}}, args: args{i: 0, j: 1}, }, } @@ -118,14 +120,14 @@ func TestFetchQueuePush(t *testing.T) { { name: "Push element to queue", fq: &FetchQueue{}, - args: args{element: &FetchInfo{FetchTime: 10}}, + args: args{element: &fetchInfo{fetchTime: 10}}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.fq.Push(tt.args.element) q := *tt.fq - assert.Equal(t, q[0], &FetchInfo{FetchTime: 10}) + assert.Equal(t, q[0], &fetchInfo{fetchTime: 10}) }) } } @@ -138,8 +140,8 @@ func TestFetchQueuePop(t *testing.T) { }{ { name: "Pop element from queue", - fq: &FetchQueue{&FetchInfo{FetchTime: 10}}, - want: &FetchInfo{FetchTime: 10}, + fq: &FetchQueue{&fetchInfo{fetchTime: 10}}, + want: &fetchInfo{fetchTime: 10}, }, } for _, tt := range tests { @@ -155,12 +157,12 @@ func TestFetchQueueTop(t *testing.T) { tests := []struct { name string fq *FetchQueue - want *FetchInfo + want *fetchInfo }{ { name: "Get top element from queue", - fq: &FetchQueue{&FetchInfo{FetchTime: 20}}, - want: &FetchInfo{FetchTime: 20}, + fq: &FetchQueue{&fetchInfo{fetchTime: 20}}, + want: &fetchInfo{fetchTime: 20}, }, { name: "Queue is empty", @@ -178,7 +180,6 @@ func TestFetchQueueTop(t *testing.T) { } func TestValidatePriceFloorRules(t *testing.T) { - var zero = 0 var one_o_one = 101 var testURL = "abc.com" @@ -195,13 +196,13 @@ func TestValidatePriceFloorRules(t *testing.T) { name: "Price floor data is empty", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - URL: testURL, - Timeout: 5, - MaxFileSize: 20, - MaxRules: 5, - MaxAge: 20, - Period: 10, + Enabled: true, + URL: testURL, + Timeout: 5, + MaxFileSizeKB: 20, + MaxRules: 5, + MaxAge: 20, + Period: 10, }, priceFloors: &openrtb_ext.PriceFloorRules{}, }, @@ -211,13 +212,13 @@ func TestValidatePriceFloorRules(t *testing.T) { name: "Model group array is empty", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - URL: testURL, - Timeout: 5, - MaxFileSize: 20, - MaxRules: 5, - MaxAge: 20, - Period: 10, + Enabled: true, + URL: testURL, + Timeout: 5, + MaxFileSizeKB: 20, + MaxRules: 5, + MaxAge: 20, + Period: 10, }, priceFloors: &openrtb_ext.PriceFloorRules{ Data: &openrtb_ext.PriceFloorData{}, @@ -229,13 +230,13 @@ func TestValidatePriceFloorRules(t *testing.T) { name: "floor rules is empty", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - URL: testURL, - Timeout: 5, - MaxFileSize: 20, - MaxRules: 5, - MaxAge: 20, - Period: 10, + Enabled: true, + URL: testURL, + Timeout: 5, + MaxFileSizeKB: 20, + MaxRules: 5, + MaxAge: 20, + Period: 10, }, priceFloors: &openrtb_ext.PriceFloorRules{ Data: &openrtb_ext.PriceFloorData{ @@ -251,13 +252,13 @@ func TestValidatePriceFloorRules(t *testing.T) { name: "floor rules is grater than max floor rules", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - URL: testURL, - Timeout: 5, - MaxFileSize: 20, - MaxRules: 0, - MaxAge: 20, - Period: 10, + Enabled: true, + URL: testURL, + Timeout: 5, + MaxFileSizeKB: 20, + MaxRules: 0, + MaxAge: 20, + Period: 10, }, priceFloors: &openrtb_ext.PriceFloorRules{ Data: &openrtb_ext.PriceFloorData{ @@ -275,13 +276,13 @@ func TestValidatePriceFloorRules(t *testing.T) { name: "Modelweight is zero", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - URL: testURL, - Timeout: 5, - MaxFileSize: 20, - MaxRules: 1, - MaxAge: 20, - Period: 10, + Enabled: true, + URL: testURL, + Timeout: 5, + MaxFileSizeKB: 20, + MaxRules: 1, + MaxAge: 20, + Period: 10, }, priceFloors: &openrtb_ext.PriceFloorRules{ Data: &openrtb_ext.PriceFloorData{ @@ -300,13 +301,13 @@ func TestValidatePriceFloorRules(t *testing.T) { name: "Modelweight is 101", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - URL: testURL, - Timeout: 5, - MaxFileSize: 20, - MaxRules: 1, - MaxAge: 20, - Period: 10, + Enabled: true, + URL: testURL, + Timeout: 5, + MaxFileSizeKB: 20, + MaxRules: 1, + MaxAge: 20, + Period: 10, }, priceFloors: &openrtb_ext.PriceFloorRules{ Data: &openrtb_ext.PriceFloorData{ @@ -325,13 +326,13 @@ func TestValidatePriceFloorRules(t *testing.T) { name: "skiprate is 101", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - URL: testURL, - Timeout: 5, - MaxFileSize: 20, - MaxRules: 1, - MaxAge: 20, - Period: 10, + Enabled: true, + URL: testURL, + Timeout: 5, + MaxFileSizeKB: 20, + MaxRules: 1, + MaxAge: 20, + Period: 10, }, priceFloors: &openrtb_ext.PriceFloorRules{ Data: &openrtb_ext.PriceFloorData{ @@ -350,13 +351,13 @@ func TestValidatePriceFloorRules(t *testing.T) { name: "Default is -1", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - URL: testURL, - Timeout: 5, - MaxFileSize: 20, - MaxRules: 1, - MaxAge: 20, - Period: 10, + Enabled: true, + URL: testURL, + Timeout: 5, + MaxFileSizeKB: 20, + MaxRules: 1, + MaxAge: 20, + Period: 10, }, priceFloors: &openrtb_ext.PriceFloorRules{ Data: &openrtb_ext.PriceFloorData{ @@ -375,13 +376,13 @@ func TestValidatePriceFloorRules(t *testing.T) { name: "Invalid skip rate in data", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - URL: testURL, - Timeout: 5, - MaxFileSize: 20, - MaxRules: 1, - MaxAge: 20, - Period: 10, + Enabled: true, + URL: testURL, + Timeout: 5, + MaxFileSizeKB: 20, + MaxRules: 1, + MaxAge: 20, + Period: 10, }, priceFloors: &openrtb_ext.PriceFloorRules{ Data: &openrtb_ext.PriceFloorData{ @@ -397,16 +398,16 @@ func TestValidatePriceFloorRules(t *testing.T) { wantErr: true, }, { - name: "Invalid useFetchDataRate", + name: "Invalid FetchRate", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - URL: testURL, - Timeout: 5, - MaxFileSize: 20, - MaxRules: 1, - MaxAge: 20, - Period: 10, + Enabled: true, + URL: testURL, + Timeout: 5, + MaxFileSizeKB: 20, + MaxRules: 1, + MaxAge: 20, + Period: 10, }, priceFloors: &openrtb_ext.PriceFloorRules{ Data: &openrtb_ext.PriceFloorData{ @@ -416,7 +417,7 @@ func TestValidatePriceFloorRules(t *testing.T) { "*|*|www.website.com": 15.01, }, }}, - UseFetchDataRate: ptrutil.ToPtr(-11), + FetchRate: ptrutil.ToPtr(-11), }, }, }, @@ -433,7 +434,6 @@ func TestValidatePriceFloorRules(t *testing.T) { } func TestFetchFloorRulesFromURL(t *testing.T) { - mockHandler := func(mockResponse []byte, mockStatus int) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Add("Content-Length", "645") @@ -538,7 +538,10 @@ func TestFetchFloorRulesFromURL(t *testing.T) { if tt.args.configs.URL == "" { tt.args.configs.URL = mockHttpServer.URL } - got, got1, err := fetchFloorRulesFromURL(tt.args.configs) + pff := PriceFloorFetcher{ + httpClient: mockHttpServer.Client(), + } + got, got1, err := pff.fetchFloorRulesFromURL(tt.args.configs) if (err != nil) != tt.wantErr { t.Errorf("fetchFloorRulesFromURL() error = %v, wantErr %v", err, tt.wantErr) return @@ -554,7 +557,6 @@ func TestFetchFloorRulesFromURL(t *testing.T) { } func TestFetchFloorRulesFromURLInvalidMaxAge(t *testing.T) { - mockHandler := func(mockResponse []byte, mockStatus int) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Add("Content-Length", "645") @@ -607,7 +609,10 @@ func TestFetchFloorRulesFromURLInvalidMaxAge(t *testing.T) { tt.args.configs.URL = mockHttpServer.URL } - got, got1, err := fetchFloorRulesFromURL(tt.args.configs) + ppf := PriceFloorFetcher{ + httpClient: mockHttpServer.Client(), + } + got, got1, err := ppf.fetchFloorRulesFromURL(tt.args.configs) if (err != nil) != tt.wantErr { t.Errorf("fetchFloorRulesFromURL() error = %v, wantErr %v", err, tt.wantErr) return @@ -623,7 +628,6 @@ func TestFetchFloorRulesFromURLInvalidMaxAge(t *testing.T) { } func TestFetchAndValidate(t *testing.T) { - mockHandler := func(mockResponse []byte, mockStatus int) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Add(MaxAge, "30") @@ -647,12 +651,12 @@ func TestFetchAndValidate(t *testing.T) { name: "Recieved valid price floor rules response", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - Timeout: 30, - MaxFileSize: 700, - MaxRules: 30, - MaxAge: 60, - Period: 40, + Enabled: true, + Timeout: 30, + MaxFileSizeKB: 700, + MaxRules: 30, + MaxAge: 60, + Period: 40, }, }, response: func() []byte { @@ -672,12 +676,12 @@ func TestFetchAndValidate(t *testing.T) { name: "No response from server", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - Timeout: 30, - MaxFileSize: 700, - MaxRules: 30, - MaxAge: 60, - Period: 40, + Enabled: true, + Timeout: 30, + MaxFileSizeKB: 700, + MaxRules: 30, + MaxAge: 60, + Period: 40, }, }, response: []byte{}, @@ -689,12 +693,12 @@ func TestFetchAndValidate(t *testing.T) { name: "File is greater than MaxFileSize", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - Timeout: 30, - MaxFileSize: 1, - MaxRules: 30, - MaxAge: 60, - Period: 40, + Enabled: true, + Timeout: 30, + MaxFileSizeKB: 1, + MaxRules: 30, + MaxAge: 60, + Period: 40, }, }, response: func() []byte { @@ -709,12 +713,12 @@ func TestFetchAndValidate(t *testing.T) { name: "Malformed response : json unmarshalling failed", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - Timeout: 30, - MaxFileSize: 800, - MaxRules: 30, - MaxAge: 60, - Period: 40, + Enabled: true, + Timeout: 30, + MaxFileSizeKB: 800, + MaxRules: 30, + MaxAge: 60, + Period: 40, }, }, response: func() []byte { @@ -729,12 +733,12 @@ func TestFetchAndValidate(t *testing.T) { name: "Validations failed for price floor rules response", args: args{ configs: config.AccountFloorFetch{ - Enabled: true, - Timeout: 30, - MaxFileSize: 700, - MaxRules: 30, - MaxAge: 60, - Period: 40, + Enabled: true, + Timeout: 30, + MaxFileSizeKB: 700, + MaxRules: 30, + MaxAge: 60, + Period: 40, }, }, response: func() []byte { @@ -750,9 +754,11 @@ func TestFetchAndValidate(t *testing.T) { t.Run(tt.name, func(t *testing.T) { mockHttpServer := httptest.NewServer(mockHandler(tt.response, tt.responseStatus)) defer mockHttpServer.Close() - + ppf := PriceFloorFetcher{ + httpClient: mockHttpServer.Client(), + } tt.args.configs.URL = mockHttpServer.URL - got, got1 := fetchAndValidate(tt.args.configs, &metricsConf.NilMetricsEngine{}) + got, got1 := ppf.fetchAndValidate(tt.args.configs) if !reflect.DeepEqual(got, tt.want) { t.Errorf("fetchAndValidate() got = %v, want %v", got, tt.want) } @@ -763,8 +769,30 @@ func TestFetchAndValidate(t *testing.T) { } } -func TestFetcherWhenRequestGetSameURLInrequest(t *testing.T) { +func mockFetcherInstance(config config.PriceFloors, httpClient *http.Client, metricEngine metrics.MetricsEngine) *PriceFloorFetcher { + if !config.Enabled { + return nil + } + + floorFetcher := PriceFloorFetcher{ + pool: pond.New(config.Fetcher.Worker, config.Fetcher.Capacity, pond.PanicHandler(workerPanicHandler)), + fetchQueue: make(FetchQueue, 0, 100), + fetchInProgress: make(map[string]bool), + configReceiver: make(chan fetchInfo, config.Fetcher.Capacity), + done: make(chan struct{}), + cache: freecache.NewCache(config.Fetcher.CacheSize * 1024 * 1024), + httpClient: httpClient, + time: &timeutil.RealTime{}, + metricEngine: metricEngine, + maxRetries: 10, + } + go floorFetcher.Fetcher() + + return &floorFetcher +} + +func TestFetcherWhenRequestGetSameURLInrequest(t *testing.T) { refetchCheckInterval = 1 response := []byte(`{"currency":"USD","modelgroups":[{"modelweight":40,"modelversion":"version1","default":5,"values":{"banner|300x600|www.website.com":3,"banner|728x90|www.website.com":5,"banner|300x600|*":4,"banner|300x250|*":2,"*|*|*":16,"*|300x250|*":10,"*|300x600|*":12,"*|300x600|www.website.com":11,"banner|*|*":8,"banner|300x250|www.website.com":1,"*|728x90|www.website.com":13,"*|300x250|www.website.com":9,"*|728x90|*":14,"banner|728x90|*":6,"banner|*|www.website.com":7,"*|*|www.website.com":15},"schema":{"fields":["mediaType","size","domain"],"delimiter":"|"}}]}`) mockHandler := func(mockResponse []byte, mockStatus int) http.Handler { @@ -777,91 +805,179 @@ func TestFetcherWhenRequestGetSameURLInrequest(t *testing.T) { mockHttpServer := httptest.NewServer(mockHandler(response, 200)) defer mockHttpServer.Close() - fectherInstance := NewPriceFloorFetcher(5, 10, 1, 20, &metricsConf.NilMetricsEngine{}) - defer fectherInstance.Stop() - defer fectherInstance.pool.Stop() + floorConfig := config.PriceFloors{ + Enabled: true, + Fetcher: config.PriceFloorFetcher{ + CacheSize: 1, + Worker: 5, + Capacity: 10, + }, + } + fetcherInstance := mockFetcherInstance(floorConfig, mockHttpServer.Client(), &metricsConf.NilMetricsEngine{}) + defer fetcherInstance.Stop() fetchConfig := config.AccountPriceFloors{ Enabled: true, UseDynamicData: true, - Fetch: config.AccountFloorFetch{ - Enabled: true, - URL: mockHttpServer.URL, - Timeout: 100, - MaxFileSize: 1000, - MaxRules: 100, - MaxAge: 20, - Period: 1, + Fetcher: config.AccountFloorFetch{ + Enabled: true, + URL: mockHttpServer.URL, + Timeout: 100, + MaxFileSizeKB: 1000, + MaxRules: 100, + MaxAge: 20, + Period: 1, }, } for i := 0; i < 50; i++ { - fectherInstance.Fetch(fetchConfig) + fetcherInstance.Fetch(fetchConfig) } - assert.Never(t, func() bool { return len(fectherInstance.fetchQueue) > 1 }, time.Duration(2*time.Second), 100*time.Millisecond, "Queue Got more than one entry") - assert.Never(t, func() bool { return len(fectherInstance.fetchInprogress) > 1 }, time.Duration(2*time.Second), 100*time.Millisecond, "Map Got more than one entry") + assert.Never(t, func() bool { return len(fetcherInstance.fetchQueue) > 1 }, time.Duration(2*time.Second), 100*time.Millisecond, "Queue Got more than one entry") + assert.Never(t, func() bool { return len(fetcherInstance.fetchInProgress) > 1 }, time.Duration(2*time.Second), 100*time.Millisecond, "Map Got more than one entry") } func TestFetcherDataPresentInCache(t *testing.T) { + floorConfig := config.PriceFloors{ + Enabled: true, + Fetcher: config.PriceFloorFetcher{ + CacheSize: 1, + Worker: 2, + Capacity: 5, + }, + } - fectherInstance := NewPriceFloorFetcher(2, 5, 5, 20, &metricsConf.NilMetricsEngine{}) - defer fectherInstance.Stop() - defer fectherInstance.pool.Stop() + fetcherInstance := mockFetcherInstance(floorConfig, http.DefaultClient, &metricsConf.NilMetricsEngine{}) + defer fetcherInstance.Stop() fetchConfig := config.AccountPriceFloors{ Enabled: true, UseDynamicData: true, - Fetch: config.AccountFloorFetch{ - Enabled: true, - URL: "http://test.com/floor", - Timeout: 100, - MaxFileSize: 1000, - MaxRules: 100, - MaxAge: 20, - Period: 5, + Fetcher: config.AccountFloorFetch{ + Enabled: true, + URL: "http://test.com/floor", + Timeout: 100, + MaxFileSizeKB: 1000, + MaxRules: 100, + MaxAge: 20, + Period: 5, }, } var res *openrtb_ext.PriceFloorRules data := `{"data":{"currency":"USD","modelgroups":[{"modelweight":40,"modelversion":"version1","default":5,"values":{"banner|300x600|www.website.com":3,"banner|728x90|www.website.com":5,"banner|300x600|*":4,"banner|300x250|*":2,"*|*|*":16,"*|300x250|*":10,"*|300x600|*":12,"*|300x600|www.website.com":11,"banner|*|*":8,"banner|300x250|www.website.com":1,"*|728x90|www.website.com":13,"*|300x250|www.website.com":9,"*|728x90|*":14,"banner|728x90|*":6,"banner|*|www.website.com":7,"*|*|www.website.com":15},"schema":{"fields":["mediaType","size","domain"],"delimiter":"|"}}]},"enabled":true,"floormin":1,"enforcement":{"enforcepbs":false,"floordeals":true}}` _ = json.Unmarshal([]byte(data), &res) - fectherInstance.SetWithExpiry("http://test.com/floor", res, fectherInstance.cacheExpiry) + fetcherInstance.SetWithExpiry("http://test.com/floor", []byte(data), fetchConfig.Fetcher.MaxAge) - val, status := fectherInstance.Fetch(fetchConfig) + val, status := fetcherInstance.Fetch(fetchConfig) assert.Equal(t, res, val, "Invalid value in cache or cache is empty") assert.Equal(t, "success", status, "Floor fetch should be success") } func TestFetcherDataNotPresentInCache(t *testing.T) { + floorConfig := config.PriceFloors{ + Enabled: true, + Fetcher: config.PriceFloorFetcher{ + CacheSize: 1, + Worker: 2, + Capacity: 5, + }, + } - fectherInstance := NewPriceFloorFetcher(2, 5, 5, 20, &metricsConf.NilMetricsEngine{}) - defer fectherInstance.Stop() - defer fectherInstance.pool.Stop() + fetcherInstance := mockFetcherInstance(floorConfig, http.DefaultClient, &metricsConf.NilMetricsEngine{}) + defer fetcherInstance.Stop() fetchConfig := config.AccountPriceFloors{ Enabled: true, UseDynamicData: true, - Fetch: config.AccountFloorFetch{ - Enabled: true, - URL: "http://test.com/floor", - Timeout: 100, - MaxFileSize: 1000, - MaxRules: 100, - MaxAge: 20, - Period: 5, + Fetcher: config.AccountFloorFetch{ + Enabled: true, + URL: "http://test.com/floor", + Timeout: 100, + MaxFileSizeKB: 1000, + MaxRules: 100, + MaxAge: 20, + Period: 5, }, } - fectherInstance.SetWithExpiry("http://test.com/floor", nil, fectherInstance.cacheExpiry) + fetcherInstance.SetWithExpiry("http://test.com/floor", nil, fetchConfig.Fetcher.MaxAge) - val, status := fectherInstance.Fetch(fetchConfig) + val, status := fetcherInstance.Fetch(fetchConfig) assert.Equal(t, (*openrtb_ext.PriceFloorRules)(nil), val, "Floor data should be nil") assert.Equal(t, "error", status, "Floor fetch should be error") } -func TestPriceFloorFetcherWorker(t *testing.T) { +func TestFetcherEntryNotPresentInCache(t *testing.T) { + floorConfig := config.PriceFloors{ + Enabled: true, + Fetcher: config.PriceFloorFetcher{ + CacheSize: 1, + Worker: 2, + Capacity: 5, + MaxRetries: 10, + }, + } + + fetcherInstance := NewPriceFloorFetcher(floorConfig, http.DefaultClient, &metricsConf.NilMetricsEngine{}) + defer fetcherInstance.Stop() + + fetchConfig := config.AccountPriceFloors{ + Enabled: true, + UseDynamicData: true, + Fetcher: config.AccountFloorFetch{ + Enabled: true, + URL: "http://test.com/floor", + Timeout: 100, + MaxFileSizeKB: 1000, + MaxRules: 100, + MaxAge: 20, + Period: 5, + }, + } + + val, status := fetcherInstance.Fetch(fetchConfig) + + assert.Equal(t, (*openrtb_ext.PriceFloorRules)(nil), val, "Floor data should be nil") + assert.Equal(t, openrtb_ext.FetchInprogress, status, "Floor fetch should be error") +} +func TestFetcherDynamicFetchDisable(t *testing.T) { + floorConfig := config.PriceFloors{ + Enabled: true, + Fetcher: config.PriceFloorFetcher{ + CacheSize: 1, + Worker: 2, + Capacity: 5, + MaxRetries: 5, + }, + } + + fetcherInstance := NewPriceFloorFetcher(floorConfig, http.DefaultClient, &metricsConf.NilMetricsEngine{}) + defer fetcherInstance.Stop() + + fetchConfig := config.AccountPriceFloors{ + Enabled: true, + UseDynamicData: false, + Fetcher: config.AccountFloorFetch{ + Enabled: true, + URL: "http://test.com/floor", + Timeout: 100, + MaxFileSizeKB: 1000, + MaxRules: 100, + MaxAge: 20, + Period: 5, + }, + } + + val, status := fetcherInstance.Fetch(fetchConfig) + + assert.Equal(t, (*openrtb_ext.PriceFloorRules)(nil), val, "Floor data should be nil") + assert.Equal(t, openrtb_ext.FetchNone, status, "Floor fetch should be error") +} + +func TestPriceFloorFetcherWorker(t *testing.T) { var floorData openrtb_ext.PriceFloorData response := []byte(`{"currency":"USD","modelgroups":[{"modelweight":40,"modelversion":"version1","default":5,"values":{"banner|300x600|www.website.com":3,"banner|728x90|www.website.com":5,"banner|300x600|*":4,"banner|300x250|*":2,"*|*|*":16,"*|300x250|*":10,"*|300x600|*":12,"*|300x600|www.website.com":11,"banner|*|*":8,"banner|300x250|www.website.com":1,"*|728x90|www.website.com":13,"*|300x250|www.website.com":9,"*|728x90|*":14,"banner|728x90|*":6,"banner|*|www.website.com":7,"*|*|www.website.com":15},"schema":{"fields":["mediaType","size","domain"],"delimiter":"|"}}]}`) _ = json.Unmarshal(response, &floorData) @@ -880,38 +996,88 @@ func TestPriceFloorFetcherWorker(t *testing.T) { mockHttpServer := httptest.NewServer(mockHandler(response, 200)) defer mockHttpServer.Close() - fectherInstance := PriceFloorFetcher{ + fetcherInstance := PriceFloorFetcher{ pool: nil, fetchQueue: nil, - fetchInprogress: nil, - configReceiver: make(chan FetchInfo, 1), + fetchInProgress: nil, + configReceiver: make(chan fetchInfo, 1), done: nil, - cache: cache.New(time.Duration(5)*time.Second, time.Duration(2)*time.Second), - cacheExpiry: 10, + cache: freecache.NewCache(1 * 1024 * 1024), + httpClient: mockHttpServer.Client(), + time: &timeutil.RealTime{}, + metricEngine: &metricsConf.NilMetricsEngine{}, + maxRetries: 10, } + defer close(fetcherInstance.configReceiver) - fetchConfig := config.AccountFloorFetch{ - Enabled: true, - URL: mockHttpServer.URL, - Timeout: 100, - MaxFileSize: 1000, - MaxRules: 100, - MaxAge: 20, - Period: 1, + fetchConfig := fetchInfo{ + AccountFloorFetch: config.AccountFloorFetch{ + Enabled: true, + URL: mockHttpServer.URL, + Timeout: 100, + MaxFileSizeKB: 1000, + MaxRules: 100, + MaxAge: 20, + Period: 1, + }, } - fectherInstance.worker(fetchConfig) - dataInCache, _ := fectherInstance.Get(mockHttpServer.URL) - assert.Equal(t, floorResp, dataInCache, "Data should be stored in cache") + fetcherInstance.worker(fetchConfig) + dataInCache, _ := fetcherInstance.Get(mockHttpServer.URL) + var gotFloorData *openrtb_ext.PriceFloorRules + json.Unmarshal(dataInCache, &gotFloorData) + assert.Equal(t, floorResp, gotFloorData, "Data should be stored in cache") - info := <-fectherInstance.configReceiver - assert.Equal(t, true, info.RefetchRequest, "Recieved request is not refetch request") + info := <-fetcherInstance.configReceiver + assert.Equal(t, true, info.refetchRequest, "Recieved request is not refetch request") assert.Equal(t, mockHttpServer.URL, info.AccountFloorFetch.URL, "Recieved request with different url") } -func TestPriceFloorFetcherWorkerDefaultCacheExpiry(t *testing.T) { +func TestPriceFloorFetcherWorkerRetry(t *testing.T) { + mockHandler := func(mockResponse []byte, mockStatus int) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(mockStatus) + w.Write(mockResponse) + }) + } + + mockHttpServer := httptest.NewServer(mockHandler(nil, 500)) + defer mockHttpServer.Close() + + fetcherInstance := PriceFloorFetcher{ + pool: nil, + fetchQueue: nil, + fetchInProgress: nil, + configReceiver: make(chan fetchInfo, 1), + done: nil, + cache: nil, + httpClient: mockHttpServer.Client(), + time: &timeutil.RealTime{}, + metricEngine: &metricsConf.NilMetricsEngine{}, + maxRetries: 5, + } + defer close(fetcherInstance.configReceiver) + + fetchConfig := fetchInfo{ + AccountFloorFetch: config.AccountFloorFetch{ + Enabled: true, + URL: mockHttpServer.URL, + Timeout: 100, + MaxFileSizeKB: 1000, + MaxRules: 100, + MaxAge: 20, + Period: 1, + }, + } + + fetcherInstance.worker(fetchConfig) + info := <-fetcherInstance.configReceiver + assert.Equal(t, 1, info.retryCount, "Retry Count is not 1") +} + +func TestPriceFloorFetcherWorkerDefaultCacheExpiry(t *testing.T) { var floorData openrtb_ext.PriceFloorData response := []byte(`{"currency":"USD","modelgroups":[{"modelweight":40,"modelversion":"version1","default":5,"values":{"banner|300x600|www.website.com":3,"banner|728x90|www.website.com":5,"banner|300x600|*":4,"banner|300x250|*":2,"*|*|*":16,"*|300x250|*":10,"*|300x600|*":12,"*|300x600|www.website.com":11,"banner|*|*":8,"banner|300x250|www.website.com":1,"*|728x90|www.website.com":13,"*|300x250|www.website.com":9,"*|728x90|*":14,"banner|728x90|*":6,"banner|*|www.website.com":7,"*|*|www.website.com":15},"schema":{"fields":["mediaType","size","domain"],"delimiter":"|"}}]}`) _ = json.Unmarshal(response, &floorData) @@ -929,39 +1095,45 @@ func TestPriceFloorFetcherWorkerDefaultCacheExpiry(t *testing.T) { mockHttpServer := httptest.NewServer(mockHandler(response, 200)) defer mockHttpServer.Close() - fectherInstance := &PriceFloorFetcher{ + fetcherInstance := &PriceFloorFetcher{ pool: nil, fetchQueue: nil, - fetchInprogress: nil, - configReceiver: make(chan FetchInfo, 1), + fetchInProgress: nil, + configReceiver: make(chan fetchInfo, 1), done: nil, - cache: cache.New(time.Duration(5)*time.Second, time.Duration(2)*time.Second), - cacheExpiry: time.Duration(10) * time.Second, + cache: freecache.NewCache(1 * 1024 * 1024), + httpClient: mockHttpServer.Client(), + time: &timeutil.RealTime{}, + metricEngine: &metricsConf.NilMetricsEngine{}, + maxRetries: 5, } - fetchConfig := config.AccountFloorFetch{ - Enabled: true, - URL: mockHttpServer.URL, - Timeout: 100, - MaxFileSize: 1000, - MaxRules: 100, - MaxAge: 20, - Period: 1, + fetchConfig := fetchInfo{ + AccountFloorFetch: config.AccountFloorFetch{ + Enabled: true, + URL: mockHttpServer.URL, + Timeout: 100, + MaxFileSizeKB: 1000, + MaxRules: 100, + MaxAge: 20, + Period: 1, + }, } - fectherInstance.worker(fetchConfig) - dataInCache, _ := fectherInstance.Get(mockHttpServer.URL) - assert.Equal(t, floorResp, dataInCache, "Data should be stored in cache") + fetcherInstance.worker(fetchConfig) + dataInCache, _ := fetcherInstance.Get(mockHttpServer.URL) + var gotFloorData *openrtb_ext.PriceFloorRules + json.Unmarshal(dataInCache, &gotFloorData) + assert.Equal(t, floorResp, gotFloorData, "Data should be stored in cache") - info := <-fectherInstance.configReceiver - close(fectherInstance.configReceiver) - assert.Equal(t, true, info.RefetchRequest, "Recieved request is not refetch request") + info := <-fetcherInstance.configReceiver + defer close(fetcherInstance.configReceiver) + assert.Equal(t, true, info.refetchRequest, "Recieved request is not refetch request") assert.Equal(t, mockHttpServer.URL, info.AccountFloorFetch.URL, "Recieved request with different url") } func TestPriceFloorFetcherSubmit(t *testing.T) { - response := []byte(`{"currency":"USD","modelgroups":[{"modelweight":40,"modelversion":"version1","default":5,"values":{"banner|300x600|www.website.com":3,"banner|728x90|www.website.com":5,"banner|300x600|*":4,"banner|300x250|*":2,"*|*|*":16,"*|300x250|*":10,"*|300x600|*":12,"*|300x600|www.website.com":11,"banner|*|*":8,"banner|300x250|www.website.com":1,"*|728x90|www.website.com":13,"*|300x250|www.website.com":9,"*|728x90|*":14,"banner|728x90|*":6,"banner|*|www.website.com":7,"*|*|www.website.com":15},"schema":{"fields":["mediaType","size","domain"],"delimiter":"|"}}]}`) mockHandler := func(mockResponse []byte, mockStatus int) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { @@ -973,36 +1145,38 @@ func TestPriceFloorFetcherSubmit(t *testing.T) { mockHttpServer := httptest.NewServer(mockHandler(response, 200)) defer mockHttpServer.Close() - fectherInstance := &PriceFloorFetcher{ + fetcherInstance := &PriceFloorFetcher{ pool: pond.New(1, 1), fetchQueue: make(FetchQueue, 0), - fetchInprogress: nil, - configReceiver: make(chan FetchInfo, 1), - done: nil, - cache: cache.New(time.Duration(2)*time.Second, time.Duration(1)*time.Second), - cacheExpiry: 2, + fetchInProgress: nil, + configReceiver: make(chan fetchInfo, 1), + done: make(chan struct{}), + cache: freecache.NewCache(1 * 1024 * 1024), + httpClient: mockHttpServer.Client(), + time: &timeutil.RealTime{}, + metricEngine: &metricsConf.NilMetricsEngine{}, + maxRetries: 5, } - defer fectherInstance.pool.Stop() + defer fetcherInstance.Stop() - fetchInfo := FetchInfo{ - RefetchRequest: false, - FetchTime: time.Now().Unix(), + fetchInfo := fetchInfo{ + refetchRequest: false, + fetchTime: time.Now().Unix(), AccountFloorFetch: config.AccountFloorFetch{ - Enabled: true, - URL: mockHttpServer.URL, - Timeout: 100, - MaxFileSize: 1000, - MaxRules: 100, - MaxAge: 2, - Period: 1, + Enabled: true, + URL: mockHttpServer.URL, + Timeout: 100, + MaxFileSizeKB: 1000, + MaxRules: 100, + MaxAge: 2, + Period: 1, }, } - fectherInstance.submit(&fetchInfo) + fetcherInstance.submit(&fetchInfo) - info := <-fectherInstance.configReceiver - close(fectherInstance.configReceiver) - assert.Equal(t, true, info.RefetchRequest, "Recieved request is not refetch request") + info := <-fetcherInstance.configReceiver + assert.Equal(t, true, info.refetchRequest, "Recieved request is not refetch request") assert.Equal(t, mockHttpServer.URL, info.AccountFloorFetch.URL, "Recieved request with different url") } @@ -1016,34 +1190,32 @@ func (t *testPool) TrySubmit(task func()) bool { func (t *testPool) Stop() {} func TestPriceFloorFetcherSubmitFailed(t *testing.T) { - - fectherInstance := &PriceFloorFetcher{ + fetcherInstance := &PriceFloorFetcher{ pool: &testPool{}, fetchQueue: make(FetchQueue, 0), - fetchInprogress: nil, + fetchInProgress: nil, configReceiver: nil, done: nil, cache: nil, - cacheExpiry: 2, } - defer fectherInstance.pool.Stop() + defer fetcherInstance.pool.Stop() - fetchInfo := FetchInfo{ - RefetchRequest: false, - FetchTime: time.Now().Unix(), + fetchInfo := fetchInfo{ + refetchRequest: false, + fetchTime: time.Now().Unix(), AccountFloorFetch: config.AccountFloorFetch{ - Enabled: true, - URL: "http://test.com", - Timeout: 100, - MaxFileSize: 1000, - MaxRules: 100, - MaxAge: 2, - Period: 1, + Enabled: true, + URL: "http://test.com", + Timeout: 100, + MaxFileSizeKB: 1000, + MaxRules: 100, + MaxAge: 2, + Period: 1, }, } - fectherInstance.submit(&fetchInfo) - assert.Equal(t, 1, len(fectherInstance.fetchQueue), "Unable to submit the task") + fetcherInstance.submit(&fetchInfo) + assert.Equal(t, 1, len(fetcherInstance.fetchQueue), "Unable to submit the task") } func getRandomNumber() int { @@ -1054,7 +1226,6 @@ func getRandomNumber() int { } func TestFetcherWhenRequestGetDifferentURLInrequest(t *testing.T) { - refetchCheckInterval = 1 response := []byte(`{"currency":"USD","modelgroups":[{"modelweight":40,"modelversion":"version1","default":5,"values":{"banner|300x600|www.website.com":3,"banner|728x90|www.website.com":5,"banner|300x600|*":4,"banner|300x250|*":2,"*|*|*":16,"*|300x250|*":10,"*|300x600|*":12,"*|300x600|www.website.com":11,"banner|*|*":8,"banner|300x250|www.website.com":1,"*|728x90|www.website.com":13,"*|300x250|www.website.com":9,"*|728x90|*":14,"banner|728x90|*":6,"banner|*|www.website.com":7,"*|*|www.website.com":15},"schema":{"fields":["mediaType","size","domain"],"delimiter":"|"}}]}`) mockHandler := func(mockResponse []byte, mockStatus int) http.Handler { @@ -1067,29 +1238,69 @@ func TestFetcherWhenRequestGetDifferentURLInrequest(t *testing.T) { mockHttpServer := httptest.NewServer(mockHandler(response, 200)) defer mockHttpServer.Close() - fectherInstance := NewPriceFloorFetcher(5, 10, 1, 20, &metricsConf.NilMetricsEngine{}) - defer fectherInstance.Stop() - defer fectherInstance.pool.Stop() + floorConfig := config.PriceFloors{ + Enabled: true, + Fetcher: config.PriceFloorFetcher{ + CacheSize: 1, + Worker: 5, + Capacity: 10, + MaxRetries: 5, + }, + } + fetcherInstance := mockFetcherInstance(floorConfig, mockHttpServer.Client(), &metricsConf.NilMetricsEngine{}) + defer fetcherInstance.Stop() fetchConfig := config.AccountPriceFloors{ Enabled: true, UseDynamicData: true, - Fetch: config.AccountFloorFetch{ - Enabled: true, - URL: mockHttpServer.URL, - Timeout: 100, - MaxFileSize: 1000, - MaxRules: 100, - MaxAge: 5, - Period: 1, + Fetcher: config.AccountFloorFetch{ + Enabled: true, + URL: mockHttpServer.URL, + Timeout: 100, + MaxFileSizeKB: 1000, + MaxRules: 100, + MaxAge: 5, + Period: 1, }, } for i := 0; i < 50; i++ { - fetchConfig.Fetch.URL = fmt.Sprintf("%s?id=%d", mockHttpServer.URL, getRandomNumber()) - fectherInstance.Fetch(fetchConfig) + fetchConfig.Fetcher.URL = fmt.Sprintf("%s?id=%d", mockHttpServer.URL, getRandomNumber()) + fetcherInstance.Fetch(fetchConfig) } - assert.Never(t, func() bool { return len(fectherInstance.fetchQueue) > 10 }, time.Duration(2*time.Second), 100*time.Millisecond, "Queue Got more than one entry") - assert.Never(t, func() bool { return len(fectherInstance.fetchInprogress) > 10 }, time.Duration(2*time.Second), 100*time.Millisecond, "Map Got more than one entry") + assert.Never(t, func() bool { return len(fetcherInstance.fetchQueue) > 10 }, time.Duration(2*time.Second), 100*time.Millisecond, "Queue Got more than one entry") + assert.Never(t, func() bool { return len(fetcherInstance.fetchInProgress) > 10 }, time.Duration(2*time.Second), 100*time.Millisecond, "Map Got more than one entry") +} + +func TestFetchWhenPriceFloorsDisabled(t *testing.T) { + floorConfig := config.PriceFloors{ + Enabled: false, + Fetcher: config.PriceFloorFetcher{ + CacheSize: 1, + Worker: 5, + Capacity: 10, + }, + } + fetcherInstance := NewPriceFloorFetcher(floorConfig, http.DefaultClient, &metricsConf.NilMetricsEngine{}) + defer fetcherInstance.Stop() + + fetchConfig := config.AccountPriceFloors{ + Enabled: true, + UseDynamicData: true, + Fetcher: config.AccountFloorFetch{ + Enabled: true, + URL: "http://test.com/floors", + Timeout: 100, + MaxFileSizeKB: 1000, + MaxRules: 100, + MaxAge: 5, + Period: 1, + }, + } + + data, status := fetcherInstance.Fetch(fetchConfig) + + assert.Equal(t, (*openrtb_ext.PriceFloorRules)(nil), data, "floor data should be nil as fetcher instance does not created") + assert.Equal(t, openrtb_ext.FetchNone, status, "floor status should be none as fetcher instance does not created") } diff --git a/floors/floors.go b/floors/floors.go index ca4bedf4c51..12642d6917f 100644 --- a/floors/floors.go +++ b/floors/floors.go @@ -4,10 +4,12 @@ import ( "errors" "math" "math/rand" + "strings" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) type Price struct { @@ -33,7 +35,6 @@ const ( // EnrichWithPriceFloors checks for floors enabled in account and request and selects floors data from dynamic fetched if present // else selects floors data from req.ext.prebid.floors and update request with selected floors details func EnrichWithPriceFloors(bidRequestWrapper *openrtb_ext.RequestWrapper, account config.Account, conversions currency.Conversions, priceFloorFetcher FloorFetcher) []error { - if bidRequestWrapper == nil || bidRequestWrapper.BidRequest == nil { return []error{errors.New("Empty bidrequest")} } @@ -135,8 +136,8 @@ func isPriceFloorsEnabledForRequest(bidRequestWrapper *openrtb_ext.RequestWrappe return true } -// shouldUseFetchedData will check if to use fetched data or request data -func shouldUseFetchedData(rate *int) bool { +// useFetchedData will check if to use fetched data or request data +func useFetchedData(rate *int) bool { if rate == nil { return true } @@ -144,34 +145,34 @@ func shouldUseFetchedData(rate *int) bool { return randomNumber < *rate } -// resolveFloors does selection of floors fields from requet JSON and dynamic fetched floors JSON if dynamic fetch is enabled +// resolveFloors does selection of floors fields from request data and dynamic fetched data if dynamic fetch is enabled func resolveFloors(account config.Account, bidRequestWrapper *openrtb_ext.RequestWrapper, conversions currency.Conversions, priceFloorFetcher FloorFetcher) (*openrtb_ext.PriceFloorRules, []error) { var ( - errlist []error - floorsJson *openrtb_ext.PriceFloorRules + errList []error + floorRules *openrtb_ext.PriceFloorRules fetchResult *openrtb_ext.PriceFloorRules - fetchStatus = openrtb_ext.FetchNone + fetchStatus string ) reqFloor := extractFloorsFromRequest(bidRequestWrapper) if reqFloor != nil && reqFloor.Location != nil && len(reqFloor.Location.URL) > 0 { - account.PriceFloors.Fetch.URL = reqFloor.Location.URL + account.PriceFloors.Fetcher.URL = reqFloor.Location.URL } - account.PriceFloors.Fetch.AccountID = account.ID + account.PriceFloors.Fetcher.AccountID = account.ID - if shouldUseDynamicFetchedFloor(account) { + if priceFloorFetcher != nil && account.PriceFloors.UseDynamicData { fetchResult, fetchStatus = priceFloorFetcher.Fetch(account.PriceFloors) } - if fetchResult != nil && fetchStatus == openrtb_ext.FetchSuccess && shouldUseFetchedData(fetchResult.Data.UseFetchDataRate) { - mergedFloor := mergeFloors(reqFloor, *fetchResult, conversions) - floorsJson, errlist = createFloorsFrom(mergedFloor, account, fetchStatus, openrtb_ext.FetchLocation) + if fetchResult != nil && fetchStatus == openrtb_ext.FetchSuccess && useFetchedData(fetchResult.Data.FetchRate) { + mergedFloor := mergeFloors(reqFloor, fetchResult, conversions) + floorRules, errList = createFloorsFrom(mergedFloor, account, fetchStatus, openrtb_ext.FetchLocation) } else if reqFloor != nil { - floorsJson, errlist = createFloorsFrom(reqFloor, account, fetchStatus, openrtb_ext.RequestLocation) + floorRules, errList = createFloorsFrom(reqFloor, account, fetchStatus, openrtb_ext.RequestLocation) } else { - floorsJson, errlist = createFloorsFrom(nil, account, fetchStatus, openrtb_ext.NoDataLocation) + floorRules, errList = createFloorsFrom(nil, account, fetchStatus, openrtb_ext.NoDataLocation) } - return floorsJson, errlist + return floorRules, errList } // createFloorsFrom does preparation of floors data which shall be used for further processing @@ -214,46 +215,6 @@ func createFloorsFrom(floors *openrtb_ext.PriceFloorRules, account config.Accoun return finalFloors, floorModelErrList } -// mergeFloors does merging for floors data from request and dynamic fetch -func mergeFloors(reqFloors *openrtb_ext.PriceFloorRules, fetchFloors openrtb_ext.PriceFloorRules, conversions currency.Conversions) *openrtb_ext.PriceFloorRules { - var enforceRate int - - mergedFloors := fetchFloors - floorsEnabledByRequest := reqFloors.GetEnabled() - floorMinPrice := resolveFloorMin(reqFloors, fetchFloors, conversions) - - if reqFloors != nil && reqFloors.Enforcement != nil { - enforceRate = reqFloors.Enforcement.EnforceRate - } - - if floorsEnabledByRequest || enforceRate > 0 || floorMinPrice.FloorMin > float64(0) { - floorsEnabledByProvider := getFloorsEnabledFlag(fetchFloors) - floorsProviderEnforcement := fetchFloors.Enforcement - - if mergedFloors.Enabled == nil { - mergedFloors.Enabled = new(bool) - } - *mergedFloors.Enabled = floorsEnabledByProvider && floorsEnabledByRequest - mergedFloors.Enforcement = resolveEnforcement(floorsProviderEnforcement, enforceRate) - if reqFloors != nil && reqFloors.Enforcement != nil && reqFloors.Enforcement.EnforcePBS != nil { - enforcepbs := *reqFloors.Enforcement.EnforcePBS - mergedFloors.Enforcement.EnforcePBS = &enforcepbs - } - if floorMinPrice.FloorMin > float64(0) { - mergedFloors.FloorMin = floorMinPrice.FloorMin - mergedFloors.FloorMinCur = floorMinPrice.FloorMinCur - } - } - if reqFloors != nil && reqFloors.Location != nil && reqFloors.Location.URL != "" { - if mergedFloors.Location == nil { - mergedFloors.Location = new(openrtb_ext.PriceFloorEndpoint) - } - (*mergedFloors.Location).URL = (*reqFloors.Location).URL - } - - return &mergedFloors -} - // resolveEnforcement does retrieval of enforceRate from request func resolveEnforcement(enforcement *openrtb_ext.PriceFloorEnforcement, enforceRate int) *openrtb_ext.PriceFloorEnforcement { if enforcement == nil { @@ -271,11 +232,6 @@ func getFloorsEnabledFlag(reqFloors openrtb_ext.PriceFloorRules) bool { return true } -// resolveFloorMin gets floorMin valud from request and dynamic fetched data -func resolveFloorMin(reqFloors *openrtb_ext.PriceFloorRules, fetchFloors openrtb_ext.PriceFloorRules, conversions currency.Conversions) Price { - return resolveFloorMinOW(reqFloors, &fetchFloors, conversions) -} - // shouldUseDynamicFetchedFloor gets UseDynamicData flag from account level config func shouldUseDynamicFetchedFloor(Account config.Account) bool { return Account.PriceFloors.UseDynamicData @@ -306,3 +262,83 @@ func updateFloorsInRequest(bidRequestWrapper *openrtb_ext.RequestWrapper, priceF bidRequestWrapper.RebuildRequest() } } + +// resolveFloorMin gets floorMin value from request and dynamic fetched data +func resolveFloorMin(reqFloors *openrtb_ext.PriceFloorRules, fetchFloors *openrtb_ext.PriceFloorRules, conversions currency.Conversions) Price { + var requestFloorMinCur, providerFloorMinCur string + var requestFloorMin, providerFloorMin float64 + + if reqFloors != nil { + requestFloorMin = reqFloors.FloorMin + requestFloorMinCur = reqFloors.FloorMinCur + if len(requestFloorMinCur) == 0 && reqFloors.Data != nil { + requestFloorMinCur = reqFloors.Data.Currency + } + } + + if fetchFloors != nil { + providerFloorMin = fetchFloors.FloorMin + providerFloorMinCur = fetchFloors.FloorMinCur + if len(providerFloorMinCur) == 0 && fetchFloors.Data != nil { + providerFloorMinCur = fetchFloors.Data.Currency + } + } + + if len(requestFloorMinCur) > 0 { + if requestFloorMin > 0 { + return Price{FloorMin: requestFloorMin, FloorMinCur: requestFloorMinCur} + } + + if providerFloorMin > 0 { + if strings.Compare(providerFloorMinCur, requestFloorMinCur) == 0 || len(providerFloorMinCur) == 0 { + return Price{FloorMin: providerFloorMin, FloorMinCur: requestFloorMinCur} + } + rate, err := conversions.GetRate(providerFloorMinCur, requestFloorMinCur) + if err != nil { + return Price{FloorMin: 0, FloorMinCur: requestFloorMinCur} + } + return Price{FloorMin: roundToFourDecimals(rate * providerFloorMin), FloorMinCur: requestFloorMinCur} + } + } + + if len(providerFloorMinCur) > 0 { + if providerFloorMin > 0 { + return Price{FloorMin: providerFloorMin, FloorMinCur: providerFloorMinCur} + } + if requestFloorMin > 0 { + return Price{FloorMin: requestFloorMin, FloorMinCur: providerFloorMinCur} + } + } + + return Price{FloorMin: requestFloorMin, FloorMinCur: requestFloorMinCur} + +} + +// mergeFloors does merging for floors data from request and dynamic fetch +func mergeFloors(reqFloors *openrtb_ext.PriceFloorRules, fetchFloors *openrtb_ext.PriceFloorRules, conversions currency.Conversions) *openrtb_ext.PriceFloorRules { + mergedFloors := fetchFloors.DeepCopy() + if mergedFloors.Enabled == nil { + mergedFloors.Enabled = new(bool) + } + *mergedFloors.Enabled = fetchFloors.GetEnabled() && reqFloors.GetEnabled() + + if reqFloors == nil { + return mergedFloors + } + + if reqFloors.Enforcement != nil { + mergedFloors.Enforcement = reqFloors.Enforcement.DeepCopy() + } + + floorMinPrice := resolveFloorMin(reqFloors, fetchFloors, conversions) + if floorMinPrice.FloorMin > 0 { + mergedFloors.FloorMin = floorMinPrice.FloorMin + mergedFloors.FloorMinCur = floorMinPrice.FloorMinCur + } + + if reqFloors != nil && reqFloors.Location != nil && reqFloors.Location.URL != "" { + mergedFloors.Location = ptrutil.Clone(reqFloors.Location) + } + + return mergedFloors +} diff --git a/floors/floors_ow.go b/floors/floors_ow.go index d406c84e9c3..58894999623 100644 --- a/floors/floors_ow.go +++ b/floors/floors_ow.go @@ -3,9 +3,9 @@ package floors import ( "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func RequestHasFloors(bidRequest *openrtb2.BidRequest) bool { diff --git a/floors/floors_ow_test.go b/floors/floors_ow_test.go index b828da6c668..0474e11dfeb 100644 --- a/floors/floors_ow_test.go +++ b/floors/floors_ow_test.go @@ -3,7 +3,7 @@ package floors import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" ) func TestRequestHasFloors(t *testing.T) { diff --git a/floors/floors_test.go b/floors/floors_test.go index e122ffd0ee3..f065d2ac84e 100644 --- a/floors/floors_test.go +++ b/floors/floors_test.go @@ -6,11 +6,12 @@ import ( "reflect" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -51,6 +52,14 @@ func getCurrencyRates(rates map[string]map[string]float64) currency.Conversions return currency.NewRates(rates) } +type mockPriceFloorFetcher struct{} + +func (mpf *mockPriceFloorFetcher) Fetch(configs config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) { + return nil, openrtb_ext.FetchNone +} + +func (mpf *mockPriceFloorFetcher) Stop() {} + func TestEnrichWithPriceFloors(t *testing.T) { rates := map[string]map[string]float64{ "USD": { @@ -366,7 +375,7 @@ func TestEnrichWithPriceFloors(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - ErrList := EnrichWithPriceFloors(tc.bidRequestWrapper, tc.account, getCurrencyRates(rates), &PriceFloorFetcher{}) + ErrList := EnrichWithPriceFloors(tc.bidRequestWrapper, tc.account, getCurrencyRates(rates), &mockPriceFloorFetcher{}) if tc.bidRequestWrapper != nil { assert.Equal(t, tc.bidRequestWrapper.Imp[0].BidFloor, tc.expFloorVal, tc.name) assert.Equal(t, tc.bidRequestWrapper.Imp[0].BidFloorCur, tc.expFloorCur, tc.name) @@ -389,177 +398,22 @@ func TestEnrichWithPriceFloors(t *testing.T) { } } -func TestResolveFloorMin(t *testing.T) { - rates := map[string]map[string]float64{ - "USD": { - "INR": 70, - "EUR": 0.9, - "JPY": 5.09, - }, - } - - tt := []struct { - name string - reqFloors openrtb_ext.PriceFloorRules - fetchFloors openrtb_ext.PriceFloorRules - conversions currency.Conversions - expPrice Price - }{ - { - name: "FloorsMin present in request Floors only", - reqFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 10, - FloorMinCur: "JPY", - }, - fetchFloors: openrtb_ext.PriceFloorRules{}, - expPrice: Price{FloorMin: 10, FloorMinCur: "JPY"}, - }, - { - name: "FloorsMin present in request Floors and data currency present", - reqFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 10, - FloorMinCur: "JPY", - Data: &openrtb_ext.PriceFloorData{ - Currency: "JPY", - }, - }, - fetchFloors: openrtb_ext.PriceFloorRules{}, - expPrice: Price{FloorMin: 10, FloorMinCur: "JPY"}, - }, - { - name: "FloorsMin present in request Floors and fetched floors", - reqFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 10, - FloorMinCur: "USD", - }, - fetchFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 15, - FloorMinCur: "USD", - }, - expPrice: Price{FloorMin: 10, FloorMinCur: "USD"}, - }, - { - name: "FloorsMin present fetched floors only", - reqFloors: openrtb_ext.PriceFloorRules{}, - fetchFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 15, - FloorMinCur: "EUR", - }, - expPrice: Price{FloorMin: 15, FloorMinCur: "EUR"}, - }, - { - name: "FloorMinCur present in reqFloors And FloorsMin, FloorMinCur present fetched floors (Same Currency)", - reqFloors: openrtb_ext.PriceFloorRules{ - FloorMinCur: "EUR", - }, - fetchFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 15, - FloorMinCur: "EUR", - }, - expPrice: Price{FloorMin: 15, FloorMinCur: "EUR"}, - }, - { - name: "FloorMinCur present in reqFloors And FloorsMin, FloorMinCur present fetched floors (Different Currency)", - reqFloors: openrtb_ext.PriceFloorRules{ - FloorMinCur: "USD", - }, - fetchFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 15, - FloorMinCur: "EUR", - }, - expPrice: Price{FloorMin: 16.6667, FloorMinCur: "USD"}, - }, - { - name: "FloorMin present in reqFloors And FloorMinCur present fetched floors", - reqFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 11, - }, - fetchFloors: openrtb_ext.PriceFloorRules{ - FloorMinCur: "EUR", - }, - expPrice: Price{FloorMin: 11, FloorMinCur: "EUR"}, - }, - { - name: "FloorMinCur present in reqFloors And FloorMin present fetched floors", - reqFloors: openrtb_ext.PriceFloorRules{ - FloorMinCur: "INR", - }, - fetchFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 12, - }, - expPrice: Price{FloorMin: 12, FloorMinCur: "INR"}, - }, - { - name: "FloorMinCur present in reqFloors And FloorMin present fetched floors", - reqFloors: openrtb_ext.PriceFloorRules{ - FloorMinCur: "INR", - }, - fetchFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 1, - Data: &openrtb_ext.PriceFloorData{Currency: "USD"}, - }, - expPrice: Price{FloorMin: 70, FloorMinCur: "INR"}, - }, - { - name: "FloorMinCur present in fetched Floors And FloorMin present reqFloors", - reqFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 2, - }, - fetchFloors: openrtb_ext.PriceFloorRules{ - Data: &openrtb_ext.PriceFloorData{Currency: "USD"}, - }, - expPrice: Price{FloorMin: 2, FloorMinCur: "USD"}, - }, - { - name: "FloorMinCur and FloorMin present in fetched floors", - reqFloors: openrtb_ext.PriceFloorRules{}, - fetchFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 12, - Data: &openrtb_ext.PriceFloorData{Currency: "USD"}, - }, - expPrice: Price{FloorMin: 12, FloorMinCur: "USD"}, - }, - { - name: "FloorsMin, FloorCur present in request Floors", - reqFloors: openrtb_ext.PriceFloorRules{ - FloorMin: 11, - Data: &openrtb_ext.PriceFloorData{ - Currency: "EUR", - }, - }, - fetchFloors: openrtb_ext.PriceFloorRules{}, - expPrice: Price{FloorMin: 11, FloorMinCur: "EUR"}, - }, - { - name: "Empty reqFloors And Empty fetched floors", - reqFloors: openrtb_ext.PriceFloorRules{}, - fetchFloors: openrtb_ext.PriceFloorRules{}, - expPrice: Price{FloorMin: 0.0, FloorMinCur: ""}, - }, - } - for _, tc := range tt { - t.Run(tc.name, func(t *testing.T) { - price := resolveFloorMin(&tc.reqFloors, tc.fetchFloors, getCurrencyRates(rates)) - if !reflect.DeepEqual(price.FloorMin, tc.expPrice.FloorMin) { - t.Errorf("Floor Value error: \nreturn:\t%v\nwant:\t%v", price.FloorMin, tc.expPrice.FloorMin) - } - if !reflect.DeepEqual(price.FloorMinCur, tc.expPrice.FloorMinCur) { - t.Errorf("Floor Currency error: \nreturn:\t%v\nwant:\t%v", price.FloorMinCur, tc.expPrice.FloorMinCur) - } - - }) - } -} - func getTrue() *bool { trueFlag := true return &trueFlag } +func getFalse() *bool { + falseFlag := false + return &falseFlag +} + type MockFetch struct { FakeFetch func(configs config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) } +func (m *MockFetch) Stop() {} + func (m *MockFetch) Fetch(configs config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) { if !configs.UseDynamicData { @@ -577,14 +431,15 @@ func (m *MockFetch) Fetch(configs config.AccountPriceFloors) (*openrtb_ext.Price Currency: "USD", ModelGroups: []openrtb_ext.PriceFloorModelGroup{ { - ModelVersion: "model from fetched", + ModelVersion: "Version 101", Currency: "USD", Values: map[string]float64{ "banner|300x600|www.website5.com": 15, "*|*|*": 25, }, Schema: openrtb_ext.PriceFloorSchema{ - Fields: []string{"mediaType", "size", "domain"}, + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", }, }, }, @@ -607,6 +462,7 @@ func TestResolveFloors(t *testing.T) { bidRequestWrapper *openrtb_ext.RequestWrapper account config.Account conversions currency.Conversions + fetcher FloorFetcher expErr []error expFloors *openrtb_ext.PriceFloorRules }{ @@ -627,9 +483,9 @@ func TestResolveFloors(t *testing.T) { UseDynamicData: false, }, }, + fetcher: &MockFetch{}, expFloors: &openrtb_ext.PriceFloorRules{ Enabled: getTrue(), - FetchStatus: openrtb_ext.FetchNone, PriceFloorLocation: openrtb_ext.RequestLocation, Enforcement: &openrtb_ext.PriceFloorEnforcement{ EnforcePBS: getTrue(), @@ -671,26 +527,29 @@ func TestResolveFloors(t *testing.T) { UseDynamicData: true, }, }, + fetcher: &MockFetch{}, expFloors: &openrtb_ext.PriceFloorRules{ Enabled: getTrue(), FetchStatus: openrtb_ext.FetchSuccess, PriceFloorLocation: openrtb_ext.FetchLocation, Enforcement: &openrtb_ext.PriceFloorEnforcement{ - EnforcePBS: getTrue(), - FloorDeals: getTrue(), + EnforcePBS: getTrue(), + FloorDeals: getTrue(), + EnforceRate: 100, }, Data: &openrtb_ext.PriceFloorData{ Currency: "USD", ModelGroups: []openrtb_ext.PriceFloorModelGroup{ { - ModelVersion: "model from fetched", + ModelVersion: "Version 101", Currency: "USD", Values: map[string]float64{ "banner|300x600|www.website5.com": 15, "*|*|*": 25, }, Schema: openrtb_ext.PriceFloorSchema{ - Fields: []string{"mediaType", "size", "domain"}, + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", }, }, }, @@ -714,6 +573,7 @@ func TestResolveFloors(t *testing.T) { UseDynamicData: true, }, }, + fetcher: &MockFetch{}, expFloors: &openrtb_ext.PriceFloorRules{ Enabled: getTrue(), FloorMin: 10.11, @@ -729,14 +589,15 @@ func TestResolveFloors(t *testing.T) { Currency: "USD", ModelGroups: []openrtb_ext.PriceFloorModelGroup{ { - ModelVersion: "model from fetched", + ModelVersion: "Version 101", Currency: "USD", Values: map[string]float64{ "banner|300x600|www.website5.com": 15, "*|*|*": 25, }, Schema: openrtb_ext.PriceFloorSchema{ - Fields: []string{"mediaType", "size", "domain"}, + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", }, }, }, @@ -760,25 +621,56 @@ func TestResolveFloors(t *testing.T) { UseDynamicData: false, }, }, + fetcher: &MockFetch{}, expFloors: &openrtb_ext.PriceFloorRules{ Enforcement: &openrtb_ext.PriceFloorEnforcement{ EnforcePBS: getTrue(), EnforceRate: 100, FloorDeals: getTrue(), }, - FetchStatus: openrtb_ext.FetchNone, PriceFloorLocation: openrtb_ext.NoDataLocation, }, }, + } + + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + resolvedFloors, _ := resolveFloors(tc.account, tc.bidRequestWrapper, getCurrencyRates(rates), &MockFetch{}) + if !reflect.DeepEqual(resolvedFloors, tc.expFloors) { + t.Errorf("resolveFloors error: \nreturn:\t%v\nwant:\t%v", printFloors(resolvedFloors), printFloors(tc.expFloors)) + } + }) + } +} + +func TestResolveFloorsWithUseDataRate(t *testing.T) { + rates := map[string]map[string]float64{ + "USD": { + "INR": 70, + "EUR": 0.9, + "JPY": 5.09, + }, + } + + testCases := []struct { + name string + bidRequestWrapper *openrtb_ext.RequestWrapper + account config.Account + conversions currency.Conversions + expErr []error + expFloors *openrtb_ext.PriceFloorRules + fetcher FloorFetcher + }{ { - name: "Dynamic fetch enabled, floors from fetched selected and new URL is updated", + name: "Dynamic fetch enabled, floors from request selected as data rate 0", + fetcher: &MockFetchDataRate0{}, bidRequestWrapper: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{ Site: &openrtb2.Site{ Publisher: &openrtb2.Publisher{Domain: "www.website.com"}, }, Imp: []openrtb2.Imp{{ID: "1234", Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}}}}}, - Ext: json.RawMessage(`{"prebid":{"floors":{"floorendpoint":{"url":"http://test.com/floor"},"enabled":true}}}`), + Ext: json.RawMessage(`{"prebid":{"floors":{"data":{"currency":"USD","modelgroups":[{"modelversion":"model 1 from req","currency":"USD","values":{"banner|300x600|www.website5.com":5,"*|*|*":7},"schema":{"fields":["mediaType","size","domain"],"delimiter":"|"}}]},"enabled":true,"enforcement":{"enforcepbs":true,"floordeals":true,"enforcerate":100}}}}`), }, }, account: config.Account{ @@ -790,144 +682,85 @@ func TestResolveFloors(t *testing.T) { expFloors: &openrtb_ext.PriceFloorRules{ Enabled: getTrue(), FetchStatus: openrtb_ext.FetchSuccess, - PriceFloorLocation: openrtb_ext.FetchLocation, + PriceFloorLocation: openrtb_ext.RequestLocation, Enforcement: &openrtb_ext.PriceFloorEnforcement{ - EnforcePBS: getTrue(), - FloorDeals: getTrue(), - }, - Location: &openrtb_ext.PriceFloorEndpoint{ - URL: "http://test.com/floor", + EnforcePBS: getTrue(), + FloorDeals: getTrue(), + EnforceRate: 100, }, Data: &openrtb_ext.PriceFloorData{ Currency: "USD", ModelGroups: []openrtb_ext.PriceFloorModelGroup{ { - ModelVersion: "model from fetched", + ModelVersion: "model 1 from req", Currency: "USD", Values: map[string]float64{ - "banner|300x600|www.website5.com": 15, - "*|*|*": 25, + "banner|300x600|www.website5.com": 5, + "*|*|*": 7, }, Schema: openrtb_ext.PriceFloorSchema{ - Fields: []string{"mediaType", "size", "domain"}, + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", }, }, }, }, }, }, - } - - for _, tc := range tt { - t.Run(tc.name, func(t *testing.T) { - resolvedFloors, _ := resolveFloors(tc.account, tc.bidRequestWrapper, getCurrencyRates(rates), &MockFetch{}) - if !reflect.DeepEqual(resolvedFloors, tc.expFloors) { - t.Errorf("resolveFloors error: \nreturn:\t%v\nwant:\t%v", printFloors(resolvedFloors), printFloors(tc.expFloors)) - } - }) - } -} - -type MockFetchDataRate0 struct{} - -func (m *MockFetchDataRate0) Fetch(configs config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) { - - if !configs.UseDynamicData { - return nil, openrtb_ext.FetchNone - } - priceFloors := openrtb_ext.PriceFloorRules{ - Enabled: getTrue(), - PriceFloorLocation: openrtb_ext.RequestLocation, - Enforcement: &openrtb_ext.PriceFloorEnforcement{ - EnforcePBS: getTrue(), - EnforceRate: 100, - FloorDeals: getTrue(), - }, - Data: &openrtb_ext.PriceFloorData{ - Currency: "USD", - ModelGroups: []openrtb_ext.PriceFloorModelGroup{ - { - ModelVersion: "model from fetched", - Currency: "USD", - Values: map[string]float64{ - "banner|300x600|www.website5.com": 15, - "*|*|*": 25, - }, - Schema: openrtb_ext.PriceFloorSchema{ - Fields: []string{"mediaType", "size", "domain"}, + { + name: "Dynamic fetch enabled, floors from fetched selected as data rate is 100", + fetcher: &MockFetchDataRate100{}, + bidRequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Site: &openrtb2.Site{ + Publisher: &openrtb2.Publisher{Domain: "www.website.com"}, }, + Imp: []openrtb2.Imp{{ID: "1234", Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}}}}}, }, }, - UseFetchDataRate: ptrutil.ToPtr(0), - }, - } - return &priceFloors, openrtb_ext.FetchSuccess -} - -type MockFetchDataRate100 struct{} - -func (m *MockFetchDataRate100) Fetch(configs config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) { - - if !configs.UseDynamicData { - return nil, openrtb_ext.FetchNone - } - priceFloors := openrtb_ext.PriceFloorRules{ - Enabled: getTrue(), - PriceFloorLocation: openrtb_ext.RequestLocation, - Enforcement: &openrtb_ext.PriceFloorEnforcement{ - EnforcePBS: getTrue(), - EnforceRate: 100, - FloorDeals: getTrue(), - }, - Data: &openrtb_ext.PriceFloorData{ - Currency: "USD", - ModelGroups: []openrtb_ext.PriceFloorModelGroup{ - { - ModelVersion: "model from fetched", - Currency: "USD", - Values: map[string]float64{ - "banner|300x600|www.website5.com": 15, - "*|*|*": 25, - }, - Schema: openrtb_ext.PriceFloorSchema{ - Fields: []string{"mediaType", "size", "domain"}, + account: config.Account{ + PriceFloors: config.AccountPriceFloors{ + Enabled: true, + UseDynamicData: true, + }, + }, + expFloors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + FetchStatus: openrtb_ext.FetchSuccess, + PriceFloorLocation: openrtb_ext.FetchLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: getTrue(), + FloorDeals: getTrue(), + EnforceRate: 100, + }, + Data: &openrtb_ext.PriceFloorData{ + Currency: "USD", + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "model from fetched", + Currency: "USD", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 15, + "*|*|*": 25, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + }, + }, }, + FetchRate: ptrutil.ToPtr(100), }, }, - UseFetchDataRate: ptrutil.ToPtr(100), - }, - } - return &priceFloors, openrtb_ext.FetchSuccess -} - -func TestResolveFloorsWithUseDataRate(t *testing.T) { - rates := map[string]map[string]float64{ - "USD": { - "INR": 70, - "EUR": 0.9, - "JPY": 5.09, }, - } - - testCases := []struct { - name string - bidRequestWrapper *openrtb_ext.RequestWrapper - account config.Account - conversions currency.Conversions - expErr []error - expFloors *openrtb_ext.PriceFloorRules - fetcher FloorFetcher - }{ { - name: "Dynamic fetch enabled, floors from request selected as data rate 0", - fetcher: &MockFetchDataRate0{}, + name: "Dynamic fetch enabled, floors from fetched selected and new URL is updated", bidRequestWrapper: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{ Site: &openrtb2.Site{ Publisher: &openrtb2.Publisher{Domain: "www.website.com"}, }, Imp: []openrtb2.Imp{{ID: "1234", Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}}}}}, - Ext: json.RawMessage(`{"prebid":{"floors":{"data":{"currency":"USD","modelgroups":[{"modelversion":"model 1 from req","currency":"USD","values":{"banner|300x600|www.website5.com":5,"*|*|*":7},"schema":{"fields":["mediaType","size","domain"],"delimiter":"|"}}]},"enabled":true,"enforcement":{"enforcepbs":true,"floordeals":true,"enforcerate":100}}}}`), + Ext: json.RawMessage(`{"prebid":{"floors":{"floorendpoint":{"url":"http://test.com/floor"},"enabled":true}}}`), }, }, account: config.Account{ @@ -936,24 +769,28 @@ func TestResolveFloorsWithUseDataRate(t *testing.T) { UseDynamicData: true, }, }, + fetcher: &MockFetch{}, expFloors: &openrtb_ext.PriceFloorRules{ Enabled: getTrue(), FetchStatus: openrtb_ext.FetchSuccess, - PriceFloorLocation: openrtb_ext.RequestLocation, + PriceFloorLocation: openrtb_ext.FetchLocation, Enforcement: &openrtb_ext.PriceFloorEnforcement{ EnforcePBS: getTrue(), FloorDeals: getTrue(), EnforceRate: 100, }, + Location: &openrtb_ext.PriceFloorEndpoint{ + URL: "http://test.com/floor", + }, Data: &openrtb_ext.PriceFloorData{ Currency: "USD", ModelGroups: []openrtb_ext.PriceFloorModelGroup{ { - ModelVersion: "model 1 from req", + ModelVersion: "Version 101", Currency: "USD", Values: map[string]float64{ - "banner|300x600|www.website5.com": 5, - "*|*|*": 7, + "banner|300x600|www.website5.com": 15, + "*|*|*": 25, }, Schema: openrtb_ext.PriceFloorSchema{ Fields: []string{"mediaType", "size", "domain"}, @@ -965,14 +802,14 @@ func TestResolveFloorsWithUseDataRate(t *testing.T) { }, }, { - name: "Dynamic fetch enabled, floors from fetched selected as data rate is 100", - fetcher: &MockFetchDataRate100{}, + name: "Dynamic Fetch Enabled but price floor fetcher is nil, floors from request is selected", bidRequestWrapper: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{ Site: &openrtb2.Site{ Publisher: &openrtb2.Publisher{Domain: "www.website.com"}, }, Imp: []openrtb2.Imp{{ID: "1234", Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}}}}}, + Ext: json.RawMessage(`{"prebid":{"floors":{"data":{"currency":"USD","modelgroups":[{"modelversion":"model 1 from req","currency":"USD","values":{"banner|300x600|www.website5.com":5,"*|*|*":7},"schema":{"fields":["mediaType","size","domain"],"delimiter":"|"}}]},"enabled":true,"enforcement":{"enforcepbs":true,"floordeals":true,"enforcerate":100}}}}`), }, }, account: config.Account{ @@ -981,33 +818,56 @@ func TestResolveFloorsWithUseDataRate(t *testing.T) { UseDynamicData: true, }, }, + fetcher: nil, expFloors: &openrtb_ext.PriceFloorRules{ Enabled: getTrue(), - FetchStatus: openrtb_ext.FetchSuccess, - PriceFloorLocation: openrtb_ext.FetchLocation, + PriceFloorLocation: openrtb_ext.RequestLocation, Enforcement: &openrtb_ext.PriceFloorEnforcement{ - EnforcePBS: getTrue(), - FloorDeals: getTrue(), + EnforcePBS: getTrue(), + EnforceRate: 100, + FloorDeals: getTrue(), }, Data: &openrtb_ext.PriceFloorData{ Currency: "USD", ModelGroups: []openrtb_ext.PriceFloorModelGroup{ { - ModelVersion: "model from fetched", + ModelVersion: "model 1 from req", Currency: "USD", Values: map[string]float64{ - "banner|300x600|www.website5.com": 15, - "*|*|*": 25, + "banner|300x600|www.website5.com": 5, + "*|*|*": 7, }, Schema: openrtb_ext.PriceFloorSchema{ - Fields: []string{"mediaType", "size", "domain"}, + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", }, }, }, - UseFetchDataRate: ptrutil.ToPtr(100), }, }, }, + { + name: "Dynamic Fetch Enabled but price floor fetcher is nil and request has no floors", + bidRequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Site: &openrtb2.Site{ + Publisher: &openrtb2.Publisher{Domain: "www.website.com"}, + }, + Imp: []openrtb2.Imp{{ID: "1234", Banner: &openrtb2.Banner{Format: []openrtb2.Format{{W: 300, H: 250}}}}}, + Ext: json.RawMessage(`{"prebid":{}}`), + }, + }, + account: config.Account{ + PriceFloors: config.AccountPriceFloors{ + Enabled: true, + UseDynamicData: true, + }, + }, + fetcher: nil, + expFloors: &openrtb_ext.PriceFloorRules{ + PriceFloorLocation: openrtb_ext.NoDataLocation, + }, + }, } for _, tc := range testCases { @@ -1018,294 +878,1015 @@ func TestResolveFloorsWithUseDataRate(t *testing.T) { } } -func printFloors(floors *openrtb_ext.PriceFloorRules) string { - fbytes, _ := json.Marshal(floors) - return string(fbytes) +type MockFetchDataRate0 struct{} + +func (m *MockFetchDataRate0) Fetch(configs config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) { + + if !configs.UseDynamicData { + return nil, openrtb_ext.FetchNone + } + priceFloors := openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + PriceFloorLocation: openrtb_ext.RequestLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: getTrue(), + EnforceRate: 100, + FloorDeals: getTrue(), + }, + Data: &openrtb_ext.PriceFloorData{ + Currency: "USD", + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "model from fetched", + Currency: "USD", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 15, + "*|*|*": 25, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + }, + }, + }, + FetchRate: ptrutil.ToPtr(0), + }, + } + return &priceFloors, openrtb_ext.FetchSuccess } -func TestCreateFloorsFrom(t *testing.T) { +func (m *MockFetchDataRate0) Stop() { - testAccountConfig := config.Account{ - PriceFloors: config.AccountPriceFloors{ - Enabled: true, - UseDynamicData: false, - MaxRule: 100, - MaxSchemaDims: 5, +} + +type MockFetchDataRate100 struct{} + +func (m *MockFetchDataRate100) Fetch(configs config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) { + + if !configs.UseDynamicData { + return nil, openrtb_ext.FetchNone + } + priceFloors := openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + PriceFloorLocation: openrtb_ext.RequestLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: getTrue(), + EnforceRate: 100, + FloorDeals: getTrue(), + }, + Data: &openrtb_ext.PriceFloorData{ + Currency: "USD", + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "model from fetched", + Currency: "USD", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 15, + "*|*|*": 25, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + }, + }, + }, + FetchRate: ptrutil.ToPtr(100), }, } + return &priceFloors, openrtb_ext.FetchSuccess +} - type args struct { - floors *openrtb_ext.PriceFloorRules - account config.Account - fetchStatus string - floorLocation string +func (m *MockFetchDataRate100) Stop() { + +} + +type MockFetchDataRateNotProvided struct{} + +func (m *MockFetchDataRateNotProvided) Fetch(configs config.AccountPriceFloors) (*openrtb_ext.PriceFloorRules, string) { + + if !configs.UseDynamicData { + return nil, openrtb_ext.FetchNone } - testCases := []struct { - name string - args args - want *openrtb_ext.PriceFloorRules - want1 []error - }{ + priceFloors := openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + PriceFloorLocation: openrtb_ext.RequestLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: getTrue(), + EnforceRate: 100, + FloorDeals: getTrue(), + }, + Data: &openrtb_ext.PriceFloorData{ + Currency: "USD", + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "model from fetched", + Currency: "USD", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 5, + "*|*|*": 15, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + }, + }, + }, + }, + } + return &priceFloors, openrtb_ext.FetchSuccess +} + +func (m *MockFetchDataRateNotProvided) Stop() { + +} + +func printFloors(floors *openrtb_ext.PriceFloorRules) string { + fbytes, _ := jsonutil.Marshal(floors) + return string(fbytes) +} + +func TestCreateFloorsFrom(t *testing.T) { + + testAccountConfig := config.Account{ + PriceFloors: config.AccountPriceFloors{ + Enabled: true, + UseDynamicData: false, + MaxRule: 100, + MaxSchemaDims: 5, + }, + } + + type args struct { + floors *openrtb_ext.PriceFloorRules + account config.Account + fetchStatus string + floorLocation string + } + testCases := []struct { + name string + args args + want *openrtb_ext.PriceFloorRules + want1 []error + }{ + { + name: "floor provider should be selected from floor json", + args: args{ + account: testAccountConfig, + floors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + FloorMin: 10.11, + FloorMinCur: "EUR", + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: getTrue(), + EnforceRate: 100, + FloorDeals: getTrue(), + }, + Data: &openrtb_ext.PriceFloorData{ + Currency: "USD", + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "model from fetched", + Currency: "USD", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 15, + "*|*|*": 25, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + }, + }, + }, + FloorProvider: "PM", + }, + }, + fetchStatus: openrtb_ext.FetchSuccess, + floorLocation: openrtb_ext.FetchLocation, + }, + want: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + FloorMin: 10.11, + FloorMinCur: "EUR", + FetchStatus: openrtb_ext.FetchSuccess, + PriceFloorLocation: openrtb_ext.FetchLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: getTrue(), + EnforceRate: 100, + FloorDeals: getTrue(), + }, + Data: &openrtb_ext.PriceFloorData{ + Currency: "USD", + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "model from fetched", + Currency: "USD", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 15, + "*|*|*": 25, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + }, + }, + }, + FloorProvider: "PM", + }, + }, + }, + { + name: "floor provider will be empty if no value provided in floor json", + args: args{ + account: testAccountConfig, + floors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + FloorMin: 10.11, + FloorMinCur: "EUR", + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: getTrue(), + EnforceRate: 100, + FloorDeals: getTrue(), + }, + Data: &openrtb_ext.PriceFloorData{ + Currency: "USD", + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "model from request", + Currency: "USD", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 15, + "*|*|*": 25, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + }, + }, + }, + FloorProvider: "", + }, + }, + fetchStatus: openrtb_ext.FetchSuccess, + floorLocation: openrtb_ext.FetchLocation, + }, + want: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + FloorMin: 10.11, + FloorMinCur: "EUR", + FetchStatus: openrtb_ext.FetchSuccess, + PriceFloorLocation: openrtb_ext.FetchLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: getTrue(), + EnforceRate: 100, + FloorDeals: getTrue(), + }, + Data: &openrtb_ext.PriceFloorData{ + Currency: "USD", + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "model from request", + Currency: "USD", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 15, + "*|*|*": 25, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + }, + }, + }, + FloorProvider: "", + }, + }, + }, + { + name: "only floor enforcement object present", + args: args{ + account: testAccountConfig, + floors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: getTrue(), + EnforceRate: 100, + FloorDeals: getTrue(), + }, + }, + fetchStatus: openrtb_ext.FetchNone, + floorLocation: openrtb_ext.RequestLocation, + }, + want: &openrtb_ext.PriceFloorRules{ + FetchStatus: openrtb_ext.FetchNone, + PriceFloorLocation: openrtb_ext.NoDataLocation, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforcePBS: getTrue(), + EnforceRate: 100, + FloorDeals: getTrue(), + }, + }, + }, + { + name: "Invalid modelGroup with skipRate = 110", + args: args{ + account: testAccountConfig, + floors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + Data: &openrtb_ext.PriceFloorData{ + Currency: "USD", + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "model from fetched", + Currency: "USD", + SkipRate: 110, + Values: map[string]float64{ + "banner|300x600|www.website5.com": 15, + "*|*|*": 25, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + }, + }, + }, + }, + }, + fetchStatus: openrtb_ext.FetchNone, + floorLocation: openrtb_ext.RequestLocation, + }, + want: &openrtb_ext.PriceFloorRules{ + FetchStatus: openrtb_ext.FetchNone, + PriceFloorLocation: openrtb_ext.RequestLocation, + }, + want1: []error{ + errors.New("Invalid Floor Model = 'model from fetched' due to SkipRate = '110' is out of range (1-100)"), + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + got, got1 := createFloorsFrom(tc.args.floors, tc.args.account, tc.args.fetchStatus, tc.args.floorLocation) + assert.Equal(t, got1, tc.want1, tc.name) + assert.Equal(t, got, tc.want, tc.name) + }) + } +} + +func TestIsPriceFloorsEnabled(t *testing.T) { + type args struct { + account config.Account + bidRequestWrapper *openrtb_ext.RequestWrapper + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "Disabled in account and req", + args: args{ + account: config.Account{PriceFloors: config.AccountPriceFloors{Enabled: false}}, + bidRequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Ext: json.RawMessage(`{"prebid":{"floors":{"enabled": false} }}`), + }, + }, + }, + want: false, + }, + { + name: "Enabled in account and req", + args: args{ + account: config.Account{PriceFloors: config.AccountPriceFloors{Enabled: true}}, + bidRequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Ext: json.RawMessage(`{"prebid":{"floors":{"enabled": true} }}`), + }, + }, + }, + want: true, + }, + { + name: "disabled in account and enabled req", + args: args{ + account: config.Account{PriceFloors: config.AccountPriceFloors{Enabled: false}}, + bidRequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Ext: json.RawMessage(`{"prebid":{"floors":{"enabled": true} }}`), + }, + }, + }, + want: false, + }, + { + name: "Enabled in account and disabled in req", + args: args{ + account: config.Account{PriceFloors: config.AccountPriceFloors{Enabled: true}}, + bidRequestWrapper: &openrtb_ext.RequestWrapper{ + BidRequest: &openrtb2.BidRequest{ + Ext: json.RawMessage(`{"prebid":{"floors":{"enabled": false} }}`)}, + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := isPriceFloorsEnabled(tt.args.account, tt.args.bidRequestWrapper) + assert.Equal(t, got, tt.want, tt.name) + }) + } +} + +func TestResolveFloorMin(t *testing.T) { + rates := map[string]map[string]float64{ + "USD": { + "INR": 70, + "EUR": 0.9, + "JPY": 5.09, + }, + } + + tt := []struct { + name string + reqFloors openrtb_ext.PriceFloorRules + fetchFloors openrtb_ext.PriceFloorRules + conversions currency.Conversions + expPrice Price + }{ + { + name: "FloorsMin present in request Floors only", + reqFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 10, + FloorMinCur: "JPY", + }, + fetchFloors: openrtb_ext.PriceFloorRules{}, + expPrice: Price{FloorMin: 10, FloorMinCur: "JPY"}, + }, + { + name: "FloorsMin, FloorMinCur and data currency present in request Floors", + reqFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 10, + FloorMinCur: "JPY", + Data: &openrtb_ext.PriceFloorData{ + Currency: "USD", + }, + }, + fetchFloors: openrtb_ext.PriceFloorRules{}, + expPrice: Price{FloorMin: 10, FloorMinCur: "JPY"}, + }, + { + name: "FloorsMin and data currency present in request Floors", + reqFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 10, + Data: &openrtb_ext.PriceFloorData{ + Currency: "USD", + }, + }, + fetchFloors: openrtb_ext.PriceFloorRules{}, + expPrice: Price{FloorMin: 10, FloorMinCur: "USD"}, + }, + { + name: "FloorsMin and FloorMinCur present in request Floors and fetched floors", + reqFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 10, + FloorMinCur: "USD", + }, + fetchFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 15, + FloorMinCur: "INR", + }, + expPrice: Price{FloorMin: 10, FloorMinCur: "USD"}, + }, + { + name: "FloorsMin present fetched floors only", + reqFloors: openrtb_ext.PriceFloorRules{}, + fetchFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 15, + FloorMinCur: "EUR", + }, + expPrice: Price{FloorMin: 15, FloorMinCur: "EUR"}, + }, + { + name: "FloorMinCur present in reqFloors And FloorsMin, FloorMinCur present in fetched floors (Same Currency)", + reqFloors: openrtb_ext.PriceFloorRules{ + FloorMinCur: "EUR", + }, + fetchFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 15, + FloorMinCur: "EUR", + }, + expPrice: Price{FloorMin: 15, FloorMinCur: "EUR"}, + }, + { + name: "FloorMinCur present in reqFloors And FloorsMin, FloorMinCur present in fetched floors (Different Currency)", + reqFloors: openrtb_ext.PriceFloorRules{ + FloorMinCur: "USD", + }, + fetchFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 15, + FloorMinCur: "EUR", + }, + expPrice: Price{FloorMin: 16.6667, FloorMinCur: "USD"}, + }, + { + name: "FloorMin present in reqFloors And FloorMinCur present in fetched floors", + reqFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 11, + }, + fetchFloors: openrtb_ext.PriceFloorRules{ + FloorMinCur: "EUR", + }, + expPrice: Price{FloorMin: 11, FloorMinCur: "EUR"}, + }, + { + name: "FloorMinCur present in reqFloors And FloorMin present in fetched floors", + reqFloors: openrtb_ext.PriceFloorRules{ + FloorMinCur: "INR", + }, + fetchFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 12, + }, + expPrice: Price{FloorMin: 12, FloorMinCur: "INR"}, + }, + { + name: "FloorMinCur present in reqFloors And FloorMin, data currency present in fetched floors", + reqFloors: openrtb_ext.PriceFloorRules{ + FloorMinCur: "INR", + }, + fetchFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 1, + Data: &openrtb_ext.PriceFloorData{Currency: "USD"}, + }, + expPrice: Price{FloorMin: 70, FloorMinCur: "INR"}, + }, + { + name: "FloorMinCur present in fetched Floors And data currency present in reqFloors", + reqFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 2, + }, + fetchFloors: openrtb_ext.PriceFloorRules{ + Data: &openrtb_ext.PriceFloorData{Currency: "USD"}, + }, + expPrice: Price{FloorMin: 2, FloorMinCur: "USD"}, + }, + { + name: "Data currency and FloorMin present in fetched floors", + reqFloors: openrtb_ext.PriceFloorRules{}, + fetchFloors: openrtb_ext.PriceFloorRules{ + FloorMin: 12, + Data: &openrtb_ext.PriceFloorData{Currency: "USD"}, + }, + expPrice: Price{FloorMin: 12, FloorMinCur: "USD"}, + }, + { + name: "Empty reqFloors And Empty fetched floors", + reqFloors: openrtb_ext.PriceFloorRules{}, + fetchFloors: openrtb_ext.PriceFloorRules{}, + expPrice: Price{FloorMin: 0.0, FloorMinCur: ""}, + }, + } + for _, tc := range tt { + t.Run(tc.name, func(t *testing.T) { + price := resolveFloorMin(&tc.reqFloors, &tc.fetchFloors, getCurrencyRates(rates)) + if !reflect.DeepEqual(price.FloorMin, tc.expPrice.FloorMin) { + t.Errorf("Floor Value error: \nreturn:\t%v\nwant:\t%v", price.FloorMin, tc.expPrice.FloorMin) + } + if !reflect.DeepEqual(price.FloorMinCur, tc.expPrice.FloorMinCur) { + t.Errorf("Floor Currency error: \nreturn:\t%v\nwant:\t%v", price.FloorMinCur, tc.expPrice.FloorMinCur) + } + + }) + } +} + +func TestMergeFloors(t *testing.T) { + + rates := map[string]map[string]float64{ + "USD": { + "INR": 70, + "EUR": 0.9, + "JPY": 5.09, + }, + } + + type args struct { + reqFloors *openrtb_ext.PriceFloorRules + fetchFloors *openrtb_ext.PriceFloorRules + } + tests := []struct { + name string + args args + want *openrtb_ext.PriceFloorRules + }{ + { + name: "Fetched Floors are present and request Floors are empty", + args: args{ + reqFloors: nil, + fetchFloors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + Data: &openrtb_ext.PriceFloorData{ + Currency: "INR", + SkipRate: 0, + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "Version 1", + Currency: "INR", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", + }, + }, + }, + }, + }, + }, + want: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + Data: &openrtb_ext.PriceFloorData{ + Currency: "INR", + SkipRate: 0, + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "Version 1", + Currency: "INR", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", + }, + }, + }, + }, + }, + }, + { + name: "Fetched Floors are present and request Floors has floors disabled", + args: args{ + reqFloors: &openrtb_ext.PriceFloorRules{ + Enabled: getFalse(), + }, + fetchFloors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + Data: &openrtb_ext.PriceFloorData{ + Currency: "INR", + SkipRate: 0, + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "Version 1", + Currency: "INR", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", + }, + }, + }, + }, + }, + }, + want: &openrtb_ext.PriceFloorRules{ + Enabled: getFalse(), + Data: &openrtb_ext.PriceFloorData{ + Currency: "INR", + SkipRate: 0, + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "Version 1", + Currency: "INR", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", + }, + }, + }, + }, + }, + }, { - name: "floor provider should be selected from floor json", + name: "Fetched Floors are present and request Floors has enforcement (enforcepbs = true)", args: args{ - account: testAccountConfig, - floors: &openrtb_ext.PriceFloorRules{ - Enabled: getTrue(), - FloorMin: 10.11, - FloorMinCur: "EUR", + reqFloors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforceRate: 50, EnforcePBS: getTrue(), - EnforceRate: 100, - FloorDeals: getTrue(), }, + }, + fetchFloors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), Data: &openrtb_ext.PriceFloorData{ - Currency: "USD", + Currency: "INR", + SkipRate: 0, ModelGroups: []openrtb_ext.PriceFloorModelGroup{ { - ModelVersion: "model from fetched", - Currency: "USD", + ModelVersion: "Version 1", + Currency: "INR", Values: map[string]float64{ - "banner|300x600|www.website5.com": 15, - "*|*|*": 25, + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, }, Schema: openrtb_ext.PriceFloorSchema{ - Fields: []string{"mediaType", "size", "domain"}, + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", }, }, }, - FloorProvider: "PM", }, }, - fetchStatus: openrtb_ext.FetchSuccess, - floorLocation: openrtb_ext.FetchLocation, }, want: &openrtb_ext.PriceFloorRules{ - Enabled: getTrue(), - FloorMin: 10.11, - FloorMinCur: "EUR", - FetchStatus: openrtb_ext.FetchSuccess, - PriceFloorLocation: openrtb_ext.FetchLocation, - Enforcement: &openrtb_ext.PriceFloorEnforcement{ - EnforcePBS: getTrue(), - EnforceRate: 100, - FloorDeals: getTrue(), - }, + Enabled: getTrue(), Data: &openrtb_ext.PriceFloorData{ - Currency: "USD", + Currency: "INR", + SkipRate: 0, ModelGroups: []openrtb_ext.PriceFloorModelGroup{ { - ModelVersion: "model from fetched", - Currency: "USD", + ModelVersion: "Version 1", + Currency: "INR", Values: map[string]float64{ - "banner|300x600|www.website5.com": 15, - "*|*|*": 25, + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, }, Schema: openrtb_ext.PriceFloorSchema{ - Fields: []string{"mediaType", "size", "domain"}, + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", }, }, }, - FloorProvider: "PM", + }, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforceRate: 50, + EnforcePBS: getTrue(), }, }, }, { - name: "floor provider will be empty if no value provided in floor json", + name: "Fetched Floors are present and request Floors has enforcement (enforcepbs = false)", args: args{ - account: testAccountConfig, - floors: &openrtb_ext.PriceFloorRules{ - Enabled: getTrue(), - FloorMin: 10.11, - FloorMinCur: "EUR", + reqFloors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), Enforcement: &openrtb_ext.PriceFloorEnforcement{ - EnforcePBS: getTrue(), - EnforceRate: 100, - FloorDeals: getTrue(), + EnforceRate: 50, + EnforcePBS: getFalse(), }, + }, + fetchFloors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), Data: &openrtb_ext.PriceFloorData{ - Currency: "USD", + Currency: "INR", + SkipRate: 0, ModelGroups: []openrtb_ext.PriceFloorModelGroup{ { - ModelVersion: "model from request", - Currency: "USD", + ModelVersion: "Version 1", + Currency: "INR", Values: map[string]float64{ - "banner|300x600|www.website5.com": 15, - "*|*|*": 25, + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, }, Schema: openrtb_ext.PriceFloorSchema{ - Fields: []string{"mediaType", "size", "domain"}, + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", }, }, }, - FloorProvider: "", }, }, - fetchStatus: openrtb_ext.FetchSuccess, - floorLocation: openrtb_ext.FetchLocation, }, want: &openrtb_ext.PriceFloorRules{ - Enabled: getTrue(), - FloorMin: 10.11, - FloorMinCur: "EUR", - FetchStatus: openrtb_ext.FetchSuccess, - PriceFloorLocation: openrtb_ext.FetchLocation, - Enforcement: &openrtb_ext.PriceFloorEnforcement{ - EnforcePBS: getTrue(), - EnforceRate: 100, - FloorDeals: getTrue(), - }, + Enabled: getTrue(), Data: &openrtb_ext.PriceFloorData{ - Currency: "USD", + Currency: "INR", + SkipRate: 0, ModelGroups: []openrtb_ext.PriceFloorModelGroup{ { - ModelVersion: "model from request", - Currency: "USD", + ModelVersion: "Version 1", + Currency: "INR", Values: map[string]float64{ - "banner|300x600|www.website5.com": 15, - "*|*|*": 25, + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, }, Schema: openrtb_ext.PriceFloorSchema{ - Fields: []string{"mediaType", "size", "domain"}, + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", }, }, }, - FloorProvider: "", + }, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforceRate: 50, + EnforcePBS: getFalse(), }, }, }, { - name: "only floor enforcement object present", + name: "Fetched Floors are present and request Floors has Floormin", args: args{ - account: testAccountConfig, - floors: &openrtb_ext.PriceFloorRules{ + reqFloors: &openrtb_ext.PriceFloorRules{ Enabled: getTrue(), Enforcement: &openrtb_ext.PriceFloorEnforcement{ - EnforcePBS: getTrue(), - EnforceRate: 100, - FloorDeals: getTrue(), + EnforceRate: 50, + EnforcePBS: getFalse(), + }, + FloorMin: 5, + FloorMinCur: "INR", + }, + fetchFloors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + Data: &openrtb_ext.PriceFloorData{ + Currency: "INR", + SkipRate: 0, + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "Version 1", + Currency: "INR", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", + }, + }, + }, }, }, - fetchStatus: openrtb_ext.FetchNone, - floorLocation: openrtb_ext.RequestLocation, }, want: &openrtb_ext.PriceFloorRules{ - FetchStatus: openrtb_ext.FetchNone, - PriceFloorLocation: openrtb_ext.NoDataLocation, + Enabled: getTrue(), + Data: &openrtb_ext.PriceFloorData{ + Currency: "INR", + SkipRate: 0, + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "Version 1", + Currency: "INR", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", + }, + }, + }, + }, Enforcement: &openrtb_ext.PriceFloorEnforcement{ - EnforcePBS: getTrue(), - EnforceRate: 100, - FloorDeals: getTrue(), + EnforceRate: 50, + EnforcePBS: getFalse(), }, + FloorMin: 5, + FloorMinCur: "INR", }, }, { - name: "Invalid modelGroup with skipRate = 110", + name: "Fetched Floors are present and request Floors has URL", args: args{ - account: testAccountConfig, - floors: &openrtb_ext.PriceFloorRules{ + reqFloors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforceRate: 50, + EnforcePBS: getFalse(), + }, + FloorMin: 5, + FloorMinCur: "INR", + Location: &openrtb_ext.PriceFloorEndpoint{ + URL: "https://test.com/floors", + }, + }, + fetchFloors: &openrtb_ext.PriceFloorRules{ Enabled: getTrue(), Data: &openrtb_ext.PriceFloorData{ - Currency: "USD", + Currency: "INR", + SkipRate: 0, ModelGroups: []openrtb_ext.PriceFloorModelGroup{ { - ModelVersion: "model from fetched", - Currency: "USD", - SkipRate: 110, + ModelVersion: "Version 1", + Currency: "INR", Values: map[string]float64{ - "banner|300x600|www.website5.com": 15, - "*|*|*": 25, + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, }, Schema: openrtb_ext.PriceFloorSchema{ - Fields: []string{"mediaType", "size", "domain"}, + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", }, }, }, }, }, - fetchStatus: openrtb_ext.FetchNone, - floorLocation: openrtb_ext.RequestLocation, }, want: &openrtb_ext.PriceFloorRules{ - FetchStatus: openrtb_ext.FetchNone, - PriceFloorLocation: openrtb_ext.RequestLocation, - }, - want1: []error{ - errors.New("Invalid Floor Model = 'model from fetched' due to SkipRate = '110' is out of range (1-100)"), - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - got, got1 := createFloorsFrom(tc.args.floors, tc.args.account, tc.args.fetchStatus, tc.args.floorLocation) - assert.Equal(t, got1, tc.want1, tc.name) - assert.Equal(t, got, tc.want, tc.name) - }) - } -} - -func TestIsPriceFloorsEnabled(t *testing.T) { - type args struct { - account config.Account - bidRequestWrapper *openrtb_ext.RequestWrapper - } - tests := []struct { - name string - args args - want bool - }{ - { - name: "Disabled in account and req", - args: args{ - account: config.Account{PriceFloors: config.AccountPriceFloors{Enabled: false}}, - bidRequestWrapper: &openrtb_ext.RequestWrapper{ - BidRequest: &openrtb2.BidRequest{ - Ext: json.RawMessage(`{"prebid":{"floors":{"enabled": false} }}`), + Enabled: getTrue(), + Data: &openrtb_ext.PriceFloorData{ + Currency: "INR", + SkipRate: 0, + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "Version 1", + Currency: "INR", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", + }, + }, }, }, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforceRate: 50, + EnforcePBS: getFalse(), + }, + FloorMin: 5, + FloorMinCur: "INR", + Location: &openrtb_ext.PriceFloorEndpoint{ + URL: "https://test.com/floors", + }, }, - want: false, }, { - name: "Enabled in account and req", + name: "Fetched Floors has no enable atrribute are present and request Floors has URL", args: args{ - account: config.Account{PriceFloors: config.AccountPriceFloors{Enabled: true}}, - bidRequestWrapper: &openrtb_ext.RequestWrapper{ - BidRequest: &openrtb2.BidRequest{ - Ext: json.RawMessage(`{"prebid":{"floors":{"enabled": true} }}`), + reqFloors: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforceRate: 50, + EnforcePBS: getFalse(), + }, + FloorMin: 5, + FloorMinCur: "INR", + Location: &openrtb_ext.PriceFloorEndpoint{ + URL: "https://test.com/floors", }, }, - }, - want: true, - }, - { - name: "disabled in account and enabled req", - args: args{ - account: config.Account{PriceFloors: config.AccountPriceFloors{Enabled: false}}, - bidRequestWrapper: &openrtb_ext.RequestWrapper{ - BidRequest: &openrtb2.BidRequest{ - Ext: json.RawMessage(`{"prebid":{"floors":{"enabled": true} }}`), + fetchFloors: &openrtb_ext.PriceFloorRules{ + Data: &openrtb_ext.PriceFloorData{ + Currency: "INR", + SkipRate: 0, + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "Version 1", + Currency: "INR", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", + }, + }, + }, }, }, }, - want: false, - }, - { - name: "Enabled in account and disabled in req", - args: args{ - account: config.Account{PriceFloors: config.AccountPriceFloors{Enabled: true}}, - bidRequestWrapper: &openrtb_ext.RequestWrapper{ - BidRequest: &openrtb2.BidRequest{ - Ext: json.RawMessage(`{"prebid":{"floors":{"enabled": false} }}`)}, + want: &openrtb_ext.PriceFloorRules{ + Enabled: getTrue(), + Data: &openrtb_ext.PriceFloorData{ + Currency: "INR", + SkipRate: 0, + ModelGroups: []openrtb_ext.PriceFloorModelGroup{ + { + ModelVersion: "Version 1", + Currency: "INR", + Values: map[string]float64{ + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, + }, + Schema: openrtb_ext.PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", + }, + }, + }, + }, + Enforcement: &openrtb_ext.PriceFloorEnforcement{ + EnforceRate: 50, + EnforcePBS: getFalse(), + }, + FloorMin: 5, + FloorMinCur: "INR", + Location: &openrtb_ext.PriceFloorEndpoint{ + URL: "https://test.com/floors", }, }, - want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got := isPriceFloorsEnabled(tt.args.account, tt.args.bidRequestWrapper) - assert.Equal(t, got, tt.want, tt.name) + if got := mergeFloors(tt.args.reqFloors, tt.args.fetchFloors, getCurrencyRates(rates)); !reflect.DeepEqual(got, tt.want) { + t.Errorf("mergeFloors() = %v, want %v", got, tt.want) + } }) } } diff --git a/floors/rule.go b/floors/rule.go index 785dabb2057..092d8ef6616 100644 --- a/floors/rule.go +++ b/floors/rule.go @@ -8,9 +8,10 @@ import ( "strings" "github.com/golang/glog" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) const ( @@ -38,8 +39,7 @@ const ( // getFloorCurrency returns floors currency provided in floors JSON, // if currency is not provided then defaults to USD func getFloorCurrency(floorExt *openrtb_ext.PriceFloorRules) string { - var floorCur string - + floorCur := defaultCurrency if floorExt != nil && floorExt.Data != nil { if floorExt.Data.Currency != "" { floorCur = floorExt.Data.Currency @@ -50,10 +50,6 @@ func getFloorCurrency(floorExt *openrtb_ext.PriceFloorRules) string { } } - if len(floorCur) == 0 { - floorCur = defaultCurrency - } - return floorCur } @@ -300,8 +296,8 @@ func getSizeValue(imp *openrtb2.Imp) string { if imp.Banner != nil { width, height = getBannerSize(imp) } else if imp.Video != nil { - width = imp.Video.W - height = imp.Video.H + width = ptrutil.ValueOrDefault(imp.Video.W) + height = ptrutil.ValueOrDefault(imp.Video.H) } if width != 0 && height != 0 { diff --git a/floors/rule_test.go b/floors/rule_test.go index 90d0736461b..90b829459de 100644 --- a/floors/rule_test.go +++ b/floors/rule_test.go @@ -5,9 +5,10 @@ import ( "errors" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -195,7 +196,7 @@ func TestUpdateImpExtWithFloorDetails(t *testing.T) { matchedRule: "test|123|xyz", floorRuleVal: 5.5, floorVal: 5.5, - imp: &openrtb_ext.ImpWrapper{Imp: &openrtb2.Imp{ID: "1234", Video: &openrtb2.Video{W: 300, H: 250}}}, + imp: &openrtb_ext.ImpWrapper{Imp: &openrtb2.Imp{ID: "1234", Video: &openrtb2.Video{W: ptrutil.ToPtr[int64](300), H: ptrutil.ToPtr[int64](250)}}}, expected: []byte(`{"prebid":{"floors":{"floorrule":"test|123|xyz","floorrulevalue":5.5,"floorvalue":5.5}}}`), }, { @@ -203,7 +204,7 @@ func TestUpdateImpExtWithFloorDetails(t *testing.T) { matchedRule: "test|123|xyz", floorRuleVal: 5.5, floorVal: 5.5, - imp: &openrtb_ext.ImpWrapper{Imp: &openrtb2.Imp{ID: "1234", Video: &openrtb2.Video{W: 300, H: 250}, Ext: json.RawMessage{}}}, + imp: &openrtb_ext.ImpWrapper{Imp: &openrtb2.Imp{ID: "1234", Video: &openrtb2.Video{W: ptrutil.ToPtr[int64](300), H: ptrutil.ToPtr[int64](250)}, Ext: json.RawMessage{}}}, expected: []byte(`{"prebid":{"floors":{"floorrule":"test|123|xyz","floorrulevalue":5.5,"floorvalue":5.5}}}`), }, { @@ -211,7 +212,7 @@ func TestUpdateImpExtWithFloorDetails(t *testing.T) { matchedRule: "banner|www.test.com|*", floorRuleVal: 5.5, floorVal: 15.5, - imp: &openrtb_ext.ImpWrapper{Imp: &openrtb2.Imp{ID: "1234", Video: &openrtb2.Video{W: 300, H: 250}, Ext: []byte(`{"prebid": {"test": true}}`)}}, + imp: &openrtb_ext.ImpWrapper{Imp: &openrtb2.Imp{ID: "1234", Video: &openrtb2.Video{W: ptrutil.ToPtr[int64](300), H: ptrutil.ToPtr[int64](250)}, Ext: []byte(`{"prebid": {"test": true}}`)}}, expected: []byte(`{"prebid":{"floors":{"floorrule":"banner|www.test.com|*","floorrulevalue":5.5,"floorvalue":15.5}}}`), }, { @@ -219,14 +220,14 @@ func TestUpdateImpExtWithFloorDetails(t *testing.T) { matchedRule: "", floorRuleVal: 5.5, floorVal: 15.5, - imp: &openrtb_ext.ImpWrapper{Imp: &openrtb2.Imp{ID: "1234", Video: &openrtb2.Video{W: 300, H: 250}, Ext: []byte(`{"prebid": {"test": true}}`)}}, + imp: &openrtb_ext.ImpWrapper{Imp: &openrtb2.Imp{ID: "1234", Video: &openrtb2.Video{W: ptrutil.ToPtr[int64](300), H: ptrutil.ToPtr[int64](250)}, Ext: []byte(`{"prebid": {"test": true}}`)}}, expected: []byte(`{"prebid":{"floors":{"floorvalue":15.5}}}`), }, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { updateImpExtWithFloorDetails(tc.imp, tc.matchedRule, tc.floorRuleVal, tc.floorVal) - _ = tc.imp.RebuildImpressionExt() + _ = tc.imp.RebuildImp() if tc.imp.Ext != nil { assert.Equal(t, tc.imp.Ext, tc.expected, tc.name) } @@ -259,7 +260,7 @@ func TestCreateRuleKeys(t *testing.T) { Site: &openrtb2.Site{ Domain: "www.test.com", }, - Imp: []openrtb2.Imp{{ID: "1234", Video: &openrtb2.Video{W: 640, H: 480, Placement: 1}}}, + Imp: []openrtb2.Imp{{ID: "1234", Video: &openrtb2.Video{W: ptrutil.ToPtr[int64](640), H: ptrutil.ToPtr[int64](480), Placement: 1}}}, }, floorSchema: openrtb_ext.PriceFloorSchema{Delimiter: "|", Fields: []string{"mediaType", "size", "domain"}}, out: []string{"video", "640x480", "www.test.com"}, @@ -270,7 +271,7 @@ func TestCreateRuleKeys(t *testing.T) { Site: &openrtb2.Site{ Domain: "www.test.com", }, - Imp: []openrtb2.Imp{{ID: "1234", Video: &openrtb2.Video{W: 300, H: 250, Placement: 2}}}, + Imp: []openrtb2.Imp{{ID: "1234", Video: &openrtb2.Video{W: ptrutil.ToPtr[int64](300), H: ptrutil.ToPtr[int64](250), Placement: 2}}}, }, floorSchema: openrtb_ext.PriceFloorSchema{Delimiter: "|", Fields: []string{"mediaType", "size", "domain"}}, out: []string{"video-outstream", "300x250", "www.test.com"}, @@ -768,7 +769,7 @@ func TestGetMinFloorValue(t *testing.T) { }, want: 0.0, want1: "", - wantErr: errors.New("Error in getting FloorMin value : 'unexpected end of JSON input'"), + wantErr: errors.New("Error in getting FloorMin value : 'expects \" or n, but found \x00'"), }, } for _, tc := range testCases { @@ -1023,7 +1024,7 @@ func TestGetSizeValue(t *testing.T) { }, { name: "video: imp.video.w and imp.video.h present", - imp: &openrtb2.Imp{Video: &openrtb2.Video{W: 120, H: 240}}, + imp: &openrtb2.Imp{Video: &openrtb2.Video{W: ptrutil.ToPtr[int64](120), H: ptrutil.ToPtr[int64](240)}}, want: "120x240", }, { @@ -1053,7 +1054,7 @@ func TestGetMediaType(t *testing.T) { }{ { name: "more than one of these: imp.banner, imp.video, imp.native, imp.audio present", - imp: &openrtb2.Imp{Video: &openrtb2.Video{W: 120, H: 240}, Banner: &openrtb2.Banner{W: getInt64Ptr(320), H: getInt64Ptr(240)}}, + imp: &openrtb2.Imp{Video: &openrtb2.Video{W: ptrutil.ToPtr[int64](120), H: ptrutil.ToPtr[int64](240)}, Banner: &openrtb2.Banner{W: getInt64Ptr(320), H: getInt64Ptr(240)}}, want: "*", }, { @@ -1063,12 +1064,12 @@ func TestGetMediaType(t *testing.T) { }, { name: "video-outstream present", - imp: &openrtb2.Imp{Video: &openrtb2.Video{W: 120, H: 240, Placement: 2}}, + imp: &openrtb2.Imp{Video: &openrtb2.Video{W: ptrutil.ToPtr[int64](120), H: ptrutil.ToPtr[int64](240), Placement: 2}}, want: "video-outstream", }, { name: "video-instream present", - imp: &openrtb2.Imp{Video: &openrtb2.Video{W: 120, H: 240, Placement: 1}}, + imp: &openrtb2.Imp{Video: &openrtb2.Video{W: ptrutil.ToPtr[int64](120), H: ptrutil.ToPtr[int64](240), Placement: 1}}, want: "video", }, { diff --git a/floors/validate.go b/floors/validate.go index 6052ed9cfb2..447614150c4 100644 --- a/floors/validate.go +++ b/floors/validate.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) var validSchemaDimensions = map[string]struct{}{ diff --git a/floors/validate_test.go b/floors/validate_test.go index 45ae335d5b9..17effe7dbfd 100644 --- a/floors/validate_test.go +++ b/floors/validate_test.go @@ -5,8 +5,8 @@ import ( "fmt" "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/gdpr/aggregated_config.go b/gdpr/aggregated_config.go index bbfb503225d..9ac03143a1c 100644 --- a/gdpr/aggregated_config.go +++ b/gdpr/aggregated_config.go @@ -3,8 +3,8 @@ package gdpr import ( "github.com/prebid/go-gdpr/consentconstants" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // TCF2ConfigReader is an interface to access TCF2 configurations @@ -17,7 +17,7 @@ type TCF2ConfigReader interface { PurposeEnforced(consentconstants.Purpose) bool PurposeEnforcementAlgo(consentconstants.Purpose) config.TCF2EnforcementAlgo PurposeEnforcingVendors(consentconstants.Purpose) bool - PurposeVendorExceptions(consentconstants.Purpose) map[openrtb_ext.BidderName]struct{} + PurposeVendorExceptions(consentconstants.Purpose) map[string]struct{} PurposeOneTreatmentEnabled() bool PurposeOneTreatmentAccessAllowed() bool } @@ -85,9 +85,9 @@ func (tc *tcf2Config) PurposeEnforcingVendors(purpose consentconstants.Purpose) } // PurposeVendorExceptions returns the vendor exception map for the specified purpose if it exists for the account; -// otherwise it returns a nil map. If a bidder is a vendor exception, the GDPR full enforcement algorithm will +// otherwise it returns a nil map. If a bidder/analytics adapter is a vendor exception, the GDPR full enforcement algorithm will // bypass the legal basis calculation assuming the request is valid and there isn't a "deny all" publisher restriction -func (tc *tcf2Config) PurposeVendorExceptions(purpose consentconstants.Purpose) map[openrtb_ext.BidderName]struct{} { +func (tc *tcf2Config) PurposeVendorExceptions(purpose consentconstants.Purpose) map[string]struct{} { if value, exists := tc.AccountConfig.PurposeVendorExceptions(purpose); exists { return value } diff --git a/gdpr/aggregated_config_test.go b/gdpr/aggregated_config_test.go index bf2d3bbb8f8..a24e9eff1b1 100644 --- a/gdpr/aggregated_config_test.go +++ b/gdpr/aggregated_config_test.go @@ -5,8 +5,8 @@ import ( "github.com/prebid/go-gdpr/consentconstants" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -317,54 +317,54 @@ func TestPurposeEnforcingVendors(t *testing.T) { func TestPurposeVendorExceptions(t *testing.T) { tests := []struct { description string - givePurpose1HostExceptionMap map[openrtb_ext.BidderName]struct{} - givePurpose1AccountExceptionMap map[openrtb_ext.BidderName]struct{} - givePurpose2HostExceptionMap map[openrtb_ext.BidderName]struct{} - givePurpose2AccountExceptionMap map[openrtb_ext.BidderName]struct{} + givePurpose1HostExceptionMap map[string]struct{} + givePurpose1AccountExceptionMap map[string]struct{} + givePurpose2HostExceptionMap map[string]struct{} + givePurpose2AccountExceptionMap map[string]struct{} givePurpose consentconstants.Purpose - wantExceptionMap map[openrtb_ext.BidderName]struct{} + wantExceptionMap map[string]struct{} }{ { description: "Purpose 1 exception list set at account level - use empty account list", - givePurpose1HostExceptionMap: map[openrtb_ext.BidderName]struct{}{}, - givePurpose1AccountExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + givePurpose1HostExceptionMap: map[string]struct{}{}, + givePurpose1AccountExceptionMap: map[string]struct{}{}, givePurpose: 1, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + wantExceptionMap: map[string]struct{}{}, }, { description: "Purpose 1 exception list set at account level - use nonempty account list", - givePurpose1HostExceptionMap: map[openrtb_ext.BidderName]struct{}{}, - givePurpose1AccountExceptionMap: map[openrtb_ext.BidderName]struct{}{"appnexus": {}, "rubicon": {}}, + givePurpose1HostExceptionMap: map[string]struct{}{}, + givePurpose1AccountExceptionMap: map[string]struct{}{"appnexus": {}, "rubicon": {}}, givePurpose: 1, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{"appnexus": {}, "rubicon": {}}, + wantExceptionMap: map[string]struct{}{"appnexus": {}, "rubicon": {}}, }, { description: "Purpose 1 exception list not set at account level - use empty host list", - givePurpose1HostExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + givePurpose1HostExceptionMap: map[string]struct{}{}, givePurpose1AccountExceptionMap: nil, givePurpose: 1, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + wantExceptionMap: map[string]struct{}{}, }, { description: "Purpose 1 exception list not set at account level - use nonempty host list", - givePurpose1HostExceptionMap: map[openrtb_ext.BidderName]struct{}{"appnexus": {}, "rubicon": {}}, + givePurpose1HostExceptionMap: map[string]struct{}{"appnexus": {}, "rubicon": {}}, givePurpose1AccountExceptionMap: nil, givePurpose: 1, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{"appnexus": {}, "rubicon": {}}, + wantExceptionMap: map[string]struct{}{"appnexus": {}, "rubicon": {}}, }, { description: "Purpose 1 exception list not set at account level or host level", givePurpose1HostExceptionMap: nil, givePurpose1AccountExceptionMap: nil, givePurpose: 1, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + wantExceptionMap: map[string]struct{}{}, }, { description: "Some other purpose exception list set at account level", - givePurpose2HostExceptionMap: map[openrtb_ext.BidderName]struct{}{}, - givePurpose2AccountExceptionMap: map[openrtb_ext.BidderName]struct{}{"appnexus": {}, "rubicon": {}}, + givePurpose2HostExceptionMap: map[string]struct{}{}, + givePurpose2AccountExceptionMap: map[string]struct{}{"appnexus": {}, "rubicon": {}}, givePurpose: 2, - wantExceptionMap: map[openrtb_ext.BidderName]struct{}{"appnexus": {}, "rubicon": {}}, + wantExceptionMap: map[string]struct{}{"appnexus": {}, "rubicon": {}}, }, } diff --git a/gdpr/basic_enforcement.go b/gdpr/basic_enforcement.go index f4559c4643d..2bb11857a16 100644 --- a/gdpr/basic_enforcement.go +++ b/gdpr/basic_enforcement.go @@ -2,10 +2,9 @@ package gdpr import ( tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2" - "github.com/prebid/prebid-server/openrtb_ext" ) -// BasicEnforcement determines if legal basis is satisfied for a given purpose and bidder using +// BasicEnforcement determines if legal basis is satisfied for a given purpose and bidder/analytics adapter using // the TCF2 basic enforcement algorithm. The algorithm is a high-level mode of consent confirmation // that looks for a good-faith indication that the user has provided consent or legal basis signals // necessary to perform a privacy-protected activity. The algorithm does not involve the GVL. @@ -14,21 +13,21 @@ type BasicEnforcement struct { cfg purposeConfig } -// LegalBasis determines if legal basis is satisfied for a given purpose and bidder based on user consent +// LegalBasis determines if legal basis is satisfied for a given purpose and bidder/analytics adapter based on user consent // and legal basis signals. -func (be *BasicEnforcement) LegalBasis(vendorInfo VendorInfo, bidder openrtb_ext.BidderName, consent tcf2.ConsentMetadata, overrides Overrides) bool { +func (be *BasicEnforcement) LegalBasis(vendorInfo VendorInfo, name string, consent tcf2.ConsentMetadata, overrides Overrides) bool { enforcePurpose, enforceVendors := be.applyEnforceOverrides(overrides) if !enforcePurpose && !enforceVendors { return true } - if be.cfg.vendorException(bidder) && !overrides.blockVendorExceptions { + if be.cfg.vendorException(name) && !overrides.blockVendorExceptions { return true } - if !enforcePurpose && be.cfg.basicEnforcementVendor(bidder) { + if !enforcePurpose && be.cfg.basicEnforcementVendor(name) { return true } - if enforcePurpose && consent.PurposeAllowed(be.cfg.PurposeID) && be.cfg.basicEnforcementVendor(bidder) { + if enforcePurpose && consent.PurposeAllowed(be.cfg.PurposeID) && be.cfg.basicEnforcementVendor(name) { return true } if enforcePurpose && consent.PurposeLITransparency(be.cfg.PurposeID) && overrides.allowLITransparency { diff --git a/gdpr/basic_enforcement_test.go b/gdpr/basic_enforcement_test.go index c49e59ea595..eb22f59bc0b 100644 --- a/gdpr/basic_enforcement_test.go +++ b/gdpr/basic_enforcement_test.go @@ -6,13 +6,16 @@ import ( "github.com/prebid/go-gdpr/consentconstants" "github.com/prebid/go-gdpr/vendorconsent" tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) func TestBasicLegalBasis(t *testing.T) { - appnexusID := uint16(32) + var ( + appnexus = string(openrtb_ext.BidderAppnexus) + appnexusID = uint16(32) + ) noConsents := "CPerMsAPerMsAAAAAAENCfCAAAAAAAAAAAAAAAAAAAAA" purpose2Consent := "CPerMsAPerMsAAAAAAENCfCAAEAAAAAAAAAAAAAAAAAA" @@ -149,7 +152,7 @@ func TestBasicLegalBasis(t *testing.T) { PurposeID: consentconstants.Purpose(2), EnforcePurpose: false, EnforceVendors: true, - BasicEnforcementVendorsMap: map[string]struct{}{string(openrtb_ext.BidderAppnexus): {}}, + BasicEnforcementVendorsMap: map[string]struct{}{appnexus: {}}, }, wantResult: true, }, @@ -170,7 +173,7 @@ func TestBasicLegalBasis(t *testing.T) { PurposeID: consentconstants.Purpose(2), EnforcePurpose: true, EnforceVendors: true, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + VendorExceptionMap: map[string]struct{}{appnexus: {}}, }, wantResult: true, }, @@ -181,7 +184,7 @@ func TestBasicLegalBasis(t *testing.T) { PurposeID: consentconstants.Purpose(2), EnforcePurpose: true, EnforceVendors: true, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + VendorExceptionMap: map[string]struct{}{appnexus: {}}, }, overrides: Overrides{blockVendorExceptions: true}, wantResult: false, @@ -203,7 +206,7 @@ func TestBasicLegalBasis(t *testing.T) { PurposeID: consentconstants.Purpose(2), EnforcePurpose: true, EnforceVendors: true, - BasicEnforcementVendorsMap: map[string]struct{}{string(openrtb_ext.BidderAppnexus): {}}, + BasicEnforcementVendorsMap: map[string]struct{}{appnexus: {}}, }, wantResult: true, }, @@ -233,7 +236,7 @@ func TestBasicLegalBasis(t *testing.T) { enforcer := BasicEnforcement{cfg: tt.config} vendorInfo := VendorInfo{vendorID: appnexusID, vendor: nil} - result := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides) + result := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides) assert.Equal(t, tt.wantResult, result, tt.description) } diff --git a/gdpr/full_enforcement.go b/gdpr/full_enforcement.go index eefa28d5499..9c2b5221385 100644 --- a/gdpr/full_enforcement.go +++ b/gdpr/full_enforcement.go @@ -2,7 +2,6 @@ package gdpr import ( tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2" - "github.com/prebid/prebid-server/openrtb_ext" ) const ( @@ -11,7 +10,7 @@ const ( pubRestrictRequireLegitInterest = 2 ) -// FullEnforcement determines if legal basis is satisfied for a given purpose and bidder using +// FullEnforcement determines if legal basis is satisfied for a given purpose and bidde/analytics adapterr using // the TCF2 full enforcement algorithm. The algorithm is a detailed confirmation that reads the // GVL, interprets the consent string and performs legal basis analysis necessary to perform a // privacy-protected activity. @@ -20,9 +19,9 @@ type FullEnforcement struct { cfg purposeConfig } -// LegalBasis determines if legal basis is satisfied for a given purpose and bidder based on the +// LegalBasis determines if legal basis is satisfied for a given purpose and bidder/analytics adapter based on the // vendor claims in the GVL, publisher restrictions and user consent. -func (fe *FullEnforcement) LegalBasis(vendorInfo VendorInfo, bidder openrtb_ext.BidderName, consent tcf2.ConsentMetadata, overrides Overrides) bool { +func (fe *FullEnforcement) LegalBasis(vendorInfo VendorInfo, name string, consent tcf2.ConsentMetadata, overrides Overrides) bool { enforcePurpose, enforceVendors := fe.applyEnforceOverrides(overrides) if consent.CheckPubRestriction(uint8(fe.cfg.PurposeID), pubRestrictNotAllowed, vendorInfo.vendorID) { @@ -31,7 +30,7 @@ func (fe *FullEnforcement) LegalBasis(vendorInfo VendorInfo, bidder openrtb_ext. if !enforcePurpose && !enforceVendors { return true } - if fe.cfg.vendorException(bidder) && !overrides.blockVendorExceptions { + if fe.cfg.vendorException(name) && !overrides.blockVendorExceptions { return true } diff --git a/gdpr/full_enforcement_test.go b/gdpr/full_enforcement_test.go index 61f6e8b8520..32ba2e50289 100644 --- a/gdpr/full_enforcement_test.go +++ b/gdpr/full_enforcement_test.go @@ -1,7 +1,6 @@ package gdpr import ( - "encoding/json" "testing" "github.com/prebid/go-gdpr/consentconstants" @@ -9,13 +8,17 @@ import ( tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2" "github.com/prebid/go-gdpr/vendorlist" "github.com/prebid/go-gdpr/vendorlist2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" ) func TestLegalBasisWithPubRestrictionAllowNone(t *testing.T) { - appnexusID := uint16(32) + var ( + appnexus = string(openrtb_ext.BidderAppnexus) + appnexusID = uint16(32) + ) NoConsentsWithP1P2P3V32RestrictionAllowNone := "CPfMKEAPfMKEAAAAAAENCgCAAAAAAAAAAAAAAQAAAAAAAIAAAAAAAGCAAgAgCAAQAQBgAIAIAAAA" P1P2P3PurposeConsentAndV32VendorConsentWithP1P2P3V32RestrictionAllowNone := "CPfMKEAPfMKEAAAAAAENCgCAAOAAAAAAAAAAAQAAAAAEAIAAAAAAAGCAAgAgCAAQAQBgAIAIAAAA" @@ -44,7 +47,7 @@ func TestLegalBasisWithPubRestrictionAllowNone(t *testing.T) { config: purposeConfig{ EnforcePurpose: true, EnforceVendors: true, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + VendorExceptionMap: map[string]struct{}{appnexus: {}}, }, consent: NoConsentsWithP1P2P3V32RestrictionAllowNone, wantConsentPurposeResult: false, @@ -80,21 +83,24 @@ func TestLegalBasisWithPubRestrictionAllowNone(t *testing.T) { enforcer := FullEnforcement{cfg: tt.config} enforcer.cfg.PurposeID = consentconstants.Purpose(1) - consentPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, Overrides{}) + consentPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, Overrides{}) assert.Equal(t, tt.wantConsentPurposeResult, consentPurposeResult, tt.description+" -- GVL consent purpose") enforcer.cfg.PurposeID = consentconstants.Purpose(2) - LIPurposeresult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, Overrides{}) + LIPurposeresult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, Overrides{}) assert.Equal(t, tt.wantLIPurposeResult, LIPurposeresult, tt.description+" -- GVL LI purpose") enforcer.cfg.PurposeID = consentconstants.Purpose(3) - flexPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, Overrides{}) + flexPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, Overrides{}) assert.Equal(t, tt.wantFlexPurposeResult, flexPurposeResult, tt.description+" -- GVL flex purpose") } } func TestLegalBasisWithNoPubRestrictionsAndWithPubRestrictionAllowAll(t *testing.T) { - appnexusID := uint16(32) + var ( + appnexus = string(openrtb_ext.BidderAppnexus) + appnexusID = uint16(32) + ) NoConsents := "CPfCRQAPfCRQAAAAAAENCgCAAAAAAAAAAAAAAAAAAAAA" P1P2P3PurposeConsent := "CPfCRQAPfCRQAAAAAAENCgCAAOAAAAAAAAAAAAAAAAAA" @@ -325,7 +331,7 @@ func TestLegalBasisWithNoPubRestrictionsAndWithPubRestrictionAllowAll(t *testing config: purposeConfig{ EnforcePurpose: true, EnforceVendors: true, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + VendorExceptionMap: map[string]struct{}{appnexus: {}}, }, consentNoPubRestriction: NoConsents, consentWithPubRestriction: NoConsentsWithP1P2P3V32RestrictionAllowAll, @@ -338,7 +344,7 @@ func TestLegalBasisWithNoPubRestrictionsAndWithPubRestrictionAllowAll(t *testing config: purposeConfig{ EnforcePurpose: true, EnforceVendors: true, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + VendorExceptionMap: map[string]struct{}{appnexus: {}}, }, consentNoPubRestriction: NoConsents, consentWithPubRestriction: NoConsentsWithP1P2P3V32RestrictionAllowAll, @@ -370,22 +376,25 @@ func TestLegalBasisWithNoPubRestrictionsAndWithPubRestrictionAllowAll(t *testing enforcer := FullEnforcement{cfg: tt.config} enforcer.cfg.PurposeID = consentconstants.Purpose(1) - consentPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides) + consentPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides) assert.Equal(t, tt.wantConsentPurposeResult, consentPurposeResult, tt.description+" -- GVL consent purpose -- consent string %d of %d", i+1, len(consents)) enforcer.cfg.PurposeID = consentconstants.Purpose(2) - LIPurposeresult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides) + LIPurposeresult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides) assert.Equal(t, tt.wantLIPurposeResult, LIPurposeresult, tt.description+" -- GVL LI purpose -- consent string %d of %d", i+1, len(consents)) enforcer.cfg.PurposeID = consentconstants.Purpose(3) - flexPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides) + flexPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides) assert.Equal(t, tt.wantFlexPurposeResult, flexPurposeResult, tt.description+" -- GVL flex purpose -- consent string %d of %d", i+1, len(consents)) } } } func TestLegalBasisWithPubRestrictionRequireConsent(t *testing.T) { - appnexusID := uint16(32) + var ( + appnexus = string(openrtb_ext.BidderAppnexus) + appnexusID = uint16(32) + ) NoConsentsWithP1P2P3V32RestrictionRequireConsent := "CPfFkMAPfFkMAAAAAAENCgCAAAAAAAAAAAAAAQAAAAAAAIAAAAAAAGCgAgAgCQAQAQBoAIAIAAAA" P1P2P3PurposeConsentWithP1P2P3V32RestrictionRequireConsent := "CPfFkMAPfFkMAAAAAAENCgCAAOAAAAAAAAAAAQAAAAAAAIAAAAAAAGCgAgAgCQAQAQBoAIAIAAAA" @@ -590,7 +599,7 @@ func TestLegalBasisWithPubRestrictionRequireConsent(t *testing.T) { config: purposeConfig{ EnforcePurpose: true, EnforceVendors: true, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + VendorExceptionMap: map[string]struct{}{appnexus: {}}, }, consent: NoConsentsWithP1P2P3V32RestrictionRequireConsent, wantConsentPurposeResult: true, @@ -602,7 +611,7 @@ func TestLegalBasisWithPubRestrictionRequireConsent(t *testing.T) { config: purposeConfig{ EnforcePurpose: true, EnforceVendors: true, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + VendorExceptionMap: map[string]struct{}{appnexus: {}}, }, consent: NoConsentsWithP1P2P3V32RestrictionRequireConsent, overrides: Overrides{blockVendorExceptions: true}, @@ -628,21 +637,24 @@ func TestLegalBasisWithPubRestrictionRequireConsent(t *testing.T) { enforcer := FullEnforcement{cfg: tt.config} enforcer.cfg.PurposeID = consentconstants.Purpose(1) - consentPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides) + consentPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides) assert.Equal(t, tt.wantConsentPurposeResult, consentPurposeResult, tt.description+" -- GVL consent purpose") enforcer.cfg.PurposeID = consentconstants.Purpose(2) - LIPurposeresult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides) + LIPurposeresult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides) assert.Equal(t, tt.wantLIPurposeResult, LIPurposeresult, tt.description+" -- GVL LI purpose") enforcer.cfg.PurposeID = consentconstants.Purpose(3) - flexPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides) + flexPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides) assert.Equal(t, tt.wantFlexPurposeResult, flexPurposeResult, tt.description+" -- GVL flex purpose") } } func TestLegalBasisWithPubRestrictionRequireLI(t *testing.T) { - appnexusID := uint16(32) + var ( + appnexus = string(openrtb_ext.BidderAppnexus) + appnexusID = uint16(32) + ) NoConsentsWithP1P2P3V32RestrictionRequireLI := "CPfFkMAPfFkMAAAAAAENCgCAAAAAAAAAAAAAAQAAAAAAAIAAAAAAAGDAAgAgCgAQAQBwAIAIAAAA" P1P2P3PurposeConsentWithP1P2P3V32RestrictionRequireLI := "CPfFkMAPfFkMAAAAAAENCgCAAOAAAAAAAAAAAQAAAAAAAIAAAAAAAGDAAgAgCgAQAQBwAIAIAAAA" @@ -847,7 +859,7 @@ func TestLegalBasisWithPubRestrictionRequireLI(t *testing.T) { config: purposeConfig{ EnforcePurpose: true, EnforceVendors: true, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + VendorExceptionMap: map[string]struct{}{appnexus: {}}, }, consent: NoConsentsWithP1P2P3V32RestrictionRequireLI, wantConsentPurposeResult: true, @@ -859,7 +871,7 @@ func TestLegalBasisWithPubRestrictionRequireLI(t *testing.T) { config: purposeConfig{ EnforcePurpose: true, EnforceVendors: true, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + VendorExceptionMap: map[string]struct{}{appnexus: {}}, }, consent: NoConsentsWithP1P2P3V32RestrictionRequireLI, overrides: Overrides{blockVendorExceptions: true}, @@ -885,20 +897,21 @@ func TestLegalBasisWithPubRestrictionRequireLI(t *testing.T) { enforcer := FullEnforcement{cfg: tt.config} enforcer.cfg.PurposeID = consentconstants.Purpose(1) - consentPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides) + consentPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides) assert.Equal(t, tt.wantConsentPurposeResult, consentPurposeResult, tt.description+" -- GVL consent purpose") enforcer.cfg.PurposeID = consentconstants.Purpose(2) - LIPurposeresult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides) + LIPurposeresult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides) assert.Equal(t, tt.wantLIPurposeResult, LIPurposeresult, tt.description+" -- GVL LI purpose") enforcer.cfg.PurposeID = consentconstants.Purpose(3) - flexPurposeResult := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, tt.overrides) + flexPurposeResult := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, tt.overrides) assert.Equal(t, tt.wantFlexPurposeResult, flexPurposeResult, tt.description+" -- GVL flex purpose") } } func TestLegalBasisWithoutVendor(t *testing.T) { + appnexus := string(openrtb_ext.BidderAppnexus) P1P2P3PurposeConsent := "CPfCRQAPfCRQAAAAAAENCgCAAOAAAAAAAAAAAAAAAAAA" tests := []struct { name string @@ -918,7 +931,7 @@ func TestLegalBasisWithoutVendor(t *testing.T) { config: purposeConfig{ EnforcePurpose: true, EnforceVendors: false, - VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + VendorExceptionMap: map[string]struct{}{appnexus: {}}, }, wantResult: true, }, @@ -952,7 +965,7 @@ func TestLegalBasisWithoutVendor(t *testing.T) { enforcer := FullEnforcement{cfg: tt.config} enforcer.cfg.PurposeID = consentconstants.Purpose(3) - result := enforcer.LegalBasis(vendorInfo, openrtb_ext.BidderAppnexus, consentMeta, Overrides{}) + result := enforcer.LegalBasis(vendorInfo, appnexus, consentMeta, Overrides{}) assert.Equal(t, tt.wantResult, result) }) } @@ -961,7 +974,7 @@ func TestLegalBasisWithoutVendor(t *testing.T) { func getVendorList(t *testing.T) vendorlist.VendorList { GVL := makeVendorList() - marshaledGVL, err := json.Marshal(GVL) + marshaledGVL, err := jsonutil.Marshal(GVL) if err != nil { t.Fatalf("Failed to marshal GVL") } diff --git a/gdpr/gdpr.go b/gdpr/gdpr.go index db6aa125383..1b4f6cb4680 100644 --- a/gdpr/gdpr.go +++ b/gdpr/gdpr.go @@ -3,8 +3,8 @@ package gdpr import ( "context" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type Permissions interface { diff --git a/gdpr/gdpr_test.go b/gdpr/gdpr_test.go index 1e56c5fdede..3729eda3d5b 100644 --- a/gdpr/gdpr_test.go +++ b/gdpr/gdpr_test.go @@ -6,8 +6,8 @@ import ( "github.com/prebid/go-gdpr/consentconstants" "github.com/prebid/go-gdpr/vendorlist" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -60,6 +60,6 @@ type fakePurposeEnforcerBuilder struct { purposeEnforcer PurposeEnforcer } -func (fpeb fakePurposeEnforcerBuilder) Builder(consentconstants.Purpose, openrtb_ext.BidderName) PurposeEnforcer { +func (fpeb fakePurposeEnforcerBuilder) Builder(consentconstants.Purpose, string) PurposeEnforcer { return fpeb.purposeEnforcer } diff --git a/gdpr/impl.go b/gdpr/impl.go index cc88a1fd3c6..614c06d9a6a 100644 --- a/gdpr/impl.go +++ b/gdpr/impl.go @@ -6,7 +6,7 @@ import ( "github.com/prebid/go-gdpr/api" "github.com/prebid/go-gdpr/consentconstants" tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const noBidder openrtb_ext.BidderName = "" @@ -48,7 +48,7 @@ func (p *permissionsImpl) BidderSyncAllowed(ctx context.Context, bidder openrtb_ id, ok := p.vendorIDs[bidder] if ok { vendorExceptions := p.cfg.PurposeVendorExceptions(consentconstants.Purpose(1)) - _, vendorException := vendorExceptions[bidder] + _, vendorException := vendorExceptions[string(bidder)] return p.allowSync(ctx, id, bidder, vendorException) } @@ -138,9 +138,9 @@ func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, bidder } purpose := consentconstants.Purpose(1) - enforcer := p.purposeEnforcerBuilder(purpose, bidder) + enforcer := p.purposeEnforcerBuilder(purpose, string(bidder)) - if enforcer.LegalBasis(vendorInfo, bidder, pc.consentMeta, Overrides{blockVendorExceptions: !vendorException}) { + if enforcer.LegalBasis(vendorInfo, string(bidder), pc.consentMeta, Overrides{blockVendorExceptions: !vendorException}) { return true, nil } return false, nil @@ -149,13 +149,13 @@ func (p *permissionsImpl) allowSync(ctx context.Context, vendorID uint16, bidder // allowBidRequest computes legal basis for a given bidder using the enforcement algorithms selected // by the purpose enforcer builder func (p *permissionsImpl) allowBidRequest(bidder openrtb_ext.BidderName, consentMeta tcf2.ConsentMetadata, vendorInfo VendorInfo) bool { - enforcer := p.purposeEnforcerBuilder(consentconstants.Purpose(2), bidder) + enforcer := p.purposeEnforcerBuilder(consentconstants.Purpose(2), string(bidder)) overrides := Overrides{} if _, ok := enforcer.(*BasicEnforcement); ok { overrides.allowLITransparency = true } - return enforcer.LegalBasis(vendorInfo, bidder, consentMeta, overrides) + return enforcer.LegalBasis(vendorInfo, string(bidder), consentMeta, overrides) } // allowGeo computes legal basis for a given bidder using the configs, consent and GVL pertaining to @@ -180,13 +180,13 @@ func (p *permissionsImpl) allowGeo(bidder openrtb_ext.BidderName, consentMeta tc func (p *permissionsImpl) allowID(bidder openrtb_ext.BidderName, consentMeta tcf2.ConsentMetadata, vendorInfo VendorInfo) bool { for i := 2; i <= 10; i++ { purpose := consentconstants.Purpose(i) - enforcer := p.purposeEnforcerBuilder(purpose, bidder) + enforcer := p.purposeEnforcerBuilder(purpose, string(bidder)) overrides := Overrides{enforcePurpose: true, enforceVendors: true} if _, ok := enforcer.(*BasicEnforcement); ok && purpose == consentconstants.Purpose(2) { overrides.allowLITransparency = true } - if enforcer.LegalBasis(vendorInfo, bidder, consentMeta, overrides) { + if enforcer.LegalBasis(vendorInfo, string(bidder), consentMeta, overrides) { return true } } diff --git a/gdpr/impl_test.go b/gdpr/impl_test.go index 835a580f6e2..64fa4434d4d 100644 --- a/gdpr/impl_test.go +++ b/gdpr/impl_test.go @@ -9,8 +9,8 @@ import ( "github.com/prebid/go-gdpr/consentconstants" "github.com/prebid/go-gdpr/vendorlist" "github.com/prebid/go-gdpr/vendorlist2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -350,7 +350,7 @@ func TestAllowActivitiesBidderWithoutGVLID(t *testing.T) { tests := []struct { name string enforceAlgoID config.TCF2EnforcementAlgo - vendorExceptions map[openrtb_ext.BidderName]struct{} + vendorExceptions map[string]struct{} basicEnforcementVendors map[string]struct{} consent string allowBidRequest bool @@ -364,7 +364,7 @@ func TestAllowActivitiesBidderWithoutGVLID(t *testing.T) { { name: "full_enforcement_vendor_exception_user_consents_to_purpose_2", enforceAlgoID: config.TCF2FullEnforcement, - vendorExceptions: map[openrtb_ext.BidderName]struct{}{bidderWithoutGVLID: {}}, + vendorExceptions: map[string]struct{}{string(bidderWithoutGVLID): {}}, consent: purpose2Consent, allowBidRequest: true, passID: true, @@ -375,7 +375,7 @@ func TestAllowActivitiesBidderWithoutGVLID(t *testing.T) { }, { name: "basic_enforcement_vendor_exception_user_consents_to_purpose_2", - vendorExceptions: map[openrtb_ext.BidderName]struct{}{bidderWithoutGVLID: {}}, + vendorExceptions: map[string]struct{}{string(bidderWithoutGVLID): {}}, consent: purpose2Consent, allowBidRequest: true, passID: true, @@ -1110,12 +1110,13 @@ func TestAllowActivitiesBidRequests(t *testing.T) { } func TestAllowActivitiesVendorException(t *testing.T) { + appnexus := string(openrtb_ext.BidderAppnexus) noPurposeOrVendorConsentAndPubRestrictsP2 := "CPF_61ePF_61eFxAAAENAiCAAAAAAAAAAAAAACEAAAACEAAgAgAA" noPurposeOrVendorConsentAndPubRestrictsNone := "CPF_61ePF_61eFxAAAENAiCAAAAAAAAAAAAAACEAAAAA" testDefs := []struct { description string - p2VendorExceptionMap map[openrtb_ext.BidderName]struct{} + p2VendorExceptionMap map[string]struct{} sf1VendorExceptionMap map[openrtb_ext.BidderName]struct{} bidder openrtb_ext.BidderName consent string @@ -1126,7 +1127,7 @@ func TestAllowActivitiesVendorException(t *testing.T) { }{ { description: "Bid/ID blocked by publisher - p2 enabled with p2 vendor exception, pub restricts p2 for vendor", - p2VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + p2VendorExceptionMap: map[string]struct{}{appnexus: {}}, bidder: openrtb_ext.BidderAppnexus, bidderCoreName: openrtb_ext.BidderAppnexus, consent: noPurposeOrVendorConsentAndPubRestrictsP2, @@ -1136,7 +1137,7 @@ func TestAllowActivitiesVendorException(t *testing.T) { }, { description: "Bid/ID allowed by vendor exception - p2 enabled with p2 vendor exception, pub restricts none", - p2VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + p2VendorExceptionMap: map[string]struct{}{appnexus: {}}, sf1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, bidder: openrtb_ext.BidderAppnexus, bidderCoreName: openrtb_ext.BidderAppnexus, @@ -1147,7 +1148,7 @@ func TestAllowActivitiesVendorException(t *testing.T) { }, { description: "Geo blocked - sf1 enabled but no consent", - p2VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + p2VendorExceptionMap: map[string]struct{}{}, sf1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, bidder: openrtb_ext.BidderAppnexus, bidderCoreName: openrtb_ext.BidderAppnexus, @@ -1158,7 +1159,7 @@ func TestAllowActivitiesVendorException(t *testing.T) { }, { description: "Geo allowed by vendor exception - sf1 enabled with sf1 vendor exception", - p2VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + p2VendorExceptionMap: map[string]struct{}{}, sf1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, bidder: openrtb_ext.BidderAppnexus, bidderCoreName: openrtb_ext.BidderAppnexus, @@ -1204,33 +1205,34 @@ func TestAllowActivitiesVendorException(t *testing.T) { } func TestBidderSyncAllowedVendorException(t *testing.T) { + appnexus := string(openrtb_ext.BidderAppnexus) noPurposeOrVendorConsentAndPubRestrictsP1 := "CPF_61ePF_61eFxAAAENAiCAAAAAAAAAAAAAAQAAAAAAAAAAIIACACA" noPurposeOrVendorConsentAndPubRestrictsNone := "CPF_61ePF_61eFxAAAENAiCAAAAAAAAAAAAAACEAAAAA" testDefs := []struct { description string - p1VendorExceptionMap map[openrtb_ext.BidderName]struct{} + p1VendorExceptionMap map[string]struct{} bidder openrtb_ext.BidderName consent string allowSync bool }{ { description: "Sync blocked by no consent - p1 enabled, no p1 vendor exception, pub restricts none", - p1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + p1VendorExceptionMap: map[string]struct{}{}, bidder: openrtb_ext.BidderAppnexus, consent: noPurposeOrVendorConsentAndPubRestrictsNone, allowSync: false, }, { description: "Sync blocked by publisher - p1 enabled with p1 vendor exception, pub restricts p1 for vendor", - p1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + p1VendorExceptionMap: map[string]struct{}{appnexus: {}}, bidder: openrtb_ext.BidderAppnexus, consent: noPurposeOrVendorConsentAndPubRestrictsP1, allowSync: false, }, { description: "Sync allowed by vendor exception - p1 enabled with p1 vendor exception, pub restricts none", - p1VendorExceptionMap: map[openrtb_ext.BidderName]struct{}{openrtb_ext.BidderAppnexus: {}}, + p1VendorExceptionMap: map[string]struct{}{appnexus: {}}, bidder: openrtb_ext.BidderAppnexus, consent: noPurposeOrVendorConsentAndPubRestrictsNone, allowSync: true, diff --git a/gdpr/purpose_config.go b/gdpr/purpose_config.go index 015f23269ef..ff3a92300ff 100644 --- a/gdpr/purpose_config.go +++ b/gdpr/purpose_config.go @@ -2,8 +2,7 @@ package gdpr import ( "github.com/prebid/go-gdpr/consentconstants" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" ) // purposeConfig represents all of the config info selected from the host and account configs for @@ -13,26 +12,26 @@ type purposeConfig struct { EnforceAlgo config.TCF2EnforcementAlgo EnforcePurpose bool EnforceVendors bool - VendorExceptionMap map[openrtb_ext.BidderName]struct{} + VendorExceptionMap map[string]struct{} BasicEnforcementVendorsMap map[string]struct{} } -// basicEnforcementVendor returns true if a given bidder is configured as a basic enforcement vendor +// basicEnforcementVendor returns true if a given bidder/analytics adapter is configured as a basic enforcement vendor // for the purpose -func (pc *purposeConfig) basicEnforcementVendor(bidder openrtb_ext.BidderName) bool { +func (pc *purposeConfig) basicEnforcementVendor(name string) bool { if pc.BasicEnforcementVendorsMap == nil { return false } - _, found := pc.BasicEnforcementVendorsMap[string(bidder)] + _, found := pc.BasicEnforcementVendorsMap[name] return found } -// vendorException returns true if a given bidder is configured as a vendor exception +// vendorException returns true if a given bidder/analytics adapter is configured as a vendor exception // for the purpose -func (pc *purposeConfig) vendorException(bidder openrtb_ext.BidderName) bool { +func (pc *purposeConfig) vendorException(name string) bool { if pc.VendorExceptionMap == nil { return false } - _, found := pc.VendorExceptionMap[bidder] + _, found := pc.VendorExceptionMap[name] return found } diff --git a/gdpr/purpose_config_test.go b/gdpr/purpose_config_test.go index e80733cc8ca..c2bd6f56d8f 100644 --- a/gdpr/purpose_config_test.go +++ b/gdpr/purpose_config_test.go @@ -3,65 +3,72 @@ package gdpr import ( "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) func TestPurposeConfigBasicEnforcementVendor(t *testing.T) { + var ( + appnexus = string(openrtb_ext.BidderAppnexus) + ix = string(openrtb_ext.BidderIx) + pubmatic = string(openrtb_ext.BidderPubmatic) + rubicon = string(openrtb_ext.BidderRubicon) + ) + tests := []struct { description string giveBasicVendors map[string]struct{} - giveBidder openrtb_ext.BidderName + giveBidder string wantFound bool }{ { description: "vendor map is nil", giveBasicVendors: nil, - giveBidder: openrtb_ext.BidderAppnexus, + giveBidder: appnexus, wantFound: false, }, { description: "vendor map is empty", giveBasicVendors: map[string]struct{}{}, - giveBidder: openrtb_ext.BidderAppnexus, + giveBidder: appnexus, wantFound: false, }, { description: "vendor map has one bidders - bidder not found", giveBasicVendors: map[string]struct{}{ - string(openrtb_ext.BidderPubmatic): {}, + pubmatic: {}, }, - giveBidder: openrtb_ext.BidderAppnexus, + giveBidder: appnexus, wantFound: false, }, { description: "vendor map has one bidders - bidder found", giveBasicVendors: map[string]struct{}{ - string(openrtb_ext.BidderAppnexus): {}, + appnexus: {}, }, - giveBidder: openrtb_ext.BidderAppnexus, + giveBidder: appnexus, wantFound: true, }, { description: "vendor map has many bidderss - bidder not found", giveBasicVendors: map[string]struct{}{ - string(openrtb_ext.BidderIx): {}, - string(openrtb_ext.BidderPubmatic): {}, - string(openrtb_ext.BidderRubicon): {}, + ix: {}, + pubmatic: {}, + rubicon: {}, }, - giveBidder: openrtb_ext.BidderAppnexus, + giveBidder: appnexus, wantFound: false, }, { description: "vendor map has many bidderss - bidder found", giveBasicVendors: map[string]struct{}{ - string(openrtb_ext.BidderIx): {}, - string(openrtb_ext.BidderPubmatic): {}, - string(openrtb_ext.BidderAppnexus): {}, - string(openrtb_ext.BidderRubicon): {}, + ix: {}, + pubmatic: {}, + appnexus: {}, + rubicon: {}, }, - giveBidder: openrtb_ext.BidderAppnexus, + giveBidder: appnexus, wantFound: true, }, } @@ -77,59 +84,66 @@ func TestPurposeConfigBasicEnforcementVendor(t *testing.T) { } func TestPurposeConfigVendorException(t *testing.T) { + var ( + appnexus = string(openrtb_ext.BidderAppnexus) + ix = string(openrtb_ext.BidderIx) + pubmatic = string(openrtb_ext.BidderPubmatic) + rubicon = string(openrtb_ext.BidderRubicon) + ) + tests := []struct { description string - giveExceptions map[openrtb_ext.BidderName]struct{} - giveBidder openrtb_ext.BidderName + giveExceptions map[string]struct{} + giveBidder string wantFound bool }{ { description: "vendor exception map is nil", giveExceptions: nil, - giveBidder: openrtb_ext.BidderAppnexus, + giveBidder: appnexus, wantFound: false, }, { description: "vendor exception map is empty", - giveExceptions: map[openrtb_ext.BidderName]struct{}{}, - giveBidder: openrtb_ext.BidderAppnexus, + giveExceptions: map[string]struct{}{}, + giveBidder: appnexus, wantFound: false, }, { description: "vendor exception map has one bidders - bidder not found", - giveExceptions: map[openrtb_ext.BidderName]struct{}{ - openrtb_ext.BidderPubmatic: {}, + giveExceptions: map[string]struct{}{ + pubmatic: {}, }, - giveBidder: openrtb_ext.BidderAppnexus, + giveBidder: appnexus, wantFound: false, }, { description: "vendor exception map has one bidders - bidder found", - giveExceptions: map[openrtb_ext.BidderName]struct{}{ - openrtb_ext.BidderAppnexus: {}, + giveExceptions: map[string]struct{}{ + appnexus: {}, }, - giveBidder: openrtb_ext.BidderAppnexus, + giveBidder: appnexus, wantFound: true, }, { description: "vendor exception map has many bidderss - bidder not found", - giveExceptions: map[openrtb_ext.BidderName]struct{}{ - openrtb_ext.BidderIx: {}, - openrtb_ext.BidderPubmatic: {}, - openrtb_ext.BidderRubicon: {}, + giveExceptions: map[string]struct{}{ + ix: {}, + pubmatic: {}, + rubicon: {}, }, - giveBidder: openrtb_ext.BidderAppnexus, + giveBidder: appnexus, wantFound: false, }, { description: "vendor exception map has many bidderss - bidder found", - giveExceptions: map[openrtb_ext.BidderName]struct{}{ - openrtb_ext.BidderIx: {}, - openrtb_ext.BidderPubmatic: {}, - openrtb_ext.BidderAppnexus: {}, - openrtb_ext.BidderRubicon: {}, + giveExceptions: map[string]struct{}{ + ix: {}, + pubmatic: {}, + appnexus: {}, + rubicon: {}, }, - giveBidder: openrtb_ext.BidderAppnexus, + giveBidder: appnexus, wantFound: true, }, } diff --git a/gdpr/purpose_enforcer.go b/gdpr/purpose_enforcer.go index c8e76f988aa..ceef0e5f561 100644 --- a/gdpr/purpose_enforcer.go +++ b/gdpr/purpose_enforcer.go @@ -4,17 +4,17 @@ import ( "github.com/prebid/go-gdpr/api" "github.com/prebid/go-gdpr/consentconstants" tcf2 "github.com/prebid/go-gdpr/vendorconsent/tcf2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // PurposeEnforcer represents the enforcement strategy for determining if legal basis is achieved for a purpose type PurposeEnforcer interface { - LegalBasis(vendorInfo VendorInfo, bidder openrtb_ext.BidderName, consent tcf2.ConsentMetadata, overrides Overrides) bool + LegalBasis(vendorInfo VendorInfo, name string, consent tcf2.ConsentMetadata, overrides Overrides) bool } // PurposeEnforcerBuilder generates an instance of PurposeEnforcer for a given purpose and bidder -type PurposeEnforcerBuilder func(p consentconstants.Purpose, bidder openrtb_ext.BidderName) PurposeEnforcer +type PurposeEnforcerBuilder func(p consentconstants.Purpose, name string) PurposeEnforcer // Overrides specifies enforcement algorithm rule adjustments type Overrides struct { @@ -45,7 +45,7 @@ type PurposeEnforcers struct { func NewPurposeEnforcerBuilder(cfg TCF2ConfigReader) PurposeEnforcerBuilder { cachedEnforcers := make([]PurposeEnforcers, 10) - return func(purpose consentconstants.Purpose, bidder openrtb_ext.BidderName) PurposeEnforcer { + return func(purpose consentconstants.Purpose, name string) PurposeEnforcer { index := purpose - 1 var basicEnforcementVendor bool @@ -53,7 +53,7 @@ func NewPurposeEnforcerBuilder(cfg TCF2ConfigReader) PurposeEnforcerBuilder { basicEnforcementVendor = false } else { basicEnforcementVendors := cfg.BasicEnforcementVendors() - _, basicEnforcementVendor = basicEnforcementVendors[string(bidder)] + _, basicEnforcementVendor = basicEnforcementVendors[name] } enforceAlgo := cfg.PurposeEnforcementAlgo(purpose) diff --git a/gdpr/purpose_enforcer_test.go b/gdpr/purpose_enforcer_test.go index ea2075d9c65..fa0605702a0 100644 --- a/gdpr/purpose_enforcer_test.go +++ b/gdpr/purpose_enforcer_test.go @@ -4,22 +4,24 @@ import ( "testing" "github.com/prebid/go-gdpr/consentconstants" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) func TestNewPurposeEnforcerBuilder(t *testing.T) { + appnexus := string(openrtb_ext.BidderAppnexus) + tests := []struct { description string enforceAlgo config.TCF2EnforcementAlgo enforcePurpose bool enforceVendors bool basicVendorsMap map[string]struct{} - vendorExceptionMap map[openrtb_ext.BidderName]struct{} + vendorExceptionMap map[string]struct{} purpose consentconstants.Purpose - bidder openrtb_ext.BidderName + bidder string wantType PurposeEnforcer }{ { @@ -28,9 +30,9 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) { enforcePurpose: true, enforceVendors: true, basicVendorsMap: map[string]struct{}{}, - vendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + vendorExceptionMap: map[string]struct{}{}, purpose: consentconstants.Purpose(1), - bidder: openrtb_ext.BidderAppnexus, + bidder: appnexus, wantType: &FullEnforcement{}, }, { @@ -38,10 +40,10 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) { enforceAlgo: config.TCF2FullEnforcement, enforcePurpose: true, enforceVendors: true, - basicVendorsMap: map[string]struct{}{string(openrtb_ext.BidderAppnexus): {}}, - vendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + basicVendorsMap: map[string]struct{}{appnexus: {}}, + vendorExceptionMap: map[string]struct{}{}, purpose: consentconstants.Purpose(1), - bidder: openrtb_ext.BidderAppnexus, + bidder: appnexus, wantType: &FullEnforcement{}, }, { @@ -50,9 +52,9 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) { enforcePurpose: true, enforceVendors: true, basicVendorsMap: map[string]struct{}{}, - vendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + vendorExceptionMap: map[string]struct{}{}, purpose: consentconstants.Purpose(1), - bidder: openrtb_ext.BidderAppnexus, + bidder: appnexus, wantType: &BasicEnforcement{}, }, { @@ -61,9 +63,9 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) { enforcePurpose: true, enforceVendors: true, basicVendorsMap: map[string]struct{}{}, - vendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + vendorExceptionMap: map[string]struct{}{}, purpose: consentconstants.Purpose(2), - bidder: openrtb_ext.BidderAppnexus, + bidder: appnexus, wantType: &FullEnforcement{}, }, { @@ -71,10 +73,10 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) { enforceAlgo: config.TCF2FullEnforcement, enforcePurpose: true, enforceVendors: true, - basicVendorsMap: map[string]struct{}{string(openrtb_ext.BidderAppnexus): {}}, - vendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + basicVendorsMap: map[string]struct{}{appnexus: {}}, + vendorExceptionMap: map[string]struct{}{}, purpose: consentconstants.Purpose(2), - bidder: openrtb_ext.BidderAppnexus, + bidder: appnexus, wantType: &BasicEnforcement{}, }, { @@ -83,9 +85,9 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) { enforcePurpose: true, enforceVendors: true, basicVendorsMap: map[string]struct{}{}, - vendorExceptionMap: map[openrtb_ext.BidderName]struct{}{}, + vendorExceptionMap: map[string]struct{}{}, purpose: consentconstants.Purpose(2), - bidder: openrtb_ext.BidderAppnexus, + bidder: appnexus, wantType: &BasicEnforcement{}, }, } @@ -136,13 +138,13 @@ func TestNewPurposeEnforcerBuilder(t *testing.T) { func TestNewPurposeEnforcerBuilderCaching(t *testing.T) { - bidder1 := openrtb_ext.BidderAppnexus + bidder1 := string(openrtb_ext.BidderAppnexus) bidder1Enforcers := make([]PurposeEnforcer, 11) - bidder2 := openrtb_ext.BidderIx + bidder2 := string(openrtb_ext.BidderIx) bidder2Enforcers := make([]PurposeEnforcer, 11) - bidder3 := openrtb_ext.BidderPubmatic + bidder3 := string(openrtb_ext.BidderPubmatic) bidder3Enforcers := make([]PurposeEnforcer, 11) - bidder4 := openrtb_ext.BidderRubicon + bidder4 := string(openrtb_ext.BidderRubicon) bidder4Enforcers := make([]PurposeEnforcer, 11) cfg := fakeTCF2ConfigReader{ @@ -198,7 +200,7 @@ type fakeTCF2ConfigReader struct { enforceAlgo config.TCF2EnforcementAlgo enforcePurpose bool enforceVendors bool - vendorExceptionMap map[openrtb_ext.BidderName]struct{} + vendorExceptionMap map[string]struct{} basicEnforcementVendorsMap map[string]struct{} } @@ -226,7 +228,7 @@ func (fcr *fakeTCF2ConfigReader) PurposeEnforcementAlgo(purpose consentconstants func (fcr *fakeTCF2ConfigReader) PurposeEnforcingVendors(purpose consentconstants.Purpose) bool { return fcr.enforceVendors } -func (fcr *fakeTCF2ConfigReader) PurposeVendorExceptions(purpose consentconstants.Purpose) map[openrtb_ext.BidderName]struct{} { +func (fcr *fakeTCF2ConfigReader) PurposeVendorExceptions(purpose consentconstants.Purpose) map[string]struct{} { return fcr.vendorExceptionMap } func (fcr *fakeTCF2ConfigReader) PurposeOneTreatmentEnabled() bool { diff --git a/gdpr/signal.go b/gdpr/signal.go index ed7fe1dd8ea..3d2e4de1251 100644 --- a/gdpr/signal.go +++ b/gdpr/signal.go @@ -3,7 +3,7 @@ package gdpr import ( "strconv" - "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/v2/errortypes" ) type Signal int diff --git a/gdpr/vendorlist-fetching.go b/gdpr/vendorlist-fetching.go index bcd2a4d9fc6..4c1c8735d6c 100644 --- a/gdpr/vendorlist-fetching.go +++ b/gdpr/vendorlist-fetching.go @@ -14,7 +14,7 @@ import ( "github.com/prebid/go-gdpr/api" "github.com/prebid/go-gdpr/vendorlist" "github.com/prebid/go-gdpr/vendorlist2" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" "golang.org/x/net/context/ctxhttp" ) diff --git a/gdpr/vendorlist-fetching_test.go b/gdpr/vendorlist-fetching_test.go index a1dfb7fefb8..98dc4ba5aa3 100644 --- a/gdpr/vendorlist-fetching_test.go +++ b/gdpr/vendorlist-fetching_test.go @@ -2,7 +2,6 @@ package gdpr import ( "context" - "encoding/json" "net/http" "net/http/httptest" "strconv" @@ -12,7 +11,8 @@ import ( "github.com/prebid/go-gdpr/api" "github.com/prebid/go-gdpr/consentconstants" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) func TestFetcherDynamicLoadListExists(t *testing.T) { @@ -305,7 +305,7 @@ type vendor struct { } func MarshalVendorList(vendorList vendorList) string { - json, _ := json.Marshal(vendorList) + json, _ := jsonutil.Marshal(vendorList) return string(json) } diff --git a/go.mod b/go.mod index ea7cd0c7833..1d04b994a2c 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/PubMatic-OpenWrap/prebid-server +module github.com/PubMatic-OpenWrap/prebid-server/v2 go 1.20 @@ -8,7 +8,7 @@ require ( github.com/DATA-DOG/go-sqlmock v1.5.0 github.com/IABTechLab/adscert v0.34.0 github.com/NYTimes/gziphandler v1.1.1 - github.com/alitto/pond v1.8.2 + github.com/alitto/pond v1.8.3 github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d github.com/beevik/etree v1.0.2 github.com/benbjohnson/clock v1.3.0 @@ -17,7 +17,8 @@ require ( github.com/coocood/freecache v1.2.1 github.com/docker/go-units v0.4.0 github.com/gofrs/uuid v4.2.0+incompatible - github.com/golang/glog v1.0.0 + github.com/golang/glog v1.1.0 + github.com/json-iterator/go v1.1.12 github.com/julienschmidt/httprouter v1.3.0 github.com/lib/pq v1.10.4 github.com/magiconair/properties v1.8.7 @@ -25,9 +26,9 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 github.com/prebid/go-gdpr v1.12.0 - github.com/prebid/go-gpp v0.1.1 - github.com/prebid/openrtb/v19 v19.0.0 - github.com/prebid/prebid-server v0.0.0-00010101000000-000000000000 + github.com/prebid/go-gpp v0.2.0 + github.com/prebid/openrtb/v20 v20.1.0 + github.com/prebid/prebid-server/v2 v2.10.0 github.com/prometheus/client_golang v1.12.1 github.com/prometheus/client_model v0.2.0 github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 @@ -38,10 +39,9 @@ require ( github.com/vrischmann/go-metrics-influxdb v0.1.1 github.com/xeipuuv/gojsonschema v1.2.0 github.com/yudai/gojsondiff v1.0.0 - github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect - golang.org/x/net v0.7.0 - golang.org/x/text v0.7.0 - google.golang.org/grpc v1.53.0 + golang.org/x/net v0.17.0 + golang.org/x/text v0.14.0 + google.golang.org/grpc v1.56.3 gopkg.in/evanphx/json-patch.v4 v4.12.0 gopkg.in/yaml.v3 v3.0.1 ) @@ -50,6 +50,7 @@ require ( git.pubmatic.com/vastunwrap v0.0.0-00010101000000-000000000000 github.com/go-sql-driver/mysql v1.7.1 github.com/golang/mock v1.6.0 + github.com/modern-go/reflect2 v1.0.2 github.com/satori/go.uuid v1.2.0 golang.org/x/exp v0.0.0-20231108232855-2478ac86f678 ) @@ -61,13 +62,13 @@ require ( github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/pelletier/go-toml/v2 v2.0.6 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/common v0.32.1 // indirect @@ -80,17 +81,17 @@ require ( github.com/subosito/gotenv v1.4.2 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e // indirect - golang.org/x/sys v0.14.0 // indirect - google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect - google.golang.org/protobuf v1.28.1 // indirect + github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/sys v0.15.0 // indirect + google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect + google.golang.org/protobuf v1.30.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect ) -replace github.com/prebid/prebid-server => ./ +replace github.com/prebid/prebid-server/v2 => ./ -replace github.com/prebid/openrtb/v19 => github.com/PubMatic-OpenWrap/prebid-openrtb/v19 v19.0.0-20230517094918-56ce40c97ced +replace github.com/prebid/openrtb/v20 => github.com/PubMatic-OpenWrap/prebid-openrtb/v20 v20.0.0-20240222072752-2d647d1707ef replace github.com/beevik/etree v1.0.2 => github.com/PubMatic-OpenWrap/etree v1.0.2-0.20210129100623-8f30cfecf9f4 diff --git a/go.sum b/go.sum index 4cd4a74e925..b0558e61c90 100644 --- a/go.sum +++ b/go.sum @@ -51,12 +51,6 @@ cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3f dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= git.pubmatic.com/PubMatic/go-common v0.0.0-20240313090142-97ff3d63b7c3 h1:Ea8zwi1eeX4kqvi9RyyXrizIIRcstM0XBwf8U7NHrno= git.pubmatic.com/PubMatic/go-common v0.0.0-20240313090142-97ff3d63b7c3/go.mod h1:c/I6IcDn4Mtq4mmw8wGJN3v0o10nIMX7VTuQnsalUw0= -git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20240215114830-026b72bf5a2f h1:nCrSziQxM2ITd9w71lhqAhIyZthZvvBn78aYvdQmt8s= -git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20240215114830-026b72bf5a2f/go.mod h1:dgTumQ6/KYeLbpWq3HVOaqkZos6Q0QGwZmQmEIhQ3To= -git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20240314073537-3d509a39c7d3 h1:MRuY2vSVOLeO1QdSMI+m9I06l7jKwOV5pI+ntEMatp8= -git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20240314073537-3d509a39c7d3/go.mod h1:kcoJf7k+xug8X8fLWmsiKhPnYP+k7RZkfUoUo5QF+KA= -git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20240314123642-8a380e78e281 h1:9orT+Dljy2DRGWZvLqK96HObhfsVNedRfGHv+h/agH4= -git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20240314123642-8a380e78e281/go.mod h1:kcoJf7k+xug8X8fLWmsiKhPnYP+k7RZkfUoUo5QF+KA= git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20240319050712-0b288cbb5a5d h1:BgLUpJQ9Z89eDGz//voK74G/8FgjgVg2PWVbjgCJ4+A= git.pubmatic.com/PubMatic/vastunwrap v0.0.0-20240319050712-0b288cbb5a5d/go.mod h1:kcoJf7k+xug8X8fLWmsiKhPnYP+k7RZkfUoUo5QF+KA= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= @@ -71,15 +65,15 @@ github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMo github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/PubMatic-OpenWrap/etree v1.0.2-0.20210129100623-8f30cfecf9f4 h1:EhiijwjoKTx7FVP8p2wwC/z4n5l4c8l2CGmsrFv2uhI= github.com/PubMatic-OpenWrap/etree v1.0.2-0.20210129100623-8f30cfecf9f4/go.mod h1:5Y8qgcuDoy3XXG907UXkGnVTwihF16rXyJa4zRT7hOE= -github.com/PubMatic-OpenWrap/prebid-openrtb/v19 v19.0.0-20230517094918-56ce40c97ced h1:a4dslMnlBKJTTgBuKKKPT4V43/cespgaVd1y0TO0b4M= -github.com/PubMatic-OpenWrap/prebid-openrtb/v19 v19.0.0-20230517094918-56ce40c97ced/go.mod h1:jK+/g4Dh5vOnNl0Nh7isbZlub29aJYyrtoBkjmhzTIg= +github.com/PubMatic-OpenWrap/prebid-openrtb/v20 v20.0.0-20240222072752-2d647d1707ef h1:CXsyYtEgZz/0++fiug6QZXrRYG6BBrl9IGveCxsnhiE= +github.com/PubMatic-OpenWrap/prebid-openrtb/v20 v20.0.0-20240222072752-2d647d1707ef/go.mod h1:hLBrA/APkSrxs5MaW639l+y/EAHivDfRagO2TX/wbSc= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alitto/pond v1.8.2 h1:k0k3GIE7CFLW/kyMJj5DDKLFg1VH09l8skZqg/yJNng= -github.com/alitto/pond v1.8.2/go.mod h1:CmvIIGd5jKLasGI3D87qDkQxjzChdKMmnXMg3fG6M6Q= +github.com/alitto/pond v1.8.3 h1:ydIqygCLVPqIX/USe5EaV/aSRXTRXDEI9JwuDdu+/xs= +github.com/alitto/pond v1.8.3/go.mod h1:CmvIIGd5jKLasGI3D87qDkQxjzChdKMmnXMg3fG6M6Q= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= @@ -154,8 +148,6 @@ github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -169,8 +161,6 @@ github.com/go-ldap/ldap v3.0.2+incompatible/go.mod h1:qfd9rJvER9Q0/D/Sqn1DfHRoBp github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= -github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -182,8 +172,8 @@ github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRx github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ= -github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -214,8 +204,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -319,6 +310,7 @@ github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCV github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= @@ -341,8 +333,6 @@ github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= @@ -381,9 +371,11 @@ github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -404,10 +396,6 @@ github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144T github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.1 h1:8e3L2cCQzLFi2CR4g7vGFuFxX7Jl1kKX8gW+iV0GUKU= -github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= @@ -423,8 +411,8 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= github.com/prebid/go-gdpr v1.12.0 h1:OrjQ7Uc+lCRYaOirQ48jjG/PBMvZsKNAaRTgzxN6iZ0= github.com/prebid/go-gdpr v1.12.0/go.mod h1:mPZAdkRxn+iuSjaUuJAi9+0SppBOdM1PCzv/55UH3pY= -github.com/prebid/go-gpp v0.1.1 h1:uTMJ+eHmKWL9WvDuxFT4LDoOeJW1yOsfWITqi49ZuY0= -github.com/prebid/go-gpp v0.1.1/go.mod h1:b0TLoVln+HXFD9L9xeimxIH3FN8WDKPJ42auslxEkow= +github.com/prebid/go-gpp v0.2.0 h1:41Ssxd4Zxr50WgwG1q/1+6awGU3pFnwV7FR4XCLQSuM= +github.com/prebid/go-gpp v0.2.0/go.mod h1:b0TLoVln+HXFD9L9xeimxIH3FN8WDKPJ42auslxEkow= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= @@ -474,8 +462,6 @@ github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrf github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo= -github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo= github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.4.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= @@ -487,8 +473,6 @@ github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0 github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= -github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ= -github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= @@ -504,13 +488,10 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI= -github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= @@ -560,10 +541,8 @@ golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 h1:kUhD7nTDoI3fVd9G4ORWrbV5NY0liEs/Jg2pv5f+bBA= -golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -650,8 +629,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -761,10 +740,9 @@ golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211205182925-97ca703d548d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -777,8 +755,8 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -949,8 +927,8 @@ google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= -google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 h1:KpwkzHKEF7B9Zxg18WzOa7djJ+Ha5DzthMyZYQfEn2A= +google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU= google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= @@ -981,8 +959,8 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= -google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc v1.56.3 h1:8I4C0Yq1EjstUzUJzpcRVbuYA2mODtEmpWiQoN/b2nc= +google.golang.org/grpc v1.56.3/go.mod h1:I9bI3vqKfayGqPUAwGdOSu7kt6oIJLixfffKrpXqQ9s= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -997,8 +975,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= +google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUyUwEgHQXw849cJrilpS5NeIjOWESAw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -1010,8 +988,6 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4= -gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= diff --git a/hooks/empty_plan.go b/hooks/empty_plan.go index 15aa7e6970f..e8ae505878a 100644 --- a/hooks/empty_plan.go +++ b/hooks/empty_plan.go @@ -1,8 +1,8 @@ package hooks import ( - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks/hookstage" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks/hookstage" ) // EmptyPlanBuilder implements the ExecutionPlanBuilder interface diff --git a/hooks/hookanalytics/analytics_test.go b/hooks/hookanalytics/analytics_test.go index 177a9335da9..27584cf0d39 100644 --- a/hooks/hookanalytics/analytics_test.go +++ b/hooks/hookanalytics/analytics_test.go @@ -1,9 +1,9 @@ package hookanalytics import ( - "encoding/json" "testing" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" ) @@ -50,7 +50,7 @@ func TestAnalytics(t *testing.T) { Activity{Name: "define-blocks", Status: ActivityStatusError}, ) - gotAnalytics, err := json.Marshal(analytics) + gotAnalytics, err := jsonutil.Marshal(analytics) assert.NoError(t, err, "Failed to marshal analytics: %s", err) assert.JSONEq(t, string(expectedAnalytics), string(gotAnalytics)) } diff --git a/hooks/hookexecution/context.go b/hooks/hookexecution/context.go index 8c3b3b10b3a..350544472b5 100644 --- a/hooks/hookexecution/context.go +++ b/hooks/hookexecution/context.go @@ -4,17 +4,19 @@ import ( "sync" "github.com/golang/glog" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks/hookstage" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/privacy" ) // executionContext holds information passed to module's hook during hook execution. type executionContext struct { - endpoint string - stage string - accountId string - account *config.Account - moduleContexts *moduleContexts + endpoint string + stage string + accountID string + account *config.Account + moduleContexts *moduleContexts + activityControl privacy.ActivityControl } func (ctx executionContext) getModuleContext(moduleName string) hookstage.ModuleInvocationContext { diff --git a/hooks/hookexecution/enricher.go b/hooks/hookexecution/enricher.go index 2978c21957d..3617a528660 100644 --- a/hooks/hookexecution/enricher.go +++ b/hooks/hookexecution/enricher.go @@ -4,8 +4,9 @@ import ( "encoding/json" "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/util/jsonutil" jsonpatch "gopkg.in/evanphx/json-patch.v4" ) @@ -52,7 +53,7 @@ func EnrichExtBidResponse( return ext, warnings, err } - response, err := json.Marshal(extPrebid{Prebid: extModules{Modules: modules}}) + response, err := jsonutil.Marshal(extPrebid{Prebid: extModules{Modules: modules}}) if err != nil { return ext, warnings, err } @@ -83,7 +84,7 @@ func GetModulesJSON( return nil, warnings, nil } - data, err := json.Marshal(modulesOutcome) + data, err := jsonutil.Marshal(modulesOutcome) return data, warnings, err } diff --git a/hooks/hookexecution/enricher_test.go b/hooks/hookexecution/enricher_test.go index 7bb19c2ecf2..4f962dea279 100644 --- a/hooks/hookexecution/enricher_test.go +++ b/hooks/hookexecution/enricher_test.go @@ -6,10 +6,11 @@ import ( "os" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks/hookanalytics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -258,7 +259,7 @@ func TestGetModulesJSON(t *testing.T) { assert.Empty(t, expectedResponse) } else { var expectedExtBidResponse openrtb_ext.ExtBidResponse - err := json.Unmarshal(expectedResponse, &expectedExtBidResponse) + err := jsonutil.UnmarshalValid(expectedResponse, &expectedExtBidResponse) assert.NoError(t, err, "Failed to unmarshal prebid response extension") assert.JSONEq(t, string(expectedExtBidResponse.Prebid.Modules), string(modules)) } @@ -271,7 +272,7 @@ func getStageOutcomes(t *testing.T, file string) []StageOutcome { var stageOutcomesTest []StageOutcomeTest data := readFile(t, file) - err := json.Unmarshal(data, &stageOutcomesTest) + err := jsonutil.UnmarshalValid(data, &stageOutcomesTest) require.NoError(t, err, "Failed to unmarshal stage outcomes: %s", err) for _, stageT := range stageOutcomesTest { diff --git a/hooks/hookexecution/errors.go b/hooks/hookexecution/errors.go index b1cf912ccee..1d016e26019 100644 --- a/hooks/hookexecution/errors.go +++ b/hooks/hookexecution/errors.go @@ -3,7 +3,7 @@ package hookexecution import ( "fmt" - "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/v2/errortypes" ) // TimeoutError indicates exceeding of the max execution time allotted for hook. diff --git a/hooks/hookexecution/execution.go b/hooks/hookexecution/execution.go index 18c927896b9..03500bb5f0e 100644 --- a/hooks/hookexecution/execution.go +++ b/hooks/hookexecution/execution.go @@ -7,9 +7,14 @@ import ( "sync" "time" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/metrics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/ortb" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/util/iputil" ) type hookResponse[T any] struct { @@ -66,10 +71,11 @@ func executeGroup[H any, P any]( for _, hook := range group.Hooks { mCtx := executionCtx.getModuleContext(hook.Module) + newPayload := handleModuleActivities(hook.Code, executionCtx.activityControl, payload, executionCtx.account) wg.Add(1) go func(hw hooks.HookWrapper[H], moduleCtx hookstage.ModuleInvocationContext) { defer wg.Done() - executeHook(moduleCtx, hw, payload, hookHandler, group.Timeout, resp, rejected) + executeHook(moduleCtx, hw, newPayload, hookHandler, group.Timeout, resp, rejected) }(hook, mCtx) } @@ -176,7 +182,7 @@ func handleHookResponse[P any]( metricEngine metrics.MetricsEngine, ) (P, HookOutcome, *RejectError) { var rejectErr *RejectError - labels := metrics.ModuleLabels{Module: moduleReplacer.Replace(hr.HookID.ModuleCode), Stage: ctx.stage, AccountID: ctx.accountId} + labels := metrics.ModuleLabels{Module: moduleReplacer.Replace(hr.HookID.ModuleCode), Stage: ctx.stage, AccountID: ctx.accountID} metricEngine.RecordModuleCalled(labels, hr.ExecutionTime) hookOutcome := HookOutcome{ @@ -311,3 +317,47 @@ func handleHookMutations[P any]( return payload } + +func handleModuleActivities[P any](hookCode string, activityControl privacy.ActivityControl, payload P, account *config.Account) P { + payloadData, ok := any(&payload).(hookstage.RequestUpdater) + if !ok { + return payload + } + + scopeGeneral := privacy.Component{Type: privacy.ComponentTypeGeneral, Name: hookCode} + transmitUserFPDActivityAllowed := activityControl.Allow(privacy.ActivityTransmitUserFPD, scopeGeneral, privacy.ActivityRequest{}) + transmitPreciseGeoActivityAllowed := activityControl.Allow(privacy.ActivityTransmitPreciseGeo, scopeGeneral, privacy.ActivityRequest{}) + + if transmitUserFPDActivityAllowed && transmitPreciseGeoActivityAllowed { + return payload + } + + // changes need to be applied to new payload and leave original payload unchanged + bidderReq := payloadData.GetBidderRequestPayload() + + bidderReqCopy := &openrtb_ext.RequestWrapper{ + BidRequest: ortb.CloneBidRequestPartial(bidderReq.BidRequest), + } + + if !transmitUserFPDActivityAllowed { + privacy.ScrubUserFPD(bidderReqCopy) + } + if !transmitPreciseGeoActivityAllowed { + ipConf := privacy.IPConf{} + if account != nil { + ipConf = privacy.IPConf{IPV6: account.Privacy.IPv6Config, IPV4: account.Privacy.IPv4Config} + } else { + ipConf = privacy.IPConf{ + IPV6: config.IPv6{AnonKeepBits: iputil.IPv6DefaultMaskingBitSize}, + IPV4: config.IPv4{AnonKeepBits: iputil.IPv4DefaultMaskingBitSize}} + } + + privacy.ScrubGeoAndDeviceIP(bidderReqCopy, ipConf) + } + + var newPayload = payload + var np = any(&newPayload).(hookstage.RequestUpdater) + np.SetBidderRequestPayload(bidderReqCopy) + return newPayload + +} diff --git a/hooks/hookexecution/execution_test.go b/hooks/hookexecution/execution_test.go new file mode 100644 index 00000000000..33f652faa2f --- /dev/null +++ b/hooks/hookexecution/execution_test.go @@ -0,0 +1,235 @@ +package hookexecution + +import ( + "testing" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/stretchr/testify/assert" +) + +const ( + testIpv6 = "1111:2222:3333:4444:5555:6666:7777:8888" + testIPv6Scrubbed = "1111:2222::" + testIPv6ScrubbedDefault = "1111:2222:3333:4400::" + testIPv6ScrubBytes = 32 +) + +func TestHandleModuleActivitiesBidderRequestPayload(t *testing.T) { + + testCases := []struct { + description string + hookCode string + privacyConfig *config.AccountPrivacy + inPayloadData hookstage.BidderRequestPayload + expectedPayloadData hookstage.BidderRequestPayload + }{ + { + description: "payload should change when userFPD is blocked by activity", + hookCode: "foo", + inPayloadData: hookstage.BidderRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + User: &openrtb2.User{ID: "test_user_id"}, + }}, + }, + privacyConfig: getTransmitUFPDActivityConfig("foo", false), + expectedPayloadData: hookstage.BidderRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + User: &openrtb2.User{ID: ""}, + }, + }}, + }, + { + description: "payload should not change when userFPD is not blocked by activity", + hookCode: "foo", + inPayloadData: hookstage.BidderRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + User: &openrtb2.User{ID: "test_user_id"}, + }}, + }, + privacyConfig: getTransmitUFPDActivityConfig("foo", true), + expectedPayloadData: hookstage.BidderRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + User: &openrtb2.User{ID: "test_user_id"}, + }}, + }, + }, + { + description: "payload should change when transmitPreciseGeo is blocked by activity", + hookCode: "foo", + inPayloadData: hookstage.BidderRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{IPv6: testIpv6}, + }}, + }, + privacyConfig: getTransmitPreciseGeoActivityConfig("foo", false), + expectedPayloadData: hookstage.BidderRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{IPv6: testIPv6ScrubbedDefault}, + }, + }}, + }, + { + description: "payload should not change when transmitPreciseGeo is not blocked by activity", + hookCode: "foo", + inPayloadData: hookstage.BidderRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{IPv6: testIpv6}, + }}, + }, + privacyConfig: getTransmitPreciseGeoActivityConfig("foo", true), + expectedPayloadData: hookstage.BidderRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{IPv6: testIpv6}, + }}, + }, + }, + } + for _, test := range testCases { + t.Run(test.description, func(t *testing.T) { + //check input payload didn't change + origInPayloadData := test.inPayloadData + activityControl := privacy.NewActivityControl(test.privacyConfig) + newPayload := handleModuleActivities(test.hookCode, activityControl, test.inPayloadData, nil) + assert.Equal(t, test.expectedPayloadData.Request.BidRequest, newPayload.Request.BidRequest) + assert.Equal(t, origInPayloadData, test.inPayloadData) + }) + } +} + +func TestHandleModuleActivitiesProcessedAuctionRequestPayload(t *testing.T) { + + testCases := []struct { + description string + hookCode string + privacyConfig *config.AccountPrivacy + inPayloadData hookstage.ProcessedAuctionRequestPayload + expectedPayloadData hookstage.ProcessedAuctionRequestPayload + }{ + { + description: "payload should change when userFPD is blocked by activity", + hookCode: "foo", + inPayloadData: hookstage.ProcessedAuctionRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + User: &openrtb2.User{ID: "test_user_id"}, + }}, + }, + privacyConfig: getTransmitUFPDActivityConfig("foo", false), + expectedPayloadData: hookstage.ProcessedAuctionRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + User: &openrtb2.User{ID: ""}, + }}, + }, + }, + { + description: "payload should not change when userFPD is not blocked by activity", + hookCode: "foo", + inPayloadData: hookstage.ProcessedAuctionRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + User: &openrtb2.User{ID: "test_user_id"}, + }}, + }, + privacyConfig: getTransmitUFPDActivityConfig("foo", true), + expectedPayloadData: hookstage.ProcessedAuctionRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + User: &openrtb2.User{ID: "test_user_id"}, + }}, + }, + }, + + { + description: "payload should change when transmitPreciseGeo is blocked by activity", + hookCode: "foo", + inPayloadData: hookstage.ProcessedAuctionRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{IPv6: testIpv6}, + }}, + }, + privacyConfig: getTransmitPreciseGeoActivityConfig("foo", false), + expectedPayloadData: hookstage.ProcessedAuctionRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{IPv6: testIPv6Scrubbed}, + }}, + }, + }, + { + description: "payload should not change when transmitPreciseGeo is not blocked by activity", + hookCode: "foo", + inPayloadData: hookstage.ProcessedAuctionRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{IPv6: testIpv6}, + }}, + }, + privacyConfig: getTransmitPreciseGeoActivityConfig("foo", true), + expectedPayloadData: hookstage.ProcessedAuctionRequestPayload{ + Request: &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{ + Device: &openrtb2.Device{IPv6: testIpv6}, + }}, + }, + }, + } + for _, test := range testCases { + t.Run(test.description, func(t *testing.T) { + //check input payload didn't change + origInPayloadData := test.inPayloadData + activityControl := privacy.NewActivityControl(test.privacyConfig) + account := &config.Account{Privacy: config.AccountPrivacy{IPv6Config: config.IPv6{AnonKeepBits: testIPv6ScrubBytes}}} + newPayload := handleModuleActivities(test.hookCode, activityControl, test.inPayloadData, account) + assert.Equal(t, test.expectedPayloadData.Request.BidRequest, newPayload.Request.BidRequest) + assert.Equal(t, origInPayloadData, test.inPayloadData) + }) + } +} + +func TestHandleModuleActivitiesNoBidderRequestPayload(t *testing.T) { + + testCases := []struct { + description string + hookCode string + privacyConfig *config.AccountPrivacy + inPayloadData hookstage.RawAuctionRequestPayload + expectedPayloadData hookstage.RawAuctionRequestPayload + }{ + { + description: "payload should not change when userFPD is blocked by activity", + hookCode: "foo", + inPayloadData: hookstage.RawAuctionRequestPayload{}, + privacyConfig: getTransmitUFPDActivityConfig("foo", false), + expectedPayloadData: hookstage.RawAuctionRequestPayload{}, + }, + { + description: "payload should not change when userFPD is not blocked by activity", + hookCode: "foo", + inPayloadData: hookstage.RawAuctionRequestPayload{}, + privacyConfig: getTransmitUFPDActivityConfig("foo", true), + expectedPayloadData: hookstage.RawAuctionRequestPayload{}, + }, + { + description: "payload should not change when transmitPreciseGeo is blocked by activity", + hookCode: "foo", + inPayloadData: hookstage.RawAuctionRequestPayload{}, + privacyConfig: getTransmitPreciseGeoActivityConfig("foo", false), + expectedPayloadData: hookstage.RawAuctionRequestPayload{}, + }, + { + description: "payload should not change when transmitPreciseGeo is not blocked by activity", + hookCode: "foo", + inPayloadData: hookstage.RawAuctionRequestPayload{}, + privacyConfig: getTransmitPreciseGeoActivityConfig("foo", true), + expectedPayloadData: hookstage.RawAuctionRequestPayload{}, + }, + } + for _, test := range testCases { + t.Run(test.description, func(t *testing.T) { + //check input payload didn't change + origInPayloadData := test.inPayloadData + activityControl := privacy.NewActivityControl(test.privacyConfig) + newPayload := handleModuleActivities(test.hookCode, activityControl, test.inPayloadData, &config.Account{}) + assert.Equal(t, test.expectedPayloadData, newPayload) + assert.Equal(t, origInPayloadData, test.inPayloadData) + }) + } +} diff --git a/hooks/hookexecution/executor.go b/hooks/hookexecution/executor.go index e77029edf6f..a353e9000dc 100644 --- a/hooks/hookexecution/executor.go +++ b/hooks/hookexecution/executor.go @@ -5,14 +5,15 @@ import ( "net/http" "sync" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/privacy" ) const ( @@ -35,7 +36,7 @@ type StageExecutor interface { ExecuteEntrypointStage(req *http.Request, body []byte) ([]byte, *RejectError) ExecuteRawAuctionStage(body []byte) ([]byte, *RejectError) ExecuteProcessedAuctionStage(req *openrtb_ext.RequestWrapper) error - ExecuteBidderRequestStage(req *openrtb2.BidRequest, bidder string) *RejectError + ExecuteBidderRequestStage(req *openrtb_ext.RequestWrapper, bidder string) *RejectError ExecuteRawBidderResponseStage(response *adapters.BidderResponse, bidder string) *RejectError ExecuteAllProcessedBidResponsesStage(adapterBids map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid) ExecuteAuctionResponseStage(response *openrtb2.BidResponse) @@ -45,17 +46,19 @@ type StageExecutor interface { type HookStageExecutor interface { StageExecutor SetAccount(account *config.Account) + SetActivityControl(activityControl privacy.ActivityControl) GetOutcomes() []StageOutcome } type hookExecutor struct { - account *config.Account - accountID string - endpoint string - planBuilder hooks.ExecutionPlanBuilder - stageOutcomes []StageOutcome - moduleContexts *moduleContexts - metricEngine metrics.MetricsEngine + account *config.Account + accountID string + endpoint string + planBuilder hooks.ExecutionPlanBuilder + stageOutcomes []StageOutcome + moduleContexts *moduleContexts + metricEngine metrics.MetricsEngine + activityControl privacy.ActivityControl // Mutex needed for BidderRequest and RawBidderResponse Stages as they are run in several goroutines sync.Mutex } @@ -79,6 +82,10 @@ func (e *hookExecutor) SetAccount(account *config.Account) { e.accountID = account.ID } +func (e *hookExecutor) SetActivityControl(activityControl privacy.ActivityControl) { + e.activityControl = activityControl +} + func (e *hookExecutor) GetOutcomes() []StageOutcome { return e.stageOutcomes } @@ -191,7 +198,7 @@ func (e *hookExecutor) ExecuteProcessedAuctionStage(request *openrtb_ext.Request stageName := hooks.StageProcessedAuctionRequest.String() executionCtx := e.newContext(stageName) - payload := hookstage.ProcessedAuctionRequestPayload{RequestWrapper: request} + payload := hookstage.ProcessedAuctionRequestPayload{Request: request} outcome, _, contexts, reject := executeStage(executionCtx, plan, payload, handler, e.metricEngine) outcome.Entity = entityAuctionRequest @@ -208,7 +215,7 @@ func (e *hookExecutor) ExecuteProcessedAuctionStage(request *openrtb_ext.Request return reject } -func (e *hookExecutor) ExecuteBidderRequestStage(req *openrtb2.BidRequest, bidder string) *RejectError { +func (e *hookExecutor) ExecuteBidderRequestStage(req *openrtb_ext.RequestWrapper, bidder string) *RejectError { plan := e.planBuilder.PlanForBidderRequestStage(e.endpoint, e.account) if len(plan) == 0 { return nil @@ -225,7 +232,7 @@ func (e *hookExecutor) ExecuteBidderRequestStage(req *openrtb2.BidRequest, bidde stageName := hooks.StageBidderRequest.String() executionCtx := e.newContext(stageName) - payload := hookstage.BidderRequestPayload{BidRequest: req, Bidder: bidder} + payload := hookstage.BidderRequestPayload{Request: req, Bidder: bidder} outcome, payload, contexts, reject := executeStage(executionCtx, plan, payload, handler, e.metricEngine) outcome.Entity = entity(bidder) outcome.Stage = stageName @@ -321,11 +328,12 @@ func (e *hookExecutor) ExecuteAuctionResponseStage(response *openrtb2.BidRespons func (e *hookExecutor) newContext(stage string) executionContext { return executionContext{ - account: e.account, - accountId: e.accountID, - endpoint: e.endpoint, - moduleContexts: e.moduleContexts, - stage: stage, + account: e.account, + accountID: e.accountID, + endpoint: e.endpoint, + moduleContexts: e.moduleContexts, + stage: stage, + activityControl: e.activityControl, } } @@ -347,6 +355,8 @@ type EmptyHookExecutor struct{} func (executor EmptyHookExecutor) SetAccount(_ *config.Account) {} +func (executor EmptyHookExecutor) SetActivityControl(_ privacy.ActivityControl) {} + func (executor EmptyHookExecutor) GetOutcomes() []StageOutcome { return []StageOutcome{} } @@ -363,7 +373,7 @@ func (executor EmptyHookExecutor) ExecuteProcessedAuctionStage(_ *openrtb_ext.Re return nil } -func (executor EmptyHookExecutor) ExecuteBidderRequestStage(_ *openrtb2.BidRequest, bidder string) *RejectError { +func (executor EmptyHookExecutor) ExecuteBidderRequestStage(_ *openrtb_ext.RequestWrapper, bidder string) *RejectError { return nil } diff --git a/hooks/hookexecution/executor_test.go b/hooks/hookexecution/executor_test.go index a936d05fa8f..233ebbc7ebe 100644 --- a/hooks/hookexecution/executor_test.go +++ b/hooks/hookexecution/executor_test.go @@ -8,23 +8,24 @@ import ( "testing" "time" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/hooks/hookanalytics" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/metrics" - metricsConfig "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/metrics" + metricsConfig "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/privacy" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) func TestEmptyHookExecutor(t *testing.T) { executor := EmptyHookExecutor{} - executor.SetAccount(&config.Account{}) body := []byte(`{"foo": "bar"}`) reader := bytes.NewReader(body) @@ -37,7 +38,7 @@ func TestEmptyHookExecutor(t *testing.T) { entrypointBody, entrypointRejectErr := executor.ExecuteEntrypointStage(req, body) rawAuctionBody, rawAuctionRejectErr := executor.ExecuteRawAuctionStage(body) processedAuctionRejectErr := executor.ExecuteProcessedAuctionStage(&openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{}}) - bidderRequestRejectErr := executor.ExecuteBidderRequestStage(bidderRequest, "bidder-name") + bidderRequestRejectErr := executor.ExecuteBidderRequestStage(&openrtb_ext.RequestWrapper{BidRequest: bidderRequest}, "bidder-name") executor.ExecuteAuctionResponseStage(&openrtb2.BidResponse{}) outcomes := executor.GetOutcomes() @@ -417,14 +418,12 @@ func TestExecuteRawAuctionStage(t *testing.T) { const urlString string = "https://prebid.com/openrtb2/auction" foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}} - account := &config.Account{} testCases := []struct { description string givenBody string givenUrl string givenPlanBuilder hooks.ExecutionPlanBuilder - givenAccount *config.Account expectedBody string expectedReject *RejectError expectedModuleContexts *moduleContexts @@ -435,7 +434,6 @@ func TestExecuteRawAuctionStage(t *testing.T) { givenBody: body, givenUrl: urlString, givenPlanBuilder: hooks.EmptyPlanBuilder{}, - givenAccount: account, expectedBody: body, expectedReject: nil, expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{}}, @@ -446,7 +444,6 @@ func TestExecuteRawAuctionStage(t *testing.T) { givenBody: body, givenUrl: urlString, givenPlanBuilder: TestApplyHookMutationsBuilder{}, - givenAccount: account, expectedBody: bodyUpdated, expectedReject: nil, expectedModuleContexts: foobarModuleCtx, @@ -505,7 +502,6 @@ func TestExecuteRawAuctionStage(t *testing.T) { givenBody: body, givenUrl: urlString, givenPlanBuilder: TestRejectPlanBuilder{}, - givenAccount: nil, expectedBody: bodyUpdated, expectedReject: &RejectError{0, HookID{ModuleCode: "foobar", HookImplCode: "bar"}, hooks.StageRawAuctionRequest.String()}, expectedModuleContexts: foobarModuleCtx, @@ -566,7 +562,6 @@ func TestExecuteRawAuctionStage(t *testing.T) { givenBody: body, givenUrl: urlString, givenPlanBuilder: TestWithTimeoutPlanBuilder{}, - givenAccount: account, expectedBody: bodyUpdated, expectedReject: nil, expectedModuleContexts: foobarModuleCtx, @@ -615,7 +610,6 @@ func TestExecuteRawAuctionStage(t *testing.T) { givenBody: body, givenUrl: urlString, givenPlanBuilder: TestWithModuleContextsPlanBuilder{}, - givenAccount: account, expectedBody: body, expectedReject: nil, expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{ @@ -674,7 +668,10 @@ func TestExecuteRawAuctionStage(t *testing.T) { for _, test := range testCases { t.Run(test.description, func(t *testing.T) { exec := NewHookExecutor(test.givenPlanBuilder, EndpointAuction, &metricsConfig.NilMetricsEngine{}) - exec.SetAccount(test.givenAccount) + + privacyConfig := getModuleActivities("foo", false, false) + ac := privacy.NewActivityControl(privacyConfig) + exec.SetActivityControl(ac) newBody, reject := exec.ExecuteRawAuctionStage([]byte(test.givenBody)) @@ -694,14 +691,12 @@ func TestExecuteRawAuctionStage(t *testing.T) { func TestExecuteProcessedAuctionStage(t *testing.T) { foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}} - account := &config.Account{} req := openrtb2.BidRequest{ID: "some-id", User: &openrtb2.User{ID: "user-id"}} reqUpdated := openrtb2.BidRequest{ID: "some-id", User: &openrtb2.User{ID: "user-id", Yob: 2000, Consent: "true"}} testCases := []struct { description string givenPlanBuilder hooks.ExecutionPlanBuilder - givenAccount *config.Account givenRequest openrtb_ext.RequestWrapper expectedRequest openrtb2.BidRequest expectedErr error @@ -711,7 +706,6 @@ func TestExecuteProcessedAuctionStage(t *testing.T) { { description: "Request not changed if hook execution plan empty", givenPlanBuilder: hooks.EmptyPlanBuilder{}, - givenAccount: account, givenRequest: openrtb_ext.RequestWrapper{BidRequest: &req}, expectedRequest: req, expectedErr: nil, @@ -721,7 +715,6 @@ func TestExecuteProcessedAuctionStage(t *testing.T) { { description: "Request changed if hooks return mutations", givenPlanBuilder: TestApplyHookMutationsBuilder{}, - givenAccount: account, givenRequest: openrtb_ext.RequestWrapper{BidRequest: &req}, expectedRequest: reqUpdated, expectedErr: nil, @@ -755,7 +748,6 @@ func TestExecuteProcessedAuctionStage(t *testing.T) { { description: "Stage execution can be rejected - and later hooks rejected", givenPlanBuilder: TestRejectPlanBuilder{}, - givenAccount: nil, givenRequest: openrtb_ext.RequestWrapper{BidRequest: &req}, expectedRequest: req, expectedErr: &RejectError{0, HookID{ModuleCode: "foobar", HookImplCode: "foo"}, hooks.StageProcessedAuctionRequest.String()}, @@ -788,7 +780,6 @@ func TestExecuteProcessedAuctionStage(t *testing.T) { { description: "Request can be changed when a hook times out", givenPlanBuilder: TestWithTimeoutPlanBuilder{}, - givenAccount: account, givenRequest: openrtb_ext.RequestWrapper{BidRequest: &req}, expectedRequest: reqUpdated, expectedErr: nil, @@ -836,7 +827,6 @@ func TestExecuteProcessedAuctionStage(t *testing.T) { { description: "Modules contexts are preserved and correct", givenPlanBuilder: TestWithModuleContextsPlanBuilder{}, - givenAccount: account, givenRequest: openrtb_ext.RequestWrapper{BidRequest: &req}, expectedRequest: req, expectedErr: nil, @@ -896,7 +886,10 @@ func TestExecuteProcessedAuctionStage(t *testing.T) { for _, test := range testCases { t.Run(test.description, func(ti *testing.T) { exec := NewHookExecutor(test.givenPlanBuilder, EndpointAuction, &metricsConfig.NilMetricsEngine{}) - exec.SetAccount(test.givenAccount) + + privacyConfig := getModuleActivities("foo", false, false) + ac := privacy.NewActivityControl(privacyConfig) + exec.SetActivityControl(ac) err := exec.ExecuteProcessedAuctionStage(&test.givenRequest) @@ -917,7 +910,6 @@ func TestExecuteProcessedAuctionStage(t *testing.T) { func TestExecuteBidderRequestStage(t *testing.T) { bidderName := "the-bidder" foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}} - account := &config.Account{} expectedBidderRequest := &openrtb2.BidRequest{ID: "some-id", User: &openrtb2.User{ID: "user-id"}} expectedUpdatedBidderRequest := &openrtb2.BidRequest{ @@ -933,17 +925,16 @@ func TestExecuteBidderRequestStage(t *testing.T) { description string givenBidderRequest *openrtb2.BidRequest givenPlanBuilder hooks.ExecutionPlanBuilder - givenAccount *config.Account expectedBidderRequest *openrtb2.BidRequest expectedReject *RejectError expectedModuleContexts *moduleContexts expectedStageOutcomes []StageOutcome + privacyConfig *config.AccountPrivacy }{ { description: "Payload not changed if hook execution plan empty", givenBidderRequest: &openrtb2.BidRequest{ID: "some-id", User: &openrtb2.User{ID: "user-id"}}, givenPlanBuilder: hooks.EmptyPlanBuilder{}, - givenAccount: account, expectedBidderRequest: expectedBidderRequest, expectedReject: nil, expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{}}, @@ -953,7 +944,6 @@ func TestExecuteBidderRequestStage(t *testing.T) { description: "Payload changed if hooks return mutations", givenBidderRequest: &openrtb2.BidRequest{ID: "some-id", User: &openrtb2.User{ID: "user-id"}}, givenPlanBuilder: TestApplyHookMutationsBuilder{}, - givenAccount: account, expectedBidderRequest: expectedUpdatedBidderRequest, expectedReject: nil, expectedModuleContexts: foobarModuleCtx, @@ -1011,7 +1001,6 @@ func TestExecuteBidderRequestStage(t *testing.T) { description: "Stage execution can be rejected - and later hooks rejected", givenBidderRequest: &openrtb2.BidRequest{ID: "some-id", User: &openrtb2.User{ID: "user-id"}}, givenPlanBuilder: TestRejectPlanBuilder{}, - givenAccount: nil, expectedBidderRequest: expectedBidderRequest, expectedReject: &RejectError{0, HookID{ModuleCode: "foobar", HookImplCode: "foo"}, hooks.StageBidderRequest.String()}, expectedModuleContexts: foobarModuleCtx, @@ -1063,7 +1052,6 @@ func TestExecuteBidderRequestStage(t *testing.T) { description: "Stage execution can be timed out", givenBidderRequest: &openrtb2.BidRequest{ID: "some-id", User: &openrtb2.User{ID: "user-id"}}, givenPlanBuilder: TestWithTimeoutPlanBuilder{}, - givenAccount: account, expectedBidderRequest: expectedUpdatedBidderRequest, expectedReject: nil, expectedModuleContexts: foobarModuleCtx, @@ -1115,7 +1103,6 @@ func TestExecuteBidderRequestStage(t *testing.T) { description: "Modules contexts are preserved and correct", givenBidderRequest: &openrtb2.BidRequest{ID: "some-id", User: &openrtb2.User{ID: "user-id"}}, givenPlanBuilder: TestWithModuleContextsPlanBuilder{}, - givenAccount: account, expectedBidderRequest: expectedBidderRequest, expectedReject: nil, expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{ @@ -1169,9 +1156,11 @@ func TestExecuteBidderRequestStage(t *testing.T) { for _, test := range testCases { t.Run(test.description, func(t *testing.T) { exec := NewHookExecutor(test.givenPlanBuilder, EndpointAuction, &metricsConfig.NilMetricsEngine{}) - exec.SetAccount(test.givenAccount) + privacyConfig := getModuleActivities("foo", false, false) + ac := privacy.NewActivityControl(privacyConfig) + exec.SetActivityControl(ac) - reject := exec.ExecuteBidderRequestStage(test.givenBidderRequest, bidderName) + reject := exec.ExecuteBidderRequestStage(&openrtb_ext.RequestWrapper{BidRequest: test.givenBidderRequest}, bidderName) assert.Equal(t, test.expectedReject, reject, "Unexpected stage reject.") assert.Equal(t, test.expectedBidderRequest, test.givenBidderRequest, "Incorrect bidder request.") @@ -1187,9 +1176,48 @@ func TestExecuteBidderRequestStage(t *testing.T) { } } +func getModuleActivities(componentName string, allowTransmitUserFPD, allowTransmitPreciseGeo bool) *config.AccountPrivacy { + return &config.AccountPrivacy{ + AllowActivities: &config.AllowActivities{ + TransmitUserFPD: buildDefaultActivityConfig(componentName, allowTransmitUserFPD), + TransmitPreciseGeo: buildDefaultActivityConfig(componentName, allowTransmitPreciseGeo), + }, + } +} + +func getTransmitUFPDActivityConfig(componentName string, allow bool) *config.AccountPrivacy { + return &config.AccountPrivacy{ + AllowActivities: &config.AllowActivities{ + TransmitUserFPD: buildDefaultActivityConfig(componentName, allow), + }, + } +} + +func getTransmitPreciseGeoActivityConfig(componentName string, allow bool) *config.AccountPrivacy { + return &config.AccountPrivacy{ + AllowActivities: &config.AllowActivities{ + TransmitPreciseGeo: buildDefaultActivityConfig(componentName, allow), + }, + } +} + +func buildDefaultActivityConfig(componentName string, allow bool) config.Activity { + return config.Activity{ + Default: ptrutil.ToPtr(true), + Rules: []config.ActivityRule{ + { + Allow: allow, + Condition: config.ActivityCondition{ + ComponentName: []string{componentName}, + ComponentType: []string{"general"}, + }, + }, + }, + } +} + func TestExecuteRawBidderResponseStage(t *testing.T) { foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}} - account := &config.Account{} resp := adapters.BidderResponse{Bids: []*adapters.TypedBid{{DealPriority: 1}}} expResp := adapters.BidderResponse{Bids: []*adapters.TypedBid{{DealPriority: 10}}} vEntity := entity("the-bidder") @@ -1197,7 +1225,6 @@ func TestExecuteRawBidderResponseStage(t *testing.T) { testCases := []struct { description string givenPlanBuilder hooks.ExecutionPlanBuilder - givenAccount *config.Account givenBidderResponse adapters.BidderResponse expectedBidderResponse adapters.BidderResponse expectedReject *RejectError @@ -1207,7 +1234,6 @@ func TestExecuteRawBidderResponseStage(t *testing.T) { { description: "Payload not changed if hook execution plan empty", givenPlanBuilder: hooks.EmptyPlanBuilder{}, - givenAccount: account, givenBidderResponse: resp, expectedBidderResponse: resp, expectedReject: nil, @@ -1217,7 +1243,6 @@ func TestExecuteRawBidderResponseStage(t *testing.T) { { description: "Payload changed if hooks return mutations", givenPlanBuilder: TestApplyHookMutationsBuilder{}, - givenAccount: account, givenBidderResponse: resp, expectedBidderResponse: expResp, expectedReject: nil, @@ -1250,7 +1275,6 @@ func TestExecuteRawBidderResponseStage(t *testing.T) { { description: "Stage execution can be rejected", givenPlanBuilder: TestRejectPlanBuilder{}, - givenAccount: nil, givenBidderResponse: resp, expectedBidderResponse: resp, expectedReject: &RejectError{0, HookID{ModuleCode: "foobar", HookImplCode: "foo"}, hooks.StageRawBidderResponse.String()}, @@ -1283,7 +1307,6 @@ func TestExecuteRawBidderResponseStage(t *testing.T) { { description: "Response can be changed when a hook times out", givenPlanBuilder: TestWithTimeoutPlanBuilder{}, - givenAccount: account, givenBidderResponse: resp, expectedBidderResponse: expResp, expectedReject: nil, @@ -1330,7 +1353,6 @@ func TestExecuteRawBidderResponseStage(t *testing.T) { { description: "Modules contexts are preserved and correct", givenPlanBuilder: TestWithModuleContextsPlanBuilder{}, - givenAccount: account, givenBidderResponse: resp, expectedBidderResponse: expResp, expectedReject: nil, @@ -1390,7 +1412,10 @@ func TestExecuteRawBidderResponseStage(t *testing.T) { for _, test := range testCases { t.Run(test.description, func(ti *testing.T) { exec := NewHookExecutor(test.givenPlanBuilder, EndpointAuction, &metricsConfig.NilMetricsEngine{}) - exec.SetAccount(test.givenAccount) + + privacyConfig := getModuleActivities("foo", false, false) + ac := privacy.NewActivityControl(privacyConfig) + exec.SetActivityControl(ac) reject := exec.ExecuteRawBidderResponseStage(&test.givenBidderResponse, "the-bidder") @@ -1410,7 +1435,6 @@ func TestExecuteRawBidderResponseStage(t *testing.T) { func TestExecuteAllProcessedBidResponsesStage(t *testing.T) { foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}} - account := &config.Account{} expectedAllProcBidResponses := map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid{ "some-bidder": {Bids: []*entities.PbsOrtbBid{{DealPriority: 1}}}, @@ -1423,7 +1447,6 @@ func TestExecuteAllProcessedBidResponsesStage(t *testing.T) { description string givenBiddersResponse map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid givenPlanBuilder hooks.ExecutionPlanBuilder - givenAccount *config.Account expectedBiddersResponse map[openrtb_ext.BidderName]*entities.PbsOrtbSeatBid expectedReject *RejectError expectedModuleContexts *moduleContexts @@ -1435,7 +1458,6 @@ func TestExecuteAllProcessedBidResponsesStage(t *testing.T) { "some-bidder": {Bids: []*entities.PbsOrtbBid{{DealPriority: 1}}}, }, givenPlanBuilder: hooks.EmptyPlanBuilder{}, - givenAccount: account, expectedBiddersResponse: expectedAllProcBidResponses, expectedReject: nil, expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{}}, @@ -1447,7 +1469,6 @@ func TestExecuteAllProcessedBidResponsesStage(t *testing.T) { "some-bidder": {Bids: []*entities.PbsOrtbBid{{DealPriority: 1}}}, }, givenPlanBuilder: TestApplyHookMutationsBuilder{}, - givenAccount: account, expectedBiddersResponse: expectedUpdatedAllProcBidResponses, expectedReject: nil, expectedModuleContexts: foobarModuleCtx, @@ -1506,7 +1527,6 @@ func TestExecuteAllProcessedBidResponsesStage(t *testing.T) { "some-bidder": {Bids: []*entities.PbsOrtbBid{{DealPriority: 1}}}, }, givenPlanBuilder: TestRejectPlanBuilder{}, - givenAccount: nil, expectedBiddersResponse: expectedUpdatedAllProcBidResponses, expectedReject: &RejectError{0, HookID{ModuleCode: "foobar", HookImplCode: "foo"}, hooks.StageAllProcessedBidResponses.String()}, expectedModuleContexts: foobarModuleCtx, @@ -1571,7 +1591,6 @@ func TestExecuteAllProcessedBidResponsesStage(t *testing.T) { "some-bidder": {Bids: []*entities.PbsOrtbBid{{DealPriority: 1}}}, }, givenPlanBuilder: TestWithTimeoutPlanBuilder{}, - givenAccount: account, expectedBiddersResponse: expectedUpdatedAllProcBidResponses, expectedReject: nil, expectedModuleContexts: foobarModuleCtx, @@ -1620,7 +1639,6 @@ func TestExecuteAllProcessedBidResponsesStage(t *testing.T) { "some-bidder": {Bids: []*entities.PbsOrtbBid{{DealPriority: 1}}}, }, givenPlanBuilder: TestWithModuleContextsPlanBuilder{}, - givenAccount: account, expectedBiddersResponse: expectedAllProcBidResponses, expectedReject: nil, expectedModuleContexts: &moduleContexts{ctxs: map[string]hookstage.ModuleContext{ @@ -1669,7 +1687,10 @@ func TestExecuteAllProcessedBidResponsesStage(t *testing.T) { for _, test := range testCases { t.Run(test.description, func(t *testing.T) { exec := NewHookExecutor(test.givenPlanBuilder, EndpointAuction, &metricsConfig.NilMetricsEngine{}) - exec.SetAccount(test.givenAccount) + + privacyConfig := getModuleActivities("foo", false, false) + ac := privacy.NewActivityControl(privacyConfig) + exec.SetActivityControl(ac) exec.ExecuteAllProcessedBidResponsesStage(test.givenBiddersResponse) @@ -1688,14 +1709,12 @@ func TestExecuteAllProcessedBidResponsesStage(t *testing.T) { func TestExecuteAuctionResponseStage(t *testing.T) { foobarModuleCtx := &moduleContexts{ctxs: map[string]hookstage.ModuleContext{"foobar": nil}} - account := &config.Account{} resp := &openrtb2.BidResponse{CustomData: "some-custom-data"} expResp := &openrtb2.BidResponse{CustomData: "new-custom-data"} testCases := []struct { description string givenPlanBuilder hooks.ExecutionPlanBuilder - givenAccount *config.Account givenResponse *openrtb2.BidResponse expectedResponse *openrtb2.BidResponse expectedReject *RejectError @@ -1705,7 +1724,6 @@ func TestExecuteAuctionResponseStage(t *testing.T) { { description: "Payload not changed if hook execution plan empty", givenPlanBuilder: hooks.EmptyPlanBuilder{}, - givenAccount: account, givenResponse: resp, expectedResponse: resp, expectedReject: nil, @@ -1715,7 +1733,6 @@ func TestExecuteAuctionResponseStage(t *testing.T) { { description: "Payload changed if hooks return mutations", givenPlanBuilder: TestApplyHookMutationsBuilder{}, - givenAccount: account, givenResponse: resp, expectedResponse: expResp, expectedReject: nil, @@ -1748,7 +1765,6 @@ func TestExecuteAuctionResponseStage(t *testing.T) { { description: "Stage execution can't be rejected - stage doesn't support rejection", givenPlanBuilder: TestRejectPlanBuilder{}, - givenAccount: nil, givenResponse: resp, expectedResponse: expResp, expectedReject: &RejectError{0, HookID{ModuleCode: "foobar", HookImplCode: "foo"}, hooks.StageAuctionResponse.String()}, @@ -1811,7 +1827,6 @@ func TestExecuteAuctionResponseStage(t *testing.T) { { description: "Request can be changed when a hook times out", givenPlanBuilder: TestWithTimeoutPlanBuilder{}, - givenAccount: account, givenResponse: resp, expectedResponse: expResp, expectedReject: nil, @@ -1858,7 +1873,6 @@ func TestExecuteAuctionResponseStage(t *testing.T) { { description: "Modules contexts are preserved and correct", givenPlanBuilder: TestWithModuleContextsPlanBuilder{}, - givenAccount: account, givenResponse: resp, expectedResponse: resp, expectedReject: nil, @@ -1918,7 +1932,10 @@ func TestExecuteAuctionResponseStage(t *testing.T) { for _, test := range testCases { t.Run(test.description, func(t *testing.T) { exec := NewHookExecutor(test.givenPlanBuilder, EndpointAuction, &metricsConfig.NilMetricsEngine{}) - exec.SetAccount(test.givenAccount) + + privacyConfig := getModuleActivities("foo", false, false) + ac := privacy.NewActivityControl(privacyConfig) + exec.SetActivityControl(ac) exec.ExecuteAuctionResponseStage(test.givenResponse) diff --git a/hooks/hookexecution/mocks_test.go b/hooks/hookexecution/mocks_test.go index 76a8d6d9ccf..a52fdbe12a7 100644 --- a/hooks/hookexecution/mocks_test.go +++ b/hooks/hookexecution/mocks_test.go @@ -5,8 +5,9 @@ import ( "errors" "time" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) type mockUpdateHeaderEntrypointHook struct{} @@ -131,7 +132,7 @@ func (e mockTimeoutHook) HandleProcessedAuctionHook(_ context.Context, _ hooksta time.Sleep(20 * time.Millisecond) c := hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{} c.AddMutation(func(payload hookstage.ProcessedAuctionRequestPayload) (hookstage.ProcessedAuctionRequestPayload, error) { - payload.RequestWrapper.User.CustomData = "some-custom-data" + payload.Request.User.CustomData = "some-custom-data" return payload, nil }, hookstage.MutationUpdate, "bidRequest", "user.customData") @@ -142,7 +143,7 @@ func (e mockTimeoutHook) HandleBidderRequestHook(_ context.Context, _ hookstage. time.Sleep(20 * time.Millisecond) c := hookstage.ChangeSet[hookstage.BidderRequestPayload]{} c.AddMutation(func(payload hookstage.BidderRequestPayload) (hookstage.BidderRequestPayload, error) { - payload.BidRequest.User.CustomData = "some-custom-data" + payload.Request.User.CustomData = "some-custom-data" return payload, nil }, hookstage.MutationUpdate, "bidRequest", "user.customData") @@ -322,8 +323,8 @@ func (e mockUpdateBidRequestHook) HandleProcessedAuctionHook(_ context.Context, c := hookstage.ChangeSet[hookstage.ProcessedAuctionRequestPayload]{} c.AddMutation( func(payload hookstage.ProcessedAuctionRequestPayload) (hookstage.ProcessedAuctionRequestPayload, error) { - payload.RequestWrapper.User.Yob = 2000 - userExt, err := payload.RequestWrapper.GetUserExt() + payload.Request.User.Yob = 2000 + userExt, err := payload.Request.GetUserExt() if err != nil { return payload, err } @@ -335,7 +336,7 @@ func (e mockUpdateBidRequestHook) HandleProcessedAuctionHook(_ context.Context, }, hookstage.MutationUpdate, "bidRequest", "user.yob", ).AddMutation( func(payload hookstage.ProcessedAuctionRequestPayload) (hookstage.ProcessedAuctionRequestPayload, error) { - payload.RequestWrapper.User.Consent = "true" + payload.Request.User.Consent = "true" return payload, nil }, hookstage.MutationUpdate, "bidRequest", "user.consent", ) @@ -347,12 +348,16 @@ func (e mockUpdateBidRequestHook) HandleBidderRequestHook(_ context.Context, _ h c := hookstage.ChangeSet[hookstage.BidderRequestPayload]{} c.AddMutation( func(payload hookstage.BidderRequestPayload) (hookstage.BidderRequestPayload, error) { - payload.BidRequest.User.Yob = 2000 + user := ptrutil.Clone(payload.Request.User) + user.Yob = 2000 + payload.Request.User = user return payload, nil }, hookstage.MutationUpdate, "bidRequest", "user.yob", ).AddMutation( func(payload hookstage.BidderRequestPayload) (hookstage.BidderRequestPayload, error) { - payload.BidRequest.User.Consent = "true" + user := ptrutil.Clone(payload.Request.User) + user.Consent = "true" + payload.Request.User = user return payload, nil }, hookstage.MutationUpdate, "bidRequest", "user.consent", ) diff --git a/hooks/hookexecution/outcome.go b/hooks/hookexecution/outcome.go index 3eeb7bcef5e..ff8bf1e973e 100644 --- a/hooks/hookexecution/outcome.go +++ b/hooks/hookexecution/outcome.go @@ -3,7 +3,7 @@ package hookexecution import ( "time" - "github.com/prebid/prebid-server/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" ) // Status indicates the result of hook execution. diff --git a/hooks/hookexecution/test_utils.go b/hooks/hookexecution/test_utils.go index bd94d5778b4..32ae9fd7a22 100644 --- a/hooks/hookexecution/test_utils.go +++ b/hooks/hookexecution/test_utils.go @@ -4,6 +4,7 @@ import ( "encoding/json" "testing" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" ) @@ -16,8 +17,8 @@ func AssertEqualModulesData(t *testing.T, expectedData, actualData json.RawMessa var expectedModulesOutcome ModulesOutcome var actualModulesOutcome ModulesOutcome - assert.NoError(t, json.Unmarshal(expectedData, &expectedModulesOutcome), "Failed to unmarshal expected modules data.") - assert.NoError(t, json.Unmarshal(actualData, &actualModulesOutcome), "Failed to unmarshal actual modules data.") + assert.NoError(t, jsonutil.UnmarshalValid(expectedData, &expectedModulesOutcome), "Failed to unmarshal expected modules data.") + assert.NoError(t, jsonutil.UnmarshalValid(actualData, &actualModulesOutcome), "Failed to unmarshal actual modules data.") assert.Equal(t, expectedModulesOutcome.Errors, actualModulesOutcome.Errors, "Invalid error messages.") assert.Equal(t, expectedModulesOutcome.Warnings, actualModulesOutcome.Warnings, "Invalid warning messages.") diff --git a/hooks/hookstage/allprocessedbidresponses.go b/hooks/hookstage/allprocessedbidresponses.go index 3f90c5624ee..233a68b6efd 100644 --- a/hooks/hookstage/allprocessedbidresponses.go +++ b/hooks/hookstage/allprocessedbidresponses.go @@ -3,8 +3,8 @@ package hookstage import ( "context" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // AllProcessedBidResponses hooks are invoked over a list of all diff --git a/hooks/hookstage/auctionresponse.go b/hooks/hookstage/auctionresponse.go index d47c78de5e1..c0810b196c1 100644 --- a/hooks/hookstage/auctionresponse.go +++ b/hooks/hookstage/auctionresponse.go @@ -3,7 +3,7 @@ package hookstage import ( "context" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" ) // AuctionResponse hooks are invoked at the very end of request processing. diff --git a/hooks/hookstage/bidderrequest.go b/hooks/hookstage/bidderrequest.go index 6609a103279..05f3574c8bf 100644 --- a/hooks/hookstage/bidderrequest.go +++ b/hooks/hookstage/bidderrequest.go @@ -2,8 +2,7 @@ package hookstage import ( "context" - - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // BidderRequest hooks are invoked for each bidder participating in auction. @@ -25,6 +24,20 @@ type BidderRequest interface { // distilled for the particular bidder. // Hooks are allowed to modify openrtb2.BidRequest using mutations. type BidderRequestPayload struct { - BidRequest *openrtb2.BidRequest - Bidder string + Request *openrtb_ext.RequestWrapper + Bidder string +} + +func (brp *BidderRequestPayload) GetBidderRequestPayload() *openrtb_ext.RequestWrapper { + return brp.Request +} + +func (brp *BidderRequestPayload) SetBidderRequestPayload(br *openrtb_ext.RequestWrapper) { + brp.Request = br +} + +// RequestUpdater allows reading and writing a bid request +type RequestUpdater interface { + GetBidderRequestPayload() *openrtb_ext.RequestWrapper + SetBidderRequestPayload(br *openrtb_ext.RequestWrapper) } diff --git a/hooks/hookstage/bidderrequest_mutations.go b/hooks/hookstage/bidderrequest_mutations.go index 6dfd1c6438f..6f4d5a5bacb 100644 --- a/hooks/hookstage/bidderrequest_mutations.go +++ b/hooks/hookstage/bidderrequest_mutations.go @@ -3,8 +3,9 @@ package hookstage import ( "errors" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" + + "github.com/prebid/openrtb/v20/adcom1" ) func (c *ChangeSet[T]) BidderRequest() ChangeSetBidderRequest[T] { @@ -31,12 +32,12 @@ func (c ChangeSetBidderRequest[T]) BApp() ChangeSetBApp[T] { return ChangeSetBApp[T]{changeSetBidderRequest: c} } -func (c ChangeSetBidderRequest[T]) castPayload(p T) (*openrtb2.BidRequest, error) { +func (c ChangeSetBidderRequest[T]) castPayload(p T) (*openrtb_ext.RequestWrapper, error) { if payload, ok := any(p).(BidderRequestPayload); ok { - if payload.BidRequest == nil { - return nil, errors.New("empty BidRequest provided") + if payload.Request == nil || payload.Request.BidRequest == nil { + return nil, errors.New("payload contains a nil bid request") } - return payload.BidRequest, nil + return payload.Request, nil } return nil, errors.New("failed to cast BidderRequestPayload") } diff --git a/hooks/hookstage/invocation.go b/hooks/hookstage/invocation.go index f0cc1c512f8..d6adb506066 100644 --- a/hooks/hookstage/invocation.go +++ b/hooks/hookstage/invocation.go @@ -3,7 +3,7 @@ package hookstage import ( "encoding/json" - "github.com/prebid/prebid-server/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" ) // HookResult represents the result of execution the concrete hook instance. diff --git a/hooks/hookstage/processedauctionrequest.go b/hooks/hookstage/processedauctionrequest.go index fe06bc6fdbd..02638dccc20 100644 --- a/hooks/hookstage/processedauctionrequest.go +++ b/hooks/hookstage/processedauctionrequest.go @@ -2,8 +2,7 @@ package hookstage import ( "context" - - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // ProcessedAuctionRequest hooks are invoked after the request is parsed @@ -26,5 +25,13 @@ type ProcessedAuctionRequest interface { // ProcessedAuctionRequestPayload consists of the openrtb_ext.RequestWrapper object. // Hooks are allowed to modify openrtb_ext.RequestWrapper using mutations. type ProcessedAuctionRequestPayload struct { - RequestWrapper *openrtb_ext.RequestWrapper + Request *openrtb_ext.RequestWrapper +} + +func (parp *ProcessedAuctionRequestPayload) GetBidderRequestPayload() *openrtb_ext.RequestWrapper { + return parp.Request +} + +func (parp *ProcessedAuctionRequestPayload) SetBidderRequestPayload(br *openrtb_ext.RequestWrapper) { + parp.Request = br } diff --git a/hooks/hookstage/processedbeforerequestvalidation.go b/hooks/hookstage/processedbeforerequestvalidation.go index 219d0eed580..69d897e1758 100644 --- a/hooks/hookstage/processedbeforerequestvalidation.go +++ b/hooks/hookstage/processedbeforerequestvalidation.go @@ -3,7 +3,7 @@ package hookstage import ( "context" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" ) // BeforeValidationRequest diff --git a/hooks/hookstage/rawbidderresponse.go b/hooks/hookstage/rawbidderresponse.go index d450d6d0681..7d08a7d2e02 100644 --- a/hooks/hookstage/rawbidderresponse.go +++ b/hooks/hookstage/rawbidderresponse.go @@ -3,7 +3,7 @@ package hookstage import ( "context" - "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/v2/adapters" ) // RawBidderResponse hooks are invoked for each bidder participating in auction. diff --git a/hooks/hookstage/rawbidderresponse_mutations.go b/hooks/hookstage/rawbidderresponse_mutations.go index 61c0de10bde..efab874fa15 100644 --- a/hooks/hookstage/rawbidderresponse_mutations.go +++ b/hooks/hookstage/rawbidderresponse_mutations.go @@ -3,7 +3,7 @@ package hookstage import ( "errors" - "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/v2/adapters" ) func (c *ChangeSet[T]) RawBidderResponse() ChangeSetRawBidderResponse[T] { diff --git a/hooks/plan.go b/hooks/plan.go index 042c60708d5..0d119b91b23 100644 --- a/hooks/plan.go +++ b/hooks/plan.go @@ -3,8 +3,8 @@ package hooks import ( "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks/hookstage" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks/hookstage" ) type Stage string diff --git a/hooks/plan_test.go b/hooks/plan_test.go index f5e063452ae..6ce030ec550 100644 --- a/hooks/plan_test.go +++ b/hooks/plan_test.go @@ -2,12 +2,12 @@ package hooks import ( "context" - "encoding/json" "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks/hookstage" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" ) @@ -227,7 +227,7 @@ func TestPlanForRawAuctionStage(t *testing.T) { for name, test := range testCases { t.Run(name, func(t *testing.T) { account := new(config.Account) - if err := json.Unmarshal(test.giveAccountPlanData, &account.Hooks); err != nil { + if err := jsonutil.UnmarshalValid(test.giveAccountPlanData, &account.Hooks); err != nil { t.Fatal(err) } @@ -333,7 +333,7 @@ func TestPlanForProcessedAuctionStage(t *testing.T) { for name, test := range testCases { t.Run(name, func(t *testing.T) { account := new(config.Account) - if err := json.Unmarshal(test.giveAccountPlanData, &account.Hooks); err != nil { + if err := jsonutil.UnmarshalValid(test.giveAccountPlanData, &account.Hooks); err != nil { t.Fatal(err) } @@ -439,7 +439,7 @@ func TestPlanForBidderRequestStage(t *testing.T) { for name, test := range testCases { t.Run(name, func(t *testing.T) { account := new(config.Account) - if err := json.Unmarshal(test.giveAccountPlanData, &account.Hooks); err != nil { + if err := jsonutil.UnmarshalValid(test.giveAccountPlanData, &account.Hooks); err != nil { t.Fatal(err) } @@ -545,7 +545,7 @@ func TestPlanForRawBidderResponseStage(t *testing.T) { for name, test := range testCases { t.Run(name, func(t *testing.T) { account := new(config.Account) - if err := json.Unmarshal(test.giveAccountPlanData, &account.Hooks); err != nil { + if err := jsonutil.UnmarshalValid(test.giveAccountPlanData, &account.Hooks); err != nil { t.Fatal(err) } @@ -651,7 +651,7 @@ func TestPlanForAllProcessedBidResponsesStage(t *testing.T) { for name, test := range testCases { t.Run(name, func(t *testing.T) { account := new(config.Account) - if err := json.Unmarshal(test.giveAccountPlanData, &account.Hooks); err != nil { + if err := jsonutil.UnmarshalValid(test.giveAccountPlanData, &account.Hooks); err != nil { t.Fatal(err) } @@ -757,7 +757,7 @@ func TestPlanForAuctionResponseStage(t *testing.T) { for name, test := range testCases { t.Run(name, func(t *testing.T) { account := new(config.Account) - if err := json.Unmarshal(test.giveAccountPlanData, &account.Hooks); err != nil { + if err := jsonutil.UnmarshalValid(test.giveAccountPlanData, &account.Hooks); err != nil { t.Fatal(err) } @@ -779,12 +779,12 @@ func getPlanBuilder( var hostPlan config.HookExecutionPlan var defaultAccountPlan config.HookExecutionPlan - err = json.Unmarshal(hostPlanData, &hostPlan) + err = jsonutil.UnmarshalValid(hostPlanData, &hostPlan) if err != nil { return nil, err } - err = json.Unmarshal(accountPlanData, &defaultAccountPlan) + err = jsonutil.UnmarshalValid(accountPlanData, &defaultAccountPlan) if err != nil { return nil, err } diff --git a/hooks/repo.go b/hooks/repo.go index ecafad61a77..52f838739f0 100644 --- a/hooks/repo.go +++ b/hooks/repo.go @@ -3,7 +3,7 @@ package hooks import ( "fmt" - "github.com/prebid/prebid-server/hooks/hookstage" + "github.com/prebid/prebid-server/v2/hooks/hookstage" ) // HookRepository is the interface that exposes methods diff --git a/hooks/repo_test.go b/hooks/repo_test.go index ae523c98773..1ffbf0bfbed 100644 --- a/hooks/repo_test.go +++ b/hooks/repo_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/prebid/prebid-server/hooks/hookstage" + "github.com/prebid/prebid-server/v2/hooks/hookstage" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/macros/macros.go b/macros/macros.go index 609e72cdec7..bde843c3cbb 100644 --- a/macros/macros.go +++ b/macros/macros.go @@ -15,6 +15,8 @@ type EndpointTemplateParams struct { AdUnit string MediaType string GvlID string + PageID string + SupplyId string } // UserSyncPrivacy specifies privacy policy macros, represented as strings, for user sync urls. diff --git a/macros/provider.go b/macros/provider.go index 0b0fc0de454..3cae540e22a 100644 --- a/macros/provider.go +++ b/macros/provider.go @@ -5,8 +5,8 @@ import ( "strconv" "time" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const ( diff --git a/macros/provider_test.go b/macros/provider_test.go index b6465a7f2e6..b3f5c9a88a9 100644 --- a/macros/provider_test.go +++ b/macros/provider_test.go @@ -3,9 +3,9 @@ package macros import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/macros/string_index_based_replacer_test.go b/macros/string_index_based_replacer_test.go index 97379a6d965..eb81a1520e9 100644 --- a/macros/string_index_based_replacer_test.go +++ b/macros/string_index_based_replacer_test.go @@ -3,9 +3,9 @@ package macros import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/main.go b/main.go index 0a95c4ffe51..9599783492e 100644 --- a/main.go +++ b/main.go @@ -8,12 +8,14 @@ import ( "runtime" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/router" - "github.com/prebid/prebid-server/server" - "github.com/prebid/prebid-server/util/task" + jsoniter "github.com/json-iterator/go" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/router" + "github.com/prebid/prebid-server/v2/server" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/task" "github.com/golang/glog" "github.com/spf13/viper" @@ -21,6 +23,7 @@ import ( func init() { rand.Seed(time.Now().UnixNano()) + jsoniter.RegisterExtension(&jsonutil.RawMessageExtension{}) } // TODO: revert this after PBS-OpenWrap module diff --git a/main_test.go b/main_test.go index 13be3a2b479..0969b5e71ef 100644 --- a/main_test.go +++ b/main_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" "github.com/stretchr/testify/assert" "github.com/spf13/viper" diff --git a/metrics/config/metrics.go b/metrics/config/metrics.go index 13fd1b53bfd..9fba7dcdf7c 100644 --- a/metrics/config/metrics.go +++ b/metrics/config/metrics.go @@ -4,10 +4,10 @@ import ( "time" "github.com/golang/glog" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - prometheusmetrics "github.com/prebid/prebid-server/metrics/prometheus" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + prometheusmetrics "github.com/prebid/prebid-server/v2/metrics/prometheus" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/prometheus/client_golang/prometheus" gometrics "github.com/rcrowley/go-metrics" influxdb "github.com/vrischmann/go-metrics-influxdb" @@ -392,27 +392,6 @@ func (me *MultiMetricsEngine) RecordBidValidationSecureMarkupWarn(adapter openrt } } -func (me *MultiMetricsEngine) RecordAccountGDPRPurposeWarning(account string, purposeName string) { - for _, thisME := range *me { - thisME.RecordAccountGDPRPurposeWarning(account, purposeName) - } -} -func (me *MultiMetricsEngine) RecordAccountGDPRChannelEnabledWarning(account string) { - for _, thisME := range *me { - thisME.RecordAccountGDPRChannelEnabledWarning(account) - } -} -func (me *MultiMetricsEngine) RecordAccountCCPAChannelEnabledWarning(account string) { - for _, thisME := range *me { - thisME.RecordAccountCCPAChannelEnabledWarning(account) - } -} -func (me *MultiMetricsEngine) RecordAccountUpgradeStatus(account string) { - for _, thisME := range *me { - thisME.RecordAccountUpgradeStatus(account) - } -} - func (me *MultiMetricsEngine) RecordModuleCalled(labels metrics.ModuleLabels, duration time.Duration) { for _, thisME := range *me { thisME.RecordModuleCalled(labels, duration) @@ -630,18 +609,6 @@ func (me *NilMetricsEngine) RecordBidValidationSecureMarkupError(adapter openrtb func (me *NilMetricsEngine) RecordBidValidationSecureMarkupWarn(adapter openrtb_ext.BidderName, account string) { } -func (me *NilMetricsEngine) RecordAccountGDPRPurposeWarning(account string, purposeName string) { -} - -func (me *NilMetricsEngine) RecordAccountGDPRChannelEnabledWarning(account string) { -} - -func (me *NilMetricsEngine) RecordAccountCCPAChannelEnabledWarning(account string) { -} - -func (me *NilMetricsEngine) RecordAccountUpgradeStatus(account string) { -} - func (me *NilMetricsEngine) RecordModuleCalled(labels metrics.ModuleLabels, duration time.Duration) { } diff --git a/metrics/config/metrics_ow.go b/metrics/config/metrics_ow.go index 5590984a5ec..62a950438cd 100644 --- a/metrics/config/metrics_ow.go +++ b/metrics/config/metrics_ow.go @@ -1,7 +1,7 @@ package config import ( - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/prometheus/client_golang/prometheus" gometrics "github.com/rcrowley/go-metrics" ) diff --git a/metrics/config/metrics_ow_test.go b/metrics/config/metrics_ow_test.go index 6beff5ae3fd..c34cfd703cf 100644 --- a/metrics/config/metrics_ow_test.go +++ b/metrics/config/metrics_ow_test.go @@ -3,10 +3,10 @@ package config import ( "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - prometheusmetrics "github.com/prebid/prebid-server/metrics/prometheus" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + prometheusmetrics "github.com/prebid/prebid-server/v2/metrics/prometheus" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func TestGoMetricsEngineForNilRegistry(t *testing.T) { diff --git a/metrics/config/metrics_test.go b/metrics/config/metrics_test.go index 7532e594a2b..3e666e21a24 100644 --- a/metrics/config/metrics_test.go +++ b/metrics/config/metrics_test.go @@ -2,12 +2,13 @@ package config import ( "fmt" + "strings" "testing" "time" - mainConfig "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + mainConfig "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" gometrics "github.com/rcrowley/go-metrics" ) @@ -168,13 +169,14 @@ func TestMultiMetricsEngine(t *testing.T) { VerifyMetrics(t, "Request", goEngine.RequestStatuses[metrics.ReqTypeORTB2Web][metrics.RequestStatusOK].Count(), 5) VerifyMetrics(t, "ImpMeter", goEngine.ImpMeter.Count(), 8) VerifyMetrics(t, "NoCookieMeter", goEngine.NoCookieMeter.Count(), 0) - VerifyMetrics(t, "AdapterMetrics.Pubmatic.GotBidsMeter", goEngine.AdapterMetrics[openrtb_ext.BidderPubmatic].GotBidsMeter.Count(), 5) - VerifyMetrics(t, "AdapterMetrics.Pubmatic.NoBidMeter", goEngine.AdapterMetrics[openrtb_ext.BidderPubmatic].NoBidMeter.Count(), 0) + + VerifyMetrics(t, "AdapterMetrics.pubmatic.GotBidsMeter", goEngine.AdapterMetrics[strings.ToLower(string(openrtb_ext.BidderPubmatic))].GotBidsMeter.Count(), 5) + VerifyMetrics(t, "AdapterMetrics.pubmatic.NoBidMeter", goEngine.AdapterMetrics[strings.ToLower(string(openrtb_ext.BidderPubmatic))].NoBidMeter.Count(), 0) for _, err := range metrics.AdapterErrors() { - VerifyMetrics(t, "AdapterMetrics.Pubmatic.Request.ErrorMeter."+string(err), goEngine.AdapterMetrics[openrtb_ext.BidderPubmatic].ErrorMeters[err].Count(), 0) + VerifyMetrics(t, "AdapterMetrics.pubmatic.Request.ErrorMeter."+string(err), goEngine.AdapterMetrics[strings.ToLower(string(openrtb_ext.BidderPubmatic))].ErrorMeters[err].Count(), 0) } - VerifyMetrics(t, "AdapterMetrics.AppNexus.GotBidsMeter", goEngine.AdapterMetrics[openrtb_ext.BidderAppnexus].GotBidsMeter.Count(), 0) - VerifyMetrics(t, "AdapterMetrics.AppNexus.NoBidMeter", goEngine.AdapterMetrics[openrtb_ext.BidderAppnexus].NoBidMeter.Count(), 5) + VerifyMetrics(t, "AdapterMetrics.appnexus.GotBidsMeter", goEngine.AdapterMetrics[strings.ToLower(string(openrtb_ext.BidderAppnexus))].GotBidsMeter.Count(), 0) + VerifyMetrics(t, "AdapterMetrics.appnexus.NoBidMeter", goEngine.AdapterMetrics[strings.ToLower(string(openrtb_ext.BidderAppnexus))].NoBidMeter.Count(), 5) VerifyMetrics(t, "RecordRequestQueueTime.Video.Rejected", goEngine.RequestsQueueTimer[metrics.ReqTypeVideo][false].Count(), 1) VerifyMetrics(t, "RecordRequestQueueTime.Video.Accepted", goEngine.RequestsQueueTimer[metrics.ReqTypeVideo][true].Count(), 0) @@ -186,7 +188,7 @@ func TestMultiMetricsEngine(t *testing.T) { VerifyMetrics(t, "StoredImpCache.Hit", goEngine.StoredImpCacheMeter[metrics.CacheHit].Count(), 5) VerifyMetrics(t, "AccountCache.Hit", goEngine.AccountCacheMeter[metrics.CacheHit].Count(), 6) - VerifyMetrics(t, "AdapterMetrics.AppNexus.GDPRRequestBlocked", goEngine.AdapterMetrics[openrtb_ext.BidderAppnexus].GDPRRequestBlocked.Count(), 1) + VerifyMetrics(t, "AdapterMetrics.appNexus.GDPRRequestBlocked", goEngine.AdapterMetrics[strings.ToLower(string(openrtb_ext.BidderAppnexus))].GDPRRequestBlocked.Count(), 1) // verify that each module has its own metric recorded for module, stages := range modulesStages { diff --git a/metrics/go_metrics.go b/metrics/go_metrics.go index b69dc515778..e216c58bcb4 100644 --- a/metrics/go_metrics.go +++ b/metrics/go_metrics.go @@ -2,12 +2,13 @@ package metrics import ( "fmt" + "strings" "sync" "time" "github.com/golang/glog" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" metrics "github.com/rcrowley/go-metrics" ) @@ -63,26 +64,13 @@ type Metrics struct { PrivacyLMTRequest metrics.Meter PrivacyTCFRequestVersion map[TCFVersionValue]metrics.Meter - // Ad Pod Metrics - - // podImpGenTimer indicates time taken by impression generator - // algorithm to generate impressions for given ad pod request - podImpGenTimer metrics.Timer - - // podImpGenTimer indicates time taken by combination generator - // algorithm to generate combination based on bid response and ad pod request - podCombGenTimer metrics.Timer - - // podCompExclTimer indicates time taken by compititve exclusion - // algorithm to generate final pod response based on bid response and ad pod request - podCompExclTimer metrics.Timer - - AdapterMetrics map[openrtb_ext.BidderName]*AdapterMetrics + AdapterMetrics map[string]*AdapterMetrics // Don't export accountMetrics because we need helper functions here to insure its properly populated dynamically accountMetrics map[string]*accountMetrics accountMetricsRWMutex sync.RWMutex - exchanges []openrtb_ext.BidderName + // adapter name exchanges + exchanges []string modules []string // Will hold boolean values to help us disable metric collection if needed MetricsDisabled config.DisabledMetrics @@ -135,7 +123,7 @@ type accountMetrics struct { bidsReceivedMeter metrics.Meter priceHistogram metrics.Histogram // store account by adapter metrics. Type is map[PBSBidder.BidderCode] - adapterMetrics map[openrtb_ext.BidderName]*AdapterMetrics + adapterMetrics map[string]*AdapterMetrics moduleMetrics map[string]*ModuleMetrics storedResponsesMeter metrics.Meter @@ -179,7 +167,7 @@ type ModuleMetrics struct { // rather than loading metrics that never get filled. // This will also eventually let us configure metrics, such as setting a limited set of metrics // for a production instance, and then expanding again when we need more debugging. -func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, disabledMetrics config.DisabledMetrics, moduleStageNames map[string][]string) *Metrics { +func NewBlankMetrics(registry metrics.Registry, exchanges []string, disabledMetrics config.DisabledMetrics, moduleStageNames map[string][]string) *Metrics { blankMeter := &metrics.NilMeter{} blankTimer := &metrics.NilTimer{} @@ -227,7 +215,7 @@ func NewBlankMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderNa PrivacyLMTRequest: blankMeter, PrivacyTCFRequestVersion: make(map[TCFVersionValue]metrics.Meter, len(TCFVersions())), - AdapterMetrics: make(map[openrtb_ext.BidderName]*AdapterMetrics, len(exchanges)), + AdapterMetrics: make(map[string]*AdapterMetrics, len(exchanges)), accountMetrics: make(map[string]*accountMetrics), MetricsDisabled: disabledMetrics, @@ -306,7 +294,11 @@ func getModuleNames(moduleStageNames map[string][]string) []string { // mode metrics. The code would allways try to record the metrics, but effectively noop if we are // using a blank meter/timer. func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, disableAccountMetrics config.DisabledMetrics, syncerKeys []string, moduleStageNames map[string][]string) *Metrics { - newMetrics := NewBlankMetrics(registry, exchanges, disableAccountMetrics, moduleStageNames) + lowerCaseExchanges := []string{} + for _, exchange := range exchanges { + lowerCaseExchanges = append(lowerCaseExchanges, strings.ToLower(string(exchange))) + } + newMetrics := NewBlankMetrics(registry, lowerCaseExchanges, disableAccountMetrics, moduleStageNames) newMetrics.ConnectionCounter = metrics.GetOrRegisterCounter("active_connections", registry) newMetrics.TMaxTimeoutCounter = metrics.GetOrRegisterCounter("tmax_timeout", registry) newMetrics.ConnectionAcceptErrorMeter = metrics.GetOrRegisterMeter("connection_accept_errors", registry) @@ -364,9 +356,9 @@ func NewMetrics(registry metrics.Registry, exchanges []openrtb_ext.BidderName, d } } - for _, a := range exchanges { + for _, a := range lowerCaseExchanges { registerAdapterMetrics(registry, "adapter", string(a), newMetrics.AdapterMetrics[a]) - newMetrics.FloorRejectedBidsMeter[a] = metrics.GetOrRegisterMeter(fmt.Sprintf("rejected_bid.%s", a), registry) + newMetrics.FloorRejectedBidsMeter[openrtb_ext.BidderName(a)] = metrics.GetOrRegisterMeter(fmt.Sprintf("rejected_bid.%s", a), registry) } for typ, statusMap := range newMetrics.RequestStatuses { @@ -585,7 +577,7 @@ func (me *Metrics) getAccountMetrics(id string) *accountMetrics { am.debugRequestMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.debug_requests", id), me.MetricsRegistry) am.bidsReceivedMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.bids_received", id), me.MetricsRegistry) am.priceHistogram = metrics.GetOrRegisterHistogram(fmt.Sprintf("account.%s.prices", id), me.MetricsRegistry, metrics.NewExpDecaySample(1028, 0.015)) - am.adapterMetrics = make(map[openrtb_ext.BidderName]*AdapterMetrics, len(me.exchanges)) + am.adapterMetrics = make(map[string]*AdapterMetrics, len(me.exchanges)) am.moduleMetrics = make(map[string]*ModuleMetrics) am.storedResponsesMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.stored_responses", id), me.MetricsRegistry) if !me.MetricsDisabled.AccountAdapterDetails { @@ -600,20 +592,6 @@ func (me *Metrics) getAccountMetrics(id string) *accountMetrics { am.bidValidationSecureMarkupMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.response.validation.secure.err", id), me.MetricsRegistry) am.bidValidationSecureMarkupWarnMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.response.validation.secure.warn", id), me.MetricsRegistry) - am.accountDeprecationWarningsPurpose1Meter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.gdpr.purpose1.warn", id), me.MetricsRegistry) - am.accountDeprecationWarningsPurpose2Meter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.gdpr.purpose2.warn", id), me.MetricsRegistry) - am.accountDeprecationWarningsPurpose3Meter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.gdpr.purpose3.warn", id), me.MetricsRegistry) - am.accountDeprecationWarningsPurpose4Meter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.gdpr.purpose4.warn", id), me.MetricsRegistry) - am.accountDeprecationWarningsPurpose5Meter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.gdpr.purpose5.warn", id), me.MetricsRegistry) - am.accountDeprecationWarningsPurpose6Meter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.gdpr.purpose6.warn", id), me.MetricsRegistry) - am.accountDeprecationWarningsPurpose7Meter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.gdpr.purpose7.warn", id), me.MetricsRegistry) - am.accountDeprecationWarningsPurpose8Meter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.gdpr.purpose8.warn", id), me.MetricsRegistry) - am.accountDeprecationWarningsPurpose9Meter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.gdpr.purpose9.warn", id), me.MetricsRegistry) - am.accountDeprecationWarningsPurpose10Meter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.gdpr.purpose10.warn", id), me.MetricsRegistry) - am.channelEnabledCCPAMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.ccpa.channel_enabled.warn", id), me.MetricsRegistry) - am.channelEnabledGDPRMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.gdpr.channel_enabled.warn", id), me.MetricsRegistry) - am.accountDeprecationSummaryMeter = metrics.GetOrRegisterMeter(fmt.Sprintf("account.%s.config.summary", id), me.MetricsRegistry) - if !me.MetricsDisabled.AccountModulesMetrics { for _, mod := range me.modules { am.moduleMetrics[mod] = makeBlankModuleMetrics() @@ -659,55 +637,6 @@ func (me *Metrics) RecordDebugRequest(debugEnabled bool, pubID string) { } } -func (me *Metrics) RecordAccountGDPRPurposeWarning(account string, purposeName string) { - if account != PublisherUnknown { - am := me.getAccountMetrics(account) - switch purposeName { - case "purpose1": - am.accountDeprecationWarningsPurpose1Meter.Mark(1) - case "purpose2": - am.accountDeprecationWarningsPurpose2Meter.Mark(1) - case "purpose3": - am.accountDeprecationWarningsPurpose3Meter.Mark(1) - case "purpose4": - am.accountDeprecationWarningsPurpose4Meter.Mark(1) - case "purpose5": - am.accountDeprecationWarningsPurpose5Meter.Mark(1) - case "purpose6": - am.accountDeprecationWarningsPurpose6Meter.Mark(1) - case "purpose7": - am.accountDeprecationWarningsPurpose7Meter.Mark(1) - case "purpose8": - am.accountDeprecationWarningsPurpose8Meter.Mark(1) - case "purpose9": - am.accountDeprecationWarningsPurpose9Meter.Mark(1) - case "purpose10": - am.accountDeprecationWarningsPurpose10Meter.Mark(1) - } - } -} - -func (me *Metrics) RecordAccountGDPRChannelEnabledWarning(account string) { - if account != PublisherUnknown { - am := me.getAccountMetrics(account) - am.channelEnabledGDPRMeter.Mark(1) - } -} - -func (me *Metrics) RecordAccountCCPAChannelEnabledWarning(account string) { - if account != PublisherUnknown { - am := me.getAccountMetrics(account) - am.channelEnabledCCPAMeter.Mark(1) - } -} - -func (me *Metrics) RecordAccountUpgradeStatus(account string) { - if account != PublisherUnknown { - am := me.getAccountMetrics(account) - am.accountDeprecationSummaryMeter.Mark(1) - } -} - func (me *Metrics) RecordStoredResponse(pubId string) { me.StoredResponsesMeter.Mark(1) if pubId != PublisherUnknown && !me.MetricsDisabled.AccountStoredResponses { @@ -790,9 +719,11 @@ func (me *Metrics) RecordStoredDataError(labels StoredDataLabels) { // RecordAdapterPanic implements a part of the MetricsEngine interface func (me *Metrics) RecordAdapterPanic(labels AdapterLabels) { - am, ok := me.AdapterMetrics[labels.Adapter] + adapterStr := string(labels.Adapter) + lowerCaseAdapterName := strings.ToLower(adapterStr) + am, ok := me.AdapterMetrics[lowerCaseAdapterName] if !ok { - glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", string(labels.Adapter)) + glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", adapterStr) return } am.PanicMeter.Mark(1) @@ -800,13 +731,15 @@ func (me *Metrics) RecordAdapterPanic(labels AdapterLabels) { // RecordAdapterRequest implements a part of the MetricsEngine interface func (me *Metrics) RecordAdapterRequest(labels AdapterLabels) { - am, ok := me.AdapterMetrics[labels.Adapter] + adapterStr := string(labels.Adapter) + lowerCaseAdapter := strings.ToLower(adapterStr) + am, ok := me.AdapterMetrics[lowerCaseAdapter] if !ok { - glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", string(labels.Adapter)) + glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", adapterStr) return } - aam, ok := me.getAccountMetrics(labels.PubID).adapterMetrics[labels.Adapter] + aam, ok := me.getAccountMetrics(labels.PubID).adapterMetrics[lowerCaseAdapter] switch labels.AdapterBids { case AdapterBidNone: am.NoBidMeter.Mark(1) @@ -839,8 +772,8 @@ func (me *Metrics) RecordAdapterConnections(adapterName openrtb_ext.BidderName, if me.MetricsDisabled.AdapterConnectionMetrics { return } - - am, ok := me.AdapterMetrics[adapterName] + lowerCaseAdapterName := strings.ToLower(string(adapterName)) + am, ok := me.AdapterMetrics[lowerCaseAdapterName] if !ok { glog.Errorf("Trying to log adapter connection metrics for %s: adapter not found", string(adapterName)) return @@ -863,7 +796,7 @@ func (me *Metrics) RecordTLSHandshakeTime(adapterName openrtb_ext.BidderName, tl return } - am, ok := me.AdapterMetrics[adapterName] + am, ok := me.AdapterMetrics[string(adapterName)] if !ok { glog.Errorf("Trying to log adapter TLS Handshake metrics for %s: adapter not found", string(adapterName)) return @@ -879,16 +812,18 @@ func (me *Metrics) RecordBidderServerResponseTime(bidderServerResponseTime time. // RecordAdapterBidReceived implements a part of the MetricsEngine interface. // This tracks how many bids from each Bidder use `adm` vs. `nurl. func (me *Metrics) RecordAdapterBidReceived(labels AdapterLabels, bidType openrtb_ext.BidType, hasAdm bool) { - am, ok := me.AdapterMetrics[labels.Adapter] + adapterStr := string(labels.Adapter) + lowerCaseAdapterName := strings.ToLower(adapterStr) + am, ok := me.AdapterMetrics[lowerCaseAdapterName] if !ok { - glog.Errorf("Trying to run adapter bid metrics on %s: adapter metrics not found", string(labels.Adapter)) + glog.Errorf("Trying to run adapter bid metrics on %s: adapter metrics not found", adapterStr) return } // Adapter metrics am.BidsReceivedMeter.Mark(1) // Account-Adapter metrics - if aam, ok := me.getAccountMetrics(labels.PubID).adapterMetrics[labels.Adapter]; ok { + if aam, ok := me.getAccountMetrics(labels.PubID).adapterMetrics[lowerCaseAdapterName]; ok { aam.BidsReceivedMeter.Mark(1) } @@ -905,15 +840,17 @@ func (me *Metrics) RecordAdapterBidReceived(labels AdapterLabels, bidType openrt // RecordAdapterPrice implements a part of the MetricsEngine interface. Generates a histogram of winning bid prices func (me *Metrics) RecordAdapterPrice(labels AdapterLabels, cpm float64) { - am, ok := me.AdapterMetrics[labels.Adapter] + adapterStr := string(labels.Adapter) + lowercaseAdapter := strings.ToLower(adapterStr) + am, ok := me.AdapterMetrics[lowercaseAdapter] if !ok { - glog.Errorf("Trying to run adapter price metrics on %s: adapter metrics not found", string(labels.Adapter)) + glog.Errorf("Trying to run adapter price metrics on %s: adapter metrics not found", adapterStr) return } // Adapter metrics am.PriceHistogram.Update(int64(cpm)) // Account-Adapter metrics - if aam, ok := me.getAccountMetrics(labels.PubID).adapterMetrics[labels.Adapter]; ok { + if aam, ok := me.getAccountMetrics(labels.PubID).adapterMetrics[lowercaseAdapter]; ok { aam.PriceHistogram.Update(int64(cpm)) } } @@ -927,7 +864,9 @@ func (me *Metrics) RecordRejectedBidsForBidder(bidder openrtb_ext.BidderName) { // RecordAdapterTime implements a part of the MetricsEngine interface. Records the adapter response time func (me *Metrics) RecordAdapterTime(labels AdapterLabels, length time.Duration) { - am, ok := me.AdapterMetrics[labels.Adapter] + adapterStr := string(labels.Adapter) + lowercaseAdapter := strings.ToLower(adapterStr) + am, ok := me.AdapterMetrics[lowercaseAdapter] if !ok { glog.Errorf("Trying to run adapter latency metrics on %s: adapter metrics not found", string(labels.Adapter)) return @@ -935,7 +874,7 @@ func (me *Metrics) RecordAdapterTime(labels AdapterLabels, length time.Duration) // Adapter metrics am.RequestTimer.Update(length) // Account-Adapter metrics - if aam, ok := me.getAccountMetrics(labels.PubID).adapterMetrics[labels.Adapter]; ok { + if aam, ok := me.getAccountMetrics(labels.PubID).adapterMetrics[lowercaseAdapter]; ok { aam.RequestTimer.Update(length) } } @@ -1048,13 +987,14 @@ func (me *Metrics) RecordRequestPrivacy(privacy PrivacyLabels) { } func (me *Metrics) RecordAdapterGDPRRequestBlocked(adapterName openrtb_ext.BidderName) { + adapterStr := string(adapterName) if me.MetricsDisabled.AdapterGDPRRequestBlocked { return } - am, ok := me.AdapterMetrics[adapterName] + am, ok := me.AdapterMetrics[strings.ToLower(adapterStr)] if !ok { - glog.Errorf("Trying to log adapter GDPR request blocked metric for %s: adapter not found", string(adapterName)) + glog.Errorf("Trying to log adapter GDPR request blocked metric for %s: adapter not found", adapterStr) return } @@ -1074,9 +1014,10 @@ func (me *Metrics) RecordAdsCertSignTime(adsCertSignTime time.Duration) { } func (me *Metrics) RecordBidValidationCreativeSizeError(adapter openrtb_ext.BidderName, pubID string) { - am, ok := me.AdapterMetrics[adapter] + adapterStr := string(adapter) + am, ok := me.AdapterMetrics[strings.ToLower(adapterStr)] if !ok { - glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", string(adapter)) + glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", adapterStr) return } am.BidValidationCreativeSizeErrorMeter.Mark(1) @@ -1088,9 +1029,10 @@ func (me *Metrics) RecordBidValidationCreativeSizeError(adapter openrtb_ext.Bidd } func (me *Metrics) RecordBidValidationCreativeSizeWarn(adapter openrtb_ext.BidderName, pubID string) { - am, ok := me.AdapterMetrics[adapter] + adapterStr := string(adapter) + am, ok := me.AdapterMetrics[strings.ToLower(adapterStr)] if !ok { - glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", string(adapter)) + glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", adapterStr) return } am.BidValidationCreativeSizeWarnMeter.Mark(1) @@ -1102,9 +1044,10 @@ func (me *Metrics) RecordBidValidationCreativeSizeWarn(adapter openrtb_ext.Bidde } func (me *Metrics) RecordBidValidationSecureMarkupError(adapter openrtb_ext.BidderName, pubID string) { - am, ok := me.AdapterMetrics[adapter] + adapterStr := string(adapter) + am, ok := me.AdapterMetrics[strings.ToLower(adapterStr)] if !ok { - glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", string(adapter)) + glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", adapterStr) return } am.BidValidationSecureMarkupErrorMeter.Mark(1) @@ -1116,9 +1059,10 @@ func (me *Metrics) RecordBidValidationSecureMarkupError(adapter openrtb_ext.Bidd } func (me *Metrics) RecordBidValidationSecureMarkupWarn(adapter openrtb_ext.BidderName, pubID string) { - am, ok := me.AdapterMetrics[adapter] + adapterStr := string(adapter) + am, ok := me.AdapterMetrics[strings.ToLower(adapterStr)] if !ok { - glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", string(adapter)) + glog.Errorf("Trying to run adapter metrics on %s: adapter metrics not found", adapterStr) return } am.BidValidationSecureMarkupWarnMeter.Mark(1) diff --git a/metrics/go_metrics_test.go b/metrics/go_metrics_test.go index 58b1559c16d..3118dcec84d 100644 --- a/metrics/go_metrics_test.go +++ b/metrics/go_metrics_test.go @@ -2,11 +2,12 @@ package metrics import ( "fmt" + "strings" "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" metrics "github.com/rcrowley/go-metrics" "github.com/stretchr/testify/assert" ) @@ -15,15 +16,15 @@ func TestNewMetrics(t *testing.T) { registry := metrics.NewRegistry() syncerKeys := []string{"foo"} moduleStageNames := map[string][]string{"foobar": {"entry", "raw"}, "another_module": {"raw", "auction"}} - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{}, syncerKeys, moduleStageNames) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("Adapter1"), openrtb_ext.BidderName("Adapter2")}, config.DisabledMetrics{}, syncerKeys, moduleStageNames) ensureContains(t, registry, "app_requests", m.AppRequestMeter) ensureContains(t, registry, "debug_requests", m.DebugRequestMeter) ensureContains(t, registry, "no_cookie_requests", m.NoCookieMeter) ensureContains(t, registry, "request_time", m.RequestTimer) ensureContains(t, registry, "amp_no_cookie_requests", m.AmpNoCookieMeter) - ensureContainsAdapterMetrics(t, registry, "adapter.appnexus", m.AdapterMetrics["appnexus"]) - ensureContainsAdapterMetrics(t, registry, "adapter.rubicon", m.AdapterMetrics["rubicon"]) + ensureContainsAdapterMetrics(t, registry, "adapter.adapter1", m.AdapterMetrics["adapter1"]) + ensureContainsAdapterMetrics(t, registry, "adapter.adapter2", m.AdapterMetrics["adapter2"]) ensureContains(t, registry, "cookie_sync_requests", m.CookieSyncMeter) ensureContains(t, registry, "cookie_sync_requests.ok", m.CookieSyncStatusMeter[CookieSyncOK]) ensureContains(t, registry, "cookie_sync_requests.bad_request", m.CookieSyncStatusMeter[CookieSyncBadRequest]) @@ -95,19 +96,21 @@ func TestNewMetrics(t *testing.T) { func TestRecordBidType(t *testing.T) { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{}, nil, nil) + adapterName := "FOO" + lowerCaseAdapterName := "foo" + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapterName)}, config.DisabledMetrics{}, nil, nil) m.RecordAdapterBidReceived(AdapterLabels{ - Adapter: openrtb_ext.BidderAppnexus, + Adapter: openrtb_ext.BidderName(adapterName), }, openrtb_ext.BidTypeBanner, true) - VerifyMetrics(t, "Appnexus Banner Adm Bids", m.AdapterMetrics[openrtb_ext.BidderAppnexus].MarkupMetrics[openrtb_ext.BidTypeBanner].AdmMeter.Count(), 1) - VerifyMetrics(t, "Appnexus Banner Nurl Bids", m.AdapterMetrics[openrtb_ext.BidderAppnexus].MarkupMetrics[openrtb_ext.BidTypeBanner].NurlMeter.Count(), 0) + VerifyMetrics(t, "foo Banner Adm Bids", m.AdapterMetrics[lowerCaseAdapterName].MarkupMetrics[openrtb_ext.BidTypeBanner].AdmMeter.Count(), 1) + VerifyMetrics(t, "foo Banner Nurl Bids", m.AdapterMetrics[lowerCaseAdapterName].MarkupMetrics[openrtb_ext.BidTypeBanner].NurlMeter.Count(), 0) m.RecordAdapterBidReceived(AdapterLabels{ - Adapter: openrtb_ext.BidderAppnexus, + Adapter: openrtb_ext.BidderName(adapterName), }, openrtb_ext.BidTypeVideo, false) - VerifyMetrics(t, "Appnexus Video Adm Bids", m.AdapterMetrics[openrtb_ext.BidderAppnexus].MarkupMetrics[openrtb_ext.BidTypeVideo].AdmMeter.Count(), 0) - VerifyMetrics(t, "Appnexus Video Nurl Bids", m.AdapterMetrics[openrtb_ext.BidderAppnexus].MarkupMetrics[openrtb_ext.BidTypeVideo].NurlMeter.Count(), 1) + VerifyMetrics(t, "foo Video Adm Bids", m.AdapterMetrics[lowerCaseAdapterName].MarkupMetrics[openrtb_ext.BidTypeVideo].AdmMeter.Count(), 0) + VerifyMetrics(t, "foo Video Nurl Bids", m.AdapterMetrics[lowerCaseAdapterName].MarkupMetrics[openrtb_ext.BidTypeVideo].NurlMeter.Count(), 1) } func ensureContains(t *testing.T, registry metrics.Registry, name string, metric interface{}) { @@ -198,17 +201,19 @@ func TestRecordBidTypeDisabledConfig(t *testing.T) { PubID: "acct-id", }, } - + adapter := "AnyName" + lowerCaseAdapter := "anyname" for _, test := range testCases { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, test.DisabledMetrics, nil, nil) + + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, test.DisabledMetrics, nil, nil) m.RecordAdapterBidReceived(AdapterLabels{ - Adapter: openrtb_ext.BidderAppnexus, + Adapter: openrtb_ext.BidderName(adapter), PubID: test.PubID, }, test.BidType, test.hasAdm) - assert.Equal(t, test.ExpectedAdmMeterCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].MarkupMetrics[test.BidType].AdmMeter.Count(), "Appnexus Banner Adm Bids") - assert.Equal(t, test.ExpectedNurlMeterCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].MarkupMetrics[test.BidType].NurlMeter.Count(), "Appnexus Banner Nurl Bids") + assert.Equal(t, test.ExpectedAdmMeterCount, m.AdapterMetrics[lowerCaseAdapter].MarkupMetrics[test.BidType].AdmMeter.Count(), "AnyName Banner Adm Bids") + assert.Equal(t, test.ExpectedNurlMeterCount, m.AdapterMetrics[lowerCaseAdapter].MarkupMetrics[test.BidType].NurlMeter.Count(), "AnyName Banner Nurl Bids") if test.DisabledMetrics.AccountAdapterDetails { assert.Len(t, m.accountMetrics[test.PubID].adapterMetrics, 0, "Test failed. Account metrics that contain adapter information are disabled, therefore we expect no entries in m.accountMetrics[accountId].adapterMetrics, we have %d \n", len(m.accountMetrics[test.PubID].adapterMetrics)) @@ -272,9 +277,10 @@ func TestRecordDebugRequest(t *testing.T) { expectedDebugCount: 0, }, } + adapter := "AnyName" for _, test := range testCases { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, test.givenDisabledMetrics, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, test.givenDisabledMetrics, nil, nil) m.RecordDebugRequest(test.givenDebugEnabledFlag, test.givenPubID) am := m.getAccountMetrics(test.givenPubID) @@ -311,16 +317,18 @@ func TestRecordBidValidationCreativeSize(t *testing.T) { expectedAccountCount: 0, }, } + adapter := "AnyName" + lowerCaseAdapter := "anyname" for _, test := range testCases { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, test.givenDisabledMetrics, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, test.givenDisabledMetrics, nil, nil) - m.RecordBidValidationCreativeSizeError(openrtb_ext.BidderAppnexus, test.givenPubID) - m.RecordBidValidationCreativeSizeWarn(openrtb_ext.BidderAppnexus, test.givenPubID) + m.RecordBidValidationCreativeSizeError(openrtb_ext.BidderName(adapter), test.givenPubID) + m.RecordBidValidationCreativeSizeWarn(openrtb_ext.BidderName(adapter), test.givenPubID) am := m.getAccountMetrics(test.givenPubID) - assert.Equal(t, test.expectedAdapterCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].BidValidationCreativeSizeErrorMeter.Count()) - assert.Equal(t, test.expectedAdapterCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].BidValidationCreativeSizeWarnMeter.Count()) + assert.Equal(t, test.expectedAdapterCount, m.AdapterMetrics[lowerCaseAdapter].BidValidationCreativeSizeErrorMeter.Count()) + assert.Equal(t, test.expectedAdapterCount, m.AdapterMetrics[lowerCaseAdapter].BidValidationCreativeSizeWarnMeter.Count()) assert.Equal(t, test.expectedAccountCount, am.bidValidationCreativeSizeMeter.Count()) assert.Equal(t, test.expectedAccountCount, am.bidValidationCreativeSizeWarnMeter.Count()) } @@ -353,16 +361,18 @@ func TestRecordBidValidationSecureMarkup(t *testing.T) { expectedAccountCount: 0, }, } + adapter := "AnyName" + lowerCaseAdapter := "anyname" for _, test := range testCases { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, test.givenDisabledMetrics, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, test.givenDisabledMetrics, nil, nil) - m.RecordBidValidationSecureMarkupError(openrtb_ext.BidderAppnexus, test.givenPubID) - m.RecordBidValidationSecureMarkupWarn(openrtb_ext.BidderAppnexus, test.givenPubID) + m.RecordBidValidationSecureMarkupError(openrtb_ext.BidderName(adapter), test.givenPubID) + m.RecordBidValidationSecureMarkupWarn(openrtb_ext.BidderName(adapter), test.givenPubID) am := m.getAccountMetrics(test.givenPubID) - assert.Equal(t, test.expectedAdapterCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].BidValidationSecureMarkupErrorMeter.Count()) - assert.Equal(t, test.expectedAdapterCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].BidValidationSecureMarkupWarnMeter.Count()) + assert.Equal(t, test.expectedAdapterCount, m.AdapterMetrics[lowerCaseAdapter].BidValidationSecureMarkupErrorMeter.Count()) + assert.Equal(t, test.expectedAdapterCount, m.AdapterMetrics[lowerCaseAdapter].BidValidationSecureMarkupWarnMeter.Count()) assert.Equal(t, test.expectedAccountCount, am.bidValidationSecureMarkupMeter.Count()) assert.Equal(t, test.expectedAccountCount, am.bidValidationSecureMarkupWarnMeter.Count()) } @@ -385,9 +395,10 @@ func TestRecordDNSTime(t *testing.T) { outExpDuration: time.Duration(0), }, } + adapter := "AnyName" for _, test := range testCases { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) m.RecordDNSTime(test.inDnsLookupDuration) @@ -434,13 +445,15 @@ func TestRecordTLSHandshakeTime(t *testing.T) { }, }, } + adapter := "AnyName" for _, test := range testCases { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) - m.RecordTLSHandshakeTime(test.in.adapterName, test.in.tLSHandshakeDuration) + m.RecordTLSHandshakeTime(openrtb_ext.BidderName(strings.ToLower(adapter)), test.in.tLSHandshakeDuration) + + assert.Equal(t, test.out.expectedDuration.Nanoseconds(), m.AdapterMetrics[strings.ToLower(adapter)].TLSHandshakeTimer.Sum(), test.description) - assert.Equal(t, test.out.expectedDuration.Nanoseconds(), m.AdapterMetrics[openrtb_ext.BidderAppnexus].TLSHandshakeTimer.Sum(), test.description) } } @@ -464,9 +477,10 @@ func TestRecordBidderServerResponseTime(t *testing.T) { expectedSum: 1000, }, } + adapter := "AnyName" for _, test := range testCases { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) m.RecordBidderServerResponseTime(test.time) @@ -489,7 +503,8 @@ func TestRecordAdapterConnections(t *testing.T) { expectedConnCreatedCount int64 expectedConnWaitTime time.Duration } - + adapter := "AnyName" + lowerCaseAdapterName := "anyname" testCases := []struct { description string in testIn @@ -498,7 +513,7 @@ func TestRecordAdapterConnections(t *testing.T) { { description: "Successful, new connection created, has connection wait", in: testIn{ - adapterName: openrtb_ext.BidderAppnexus, + adapterName: openrtb_ext.BidderName(adapter), connWasReused: false, connWait: time.Second * 5, connMetricsDisabled: false, @@ -512,7 +527,7 @@ func TestRecordAdapterConnections(t *testing.T) { { description: "Successful, new connection created, has connection wait", in: testIn{ - adapterName: openrtb_ext.BidderAppnexus, + adapterName: openrtb_ext.BidderName(adapter), connWasReused: false, connWait: time.Second * 4, connMetricsDisabled: false, @@ -525,7 +540,7 @@ func TestRecordAdapterConnections(t *testing.T) { { description: "Successful, was reused, no connection wait", in: testIn{ - adapterName: openrtb_ext.BidderAppnexus, + adapterName: openrtb_ext.BidderName(adapter), connWasReused: true, connMetricsDisabled: false, }, @@ -537,7 +552,7 @@ func TestRecordAdapterConnections(t *testing.T) { { description: "Successful, was reused, has connection wait", in: testIn{ - adapterName: openrtb_ext.BidderAppnexus, + adapterName: openrtb_ext.BidderName(adapter), connWasReused: true, connWait: time.Second * 5, connMetricsDisabled: false, @@ -560,7 +575,7 @@ func TestRecordAdapterConnections(t *testing.T) { { description: "Adapter connection metrics are disabled, nothing gets updated", in: testIn{ - adapterName: openrtb_ext.BidderAppnexus, + adapterName: openrtb_ext.BidderName(adapter), connWasReused: false, connWait: time.Second * 5, connMetricsDisabled: true, @@ -571,19 +586,18 @@ func TestRecordAdapterConnections(t *testing.T) { for i, test := range testCases { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AdapterConnectionMetrics: test.in.connMetricsDisabled}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, config.DisabledMetrics{AdapterConnectionMetrics: test.in.connMetricsDisabled}, nil, nil) m.RecordAdapterConnections(test.in.adapterName, test.in.connWasReused, test.in.connWait) - - assert.Equal(t, test.out.expectedConnReusedCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].ConnReused.Count(), "Test [%d] incorrect number of reused connections to adapter", i) - assert.Equal(t, test.out.expectedConnCreatedCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].ConnCreated.Count(), "Test [%d] incorrect number of new connections to adapter created", i) - assert.Equal(t, test.out.expectedConnWaitTime.Nanoseconds(), m.AdapterMetrics[openrtb_ext.BidderAppnexus].ConnWaitTime.Sum(), "Test [%d] incorrect wait time in connection to adapter", i) + assert.Equal(t, test.out.expectedConnReusedCount, m.AdapterMetrics[lowerCaseAdapterName].ConnReused.Count(), "Test [%d] incorrect number of reused connections to adapter", i) + assert.Equal(t, test.out.expectedConnCreatedCount, m.AdapterMetrics[lowerCaseAdapterName].ConnCreated.Count(), "Test [%d] incorrect number of new connections to adapter created", i) + assert.Equal(t, test.out.expectedConnWaitTime.Nanoseconds(), m.AdapterMetrics[lowerCaseAdapterName].ConnWaitTime.Sum(), "Test [%d] incorrect wait time in connection to adapter", i) } } func TestNewMetricsWithDisabledConfig(t *testing.T) { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{AccountAdapterDetails: true, AccountModulesMetrics: true}, nil, map[string][]string{"foobar": {"entry", "raw"}}) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("Foo"), openrtb_ext.BidderName("bar")}, config.DisabledMetrics{AccountAdapterDetails: true, AccountModulesMetrics: true}, nil, map[string][]string{"foobar": {"entry", "raw"}}) assert.True(t, m.MetricsDisabled.AccountAdapterDetails, "Accound adapter metrics should be disabled") assert.True(t, m.MetricsDisabled.AccountModulesMetrics, "Accound modules metrics should be disabled") @@ -591,7 +605,7 @@ func TestNewMetricsWithDisabledConfig(t *testing.T) { func TestRecordPrebidCacheRequestTimeWithSuccess(t *testing.T) { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("Foo")}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) m.RecordPrebidCacheRequestTime(true, 42) @@ -601,7 +615,7 @@ func TestRecordPrebidCacheRequestTimeWithSuccess(t *testing.T) { func TestRecordPrebidCacheRequestTimeWithNotSuccess(t *testing.T) { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("Foo")}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) m.RecordPrebidCacheRequestTime(false, 42) @@ -669,7 +683,7 @@ func TestRecordStoredDataFetchTime(t *testing.T) { for _, tt := range tests { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("Foo"), openrtb_ext.BidderName("Bar")}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) m.RecordStoredDataFetchTime(StoredDataLabels{ DataType: tt.dataType, DataFetchType: tt.fetchType, @@ -743,7 +757,7 @@ func TestRecordStoredDataError(t *testing.T) { for _, tt := range tests { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("Foo"), openrtb_ext.BidderName("Bar")}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) m.RecordStoredDataError(StoredDataLabels{ DataType: tt.dataType, Error: tt.errorType, @@ -756,7 +770,7 @@ func TestRecordStoredDataError(t *testing.T) { func TestRecordRequestPrivacy(t *testing.T) { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("Foo"), openrtb_ext.BidderName("Bar")}, config.DisabledMetrics{AccountAdapterDetails: true}, nil, nil) // CCPA m.RecordRequestPrivacy(PrivacyLabels{ @@ -802,6 +816,8 @@ func TestRecordRequestPrivacy(t *testing.T) { func TestRecordAdapterGDPRRequestBlocked(t *testing.T) { var fakeBidder openrtb_ext.BidderName = "fooAdvertising" + adapter := "AnyName" + lowerCaseAdapterName := "anyname" tests := []struct { description string @@ -812,7 +828,7 @@ func TestRecordAdapterGDPRRequestBlocked(t *testing.T) { { description: "", metricsDisabled: false, - adapterName: openrtb_ext.BidderAppnexus, + adapterName: openrtb_ext.BidderName(adapter), expectedCount: 1, }, { @@ -824,24 +840,23 @@ func TestRecordAdapterGDPRRequestBlocked(t *testing.T) { { description: "", metricsDisabled: true, - adapterName: openrtb_ext.BidderAppnexus, + adapterName: openrtb_ext.BidderName(adapter), expectedCount: 0, }, } - for _, tt := range tests { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AdapterGDPRRequestBlocked: tt.metricsDisabled}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, config.DisabledMetrics{AdapterGDPRRequestBlocked: tt.metricsDisabled}, nil, nil) m.RecordAdapterGDPRRequestBlocked(tt.adapterName) - assert.Equal(t, tt.expectedCount, m.AdapterMetrics[openrtb_ext.BidderAppnexus].GDPRRequestBlocked.Count(), tt.description) + assert.Equal(t, tt.expectedCount, m.AdapterMetrics[lowerCaseAdapterName].GDPRRequestBlocked.Count(), tt.description) } } func TestRecordCookieSync(t *testing.T) { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("Foo"), openrtb_ext.BidderName("Bar")}, config.DisabledMetrics{}, nil, nil) // Known m.RecordCookieSync(CookieSyncBadRequest) @@ -859,7 +874,7 @@ func TestRecordCookieSync(t *testing.T) { func TestRecordSyncerRequest(t *testing.T) { registry := metrics.NewRegistry() syncerKeys := []string{"foo"} - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{}, syncerKeys, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("Adapter1"), openrtb_ext.BidderName("Adapter2")}, config.DisabledMetrics{}, syncerKeys, nil) // Known m.RecordSyncerRequest("foo", SyncerCookieSyncOK) @@ -878,7 +893,7 @@ func TestRecordSyncerRequest(t *testing.T) { func TestRecordSetUid(t *testing.T) { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("Foo"), openrtb_ext.BidderName("Bar")}, config.DisabledMetrics{}, nil, nil) // Known m.RecordSetUid(SetUidOptOut) @@ -897,7 +912,7 @@ func TestRecordSetUid(t *testing.T) { func TestRecordSyncerSet(t *testing.T) { registry := metrics.NewRegistry() syncerKeys := []string{"foo"} - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus, openrtb_ext.BidderRubicon}, config.DisabledMetrics{}, syncerKeys, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("Adapter1"), openrtb_ext.BidderName("Adapter2")}, config.DisabledMetrics{}, syncerKeys, nil) // Known m.RecordSyncerSet("foo", SyncerSetUidCleared) @@ -970,7 +985,7 @@ func TestStoredResponses(t *testing.T) { } for _, test := range testCases { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{AccountStoredResponses: test.accountStoredResponsesMetricsDisabled}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("AnyName")}, config.DisabledMetrics{AccountStoredResponses: test.accountStoredResponsesMetricsDisabled}, nil, nil) m.RecordStoredResponse(test.givenPubID) am := m.getAccountMetrics(test.givenPubID) @@ -1004,7 +1019,7 @@ func TestRecordAdsCertSignTime(t *testing.T) { } for _, test := range testCases { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("AnyName")}, config.DisabledMetrics{}, nil, nil) m.RecordAdsCertSignTime(test.inAdsCertSignDuration) @@ -1035,7 +1050,7 @@ func TestRecordAdsCertReqMetric(t *testing.T) { for _, test := range testCases { registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{}, nil, nil) + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName("AnyName")}, config.DisabledMetrics{}, nil, nil) m.RecordAdsCertReq(test.requestSuccess) @@ -1107,169 +1122,6 @@ func TestRecordModuleAccountMetrics(t *testing.T) { } } -func TestRecordAccountGDPRPurposeWarningMetrics(t *testing.T) { - testCases := []struct { - name string - givenPurposeName string - expectedP1MetricCount int64 - expectedP2MetricCount int64 - expectedP3MetricCount int64 - expectedP4MetricCount int64 - expectedP5MetricCount int64 - expectedP6MetricCount int64 - expectedP7MetricCount int64 - expectedP8MetricCount int64 - expectedP9MetricCount int64 - expectedP10MetricCount int64 - }{ - { - name: "Purpose1MetricIncremented", - givenPurposeName: "purpose1", - expectedP1MetricCount: 1, - }, - { - name: "Purpose2MetricIncremented", - givenPurposeName: "purpose2", - expectedP2MetricCount: 1, - }, - { - name: "Purpose3MetricIncremented", - givenPurposeName: "purpose3", - expectedP3MetricCount: 1, - }, - { - name: "Purpose4MetricIncremented", - givenPurposeName: "purpose4", - expectedP4MetricCount: 1, - }, - { - name: "Purpose5MetricIncremented", - givenPurposeName: "purpose5", - expectedP5MetricCount: 1, - }, - { - name: "Purpose6MetricIncremented", - givenPurposeName: "purpose6", - expectedP6MetricCount: 1, - }, - { - name: "Purpose7MetricIncremented", - givenPurposeName: "purpose7", - expectedP7MetricCount: 1, - }, - { - name: "Purpose8MetricIncremented", - givenPurposeName: "purpose8", - expectedP8MetricCount: 1, - }, - { - name: "Purpose9MetricIncremented", - givenPurposeName: "purpose9", - expectedP9MetricCount: 1, - }, - { - name: "Purpose10MetricIncremented", - givenPurposeName: "purpose10", - expectedP10MetricCount: 1, - }, - } - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{}, nil, nil) - - m.RecordAccountGDPRPurposeWarning("acct-id", test.givenPurposeName) - am := m.getAccountMetrics("acct-id") - - assert.Equal(t, test.expectedP1MetricCount, am.accountDeprecationWarningsPurpose1Meter.Count()) - assert.Equal(t, test.expectedP2MetricCount, am.accountDeprecationWarningsPurpose2Meter.Count()) - assert.Equal(t, test.expectedP3MetricCount, am.accountDeprecationWarningsPurpose3Meter.Count()) - assert.Equal(t, test.expectedP4MetricCount, am.accountDeprecationWarningsPurpose4Meter.Count()) - assert.Equal(t, test.expectedP5MetricCount, am.accountDeprecationWarningsPurpose5Meter.Count()) - assert.Equal(t, test.expectedP6MetricCount, am.accountDeprecationWarningsPurpose6Meter.Count()) - assert.Equal(t, test.expectedP7MetricCount, am.accountDeprecationWarningsPurpose7Meter.Count()) - assert.Equal(t, test.expectedP8MetricCount, am.accountDeprecationWarningsPurpose8Meter.Count()) - assert.Equal(t, test.expectedP9MetricCount, am.accountDeprecationWarningsPurpose9Meter.Count()) - assert.Equal(t, test.expectedP10MetricCount, am.accountDeprecationWarningsPurpose10Meter.Count()) - }) - } -} - -func TestRecordAccountGDPRChannelEnabledWarningMetrics(t *testing.T) { - testCases := []struct { - name string - givenPubID string - expectedMetricCount int64 - }{ - { - name: "GdprChannelMetricIncremented", - givenPubID: "acct-id", - expectedMetricCount: 1, - }, - } - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{}, nil, nil) - - m.RecordAccountGDPRChannelEnabledWarning(test.givenPubID) - am := m.getAccountMetrics(test.givenPubID) - - assert.Equal(t, test.expectedMetricCount, am.channelEnabledGDPRMeter.Count()) - }) - } -} - -func TestRecordAccountCCPAChannelEnabledWarningMetrics(t *testing.T) { - testCases := []struct { - name string - givenPubID string - expectedMetricCount int64 - }{ - { - name: "CcpaChannelMetricIncremented", - givenPubID: "acct-id", - expectedMetricCount: 1, - }, - } - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{}, nil, nil) - - m.RecordAccountCCPAChannelEnabledWarning(test.givenPubID) - am := m.getAccountMetrics(test.givenPubID) - - assert.Equal(t, test.expectedMetricCount, am.channelEnabledCCPAMeter.Count()) - }) - } -} - -func TestRecordAccountUpgradeStatusMetrics(t *testing.T) { - testCases := []struct { - name string - givenPubID string - expectedMetricCount int64 - }{ - { - name: "AccountDeprecationMeterIncremented", - givenPubID: "acct-id", - expectedMetricCount: 1, - }, - } - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - registry := metrics.NewRegistry() - m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderAppnexus}, config.DisabledMetrics{}, nil, nil) - - m.RecordAccountUpgradeStatus(test.givenPubID) - am := m.getAccountMetrics(test.givenPubID) - - assert.Equal(t, test.expectedMetricCount, am.accountDeprecationSummaryMeter.Count()) - }) - } -} - func TestRecordOverheadTime(t *testing.T) { testCases := []struct { name string @@ -1335,3 +1187,127 @@ func VerifyMetrics(t *testing.T, name string, expected int64, actual int64) { t.Errorf("Error in metric %s: expected %d, got %d.", name, expected, actual) } } + +func TestRecordAdapterPanic(t *testing.T) { + registry := metrics.NewRegistry() + adapter := "AnyName" + lowerCaseAdapterName := "anyname" + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, config.DisabledMetrics{AccountAdapterDetails: true, AccountModulesMetrics: true}, nil, map[string][]string{"foobar": {"entry", "raw"}}) + m.RecordAdapterPanic(AdapterLabels{Adapter: openrtb_ext.BidderName(adapter)}) + assert.Equal(t, m.AdapterMetrics[lowerCaseAdapterName].PanicMeter.Count(), int64(1)) +} + +func TestRecordAdapterPrice(t *testing.T) { + registry := metrics.NewRegistry() + syncerKeys := []string{"foo"} + adapter := "AnyName" + lowerCaseAdapterName := "anyname" + pubID := "pub1" + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter), openrtb_ext.BidderAppnexus}, config.DisabledMetrics{}, syncerKeys, nil) + m.RecordAdapterPrice(AdapterLabels{Adapter: openrtb_ext.BidderName(adapter), PubID: pubID}, 1000) + assert.Equal(t, m.AdapterMetrics[lowerCaseAdapterName].PriceHistogram.Max(), int64(1000)) + assert.Equal(t, m.getAccountMetrics(pubID).adapterMetrics[lowerCaseAdapterName].PriceHistogram.Max(), int64(1000)) +} + +func TestRecordAdapterTime(t *testing.T) { + registry := metrics.NewRegistry() + syncerKeys := []string{"foo"} + adapter := "AnyName" + lowerCaseAdapterName := "anyname" + pubID := "pub1" + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter), openrtb_ext.BidderAppnexus, openrtb_ext.BidderName("Adapter2")}, config.DisabledMetrics{}, syncerKeys, nil) + m.RecordAdapterTime(AdapterLabels{Adapter: openrtb_ext.BidderName(adapter), PubID: pubID}, 1000) + assert.Equal(t, m.AdapterMetrics[lowerCaseAdapterName].RequestTimer.Max(), int64(1000)) + assert.Equal(t, m.getAccountMetrics(pubID).adapterMetrics[lowerCaseAdapterName].RequestTimer.Max(), int64(1000)) +} + +func TestRecordAdapterRequest(t *testing.T) { + syncerKeys := []string{"foo"} + moduleStageNames := map[string][]string{"foobar": {"entry", "raw"}, "another_module": {"raw", "auction"}} + adapter := "AnyName" + lowerCaseAdapter := "anyname" + type errorCount struct { + badInput, badServer, timeout, failedToRequestBid, validation, tmaxTimeout, unknown int64 + } + type adapterBidsCount struct { + NoBid, GotBid int64 + } + tests := []struct { + description string + labels AdapterLabels + expectedNoCookieCount int64 + expectedAdapterBidsCount adapterBidsCount + expectedErrorCount errorCount + }{ + { + description: "no-bid", + labels: AdapterLabels{ + Adapter: openrtb_ext.BidderName(adapter), + AdapterBids: AdapterBidNone, + PubID: "acc-1", + }, + expectedAdapterBidsCount: adapterBidsCount{NoBid: 1}, + }, + { + description: "got-bid", + labels: AdapterLabels{ + Adapter: openrtb_ext.BidderName(adapter), + AdapterBids: AdapterBidPresent, + PubID: "acc-2", + }, + expectedAdapterBidsCount: adapterBidsCount{GotBid: 1}, + }, + { + description: "adapter-errors", + labels: AdapterLabels{ + Adapter: openrtb_ext.BidderName(adapter), + PubID: "acc-1", + AdapterErrors: map[AdapterError]struct{}{ + AdapterErrorBadInput: {}, + AdapterErrorBadServerResponse: {}, + AdapterErrorFailedToRequestBids: {}, + AdapterErrorTimeout: {}, + AdapterErrorValidation: {}, + AdapterErrorTmaxTimeout: {}, + AdapterErrorUnknown: {}, + }, + }, + expectedErrorCount: errorCount{ + badInput: 1, + badServer: 1, + timeout: 1, + failedToRequestBid: 1, + validation: 1, + tmaxTimeout: 1, + unknown: 1, + }, + }, + } + for _, test := range tests { + t.Run(test.description, func(t *testing.T) { + registry := metrics.NewRegistry() + m := NewMetrics(registry, []openrtb_ext.BidderName{openrtb_ext.BidderName(adapter)}, config.DisabledMetrics{}, syncerKeys, moduleStageNames) + m.RecordAdapterRequest(test.labels) + adapterMetric := m.AdapterMetrics[lowerCaseAdapter] + if assert.NotNil(t, adapterMetric) { + assert.Equal(t, test.expectedAdapterBidsCount, adapterBidsCount{ + NoBid: adapterMetric.NoBidMeter.Count(), + GotBid: adapterMetric.GotBidsMeter.Count(), + }) + } + assert.Equal(t, test.expectedNoCookieCount, adapterMetric.NoCookieMeter.Count()) + adapterErrMetric := adapterMetric.ErrorMeters + if assert.NotNil(t, adapterErrMetric) { + assert.Equal(t, test.expectedErrorCount, errorCount{ + badInput: adapterErrMetric[AdapterErrorBadInput].Count(), + badServer: adapterErrMetric[AdapterErrorBadServerResponse].Count(), + timeout: adapterErrMetric[AdapterErrorTimeout].Count(), + failedToRequestBid: adapterErrMetric[AdapterErrorFailedToRequestBids].Count(), + validation: adapterErrMetric[AdapterErrorValidation].Count(), + tmaxTimeout: adapterErrMetric[AdapterErrorTmaxTimeout].Count(), + unknown: adapterErrMetric[AdapterErrorUnknown].Count(), + }) + } + }) + } +} diff --git a/metrics/metrics.go b/metrics/metrics.go index 8a77a2f1278..682589f8e88 100644 --- a/metrics/metrics.go +++ b/metrics/metrics.go @@ -3,7 +3,7 @@ package metrics import ( "time" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // Labels defines the labels that can be attached to the metrics. @@ -173,6 +173,7 @@ const PublisherUnknown = "unknown" const ( DemandWeb DemandSource = "web" DemandApp DemandSource = "app" + DemandDOOH DemandSource = "dooh" DemandUnknown DemandSource = "unknown" ) @@ -180,22 +181,25 @@ func DemandTypes() []DemandSource { return []DemandSource{ DemandWeb, DemandApp, + DemandDOOH, DemandUnknown, } } // The request types (endpoints) const ( - ReqTypeORTB2Web RequestType = "openrtb2-web" - ReqTypeORTB2App RequestType = "openrtb2-app" - ReqTypeAMP RequestType = "amp" - ReqTypeVideo RequestType = "video" + ReqTypeORTB2Web RequestType = "openrtb2-web" + ReqTypeORTB2App RequestType = "openrtb2-app" + ReqTypeORTB2DOOH RequestType = "openrtb2-dooh" + ReqTypeAMP RequestType = "amp" + ReqTypeVideo RequestType = "video" ) func RequestTypes() []RequestType { return []RequestType{ ReqTypeORTB2Web, ReqTypeORTB2App, + ReqTypeORTB2DOOH, ReqTypeAMP, ReqTypeVideo, } @@ -276,6 +280,7 @@ const ( AdapterErrorTimeout AdapterError = "timeout" AdapterErrorFailedToRequestBids AdapterError = "failedtorequestbid" AdapterErrorValidation AdapterError = "validation" + AdapterErrorTmaxTimeout AdapterError = "tmaxtimeout" AdapterErrorUnknown AdapterError = "unknown_error" ) @@ -286,6 +291,7 @@ func AdapterErrors() []AdapterError { AdapterErrorTimeout, AdapterErrorFailedToRequestBids, AdapterErrorValidation, + AdapterErrorTmaxTimeout, AdapterErrorUnknown, } } @@ -478,10 +484,6 @@ type MetricsEngine interface { RecordModuleSuccessRejected(labels ModuleLabels) RecordModuleExecutionError(labels ModuleLabels) RecordModuleTimeout(labels ModuleLabels) - RecordAccountGDPRPurposeWarning(account string, purposeName string) - RecordAccountGDPRChannelEnabledWarning(account string) - RecordAccountCCPAChannelEnabledWarning(account string) - RecordAccountUpgradeStatus(account string) // RecordAdapterDuplicateBidID captures the bid.ID collisions when adaptor // gives the bid response with multiple bids containing same bid.ID diff --git a/metrics/metrics_mock.go b/metrics/metrics_mock.go index e2f71232bf2..07e09db5f0a 100644 --- a/metrics/metrics_mock.go +++ b/metrics/metrics_mock.go @@ -3,7 +3,7 @@ package metrics import ( "time" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/mock" ) @@ -234,22 +234,6 @@ func (me *MetricsEngineMock) RecordModuleTimeout(labels ModuleLabels) { me.Called(labels) } -func (me *MetricsEngineMock) RecordAccountGDPRPurposeWarning(account string, purposeName string) { - me.Called(account, purposeName) -} - -func (me *MetricsEngineMock) RecordAccountGDPRChannelEnabledWarning(account string) { - me.Called(account) -} - -func (me *MetricsEngineMock) RecordAccountCCPAChannelEnabledWarning(account string) { - me.Called(account) -} - -func (me *MetricsEngineMock) RecordAccountUpgradeStatus(account string) { - me.Called(account) -} - func (me *MetricsEngineMock) RecordRejectedBids(pubid, bidder, code string) { me.Called(pubid, bidder, code) } diff --git a/metrics/prometheus/preload.go b/metrics/prometheus/preload.go index dae7c14dc5b..a4a70017355 100644 --- a/metrics/prometheus/preload.go +++ b/metrics/prometheus/preload.go @@ -1,29 +1,31 @@ package prometheusmetrics import ( - "github.com/prebid/prebid-server/metrics" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/prometheus/client_golang/prometheus" ) func preloadLabelValues(m *Metrics, syncerKeys []string, moduleStageNames map[string][]string) { var ( - setUidStatusValues = setUidStatusesAsString() - adapterErrorValues = adapterErrorsAsString() - adapterValues = adaptersAsString() + adapterErrorValues = enumAsString(metrics.AdapterErrors()) + adapterValues = enumAsLowerCaseString(openrtb_ext.CoreBidderNames()) bidTypeValues = []string{markupDeliveryAdm, markupDeliveryNurl} boolValues = boolValuesAsString() - cacheResultValues = cacheResultsAsString() + cacheResultValues = enumAsString(metrics.CacheResults()) connectionErrorValues = []string{connectionAcceptError, connectionCloseError} - cookieValues = cookieTypesAsString() - cookieSyncStatusValues = cookieSyncStatusesAsString() - overheadTypes = overheadTypesAsString() - requestTypeValues = requestTypesAsString() - requestStatusValues = requestStatusesAsString() - storedDataFetchTypeValues = storedDataFetchTypesAsString() - storedDataErrorValues = storedDataErrorsAsString() - syncerRequestStatusValues = syncerRequestStatusesAsString() - syncerSetsStatusValues = syncerSetStatusesAsString() + cookieSyncStatusValues = enumAsString(metrics.CookieSyncStatuses()) + cookieValues = enumAsString(metrics.CookieTypes()) + overheadTypes = enumAsString(metrics.OverheadTypes()) + requestStatusValues = enumAsString(metrics.RequestStatuses()) + requestTypeValues = enumAsString(metrics.RequestTypes()) + setUidStatusValues = enumAsString(metrics.SetUidStatuses()) sourceValues = []string{sourceRequest} + storedDataErrorValues = enumAsString(metrics.StoredDataErrors()) + storedDataFetchTypeValues = enumAsString(metrics.StoredDataFetchTypes()) + syncerRequestStatusValues = enumAsString(metrics.SyncerRequestStatuses()) + syncerSetsStatusValues = enumAsString(metrics.SyncerSetUidStatuses()) + tcfVersionValues = enumAsString(metrics.TCFVersions()) ) preloadLabelValuesForCounter(m.connectionsError, map[string][]string{ @@ -224,7 +226,7 @@ func preloadLabelValues(m *Metrics, syncerKeys []string, moduleStageNames map[st preloadLabelValuesForCounter(m.privacyTCF, map[string][]string{ sourceLabel: sourceValues, - versionLabel: tcfVersionsAsString(), + versionLabel: tcfVersionValues, }) if !m.metricsDisabled.AdapterGDPRRequestBlocked { diff --git a/metrics/prometheus/prometheus.go b/metrics/prometheus/prometheus.go index cfaa87bf4ae..c72de8463a6 100644 --- a/metrics/prometheus/prometheus.go +++ b/metrics/prometheus/prometheus.go @@ -3,11 +3,12 @@ package prometheusmetrics import ( "fmt" "strconv" + "strings" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/prometheus/client_golang/prometheus" promCollector "github.com/prometheus/client_golang/prometheus/collectors" ) @@ -90,21 +91,6 @@ type Metrics struct { accountBidResponseSecureMarkupError *prometheus.CounterVec accountBidResponseSecureMarkupWarn *prometheus.CounterVec - // Account Deprecation Metrics - accountDeprecationWarningsPurpose1 prometheus.Counter - accountDeprecationWarningsPurpose2 prometheus.Counter - accountDeprecationWarningsPurpose3 prometheus.Counter - accountDeprecationWarningsPurpose4 prometheus.Counter - accountDeprecationWarningsPurpose5 prometheus.Counter - accountDeprecationWarningsPurpose6 prometheus.Counter - accountDeprecationWarningsPurpose7 prometheus.Counter - accountDeprecationWarningsPurpose8 prometheus.Counter - accountDeprecationWarningsPurpose9 prometheus.Counter - accountDeprecationWarningsPurpose10 prometheus.Counter - channelEnabledGDPR prometheus.Counter - channelEnabledCCPA prometheus.Counter - accountDeprecationSummary prometheus.Counter - // Module Metrics as a map where the key is the module name moduleDuration map[string]*prometheus.HistogramVec moduleCalls map[string]*prometheus.CounterVec @@ -532,48 +518,6 @@ func NewMetrics(cfg config.PrometheusMetrics, reg *prometheus.Registry, disabled "Count of AdsCert request, and if they were successfully sent.", []string{successLabel}) - metrics.accountDeprecationWarningsPurpose1 = newCounterWithoutLabels(cfg, reg, - "account_config_gdpr_tcf2_purpose1_warn", - "Count of requests referencing an account whose config specifies a deprecated gdpr.tcf2.purpose1 field") - metrics.accountDeprecationWarningsPurpose2 = newCounterWithoutLabels(cfg, reg, - "account_config_gdpr_tcf2_purpose2_warn", - "Count of requests referencing an account whose config specifies a deprecated gdpr.tcf2.purpose2 field") - metrics.accountDeprecationWarningsPurpose3 = newCounterWithoutLabels(cfg, reg, - "account_config_gdpr_tcf2_purpose3_warn", - "Count of requests referencing an account whose config specifies a deprecated gdpr.tcf2.purpose3 field") - metrics.accountDeprecationWarningsPurpose4 = newCounterWithoutLabels(cfg, reg, - "account_config_gdpr_tcf2_purpose4_warn", - "Count of requests referencing an account whose config specifies a deprecated gdpr.tcf2.purpose4 field") - metrics.accountDeprecationWarningsPurpose5 = newCounterWithoutLabels(cfg, reg, - "account_config_gdpr_tcf2_purpose5_warn", - "Count of requests referencing an account whose config specifies a deprecated gdpr.tcf2.purpose5 field") - metrics.accountDeprecationWarningsPurpose6 = newCounterWithoutLabels(cfg, reg, - "account_config_gdpr_tcf2_purpose6_warn", - "Count of requests referencing an account whose config specifies a deprecated gdpr.tcf2.purpose6 field") - metrics.accountDeprecationWarningsPurpose7 = newCounterWithoutLabels(cfg, reg, - "account_config_gdpr_tcf2_purpose7_warn", - "Count of requests referencing an account whose config specifies a deprecated gdpr.tcf2.purpose7 field") - metrics.accountDeprecationWarningsPurpose8 = newCounterWithoutLabels(cfg, reg, - "account_config_gdpr_tcf2_purpose8_warn", - "Count of requests referencing an account whose config specifies a deprecated gdpr.tcf2.purpose8 field") - metrics.accountDeprecationWarningsPurpose9 = newCounterWithoutLabels(cfg, reg, - "account_config_gdpr_tcf2_purpose9_warn", - "Count of requests referencing an account whose config specifies a deprecated gdpr.tcf2.purpose9 field") - metrics.accountDeprecationWarningsPurpose10 = newCounterWithoutLabels(cfg, reg, - "account_config_gdpr_tcf2_purpose10_warn", - "Count of requests referencing an account whose config specifies a deprecated gdpr.tcf2.purpose10 field") - - metrics.channelEnabledCCPA = newCounterWithoutLabels(cfg, reg, - "account_config_ccpa_channel_enabled_warn", - "Count of requests referencing an account whose config specifies a depreceated ccpa.channel_enabled field") - metrics.channelEnabledGDPR = newCounterWithoutLabels(cfg, reg, - "account_config_gdpr_channel_enabled_warn", - "Count of requests referencing an account whose config specifies a depreceated gdpr.channel_enabled field") - - metrics.accountDeprecationSummary = newCounterWithoutLabels(cfg, reg, - "account_config_summary", - "Count of deprecated account config fields encountered across all accounts") - createModulesMetrics(cfg, reg, &metrics, moduleStageNames, standardTimeBuckets) metrics.Gatherer = reg @@ -753,51 +697,6 @@ func (m *Metrics) RecordDebugRequest(debugEnabled bool, pubID string) { } } -func (m *Metrics) RecordAccountGDPRPurposeWarning(account string, purposeName string) { - if account != metrics.PublisherUnknown { - switch purposeName { - case "purpose1": - m.accountDeprecationWarningsPurpose1.Inc() - case "purpose2": - m.accountDeprecationWarningsPurpose2.Inc() - case "purpose3": - m.accountDeprecationWarningsPurpose3.Inc() - case "purpose4": - m.accountDeprecationWarningsPurpose4.Inc() - case "purpose5": - m.accountDeprecationWarningsPurpose5.Inc() - case "purpose6": - m.accountDeprecationWarningsPurpose6.Inc() - case "purpose7": - m.accountDeprecationWarningsPurpose7.Inc() - case "purpose8": - m.accountDeprecationWarningsPurpose8.Inc() - case "purpose9": - m.accountDeprecationWarningsPurpose9.Inc() - case "purpose10": - m.accountDeprecationWarningsPurpose10.Inc() - } - } -} - -func (m *Metrics) RecordAccountGDPRChannelEnabledWarning(account string) { - if account != metrics.PublisherUnknown { - m.channelEnabledGDPR.Inc() - } -} - -func (m *Metrics) RecordAccountCCPAChannelEnabledWarning(account string) { - if account != metrics.PublisherUnknown { - m.channelEnabledCCPA.Inc() - } -} - -func (m *Metrics) RecordAccountUpgradeStatus(account string) { - if account != metrics.PublisherUnknown { - m.accountDeprecationSummary.Inc() - } -} - func (m *Metrics) RecordStoredResponse(pubId string) { m.storedResponses.Inc() if !m.metricsDisabled.AccountStoredResponses && pubId != metrics.PublisherUnknown { @@ -883,15 +782,16 @@ func (m *Metrics) RecordStoredDataError(labels metrics.StoredDataLabels) { } func (m *Metrics) RecordAdapterRequest(labels metrics.AdapterLabels) { + lowerCasedAdapter := strings.ToLower(string(labels.Adapter)) m.adapterRequests.With(prometheus.Labels{ - adapterLabel: string(labels.Adapter), + adapterLabel: lowerCasedAdapter, cookieLabel: string(labels.CookieFlag), hasBidsLabel: strconv.FormatBool(labels.AdapterBids == metrics.AdapterBidPresent), }).Inc() for err := range labels.AdapterErrors { m.adapterErrors.With(prometheus.Labels{ - adapterLabel: string(labels.Adapter), + adapterLabel: lowerCasedAdapter, adapterErrorLabel: string(err), }).Inc() } @@ -908,22 +808,23 @@ func (m *Metrics) RecordRejectedBidsForBidder(Adapter openrtb_ext.BidderName) { // Keeps track of created and reused connections to adapter bidders and the time from the // connection request, to the connection creation, or reuse from the pool across all engines func (m *Metrics) RecordAdapterConnections(adapterName openrtb_ext.BidderName, connWasReused bool, connWaitTime time.Duration) { + lowerCasedAdapterName := strings.ToLower(string(adapterName)) if m.metricsDisabled.AdapterConnectionMetrics { return } if connWasReused { m.adapterReusedConnections.With(prometheus.Labels{ - adapterLabel: string(adapterName), + adapterLabel: lowerCasedAdapterName, }).Inc() } else { m.adapterCreatedConnections.With(prometheus.Labels{ - adapterLabel: string(adapterName), + adapterLabel: lowerCasedAdapterName, }).Inc() } m.adapterConnectionWaitTime.With(prometheus.Labels{ - adapterLabel: string(adapterName), + adapterLabel: lowerCasedAdapterName, }).Observe(connWaitTime.Seconds()) } @@ -944,7 +845,7 @@ func (m *Metrics) RecordBidderServerResponseTime(bidderServerResponseTime time.D func (m *Metrics) RecordAdapterPanic(labels metrics.AdapterLabels) { m.adapterPanics.With(prometheus.Labels{ - adapterLabel: string(labels.Adapter), + adapterLabel: strings.ToLower(string(labels.Adapter)), }).Inc() } @@ -955,14 +856,14 @@ func (m *Metrics) RecordAdapterBidReceived(labels metrics.AdapterLabels, bidType } m.adapterBids.With(prometheus.Labels{ - adapterLabel: string(labels.Adapter), + adapterLabel: strings.ToLower(string(labels.Adapter)), markupDeliveryLabel: markupDelivery, }).Inc() } func (m *Metrics) RecordAdapterPrice(labels metrics.AdapterLabels, cpm float64) { m.adapterPrices.With(prometheus.Labels{ - adapterLabel: string(labels.Adapter), + adapterLabel: strings.ToLower(string(labels.Adapter)), }).Observe(cpm) } @@ -975,7 +876,7 @@ func (m *Metrics) RecordOverheadTime(overhead metrics.OverheadType, duration tim func (m *Metrics) RecordAdapterTime(labels metrics.AdapterLabels, length time.Duration) { if len(labels.AdapterErrors) == 0 { m.adapterRequestsTimer.With(prometheus.Labels{ - adapterLabel: string(labels.Adapter), + adapterLabel: strings.ToLower(string(labels.Adapter)), }).Observe(length.Seconds()) } } @@ -1087,7 +988,7 @@ func (m *Metrics) RecordAdapterGDPRRequestBlocked(adapterName openrtb_ext.Bidder } m.adapterGDPRBlockedRequests.With(prometheus.Labels{ - adapterLabel: string(adapterName), + adapterLabel: strings.ToLower(string(adapterName)), }).Inc() } @@ -1107,8 +1008,9 @@ func (m *Metrics) RecordAdsCertSignTime(adsCertSignTime time.Duration) { } func (m *Metrics) RecordBidValidationCreativeSizeError(adapter openrtb_ext.BidderName, account string) { + lowerCasedAdapter := strings.ToLower(string(adapter)) m.adapterBidResponseValidationSizeError.With(prometheus.Labels{ - adapterLabel: string(adapter), successLabel: successLabel, + adapterLabel: lowerCasedAdapter, successLabel: successLabel, }).Inc() if !m.metricsDisabled.AccountAdapterDetails && account != metrics.PublisherUnknown { @@ -1119,8 +1021,9 @@ func (m *Metrics) RecordBidValidationCreativeSizeError(adapter openrtb_ext.Bidde } func (m *Metrics) RecordBidValidationCreativeSizeWarn(adapter openrtb_ext.BidderName, account string) { + lowerCasedAdapter := strings.ToLower(string(adapter)) m.adapterBidResponseValidationSizeWarn.With(prometheus.Labels{ - adapterLabel: string(adapter), successLabel: successLabel, + adapterLabel: lowerCasedAdapter, successLabel: successLabel, }).Inc() if !m.metricsDisabled.AccountAdapterDetails && account != metrics.PublisherUnknown { @@ -1132,7 +1035,7 @@ func (m *Metrics) RecordBidValidationCreativeSizeWarn(adapter openrtb_ext.Bidder func (m *Metrics) RecordBidValidationSecureMarkupError(adapter openrtb_ext.BidderName, account string) { m.adapterBidResponseSecureMarkupError.With(prometheus.Labels{ - adapterLabel: string(adapter), successLabel: successLabel, + adapterLabel: strings.ToLower(string(adapter)), successLabel: successLabel, }).Inc() if !m.metricsDisabled.AccountAdapterDetails && account != metrics.PublisherUnknown { @@ -1144,7 +1047,7 @@ func (m *Metrics) RecordBidValidationSecureMarkupError(adapter openrtb_ext.Bidde func (m *Metrics) RecordBidValidationSecureMarkupWarn(adapter openrtb_ext.BidderName, account string) { m.adapterBidResponseSecureMarkupWarn.With(prometheus.Labels{ - adapterLabel: string(adapter), successLabel: successLabel, + adapterLabel: strings.ToLower(string(adapter)), successLabel: successLabel, }).Inc() if !m.metricsDisabled.AccountAdapterDetails && account != metrics.PublisherUnknown { diff --git a/metrics/prometheus/prometheus_ow.go b/metrics/prometheus/prometheus_ow.go index be9977626b6..789788a93ce 100644 --- a/metrics/prometheus/prometheus_ow.go +++ b/metrics/prometheus/prometheus_ow.go @@ -4,8 +4,8 @@ import ( "strconv" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" "github.com/prometheus/client_golang/prometheus" ) diff --git a/metrics/prometheus/prometheus_test.go b/metrics/prometheus/prometheus_test.go index 0b00f4ef022..be6ea141c68 100644 --- a/metrics/prometheus/prometheus_test.go +++ b/metrics/prometheus/prometheus_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/assert" @@ -65,7 +65,7 @@ func TestMetricCountGatekeeping(t *testing.T) { // Verify Per-Adapter Cardinality // - This assertion provides a warning for newly added adapter metrics. Threre are 40+ adapters which makes the // cost of new per-adapter metrics rather expensive. Thought should be given when adding new per-adapter metrics. - assert.True(t, perAdapterCardinalityCount <= 29, "Per-Adapter Cardinality count equals %d \n", perAdapterCardinalityCount) + assert.True(t, perAdapterCardinalityCount <= 30, "Per-Adapter Cardinality count equals %d \n", perAdapterCardinalityCount) } func TestConnectionMetrics(t *testing.T) { @@ -227,18 +227,19 @@ func TestBidValidationCreativeSizeMetric(t *testing.T) { expectedAccountCount: 0, }, } - + adapterName := openrtb_ext.BidderName("AnyName") + lowerCasedAdapterName := "anyname" for _, test := range testCases { m := createMetricsForTesting() m.metricsDisabled.AccountAdapterDetails = test.givenAccountAdapterMetricsDisabled - m.RecordBidValidationCreativeSizeError(adapterLabel, "acct-id") - m.RecordBidValidationCreativeSizeWarn(adapterLabel, "acct-id") + m.RecordBidValidationCreativeSizeError(adapterName, "acct-id") + m.RecordBidValidationCreativeSizeWarn(adapterName, "acct-id") assertCounterVecValue(t, "", "account bid validation", m.accountBidResponseValidationSizeError, test.expectedAccountCount, prometheus.Labels{accountLabel: "acct-id", successLabel: successLabel}) - assertCounterVecValue(t, "", "adapter bid validation", m.adapterBidResponseValidationSizeError, test.expectedAdapterCount, prometheus.Labels{adapterLabel: adapterLabel, successLabel: successLabel}) + assertCounterVecValue(t, "", "adapter bid validation", m.adapterBidResponseValidationSizeError, test.expectedAdapterCount, prometheus.Labels{adapterLabel: lowerCasedAdapterName, successLabel: successLabel}) assertCounterVecValue(t, "", "account bid validation", m.accountBidResponseValidationSizeWarn, test.expectedAccountCount, prometheus.Labels{accountLabel: "acct-id", successLabel: successLabel}) - assertCounterVecValue(t, "", "adapter bid validation", m.adapterBidResponseValidationSizeWarn, test.expectedAdapterCount, prometheus.Labels{adapterLabel: adapterLabel, successLabel: successLabel}) + assertCounterVecValue(t, "", "adapter bid validation", m.adapterBidResponseValidationSizeWarn, test.expectedAdapterCount, prometheus.Labels{adapterLabel: lowerCasedAdapterName, successLabel: successLabel}) } } @@ -264,17 +265,19 @@ func TestBidValidationSecureMarkupMetric(t *testing.T) { }, } + adapterName := openrtb_ext.BidderName("AnyName") + lowerCasedAdapterName := "anyname" for _, test := range testCases { m := createMetricsForTesting() m.metricsDisabled.AccountAdapterDetails = test.givenAccountAdapterMetricsDisabled - m.RecordBidValidationSecureMarkupError(adapterLabel, "acct-id") - m.RecordBidValidationSecureMarkupWarn(adapterLabel, "acct-id") + m.RecordBidValidationSecureMarkupError(adapterName, "acct-id") + m.RecordBidValidationSecureMarkupWarn(adapterName, "acct-id") assertCounterVecValue(t, "", "Account Secure Markup Error", m.accountBidResponseSecureMarkupError, test.expectedAccountCount, prometheus.Labels{accountLabel: "acct-id", successLabel: successLabel}) - assertCounterVecValue(t, "", "Adapter Secure Markup Error", m.adapterBidResponseSecureMarkupError, test.expectedAdapterCount, prometheus.Labels{adapterLabel: adapterLabel, successLabel: successLabel}) + assertCounterVecValue(t, "", "Adapter Secure Markup Error", m.adapterBidResponseSecureMarkupError, test.expectedAdapterCount, prometheus.Labels{adapterLabel: lowerCasedAdapterName, successLabel: successLabel}) assertCounterVecValue(t, "", "Account Secure Markup Warn", m.accountBidResponseSecureMarkupWarn, test.expectedAccountCount, prometheus.Labels{accountLabel: "acct-id", successLabel: successLabel}) - assertCounterVecValue(t, "", "Adapter Secure Markup Warn", m.adapterBidResponseSecureMarkupWarn, test.expectedAdapterCount, prometheus.Labels{adapterLabel: adapterLabel, successLabel: successLabel}) + assertCounterVecValue(t, "", "Adapter Secure Markup Warn", m.adapterBidResponseSecureMarkupWarn, test.expectedAdapterCount, prometheus.Labels{adapterLabel: lowerCasedAdapterName, successLabel: successLabel}) } } @@ -769,10 +772,11 @@ func TestRecordStoredDataError(t *testing.T) { } func TestAdapterBidReceivedMetric(t *testing.T) { - adapterName := "anyName" + adapterName := openrtb_ext.BidderName("anyName") + lowerCasedAdapterName := "anyname" performTest := func(m *Metrics, hasAdm bool) { labels := metrics.AdapterLabels{ - Adapter: openrtb_ext.BidderName(adapterName), + Adapter: adapterName, } bidType := openrtb_ext.BidTypeBanner m.RecordAdapterBidReceived(labels, bidType, hasAdm) @@ -810,13 +814,13 @@ func TestAdapterBidReceivedMetric(t *testing.T) { assertCounterVecValue(t, test.description, "adapterBids[adm]", m.adapterBids, test.expectedAdmCount, prometheus.Labels{ - adapterLabel: adapterName, + adapterLabel: lowerCasedAdapterName, markupDeliveryLabel: markupDeliveryAdm, }) assertCounterVecValue(t, test.description, "adapterBids[nurl]", m.adapterBids, test.expectedNurlCount, prometheus.Labels{ - adapterLabel: adapterName, + adapterLabel: lowerCasedAdapterName, markupDeliveryLabel: markupDeliveryNurl, }) } @@ -825,6 +829,7 @@ func TestAdapterBidReceivedMetric(t *testing.T) { func TestRecordAdapterPriceMetric(t *testing.T) { m := createMetricsForTesting() adapterName := "anyName" + lowerCasedAdapterName := "anyname" cpm := float64(42) m.RecordAdapterPrice(metrics.AdapterLabels{ @@ -833,12 +838,13 @@ func TestRecordAdapterPriceMetric(t *testing.T) { expectedCount := uint64(1) expectedSum := cpm - result := getHistogramFromHistogramVec(m.adapterPrices, adapterLabel, adapterName) + result := getHistogramFromHistogramVec(m.adapterPrices, adapterLabel, lowerCasedAdapterName) assertHistogram(t, "adapterPrices", result, expectedCount, expectedSum) } func TestAdapterRequestMetrics(t *testing.T) { adapterName := "anyName" + lowerCasedAdapterName := "anyname" performTest := func(m *Metrics, cookieFlag metrics.CookieFlag, adapterBids metrics.AdapterBid) { labels := metrics.AdapterLabels{ Adapter: openrtb_ext.BidderName(adapterName), @@ -938,7 +944,7 @@ func TestAdapterRequestMetrics(t *testing.T) { processMetrics(m.adapterRequests, func(m dto.Metric) { isMetricForAdapter := false for _, label := range m.GetLabel() { - if label.GetName() == adapterLabel && label.GetValue() == adapterName { + if label.GetName() == adapterLabel && label.GetValue() == lowerCasedAdapterName { isMetricForAdapter = true } } @@ -975,6 +981,7 @@ func TestAdapterRequestMetrics(t *testing.T) { func TestAdapterRequestErrorMetrics(t *testing.T) { adapterName := "anyName" + lowerCasedAdapterName := "anyname" performTest := func(m *Metrics, adapterErrors map[metrics.AdapterError]struct{}) { labels := metrics.AdapterLabels{ Adapter: openrtb_ext.BidderName(adapterName), @@ -1031,7 +1038,7 @@ func TestAdapterRequestErrorMetrics(t *testing.T) { processMetrics(m.adapterErrors, func(m dto.Metric) { isMetricForAdapter := false for _, label := range m.GetLabel() { - if label.GetName() == adapterLabel && label.GetValue() == adapterName { + if label.GetName() == adapterLabel && label.GetValue() == lowerCasedAdapterName { isMetricForAdapter = true } } @@ -1053,6 +1060,7 @@ func TestAdapterRequestErrorMetrics(t *testing.T) { func TestAdapterTimeMetric(t *testing.T) { adapterName := "anyName" + lowerCasedAdapterName := "anyname" performTest := func(m *Metrics, timeInMs float64, adapterErrors map[metrics.AdapterError]struct{}) { m.RecordAdapterTime(metrics.AdapterLabels{ Adapter: openrtb_ext.BidderName(adapterName), @@ -1091,24 +1099,24 @@ func TestAdapterTimeMetric(t *testing.T) { test.testCase(m) - result := getHistogramFromHistogramVec(m.adapterRequestsTimer, adapterLabel, adapterName) + result := getHistogramFromHistogramVec(m.adapterRequestsTimer, adapterLabel, lowerCasedAdapterName) assertHistogram(t, test.description, result, test.expectedCount, test.expectedSum) } } func TestAdapterPanicMetric(t *testing.T) { m := createMetricsForTesting() - adapterName := "anyName" - + adapterName := openrtb_ext.BidderName("anyName") + lowerCasedAdapterName := "anyname" m.RecordAdapterPanic(metrics.AdapterLabels{ - Adapter: openrtb_ext.BidderName(adapterName), + Adapter: adapterName, }) expectedCount := float64(1) assertCounterVecValue(t, "", "adapterPanics", m.adapterPanics, expectedCount, prometheus.Labels{ - adapterLabel: adapterName, + adapterLabel: lowerCasedAdapterName, }) } @@ -1532,6 +1540,8 @@ func TestRecordBidderServerResponseTime(t *testing.T) { } func TestRecordAdapterConnections(t *testing.T) { + adapterName := openrtb_ext.BidderName("Adapter") + lowerCasedAdapterName := "adapter" type testIn struct { adapterName openrtb_ext.BidderName @@ -1554,7 +1564,7 @@ func TestRecordAdapterConnections(t *testing.T) { { description: "[1] Successful, new connection created, was idle, has connection wait", in: testIn{ - adapterName: openrtb_ext.BidderAppnexus, + adapterName: adapterName, connWasReused: false, connWait: time.Second * 5, }, @@ -1568,7 +1578,7 @@ func TestRecordAdapterConnections(t *testing.T) { { description: "[2] Successful, new connection created, not idle, has connection wait", in: testIn{ - adapterName: openrtb_ext.BidderAppnexus, + adapterName: adapterName, connWasReused: false, connWait: time.Second * 4, }, @@ -1582,7 +1592,7 @@ func TestRecordAdapterConnections(t *testing.T) { { description: "[3] Successful, was reused, was idle, no connection wait", in: testIn{ - adapterName: openrtb_ext.BidderAppnexus, + adapterName: adapterName, connWasReused: true, }, out: testOut{ @@ -1595,7 +1605,7 @@ func TestRecordAdapterConnections(t *testing.T) { { description: "[4] Successful, was reused, not idle, has connection wait", in: testIn{ - adapterName: openrtb_ext.BidderAppnexus, + adapterName: adapterName, connWasReused: true, connWait: time.Second * 5, }, @@ -1625,7 +1635,7 @@ func TestRecordAdapterConnections(t *testing.T) { "adapter_connection_reused", m.adapterReusedConnections, float64(test.out.expectedConnReusedCount), - prometheus.Labels{adapterLabel: string(test.in.adapterName)}) + prometheus.Labels{adapterLabel: lowerCasedAdapterName}) // Assert number of new created connections assertCounterVecValue(t, @@ -1633,10 +1643,10 @@ func TestRecordAdapterConnections(t *testing.T) { "adapter_connection_created", m.adapterCreatedConnections, float64(test.out.expectedConnCreatedCount), - prometheus.Labels{adapterLabel: string(test.in.adapterName)}) + prometheus.Labels{adapterLabel: lowerCasedAdapterName}) // Assert connection wait time - histogram := getHistogramFromHistogramVec(m.adapterConnectionWaitTime, adapterLabel, string(test.in.adapterName)) + histogram := getHistogramFromHistogramVec(m.adapterConnectionWaitTime, adapterLabel, lowerCasedAdapterName) assert.Equal(t, test.out.expectedConnWaitCount, histogram.GetSampleCount(), assertDesciptions[2]) assert.Equal(t, test.out.expectedConnWaitTime, histogram.GetSampleSum(), assertDesciptions[3]) } @@ -1989,8 +1999,9 @@ func assertHistogram(t *testing.T, name string, histogram dto.Histogram, expecte func TestRecordAdapterGDPRRequestBlocked(t *testing.T) { m := createMetricsForTesting() - - m.RecordAdapterGDPRRequestBlocked(openrtb_ext.BidderAppnexus) + adapterName := openrtb_ext.BidderName("AnyName") + lowerCasedAdapterName := "anyname" + m.RecordAdapterGDPRRequestBlocked(adapterName) assertCounterVecValue(t, "Increment adapter GDPR request blocked counter", @@ -1998,7 +2009,7 @@ func TestRecordAdapterGDPRRequestBlocked(t *testing.T) { m.adapterGDPRBlockedRequests, 1, prometheus.Labels{ - adapterLabel: string(openrtb_ext.BidderAppnexus), + adapterLabel: lowerCasedAdapterName, }) } @@ -2184,263 +2195,3 @@ func TestRecordModuleMetrics(t *testing.T) { } } } - -func TestRecordAccountGDPRPurposeWarningMetrics(t *testing.T) { - testCases := []struct { - name string - givenPurposeName string - expectedP1MetricCount float64 - expectedP2MetricCount float64 - expectedP3MetricCount float64 - expectedP4MetricCount float64 - expectedP5MetricCount float64 - expectedP6MetricCount float64 - expectedP7MetricCount float64 - expectedP8MetricCount float64 - expectedP9MetricCount float64 - expectedP10MetricCount float64 - }{ - { - name: "Purpose1MetricIncremented", - givenPurposeName: "purpose1", - expectedP1MetricCount: 1, - }, - { - name: "Purpose2MetricIncremented", - givenPurposeName: "purpose2", - expectedP2MetricCount: 1, - }, - { - name: "Purpose3MetricIncremented", - givenPurposeName: "purpose3", - expectedP3MetricCount: 1, - }, - { - name: "Purpose4MetricIncremented", - givenPurposeName: "purpose4", - expectedP4MetricCount: 1, - }, - { - name: "Purpose5MetricIncremented", - givenPurposeName: "purpose5", - expectedP5MetricCount: 1, - }, - { - name: "Purpose6MetricIncremented", - givenPurposeName: "purpose6", - expectedP6MetricCount: 1, - }, - { - name: "Purpose7MetricIncremented", - givenPurposeName: "purpose7", - expectedP7MetricCount: 1, - }, - { - name: "Purpose8MetricIncremented", - givenPurposeName: "purpose8", - expectedP8MetricCount: 1, - }, - { - name: "Purpose9MetricIncremented", - givenPurposeName: "purpose9", - expectedP9MetricCount: 1, - }, - { - name: "Purpose10MetricIncremented", - givenPurposeName: "purpose10", - expectedP10MetricCount: 1, - }, - } - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - m := createMetricsForTesting() - m.RecordAccountGDPRPurposeWarning("acct-id", test.givenPurposeName) - - assertCounterValue(t, "", "Account Deprecation Warnings", m.accountDeprecationWarningsPurpose1, test.expectedP1MetricCount) - assertCounterValue(t, "", "Account Deprecation Warnings", m.accountDeprecationWarningsPurpose2, test.expectedP2MetricCount) - assertCounterValue(t, "", "Account Deprecation Warnings", m.accountDeprecationWarningsPurpose3, test.expectedP3MetricCount) - assertCounterValue(t, "", "Account Deprecation Warnings", m.accountDeprecationWarningsPurpose4, test.expectedP4MetricCount) - assertCounterValue(t, "", "Account Deprecation Warnings", m.accountDeprecationWarningsPurpose5, test.expectedP5MetricCount) - assertCounterValue(t, "", "Account Deprecation Warnings", m.accountDeprecationWarningsPurpose6, test.expectedP6MetricCount) - assertCounterValue(t, "", "Account Deprecation Warnings", m.accountDeprecationWarningsPurpose7, test.expectedP7MetricCount) - assertCounterValue(t, "", "Account Deprecation Warnings", m.accountDeprecationWarningsPurpose8, test.expectedP8MetricCount) - assertCounterValue(t, "", "Account Deprecation Warnings", m.accountDeprecationWarningsPurpose9, test.expectedP9MetricCount) - assertCounterValue(t, "", "Account Deprecation Warnings", m.accountDeprecationWarningsPurpose10, test.expectedP10MetricCount) - }) - } -} - -func TestRecordAccountGDPRChannelEnabledWarningMetrics(t *testing.T) { - testCases := []struct { - name string - givenPubID string - expectedMetricCount float64 - }{ - { - name: "GdprChannelMetricIncremented", - givenPubID: "acct-id", - expectedMetricCount: 1, - }, - } - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - m := createMetricsForTesting() - m.RecordAccountGDPRChannelEnabledWarning(test.givenPubID) - - assertCounterValue(t, "", "GDPR Channel Enabled Deprecation Warnings", m.channelEnabledGDPR, test.expectedMetricCount) - }) - } -} - -func TestRecordAccountCCPAChannelEnabledWarningMetrics(t *testing.T) { - testCases := []struct { - name string - givenPubID string - expectedMetricCount float64 - }{ - { - name: "CcpaChannelMetricIncremented", - givenPubID: "acct-id", - expectedMetricCount: 1, - }, - } - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - m := createMetricsForTesting() - m.RecordAccountCCPAChannelEnabledWarning(test.givenPubID) - - assertCounterValue(t, "", "CCPA Channel Enabled Deprecation Warnings", m.channelEnabledCCPA, test.expectedMetricCount) - }) - } -} - -func TestRecordAccountUpgradeStatusMetrics(t *testing.T) { - testCases := []struct { - name string - givenPubID string - expectedMetricCount float64 - }{ - { - name: "AccountDeprecationMeterIncremented", - givenPubID: "acct-id", - expectedMetricCount: 1, - }, - } - for _, test := range testCases { - t.Run(test.name, func(t *testing.T) { - m := createMetricsForTesting() - m.RecordAccountUpgradeStatus(test.givenPubID) - - assertCounterValue(t, "", "Account Depreciation Summary Meter should be incremented", m.accountDeprecationSummary, test.expectedMetricCount) - }) - } -} - -func TestRecordDynamicFetchFailure(t *testing.T) { - type testIn struct { - pubid, code string - } - type testOut struct { - expCount int - } - testCases := []struct { - description string - in testIn - out testOut - }{ - { - description: "record dynamic fetch failure", - in: testIn{ - pubid: "5890", - code: "1", - }, - out: testOut{ - expCount: 1, - }, - }, - } - for _, test := range testCases { - pm := createMetricsForTesting() - pm.RecordDynamicFetchFailure(test.in.pubid, test.in.code) - - assertCounterVecValue(t, - "", - "", - pm.dynamicFetchFailure, - float64(test.out.expCount), - prometheus.Labels{ - accountLabel: test.in.pubid, - codeLabel: test.in.code, - }) - } -} -func TestRecordFloorsRequestForAccount(t *testing.T) { - type testIn struct { - pubid string - } - type testOut struct { - expCount int - } - testCases := []struct { - description string - in testIn - out testOut - }{ - { - description: "record floors request", - in: testIn{ - pubid: "1010", - }, - out: testOut{ - expCount: 1, - }, - }, - } - for _, test := range testCases { - pm := createMetricsForTesting() - pm.RecordFloorsRequestForAccount(test.in.pubid) - assertCounterVecValue(t, - "", - "floors_account_requests", - pm.accountFloorsRequest, - float64(test.out.expCount), - prometheus.Labels{ - accountLabel: test.in.pubid, - }) - } -} -func TestRecordRejectedBidsForAccount(t *testing.T) { - type testIn struct { - pubid string - } - type testOut struct { - expCount int - } - testCases := []struct { - description string - in testIn - out testOut - }{ - { - description: "record rejected bid", - in: testIn{ - pubid: "1010", - }, - out: testOut{ - expCount: 1, - }, - }, - } - for _, test := range testCases { - pm := createMetricsForTesting() - pm.RecordRejectedBidsForAccount(test.in.pubid) - assertCounterVecValue(t, - "", - "floors_account_rejected_bid_requests", - pm.accountRejectedBid, - float64(test.out.expCount), - prometheus.Labels{ - accountLabel: test.in.pubid, - }) - } -} diff --git a/metrics/prometheus/type_conversion.go b/metrics/prometheus/type_conversion.go index 07e4e89c43d..9bf2ec94a08 100644 --- a/metrics/prometheus/type_conversion.go +++ b/metrics/prometheus/type_conversion.go @@ -2,13 +2,10 @@ package prometheusmetrics import ( "strconv" - - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/openrtb_ext" + "strings" ) -func adaptersAsString() []string { - values := openrtb_ext.CoreBidderNames() +func enumAsString[T ~string](values []T) []string { valuesAsString := make([]string, len(values)) for i, v := range values { valuesAsString[i] = string(v) @@ -16,11 +13,10 @@ func adaptersAsString() []string { return valuesAsString } -func adapterErrorsAsString() []string { - values := metrics.AdapterErrors() +func enumAsLowerCaseString[T ~string](values []T) []string { valuesAsString := make([]string, len(values)) for i, v := range values { - valuesAsString[i] = string(v) + valuesAsString[i] = strings.ToLower(string(v)) } return valuesAsString } @@ -31,111 +27,3 @@ func boolValuesAsString() []string { strconv.FormatBool(false), } } - -func cacheResultsAsString() []string { - values := metrics.CacheResults() - valuesAsString := make([]string, len(values)) - for i, v := range values { - valuesAsString[i] = string(v) - } - return valuesAsString -} - -func cookieTypesAsString() []string { - values := metrics.CookieTypes() - valuesAsString := make([]string, len(values)) - for i, v := range values { - valuesAsString[i] = string(v) - } - return valuesAsString -} - -func cookieSyncStatusesAsString() []string { - values := metrics.CookieSyncStatuses() - valuesAsString := make([]string, len(values)) - for i, v := range values { - valuesAsString[i] = string(v) - } - return valuesAsString -} - -func requestStatusesAsString() []string { - values := metrics.RequestStatuses() - valuesAsString := make([]string, len(values)) - for i, v := range values { - valuesAsString[i] = string(v) - } - return valuesAsString -} - -func syncerRequestStatusesAsString() []string { - values := metrics.SyncerRequestStatuses() - valuesAsString := make([]string, len(values)) - for i, v := range values { - valuesAsString[i] = string(v) - } - return valuesAsString -} - -func overheadTypesAsString() []string { - overheadTypes := metrics.OverheadTypes() - overheadTypesAsString := make([]string, len(overheadTypes)) - for i, ot := range overheadTypes { - overheadTypesAsString[i] = ot.String() - } - return overheadTypesAsString -} - -func syncerSetStatusesAsString() []string { - values := metrics.SyncerSetUidStatuses() - valuesAsString := make([]string, len(values)) - for i, v := range values { - valuesAsString[i] = string(v) - } - return valuesAsString -} - -func requestTypesAsString() []string { - values := metrics.RequestTypes() - valuesAsString := make([]string, len(values)) - for i, v := range values { - valuesAsString[i] = string(v) - } - return valuesAsString -} - -func setUidStatusesAsString() []string { - values := metrics.SetUidStatuses() - valuesAsString := make([]string, len(values)) - for i, v := range values { - valuesAsString[i] = string(v) - } - return valuesAsString -} - -func storedDataFetchTypesAsString() []string { - values := metrics.StoredDataFetchTypes() - valuesAsString := make([]string, len(values)) - for i, v := range values { - valuesAsString[i] = string(v) - } - return valuesAsString -} - -func storedDataErrorsAsString() []string { - values := metrics.StoredDataErrors() - valuesAsString := make([]string, len(values)) - for i, v := range values { - valuesAsString[i] = string(v) - } - return valuesAsString -} - -func tcfVersionsAsString() []string { - values := metrics.TCFVersions() - valuesAsString := make([]string, len(values)) - for i, v := range values { - valuesAsString[i] = string(v) - } - return valuesAsString -} diff --git a/modules/builder.go b/modules/builder.go index b3c6223a9b6..80ea5b395f8 100644 --- a/modules/builder.go +++ b/modules/builder.go @@ -1,9 +1,8 @@ package modules import ( - prebidOrtb2blocking "github.com/prebid/prebid-server/modules/prebid/ortb2blocking" - pubmaticOpenwrap "github.com/prebid/prebid-server/modules/pubmatic/openwrap" - vastunwrap "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap" + prebidOrtb2blocking "github.com/prebid/prebid-server/v2/modules/prebid/ortb2blocking" + pubmaticOpenwrap "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap" ) // builders returns mapping between module name and its builder @@ -14,8 +13,7 @@ func builders() ModuleBuilders { "ortb2blocking": prebidOrtb2blocking.Builder, }, "pubmatic": { - "vastunwrap": vastunwrap.Builder, - "openwrap": pubmaticOpenwrap.Builder, + "openwrap": pubmaticOpenwrap.Builder, }, } } diff --git a/modules/generator/builder.tmpl b/modules/generator/builder.tmpl index f89cc21c87f..db0cd1a4fb4 100644 --- a/modules/generator/builder.tmpl +++ b/modules/generator/builder.tmpl @@ -1,20 +1,21 @@ package modules - -{{if .}} import ( - {{- range .}} - {{.Vendor}}{{.Module | Title}} "github.com/prebid/prebid-server/modules/{{.Vendor}}/{{.Module}}" +{{- range $vendor, $modules := .}} + {{- range $module := $modules}} + {{$vendor}}{{$module | Title}} "github.com/prebid/prebid-server/v2/modules/{{$vendor}}/{{$module}}" {{- end}} +{{- end}} ) -{{end}} // builders returns mapping between module name and its builder // vendor and module names are chosen based on the module directory name func builders() ModuleBuilders { return ModuleBuilders{ - {{- range .}} - "{{.Vendor}}": { - "{{.Module}}": {{.Vendor}}{{.Module | Title}}.Builder, + {{- range $vendor, $modules := .}} + "{{$vendor}}": { + {{- range $module := $modules}} + "{{$module}}": {{$vendor}}{{$module | Title}}.Builder, + {{- end}} }, {{- end}} } diff --git a/modules/generator/buildergen.go b/modules/generator/buildergen.go index b906682b7a3..219932420bd 100644 --- a/modules/generator/buildergen.go +++ b/modules/generator/buildergen.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" "regexp" + "sort" "strings" "text/template" ) @@ -20,26 +21,26 @@ var ( outName = "builder.go" ) -type Module struct { - Vendor string - Module string -} - func main() { - var modules []Module + modules := make(map[string][]string) filepath.WalkDir("./", func(path string, d fs.DirEntry, err error) error { if !r.MatchString(path) { return nil } match := r.FindStringSubmatch(path) - modules = append(modules, Module{ - Vendor: match[1], - Module: match[2], - }) + vendorModules := modules[match[1]] + vendorModules = append(vendorModules, match[2]) + modules[match[1]] = vendorModules + return nil }) + for vendorName, names := range modules { + sort.Strings(names) + modules[vendorName] = names + } + funcMap := template.FuncMap{"Title": strings.Title} t, err := template.New(tmplName).Funcs(funcMap).ParseFiles(fmt.Sprintf("generator/%s", tmplName)) if err != nil { diff --git a/modules/helpers.go b/modules/helpers.go index f91daeff881..8728f7c1e77 100644 --- a/modules/helpers.go +++ b/modules/helpers.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/hooks/hookstage" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/hooks/hookstage" ) var moduleReplacer = strings.NewReplacer(".", "_", "-", "_") diff --git a/modules/moduledeps/deps.go b/modules/moduledeps/deps.go index 5b917a9ab33..98ad0cbd322 100644 --- a/modules/moduledeps/deps.go +++ b/modules/moduledeps/deps.go @@ -3,15 +3,16 @@ package moduledeps import ( "net/http" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - metricsCfg "github.com/prebid/prebid-server/metrics/config" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + metricsCfg "github.com/prebid/prebid-server/v2/metrics/config" ) // ModuleDeps provides dependencies that custom modules may need for hooks execution. // Additional dependencies can be added here if modules need something more. type ModuleDeps struct { HTTPClient *http.Client + RateConvertor *currency.RateConverter MetricsCfg *config.Metrics MetricsRegistry metricsCfg.MetricsRegistry CurrencyConversion currency.Conversions diff --git a/modules/modules.go b/modules/modules.go index ac60ec58082..f3ccd6b1ece 100644 --- a/modules/modules.go +++ b/modules/modules.go @@ -5,9 +5,10 @@ import ( "fmt" "github.com/golang/glog" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/modules/moduledeps" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/modules/moduledeps" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) //go:generate go run ./generator/buildergen.go @@ -58,7 +59,7 @@ func (m *builder) Build( id := fmt.Sprintf("%s.%s", vendor, moduleName) if data, ok := cfg[vendor][moduleName]; ok { - if conf, err = json.Marshal(data); err != nil { + if conf, err = jsonutil.Marshal(data); err != nil { return nil, nil, fmt.Errorf(`failed to marshal "%s" module config: %s`, id, err) } diff --git a/modules/modules_test.go b/modules/modules_test.go index 1c70ce4badf..008c1e75c51 100644 --- a/modules/modules_test.go +++ b/modules/modules_test.go @@ -9,10 +9,10 @@ import ( "net/http" "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/moduledeps" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/modules/moduledeps" "github.com/stretchr/testify/assert" ) @@ -85,7 +85,7 @@ func TestModuleBuilderBuild(t *testing.T) { givenConfig: map[string]map[string]interface{}{vendor: {moduleName: math.Inf(1)}}, expectedHookRepo: nil, expectedModulesStages: nil, - expectedErr: fmt.Errorf(`failed to marshal "%s.%s" module config: json: unsupported value: +Inf`, vendor, moduleName), + expectedErr: fmt.Errorf(`failed to marshal "%s.%s" module config: unsupported value: +Inf`, vendor, moduleName), }, } diff --git a/modules/prebid/ortb2blocking/analytics.go b/modules/prebid/ortb2blocking/analytics.go index 4026b6722b9..1309858c7b6 100644 --- a/modules/prebid/ortb2blocking/analytics.go +++ b/modules/prebid/ortb2blocking/analytics.go @@ -1,8 +1,8 @@ package ortb2blocking import ( - "github.com/prebid/prebid-server/hooks/hookanalytics" - "github.com/prebid/prebid-server/hooks/hookstage" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookstage" ) const enforceBlockingTag = "enforce_blocking" diff --git a/modules/prebid/ortb2blocking/config.go b/modules/prebid/ortb2blocking/config.go index cefd436b402..634170ef192 100644 --- a/modules/prebid/ortb2blocking/config.go +++ b/modules/prebid/ortb2blocking/config.go @@ -4,12 +4,13 @@ import ( "encoding/json" "fmt" - "github.com/prebid/openrtb/v19/adcom1" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) func newConfig(data json.RawMessage) (config, error) { var cfg config - if err := json.Unmarshal(data, &cfg); err != nil { + if err := jsonutil.UnmarshalValid(data, &cfg); err != nil { return cfg, fmt.Errorf("failed to parse config: %s", err) } return cfg, nil @@ -112,7 +113,7 @@ type Override struct { func (o *Override) UnmarshalJSON(bytes []byte) error { var overrideData interface{} - if err := json.Unmarshal(bytes, &overrideData); err != nil { + if err := jsonutil.UnmarshalValid(bytes, &overrideData); err != nil { return err } diff --git a/modules/prebid/ortb2blocking/config_test.go b/modules/prebid/ortb2blocking/config_test.go index 55ca3d21807..067e0786930 100644 --- a/modules/prebid/ortb2blocking/config_test.go +++ b/modules/prebid/ortb2blocking/config_test.go @@ -3,7 +3,7 @@ package ortb2blocking import ( "testing" - "github.com/prebid/openrtb/v19/adcom1" + "github.com/prebid/openrtb/v20/adcom1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/modules/prebid/ortb2blocking/hook_bidderrequest.go b/modules/prebid/ortb2blocking/hook_bidderrequest.go index 8f7ce42021c..02e3634f679 100644 --- a/modules/prebid/ortb2blocking/hook_bidderrequest.go +++ b/modules/prebid/ortb2blocking/hook_bidderrequest.go @@ -5,22 +5,22 @@ import ( "fmt" "strings" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func handleBidderRequestHook( cfg config, payload hookstage.BidderRequestPayload, ) (result hookstage.HookResult[hookstage.BidderRequestPayload], err error) { - if payload.BidRequest == nil { - return result, hookexecution.NewFailure("empty BidRequest provided") + if payload.Request == nil || payload.Request.BidRequest == nil { + return result, hookexecution.NewFailure("payload contains a nil bid request") } - mediaTypes := mediaTypesFrom(payload.BidRequest) + mediaTypes := mediaTypesFrom(payload.Request.BidRequest) changeSet := hookstage.ChangeSet[hookstage.BidderRequestPayload]{} blockingAttributes := blockingAttributes{} @@ -60,7 +60,7 @@ func updateBAdv( result *hookstage.HookResult[hookstage.BidderRequestPayload], changeSet *hookstage.ChangeSet[hookstage.BidderRequestPayload], ) (err error) { - if len(payload.BidRequest.BAdv) > 0 { + if len(payload.Request.BAdv) > 0 { return nil } @@ -87,7 +87,7 @@ func updateBApp( result *hookstage.HookResult[hookstage.BidderRequestPayload], changeSet *hookstage.ChangeSet[hookstage.BidderRequestPayload], ) (err error) { - if len(payload.BidRequest.BApp) > 0 { + if len(payload.Request.BApp) > 0 { return nil } @@ -114,7 +114,7 @@ func updateBCat( result *hookstage.HookResult[hookstage.BidderRequestPayload], changeSet *hookstage.ChangeSet[hookstage.BidderRequestPayload], ) (err error) { - if len(payload.BidRequest.BCat) > 0 { + if len(payload.Request.BCat) > 0 { return nil } @@ -191,7 +191,7 @@ func updateCatTax( attributes *blockingAttributes, changeSet *hookstage.ChangeSet[hookstage.BidderRequestPayload], ) { - if payload.BidRequest.CatTax > 0 { + if payload.Request.CatTax > 0 { return } @@ -226,7 +226,7 @@ func mutationForImp( impUpdater impUpdateFunc, ) hookstage.MutationFunc[hookstage.BidderRequestPayload] { return func(payload hookstage.BidderRequestPayload) (hookstage.BidderRequestPayload, error) { - for i, imp := range payload.BidRequest.Imp { + for i, imp := range payload.Request.Imp { if values, ok := valuesByImp[imp.ID]; ok { if len(values) == 0 { continue @@ -236,7 +236,7 @@ func mutationForImp( imp.Banner = &openrtb2.Banner{} } - payload.BidRequest.Imp[i] = impUpdater(imp, values) + payload.Request.Imp[i] = impUpdater(imp, values) } } return payload, nil @@ -310,7 +310,7 @@ func findImpressionOverrides( overrides := map[string][]int{} messages := []string{} - for _, imp := range payload.BidRequest.Imp { + for _, imp := range payload.Request.Imp { // do not add override for attribute if it already exists in request if isAttrPresent(imp) { continue diff --git a/modules/prebid/ortb2blocking/hook_raw_bidder_response.go b/modules/prebid/ortb2blocking/hook_raw_bidder_response.go index 1c51256211b..215de260b09 100644 --- a/modules/prebid/ortb2blocking/hook_raw_bidder_response.go +++ b/modules/prebid/ortb2blocking/hook_raw_bidder_response.go @@ -5,11 +5,11 @@ import ( "fmt" "strings" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/hooks/hookstage" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/hooks/hookstage" ) func handleRawBidderResponseHook( diff --git a/modules/prebid/ortb2blocking/module.go b/modules/prebid/ortb2blocking/module.go index 7386aa62bad..0b85c43dad2 100644 --- a/modules/prebid/ortb2blocking/module.go +++ b/modules/prebid/ortb2blocking/module.go @@ -4,9 +4,9 @@ import ( "context" "encoding/json" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/moduledeps" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/modules/moduledeps" ) func Builder(_ json.RawMessage, _ moduledeps.ModuleDeps) (interface{}, error) { diff --git a/modules/prebid/ortb2blocking/module_test.go b/modules/prebid/ortb2blocking/module_test.go index b47f9f58a02..8178cba15fe 100644 --- a/modules/prebid/ortb2blocking/module_test.go +++ b/modules/prebid/ortb2blocking/module_test.go @@ -6,13 +6,14 @@ import ( "errors" "testing" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/hooks/hookanalytics" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/moduledeps" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/modules/moduledeps" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -488,7 +489,7 @@ func TestHandleBidderRequestHook(t *testing.T) { bidRequest: &openrtb2.BidRequest{}, expectedBidRequest: &openrtb2.BidRequest{}, expectedHookResult: hookstage.HookResult[hookstage.BidderRequestPayload]{}, - expectedError: errors.New("failed to parse config: invalid character '.' looking for beginning of value"), + expectedError: errors.New("failed to parse config: expect { or n, but found ."), }, { description: "Expect error if nil BidRequest provided", @@ -497,7 +498,7 @@ func TestHandleBidderRequestHook(t *testing.T) { bidRequest: nil, expectedBidRequest: nil, expectedHookResult: hookstage.HookResult[hookstage.BidderRequestPayload]{}, - expectedError: hookexecution.NewFailure("empty BidRequest provided"), + expectedError: hookexecution.NewFailure("payload contains a nil bid request"), }, { description: "Expect baadv error if bidders and media_types not defined in config conditions", @@ -566,7 +567,8 @@ func TestHandleBidderRequestHook(t *testing.T) { for _, test := range testCases { t.Run(test.description, func(t *testing.T) { - payload := hookstage.BidderRequestPayload{Bidder: test.bidder, BidRequest: test.bidRequest} + brw := openrtb_ext.RequestWrapper{BidRequest: test.bidRequest} + payload := hookstage.BidderRequestPayload{Bidder: test.bidder, Request: &brw} result, err := Builder(nil, moduledeps.ModuleDeps{}) assert.NoError(t, err, "Failed to build module.") @@ -590,7 +592,8 @@ func TestHandleBidderRequestHook(t *testing.T) { _, err := mut.Apply(payload) assert.NoError(t, err) } - assert.Equal(t, test.expectedBidRequest, payload.BidRequest, "Invalid BidRequest after executing BidderRequestHook.") + + assert.Equal(t, test.expectedBidRequest, payload.Request.BidRequest, "Invalid BidRequest after executing BidderRequestHook.") // reset ChangeSet not to break hookResult assertion, we validated ChangeSet separately hookResult.ChangeSet = hookstage.ChangeSet[hookstage.BidderRequestPayload]{} diff --git a/modules/prebid/ortb2blocking/utils.go b/modules/prebid/ortb2blocking/utils.go index 701658dc6cc..a6b9c02c708 100644 --- a/modules/prebid/ortb2blocking/utils.go +++ b/modules/prebid/ortb2blocking/utils.go @@ -3,8 +3,8 @@ package ortb2blocking import ( "strings" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" ) func mergeStrings(messages []string, newMessages ...string) []string { diff --git a/modules/pubmatic/openwrap/abtest.go b/modules/pubmatic/openwrap/abtest.go index d9a6fcd735d..f2a9f8a71ef 100644 --- a/modules/pubmatic/openwrap/abtest.go +++ b/modules/pubmatic/openwrap/abtest.go @@ -4,7 +4,7 @@ import ( "strconv" "strings" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) // CheckABTestEnabled checks whether a given request is AB test enabled or not diff --git a/modules/pubmatic/openwrap/abtest_test.go b/modules/pubmatic/openwrap/abtest_test.go index e1526c0b1f5..585a5c2ff4d 100644 --- a/modules/pubmatic/openwrap/abtest_test.go +++ b/modules/pubmatic/openwrap/abtest_test.go @@ -3,7 +3,7 @@ package openwrap import ( "testing" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/adapters/bidder_alias.go b/modules/pubmatic/openwrap/adapters/bidder_alias.go index 1fcae6cb91d..569d3099515 100644 --- a/modules/pubmatic/openwrap/adapters/bidder_alias.go +++ b/modules/pubmatic/openwrap/adapters/bidder_alias.go @@ -1,8 +1,8 @@ package adapters import ( - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // Alias will return copy of exisiting alias @@ -34,6 +34,14 @@ func ResolveOWBidder(bidderName string) string { coreBidderName = string(openrtb_ext.BidderAppnexus) case models.BidderAndBeyondAlias: coreBidderName = string(openrtb_ext.BidderAdkernel) + case models.BidderAdformAdfAlias: + coreBidderName = string(openrtb_ext.BidderAdf) + case models.BidderTrustxAlias: + coreBidderName = string(openrtb_ext.BidderGrid) + case models.BidderSynacormediaAlias: + coreBidderName = string(openrtb_ext.BidderImds) + case models.BidderViewDeos: + coreBidderName = string(openrtb_ext.BidderAdtelligent) default: coreBidderName = bidderName } diff --git a/modules/pubmatic/openwrap/adapters/bidder_alias_test.go b/modules/pubmatic/openwrap/adapters/bidder_alias_test.go index 8e628f69217..17af57cc804 100644 --- a/modules/pubmatic/openwrap/adapters/bidder_alias_test.go +++ b/modules/pubmatic/openwrap/adapters/bidder_alias_test.go @@ -3,8 +3,8 @@ package adapters import ( "testing" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/adapters/bidders.go b/modules/pubmatic/openwrap/adapters/bidders.go index c48f234b1c2..bb55b21f04d 100644 --- a/modules/pubmatic/openwrap/adapters/bidders.go +++ b/modules/pubmatic/openwrap/adapters/bidders.go @@ -6,7 +6,7 @@ import ( "fmt" "strings" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) // PrepareBidParamJSONForPartner preparing bid params json for partner @@ -309,16 +309,24 @@ func builderSovrn(params BidderParameters) (json.RawMessage, error) { func builderImproveDigital(params BidderParameters) (json.RawMessage, error) { jsonStr := bytes.Buffer{} jsonStr.WriteByte('{') + var placementID, publisherID int + var ok bool - if placementID, ok := getInt(params.FieldMap["placementId"]); ok { + if placementID, ok = getInt(params.FieldMap["placementId"]); ok { fmt.Fprintf(&jsonStr, `"placementId":%d`, placementID) - } else { - publisherID, ok1 := getInt(params.FieldMap["publisherId"]) - placement, ok2 := getString(params.FieldMap["placementKey"]) - if !ok1 || !ok2 { - return nil, fmt.Errorf(errMandatoryParameterMissingFormat, params.AdapterName, "['placementId'] or ['publisherId', 'placementKey']") - } - fmt.Fprintf(&jsonStr, `"publisherId":%d,"placementKey":"%s"`, publisherID, placement) + } + + if placementID == 0 { + return nil, fmt.Errorf(errMandatoryParameterMissingFormat, params.AdapterName, "['placementId']") + } + + //UOE-10317: Adding change as per discussion with improve digital + if publisherID, ok = getInt(params.FieldMap["publisherId"]); ok { + fmt.Fprintf(&jsonStr, `,"publisherId":%d`, publisherID) + } + + if publisherID == 0 { + return nil, fmt.Errorf(errMandatoryParameterMissingFormat, params.AdapterName, "['publisherId']") } width, height := params.Width, params.Height diff --git a/modules/pubmatic/openwrap/adapters/bidders_test.go b/modules/pubmatic/openwrap/adapters/bidders_test.go index ab4a35d5567..30900db7a91 100644 --- a/modules/pubmatic/openwrap/adapters/bidders_test.go +++ b/modules/pubmatic/openwrap/adapters/bidders_test.go @@ -7,9 +7,9 @@ import ( "strconv" "testing" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -339,7 +339,7 @@ func TestPrepareBidParamJSONForPartnerAdform(t *testing.T) { "mid": "1234", }, slotKey: "", - adapterName: string(openrtb_ext.BidderAdform), + adapterName: string(openrtb_ext.BidderAdf), }, want: json.RawMessage(`{"mid":1234}`), }, @@ -351,14 +351,14 @@ func TestPrepareBidParamJSONForPartnerAdform(t *testing.T) { height: nil, fieldMap: map[string]interface{}{}, slotKey: "", - adapterName: string(openrtb_ext.BidderAdform), + adapterName: string(openrtb_ext.BidderAdf), }, want: nil, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, string(openrtb_ext.BidderAdform), nil) + got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, string(openrtb_ext.BidderAdf), nil) AssertJSON(t, tt.want, got) }) } @@ -725,7 +725,7 @@ func TestPrepareBidParamJSONForPartnerForTripleLiftWhenRequiredParamMissing(t *t } } -func TestPrepareBidParamJSONForPartnerForImproveDigitalWithPlacementId(t *testing.T) { +func TestPrepareBidParamJSONForPartnerForImproveDigitalWithPlacementIdAndPublisherId(t *testing.T) { type improveDigitalTestObj struct { PlacementID int `json:"placementId,omitempty"` PublisherID int `json:"publisherId,omitempty"` @@ -734,6 +734,7 @@ func TestPrepareBidParamJSONForPartnerForImproveDigitalWithPlacementId(t *testin fieldMap := map[string]interface{}{ "placementId": "121", + "publisherId": "911", } width := new(int64) @@ -753,7 +754,7 @@ func TestPrepareBidParamJSONForPartnerForImproveDigitalWithPlacementId(t *testin return } - if obj.PublisherID != 0 { + if obj.PublisherID != 911 { t.Error("wrong publisherId value set") return } @@ -828,8 +829,7 @@ func TestPrepareBidParamJSONForPartnerForImproveDigitalWithoutPlacementId(t *tes } fieldMap := map[string]interface{}{ - "publisherId": "911", - "placementKey": "key_1", + "publisherId": "911", } width := new(int64) @@ -838,24 +838,8 @@ func TestPrepareBidParamJSONForPartnerForImproveDigitalWithoutPlacementId(t *tes *height = 250 jsonStrBuf, _ := PrepareBidParamJSONForPartner(width, height, fieldMap, "adunit", string(openrtb_ext.BidderImprovedigital), string(openrtb_ext.BidderImprovedigital), nil) - var obj improveDigitalTestObj - if err := json.Unmarshal([]byte(jsonStrBuf), &obj); err != nil { - t.Error("Failed to form json") - return - } - - if obj.PlacementID != 0 { - t.Error("wrong placementId value set") - return - } - - if obj.PublisherID != 911 { - t.Error("wrong publisherId value set") - return - } - - if obj.PlacementKey != "key_1" { - t.Error("wrong placementKey value set") + if jsonStrBuf != nil { + t.Error("jsonStrBuf should be nil") return } } @@ -1033,8 +1017,8 @@ func TestPrepareBidParamJSONForPartnerSynacorMedia(t *testing.T) { "tagId": "testTagId", }, slotKey: "", - adapterName: string(openrtb_ext.BidderSynacormedia), - bidderCode: string(openrtb_ext.BidderSynacormedia), + adapterName: string(openrtb_ext.BidderImds), + bidderCode: string(openrtb_ext.BidderImds), }, want: json.RawMessage(`{"seatId":"testSeatId","tagId":"testTagId"}`), }, @@ -1047,8 +1031,8 @@ func TestPrepareBidParamJSONForPartnerSynacorMedia(t *testing.T) { "tagId": "testTagId", }, slotKey: "", - adapterName: string(openrtb_ext.BidderSynacormedia), - bidderCode: string(openrtb_ext.BidderSynacormedia), + adapterName: string(openrtb_ext.BidderImds), + bidderCode: string(openrtb_ext.BidderImds), }, want: nil, }, @@ -1061,8 +1045,8 @@ func TestPrepareBidParamJSONForPartnerSynacorMedia(t *testing.T) { "seatId": "testSeatId", }, slotKey: "", - adapterName: string(openrtb_ext.BidderSynacormedia), - bidderCode: string(openrtb_ext.BidderSynacormedia), + adapterName: string(openrtb_ext.BidderImds), + bidderCode: string(openrtb_ext.BidderImds), }, want: json.RawMessage(`{"seatId":"testSeatId"}`), }, @@ -1075,106 +1059,6 @@ func TestPrepareBidParamJSONForPartnerSynacorMedia(t *testing.T) { } } -func TestPrepareBidParamJSONForPartnerRhythmOne(t *testing.T) { - type args struct { - width *int64 - height *int64 - fieldMap map[string]interface{} - slotKey string - adapterName string - bidderCode string - } - tests := []struct { - name string - args args - want json.RawMessage - }{ - { - name: "All params present", - args: args{ - - width: nil, - height: nil, - fieldMap: map[string]interface{}{ - "placementId": "testPlacementId", - "path": "testPath", - "zone": "testZone", - }, - slotKey: "", - adapterName: string(openrtb_ext.BidderRhythmone), - bidderCode: string(openrtb_ext.BidderRhythmone), - }, - want: json.RawMessage(`{"placementId":"testPlacementId","path":"testPath","zone":"testZone"}`), - }, - { - name: "placementId param missing", - args: args{ - - width: nil, - height: nil, - fieldMap: map[string]interface{}{ - "path": "testPath", - "zone": "testZone", - }, - slotKey: "", - adapterName: string(openrtb_ext.BidderRhythmone), - bidderCode: string(openrtb_ext.BidderRhythmone), - }, - want: nil, - }, - { - name: "path param missing", - args: args{ - - width: nil, - height: nil, - fieldMap: map[string]interface{}{ - "placementId": "testPlacementId", - "zone": "testZone", - }, - slotKey: "", - adapterName: string(openrtb_ext.BidderRhythmone), - bidderCode: string(openrtb_ext.BidderRhythmone), - }, - want: nil, - }, - { - name: "zone param missing", - args: args{ - - width: nil, - height: nil, - fieldMap: map[string]interface{}{ - "placementId": "testPlacementId", - "path": "testPath", - }, - slotKey: "", - adapterName: string(openrtb_ext.BidderRhythmone), - bidderCode: string(openrtb_ext.BidderRhythmone), - }, - want: nil, - }, - { - name: "required params are missing", - args: args{ - width: nil, - height: nil, - fieldMap: nil, - slotKey: "", - adapterName: string(openrtb_ext.BidderRhythmone), - bidderCode: string(openrtb_ext.BidderRhythmone), - }, - want: nil, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, _ := PrepareBidParamJSONForPartner(tt.args.width, tt.args.height, tt.args.fieldMap, tt.args.slotKey, tt.args.adapterName, tt.args.bidderCode, nil) - AssertJSON(t, tt.want, got) - }) - } -} - func TestPrepareBidParamJSONForPartnerGumGum(t *testing.T) { type args struct { reqID string @@ -2394,7 +2278,7 @@ func TestPrepareBidParamJSONForPartnerImproveDigital(t *testing.T) { adapterName: string(openrtb_ext.BidderImprovedigital), bidderCode: string(openrtb_ext.BidderImprovedigital), }, - want: json.RawMessage(`{"placementId":1234,"size":{"w":300,"h":250}}`), + want: json.RawMessage(`{"placementId":1234,"publisherId":5678,"size":{"w":300,"h":250}}`), }, { name: "PlacementId present, publisherId and placementKey missing", @@ -2409,7 +2293,23 @@ func TestPrepareBidParamJSONForPartnerImproveDigital(t *testing.T) { adapterName: string(openrtb_ext.BidderImprovedigital), bidderCode: string(openrtb_ext.BidderImprovedigital), }, - want: json.RawMessage(`{"placementId":1234,"size":{"w":300,"h":250}}`), + want: nil, + }, + { + name: "PlacementId and publisherId present, placementKey missing", + args: args{ + + width: ptrutil.ToPtr[int64](300), + height: ptrutil.ToPtr[int64](250), + fieldMap: map[string]interface{}{ + "placementId": "1234", + "publisherId": "5678", + }, + slotKey: "", + adapterName: string(openrtb_ext.BidderImprovedigital), + bidderCode: string(openrtb_ext.BidderImprovedigital), + }, + want: json.RawMessage(`{"placementId":1234,"publisherId":5678,"size":{"w":300,"h":250}}`), }, { name: "PlacementId absent, publisherId and placementKey present", @@ -2425,7 +2325,7 @@ func TestPrepareBidParamJSONForPartnerImproveDigital(t *testing.T) { adapterName: string(openrtb_ext.BidderImprovedigital), bidderCode: string(openrtb_ext.BidderImprovedigital), }, - want: json.RawMessage(`{"publisherId":5678,"placementKey":"key1","size":{"w":300,"h":250}}`), + want: nil, }, { name: "required params missing", @@ -2482,12 +2382,13 @@ func TestPrepareBidParamJSONForPartnerImproveDigital(t *testing.T) { height: nil, fieldMap: map[string]interface{}{ "placementId": "1234", + "publisherId": "5678", }, slotKey: "", adapterName: string(openrtb_ext.BidderImprovedigital), bidderCode: string(openrtb_ext.BidderImprovedigital), }, - want: json.RawMessage(`{"placementId":1234}`), + want: json.RawMessage(`{"placementId":1234, "publisherId":5678}`), }, } for _, tt := range tests { diff --git a/modules/pubmatic/openwrap/adapters/builder.go b/modules/pubmatic/openwrap/adapters/builder.go index 3d526520cb3..6cafb628c78 100644 --- a/modules/pubmatic/openwrap/adapters/builder.go +++ b/modules/pubmatic/openwrap/adapters/builder.go @@ -3,8 +3,8 @@ package adapters import ( "encoding/json" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // BidderParameters provides all properties requires for bidder to generate bidder json @@ -32,7 +32,6 @@ var _bidderBuilderFactory map[string]builder // initBidderBuilderFactory initialise all hard coded bidder builder func initBidderBuilderFactory() { _bidderBuilderFactory = map[string]builder{ - string(openrtb_ext.BidderAdform): builderAdform, string(openrtb_ext.BidderAdf): builderAdform, string(openrtb_ext.BidderAppnexus): builderAppNexus, string(openrtb_ext.BidderBeachfront): builderBeachfront, @@ -63,8 +62,10 @@ func initBidderBuilderFactory() { func GetBuilder(adapterName string) builder { //resolve hardcoded bidder alias adapterName = ResolveOWBidder(adapterName) + normalisedBidderName, _ := openrtb_ext.NormalizeBidderName(adapterName) + coreBidderName := normalisedBidderName.String() - if callback, ok := _bidderBuilderFactory[adapterName]; ok { + if callback, ok := _bidderBuilderFactory[coreBidderName]; ok { return callback } return defaultBuilder diff --git a/modules/pubmatic/openwrap/adapters/converter_test.go b/modules/pubmatic/openwrap/adapters/converter_test.go index 1aa7eddf11d..6c8a2ddd4d8 100644 --- a/modules/pubmatic/openwrap/adapters/converter_test.go +++ b/modules/pubmatic/openwrap/adapters/converter_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/adapters/default_bidder.go b/modules/pubmatic/openwrap/adapters/default_bidder.go index a29e2ede26e..d79ec1f09d8 100644 --- a/modules/pubmatic/openwrap/adapters/default_bidder.go +++ b/modules/pubmatic/openwrap/adapters/default_bidder.go @@ -8,7 +8,7 @@ import ( "strconv" "strings" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) // Map containing []ParameterMapping for all partners (partner name) diff --git a/modules/pubmatic/openwrap/adapters/default_bidder_parameter.go b/modules/pubmatic/openwrap/adapters/default_bidder_parameter.go index 778facdf8a7..e94d71e6d91 100644 --- a/modules/pubmatic/openwrap/adapters/default_bidder_parameter.go +++ b/modules/pubmatic/openwrap/adapters/default_bidder_parameter.go @@ -9,7 +9,7 @@ import ( "path/filepath" "strings" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // BidderParamJSON defines type as per JSON schema files in static/bidder-param diff --git a/modules/pubmatic/openwrap/adapters/default_bidder_parameter_test.go b/modules/pubmatic/openwrap/adapters/default_bidder_parameter_test.go index 7a590cd54ca..a1a6bb5968f 100644 --- a/modules/pubmatic/openwrap/adapters/default_bidder_parameter_test.go +++ b/modules/pubmatic/openwrap/adapters/default_bidder_parameter_test.go @@ -111,7 +111,7 @@ func TestGetType(t *testing.T) { func TestParseBidderParams(t *testing.T) { parseBidderParams("../../static/bidder-params") - assert.Equal(t, 174, len(adapterParams), "Length of expected entries should match") + assert.Equal(t, 160, len(adapterParams), "Length of expected entries should match") // calculate this number using X-Y // where X is calculated using command - `ls -l | wc -l` (substract 1 from result) // Y is calculated using command `grep -EinR 'oneof|not|anyof|dependenc' static/bidder-params | grep -v "description" | grep -oE './.*.json' | uniq | wc -l` @@ -119,7 +119,7 @@ func TestParseBidderParams(t *testing.T) { func TestParseBidderSchemaDefinitions(t *testing.T) { schemaDefinitions, _ := parseBidderSchemaDefinitions("../../../../static/bidder-params") - assert.Equal(t, 210, len(schemaDefinitions), "Length of expected entries should match") + assert.Equal(t, 198, len(schemaDefinitions), "Length of expected entries should match") // calculate this number using command - `ls -l | wc -l` (substract 1 from result) } diff --git a/modules/pubmatic/openwrap/adapters/default_bidder_test.go b/modules/pubmatic/openwrap/adapters/default_bidder_test.go index fa0fe6125c2..a6861ab3736 100644 --- a/modules/pubmatic/openwrap/adapters/default_bidder_test.go +++ b/modules/pubmatic/openwrap/adapters/default_bidder_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) type prepareBidParamJSONDefaultArgs struct { diff --git a/modules/pubmatic/openwrap/adapters/tests/hybrid_bidders.json b/modules/pubmatic/openwrap/adapters/tests/hybrid_bidders.json index f96346584d2..bbc1720c0e6 100644 --- a/modules/pubmatic/openwrap/adapters/tests/hybrid_bidders.json +++ b/modules/pubmatic/openwrap/adapters/tests/hybrid_bidders.json @@ -173,6 +173,7 @@ "want": { "expectedJSON": { "placementId": 1234567, + "publisherId": 5890, "size": { "w": 720, "h": 120 diff --git a/modules/pubmatic/openwrap/adapters/util.go b/modules/pubmatic/openwrap/adapters/util.go index 9b493d5ba67..20509072ea8 100644 --- a/modules/pubmatic/openwrap/adapters/util.go +++ b/modules/pubmatic/openwrap/adapters/util.go @@ -8,7 +8,7 @@ import ( "os" "strconv" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) const FloatValuePrecision = 2 diff --git a/modules/pubmatic/openwrap/adapters/vastbidder.go b/modules/pubmatic/openwrap/adapters/vastbidder.go index 166ae3e28d3..8de0d58bb2e 100644 --- a/modules/pubmatic/openwrap/adapters/vastbidder.go +++ b/modules/pubmatic/openwrap/adapters/vastbidder.go @@ -5,9 +5,9 @@ import ( "strconv" "strings" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func PrepareVASTBidderParamJSON(pubVASTTags models.PublisherVASTTags, matchedSlotKeys []string, slotMap map[string]models.SlotMapping) json.RawMessage { diff --git a/modules/pubmatic/openwrap/adapters/vastbidder_test.go b/modules/pubmatic/openwrap/adapters/vastbidder_test.go index 9c2e9e40d46..5e73fc10964 100644 --- a/modules/pubmatic/openwrap/adapters/vastbidder_test.go +++ b/modules/pubmatic/openwrap/adapters/vastbidder_test.go @@ -4,8 +4,9 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -24,8 +25,8 @@ func formImp(isVideo bool) *openrtb2.Imp { if isVideo { imp.Video = new(openrtb2.Video) - imp.Video.W = 300 - imp.Video.H = 250 + imp.Video.W = ptrutil.ToPtr[int64](300) + imp.Video.H = ptrutil.ToPtr[int64](250) imp.Video.MIMEs = []string{"video/mp4"} } diff --git a/modules/pubmatic/openwrap/adapterthrottle.go b/modules/pubmatic/openwrap/adapterthrottle.go index 41bebb1c977..25dd6b699fe 100644 --- a/modules/pubmatic/openwrap/adapterthrottle.go +++ b/modules/pubmatic/openwrap/adapterthrottle.go @@ -4,7 +4,7 @@ import ( "math/rand" "strconv" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) // GetAdapterThrottleMap creates map of adapter and bool value which tells whether the adapter should be throtled or not diff --git a/modules/pubmatic/openwrap/adapterthrottle_test.go b/modules/pubmatic/openwrap/adapterthrottle_test.go index f07c98cc94b..c3d449ede7a 100644 --- a/modules/pubmatic/openwrap/adapterthrottle_test.go +++ b/modules/pubmatic/openwrap/adapterthrottle_test.go @@ -3,7 +3,7 @@ package openwrap import ( "testing" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/adunitconfig/app.go b/modules/pubmatic/openwrap/adunitconfig/app.go index 3b329b278c7..e28df7822b8 100644 --- a/modules/pubmatic/openwrap/adunitconfig/app.go +++ b/modules/pubmatic/openwrap/adunitconfig/app.go @@ -1,9 +1,9 @@ package adunitconfig import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) func ReplaceAppObjectFromAdUnitConfig(rCtx models.RequestCtx, app *openrtb2.App) { @@ -63,11 +63,11 @@ func ReplaceAppObjectFromAdUnitConfig(rCtx models.RequestCtx, app *openrtb2.App) app.Ver = adUnitCfg.App.Ver } - if app.PrivacyPolicy == 0 { + if app.PrivacyPolicy == nil || *app.PrivacyPolicy == 0 { app.PrivacyPolicy = adUnitCfg.App.PrivacyPolicy } - if app.Paid == 0 { + if app.Paid == nil || *app.Paid == 0 { app.Paid = adUnitCfg.App.Paid } diff --git a/modules/pubmatic/openwrap/adunitconfig/banner.go b/modules/pubmatic/openwrap/adunitconfig/banner.go index b94cf86ef45..373c21c1137 100644 --- a/modules/pubmatic/openwrap/adunitconfig/banner.go +++ b/modules/pubmatic/openwrap/adunitconfig/banner.go @@ -4,9 +4,9 @@ import ( "runtime/debug" "github.com/golang/glog" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) func UpdateBannerObjectWithAdunitConfig(rCtx models.RequestCtx, imp openrtb2.Imp, div string) (adUnitCtx models.AdUnitCtx) { diff --git a/modules/pubmatic/openwrap/adunitconfig/banner_test.go b/modules/pubmatic/openwrap/adunitconfig/banner_test.go index 70016dbc67a..ff42f09de12 100644 --- a/modules/pubmatic/openwrap/adunitconfig/banner_test.go +++ b/modules/pubmatic/openwrap/adunitconfig/banner_test.go @@ -4,11 +4,11 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/openrtb2" - mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/adunitconfig/common.go b/modules/pubmatic/openwrap/adunitconfig/common.go index 35a6d3ff674..aac08a205e8 100644 --- a/modules/pubmatic/openwrap/adunitconfig/common.go +++ b/modules/pubmatic/openwrap/adunitconfig/common.go @@ -4,8 +4,8 @@ import ( "encoding/json" "strings" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) func selectSlot(rCtx models.RequestCtx, h, w int64, tagid, div, source string) (slotAdUnitConfig *adunitconfig.AdConfig, slotName string, isRegex bool, matchedRegex string) { diff --git a/modules/pubmatic/openwrap/adunitconfig/common_test.go b/modules/pubmatic/openwrap/adunitconfig/common_test.go index db2c242dc02..0f052d61aba 100644 --- a/modules/pubmatic/openwrap/adunitconfig/common_test.go +++ b/modules/pubmatic/openwrap/adunitconfig/common_test.go @@ -3,10 +3,10 @@ package adunitconfig import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/adunitconfig/device.go b/modules/pubmatic/openwrap/adunitconfig/device.go index b52644afd1f..32908adb398 100644 --- a/modules/pubmatic/openwrap/adunitconfig/device.go +++ b/modules/pubmatic/openwrap/adunitconfig/device.go @@ -1,10 +1,10 @@ package adunitconfig import ( - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) func ReplaceDeviceTypeFromAdUnitConfig(rCtx models.RequestCtx, device **openrtb2.Device) { diff --git a/modules/pubmatic/openwrap/adunitconfig/floors.go b/modules/pubmatic/openwrap/adunitconfig/floors.go index 3bd8ebe9930..f1686b32b0e 100644 --- a/modules/pubmatic/openwrap/adunitconfig/floors.go +++ b/modules/pubmatic/openwrap/adunitconfig/floors.go @@ -1,8 +1,8 @@ package adunitconfig import ( - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) func UpdateFloorsExtObjectFromAdUnitConfig(rCtx models.RequestCtx, requestExt *models.RequestExt) { diff --git a/modules/pubmatic/openwrap/adunitconfig/regex.go b/modules/pubmatic/openwrap/adunitconfig/regex.go index 617e5174799..8a1b5978281 100644 --- a/modules/pubmatic/openwrap/adunitconfig/regex.go +++ b/modules/pubmatic/openwrap/adunitconfig/regex.go @@ -1,7 +1,7 @@ package adunitconfig import ( - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) func getRegexMatch(rctx models.RequestCtx, slotName string) string { diff --git a/modules/pubmatic/openwrap/adunitconfig/regex_test.go b/modules/pubmatic/openwrap/adunitconfig/regex_test.go index 4d0e472e615..59d54dda40d 100644 --- a/modules/pubmatic/openwrap/adunitconfig/regex_test.go +++ b/modules/pubmatic/openwrap/adunitconfig/regex_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/magiconair/properties/assert" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) func TestGetRegexMatch(t *testing.T) { diff --git a/modules/pubmatic/openwrap/adunitconfig/utils.go b/modules/pubmatic/openwrap/adunitconfig/utils.go index fc1a81fb717..b6bc0fd0f3e 100644 --- a/modules/pubmatic/openwrap/adunitconfig/utils.go +++ b/modules/pubmatic/openwrap/adunitconfig/utils.go @@ -1,17 +1,24 @@ package adunitconfig import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) // TODO use this func GetMatchedSlotName(rCtx models.RequestCtx, imp openrtb2.Imp, impExt models.ImpExtension) (slotAdUnitConfig *adunitconfig.AdConfig, isRegex bool) { div := "" - height := imp.Video.H - width := imp.Video.W + var height, width int64 + if imp.Video != nil { + if imp.Video.H != nil { + height = *imp.Video.H + } + if imp.Video.W != nil { + width = *imp.Video.W + } + } tagID := imp.TagID if impExt.Wrapper != nil { diff --git a/modules/pubmatic/openwrap/adunitconfig/utils_test.go b/modules/pubmatic/openwrap/adunitconfig/utils_test.go index 4ea1b5bf0fe..5f82eb5f3f2 100644 --- a/modules/pubmatic/openwrap/adunitconfig/utils_test.go +++ b/modules/pubmatic/openwrap/adunitconfig/utils_test.go @@ -3,12 +3,12 @@ package adunitconfig import ( "testing" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/adunitconfig/video.go b/modules/pubmatic/openwrap/adunitconfig/video.go index b09bc40cecb..f794bba2e01 100644 --- a/modules/pubmatic/openwrap/adunitconfig/video.go +++ b/modules/pubmatic/openwrap/adunitconfig/video.go @@ -4,10 +4,10 @@ import ( "runtime/debug" "github.com/golang/glog" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) func UpdateVideoObjectWithAdunitConfig(rCtx models.RequestCtx, imp openrtb2.Imp, div string, connectionType *adcom1.ConnectionType) (adUnitCtx models.AdUnitCtx) { @@ -41,8 +41,12 @@ func UpdateVideoObjectWithAdunitConfig(rCtx models.RequestCtx, imp openrtb2.Imp, var height, width int64 if imp.Video != nil { - height = imp.Video.H - width = imp.Video.W + if imp.Video.H != nil { + height = *imp.Video.H + } + if imp.Video.W != nil { + width = *imp.Video.W + } } adUnitCtx.SelectedSlotAdUnitConfig, adUnitCtx.MatchedSlot, adUnitCtx.IsRegex, adUnitCtx.MatchedRegex = selectSlot(rCtx, height, width, imp.TagID, div, rCtx.Source) diff --git a/modules/pubmatic/openwrap/adunitconfig/video_test.go b/modules/pubmatic/openwrap/adunitconfig/video_test.go index 833b4febb53..a0e66f4e291 100644 --- a/modules/pubmatic/openwrap/adunitconfig/video_test.go +++ b/modules/pubmatic/openwrap/adunitconfig/video_test.go @@ -4,12 +4,12 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/allprocessedbidresponsehook.go b/modules/pubmatic/openwrap/allprocessedbidresponsehook.go index e9565fb7a7c..7a701a5706a 100644 --- a/modules/pubmatic/openwrap/allprocessedbidresponsehook.go +++ b/modules/pubmatic/openwrap/allprocessedbidresponsehook.go @@ -3,11 +3,11 @@ package openwrap import ( "context" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/utils" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/utils" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // handleAllProcessedBidResponsesHook will create unique id for each bid in bid Response. This hook is introduced diff --git a/modules/pubmatic/openwrap/allprocessedbidresponsehook_test.go b/modules/pubmatic/openwrap/allprocessedbidresponsehook_test.go index c5b95879315..6ea94b313f5 100644 --- a/modules/pubmatic/openwrap/allprocessedbidresponsehook_test.go +++ b/modules/pubmatic/openwrap/allprocessedbidresponsehook_test.go @@ -5,13 +5,13 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/exchange/entities" - "github.com/prebid/prebid-server/hooks/hookanalytics" - "github.com/prebid/prebid-server/hooks/hookstage" - mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/exchange/entities" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/auctionresponsehook.go b/modules/pubmatic/openwrap/auctionresponsehook.go index a32e3e727be..9a5f97e112f 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook.go +++ b/modules/pubmatic/openwrap/auctionresponsehook.go @@ -6,15 +6,15 @@ import ( "strconv" "time" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/hooks/hookanalytics" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adunitconfig" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/tracker" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/utils" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/tracker" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/utils" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func (m OpenWrap) handleAuctionResponseHook( diff --git a/modules/pubmatic/openwrap/auctionresponsehook_test.go b/modules/pubmatic/openwrap/auctionresponsehook_test.go index b5d8a8ecfb7..a7f649c8239 100644 --- a/modules/pubmatic/openwrap/auctionresponsehook_test.go +++ b/modules/pubmatic/openwrap/auctionresponsehook_test.go @@ -7,16 +7,16 @@ import ( "time" "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/hooks/hookstage" - mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" - mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" - mock_feature "github.com/prebid/prebid-server/modules/pubmatic/openwrap/publisherfeature/mock" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + mock_feature "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/publisherfeature/mock" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/beforevalidationhook.go b/modules/pubmatic/openwrap/beforevalidationhook.go index 7cf6ef95598..899f661f88e 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook.go +++ b/modules/pubmatic/openwrap/beforevalidationhook.go @@ -10,20 +10,20 @@ import ( "strings" "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adapters" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adunitconfig" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/bidderparams" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/customdimensions" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - modelsAdunitConfig "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/boolutil" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adapters" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/bidderparams" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/customdimensions" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + modelsAdunitConfig "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/boolutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) func (m OpenWrap) handleBeforeValidationHook( @@ -153,6 +153,12 @@ func (m OpenWrap) handleBeforeValidationHook( result.Warnings = append(result.Warnings, "update the rCtx.PartnerConfigMap with ABTest data") } + // To check if VAST unwrap needs to be enabled for given request + if isVastUnwrapEnabled(rCtx.PartnerConfigMap, m.cfg.Features.VASTUnwrapPercent) { + rCtx.ABTestConfigApplied = 1 // Re-use AB Test flag for VAST unwrap feature + rCtx.VastUnwrapEnabled = true + } + //TMax should be updated after ABTest processing rCtx.TMax = m.setTimeout(rCtx, payload.BidRequest) @@ -669,6 +675,20 @@ func (m *OpenWrap) applyBannerAdUnitConfig(rCtx models.RequestCtx, imp *openrtb2 } } +// isVastUnwrapEnabled return whether to enable vastunwrap or not +func isVastUnwrapEnabled(partnerConfigMap map[int]map[string]string, vastUnwrapTraffic int) bool { + trafficPercentage := vastUnwrapTraffic + unwrapEnabled := models.GetVersionLevelPropertyFromPartnerConfig(partnerConfigMap, models.VastUnwrapperEnableKey) == models.Enabled + if unwrapEnabled { + if value := models.GetVersionLevelPropertyFromPartnerConfig(partnerConfigMap, models.VastUnwrapTrafficPercentKey); len(value) > 0 { + if trafficPercentDB, err := strconv.Atoi(value); err == nil { + trafficPercentage = trafficPercentDB + } + } + } + return unwrapEnabled && GetRandomNumberIn1To100() <= trafficPercentage +} + /* getSlotName will return slot name according to below priority 1. imp.ext.gpid @@ -976,11 +996,11 @@ func updateImpVideoWithVideoConfig(imp *openrtb2.Imp, configObjInVideoConfig *mo imp.Video.Protocols = configObjInVideoConfig.Protocols } - if imp.Video.W == 0 { + if imp.Video.W == nil { imp.Video.W = configObjInVideoConfig.W } - if imp.Video.H == 0 { + if imp.Video.H == nil { imp.Video.H = configObjInVideoConfig.H } @@ -988,7 +1008,7 @@ func updateImpVideoWithVideoConfig(imp *openrtb2.Imp, configObjInVideoConfig *mo imp.Video.Sequence = configObjInVideoConfig.Sequence } - if imp.Video.BoxingAllowed == 0 { + if imp.Video.BoxingAllowed == nil { imp.Video.BoxingAllowed = configObjInVideoConfig.BoxingAllowed } @@ -1023,10 +1043,10 @@ func updateImpVideoWithVideoConfig(imp *openrtb2.Imp, configObjInVideoConfig *mo func updateAmpImpVideoWithDefault(imp *openrtb2.Imp) { - if imp.Video.W == 0 { + if imp.Video.W == nil { imp.Video.W = getW(imp) } - if imp.Video.H == 0 { + if imp.Video.H == nil { imp.Video.H = getH(imp) } if imp.Video.MIMEs == nil { @@ -1067,30 +1087,30 @@ func updateAmpImpVideoWithDefault(imp *openrtb2.Imp) { } } -func getW(imp *openrtb2.Imp) int64 { +func getW(imp *openrtb2.Imp) *int64 { if imp.Banner != nil { if imp.Banner.W != nil { - return *imp.Banner.W + return imp.Banner.W } for _, format := range imp.Banner.Format { if format.W != 0 { - return format.W + return &format.W } } } - return 0 + return nil } -func getH(imp *openrtb2.Imp) int64 { +func getH(imp *openrtb2.Imp) *int64 { if imp.Banner != nil { if imp.Banner.H != nil { - return *imp.Banner.H + return imp.Banner.H } for _, format := range imp.Banner.Format { if format.H != 0 { - return format.H + return &format.H } } } - return 0 + return nil } diff --git a/modules/pubmatic/openwrap/beforevalidationhook_test.go b/modules/pubmatic/openwrap/beforevalidationhook_test.go index 24523b619af..ee24a078d44 100644 --- a/modules/pubmatic/openwrap/beforevalidationhook_test.go +++ b/modules/pubmatic/openwrap/beforevalidationhook_test.go @@ -9,23 +9,24 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/hooks/hookanalytics" - "github.com/prebid/prebid-server/hooks/hookstage" - adapters "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adapters" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" - mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - modelsAdunitConfig "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" - mock_feature "github.com/prebid/prebid-server/modules/pubmatic/openwrap/publisherfeature/mock" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + adapters "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adapters" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + modelsAdunitConfig "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + mock_feature "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/publisherfeature/mock" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/stretchr/testify/assert" ) @@ -72,8 +73,8 @@ func getTestBidRequest(isSite bool) *openrtb2.BidRequest { H: ptrutil.ToPtr[int64](300), }, Video: &openrtb2.Video{ - W: 200, - H: 300, + W: ptrutil.ToPtr[int64](200), + H: ptrutil.ToPtr[int64](300), Plcmt: 1, }, }, @@ -919,8 +920,8 @@ func TestOpenWrap_applyProfileChanges(t *testing.T) { H: ptrutil.ToPtr[int64](300), }, Video: &openrtb2.Video{ - W: 200, - H: 300, + W: ptrutil.ToPtr[int64](200), + H: ptrutil.ToPtr[int64](300), Plcmt: 1, }, }, @@ -982,8 +983,8 @@ func TestOpenWrap_applyProfileChanges(t *testing.T) { H: ptrutil.ToPtr[int64](300), }, Video: &openrtb2.Video{ - W: 200, - H: 300, + W: ptrutil.ToPtr[int64](200), + H: ptrutil.ToPtr[int64](300), Plcmt: 1, }, }, @@ -1057,8 +1058,8 @@ func TestOpenWrap_applyProfileChanges(t *testing.T) { H: ptrutil.ToPtr[int64](300), }, Video: &openrtb2.Video{ - W: 200, - H: 300, + W: ptrutil.ToPtr[int64](200), + H: ptrutil.ToPtr[int64](300), Plcmt: 1, }, }, @@ -1271,8 +1272,8 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { imp: &openrtb2.Imp{ ID: "testImp", Video: &openrtb2.Video{ - W: 200, - H: 300, + W: ptrutil.ToPtr[int64](200), + H: ptrutil.ToPtr[int64](300), }, }, }, @@ -1280,8 +1281,8 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { imp: &openrtb2.Imp{ ID: "testImp", Video: &openrtb2.Video{ - W: 200, - H: 300, + W: ptrutil.ToPtr[int64](200), + H: ptrutil.ToPtr[int64](300), }, }, rCtx: models.RequestCtx{ @@ -1316,8 +1317,8 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { imp: &openrtb2.Imp{ ID: "testImp", Video: &openrtb2.Video{ - W: 200, - H: 300, + W: ptrutil.ToPtr[int64](200), + H: ptrutil.ToPtr[int64](300), }, }, }, @@ -1365,10 +1366,10 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { MaxExtended: 50, Linearity: 1, Protocol: 1, - W: 640, - H: 480, + W: ptrutil.ToPtr[int64](640), + H: ptrutil.ToPtr[int64](480), Sequence: 2, - BoxingAllowed: 1, + BoxingAllowed: ptrutil.ToPtr[int8](1), PlaybackEnd: 2, MIMEs: []string{"mimes"}, API: []adcom1.APIFramework{1, 2}, @@ -1396,8 +1397,8 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { imp: &openrtb2.Imp{ ID: "testImp", Video: &openrtb2.Video{ - W: 640, - H: 480, + W: ptrutil.ToPtr[int64](640), + H: ptrutil.ToPtr[int64](480), MinDuration: 10, MaxDuration: 40, Skip: ptrutil.ToPtr(int8(1)), @@ -1411,7 +1412,7 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { Linearity: 1, Protocol: 1, Sequence: 2, - BoxingAllowed: 1, + BoxingAllowed: ptrutil.ToPtr[int8](1), PlaybackEnd: 2, MIMEs: []string{"mimes"}, API: []adcom1.APIFramework{1, 2}, @@ -1445,10 +1446,10 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { MaxExtended: 50, Linearity: 1, Protocol: 1, - W: 640, - H: 480, + W: ptrutil.ToPtr[int64](640), + H: ptrutil.ToPtr[int64](480), Sequence: 2, - BoxingAllowed: 1, + BoxingAllowed: ptrutil.ToPtr[int8](1), PlaybackEnd: 2, MIMEs: []string{"mimes"}, API: []adcom1.APIFramework{1, 2}, @@ -1497,8 +1498,8 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { imp: &openrtb2.Imp{ ID: "testImp", Video: &openrtb2.Video{ - W: 640, - H: 480, + W: ptrutil.ToPtr[int64](640), + H: ptrutil.ToPtr[int64](480), MinDuration: 20, MaxDuration: 60, Skip: ptrutil.ToPtr(int8(2)), @@ -1511,8 +1512,8 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { imp: &openrtb2.Imp{ ID: "testImp", Video: &openrtb2.Video{ - W: 640, - H: 480, + W: ptrutil.ToPtr[int64](640), + H: ptrutil.ToPtr[int64](480), MinDuration: 20, MaxDuration: 60, Skip: ptrutil.ToPtr(int8(2)), @@ -1578,8 +1579,8 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackClickSoundOn}, PlaybackEnd: adcom1.PlaybackFloating, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryProgressive}, - W: 300, - H: 400, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](400), }, }, }, @@ -1612,8 +1613,8 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackClickSoundOn}, PlaybackEnd: adcom1.PlaybackFloating, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryProgressive}, - W: 300, - H: 400, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](400), }, }, rCtx: models.RequestCtx{ @@ -1640,8 +1641,8 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackClickSoundOn}, PlaybackEnd: adcom1.PlaybackFloating, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryProgressive}, - W: 300, - H: 400, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](400), }, }, }, @@ -1701,8 +1702,8 @@ func TestOpenWrap_applyVideoAdUnitConfig(t *testing.T) { PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackPageLoadSoundOff}, PlaybackEnd: adcom1.PlaybackCompletion, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryProgressive, adcom1.DeliveryDownload}, - W: 250, - H: 300, + W: ptrutil.ToPtr[int64](250), + H: ptrutil.ToPtr[int64](300), }, }, rCtx: models.RequestCtx{ @@ -3191,8 +3192,8 @@ func TestOpenWrapHandleBeforeValidationHook(t *testing.T) { Config: &adunitconfig.VideoConfig{ Video: openrtb2.Video{ MIMEs: []string{"video/mp4", "video/mpeg"}, - W: 640, - H: 480, + W: ptrutil.ToPtr[int64](640), + H: ptrutil.ToPtr[int64](480), }, }, }, @@ -3376,6 +3377,386 @@ func TestUserAgent_handleBeforeValidationHook(t *testing.T) { } } +func TestVASTUnwrap_handleBeforeValidationHook(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockCache := mock_cache.NewMockCache(ctrl) + mockEngine := mock_metrics.NewMockMetricsEngine(ctrl) + mockFeature := mock_feature.NewMockFeature(ctrl) + + type fields struct { + cfg config.Config + cache cache.Cache + metricEngine metrics.MetricsEngine + } + type args struct { + ctx context.Context + moduleCtx hookstage.ModuleInvocationContext + payload hookstage.BeforeValidationRequestPayload + bidrequest json.RawMessage + randomNumber int + } + type want struct { + rctx *models.RequestCtx + error bool + } + tests := []struct { + name string + fields fields + args args + want want + setup func() + }{ + { + name: "VAST Unwrap Disabled in DB, traffic percent present in config", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": rctx, + }, + }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"tagid":"adunit","bidfloor":4.3,"bidfloorcur":"USD","ext":{"bidder":{"pubmatic":{"keywords":[{"key":"pmzoneid","value":["val1","val2"]}]}},"prebid":{}}}],"site":{"domain":"test.com","page":"www.test.com","publisher":{"id":"5890"}},"device":{"ua":"Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36","ip":"123.145.167.10"},"user":{"id":"119208432","buyeruid":"1rwe432","yob":1980,"gender":"F","geo":{"country":"US","region":"CA","metro":"90001","city":"Alamo"}},"wseat":["Wseat_0","Wseat_1"],"bseat":["Bseat_0","Bseat_1"],"cur":["cur_0","cur_1"],"wlang":["Wlang_0","Wlang_1"],"bcat":["bcat_0","bcat_1"],"badv":["badv_0","badv_1"],"bapp":["bapp_0","bapp_1"],"source":{"ext":{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}},"ext":{"prebid":{},"wrapper":{"test":123,"profileid":123,"versionid":1,"wiid":"test_display_wiid"}}}`), + }, + fields: fields{ + cache: mockCache, + metricEngine: mockEngine, + cfg: config.Config{ + Features: config.FeatureToggle{ + VASTUnwrapPercent: 10, + }, + }, + }, + setup: func() { + mockCache.EXPECT().GetMappingsFromCacheV25(gomock.Any(), gomock.Any()).Return(map[string]models.SlotMapping{ + "adunit@700x900": { + SlotName: "adunit@700x900", + SlotMappings: map[string]interface{}{ + models.SITE_CACHE_KEY: "12313", + models.TAG_CACHE_KEY: "45343", + }, + }, + }) + mockCache.EXPECT().GetSlotToHashValueMapFromCacheV25(gomock.Any(), gomock.Any()).Return(models.SlotMappingInfo{ + OrderedSlotList: []string{"adunit@700x900"}, + HashValueMap: map[string]string{ + "adunit@700x900": "1232433543534543", + }, + }) + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + 2: { + models.PARTNER_ID: "2", + models.PREBID_PARTNER_NAME: "appnexus", + models.BidderCode: "appnexus", + models.SERVER_SIDE_FLAG: "1", + models.KEY_GEN_PATTERN: "_AU_@_W_x_H_", + models.TIMEOUT: "200", + }, + -1: { + models.DisplayVersionID: "1", + models.PLATFORM_KEY: models.PLATFORM_APP, + models.VastUnwrapperEnableKey: "0", + }, + }, nil) + mockCache.EXPECT().GetAdunitConfigFromCache(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&adunitconfig.AdUnitConfig{}) + + mockEngine.EXPECT().RecordPlatformPublisherPartnerReqStats(rctx.Platform, "5890", "appnexus") + mockEngine.EXPECT().RecordPublisherRequests(rctx.Endpoint, "5890", rctx.Platform) + mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") + mockFeature.EXPECT().IsTBFFeatureEnabled(gomock.Any(), gomock.Any()).Return(false) + }, + want: want{ + rctx: &models.RequestCtx{ + VastUnwrapEnabled: false, + }, + error: false, + }, + }, + { + name: "VAST Unwrap Enabled in DB, traffic percent not present in config and DB", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": rctx, + }, + }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"tagid":"adunit","bidfloor":4.3,"bidfloorcur":"USD","ext":{"bidder":{"pubmatic":{"keywords":[{"key":"pmzoneid","value":["val1","val2"]}]}},"prebid":{}}}],"site":{"domain":"test.com","page":"www.test.com","publisher":{"id":"5890"}},"device":{"ua":"Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36","ip":"123.145.167.10"},"user":{"id":"119208432","buyeruid":"1rwe432","yob":1980,"gender":"F","geo":{"country":"US","region":"CA","metro":"90001","city":"Alamo"}},"wseat":["Wseat_0","Wseat_1"],"bseat":["Bseat_0","Bseat_1"],"cur":["cur_0","cur_1"],"wlang":["Wlang_0","Wlang_1"],"bcat":["bcat_0","bcat_1"],"badv":["badv_0","badv_1"],"bapp":["bapp_0","bapp_1"],"source":{"ext":{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}},"ext":{"prebid":{},"wrapper":{"test":123,"profileid":123,"versionid":1,"wiid":"test_display_wiid"}}}`), + randomNumber: 20, + }, + fields: fields{ + cache: mockCache, + metricEngine: mockEngine, + }, + setup: func() { + mockCache.EXPECT().GetMappingsFromCacheV25(gomock.Any(), gomock.Any()).Return(map[string]models.SlotMapping{ + "adunit@700x900": { + SlotName: "adunit@700x900", + SlotMappings: map[string]interface{}{ + models.SITE_CACHE_KEY: "12313", + models.TAG_CACHE_KEY: "45343", + }, + }, + }) + mockCache.EXPECT().GetSlotToHashValueMapFromCacheV25(gomock.Any(), gomock.Any()).Return(models.SlotMappingInfo{ + OrderedSlotList: []string{"adunit@700x900"}, + HashValueMap: map[string]string{ + "adunit@700x900": "1232433543534543", + }, + }) + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + 2: { + models.PARTNER_ID: "2", + models.PREBID_PARTNER_NAME: "appnexus", + models.BidderCode: "appnexus", + models.SERVER_SIDE_FLAG: "1", + models.KEY_GEN_PATTERN: "_AU_@_W_x_H_", + models.TIMEOUT: "200", + }, + -1: { + models.DisplayVersionID: "1", + models.PLATFORM_KEY: models.PLATFORM_APP, + models.VastUnwrapperEnableKey: "1", + }, + }, nil) + mockCache.EXPECT().GetAdunitConfigFromCache(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&adunitconfig.AdUnitConfig{}) + + mockEngine.EXPECT().RecordPlatformPublisherPartnerReqStats(rctx.Platform, "5890", "appnexus") + mockEngine.EXPECT().RecordPublisherRequests(rctx.Endpoint, "5890", rctx.Platform) + mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") + mockFeature.EXPECT().IsTBFFeatureEnabled(gomock.Any(), gomock.Any()).Return(false) + }, + want: want{ + rctx: &models.RequestCtx{ + VastUnwrapEnabled: false, + }, + error: false, + }, + }, + { + name: "VAST Unwrap Enabled in DB, traffic percent present in config", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": rctx, + }, + }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"tagid":"adunit","bidfloor":4.3,"bidfloorcur":"USD","ext":{"bidder":{"pubmatic":{"keywords":[{"key":"pmzoneid","value":["val1","val2"]}]}},"prebid":{}}}],"site":{"domain":"test.com","page":"www.test.com","publisher":{"id":"5890"}},"device":{"ua":"Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36","ip":"123.145.167.10"},"user":{"id":"119208432","buyeruid":"1rwe432","yob":1980,"gender":"F","geo":{"country":"US","region":"CA","metro":"90001","city":"Alamo"}},"wseat":["Wseat_0","Wseat_1"],"bseat":["Bseat_0","Bseat_1"],"cur":["cur_0","cur_1"],"wlang":["Wlang_0","Wlang_1"],"bcat":["bcat_0","bcat_1"],"badv":["badv_0","badv_1"],"bapp":["bapp_0","bapp_1"],"source":{"ext":{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}},"ext":{"prebid":{},"wrapper":{"test":123,"profileid":123,"versionid":1,"wiid":"test_display_wiid"}}}`), + randomNumber: 20, + }, + fields: fields{ + cache: mockCache, + metricEngine: mockEngine, + cfg: config.Config{ + Features: config.FeatureToggle{ + VASTUnwrapPercent: 100, + }, + }, + }, + setup: func() { + mockCache.EXPECT().GetMappingsFromCacheV25(gomock.Any(), gomock.Any()).Return(map[string]models.SlotMapping{ + "adunit@700x900": { + SlotName: "adunit@700x900", + SlotMappings: map[string]interface{}{ + models.SITE_CACHE_KEY: "12313", + models.TAG_CACHE_KEY: "45343", + }, + }, + }) + mockCache.EXPECT().GetSlotToHashValueMapFromCacheV25(gomock.Any(), gomock.Any()).Return(models.SlotMappingInfo{ + OrderedSlotList: []string{"adunit@700x900"}, + HashValueMap: map[string]string{ + "adunit@700x900": "1232433543534543", + }, + }) + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + 2: { + models.PARTNER_ID: "2", + models.PREBID_PARTNER_NAME: "appnexus", + models.BidderCode: "appnexus", + models.SERVER_SIDE_FLAG: "1", + models.KEY_GEN_PATTERN: "_AU_@_W_x_H_", + models.TIMEOUT: "200", + }, + -1: { + models.DisplayVersionID: "1", + models.PLATFORM_KEY: models.PLATFORM_APP, + models.VastUnwrapperEnableKey: "1", + }, + }, nil) + mockCache.EXPECT().GetAdunitConfigFromCache(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&adunitconfig.AdUnitConfig{}) + + mockEngine.EXPECT().RecordPlatformPublisherPartnerReqStats(rctx.Platform, "5890", "appnexus") + mockEngine.EXPECT().RecordPublisherRequests(rctx.Endpoint, "5890", rctx.Platform) + mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") + mockFeature.EXPECT().IsTBFFeatureEnabled(gomock.Any(), gomock.Any()).Return(false) + }, + want: want{ + rctx: &models.RequestCtx{ + VastUnwrapEnabled: true, + }, + error: false, + }, + }, + { + name: "VAST Unwrap Enabled in DB, traffic percent present in config and DB", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": rctx, + }, + }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"tagid":"adunit","bidfloor":4.3,"bidfloorcur":"USD","ext":{"bidder":{"pubmatic":{"keywords":[{"key":"pmzoneid","value":["val1","val2"]}]}},"prebid":{}}}],"site":{"domain":"test.com","page":"www.test.com","publisher":{"id":"5890"}},"device":{"ua":"Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36","ip":"123.145.167.10"},"user":{"id":"119208432","buyeruid":"1rwe432","yob":1980,"gender":"F","geo":{"country":"US","region":"CA","metro":"90001","city":"Alamo"}},"wseat":["Wseat_0","Wseat_1"],"bseat":["Bseat_0","Bseat_1"],"cur":["cur_0","cur_1"],"wlang":["Wlang_0","Wlang_1"],"bcat":["bcat_0","bcat_1"],"badv":["badv_0","badv_1"],"bapp":["bapp_0","bapp_1"],"source":{"ext":{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}},"ext":{"prebid":{},"wrapper":{"test":123,"profileid":123,"versionid":1,"wiid":"test_display_wiid"}}}`), + randomNumber: 20, + }, + fields: fields{ + cache: mockCache, + metricEngine: mockEngine, + cfg: config.Config{ + Features: config.FeatureToggle{ + VASTUnwrapPercent: 10, + }, + }, + }, + setup: func() { + mockCache.EXPECT().GetMappingsFromCacheV25(gomock.Any(), gomock.Any()).Return(map[string]models.SlotMapping{ + "adunit@700x900": { + SlotName: "adunit@700x900", + SlotMappings: map[string]interface{}{ + models.SITE_CACHE_KEY: "12313", + models.TAG_CACHE_KEY: "45343", + }, + }, + }) + mockCache.EXPECT().GetSlotToHashValueMapFromCacheV25(gomock.Any(), gomock.Any()).Return(models.SlotMappingInfo{ + OrderedSlotList: []string{"adunit@700x900"}, + HashValueMap: map[string]string{ + "adunit@700x900": "1232433543534543", + }, + }) + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + 2: { + models.PARTNER_ID: "2", + models.PREBID_PARTNER_NAME: "appnexus", + models.BidderCode: "appnexus", + models.SERVER_SIDE_FLAG: "1", + models.KEY_GEN_PATTERN: "_AU_@_W_x_H_", + models.TIMEOUT: "200", + }, + -1: { + models.DisplayVersionID: "1", + models.PLATFORM_KEY: models.PLATFORM_APP, + models.VastUnwrapperEnableKey: "1", + models.VastUnwrapTrafficPercentKey: "50", + }, + }, nil) + mockCache.EXPECT().GetAdunitConfigFromCache(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&adunitconfig.AdUnitConfig{}) + + mockEngine.EXPECT().RecordPlatformPublisherPartnerReqStats(rctx.Platform, "5890", "appnexus") + mockEngine.EXPECT().RecordPublisherRequests(rctx.Endpoint, "5890", rctx.Platform) + mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") + mockFeature.EXPECT().IsTBFFeatureEnabled(gomock.Any(), gomock.Any()).Return(false) + }, + want: want{ + rctx: &models.RequestCtx{ + VastUnwrapEnabled: true, + }, + error: false, + }, + }, + { + name: "VAST Unwrap Enabled DB, traffic percent not present in config", + args: args{ + ctx: context.Background(), + moduleCtx: hookstage.ModuleInvocationContext{ + ModuleContext: hookstage.ModuleContext{ + "rctx": rctx, + }, + }, + bidrequest: json.RawMessage(`{"id":"123-456-789","imp":[{"id":"123","banner":{"format":[{"w":728,"h":90},{"w":300,"h":250}],"w":700,"h":900},"video":{"mimes":["video/mp4","video/mpeg"],"w":640,"h":480},"tagid":"adunit","bidfloor":4.3,"bidfloorcur":"USD","ext":{"bidder":{"pubmatic":{"keywords":[{"key":"pmzoneid","value":["val1","val2"]}]}},"prebid":{}}}],"site":{"domain":"test.com","page":"www.test.com","publisher":{"id":"5890"}},"device":{"ua":"Mozilla/5.0(X11;Linuxx86_64)AppleWebKit/537.36(KHTML,likeGecko)Chrome/52.0.2743.82Safari/537.36","ip":"123.145.167.10"},"user":{"id":"119208432","buyeruid":"1rwe432","yob":1980,"gender":"F","geo":{"country":"US","region":"CA","metro":"90001","city":"Alamo"}},"wseat":["Wseat_0","Wseat_1"],"bseat":["Bseat_0","Bseat_1"],"cur":["cur_0","cur_1"],"wlang":["Wlang_0","Wlang_1"],"bcat":["bcat_0","bcat_1"],"badv":["badv_0","badv_1"],"bapp":["bapp_0","bapp_1"],"source":{"ext":{"omidpn":"MyIntegrationPartner","omidpv":"7.1"}},"ext":{"prebid":{},"wrapper":{"test":123,"profileid":123,"versionid":1,"wiid":"test_display_wiid"}}}`), + randomNumber: 20, + }, + fields: fields{ + cache: mockCache, + metricEngine: mockEngine, + }, + setup: func() { + mockCache.EXPECT().GetMappingsFromCacheV25(gomock.Any(), gomock.Any()).Return(map[string]models.SlotMapping{ + "adunit@700x900": { + SlotName: "adunit@700x900", + SlotMappings: map[string]interface{}{ + models.SITE_CACHE_KEY: "12313", + models.TAG_CACHE_KEY: "45343", + }, + }, + }) + mockCache.EXPECT().GetSlotToHashValueMapFromCacheV25(gomock.Any(), gomock.Any()).Return(models.SlotMappingInfo{ + OrderedSlotList: []string{"adunit@700x900"}, + HashValueMap: map[string]string{ + "adunit@700x900": "1232433543534543", + }, + }) + mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ + 2: { + models.PARTNER_ID: "2", + models.PREBID_PARTNER_NAME: "appnexus", + models.BidderCode: "appnexus", + models.SERVER_SIDE_FLAG: "1", + models.KEY_GEN_PATTERN: "_AU_@_W_x_H_", + models.TIMEOUT: "200", + }, + -1: { + models.DisplayVersionID: "1", + models.PLATFORM_KEY: models.PLATFORM_APP, + models.VastUnwrapperEnableKey: "1", + models.VastUnwrapTrafficPercentKey: "100", + }, + }, nil) + mockCache.EXPECT().GetAdunitConfigFromCache(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(&adunitconfig.AdUnitConfig{}) + + mockEngine.EXPECT().RecordPlatformPublisherPartnerReqStats(rctx.Platform, "5890", "appnexus") + mockEngine.EXPECT().RecordPublisherRequests(rctx.Endpoint, "5890", rctx.Platform) + mockEngine.EXPECT().RecordPublisherProfileRequests("5890", "1234") + mockFeature.EXPECT().IsTBFFeatureEnabled(gomock.Any(), gomock.Any()).Return(false) + }, + want: want{ + rctx: &models.RequestCtx{ + VastUnwrapEnabled: true, + }, + error: false, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.setup != nil { + tt.setup() + } + GetRandomNumberIn1To100 = func() int { + return tt.args.randomNumber + } + + adapters.InitBidders("./static/bidder-params/") + m := OpenWrap{ + cfg: tt.fields.cfg, + cache: tt.fields.cache, + metricEngine: tt.fields.metricEngine, + featureConfig: mockFeature, + } + tt.args.payload.BidRequest = &openrtb2.BidRequest{} + json.Unmarshal(tt.args.bidrequest, tt.args.payload.BidRequest) + + _, err := m.handleBeforeValidationHook(tt.args.ctx, tt.args.moduleCtx, tt.args.payload) + assert.Equal(t, tt.want.error, err != nil, "mismatched error received from handleBeforeValidationHook") + iRctx := tt.args.moduleCtx.ModuleContext["rctx"] + assert.Equal(t, tt.want.rctx == nil, iRctx == nil, "mismatched rctx received from handleBeforeValidationHook") + gotRctx := iRctx.(models.RequestCtx) + assert.Equal(t, tt.want.rctx.VastUnwrapEnabled, gotRctx.VastUnwrapEnabled, "mismatched rctx.VastUnwrapEnabled received from handleBeforeValidationHook") + }) + } +} func TestImpBidCtx_handleBeforeValidationHook(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -4042,8 +4423,8 @@ func TestUpdateImpVideoWithVideoConfig(t *testing.T) { }, configObjInVideoConfig: &modelsAdunitConfig.VideoConfig{ Video: openrtb2.Video{ - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), MIMEs: []string{"MP4"}, Linearity: adcom1.LinearityNonLinear, StartDelay: adcom1.StartMidRoll.Ptr(), @@ -4055,7 +4436,7 @@ func TestUpdateImpVideoWithVideoConfig(t *testing.T) { Skip: ptrutil.ToPtr(int8(1)), SkipMin: 10, SkipAfter: 5, - BoxingAllowed: 2, + BoxingAllowed: ptrutil.ToPtr[int8](2), PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackPageLoadSoundOn}, PlaybackEnd: adcom1.PlaybackCompletion, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryProgressive}, @@ -4071,8 +4452,8 @@ func TestUpdateImpVideoWithVideoConfig(t *testing.T) { }, }, wantImpVideo: &openrtb2.Video{ - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), MIMEs: []string{"MP4"}, Linearity: adcom1.LinearityNonLinear, StartDelay: adcom1.StartMidRoll.Ptr(), @@ -4084,7 +4465,7 @@ func TestUpdateImpVideoWithVideoConfig(t *testing.T) { Skip: ptrutil.ToPtr[int8](1), SkipMin: 10, SkipAfter: 5, - BoxingAllowed: 2, + BoxingAllowed: ptrutil.ToPtr[int8](2), PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackPageLoadSoundOn}, PlaybackEnd: adcom1.PlaybackCompletion, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryProgressive}, @@ -4104,15 +4485,15 @@ func TestUpdateImpVideoWithVideoConfig(t *testing.T) { imp: &openrtb2.Imp{ ID: "123", Video: &openrtb2.Video{ - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), }, }, configObjInVideoConfig: &modelsAdunitConfig.VideoConfig{ Video: openrtb2.Video{ - W: 400, - H: 300, + W: ptrutil.ToPtr[int64](400), + H: ptrutil.ToPtr[int64](300), MIMEs: []string{"MP4"}, Linearity: adcom1.LinearityNonLinear, StartDelay: adcom1.StartMidRoll.Ptr(), @@ -4124,7 +4505,7 @@ func TestUpdateImpVideoWithVideoConfig(t *testing.T) { Skip: ptrutil.ToPtr(int8(1)), SkipMin: 10, SkipAfter: 5, - BoxingAllowed: 2, + BoxingAllowed: ptrutil.ToPtr[int8](2), PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackPageLoadSoundOn}, PlaybackEnd: adcom1.PlaybackCompletion, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryProgressive}, @@ -4140,8 +4521,8 @@ func TestUpdateImpVideoWithVideoConfig(t *testing.T) { }, }, wantImpVideo: &openrtb2.Video{ - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), MIMEs: []string{"MP4"}, Linearity: adcom1.LinearityNonLinear, StartDelay: adcom1.StartMidRoll.Ptr(), @@ -4153,7 +4534,7 @@ func TestUpdateImpVideoWithVideoConfig(t *testing.T) { Skip: ptrutil.ToPtr[int8](1), SkipMin: 10, SkipAfter: 5, - BoxingAllowed: 2, + BoxingAllowed: ptrutil.ToPtr[int8](2), PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackPageLoadSoundOn}, PlaybackEnd: adcom1.PlaybackCompletion, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryProgressive}, @@ -4206,12 +4587,12 @@ func TestUpdateAmpImpVideoWithDefault(t *testing.T) { Skip: ptrutil.ToPtr[int8](0), SkipMin: 0, SkipAfter: 0, - BoxingAllowed: 1, + BoxingAllowed: ptrutil.ToPtr[int8](1), PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackPageLoadSoundOff}, PlaybackEnd: adcom1.PlaybackCompletion, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryProgressive, adcom1.DeliveryDownload}, - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), }, }, { @@ -4245,12 +4626,12 @@ func TestUpdateAmpImpVideoWithDefault(t *testing.T) { Skip: ptrutil.ToPtr[int8](0), SkipMin: 0, SkipAfter: 0, - BoxingAllowed: 1, + BoxingAllowed: ptrutil.ToPtr[int8](1), PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackPageLoadSoundOff}, PlaybackEnd: adcom1.PlaybackCompletion, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryProgressive, adcom1.DeliveryDownload}, - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), }, }, { @@ -4286,12 +4667,12 @@ func TestUpdateAmpImpVideoWithDefault(t *testing.T) { Skip: ptrutil.ToPtr[int8](0), SkipMin: 0, SkipAfter: 0, - BoxingAllowed: 1, + BoxingAllowed: ptrutil.ToPtr[int8](1), PlaybackMethod: []adcom1.PlaybackMethod{adcom1.PlaybackPageLoadSoundOff}, PlaybackEnd: adcom1.PlaybackCompletion, Delivery: []adcom1.DeliveryMethod{adcom1.DeliveryProgressive, adcom1.DeliveryDownload}, - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), }, }, } @@ -4309,7 +4690,7 @@ func TestGetW(t *testing.T) { tests := []struct { name string args args - want int64 + want *int64 }{ { name: "Empty banner and format", @@ -4320,7 +4701,7 @@ func TestGetW(t *testing.T) { }, }, }, - want: 0, + want: nil, }, { name: "both banner and format are present", @@ -4336,7 +4717,7 @@ func TestGetW(t *testing.T) { }, }, }, - want: 300, + want: ptrutil.ToPtr[int64](300), }, { name: "only format is present", @@ -4351,7 +4732,7 @@ func TestGetW(t *testing.T) { }, }, }, - want: 400, + want: ptrutil.ToPtr[int64](400), }, } for _, tt := range tests { @@ -4369,7 +4750,7 @@ func TestGetH(t *testing.T) { tests := []struct { name string args args - want int64 + want *int64 }{ { name: "Empty banner and format", @@ -4380,7 +4761,7 @@ func TestGetH(t *testing.T) { }, }, }, - want: 0, + want: nil, }, { name: "both banner and format are present", @@ -4396,7 +4777,7 @@ func TestGetH(t *testing.T) { }, }, }, - want: 300, + want: ptrutil.ToPtr[int64](300), }, { name: "only format is present", @@ -4411,7 +4792,7 @@ func TestGetH(t *testing.T) { }, }, }, - want: 400, + want: ptrutil.ToPtr[int64](400), }, } for _, tt := range tests { @@ -4421,3 +4802,95 @@ func TestGetH(t *testing.T) { }) } } + +func TestIsVastUnwrapEnabled(t *testing.T) { + + type args struct { + PartnerConfigMap map[int]map[string]string + VASTUnwrapTraffic int + } + tests := []struct { + name string + args args + randomNumber int + want bool + }{ + { + name: "vastunwrap is enabled and traffic percent in DB and config, DB percent should be preferred", + args: args{ + PartnerConfigMap: map[int]map[string]string{ + -1: { + models.VastUnwrapperEnableKey: "1", + models.VastUnwrapTrafficPercentKey: "90", + }, + }, + VASTUnwrapTraffic: 9, + }, + randomNumber: 10, + want: true, + }, + { + name: "vastunwrap is enabled and DB traffic percent is less than random number", + args: args{ + PartnerConfigMap: map[int]map[string]string{ + -1: { + models.VastUnwrapperEnableKey: "1", + models.VastUnwrapTrafficPercentKey: "90", + }, + }, + VASTUnwrapTraffic: 0, + }, + randomNumber: 91, + want: false, + }, + { + name: "vastunwrap is dissabled and config traffic percent is less than random number", + args: args{ + PartnerConfigMap: map[int]map[string]string{ + -1: { + models.VastUnwrapperEnableKey: "0", + }, + }, + VASTUnwrapTraffic: 5, + }, + randomNumber: 7, + want: false, + }, + { + name: "vastunwrap is enabled and traffic percent not present in DB, random num higher than traffic percent", + args: args{ + PartnerConfigMap: map[int]map[string]string{ + -1: { + models.VastUnwrapperEnableKey: "1", + }, + }, + VASTUnwrapTraffic: 5, + }, + randomNumber: 10, + want: false, + }, + + { + name: "vastunwrap is enabled and traffic percent not present in DB, random num less than traffic percent", + args: args{ + PartnerConfigMap: map[int]map[string]string{ + -1: { + models.VastUnwrapperEnableKey: "1", + }, + }, + VASTUnwrapTraffic: 10, + }, + randomNumber: 9, + want: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + GetRandomNumberIn1To100 = func() int { + return tt.randomNumber + } + got := isVastUnwrapEnabled(tt.args.PartnerConfigMap, tt.args.VASTUnwrapTraffic) + assert.Equal(t, tt.want, got, tt.name) + }) + } +} diff --git a/modules/pubmatic/openwrap/bidderparams/common.go b/modules/pubmatic/openwrap/bidderparams/common.go index 3ef871a5731..78ff16616cf 100644 --- a/modules/pubmatic/openwrap/bidderparams/common.go +++ b/modules/pubmatic/openwrap/bidderparams/common.go @@ -5,9 +5,9 @@ import ( "regexp" "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) var ignoreKeys = map[string]bool{ diff --git a/modules/pubmatic/openwrap/bidderparams/common_test.go b/modules/pubmatic/openwrap/bidderparams/common_test.go index 20aad471933..dddd55d59b2 100644 --- a/modules/pubmatic/openwrap/bidderparams/common_test.go +++ b/modules/pubmatic/openwrap/bidderparams/common_test.go @@ -4,10 +4,10 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/bidderparams/others.go b/modules/pubmatic/openwrap/bidderparams/others.go index 0a0cefbeb40..eaaa2b1058c 100644 --- a/modules/pubmatic/openwrap/bidderparams/others.go +++ b/modules/pubmatic/openwrap/bidderparams/others.go @@ -4,10 +4,10 @@ import ( "errors" "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adapters" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adapters" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) func PrepareAdapterParamsV25(rctx models.RequestCtx, cache cache.Cache, bidRequest openrtb2.BidRequest, imp openrtb2.Imp, impExt models.ImpExtension, partnerID int) (string, string, bool, []byte, error) { diff --git a/modules/pubmatic/openwrap/bidderparams/pubmatic.go b/modules/pubmatic/openwrap/bidderparams/pubmatic.go index e9438277240..5eadf568d1e 100644 --- a/modules/pubmatic/openwrap/bidderparams/pubmatic.go +++ b/modules/pubmatic/openwrap/bidderparams/pubmatic.go @@ -5,10 +5,10 @@ import ( "fmt" "strconv" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func PreparePubMaticParamsV25(rctx models.RequestCtx, cache cache.Cache, bidRequest openrtb2.BidRequest, imp openrtb2.Imp, impExt models.ImpExtension, partnerID int) (string, string, bool, []byte, error) { diff --git a/modules/pubmatic/openwrap/bidderparams/pubmatic_test.go b/modules/pubmatic/openwrap/bidderparams/pubmatic_test.go index 62788ad0429..3186edb4bfc 100644 --- a/modules/pubmatic/openwrap/bidderparams/pubmatic_test.go +++ b/modules/pubmatic/openwrap/bidderparams/pubmatic_test.go @@ -4,12 +4,12 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -33,8 +33,8 @@ func getTestImp(tagID string, banner bool, video bool) openrtb2.Imp { return openrtb2.Imp{ ID: "111", Video: &openrtb2.Video{ - W: 200, - H: 300, + W: ptrutil.ToPtr[int64](200), + H: ptrutil.ToPtr[int64](300), }, TagID: tagID, } diff --git a/modules/pubmatic/openwrap/bidderparams/vast.go b/modules/pubmatic/openwrap/bidderparams/vast.go index 59518141f65..1459e8ee5fe 100644 --- a/modules/pubmatic/openwrap/bidderparams/vast.go +++ b/modules/pubmatic/openwrap/bidderparams/vast.go @@ -7,10 +7,10 @@ import ( "strconv" "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adapters" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adapters" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) func PrepareVASTBidderParams(rctx models.RequestCtx, cache cache.Cache, bidRequest openrtb2.BidRequest, imp openrtb2.Imp, impExt models.ImpExtension, partnerID int, adpodExt *models.AdPod) (string, json.RawMessage, []string, error) { diff --git a/modules/pubmatic/openwrap/bidderparams/vast_test.go b/modules/pubmatic/openwrap/bidderparams/vast_test.go index e2a48f114e1..9d0051705b2 100644 --- a/modules/pubmatic/openwrap/bidderparams/vast_test.go +++ b/modules/pubmatic/openwrap/bidderparams/vast_test.go @@ -5,9 +5,9 @@ import ( "sort" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/bidders.go b/modules/pubmatic/openwrap/bidders.go index 7babf67cb6d..17b70b53c8d 100644 --- a/modules/pubmatic/openwrap/bidders.go +++ b/modules/pubmatic/openwrap/bidders.go @@ -1,8 +1,8 @@ package openwrap import ( - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) var alias = map[string]string{ diff --git a/modules/pubmatic/openwrap/cache/cache.go b/modules/pubmatic/openwrap/cache/cache.go index 392e56e9055..1890ea483c0 100644 --- a/modules/pubmatic/openwrap/cache/cache.go +++ b/modules/pubmatic/openwrap/cache/cache.go @@ -1,9 +1,9 @@ package cache import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) type Cache interface { diff --git a/modules/pubmatic/openwrap/cache/gocache/adunit_config.go b/modules/pubmatic/openwrap/cache/gocache/adunit_config.go index 507b3c9323e..07bbd5450c9 100644 --- a/modules/pubmatic/openwrap/cache/gocache/adunit_config.go +++ b/modules/pubmatic/openwrap/cache/gocache/adunit_config.go @@ -3,8 +3,8 @@ package gocache import ( "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) func (c *cache) populateCacheWithAdunitConfig(pubID int, profileID, displayVersion int) (err error) { diff --git a/modules/pubmatic/openwrap/cache/gocache/adunit_config_test.go b/modules/pubmatic/openwrap/cache/gocache/adunit_config_test.go index 1fba4efbf99..e04f3bd079d 100644 --- a/modules/pubmatic/openwrap/cache/gocache/adunit_config_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/adunit_config_test.go @@ -9,14 +9,14 @@ import ( "github.com/golang/mock/gomock" gocache "github.com/patrickmn/go-cache" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database" - mock_database "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database" + mock_database "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability.go b/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability.go index fd17754c7d0..4e8fd798083 100644 --- a/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability.go +++ b/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability.go @@ -3,7 +3,7 @@ package gocache import ( "fmt" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) var ( diff --git a/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability_test.go b/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability_test.go index 52b40fb721c..dd9a3c68cd5 100644 --- a/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/fullscreenclickability_test.go @@ -7,11 +7,11 @@ import ( "github.com/golang/mock/gomock" gocache "github.com/patrickmn/go-cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database" - mock_database "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database/mock" - mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database" + mock_database "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database/mock" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/cache/gocache/gocache.go b/modules/pubmatic/openwrap/cache/gocache/gocache.go index 05f4522b5c9..b327d0fcd34 100644 --- a/modules/pubmatic/openwrap/cache/gocache/gocache.go +++ b/modules/pubmatic/openwrap/cache/gocache/gocache.go @@ -6,9 +6,9 @@ import ( "time" gocache "github.com/patrickmn/go-cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" ) const ( diff --git a/modules/pubmatic/openwrap/cache/gocache/gocache_test.go b/modules/pubmatic/openwrap/cache/gocache/gocache_test.go index 02a5c09b22c..277be698b5d 100644 --- a/modules/pubmatic/openwrap/cache/gocache/gocache_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/gocache_test.go @@ -7,11 +7,11 @@ import ( "github.com/golang/mock/gomock" gocache "github.com/patrickmn/go-cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database" - mock_database "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" - mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database" + mock_database "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/cache/gocache/partner_config.go b/modules/pubmatic/openwrap/cache/gocache/partner_config.go index bfb44066228..51579f20958 100644 --- a/modules/pubmatic/openwrap/cache/gocache/partner_config.go +++ b/modules/pubmatic/openwrap/cache/gocache/partner_config.go @@ -6,8 +6,8 @@ import ( "strconv" "time" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) // GetPartnerConfigMap returns partnerConfigMap using given parameters diff --git a/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go b/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go index 75780e39c4b..464d1ce5ad5 100644 --- a/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/partner_config_test.go @@ -7,12 +7,12 @@ import ( "github.com/golang/mock/gomock" gocache "github.com/patrickmn/go-cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database" - mock_database "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database/mock" - mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database" + mock_database "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database/mock" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/cache/gocache/publisher_feature.go b/modules/pubmatic/openwrap/cache/gocache/publisher_feature.go index 3bc27f71274..c27ac7d9214 100644 --- a/modules/pubmatic/openwrap/cache/gocache/publisher_feature.go +++ b/modules/pubmatic/openwrap/cache/gocache/publisher_feature.go @@ -3,7 +3,7 @@ package gocache import ( "fmt" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) var errorPubFeatureUpdate = "[ErrorPubFeatureUpdate]:%w" diff --git a/modules/pubmatic/openwrap/cache/gocache/publisher_feature_test.go b/modules/pubmatic/openwrap/cache/gocache/publisher_feature_test.go index 886b3f5feca..4cba7d91d3a 100644 --- a/modules/pubmatic/openwrap/cache/gocache/publisher_feature_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/publisher_feature_test.go @@ -6,12 +6,12 @@ import ( "github.com/golang/mock/gomock" gocache "github.com/patrickmn/go-cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database" - mock_database "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" - mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database" + mock_database "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/cache/gocache/slot_mappings.go b/modules/pubmatic/openwrap/cache/gocache/slot_mappings.go index e2841fed93b..b25246a7937 100644 --- a/modules/pubmatic/openwrap/cache/gocache/slot_mappings.go +++ b/modules/pubmatic/openwrap/cache/gocache/slot_mappings.go @@ -6,8 +6,8 @@ import ( "strconv" "strings" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // PopulateCacheWithPubSlotNameHash will put the slot names and hashes for a publisher in cache diff --git a/modules/pubmatic/openwrap/cache/gocache/slot_mappings_test.go b/modules/pubmatic/openwrap/cache/gocache/slot_mappings_test.go index bbafdd4a01e..037cffbbb64 100644 --- a/modules/pubmatic/openwrap/cache/gocache/slot_mappings_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/slot_mappings_test.go @@ -8,10 +8,10 @@ import ( "github.com/golang/mock/gomock" gocache "github.com/patrickmn/go-cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database" - mock_database "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database" + mock_database "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/cache/gocache/sync_test.go b/modules/pubmatic/openwrap/cache/gocache/sync_test.go index 51179643e8b..82b6799501b 100644 --- a/modules/pubmatic/openwrap/cache/gocache/sync_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/sync_test.go @@ -7,9 +7,9 @@ import ( "github.com/golang/mock/gomock" gocache "github.com/patrickmn/go-cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database" - mock_database "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database" + mock_database "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database/mock" ) func Test_cache_LockAndLoad(t *testing.T) { diff --git a/modules/pubmatic/openwrap/cache/gocache/util.go b/modules/pubmatic/openwrap/cache/gocache/util.go index b32c44c6cf4..787c74f9a95 100644 --- a/modules/pubmatic/openwrap/cache/gocache/util.go +++ b/modules/pubmatic/openwrap/cache/gocache/util.go @@ -1,8 +1,8 @@ package gocache import ( - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) // validation check for Universal Pixels Object diff --git a/modules/pubmatic/openwrap/cache/gocache/util_test.go b/modules/pubmatic/openwrap/cache/gocache/util_test.go index 1693a2c8397..c6c274f6f66 100644 --- a/modules/pubmatic/openwrap/cache/gocache/util_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/util_test.go @@ -3,8 +3,8 @@ package gocache import ( "testing" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/cache/gocache/vast_tags.go b/modules/pubmatic/openwrap/cache/gocache/vast_tags.go index 84b8f5a63de..8f2137a03ec 100644 --- a/modules/pubmatic/openwrap/cache/gocache/vast_tags.go +++ b/modules/pubmatic/openwrap/cache/gocache/vast_tags.go @@ -1,7 +1,7 @@ package gocache import ( - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) // PopulatePublisherVASTTags will put publisher level VAST Tag details into cache diff --git a/modules/pubmatic/openwrap/cache/gocache/vast_tags_test.go b/modules/pubmatic/openwrap/cache/gocache/vast_tags_test.go index e44540632be..3d529633551 100644 --- a/modules/pubmatic/openwrap/cache/gocache/vast_tags_test.go +++ b/modules/pubmatic/openwrap/cache/gocache/vast_tags_test.go @@ -7,10 +7,10 @@ import ( "github.com/golang/mock/gomock" gocache "github.com/patrickmn/go-cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database" - mock_database "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database" + mock_database "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/cache/mock/mock.go b/modules/pubmatic/openwrap/cache/mock/mock.go index 58047adf0d4..ba5f92e24cf 100644 --- a/modules/pubmatic/openwrap/cache/mock/mock.go +++ b/modules/pubmatic/openwrap/cache/mock/mock.go @@ -6,9 +6,9 @@ package mock_cache import ( gomock "github.com/golang/mock/gomock" - openrtb2 "github.com/prebid/openrtb/v19/openrtb2" - models "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - adunitconfig "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + openrtb2 "github.com/prebid/openrtb/v20/openrtb2" + models "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + adunitconfig "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" reflect "reflect" ) diff --git a/modules/pubmatic/openwrap/config/config.go b/modules/pubmatic/openwrap/config/config.go index d3c11b92501..1f5e385195a 100755 --- a/modules/pubmatic/openwrap/config/config.go +++ b/modules/pubmatic/openwrap/config/config.go @@ -3,20 +3,22 @@ package config import ( "time" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/stats" + unWrapCfg "git.pubmatic.com/vastunwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/stats" ) // Config contains the values read from the config file at boot time type Config struct { - Server Server - Database Database - Cache Cache - Timeout Timeout - Tracker Tracker - PixelView PixelView - Features FeatureToggle - Log Log - Stats stats.Stats + Server Server + Database Database + Cache Cache + Timeout Timeout + Tracker Tracker + PixelView PixelView + Features FeatureToggle + Log Log + Stats stats.Stats + VastUnwrapCfg unWrapCfg.VastUnWrapCfg } type Server struct { @@ -80,6 +82,8 @@ type PixelView struct { } type FeatureToggle struct { + VASTUnwrapPercent int + VASTUnwrapStatsPercent int } type Log struct { //Log Details diff --git a/modules/pubmatic/openwrap/contenttransperencyobject.go b/modules/pubmatic/openwrap/contenttransperencyobject.go index 7921120ab1d..89624688638 100644 --- a/modules/pubmatic/openwrap/contenttransperencyobject.go +++ b/modules/pubmatic/openwrap/contenttransperencyobject.go @@ -1,9 +1,9 @@ package openwrap import ( - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // setContentObjectTransparencyObject from request or AdUnit Object diff --git a/modules/pubmatic/openwrap/customdimensions/customdimension.go b/modules/pubmatic/openwrap/customdimensions/customdimension.go index b97c49f9d2e..2cec9c5c111 100644 --- a/modules/pubmatic/openwrap/customdimensions/customdimension.go +++ b/modules/pubmatic/openwrap/customdimensions/customdimension.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/buger/jsonparser" ) diff --git a/modules/pubmatic/openwrap/customdimensions/customdimension_test.go b/modules/pubmatic/openwrap/customdimensions/customdimension_test.go index 866042a93c8..bd1c738878f 100644 --- a/modules/pubmatic/openwrap/customdimensions/customdimension_test.go +++ b/modules/pubmatic/openwrap/customdimensions/customdimension_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/database/database.go b/modules/pubmatic/openwrap/database/database.go index 11f1d1ec569..4474cc252d7 100644 --- a/modules/pubmatic/openwrap/database/database.go +++ b/modules/pubmatic/openwrap/database/database.go @@ -1,8 +1,8 @@ package database import ( - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) type Database interface { diff --git a/modules/pubmatic/openwrap/database/mock/mock.go b/modules/pubmatic/openwrap/database/mock/mock.go index fa60e0143c7..3f86c625b23 100644 --- a/modules/pubmatic/openwrap/database/mock/mock.go +++ b/modules/pubmatic/openwrap/database/mock/mock.go @@ -6,8 +6,8 @@ package mock_database import ( gomock "github.com/golang/mock/gomock" - models "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - adunitconfig "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + models "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + adunitconfig "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" reflect "reflect" ) diff --git a/modules/pubmatic/openwrap/database/mysql/adunit_config.go b/modules/pubmatic/openwrap/database/mysql/adunit_config.go index 6f5fce46c0f..98fbf8dd0a2 100644 --- a/modules/pubmatic/openwrap/database/mysql/adunit_config.go +++ b/modules/pubmatic/openwrap/database/mysql/adunit_config.go @@ -6,8 +6,8 @@ import ( "strconv" "strings" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) // GetAdunitConfig - Method to get adunit config for a given profile and display version from giym DB diff --git a/modules/pubmatic/openwrap/database/mysql/adunit_config_test.go b/modules/pubmatic/openwrap/database/mysql/adunit_config_test.go index 0bd64c1530c..75968ed56b0 100644 --- a/modules/pubmatic/openwrap/database/mysql/adunit_config_test.go +++ b/modules/pubmatic/openwrap/database/mysql/adunit_config_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/DATA-DOG/go-sqlmock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/database/mysql/fullscreenclickability_test.go b/modules/pubmatic/openwrap/database/mysql/fullscreenclickability_test.go index ea37a0decb3..c1aca4b29ee 100644 --- a/modules/pubmatic/openwrap/database/mysql/fullscreenclickability_test.go +++ b/modules/pubmatic/openwrap/database/mysql/fullscreenclickability_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/DATA-DOG/go-sqlmock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/database/mysql/mysql.go b/modules/pubmatic/openwrap/database/mysql/mysql.go index d8d32d32546..4b3d8edf1ed 100644 --- a/modules/pubmatic/openwrap/database/mysql/mysql.go +++ b/modules/pubmatic/openwrap/database/mysql/mysql.go @@ -4,7 +4,7 @@ import ( "database/sql" "sync" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" ) type mySqlDB struct { diff --git a/modules/pubmatic/openwrap/database/mysql/mysql_test.go b/modules/pubmatic/openwrap/database/mysql/mysql_test.go index 888072cf68f..c6d363bd4c8 100644 --- a/modules/pubmatic/openwrap/database/mysql/mysql_test.go +++ b/modules/pubmatic/openwrap/database/mysql/mysql_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/DATA-DOG/go-sqlmock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/database/mysql/partner_config.go b/modules/pubmatic/openwrap/database/mysql/partner_config.go index 8a423f0a407..aa70aca4e69 100644 --- a/modules/pubmatic/openwrap/database/mysql/partner_config.go +++ b/modules/pubmatic/openwrap/database/mysql/partner_config.go @@ -8,7 +8,7 @@ import ( "time" "github.com/golang/glog" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) // return the list of active server side header bidding partners diff --git a/modules/pubmatic/openwrap/database/mysql/partner_config_test.go b/modules/pubmatic/openwrap/database/mysql/partner_config_test.go index ea9f5e57159..4a81171d7b2 100644 --- a/modules/pubmatic/openwrap/database/mysql/partner_config_test.go +++ b/modules/pubmatic/openwrap/database/mysql/partner_config_test.go @@ -6,7 +6,7 @@ import ( "testing" "github.com/DATA-DOG/go-sqlmock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/database/mysql/publisher_feature.go b/modules/pubmatic/openwrap/database/mysql/publisher_feature.go index 6169b203ab6..66fbc3fbc52 100644 --- a/modules/pubmatic/openwrap/database/mysql/publisher_feature.go +++ b/modules/pubmatic/openwrap/database/mysql/publisher_feature.go @@ -4,7 +4,7 @@ import ( "database/sql" "github.com/golang/glog" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) func (db *mySqlDB) GetPublisherFeatureMap() (map[int]map[int]models.FeatureData, error) { diff --git a/modules/pubmatic/openwrap/database/mysql/publisher_feature_test.go b/modules/pubmatic/openwrap/database/mysql/publisher_feature_test.go index 1db4bcea8dc..4892e0f49de 100644 --- a/modules/pubmatic/openwrap/database/mysql/publisher_feature_test.go +++ b/modules/pubmatic/openwrap/database/mysql/publisher_feature_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/DATA-DOG/go-sqlmock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/database/mysql/slot_mapping.go b/modules/pubmatic/openwrap/database/mysql/slot_mapping.go index 46c4fbc8266..11135599a1f 100644 --- a/modules/pubmatic/openwrap/database/mysql/slot_mapping.go +++ b/modules/pubmatic/openwrap/database/mysql/slot_mapping.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) // GetPublisherSlotNameHash Returns a map of all slot names and hashes for a publisher diff --git a/modules/pubmatic/openwrap/database/mysql/slot_mapping_test.go b/modules/pubmatic/openwrap/database/mysql/slot_mapping_test.go index 2c74506300f..313714d7c9d 100644 --- a/modules/pubmatic/openwrap/database/mysql/slot_mapping_test.go +++ b/modules/pubmatic/openwrap/database/mysql/slot_mapping_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/DATA-DOG/go-sqlmock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/database/mysql/vasttags.go b/modules/pubmatic/openwrap/database/mysql/vasttags.go index b42fda90e41..6b036ac00a5 100644 --- a/modules/pubmatic/openwrap/database/mysql/vasttags.go +++ b/modules/pubmatic/openwrap/database/mysql/vasttags.go @@ -3,7 +3,7 @@ package mysql import ( "fmt" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) // GetPublisherVASTTags - Method to get vast tags associated with publisher id from giym DB diff --git a/modules/pubmatic/openwrap/database/mysql/vasttags_test.go b/modules/pubmatic/openwrap/database/mysql/vasttags_test.go index 8bc64ae2756..7e8b183d951 100644 --- a/modules/pubmatic/openwrap/database/mysql/vasttags_test.go +++ b/modules/pubmatic/openwrap/database/mysql/vasttags_test.go @@ -6,8 +6,8 @@ import ( "testing" "github.com/DATA-DOG/go-sqlmock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/defaultbids.go b/modules/pubmatic/openwrap/defaultbids.go index 907a9ae5839..3cadece0606 100644 --- a/modules/pubmatic/openwrap/defaultbids.go +++ b/modules/pubmatic/openwrap/defaultbids.go @@ -4,13 +4,13 @@ import ( "encoding/json" "strconv" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adunitconfig" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" uuid "github.com/satori/go.uuid" ) diff --git a/modules/pubmatic/openwrap/defaultbids_test.go b/modules/pubmatic/openwrap/defaultbids_test.go index 74711441a5e..7e493d4c373 100644 --- a/modules/pubmatic/openwrap/defaultbids_test.go +++ b/modules/pubmatic/openwrap/defaultbids_test.go @@ -3,10 +3,10 @@ package openwrap import ( "testing" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/device.go b/modules/pubmatic/openwrap/device.go index 1e5ae197851..fdd843d44e8 100644 --- a/modules/pubmatic/openwrap/device.go +++ b/modules/pubmatic/openwrap/device.go @@ -3,8 +3,8 @@ package openwrap import ( "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) func populateDeviceContext(dvc *models.DeviceCtx, device *openrtb2.Device) { diff --git a/modules/pubmatic/openwrap/device_test.go b/modules/pubmatic/openwrap/device_test.go index 9cbfd4877ff..3642014840a 100644 --- a/modules/pubmatic/openwrap/device_test.go +++ b/modules/pubmatic/openwrap/device_test.go @@ -5,9 +5,9 @@ import ( "strings" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/endpoints/legacy/openrtb/v25/video.go b/modules/pubmatic/openwrap/endpoints/legacy/openrtb/v25/video.go index c0015b20743..50076893bce 100644 --- a/modules/pubmatic/openwrap/endpoints/legacy/openrtb/v25/video.go +++ b/modules/pubmatic/openwrap/endpoints/legacy/openrtb/v25/video.go @@ -9,11 +9,12 @@ import ( "strings" "github.com/gofrs/uuid" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) func ConvertVideoToAuctionRequest(payload hookstage.EntrypointPayload, result *hookstage.HookResult[hookstage.EntrypointPayload]) (models.RequestExtWrapper, error) { @@ -75,7 +76,7 @@ func ConvertVideoToAuctionRequest(payload hookstage.EntrypointPayload, result *h bidRequest.Imp[0].Video.Sequence = *sequence } if boxingAllowed := GetCustomAtoI8(GetString(GetValueFromRequest(values, redirectQueryParams, models.BoxingAllowedORTBParam))); boxingAllowed != nil { - bidRequest.Imp[0].Video.BoxingAllowed = *boxingAllowed + bidRequest.Imp[0].Video.BoxingAllowed = boxingAllowed } if prctl := GetCustomAtoI8(GetString(GetValueFromRequest(values, redirectQueryParams, models.ProtocolORTBParam))); prctl != nil { bidRequest.Imp[0].Video.Protocol = adcom1.MediaCreativeSubtype(*prctl) @@ -98,8 +99,10 @@ func ConvertVideoToAuctionRequest(payload hookstage.EntrypointPayload, result *h size := GetString(GetValueFromRequest(values, redirectQueryParams, models.SizeORTBParam)) if size != "" && strings.Split(size, "x") != nil { sizeValues := strings.Split(size, "x") - bidRequest.Imp[0].Video.W, _ = strconv.ParseInt(sizeValues[0], 10, 64) - bidRequest.Imp[0].Video.H, _ = strconv.ParseInt(sizeValues[1], 10, 64) + w, _ := strconv.ParseInt(sizeValues[0], 10, 64) + h, _ := strconv.ParseInt(sizeValues[0], 10, 64) + bidRequest.Imp[0].Video.W = &w + bidRequest.Imp[0].Video.H = &h } slot := redirectQueryParams.Get(models.InventoryUnitKey) @@ -190,8 +193,8 @@ func ConvertVideoToAuctionRequest(payload hookstage.EntrypointPayload, result *h MACSHA1: GetString(GetValueFromRequest(values, redirectQueryParams, models.DeviceMacsha1ORTBParam)), MACMD5: GetString(GetValueFromRequest(values, redirectQueryParams, models.DeviceMacmd5ORTBParam)), Geo: &openrtb2.Geo{ - Lat: GetCustomStrToFloat(GetString(GetValueFromRequest(values, redirectQueryParams, models.GeoLatORTBParam))), - Lon: GetCustomStrToFloat(GetString(GetValueFromRequest(values, redirectQueryParams, models.GeoLonORTBParam))), + Lat: ptrutil.ToPtr(GetCustomStrToFloat(GetString(GetValueFromRequest(values, redirectQueryParams, models.GeoLatORTBParam)))), + Lon: ptrutil.ToPtr(GetCustomStrToFloat(GetString(GetValueFromRequest(values, redirectQueryParams, models.GeoLonORTBParam)))), Country: GetString(GetValueFromRequest(values, redirectQueryParams, models.GeoCountryORTBParam)), City: GetString(GetValueFromRequest(values, redirectQueryParams, models.GeoCityORTBParam)), Metro: GetString(GetValueFromRequest(values, redirectQueryParams, models.GeoMetroORTBParam)), @@ -202,12 +205,12 @@ func ConvertVideoToAuctionRequest(payload hookstage.EntrypointPayload, result *h paid := GetCustomAtoI8(GetString(GetValueFromRequest(values, redirectQueryParams, models.AppPaidORTBParam))) if paid != nil { - bidRequest.App.Paid = *paid + bidRequest.App.Paid = paid } js := GetCustomAtoI8(GetString(GetValueFromRequest(values, redirectQueryParams, models.DeviceJSORTBParam))) if js != nil { - bidRequest.Device.JS = *js + bidRequest.Device.JS = js } if locationTypeValue := GetCustomAtoI8(GetString(GetValueFromRequest(values, redirectQueryParams, models.GeoTypeORTBParam))); locationTypeValue != nil { diff --git a/modules/pubmatic/openwrap/entrypointhook.go b/modules/pubmatic/openwrap/entrypointhook.go index f99dfe34c93..7e619b8f65f 100644 --- a/modules/pubmatic/openwrap/entrypointhook.go +++ b/modules/pubmatic/openwrap/entrypointhook.go @@ -6,15 +6,15 @@ import ( "time" "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/hooks/hookstage" - v25 "github.com/prebid/prebid-server/modules/pubmatic/openwrap/endpoints/legacy/openrtb/v25" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/usersync" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + v25 "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/endpoints/legacy/openrtb/v25" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/usersync" uuid "github.com/satori/go.uuid" ) @@ -51,7 +51,8 @@ func (m OpenWrap) handleEntrypointHook( rCtx.Sshb = queryParams.Get("sshb") //Do not execute the module for requests processed in SSHB(8001) - if queryParams.Get("sshb") == "1" { + if rCtx.Sshb == models.Enabled { + rCtx.VastUnwrapEnabled = getVastUnwrapperEnable(payload.Request.Context(), models.VastUnwrapperEnableKey) return result, nil } endpoint = GetEndpoint(payload.Request.URL.Path, source) diff --git a/modules/pubmatic/openwrap/entrypointhook_test.go b/modules/pubmatic/openwrap/entrypointhook_test.go index 614835aed73..c893abeee49 100644 --- a/modules/pubmatic/openwrap/entrypointhook_test.go +++ b/modules/pubmatic/openwrap/entrypointhook_test.go @@ -6,14 +6,14 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/prebid/prebid-server/hooks/hookexecution" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/hooks/hookexecution" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/floors.go b/modules/pubmatic/openwrap/floors.go index cd1533e6a28..89f993c4524 100644 --- a/modules/pubmatic/openwrap/floors.go +++ b/modules/pubmatic/openwrap/floors.go @@ -3,9 +3,9 @@ package openwrap import ( "strings" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) func setFloorsExt(requestExt *models.RequestExt, configMap map[int]map[string]string) { diff --git a/modules/pubmatic/openwrap/floors_test.go b/modules/pubmatic/openwrap/floors_test.go index c1b30081698..50c43b4ad4f 100644 --- a/modules/pubmatic/openwrap/floors_test.go +++ b/modules/pubmatic/openwrap/floors_test.go @@ -3,8 +3,8 @@ package openwrap import ( "testing" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/hook_raw_bidder_response.go b/modules/pubmatic/openwrap/hook_raw_bidder_response.go new file mode 100644 index 00000000000..8bdc4f06b00 --- /dev/null +++ b/modules/pubmatic/openwrap/hook_raw_bidder_response.go @@ -0,0 +1,60 @@ +package openwrap + +import ( + "fmt" + "sync" + + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + + "github.com/prebid/prebid-server/v2/hooks/hookstage" +) + +func (m OpenWrap) handleRawBidderResponseHook( + miCtx hookstage.ModuleInvocationContext, + payload hookstage.RawBidderResponsePayload, +) (result hookstage.HookResult[hookstage.RawBidderResponsePayload], err error) { + vastRequestContext, ok := miCtx.ModuleContext[models.RequestContext].(models.RequestCtx) + if !ok { + result.DebugMessages = append(result.DebugMessages, "error: request-ctx not found in handleRawBidderResponseHook()") + return result, nil + } + + if vastRequestContext.VastUnwrapEnabled { + // Do Unwrap and Update Adm + wg := new(sync.WaitGroup) + for _, bid := range payload.Bids { + if string(bid.BidType) == models.MediaTypeVideo { + wg.Add(1) + go func(bid *adapters.TypedBid) { + defer wg.Done() + m.unwrap.Unwrap(miCtx.AccountID, payload.Bidder, bid, vastRequestContext.UA, vastRequestContext.IP, vastRequestContext.VastUnwrapStatsEnabled) + }(bid) + } + } + wg.Wait() + changeSet := hookstage.ChangeSet[hookstage.RawBidderResponsePayload]{} + changeSet.RawBidderResponse().Bids().Update(payload.Bids) + result.ChangeSet = changeSet + } else { + vastRequestContext.VastUnwrapStatsEnabled = GetRandomNumberIn1To100() <= m.cfg.Features.VASTUnwrapStatsPercent + if vastRequestContext.VastUnwrapStatsEnabled { + // Do Unwrap and Collect stats only + for _, bid := range payload.Bids { + if string(bid.BidType) == models.MediaTypeVideo { + go func(bid *adapters.TypedBid) { + m.unwrap.Unwrap(miCtx.AccountID, payload.Bidder, bid, vastRequestContext.UA, vastRequestContext.IP, vastRequestContext.VastUnwrapStatsEnabled) + }(bid) + } + } + } + } + + if vastRequestContext.VastUnwrapEnabled || vastRequestContext.VastUnwrapStatsEnabled { + result.DebugMessages = append(result.DebugMessages, + fmt.Sprintf("For pubid:[%d] VastUnwrapEnabled: [%v] VastUnwrapStatsEnabled:[%v] ", + vastRequestContext.PubID, vastRequestContext.VastUnwrapEnabled, vastRequestContext.VastUnwrapStatsEnabled)) + } + + return result, nil +} diff --git a/modules/pubmatic/openwrap/hook_raw_bidder_response_test.go b/modules/pubmatic/openwrap/hook_raw_bidder_response_test.go new file mode 100644 index 00000000000..07130c3aed3 --- /dev/null +++ b/modules/pubmatic/openwrap/hook_raw_bidder_response_test.go @@ -0,0 +1,435 @@ +package openwrap + +import ( + "fmt" + "net/http" + + "testing" + + unWrapCfg "git.pubmatic.com/vastunwrap/config" + "github.com/golang/mock/gomock" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/unwrap" + "github.com/stretchr/testify/assert" +) + +var vastXMLAdM = "PubMatic" +var invalidVastXMLAdM = "PubMatic" +var inlineXMLAdM = "Acudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1https://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&er=[ERRORCODE]https://track.dsptracker.com?p=1234&er=[ERRORCODE]https://aktrack.pubmatic.com/AdServer/AdDisplayTrackerServlet?operId=1&pubId=64195&siteId=47105&adId=1405154&adType=13&adServerId=243&kefact=1.000000&kaxefact=1.000000&kadNetFrequecy=0&kadwidth=0&kadheight=0&kadsizeid=97&kltstamp=1536933242&indirectAdId=0&adServerOptimizerId=2&ranreq=0.05969169352174375&kpbmtpfact=11.000000&dcId=1&tldId=0&passback=0&svr=ktk57&ekefact=er2bW2sDAwCra06ACbsIQySn5nqBtYsTl8fy5lupAexh37D_&ekaxefact=er2bW4EDAwB_LQpJJ23Fq0DcNC-NSAFXdpSQC8XBk_S33_Fa&ekpbmtpfact=er2bW5MDAwDJHdBnLBt5IrRuh7x0oqp_tjIALv_VvSQDAl6R&crID=m:1_x:3_y:3_p:11_va:3&lpu=ae.com&ucrid=678722001014421372&campaignId=16774&creativeId=0&pctr=0.000000&wDSPByrId=511&wDspId=27&wbId=0&wrId=0&wAdvID=3170&isRTB=1&rtbId=EBCA079F-8D7C-45B8-B733-92951F670AA1&imprId=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&oid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&pageURL=http%253A%252F%252Fowsdk-stagingams.pubmatic.com%253A8443%252Fvast-validator%252F%2523&sec=1&pmc=1https://DspImpressionTracker.com/https://mytracking.com/linear/closehttps://mytracking.com/linear/skiphttps://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=1https://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=2https://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=3https://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=4https://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=5https://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=600:00:04https://www.automationtester.inhttps://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=99https://stagingams.pubmatic.com:8443/openwrap/media/pubmatic.mp4https://stagingams.pubmatic.com:8443/openwrap/media/pubmatic.mp4https://stagingams.pubmatic.com:8443/openwrap/media/mp4-sample-3.mp4" + +func TestHandleRawBidderResponseHook(t *testing.T) { + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + mockMetricsEngine := mock_metrics.NewMockMetricsEngine(ctrl) + + type args struct { + module OpenWrap + payload hookstage.RawBidderResponsePayload + moduleInvocationCtx hookstage.ModuleInvocationContext + isAdmUpdated bool + randomNumber int + } + tests := []struct { + name string + args args + wantResult hookstage.HookResult[hookstage.RawBidderResponsePayload] + setup func() + wantErr bool + mockHandler http.HandlerFunc + }{ + + { + name: "Empty Request Context", + args: args{ + module: OpenWrap{}, + }, + wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{DebugMessages: []string{"error: request-ctx not found in handleRawBidderResponseHook()"}}, + wantErr: false, + }, + { + name: "Set Vast Unwrapper to false in request context with type video", + args: args{ + module: OpenWrap{ + cfg: config.Config{VastUnwrapCfg: unWrapCfg.VastUnWrapCfg{ + MaxWrapperSupport: 5, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, + APPConfig: unWrapCfg.AppConfig{UnwrapDefaultTimeout: 1500}, + }}, + metricEngine: mockMetricsEngine, + }, + payload: hookstage.RawBidderResponsePayload{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + ID: "Bid-123", + ImpID: fmt.Sprintf("div-adunit-%d", 123), + Price: 2.1, + AdM: "
This is an Ad
", + CrID: "Cr-234", + W: 100, + H: 50, + }, + BidType: "video", + }}}, + moduleInvocationCtx: hookstage.ModuleInvocationContext{ModuleContext: hookstage.ModuleContext{models.RequestContext: models.RequestCtx{VastUnwrapEnabled: false}}}, + randomNumber: 1, + }, + wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, + wantErr: false, + }, + { + name: "Set Vast Unwrapper to false in request context with type video, stats enabled true", + args: args{ + module: OpenWrap{ + cfg: config.Config{ + Features: config.FeatureToggle{ + VASTUnwrapStatsPercent: 2, + }, + VastUnwrapCfg: unWrapCfg.VastUnWrapCfg{ + MaxWrapperSupport: 5, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, + APPConfig: unWrapCfg.AppConfig{UnwrapDefaultTimeout: 1500}, + }}, + metricEngine: mockMetricsEngine, + }, + payload: hookstage.RawBidderResponsePayload{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + ID: "Bid-123", + ImpID: fmt.Sprintf("div-adunit-%d", 123), + Price: 2.1, + AdM: vastXMLAdM, + CrID: "Cr-234", + W: 100, + H: 50, + }, + BidType: "video", + }}, + Bidder: "pubmatic", + }, + moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{models.RequestContext: models.RequestCtx{VastUnwrapEnabled: false}}}, + randomNumber: 1, + }, + mockHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Add("unwrap-status", "0") + w.Header().Add("unwrap-count", "1") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(vastXMLAdM)) + }), + wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, + setup: func() { + mockMetricsEngine.EXPECT().RecordUnwrapRequestStatus("5890", "pubmatic", "0") + mockMetricsEngine.EXPECT().RecordUnwrapWrapperCount("5890", "pubmatic", "1") + mockMetricsEngine.EXPECT().RecordUnwrapRequestTime("5890", "pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()) + }, + wantErr: false, + }, + { + name: "Set Vast Unwrapper to true in request context with invalid vast xml", + args: args{ + module: OpenWrap{ + cfg: config.Config{ + Features: config.FeatureToggle{ + VASTUnwrapStatsPercent: 2, + VASTUnwrapPercent: 50, + }, + VastUnwrapCfg: unWrapCfg.VastUnWrapCfg{ + MaxWrapperSupport: 5, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, + APPConfig: unWrapCfg.AppConfig{UnwrapDefaultTimeout: 1500}, + }}, + metricEngine: mockMetricsEngine, + }, + payload: hookstage.RawBidderResponsePayload{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + ID: "Bid-123", + ImpID: fmt.Sprintf("div-adunit-%d", 123), + Price: 2.1, + AdM: invalidVastXMLAdM, + CrID: "Cr-234", + W: 100, + H: 50, + }, + BidType: "video", + }, + }, + Bidder: "pubmatic", + }, + moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{models.RequestContext: models.RequestCtx{VastUnwrapEnabled: true}}}, + }, + mockHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Add("unwrap-status", "1") + w.WriteHeader(http.StatusNoContent) + }), + wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, + setup: func() { + mockMetricsEngine.EXPECT().RecordUnwrapRequestStatus("5890", "pubmatic", "1") + mockMetricsEngine.EXPECT().RecordUnwrapRequestTime("5890", "pubmatic", gomock.Any()) + }, + wantErr: true, + }, + + { + name: "Set Vast Unwrapper to true in request context with type video", + mockHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Add("unwrap-status", "0") + w.Header().Add("unwrap-count", "1") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(inlineXMLAdM)) + }), + args: args{ + module: OpenWrap{ + cfg: config.Config{ + Features: config.FeatureToggle{ + VASTUnwrapStatsPercent: 2, + VASTUnwrapPercent: 50, + }, + VastUnwrapCfg: unWrapCfg.VastUnWrapCfg{ + MaxWrapperSupport: 5, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, + APPConfig: unWrapCfg.AppConfig{UnwrapDefaultTimeout: 1500}, + }}, + metricEngine: mockMetricsEngine, + }, + payload: hookstage.RawBidderResponsePayload{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + ID: "Bid-123", + ImpID: fmt.Sprintf("div-adunit-%d", 123), + Price: 2.1, + AdM: vastXMLAdM, + CrID: "Cr-234", + W: 100, + H: 50, + }, + BidType: "video", + }, + }, + Bidder: "pubmatic", + }, + moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{models.RequestContext: models.RequestCtx{VastUnwrapEnabled: true}}}, + + isAdmUpdated: true, + }, + wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, + setup: func() { + mockMetricsEngine.EXPECT().RecordUnwrapRequestStatus("5890", "pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapWrapperCount("5890", "pubmatic", "1").AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()).AnyTimes() + }, + wantErr: false, + }, + { + name: "Set Vast Unwrapper to true in request context for multiple bids with type video", + args: args{ + module: OpenWrap{ + cfg: config.Config{ + Features: config.FeatureToggle{ + VASTUnwrapStatsPercent: 2, + VASTUnwrapPercent: 50, + }, + VastUnwrapCfg: unWrapCfg.VastUnWrapCfg{ + MaxWrapperSupport: 5, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, + APPConfig: unWrapCfg.AppConfig{UnwrapDefaultTimeout: 1500}, + }}, + metricEngine: mockMetricsEngine, + }, + payload: hookstage.RawBidderResponsePayload{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + ID: "Bid-123", + ImpID: fmt.Sprintf("div-adunit-%d", 123), + Price: 2.1, + AdM: vastXMLAdM, + CrID: "Cr-234", + W: 100, + H: 50, + }, + BidType: "video", + }, + { + Bid: &openrtb2.Bid{ + ID: "Bid-456", + ImpID: fmt.Sprintf("div-adunit-%d", 123), + Price: 2.1, + AdM: vastXMLAdM, + CrID: "Cr-789", + W: 100, + H: 50, + }, + BidType: "video", + }}, + Bidder: "pubmatic", + }, + moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{models.RequestContext: models.RequestCtx{VastUnwrapEnabled: true}}}, + isAdmUpdated: true, + randomNumber: 10, + }, + mockHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Add("unwrap-status", "0") + w.Header().Add("unwrap-count", "1") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(inlineXMLAdM)) + }), + wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, + setup: func() { + mockMetricsEngine.EXPECT().RecordUnwrapRequestStatus("5890", "pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapWrapperCount("5890", "pubmatic", "1").AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()).AnyTimes() + }, + wantErr: false, + }, + + { + name: "Set Vast Unwrapper to true in request context for multiple bids with different type", + args: args{ + module: OpenWrap{ + cfg: config.Config{ + Features: config.FeatureToggle{ + VASTUnwrapStatsPercent: 2, + VASTUnwrapPercent: 50, + }, + VastUnwrapCfg: unWrapCfg.VastUnWrapCfg{ + MaxWrapperSupport: 5, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, + APPConfig: unWrapCfg.AppConfig{UnwrapDefaultTimeout: 1500}, + }}, + metricEngine: mockMetricsEngine, + }, + payload: hookstage.RawBidderResponsePayload{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + ID: "Bid-123", + ImpID: fmt.Sprintf("div-adunit-%d", 123), + Price: 2.1, + AdM: vastXMLAdM, + CrID: "Cr-234", + W: 100, + H: 50, + }, + BidType: "video", + }, + { + Bid: &openrtb2.Bid{ + ID: "Bid-456", + ImpID: fmt.Sprintf("div-adunit-%d", 123), + Price: 2.1, + AdM: "This is banner creative", + CrID: "Cr-789", + W: 100, + H: 50, + }, + BidType: "banner", + }}, + Bidder: "pubmatic", + }, + moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{models.RequestContext: models.RequestCtx{VastUnwrapEnabled: true}}}, + + isAdmUpdated: true, + }, + mockHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Add("unwrap-status", "0") + w.Header().Add("unwrap-count", "0") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(inlineXMLAdM)) + }), + wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, + setup: func() { + mockMetricsEngine.EXPECT().RecordUnwrapRequestStatus("5890", "pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapWrapperCount("5890", "pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "0", gomock.Any()).AnyTimes() + }, + wantErr: false, + }, + { + name: "Set Vast Unwrapper to true in request context with type video and source owsdk", + args: args{ + module: OpenWrap{ + cfg: config.Config{ + Features: config.FeatureToggle{ + VASTUnwrapStatsPercent: 2, + VASTUnwrapPercent: 50, + }, + VastUnwrapCfg: unWrapCfg.VastUnWrapCfg{ + MaxWrapperSupport: 5, + StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, + APPConfig: unWrapCfg.AppConfig{UnwrapDefaultTimeout: 1500}, + }}, + metricEngine: mockMetricsEngine, + }, + payload: hookstage.RawBidderResponsePayload{ + Bids: []*adapters.TypedBid{ + { + Bid: &openrtb2.Bid{ + ID: "Bid-123", + ImpID: fmt.Sprintf("div-adunit-%d", 123), + Price: 2.1, + AdM: vastXMLAdM, + CrID: "Cr-234", + W: 100, + H: 50, + }, + BidType: "video", + }, + }, + Bidder: "pubmatic", + }, + moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{models.RequestContext: models.RequestCtx{VastUnwrapEnabled: true}}}, + + isAdmUpdated: true, + }, + mockHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Add("unwrap-status", "0") + w.Header().Add("unwrap-count", "1") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(inlineXMLAdM)) + }), + wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, + setup: func() { + mockMetricsEngine.EXPECT().RecordUnwrapRequestStatus("5890", "pubmatic", "0").AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapWrapperCount("5890", "pubmatic", "1").AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() + mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()).AnyTimes() + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.setup != nil { + tt.setup() + } + + GetRandomNumberIn1To100 = func() int { + return tt.args.randomNumber + } + + m := tt.args.module + m.unwrap = unwrap.NewUnwrap("http://localhost:8001/unwrap", 200, tt.mockHandler, m.metricEngine) + _, err := m.handleRawBidderResponseHook(tt.args.moduleInvocationCtx, tt.args.payload) + if !assert.NoError(t, err, tt.wantErr) { + return + } + if tt.args.moduleInvocationCtx.ModuleContext != nil && tt.args.isAdmUpdated { + assert.Equal(t, inlineXMLAdM, tt.args.payload.Bids[0].Bid.AdM, "AdM is not updated correctly after executing RawBidderResponse hook.") + } + }) + } +} diff --git a/modules/pubmatic/openwrap/logger.go b/modules/pubmatic/openwrap/logger.go index e273c7d6764..dbdc7d387f3 100644 --- a/modules/pubmatic/openwrap/logger.go +++ b/modules/pubmatic/openwrap/logger.go @@ -4,8 +4,8 @@ import ( "encoding/json" "fmt" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) func getIncomingSlots(imp openrtb2.Imp) []string { @@ -23,8 +23,8 @@ func getIncomingSlots(imp openrtb2.Imp) []string { } } - if imp.Video != nil { - sizes[fmt.Sprintf("%dx%dv", imp.Video.W, imp.Video.H)] = struct{}{} + if imp.Video != nil && imp.Video.W != nil && imp.Video.H != nil { + sizes[fmt.Sprintf("%dx%dv", *imp.Video.W, *imp.Video.H)] = struct{}{} } var s []string diff --git a/modules/pubmatic/openwrap/logger_test.go b/modules/pubmatic/openwrap/logger_test.go index cad1a487354..5964e740523 100644 --- a/modules/pubmatic/openwrap/logger_test.go +++ b/modules/pubmatic/openwrap/logger_test.go @@ -3,8 +3,8 @@ package openwrap import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -83,8 +83,8 @@ func Test_getIncomingSlots(t *testing.T) { imp: openrtb2.Imp{ ID: "1", Video: &openrtb2.Video{ - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), }, }, }, @@ -109,8 +109,8 @@ func Test_getIncomingSlots(t *testing.T) { }, }, Video: &openrtb2.Video{ - W: 300, - H: 250, + W: ptrutil.ToPtr[int64](300), + H: ptrutil.ToPtr[int64](250), }, }, }, diff --git a/modules/pubmatic/openwrap/marketplace.go b/modules/pubmatic/openwrap/marketplace.go index 182a763fdfa..0aca9b8239e 100644 --- a/modules/pubmatic/openwrap/marketplace.go +++ b/modules/pubmatic/openwrap/marketplace.go @@ -3,8 +3,8 @@ package openwrap import ( "strings" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // TODO: Make this generic implementation diff --git a/modules/pubmatic/openwrap/marketplace_test.go b/modules/pubmatic/openwrap/marketplace_test.go index 9b3ca14e7fa..35048244692 100644 --- a/modules/pubmatic/openwrap/marketplace_test.go +++ b/modules/pubmatic/openwrap/marketplace_test.go @@ -3,8 +3,8 @@ package openwrap import ( "testing" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/matchedimpression.go b/modules/pubmatic/openwrap/matchedimpression.go index 4c6b3e11ce5..64c92102536 100644 --- a/modules/pubmatic/openwrap/matchedimpression.go +++ b/modules/pubmatic/openwrap/matchedimpression.go @@ -2,8 +2,8 @@ package openwrap import ( "github.com/golang/glog" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adapters" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adapters" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) func getMatchedImpression(rctx models.RequestCtx) map[string]int { diff --git a/modules/pubmatic/openwrap/metrics/config/metrics.go b/modules/pubmatic/openwrap/metrics/config/metrics.go index 8e03aa688aa..cb5609d457c 100644 --- a/modules/pubmatic/openwrap/metrics/config/metrics.go +++ b/modules/pubmatic/openwrap/metrics/config/metrics.go @@ -4,12 +4,12 @@ import ( "fmt" "time" - cfg "github.com/prebid/prebid-server/config" - metrics_cfg "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" - ow_prometheus "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/prometheus" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/stats" + cfg "github.com/prebid/prebid-server/v2/config" + metrics_cfg "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" + ow_prometheus "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/prometheus" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/stats" "github.com/prometheus/client_golang/prometheus" ) @@ -459,3 +459,37 @@ func (me *MultiMetricsEngine) RecordAmpVideoResponses(pubid, profileid string) { thisME.RecordAmpVideoResponses(pubid, profileid) } } + +func (me *MultiMetricsEngine) RecordHTTPCounter() { + for _, thisME := range *me { + thisME.RecordHTTPCounter() + } +} + +// RecordUnwrapRequestStatus record VAST unwrap status +func (me *MultiMetricsEngine) RecordUnwrapRequestStatus(accountId, bidder, status string) { + for _, thisME := range *me { + thisME.RecordUnwrapRequestStatus(accountId, bidder, status) + } +} + +// RecordUnwrapWrapperCount record wrapper count +func (me *MultiMetricsEngine) RecordUnwrapWrapperCount(accountId, bidder, wrapper_count string) { + for _, thisME := range *me { + thisME.RecordUnwrapWrapperCount(accountId, bidder, wrapper_count) + } +} + +// RecordUnwrapRequestTime record response time +func (me *MultiMetricsEngine) RecordUnwrapRequestTime(accountId, bidder string, respTime time.Duration) { + for _, thisME := range *me { + thisME.RecordUnwrapRequestTime(accountId, bidder, respTime) + } +} + +// RecordUnwrapRespTime record response time for wrapper count wise +func (me *MultiMetricsEngine) RecordUnwrapRespTime(accountId, wraperCnt string, respTime time.Duration) { + for _, thisME := range *me { + thisME.RecordUnwrapRespTime(accountId, wraperCnt, respTime) + } +} diff --git a/modules/pubmatic/openwrap/metrics/config/metrics_test.go b/modules/pubmatic/openwrap/metrics/config/metrics_test.go index 85e2b218db3..0baf16df899 100644 --- a/modules/pubmatic/openwrap/metrics/config/metrics_test.go +++ b/modules/pubmatic/openwrap/metrics/config/metrics_test.go @@ -6,14 +6,14 @@ import ( "time" "github.com/golang/mock/gomock" - cfg "github.com/prebid/prebid-server/config" - metrics_cfg "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" - mock "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/stats" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" + cfg "github.com/prebid/prebid-server/v2/config" + metrics_cfg "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" + mock "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/stats" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/assert" ) @@ -219,6 +219,7 @@ func TestRecordFunctionForMultiMetricsEngine(t *testing.T) { mockEngine.EXPECT().RecordOWServerPanic("endpoint", "methodName", "nodeName", "podName") mockEngine.EXPECT().RecordAmpVideoRequests("pubid", "profileid") mockEngine.EXPECT().RecordAmpVideoResponses("pubid", "profileid") + mockEngine.EXPECT().RecordHTTPCounter() // create the multi-metric engine multiMetricEngine := MultiMetricsEngine{} @@ -283,4 +284,5 @@ func TestRecordFunctionForMultiMetricsEngine(t *testing.T) { multiMetricEngine.RecordOWServerPanic("endpoint", "methodName", "nodeName", "podName") multiMetricEngine.RecordAmpVideoRequests("pubid", "profileid") multiMetricEngine.RecordAmpVideoResponses("pubid", "profileid") + multiMetricEngine.RecordHTTPCounter() } diff --git a/modules/pubmatic/openwrap/metrics/metrics.go b/modules/pubmatic/openwrap/metrics/metrics.go index 8fb591aa643..c46edabce7b 100644 --- a/modules/pubmatic/openwrap/metrics/metrics.go +++ b/modules/pubmatic/openwrap/metrics/metrics.go @@ -25,6 +25,7 @@ type MetricsEngine interface { RecordPublisherRequests(endpoint string, publisher string, platform string) RecordReqImpsWithContentCount(publisher, contentType string) RecordInjectTrackerErrorCount(adformat, publisher, partner string) + RecordHTTPCounter() // not-captured in openwrap module, dont provide enough insights RecordPBSAuctionRequestsStats() @@ -74,4 +75,10 @@ type MetricsEngine interface { RecordOWServerPanic(endpoint, methodName, nodeName, podName string) RecordAmpVideoRequests(pubid, profileid string) RecordAmpVideoResponses(pubid, profileid string) + + // VAST Unwrap metrics + RecordUnwrapRequestStatus(accountId, bidder, status string) + RecordUnwrapWrapperCount(accountId, bidder string, wrapper_count string) + RecordUnwrapRequestTime(accountId, bidder string, respTime time.Duration) + RecordUnwrapRespTime(accountId, wraperCnt string, respTime time.Duration) } diff --git a/modules/pubmatic/openwrap/metrics/mock/mock.go b/modules/pubmatic/openwrap/metrics/mock/mock.go index f16a0674c71..4369f29a400 100644 --- a/modules/pubmatic/openwrap/metrics/mock/mock.go +++ b/modules/pubmatic/openwrap/metrics/mock/mock.go @@ -9,7 +9,7 @@ import ( time "time" gomock "github.com/golang/mock/gomock" - metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" + metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" ) // MockMetricsEngine is a mock of MetricsEngine interface. @@ -706,3 +706,61 @@ func (mr *MockMetricsEngineMockRecorder) Shutdown() *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockMetricsEngine)(nil).Shutdown)) } + +// RecordHTTPCounter mocks base method. +func (m *MockMetricsEngine) RecordHTTPCounter() { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RecordHTTPCounter") +} + +// RecordHTTPCounter indicates an expected call of RecordHTTPCounter. +func (mr *MockMetricsEngineMockRecorder) RecordHTTPCounter() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordHTTPCounter", reflect.TypeOf((*MockMetricsEngine)(nil).RecordHTTPCounter)) +} + +// RecordUnwrapRequestStatus mocks base method +func (m *MockMetricsEngine) RecordUnwrapRequestStatus(arg0, arg1, arg2 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RecordUnwrapRequestStatus", arg0, arg1, arg2) +} + +// RecordUnwrapRequestStatus indicates an expected call of RecordUnwrapRequestStatus +func (mr *MockMetricsEngineMockRecorder) RecordUnwrapRequestStatus(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordUnwrapRequestStatus", reflect.TypeOf((*MockMetricsEngine)(nil).RecordUnwrapRequestStatus), arg0, arg1, arg2) +} + +func (m *MockMetricsEngine) RecordUnwrapWrapperCount(arg0, arg1, arg2 string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RecordUnwrapWrapperCount", arg0, arg1, arg2) +} + +// RecordUnwrapRequestStatus indicates an expected call of RecordUnwrapRequestStatus +func (mr *MockMetricsEngineMockRecorder) RecordUnwrapWrapperCount(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordUnwrapWrapperCount", reflect.TypeOf((*MockMetricsEngine)(nil).RecordUnwrapWrapperCount), arg0, arg1, arg2) +} + +// accountId string, bidder string, respTime time.Duration +func (m *MockMetricsEngine) RecordUnwrapRequestTime(arg0, arg1 string, arg2 time.Duration) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RecordUnwrapRequestTime", arg0, arg1, arg2) +} + +// RecordUnwrapRequestTime indicates an expected call of RecordUnwrapRequestTime +func (mr *MockMetricsEngineMockRecorder) RecordUnwrapRequestTime(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordUnwrapRequestTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordUnwrapRequestTime), arg0, arg1, arg2) +} + +func (m *MockMetricsEngine) RecordUnwrapRespTime(arg0, arg1 string, arg2 time.Duration) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "RecordUnwrapRespTime", arg0, arg1, arg2) +} + +// RecordUnwrapRespTime indicates an expected call of RecordUnwrapRespTime +func (mr *MockMetricsEngineMockRecorder) RecordUnwrapRespTime(arg0, arg1, arg2 interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordUnwrapRespTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordUnwrapRespTime), arg0, arg1, arg2) +} diff --git a/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go b/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go index a24bc25ec16..f4b60bdf7ed 100644 --- a/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go +++ b/modules/pubmatic/openwrap/metrics/prometheus/prometheus.go @@ -5,7 +5,7 @@ import ( "sync" "time" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" "github.com/prometheus/client_golang/prometheus" ) @@ -13,7 +13,8 @@ import ( type Metrics struct { // general metrics - panics *prometheus.CounterVec + panics *prometheus.CounterVec + httpCounter *prometheus.CounterVec // publisher-partner level metrics pubPartnerNoCookie *prometheus.CounterVec @@ -69,6 +70,12 @@ type Metrics struct { owRequestTime *prometheus.HistogramVec ampVideoRequests *prometheus.CounterVec ampVideoResponses *prometheus.CounterVec + + // VAST Unwrap + requests *prometheus.CounterVec + wrapperCount *prometheus.CounterVec + requestTime *prometheus.HistogramVec + unwrapRespTime *prometheus.HistogramVec } const ( @@ -110,6 +117,12 @@ func newMetrics(cfg *config.PrometheusMetrics, promRegistry *prometheus.Registry []string{hostLabel, methodLabel}, ) + metrics.httpCounter = newCounter(cfg, promRegistry, + "total_http_request", + "Count of total http requests", + []string{}, + ) + // publisher-partner level metrics // TODO : check description of this metrics.pubPartnerNoCookie = newCounter(cfg, promRegistry, @@ -250,6 +263,22 @@ func newMetrics(cfg *config.PrometheusMetrics, promRegistry *prometheus.Registry "Count of failures to send the logger to analytics endpoint at publisher and profile level", []string{pubIDLabel, profileIDLabel}, ) + metrics.requests = newCounter(cfg, promRegistry, + "vastunwrap_status", + "Count of vast unwrap requests labeled by status", + []string{pubIdLabel, bidderLabel, statusLabel}) + metrics.wrapperCount = newCounter(cfg, promRegistry, + "vastunwrap_wrapper_count", + "Count of vast unwrap levels labeled by bidder", + []string{pubIdLabel, bidderLabel, wrapperCountLabel}) + metrics.requestTime = newHistogramVec(cfg, promRegistry, + "vastunwrap_request_time", + "Time taken to serve the vast unwrap request in Milliseconds", []string{pubIdLabel, bidderLabel}, + []float64{50, 100, 200, 300, 500}) + metrics.unwrapRespTime = newHistogramVec(cfg, promRegistry, + "vastunwrap_resp_time", + "Time taken to serve the vast unwrap request in Milliseconds at wrapper count level", []string{pubIdLabel, wrapperCountLabel}, + []float64{50, 100, 150, 200}) newSSHBMetrics(&metrics, cfg, promRegistry) @@ -453,6 +482,10 @@ func (m *Metrics) RecordPublisherWrapperLoggerFailure(publisher, profile, versio }).Inc() } +func (m *Metrics) RecordHTTPCounter() { + m.httpCounter.With(nil).Inc() +} + // TODO - really need ? func (m *Metrics) RecordPBSAuctionRequestsStats() {} diff --git a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb.go b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb.go index 75a38220496..a3cf81a3dd1 100644 --- a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb.go +++ b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb.go @@ -3,8 +3,8 @@ package prometheus import ( "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" "github.com/prometheus/client_golang/prometheus" ) @@ -14,6 +14,8 @@ const ( profileLabel = "profileid" dealLabel = "deal" nodeal = "nodeal" + + wrapperCountLabel = "wrapper_count" ) const ( @@ -220,6 +222,40 @@ func (m *Metrics) RecordAmpVideoResponses(pubid, profileid string) { }).Inc() } +// RecordUnwrapRequestStatus record counter with vast unwrap status +func (m *Metrics) RecordUnwrapRequestStatus(accountId, bidder, status string) { + m.requests.With(prometheus.Labels{ + pubIdLabel: accountId, + bidderLabel: bidder, + statusLabel: status, + }).Inc() +} + +// RecordUnwrapWrapperCount record counter of wrapper levels +func (m *Metrics) RecordUnwrapWrapperCount(accountId, bidder, wrapper_count string) { + m.wrapperCount.With(prometheus.Labels{ + pubIdLabel: accountId, + bidderLabel: bidder, + wrapperCountLabel: wrapper_count, + }).Inc() +} + +// RecordUnwrapRequestTime records time takent to complete vast unwrap +func (m *Metrics) RecordUnwrapRequestTime(accountId, bidder string, respTime time.Duration) { + m.requestTime.With(prometheus.Labels{ + pubIdLabel: accountId, + bidderLabel: bidder, + }).Observe(float64(respTime.Milliseconds())) +} + +// RecordUnwrapRespTime records time takent to complete vast unwrap per wrapper count level +func (m *Metrics) RecordUnwrapRespTime(accountId, wraperCnt string, respTime time.Duration) { + m.unwrapRespTime.With(prometheus.Labels{ + pubIdLabel: accountId, + wrapperCountLabel: wraperCnt, + }).Observe(float64(respTime.Milliseconds())) +} + func preloadLabelValues(m *Metrics) { var ( requestStatusValues = requestStatusesAsString() diff --git a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb_test.go b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb_test.go index bf3dcbfaafd..2fd059d2b25 100644 --- a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb_test.go +++ b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_sshb_test.go @@ -5,7 +5,7 @@ import ( "time" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" "github.com/prometheus/client_golang/prometheus" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_test.go b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_test.go index 86fa9062708..4706136b4c1 100644 --- a/modules/pubmatic/openwrap/metrics/prometheus/prometheus_test.go +++ b/modules/pubmatic/openwrap/metrics/prometheus/prometheus_test.go @@ -5,9 +5,9 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" "github.com/prometheus/client_golang/prometheus" dto "github.com/prometheus/client_model/go" "github.com/stretchr/testify/assert" @@ -326,6 +326,16 @@ func TestRecordDBQueryFailure(t *testing.T) { }) } +func TestRecordHTTPCounter(t *testing.T) { + m := createMetricsForTesting() + + m.RecordHTTPCounter() + + expectedCount := float64(1) + assertCounterVecValue(t, "", "httpCounter", m.httpCounter, + expectedCount, nil) +} + func getHistogramFromHistogramVec(histogram *prometheus.HistogramVec, labelKey, labelValue string) dto.Histogram { var result dto.Histogram processMetrics(histogram, func(m dto.Metric) { diff --git a/modules/pubmatic/openwrap/metrics/stats/client_test.go b/modules/pubmatic/openwrap/metrics/stats/client_test.go index cb6dc0a8c79..fa5c89168a8 100644 --- a/modules/pubmatic/openwrap/metrics/stats/client_test.go +++ b/modules/pubmatic/openwrap/metrics/stats/client_test.go @@ -12,7 +12,7 @@ import ( "github.com/alitto/pond" "github.com/golang/mock/gomock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/stats/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/stats/mock" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/metrics/stats/tcp_stats.go b/modules/pubmatic/openwrap/metrics/stats/tcp_stats.go index 7fc14bd3716..e920cf132c1 100644 --- a/modules/pubmatic/openwrap/metrics/stats/tcp_stats.go +++ b/modules/pubmatic/openwrap/metrics/stats/tcp_stats.go @@ -5,8 +5,8 @@ import ( "time" "github.com/golang/glog" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) type StatsTCP struct { @@ -341,3 +341,8 @@ func (st *StatsTCP) RecordRequestTime(requestType string, requestTime time.Durat func (st *StatsTCP) RecordOWServerPanic(endpoint, methodName, nodeName, podName string) {} func (st *StatsTCP) RecordAmpVideoRequests(pubid, profileid string) {} func (st *StatsTCP) RecordAmpVideoResponses(pubid, profileid string) {} +func (st *StatsTCP) RecordHTTPCounter() {} +func (st *StatsTCP) RecordUnwrapRequestStatus(accountId, bidder, status string) {} +func (st *StatsTCP) RecordUnwrapWrapperCount(accountId, bidder, wrapper_count string) {} +func (st *StatsTCP) RecordUnwrapRequestTime(accountId, bidder string, respTime time.Duration) {} +func (st *StatsTCP) RecordUnwrapRespTime(accountId, wraperCnt string, respTime time.Duration) {} diff --git a/modules/pubmatic/openwrap/metrics/stats/tcp_stats_test.go b/modules/pubmatic/openwrap/metrics/stats/tcp_stats_test.go index 596157b7088..70f6a3c1a36 100644 --- a/modules/pubmatic/openwrap/metrics/stats/tcp_stats_test.go +++ b/modules/pubmatic/openwrap/metrics/stats/tcp_stats_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/models/adcom.go b/modules/pubmatic/openwrap/models/adcom.go index 22326cc904d..6fa967783c2 100644 --- a/modules/pubmatic/openwrap/models/adcom.go +++ b/modules/pubmatic/openwrap/models/adcom.go @@ -1,6 +1,6 @@ package models -import "github.com/prebid/openrtb/v19/adcom1" +import "github.com/prebid/openrtb/v20/adcom1" func GetAPIFramework(api []int) []adcom1.APIFramework { if api == nil { diff --git a/modules/pubmatic/openwrap/models/adunitconfig/adunitconfig.go b/modules/pubmatic/openwrap/models/adunitconfig/adunitconfig.go index e4647efeecc..9a6ef9c0590 100644 --- a/modules/pubmatic/openwrap/models/adunitconfig/adunitconfig.go +++ b/modules/pubmatic/openwrap/models/adunitconfig/adunitconfig.go @@ -4,8 +4,8 @@ import ( "encoding/json" "errors" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) var ErrAdUnitUnmarshal = errors.New("unmarshal error adunitconfig") diff --git a/modules/pubmatic/openwrap/models/bidders.go b/modules/pubmatic/openwrap/models/bidders.go index ccb3833e8bb..c2ae4801516 100644 --- a/modules/pubmatic/openwrap/models/bidders.go +++ b/modules/pubmatic/openwrap/models/bidders.go @@ -13,6 +13,10 @@ const ( BidderDistrictmDMXAlias = "districtmDMX" BidderPubMaticSecondaryAlias = "pubmatic2" BidderMediaFuseAlias = "mediafuse" + BidderAdformAdfAlias = "adform" + BidderTrustxAlias = "trustx" + BidderSynacormediaAlias = "synacormedia" + BidderViewDeos = "viewdeos" ) const ( diff --git a/modules/pubmatic/openwrap/models/constants.go b/modules/pubmatic/openwrap/models/constants.go index fcadcbf8d6e..5690a07c2ac 100755 --- a/modules/pubmatic/openwrap/models/constants.go +++ b/modules/pubmatic/openwrap/models/constants.go @@ -1,7 +1,6 @@ package models const ( - DEFAULT_PUB_ID = 34576 // Default PubID to get generic data like regex for browsers etc PARTNER_ID = "partnerId" ADAPTER_ID = "adapterId" PARTNER_ACCOUNT_NAME = "partnerName" @@ -360,6 +359,24 @@ const ( HardFloor = 1 CustomDimensions = "cds" + Enabled = "1" + + // VAST Unwrap + RequestContext = "rctx" + UnwrapCount = "unwrap-count" + UnwrapStatus = "unwrap-status" + Timeout = "Timeout" + UnwrapSucessStatus = "0" + UnwrapTimeout = "unwrap-timeout" + MediaTypeVideo = "video" + ProfileId = "profileID" + VersionId = "versionID" + DisplayId = "DisplayID" + XUserIP = "X-Forwarded-For" + XUserAgent = "X-Device-User-Agent" + CreativeID = "unwrap-ucrid" + PubID = "pub_id" + ImpressionID = "imr_id" ) const ( diff --git a/modules/pubmatic/openwrap/models/nbr/codes.go b/modules/pubmatic/openwrap/models/nbr/codes.go index 2c56686b79b..7b9aa7d233d 100644 --- a/modules/pubmatic/openwrap/models/nbr/codes.go +++ b/modules/pubmatic/openwrap/models/nbr/codes.go @@ -1,6 +1,6 @@ package nbr -import "github.com/prebid/openrtb/v19/openrtb3" +import "github.com/prebid/openrtb/v20/openrtb3" // vendor specific NoBidReasons (500+) const ( diff --git a/modules/pubmatic/openwrap/models/openwrap.go b/modules/pubmatic/openwrap/models/openwrap.go index e8be8c4d191..8f92f25a0c4 100644 --- a/modules/pubmatic/openwrap/models/openwrap.go +++ b/modules/pubmatic/openwrap/models/openwrap.go @@ -4,12 +4,12 @@ import ( "encoding/json" "net/http" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/usersync" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/usersync" ) type RequestCtx struct { @@ -92,13 +92,15 @@ type RequestCtx struct { ReturnAllBidStatus bool // ReturnAllBidStatus stores the value of request.ext.prebid.returnallbidstatus Sshb string //Sshb query param to identify that the request executed heder-bidding or not, sshb=1(executed HB(8001)), sshb=2(reverse proxy set from HB(8001->8000)), sshb=""(direct request(8000)). - DCName string - CachePutMiss int // to be used in case of CTV JSON endpoint/amp/inapp-ott-video endpoint - CurrencyConversion func(from string, to string, value float64) (float64, error) `json:"-"` - MatchedImpression map[string]int - CustomDimensions map[string]CustomDimension - AmpVideoEnabled bool //AmpVideoEnabled indicates whether to include a Video object in an AMP request. - IsTBFFeatureEnabled bool + DCName string + CachePutMiss int // to be used in case of CTV JSON endpoint/amp/inapp-ott-video endpoint + CurrencyConversion func(from string, to string, value float64) (float64, error) `json:"-"` + MatchedImpression map[string]int + CustomDimensions map[string]CustomDimension + AmpVideoEnabled bool //AmpVideoEnabled indicates whether to include a Video object in an AMP request. + IsTBFFeatureEnabled bool + VastUnwrapEnabled bool + VastUnwrapStatsEnabled bool } type OwBid struct { diff --git a/modules/pubmatic/openwrap/models/request.go b/modules/pubmatic/openwrap/models/request.go index ef2c4059c2c..8e85b4c061b 100644 --- a/modules/pubmatic/openwrap/models/request.go +++ b/modules/pubmatic/openwrap/models/request.go @@ -3,7 +3,7 @@ package models import ( "encoding/json" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type ExtRegs struct { diff --git a/modules/pubmatic/openwrap/models/response.go b/modules/pubmatic/openwrap/models/response.go index 83e95409c5d..96e48d7e1aa 100644 --- a/modules/pubmatic/openwrap/models/response.go +++ b/modules/pubmatic/openwrap/models/response.go @@ -3,9 +3,9 @@ package models import ( "encoding/json" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type BidExt struct { diff --git a/modules/pubmatic/openwrap/models/source.go b/modules/pubmatic/openwrap/models/source.go index fbba90277cc..be0ea5154de 100644 --- a/modules/pubmatic/openwrap/models/source.go +++ b/modules/pubmatic/openwrap/models/source.go @@ -1,6 +1,6 @@ package models -import "github.com/prebid/prebid-server/openrtb_ext" +import "github.com/prebid/prebid-server/v2/openrtb_ext" type ExtSource struct { *openrtb_ext.ExtSource diff --git a/modules/pubmatic/openwrap/models/tracking.go b/modules/pubmatic/openwrap/models/tracking.go index f3dc93a271f..b1e362d5f3d 100644 --- a/modules/pubmatic/openwrap/models/tracking.go +++ b/modules/pubmatic/openwrap/models/tracking.go @@ -1,6 +1,6 @@ package models -import "github.com/prebid/prebid-server/openrtb_ext" +import "github.com/prebid/prebid-server/v2/openrtb_ext" // impression tracker url parameters const ( diff --git a/modules/pubmatic/openwrap/models/utils.go b/modules/pubmatic/openwrap/models/utils.go index 8281827b8f6..42589e38859 100644 --- a/modules/pubmatic/openwrap/models/utils.go +++ b/modules/pubmatic/openwrap/models/utils.go @@ -13,10 +13,10 @@ import ( "github.com/buger/jsonparser" "github.com/pkg/errors" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) var videoRegex *regexp.Regexp diff --git a/modules/pubmatic/openwrap/models/utils_legacy_test.go b/modules/pubmatic/openwrap/models/utils_legacy_test.go index f9bb96eb8bb..393f19b133d 100644 --- a/modules/pubmatic/openwrap/models/utils_legacy_test.go +++ b/modules/pubmatic/openwrap/models/utils_legacy_test.go @@ -3,7 +3,7 @@ package models import ( "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/models/utils_test.go b/modules/pubmatic/openwrap/models/utils_test.go index 77ba2bb2f3b..df11e12dd7c 100644 --- a/modules/pubmatic/openwrap/models/utils_test.go +++ b/modules/pubmatic/openwrap/models/utils_test.go @@ -4,9 +4,9 @@ import ( "fmt" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/module.go b/modules/pubmatic/openwrap/module.go index 4c2cfcbd9e4..db35eaff56a 100644 --- a/modules/pubmatic/openwrap/module.go +++ b/modules/pubmatic/openwrap/module.go @@ -6,8 +6,8 @@ import ( "runtime/debug" "github.com/golang/glog" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/moduledeps" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/modules/moduledeps" ) // init openwrap module and its dependecies like config, cache, db connection, bidder cfg, etc. @@ -91,3 +91,13 @@ func (m OpenWrap) HandleAuctionResponseHook( return m.handleAuctionResponseHook(ctx, miCtx, payload) } + +// HandleRawBidderResponseHook fetches rCtx and check for vast unwrapper flag to enable/disable vast unwrapping feature +func (m OpenWrap) HandleRawBidderResponseHook( + _ context.Context, + miCtx hookstage.ModuleInvocationContext, + payload hookstage.RawBidderResponsePayload, +) (hookstage.HookResult[hookstage.RawBidderResponsePayload], error) { + + return m.handleRawBidderResponseHook(miCtx, payload) +} diff --git a/modules/pubmatic/openwrap/nonbids.go b/modules/pubmatic/openwrap/nonbids.go index 6126adf2f74..b63a912f2f9 100644 --- a/modules/pubmatic/openwrap/nonbids.go +++ b/modules/pubmatic/openwrap/nonbids.go @@ -1,9 +1,9 @@ package openwrap import ( - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // prepareSeatNonBids forms the rctx.SeatNonBids map from rctx values diff --git a/modules/pubmatic/openwrap/nonbids_test.go b/modules/pubmatic/openwrap/nonbids_test.go index 75796064b3f..ac7573a0ef9 100644 --- a/modules/pubmatic/openwrap/nonbids_test.go +++ b/modules/pubmatic/openwrap/nonbids_test.go @@ -3,9 +3,9 @@ package openwrap import ( "testing" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/openwrap.go b/modules/pubmatic/openwrap/openwrap.go index 115ef00d99c..9b6702a5c2c 100644 --- a/modules/pubmatic/openwrap/openwrap.go +++ b/modules/pubmatic/openwrap/openwrap.go @@ -10,19 +10,21 @@ import ( "sync" + vastunwrap "git.pubmatic.com/vastunwrap" "github.com/golang/glog" gocache "github.com/patrickmn/go-cache" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/modules/moduledeps" - ow_adapters "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adapters" - cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - ow_gocache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/gocache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/database/mysql" - metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" - metrics_cfg "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/config" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/publisherfeature" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/modules/moduledeps" + ow_adapters "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adapters" + cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + ow_gocache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/gocache" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database/mysql" + metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" + metrics_cfg "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/config" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/publisherfeature" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/unwrap" ) const ( @@ -35,6 +37,7 @@ type OpenWrap struct { metricEngine metrics.MetricsEngine currencyConversion currency.Conversions featureConfig publisherfeature.Feature + unwrap unwrap.Unwrap } var ow *OpenWrap @@ -78,6 +81,11 @@ func initOpenWrap(rawCfg json.RawMessage, moduleDeps moduledeps.ModuleDeps) (Ope featureConfig := publisherfeature.New(owCache, cfg.Cache.CacheDefaultExpiry) featureConfig.Start() + // Init VAST Unwrap + vastunwrap.InitUnWrapperConfig(cfg.VastUnwrapCfg) + uw := unwrap.NewUnwrap(fmt.Sprintf("http://%s:%d/unwrap", cfg.VastUnwrapCfg.APPConfig.Host, cfg.VastUnwrapCfg.APPConfig.Port), + cfg.VastUnwrapCfg.APPConfig.UnwrapDefaultTimeout, nil, &metricEngine) + once.Do(func() { ow = &OpenWrap{ cfg: cfg, @@ -85,6 +93,7 @@ func initOpenWrap(rawCfg json.RawMessage, moduleDeps moduledeps.ModuleDeps) (Ope metricEngine: &metricEngine, currencyConversion: moduleDeps.CurrencyConversion, featureConfig: featureConfig, + unwrap: uw, } }) return *ow, nil diff --git a/modules/pubmatic/openwrap/openwrap_sshb.go b/modules/pubmatic/openwrap/openwrap_sshb.go index 5d9e8827b16..b3f26fb581e 100644 --- a/modules/pubmatic/openwrap/openwrap_sshb.go +++ b/modules/pubmatic/openwrap/openwrap_sshb.go @@ -1,19 +1,13 @@ package openwrap import ( - "strconv" + "context" - "github.com/prebid/openrtb/v19/openrtb2" - cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/publisherfeature" - vastmodels "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/models" -) - -const ( - VastUnwrapperEnableValue = "1" + cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/publisherfeature" ) // GetConfig Temporary function to expose config to SSHB @@ -52,27 +46,8 @@ func (ow *OpenWrap) GetFeature() publisherfeature.Feature { return ow.featureConfig } -// GetVastUnwrapEnabled return whether to enable vastunwrap or not -func GetVastUnwrapEnabled(rctx vastmodels.RequestCtx, VASTUnwrapTraffic int) bool { - rCtx := models.RequestCtx{ - Endpoint: rctx.Endpoint, - PubID: rctx.PubID, - ProfileID: rctx.ProfileID, - DisplayID: rctx.DisplayID, - } - partnerConfigMap, err := ow.getProfileData(rCtx, openrtb2.BidRequest{}) - if err != nil || len(partnerConfigMap) == 0 { - return false - } - rCtx.PartnerConfigMap = partnerConfigMap - trafficPercentage := VASTUnwrapTraffic - unwrapEnabled := models.GetVersionLevelPropertyFromPartnerConfig(rCtx.PartnerConfigMap, models.VastUnwrapperEnableKey) == VastUnwrapperEnableValue - if unwrapEnabled { - if value := models.GetVersionLevelPropertyFromPartnerConfig(rCtx.PartnerConfigMap, models.VastUnwrapTrafficPercentKey); len(value) > 0 { - if trafficPercentDB, err := strconv.Atoi(value); err == nil { - trafficPercentage = trafficPercentDB - } - } - } - return unwrapEnabled && GetRandomNumberIn1To100() <= trafficPercentage +// getVastUnwrapperEnable checks for Vast unwrp is enabled in given context +func getVastUnwrapperEnable(ctx context.Context, field string) bool { + vastEnableUnwrapper, _ := ctx.Value(field).(string) + return vastEnableUnwrapper == models.Enabled } diff --git a/modules/pubmatic/openwrap/openwrap_sshb_test.go b/modules/pubmatic/openwrap/openwrap_sshb_test.go index f1930099917..2a0f58bb1dd 100644 --- a/modules/pubmatic/openwrap/openwrap_sshb_test.go +++ b/modules/pubmatic/openwrap/openwrap_sshb_test.go @@ -1,147 +1,51 @@ package openwrap import ( - "errors" + "context" "testing" - "github.com/golang/mock/gomock" - "github.com/magiconair/properties/assert" - mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - vastmodels "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/models" + "github.com/stretchr/testify/assert" ) -func TestGetVastUnwrapEnabled(t *testing.T) { +func TestGetVastUnwrapperEnable(t *testing.T) { type args struct { - rctx vastmodels.RequestCtx - vastunwraptraffic int + ctx context.Context + field string } - - ctrl := gomock.NewController(t) - defer ctrl.Finish() - mockCache := mock_cache.NewMockCache(ctrl) - tests := []struct { - name string - args args - setup func() - randomNumber int - want bool + name string + args args + want bool }{ { - name: "vastunwrap is enabled and trafficpercent is greater than random number", - args: args{rctx: vastmodels.RequestCtx{ - PubID: 5890, - ProfileID: 123, - DisplayID: 1, - }, - vastunwraptraffic: 10, - }, - setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ - -1: { - models.VastUnwrapperEnableKey: "1", - models.VastUnwrapTrafficPercentKey: "90", - }, - }, nil) - }, - randomNumber: 80, - want: true, - }, - { - name: "vastunwrap is enabled and trafficpercent is less than random number", - args: args{rctx: vastmodels.RequestCtx{ - PubID: 5890, - ProfileID: 123, - DisplayID: 1, - }, - vastunwraptraffic: 0, - }, - setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ - -1: { - models.VastUnwrapperEnableKey: "1", - models.VastUnwrapTrafficPercentKey: "90", - }, - }, nil) + name: "given field present in context", + args: args{ + ctx: context.WithValue(context.Background(), "abc", "1"), + field: "abc", }, - randomNumber: 91, - want: false, + want: true, }, { - name: "vastunwrap is dissabled and trafficpercent is less than random number", - args: args{rctx: vastmodels.RequestCtx{ - PubID: 5890, - ProfileID: 123, - DisplayID: 1, - }}, - setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ - -1: { - models.VastUnwrapperEnableKey: "0", - }, - }, nil) + name: "given field is not present in context", + args: args{ + ctx: context.WithValue(context.Background(), "abc", "1"), + field: "xyz", }, - randomNumber: 91, - want: false, + want: false, }, { - name: "partnerconfigmap not found", - args: args{rctx: vastmodels.RequestCtx{ - PubID: 5890, - ProfileID: 123, - DisplayID: 1, - }}, - setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, nil) - }, - randomNumber: 91, - want: false, - }, - { - name: "error while fetching partnerconfigmap ", - args: args{rctx: vastmodels.RequestCtx{ - PubID: 5890, - ProfileID: 123, - DisplayID: 1, - }}, - setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, errors.New("some error")) - }, - randomNumber: 91, - want: false, - }, - { - name: "vastunwrap is enabled and trafficpercent not present in DB ", - args: args{rctx: vastmodels.RequestCtx{ - PubID: 5890, - ProfileID: 123, - DisplayID: 1, - }, - vastunwraptraffic: 10, - }, - setup: func() { - mockCache.EXPECT().GetPartnerConfigMap(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(map[int]map[string]string{ - -1: { - models.VastUnwrapperEnableKey: "1", - }, - }, nil) + name: "No field is not present in context", + args: args{ + ctx: context.Background(), + field: "xyz", }, - randomNumber: 9, - want: true, + want: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - tt.setup() - GetRandomNumberIn1To100 = func() int { - return tt.randomNumber - } - ow = &OpenWrap{ - cache: mockCache, - } - got := GetVastUnwrapEnabled(tt.args.rctx, tt.args.vastunwraptraffic) - assert.Equal(t, got, tt.want) + got := getVastUnwrapperEnable(tt.args.ctx, tt.args.field) + assert.Equal(t, tt.want, got, tt.name) }) } } diff --git a/modules/pubmatic/openwrap/price_granularity.go b/modules/pubmatic/openwrap/price_granularity.go index 1f854c29e2a..5cc74653d19 100644 --- a/modules/pubmatic/openwrap/price_granularity.go +++ b/modules/pubmatic/openwrap/price_granularity.go @@ -1,9 +1,9 @@ package openwrap import ( - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) func computePriceGranularity(rctx models.RequestCtx) (openrtb_ext.PriceGranularity, error) { diff --git a/modules/pubmatic/openwrap/price_granularity_test.go b/modules/pubmatic/openwrap/price_granularity_test.go index 9ea2156b9f4..37a2006be31 100644 --- a/modules/pubmatic/openwrap/price_granularity_test.go +++ b/modules/pubmatic/openwrap/price_granularity_test.go @@ -4,9 +4,9 @@ import ( "fmt" "testing" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/processedauctionhook.go b/modules/pubmatic/openwrap/processedauctionhook.go index e7d38908460..80f79d9ab5a 100644 --- a/modules/pubmatic/openwrap/processedauctionhook.go +++ b/modules/pubmatic/openwrap/processedauctionhook.go @@ -3,8 +3,8 @@ package openwrap import ( "context" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) func (m OpenWrap) HandleProcessedAuctionHook( @@ -33,8 +33,8 @@ func (m OpenWrap) HandleProcessedAuctionHook( ip := rctx.IP result.ChangeSet.AddMutation(func(parp hookstage.ProcessedAuctionRequestPayload) (hookstage.ProcessedAuctionRequestPayload, error) { - if parp.RequestWrapper != nil && parp.RequestWrapper.BidRequest.Device != nil && (parp.RequestWrapper.BidRequest.Device.IP == "" && parp.RequestWrapper.BidRequest.Device.IPv6 == "") { - parp.RequestWrapper.BidRequest.Device.IP = ip + if parp.Request != nil && parp.Request.BidRequest.Device != nil && (parp.Request.BidRequest.Device.IP == "" && parp.Request.BidRequest.Device.IPv6 == "") { + parp.Request.BidRequest.Device.IP = ip } return parp, nil }, hookstage.MutationUpdate, "update-device-ip") diff --git a/modules/pubmatic/openwrap/processedauctionhook_test.go b/modules/pubmatic/openwrap/processedauctionhook_test.go index d998f69dec2..f597ef65c15 100644 --- a/modules/pubmatic/openwrap/processedauctionhook_test.go +++ b/modules/pubmatic/openwrap/processedauctionhook_test.go @@ -5,12 +5,12 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/hooks/hookanalytics" - "github.com/prebid/prebid-server/hooks/hookstage" - mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/hooks/hookanalytics" + "github.com/prebid/prebid-server/v2/hooks/hookstage" + mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -117,7 +117,7 @@ func TestOpenWrap_HandleProcessedAuctionHook(t *testing.T) { }, }, payload: hookstage.ProcessedAuctionRequestPayload{ - RequestWrapper: &openrtb_ext.RequestWrapper{ + Request: &openrtb_ext.RequestWrapper{ BidRequest: &openrtb2.BidRequest{ Device: &openrtb2.Device{ IP: "", @@ -154,7 +154,7 @@ func TestOpenWrap_HandleProcessedAuctionHook(t *testing.T) { for _, mut := range mutations { result, err := mut.Apply(tt.args.payload) assert.Nil(t, err, tt.name) - assert.Equal(t, tt.wantBidRequest, result.RequestWrapper.BidRequest, tt.name) + assert.Equal(t, tt.wantBidRequest, result.Request.BidRequest, tt.name) } } assert.Equal(t, tt.want.DebugMessages, got.DebugMessages, "Debug messages should be equal") diff --git a/modules/pubmatic/openwrap/profiledata.go b/modules/pubmatic/openwrap/profiledata.go index 4bb2c56537a..5ebc8c76ed5 100644 --- a/modules/pubmatic/openwrap/profiledata.go +++ b/modules/pubmatic/openwrap/profiledata.go @@ -3,9 +3,9 @@ package openwrap import ( "strconv" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func (m OpenWrap) getProfileData(rCtx models.RequestCtx, bidRequest openrtb2.BidRequest) (map[int]map[string]string, error) { diff --git a/modules/pubmatic/openwrap/profiledata_test.go b/modules/pubmatic/openwrap/profiledata_test.go index fe863d26d17..dace8c0d505 100644 --- a/modules/pubmatic/openwrap/profiledata_test.go +++ b/modules/pubmatic/openwrap/profiledata_test.go @@ -5,13 +5,13 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" - metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" + metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/publisherfeature/ampmultiformat.go b/modules/pubmatic/openwrap/publisherfeature/ampmultiformat.go index 72d0a7af728..44662f0771b 100644 --- a/modules/pubmatic/openwrap/publisherfeature/ampmultiformat.go +++ b/modules/pubmatic/openwrap/publisherfeature/ampmultiformat.go @@ -1,7 +1,7 @@ package publisherfeature import ( - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) type ampMultiformat struct { diff --git a/modules/pubmatic/openwrap/publisherfeature/ampmultiformat_test.go b/modules/pubmatic/openwrap/publisherfeature/ampmultiformat_test.go index f9cd45fce92..4a8044e434c 100644 --- a/modules/pubmatic/openwrap/publisherfeature/ampmultiformat_test.go +++ b/modules/pubmatic/openwrap/publisherfeature/ampmultiformat_test.go @@ -3,8 +3,8 @@ package publisherfeature import ( "testing" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/publisherfeature/fullscreenclickability.go b/modules/pubmatic/openwrap/publisherfeature/fullscreenclickability.go index dd28c23f649..d4b868bbcd4 100644 --- a/modules/pubmatic/openwrap/publisherfeature/fullscreenclickability.go +++ b/modules/pubmatic/openwrap/publisherfeature/fullscreenclickability.go @@ -3,7 +3,7 @@ package publisherfeature import ( "math/rand" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) type fsc struct { diff --git a/modules/pubmatic/openwrap/publisherfeature/fullscreenclickability_test.go b/modules/pubmatic/openwrap/publisherfeature/fullscreenclickability_test.go index 1628ab4f54f..fbd9cf7388e 100644 --- a/modules/pubmatic/openwrap/publisherfeature/fullscreenclickability_test.go +++ b/modules/pubmatic/openwrap/publisherfeature/fullscreenclickability_test.go @@ -5,8 +5,8 @@ import ( "testing" "github.com/golang/mock/gomock" - mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/publisherfeature/reloader.go b/modules/pubmatic/openwrap/publisherfeature/reloader.go index 9f134ea9043..34e384ca999 100644 --- a/modules/pubmatic/openwrap/publisherfeature/reloader.go +++ b/modules/pubmatic/openwrap/publisherfeature/reloader.go @@ -5,8 +5,8 @@ import ( "time" "github.com/golang/glog" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) type feature struct { diff --git a/modules/pubmatic/openwrap/publisherfeature/reloader_test.go b/modules/pubmatic/openwrap/publisherfeature/reloader_test.go index 8bb1028dd75..f733c15e8d3 100644 --- a/modules/pubmatic/openwrap/publisherfeature/reloader_test.go +++ b/modules/pubmatic/openwrap/publisherfeature/reloader_test.go @@ -6,9 +6,9 @@ import ( "time" "github.com/golang/mock/gomock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache" - mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache" + mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/publisherfeature/tbf.go b/modules/pubmatic/openwrap/publisherfeature/tbf.go index 9ba4514fb94..30cd667f29d 100644 --- a/modules/pubmatic/openwrap/publisherfeature/tbf.go +++ b/modules/pubmatic/openwrap/publisherfeature/tbf.go @@ -9,7 +9,7 @@ import ( "sync" "github.com/golang/glog" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) // tbf structure holds the configuration of Tracking-Beacon-First feature diff --git a/modules/pubmatic/openwrap/publisherfeature/tbf_test.go b/modules/pubmatic/openwrap/publisherfeature/tbf_test.go index 79474e26245..6ea8550d527 100644 --- a/modules/pubmatic/openwrap/publisherfeature/tbf_test.go +++ b/modules/pubmatic/openwrap/publisherfeature/tbf_test.go @@ -4,8 +4,8 @@ import ( "testing" "github.com/golang/mock/gomock" - mock_cache "github.com/prebid/prebid-server/modules/pubmatic/openwrap/cache/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + mock_cache "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/cache/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/schain.go b/modules/pubmatic/openwrap/schain.go index b321235e9f6..bdb92ac524d 100644 --- a/modules/pubmatic/openwrap/schain.go +++ b/modules/pubmatic/openwrap/schain.go @@ -2,8 +2,8 @@ package openwrap import ( "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) func getSChainObj(partnerConfigMap map[int]map[string]string) []byte { diff --git a/modules/pubmatic/openwrap/targeting.go b/modules/pubmatic/openwrap/targeting.go index cc1216ac7e5..757827990c6 100644 --- a/modules/pubmatic/openwrap/targeting.go +++ b/modules/pubmatic/openwrap/targeting.go @@ -4,10 +4,10 @@ import ( "fmt" "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/utils" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/utils" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // whitelist of prebid targeting keys diff --git a/modules/pubmatic/openwrap/tracker/banner.go b/modules/pubmatic/openwrap/tracker/banner.go index f3abe36faec..3ac1ba0262e 100644 --- a/modules/pubmatic/openwrap/tracker/banner.go +++ b/modules/pubmatic/openwrap/tracker/banner.go @@ -3,10 +3,10 @@ package tracker import ( "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func injectBannerTracker(rctx models.RequestCtx, tracker models.OWTracker, bid openrtb2.Bid, seat string, pixels []adunitconfig.UniversalPixel) string { diff --git a/modules/pubmatic/openwrap/tracker/banner_test.go b/modules/pubmatic/openwrap/tracker/banner_test.go index c5eb36b3fcd..345b1c29a22 100644 --- a/modules/pubmatic/openwrap/tracker/banner_test.go +++ b/modules/pubmatic/openwrap/tracker/banner_test.go @@ -3,9 +3,9 @@ package tracker import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/tracker/create.go b/modules/pubmatic/openwrap/tracker/create.go index 7d79ecb52f9..eec5621d06d 100644 --- a/modules/pubmatic/openwrap/tracker/create.go +++ b/modules/pubmatic/openwrap/tracker/create.go @@ -6,10 +6,10 @@ import ( "net/url" "strconv" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/customdimensions" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/utils" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/customdimensions" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/utils" ) // pubmatic's KGP details per impression diff --git a/modules/pubmatic/openwrap/tracker/create_test.go b/modules/pubmatic/openwrap/tracker/create_test.go index d47edc3f349..fbb2c653972 100644 --- a/modules/pubmatic/openwrap/tracker/create_test.go +++ b/modules/pubmatic/openwrap/tracker/create_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/tracker/inject.go b/modules/pubmatic/openwrap/tracker/inject.go index c84bf160c95..391cdd113a3 100644 --- a/modules/pubmatic/openwrap/tracker/inject.go +++ b/modules/pubmatic/openwrap/tracker/inject.go @@ -7,9 +7,9 @@ import ( "golang.org/x/exp/slices" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) func InjectTrackers(rctx models.RequestCtx, bidResponse *openrtb2.BidResponse) (*openrtb2.BidResponse, error) { diff --git a/modules/pubmatic/openwrap/tracker/inject_test.go b/modules/pubmatic/openwrap/tracker/inject_test.go index 6a19e9b27d2..675c720655a 100644 --- a/modules/pubmatic/openwrap/tracker/inject_test.go +++ b/modules/pubmatic/openwrap/tracker/inject_test.go @@ -6,10 +6,10 @@ import ( "github.com/golang/mock/gomock" "github.com/magiconair/properties/assert" - "github.com/prebid/openrtb/v19/openrtb2" - mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/openrtb/v20/openrtb2" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" ) func TestInjectTrackers(t *testing.T) { diff --git a/modules/pubmatic/openwrap/tracker/native.go b/modules/pubmatic/openwrap/tracker/native.go index 693917af68d..1999699e24c 100644 --- a/modules/pubmatic/openwrap/tracker/native.go +++ b/modules/pubmatic/openwrap/tracker/native.go @@ -6,8 +6,8 @@ import ( "strings" "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) // Inject TrackerCall in Native Adm diff --git a/modules/pubmatic/openwrap/tracker/native_test.go b/modules/pubmatic/openwrap/tracker/native_test.go index 9a1e987e78f..078ef4ec3a1 100644 --- a/modules/pubmatic/openwrap/tracker/native_test.go +++ b/modules/pubmatic/openwrap/tracker/native_test.go @@ -3,9 +3,9 @@ package tracker import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) func Test_injectNativeCreativeTrackers(t *testing.T) { diff --git a/modules/pubmatic/openwrap/tracker/tracker.go b/modules/pubmatic/openwrap/tracker/tracker.go index 1ea29ffe0c1..db1ddbf9e44 100644 --- a/modules/pubmatic/openwrap/tracker/tracker.go +++ b/modules/pubmatic/openwrap/tracker/tracker.go @@ -4,8 +4,8 @@ import ( "fmt" "net/url" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) func GetTrackerInfo(rCtx models.RequestCtx, responseExt openrtb_ext.ExtBidResponse) string { diff --git a/modules/pubmatic/openwrap/tracker/tracker_test.go b/modules/pubmatic/openwrap/tracker/tracker_test.go index e6e78fe0a32..c20154037a7 100644 --- a/modules/pubmatic/openwrap/tracker/tracker_test.go +++ b/modules/pubmatic/openwrap/tracker/tracker_test.go @@ -6,9 +6,9 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/openwrap/tracker/video.go b/modules/pubmatic/openwrap/tracker/video.go index 9e1ef8d5202..72a0150bdc4 100644 --- a/modules/pubmatic/openwrap/tracker/video.go +++ b/modules/pubmatic/openwrap/tracker/video.go @@ -6,8 +6,8 @@ import ( "strings" "github.com/beevik/etree" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) // Inject Trackers in Video Creative diff --git a/modules/pubmatic/openwrap/tracker/video_test.go b/modules/pubmatic/openwrap/tracker/video_test.go index 0ab73b68071..b69b4776010 100644 --- a/modules/pubmatic/openwrap/tracker/video_test.go +++ b/modules/pubmatic/openwrap/tracker/video_test.go @@ -4,8 +4,8 @@ import ( "testing" "github.com/beevik/etree" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" "github.com/stretchr/testify/assert" ) diff --git a/modules/pubmatic/vastunwrap/respwriter.go b/modules/pubmatic/openwrap/unwrap/respwriter.go similarity index 99% rename from modules/pubmatic/vastunwrap/respwriter.go rename to modules/pubmatic/openwrap/unwrap/respwriter.go index 1898fe73920..7e09c8152d1 100644 --- a/modules/pubmatic/vastunwrap/respwriter.go +++ b/modules/pubmatic/openwrap/unwrap/respwriter.go @@ -1,4 +1,4 @@ -package vastunwrap +package unwrap import ( "bytes" diff --git a/modules/pubmatic/openwrap/unwrap/unwrap.go b/modules/pubmatic/openwrap/unwrap/unwrap.go new file mode 100644 index 00000000000..7020d5230d6 --- /dev/null +++ b/modules/pubmatic/openwrap/unwrap/unwrap.go @@ -0,0 +1,88 @@ +package unwrap + +import ( + "net/http" + "runtime/debug" + "strconv" + "strings" + "time" + + vastunwrap "git.pubmatic.com/vastunwrap" + "github.com/golang/glog" + "github.com/prebid/prebid-server/v2/adapters" + metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" +) + +type Unwrap struct { + endpoint string + defaultTime int + metricEngine metrics.MetricsEngine + unwrapRequest http.HandlerFunc +} + +type VastUnwrapService interface { + Unwrap(accountID string, bidder string, bid *adapters.TypedBid, userAgent string, ip string, isStatsEnabled bool) +} + +func NewUnwrap(Endpoint string, DefaultTime int, handler http.HandlerFunc, MetricEngine metrics.MetricsEngine) Unwrap { + uw := Unwrap{ + endpoint: Endpoint, + defaultTime: DefaultTime, + unwrapRequest: vastunwrap.UnwrapRequest, + metricEngine: MetricEngine, + } + + if handler != nil { + uw.unwrapRequest = handler + } + return uw + +} + +func (uw Unwrap) Unwrap(accountID, bidder string, bid *adapters.TypedBid, userAgent, ip string, isStatsEnabled bool) { + startTime := time.Now() + var wrapperCnt int64 + var respStatus string + if bid == nil || bid.Bid == nil || bid.Bid.AdM == "" { + return + } + defer func() { + if r := recover(); r != nil { + glog.Errorf("AdM:[%s] Error:[%v] stacktrace:[%s]", bid.Bid.AdM, r, string(debug.Stack())) + } + respTime := time.Since(startTime) + uw.metricEngine.RecordUnwrapRequestTime(accountID, bidder, respTime) + uw.metricEngine.RecordUnwrapRequestStatus(accountID, bidder, respStatus) + if respStatus == "0" { + uw.metricEngine.RecordUnwrapWrapperCount(accountID, bidder, strconv.Itoa(int(wrapperCnt))) + uw.metricEngine.RecordUnwrapRespTime(accountID, strconv.Itoa(int(wrapperCnt)), respTime) + } + }() + + unwrapURL := uw.endpoint + "?" + models.PubID + "=" + accountID + "&" + models.ImpressionID + "=" + bid.Bid.ImpID + httpReq, err := http.NewRequest(http.MethodPost, unwrapURL, strings.NewReader(bid.Bid.AdM)) + if err != nil { + return + } + headers := http.Header{} + headers.Add(models.ContentType, "application/xml; charset=utf-8") + headers.Add(models.UserAgent, userAgent) + headers.Add(models.XUserAgent, userAgent) + headers.Add(models.XUserIP, ip) + headers.Add(models.CreativeID, bid.Bid.ID) + headers.Add(models.UnwrapTimeout, strconv.Itoa(uw.defaultTime)) + + httpReq.Header = headers + httpResp := NewCustomRecorder() + uw.unwrapRequest(httpResp, httpReq) + respStatus = httpResp.Header().Get(models.UnwrapStatus) + wrapperCnt, _ = strconv.ParseInt(httpResp.Header().Get(models.UnwrapCount), 10, 0) + if !isStatsEnabled && httpResp.Code == http.StatusOK && respStatus == models.UnwrapSucessStatus { + respBody := httpResp.Body.Bytes() + bid.Bid.AdM = string(respBody) + } + + glog.V(3).Infof("[VAST_UNWRAPPER] pubid:[%v] bidder:[%v] impid:[%v] bidid:[%v] status_code:[%v] wrapper_cnt:[%v] httpRespCode= [%v] statsEnabled:[%v]", + accountID, bidder, bid.Bid.ImpID, bid.Bid.ID, respStatus, wrapperCnt, httpResp.Code, isStatsEnabled) +} diff --git a/modules/pubmatic/vastunwrap/module_test.go b/modules/pubmatic/openwrap/unwrap/unwrap_test.go similarity index 59% rename from modules/pubmatic/vastunwrap/module_test.go rename to modules/pubmatic/openwrap/unwrap/unwrap_test.go index 7c1b5719ba0..a7f3e7be050 100644 --- a/modules/pubmatic/vastunwrap/module_test.go +++ b/modules/pubmatic/openwrap/unwrap/unwrap_test.go @@ -1,26 +1,15 @@ -package vastunwrap +package unwrap import ( - "context" - "encoding/json" - "fmt" "net/http" - "reflect" + "strings" "testing" - unWrapCfg "git.pubmatic.com/vastunwrap/config" - "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/hooks/hookstage" - metrics_cfg "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/modules/moduledeps" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap" - "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/models" - mock_stats "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/stats/mock" - "github.com/prometheus/client_golang/prometheus" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" "github.com/stretchr/testify/assert" ) @@ -28,302 +17,126 @@ var vastXMLAdM = "PubMaticAcudeo CompatibleVAST 2.0 Instream Test 1VAST 2.0 Instream Test 1https://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&er=[ERRORCODE]https://track.dsptracker.com?p=1234&er=[ERRORCODE]https://aktrack.pubmatic.com/AdServer/AdDisplayTrackerServlet?operId=1&pubId=64195&siteId=47105&adId=1405154&adType=13&adServerId=243&kefact=1.000000&kaxefact=1.000000&kadNetFrequecy=0&kadwidth=0&kadheight=0&kadsizeid=97&kltstamp=1536933242&indirectAdId=0&adServerOptimizerId=2&ranreq=0.05969169352174375&kpbmtpfact=11.000000&dcId=1&tldId=0&passback=0&svr=ktk57&ekefact=er2bW2sDAwCra06ACbsIQySn5nqBtYsTl8fy5lupAexh37D_&ekaxefact=er2bW4EDAwB_LQpJJ23Fq0DcNC-NSAFXdpSQC8XBk_S33_Fa&ekpbmtpfact=er2bW5MDAwDJHdBnLBt5IrRuh7x0oqp_tjIALv_VvSQDAl6R&crID=m:1_x:3_y:3_p:11_va:3&lpu=ae.com&ucrid=678722001014421372&campaignId=16774&creativeId=0&pctr=0.000000&wDSPByrId=511&wDspId=27&wbId=0&wrId=0&wAdvID=3170&isRTB=1&rtbId=EBCA079F-8D7C-45B8-B733-92951F670AA1&imprId=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&oid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&pageURL=http%253A%252F%252Fowsdk-stagingams.pubmatic.com%253A8443%252Fvast-validator%252F%2523&sec=1&pmc=1https://DspImpressionTracker.com/https://mytracking.com/linear/closehttps://mytracking.com/linear/skiphttps://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=1https://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=2https://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=3https://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=4https://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=5https://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=600:00:04https://www.automationtester.inhttps://aktrack.pubmatic.com/track?operId=7&p=64195&s=47105&a=1405154&wa=243&ts=1536933242&wc=16774&crId=m:1_x:3_y:3_p:11_va:3&ucrid=678722001014421372&impid=24D9FEDA-C97D-4DF7-B747-BD3CFF5AC7B5&advertiser_id=3170&ecpm=1.000000&e=99https://stagingams.pubmatic.com:8443/openwrap/media/pubmatic.mp4https://stagingams.pubmatic.com:8443/openwrap/media/pubmatic.mp4https://stagingams.pubmatic.com:8443/openwrap/media/mp4-sample-3.mp4" -func TestVastUnwrapModuleHandleEntrypointHook(t *testing.T) { - type fields struct { - cfg VastUnwrapModule - } - type args struct { - ctx context.Context - miCtx hookstage.ModuleInvocationContext - payload hookstage.EntrypointPayload - } - tests := []struct { - name string - fields fields - args args - want hookstage.HookResult[hookstage.EntrypointPayload] - wantErr bool - }{ - { - name: "Vast unwrap is enabled in the config", - fields: fields{cfg: VastUnwrapModule{Enabled: true, Cfg: unWrapCfg.VastUnWrapCfg{ - HTTPConfig: unWrapCfg.HttpConfig{MaxIdleConns: 100, MaxIdleConnsPerHost: 1, IdleConnTimeout: 300}, - APPConfig: unWrapCfg.AppConfig{Host: "", Port: 0, UnwrapDefaultTimeout: 100, Debug: 1}, - StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, - ServerConfig: unWrapCfg.ServerConfig{ServerName: "", DCName: "OW_DC"}, - }, - getVastUnwrapEnabled: func(rctx models.RequestCtx, vastunwraptraffic int) bool { - return true - }, - }}, - args: args{ - miCtx: hookstage.ModuleInvocationContext{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: true, Redirect: true}}}, - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - ctx := context.WithValue(context.Background(), VastUnwrapEnabled, "1") - r, _ := http.NewRequestWithContext(ctx, "POST", "http://localhost/video/openrtb?sshb=1", nil) - return r - }(), - }}, - want: hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: true, Redirect: true}}}, - }, - { - name: "Vast unwrap is disabled in the config", - fields: fields{ - cfg: VastUnwrapModule{Enabled: false, Cfg: unWrapCfg.VastUnWrapCfg{ - HTTPConfig: unWrapCfg.HttpConfig{MaxIdleConns: 100, MaxIdleConnsPerHost: 1, IdleConnTimeout: 300}, - APPConfig: unWrapCfg.AppConfig{Host: "", Port: 0, UnwrapDefaultTimeout: 100, Debug: 1}, - StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, - ServerConfig: unWrapCfg.ServerConfig{ServerName: "", DCName: "OW_DC"}, - }, - getVastUnwrapEnabled: func(rctx models.RequestCtx, vastunwraptraffic int) bool { - return false - }, - }}, - args: args{ - miCtx: hookstage.ModuleInvocationContext{}, - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - ctx := context.WithValue(context.Background(), VastUnwrapEnabled, "1") - r, _ := http.NewRequestWithContext(ctx, "POST", "http://localhost/video/openrtb?sshb=1", nil) - return r - }(), - }}, - want: hookstage.HookResult[hookstage.EntrypointPayload]{}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - m := VastUnwrapModule{ - Cfg: tt.fields.cfg.Cfg, - Enabled: tt.fields.cfg.Enabled, - getVastUnwrapEnabled: tt.fields.cfg.getVastUnwrapEnabled, - } - got, err := m.HandleEntrypointHook(tt.args.ctx, tt.args.miCtx, tt.args.payload) - if !assert.NoError(t, err, tt.wantErr) { - return - } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("VastUnwrapModule.HandleEntrypointHook() = %v, want %v", got, tt.want) - } - }) - } -} +func TestUnwrap_Unwrap(t *testing.T) { -func TestVastUnwrapModuleHandleRawBidderResponseHook(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() - mockMetricsEngine := mock_stats.NewMockMetricsEngine(ctrl) + mockMetricsEngine := mock_metrics.NewMockMetricsEngine(ctrl) + type fields struct { - cfg VastUnwrapModule + endpoint string + defaultTime int + metricEngine metrics.MetricsEngine + unwrapRequest http.HandlerFunc } type args struct { - in0 context.Context - miCtx hookstage.ModuleInvocationContext - payload hookstage.RawBidderResponsePayload - wantAdM bool - randomNumber int + accountID string + bidder string + bid *adapters.TypedBid + userAgent string + ip string + isStatsEnabled bool } tests := []struct { - name string - fields fields - args args - want hookstage.HookResult[hookstage.RawBidderResponsePayload] - wantErr bool - setup func() - unwrapRequest func(w http.ResponseWriter, req *http.Request) + name string + fields fields + args args + setup func() + mockHandler http.HandlerFunc + expectedAdm string }{ { - name: "Vast unwrap is enabled in the config", - fields: fields{cfg: VastUnwrapModule{Enabled: true, Cfg: unWrapCfg.VastUnWrapCfg{ - MaxWrapperSupport: 5, - HTTPConfig: unWrapCfg.HttpConfig{MaxIdleConns: 100, MaxIdleConnsPerHost: 1, IdleConnTimeout: 300}, - APPConfig: unWrapCfg.AppConfig{Host: "", Port: 0, UnwrapDefaultTimeout: 1000, Debug: 1}, - StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, - ServerConfig: unWrapCfg.ServerConfig{ServerName: "", DCName: "OW_DC"}, - }, - getVastUnwrapEnabled: func(rctx models.RequestCtx, vastunwraptraffic int) bool { - return true - }, - }}, + name: "Stats enabled only", args: args{ - miCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: true, Redirect: true}}}, - payload: hookstage.RawBidderResponsePayload{ - Bids: []*adapters.TypedBid{ - { - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: vastXMLAdM, - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }}, - Bidder: "pubmatic", + accountID: "5890", + bidder: "pubmatic", + bid: &adapters.TypedBid{ + Bid: &openrtb2.Bid{ + AdM: vastXMLAdM, + }, }, - wantAdM: true, - randomNumber: 10, + userAgent: "UA", + ip: "10.12.13.14", + isStatsEnabled: true, }, setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0") - mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1") - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordUnwrapRequestStatus("5890", "pubmatic", "0") + mockMetricsEngine.EXPECT().RecordUnwrapWrapperCount("5890", "pubmatic", "1") + mockMetricsEngine.EXPECT().RecordUnwrapRequestTime("5890", "pubmatic", gomock.Any()) mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()) - }, - unwrapRequest: func(w http.ResponseWriter, req *http.Request) { + mockHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { w.Header().Add("unwrap-status", "0") w.Header().Add("unwrap-count", "1") w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(inlineXMLAdM)) - }, - - want: hookstage.HookResult[hookstage.RawBidderResponsePayload]{}, + _, _ = w.Write([]byte(vastXMLAdM)) + }), + expectedAdm: vastXMLAdM, }, { - name: "Vast unwrap is disabled in the config", - fields: fields{cfg: VastUnwrapModule{Enabled: false, Cfg: unWrapCfg.VastUnWrapCfg{ - HTTPConfig: unWrapCfg.HttpConfig{MaxIdleConns: 100, MaxIdleConnsPerHost: 1, IdleConnTimeout: 300}, - APPConfig: unWrapCfg.AppConfig{Host: "", Port: 0, UnwrapDefaultTimeout: 100, Debug: 1}, - StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, - ServerConfig: unWrapCfg.ServerConfig{ServerName: "", DCName: "OW_DC"}, - }, - getVastUnwrapEnabled: func(rctx models.RequestCtx, vastunwraptraffic int) bool { - return true - }, - }}, + name: "Unwrap enabled with valid adm", args: args{ - miCtx: hookstage.ModuleInvocationContext{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false}}}, - payload: hookstage.RawBidderResponsePayload{ - Bids: []*adapters.TypedBid{ - { - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: "
This is an Ad
", - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }}, - }}, - want: hookstage.HookResult[hookstage.RawBidderResponsePayload]{}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.setup != nil { - tt.setup() - } - openwrap.GetRandomNumberIn1To100 = func() int { - return tt.args.randomNumber - } - m := VastUnwrapModule{ - Cfg: tt.fields.cfg.Cfg, - Enabled: tt.fields.cfg.Enabled, - MetricsEngine: mockMetricsEngine, - unwrapRequest: tt.unwrapRequest, - getVastUnwrapEnabled: tt.fields.cfg.getVastUnwrapEnabled, - } - _, err := m.HandleRawBidderResponseHook(tt.args.in0, tt.args.miCtx, tt.args.payload) - if !assert.NoError(t, err, tt.wantErr) { - return - } - if tt.args.wantAdM { - assert.Equal(t, inlineXMLAdM, tt.args.payload.Bids[0].Bid.AdM, "got, tt.want AdM is not updatd correctly after executing RawBidderResponse hook.") - } - }) - } -} - -func TestBuilder(t *testing.T) { - type args struct { - rawCfg json.RawMessage - deps moduledeps.ModuleDeps - } - tests := []struct { - name string - args args - want VastUnwrapModule - wantErr bool - }{ - { - name: "Valid vast unwrap config", - args: args{ - rawCfg: json.RawMessage(`{"enabled":true,"vastunwrap_cfg":{"app_config":{"debug":1,"unwrap_default_timeout":100},"max_wrapper_support":5,"http_config":{"idle_conn_timeout":300,"max_idle_conns":100,"max_idle_conns_per_host":1},"log_config":{"debug_log_file":"/tmp/debug.log","error_log_file":"/tmp/error.log"},"server_config":{"dc_name":"OW_DC"},"stat_config":{"host":"10.172.141.13","port":8080,"referesh_interval_in_sec":1}}}`), - deps: moduledeps.ModuleDeps{ - MetricsRegistry: metrics_cfg.MetricsRegistry{ - metrics_cfg.PrometheusRegistry: prometheus.NewRegistry(), - }, - MetricsCfg: &config.Metrics{ - Prometheus: config.PrometheusMetrics{ - Port: 14404, - Namespace: "ow", - Subsystem: "pbs", - TimeoutMillisRaw: 10, - }, + accountID: "5890", + bidder: "pubmatic", + bid: &adapters.TypedBid{ + Bid: &openrtb2.Bid{ + AdM: vastXMLAdM, }, }, + userAgent: "UA", + ip: "10.12.13.14", + isStatsEnabled: false, }, - want: VastUnwrapModule{ - Enabled: true, - Cfg: unWrapCfg.VastUnWrapCfg{ - MaxWrapperSupport: 5, - HTTPConfig: unWrapCfg.HttpConfig{MaxIdleConns: 100, MaxIdleConnsPerHost: 1, IdleConnTimeout: 300}, - APPConfig: unWrapCfg.AppConfig{Host: "", Port: 0, UnwrapDefaultTimeout: 100, Debug: 1}, - StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, - ServerConfig: unWrapCfg.ServerConfig{ServerName: "", DCName: "OW_DC"}, - }, + setup: func() { + mockMetricsEngine.EXPECT().RecordUnwrapRequestStatus("5890", "pubmatic", "0") + mockMetricsEngine.EXPECT().RecordUnwrapWrapperCount("5890", "pubmatic", "1") + mockMetricsEngine.EXPECT().RecordUnwrapRequestTime("5890", "pubmatic", gomock.Any()) + mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()) }, - wantErr: false, + mockHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Add("unwrap-status", "0") + w.Header().Add("unwrap-count", "1") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(inlineXMLAdM)) + }), + expectedAdm: inlineXMLAdM, }, { - name: "Invalid vast unwrap config", + name: "Unwrap enabled with invalid adm", args: args{ - rawCfg: json.RawMessage(`{"enabled": 1,"vastunwrap_cfg":{"app_config":{"debug":1,"unwrap_default_timeout":100},"max_wrapper_support":5,"http_config":{"idle_conn_timeout":300,"max_idle_conns":100,"max_idle_conns_per_host":1},"log_config":{"debug_log_file":"/tmp/debug.log","error_log_file":"/tmp/error.log"},"server_config":{"dc_name":"OW_DC"},"stat_config":{"host":"10.172.141.13","port":8080,"referesh_interval_in_sec":1}}}`), - deps: moduledeps.ModuleDeps{ - MetricsRegistry: metrics_cfg.MetricsRegistry{ - metrics_cfg.PrometheusRegistry: prometheus.NewRegistry(), - }, - MetricsCfg: &config.Metrics{ - Prometheus: config.PrometheusMetrics{ - Port: 14404, - Namespace: "ow", - Subsystem: "pbs", - TimeoutMillisRaw: 10, - }, + accountID: "5890", + bidder: "pubmatic", + bid: &adapters.TypedBid{ + Bid: &openrtb2.Bid{ + AdM: invalidVastXMLAdM, }, - }}, - want: VastUnwrapModule{ - Enabled: true, - Cfg: unWrapCfg.VastUnWrapCfg{ - MaxWrapperSupport: 5, - HTTPConfig: unWrapCfg.HttpConfig{MaxIdleConns: 100, MaxIdleConnsPerHost: 1, IdleConnTimeout: 300}, - APPConfig: unWrapCfg.AppConfig{Host: "", Port: 0, UnwrapDefaultTimeout: 100, Debug: 1}, - StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, - ServerConfig: unWrapCfg.ServerConfig{ServerName: "", DCName: "OW_DC"}, }, + userAgent: "UA", + ip: "10.12.13.14", + isStatsEnabled: false, }, - wantErr: true, + setup: func() { + mockMetricsEngine.EXPECT().RecordUnwrapRequestStatus("5890", "pubmatic", "1") + mockMetricsEngine.EXPECT().RecordUnwrapRequestTime("5890", "pubmatic", gomock.Any()) + }, + mockHandler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.Header().Add("unwrap-status", "1") + w.WriteHeader(http.StatusNoContent) + }), + expectedAdm: invalidVastXMLAdM, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := Builder(tt.args.rawCfg, tt.args.deps) - if (err != nil) == tt.wantErr { - return - } - got1, _ := got.(VastUnwrapModule) - if !reflect.DeepEqual(got1.Cfg, tt.want.Cfg) { - t.Errorf("initVastUnwrap() = %#v, want %#v", got, tt.want) + + if tt.setup != nil { + tt.setup() } - if !reflect.DeepEqual(got1.Enabled, tt.want.Enabled) { - t.Errorf("initVastUnwrap() = %#v, want %#v", got, tt.want) + uw := NewUnwrap("http://localhost:8001/unwrap", 200, tt.mockHandler, mockMetricsEngine) + uw.Unwrap(tt.args.accountID, tt.args.bidder, tt.args.bid, tt.args.userAgent, tt.args.ip, tt.args.isStatsEnabled) + if !tt.args.isStatsEnabled && strings.Compare(tt.args.bid.Bid.AdM, tt.expectedAdm) != 0 { + assert.Equal(t, inlineXMLAdM, tt.args.bid.Bid.AdM, "AdM is not updated correctly after unwrap ") + } }) } diff --git a/modules/pubmatic/openwrap/util.go b/modules/pubmatic/openwrap/util.go index e581daca60f..a4fd1206090 100644 --- a/modules/pubmatic/openwrap/util.go +++ b/modules/pubmatic/openwrap/util.go @@ -8,13 +8,13 @@ import ( "strings" "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/adapters" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adapters" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" ) var ( diff --git a/modules/pubmatic/openwrap/util_test.go b/modules/pubmatic/openwrap/util_test.go index 6e442760bb8..bee405afa95 100644 --- a/modules/pubmatic/openwrap/util_test.go +++ b/modules/pubmatic/openwrap/util_test.go @@ -6,17 +6,17 @@ import ( "testing" "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/macros" - mock_metrics "github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/mock" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/adunitconfig" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models/nbr" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/macros" + mock_metrics "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/mock" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/adunitconfig" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models/nbr" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -176,6 +176,10 @@ func (s fakeSyncer) DefaultSyncType() usersync.SyncType { return usersync.SyncType("") } +func (s fakeSyncer) DefaultResponseFormat() usersync.SyncType { + return usersync.SyncType("") +} + func (s fakeSyncer) SupportsType(syncTypes []usersync.SyncType) bool { return false } diff --git a/modules/pubmatic/openwrap/utils/bid.go b/modules/pubmatic/openwrap/utils/bid.go index 166976179f3..c28d85a769f 100644 --- a/modules/pubmatic/openwrap/utils/bid.go +++ b/modules/pubmatic/openwrap/utils/bid.go @@ -3,7 +3,7 @@ package utils import ( "regexp" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" ) var bidIDRegx = regexp.MustCompile("(" + models.BidIdSeparator + ")") diff --git a/modules/pubmatic/vastunwrap/README.md b/modules/pubmatic/vastunwrap/README.md deleted file mode 100644 index c1f9947a533..00000000000 --- a/modules/pubmatic/vastunwrap/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Overview - -The main aim of this module is to provide vast unwrapping feature for CTV endpoint. It has 2 main functionality - - Entryhook : Entryhook will take request payload and check for vast unwrapper enable flag in the request context and update the same in module context as rctx. - - raw bidder response : This stage will use vast unwrapper enable flag from module context and decide whether to use vast unwrapping or not - diff --git a/modules/pubmatic/vastunwrap/constant.go b/modules/pubmatic/vastunwrap/constant.go deleted file mode 100644 index af58d94e115..00000000000 --- a/modules/pubmatic/vastunwrap/constant.go +++ /dev/null @@ -1,26 +0,0 @@ -package vastunwrap - -const ( - VastUnwrapEnabled = "enableVastUnwrapper" - RequestContext = "rctx" - UserAgent = "User-Agent" - XUserIP = "X-Forwarded-For" - XUserAgent = "X-Device-User-Agent" - CreativeID = "unwrap-ucrid" - PubID = "pub_id" - ImpressionID = "imr_id" - UnwrapCount = "unwrap-count" - UnwrapStatus = "unwrap-status" - Success = "Success" - Failure = "Failure" - Timeout = "Timeout" - UnwrapStatusTimeout = "2" - UnwrapURL = "http://localhost:8003/unwrap" - ContentType = "Content-Type" - UnwrapTimeout = "unwrap-timeout" - MediaTypeVideo = "video" - ProfileId = "profileID" - VersionId = "versionID" - DisplayId = "DisplayID" - Endpoint = "Endpoint" -) diff --git a/modules/pubmatic/vastunwrap/entryhook.go b/modules/pubmatic/vastunwrap/entryhook.go deleted file mode 100644 index 301df558ffc..00000000000 --- a/modules/pubmatic/vastunwrap/entryhook.go +++ /dev/null @@ -1,67 +0,0 @@ -package vastunwrap - -import ( - "context" - "fmt" - "runtime/debug" - - "github.com/golang/glog" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap" - ow_models "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/models" -) - -// supportedEndpoints holds the list of endpoints which supports VAST-unwrap feature -var supportedEndpoints = map[string]struct{}{ - ow_models.EndpointVAST: {}, - ow_models.EndpointVideo: {}, - ow_models.EndpointJson: {}, - ow_models.EndpointV25: {}, -} - -func getVastUnwrapperEnable(ctx context.Context, field string) bool { - vastEnableUnwrapper, _ := ctx.Value(field).(string) - return vastEnableUnwrapper == "1" -} -func handleEntrypointHook( - _ context.Context, - _ hookstage.ModuleInvocationContext, - payload hookstage.EntrypointPayload, -) (hookstage.HookResult[hookstage.EntrypointPayload], error) { - defer func() { - if r := recover(); r != nil { - glog.Errorf("body:[%s] Error:[%v] stacktrace:[%s]", string(payload.Body), r, string(debug.Stack())) - } - }() - result := hookstage.HookResult[hookstage.EntrypointPayload]{ - ModuleContext: make(hookstage.ModuleContext), - } - vastRequestContext := models.RequestCtx{} - queryParams := payload.Request.URL.Query() - source := queryParams.Get("source") - if queryParams.Get("sshb") == "1" { - vastRequestContext = models.RequestCtx{ - VastUnwrapEnabled: getVastUnwrapperEnable(payload.Request.Context(), VastUnwrapEnabled), - Redirect: true, - UA: openwrap.GetRequestUserAgent(payload.Body, payload.Request), - IP: openwrap.GetRequestIP(payload.Body, payload.Request), - } - } else { - endpoint := openwrap.GetEndpoint(payload.Request.URL.Path, source) - if _, ok := supportedEndpoints[endpoint]; !ok { - result.DebugMessages = append(result.DebugMessages, fmt.Sprintf("%s endpoint does not support vast-unwrap feature", endpoint)) - return result, nil - } - requestExtWrapper, _ := openwrap.GetRequestWrapper(payload, result, endpoint) - vastRequestContext = models.RequestCtx{ - ProfileID: requestExtWrapper.ProfileId, - DisplayID: requestExtWrapper.VersionId, - Endpoint: endpoint, - UA: openwrap.GetRequestUserAgent(payload.Body, payload.Request), - IP: openwrap.GetRequestIP(payload.Body, payload.Request), - } - } - result.ModuleContext[RequestContext] = vastRequestContext - return result, nil -} diff --git a/modules/pubmatic/vastunwrap/entryhook_test.go b/modules/pubmatic/vastunwrap/entryhook_test.go deleted file mode 100644 index f3e6a71b359..00000000000 --- a/modules/pubmatic/vastunwrap/entryhook_test.go +++ /dev/null @@ -1,163 +0,0 @@ -package vastunwrap - -import ( - "context" - "net/http" - "testing" - - "github.com/prebid/prebid-server/hooks/hookstage" - ow_models "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/models" - "github.com/stretchr/testify/assert" -) - -func TestHandleEntrypointHook(t *testing.T) { - type args struct { - payload hookstage.EntrypointPayload - } - tests := []struct { - name string - args args - want hookstage.HookResult[hookstage.EntrypointPayload] - }{ - { - name: "Disable Vast Unwrapper for CTV video/openrtb request", - args: args{ - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - ctx := context.WithValue(context.Background(), VastUnwrapEnabled, "0") - ctx = context.WithValue(ctx, ProfileId, 0) - ctx = context.WithValue(ctx, VersionId, 0) - ctx = context.WithValue(ctx, DisplayId, 0) - ctx = context.WithValue(ctx, Endpoint, "") - r, _ := http.NewRequestWithContext(ctx, "POST", "http://localhost/video/openrtb?sshb=1", nil) - return r - }(), - }, - }, - want: hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false, Redirect: true}}}, - }, - { - name: "Enable Vast Unwrapper for CTV video/openrtb request", - args: args{ - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - ctx := context.WithValue(context.Background(), VastUnwrapEnabled, "1") - ctx = context.WithValue(ctx, ProfileId, 0) - ctx = context.WithValue(ctx, VersionId, 0) - ctx = context.WithValue(ctx, DisplayId, 0) - ctx = context.WithValue(ctx, Endpoint, "") - r, _ := http.NewRequestWithContext(ctx, "POST", "http://localhost/video/openrtb?sshb=1", nil) - return r - }(), - }, - }, - want: hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: true, Redirect: true}}}, - }, - { - name: "Vast Unwrapper for IN-APP openrtb2/auction request", - args: args{ - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - r, _ := http.NewRequest("POST", "http://localhost/openrtb2/auction?source=owsdk", nil) - return r - }(), - Body: []byte(`{"ext":{"wrapper":{"profileid":5890,"versionid":1}}}`), - }, - }, - want: hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false, Redirect: false, ProfileID: 5890, DisplayID: 1, Endpoint: ow_models.EndpointV25}}}, - }, - { - name: "Vast Unwrapper for IN-APP /openrtb/2.5 request coming from SSHB", - args: args{ - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - ctx := context.WithValue(context.Background(), VastUnwrapEnabled, "1") - r, _ := http.NewRequestWithContext(ctx, "POST", "http://localhost/openrtb2/auction?sshb=1", nil) - return r - }(), - Body: []byte(`{"ext":{"wrapper":{"profileid":5890,"versionid":1}}}`), - }, - }, - want: hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: true, Redirect: true, ProfileID: 0, DisplayID: 0, Endpoint: ""}}}, - }, - { - name: "Vast Unwrapper for IN-APP /openrtb/2.5 request directly coming to prebid", - args: args{ - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - ctx := context.WithValue(context.Background(), VastUnwrapEnabled, "1") - r, _ := http.NewRequestWithContext(ctx, "POST", "http://localhost/openrtb/2.5", nil) - return r - }(), - Body: []byte(`{"ext":{"wrapper":{"profileid":5890,"versionid":1}}}`), - }, - }, - want: hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false, Redirect: false, ProfileID: 5890, DisplayID: 1, Endpoint: ow_models.EndpointV25}}}, - }, - { - name: "Vast Unwrapper for WebS2S activation request", - args: args{ - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - ctx := context.WithValue(context.Background(), VastUnwrapEnabled, "1") - r, _ := http.NewRequestWithContext(ctx, "POST", "http://localhost/openrtb2/auction?source=pbjs", nil) - return r - }(), - Body: []byte(`{"ext":{"wrapper":{"profileid":5890,"versionid":1}}}`), - }, - }, - want: hookstage.HookResult[hookstage.EntrypointPayload]{ - ModuleContext: hookstage.ModuleContext{}, - DebugMessages: []string{"webs2s endpoint does not support vast-unwrap feature"}, - }, - }, - { - name: "Vast Unwrapper for Hybrid request", - args: args{ - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - ctx := context.WithValue(context.Background(), VastUnwrapEnabled, "1") - r, _ := http.NewRequestWithContext(ctx, "POST", "http://localhost:8001/pbs/openrtb2/auction", nil) - return r - }(), - Body: []byte(`{"ext":{"wrapper":{"profileid":5890,"versionid":1}}}`), - }, - }, - want: hookstage.HookResult[hookstage.EntrypointPayload]{ - ModuleContext: hookstage.ModuleContext{}, - DebugMessages: []string{"hybrid endpoint does not support vast-unwrap feature"}, - }, - }, - { - name: "Vast Unwrapper for AMP request coming from SSHB", - args: args{ - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - r, _ := http.NewRequest("GET", "http://localhost:8001/amp?sshb=1&v=1&w=300&h=250", nil) - return r - }(), - }, - }, - want: hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false, Redirect: true, Endpoint: ""}}}, - }, - { - name: "Vast Unwrapper for IN-APP OTT request coming from SSHB", - args: args{ - payload: hookstage.EntrypointPayload{ - Request: func() *http.Request { - r, _ := http.NewRequest("GET", "http://localhost:8001/openrtb/2.5/video?sshb=1&owLoggerDebug=1&pubId=5890&profId=2543", nil) - return r - }(), - }, - }, - want: hookstage.HookResult[hookstage.EntrypointPayload]{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false, Redirect: true, Endpoint: ""}}}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - got, _ := handleEntrypointHook(nil, hookstage.ModuleInvocationContext{}, tt.args.payload) - assert.Equal(t, tt.want, got, "mismatched handleEntrypointHook output") - }) - } -} diff --git a/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go b/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go deleted file mode 100644 index 71681c0e872..00000000000 --- a/modules/pubmatic/vastunwrap/hook_raw_bidder_response.go +++ /dev/null @@ -1,72 +0,0 @@ -package vastunwrap - -import ( - "fmt" - "strconv" - "sync" - - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap" - "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/models" - - "github.com/prebid/prebid-server/hooks/hookstage" -) - -func (m VastUnwrapModule) handleRawBidderResponseHook( - miCtx hookstage.ModuleInvocationContext, - payload hookstage.RawBidderResponsePayload, - unwrapURL string, -) (result hookstage.HookResult[hookstage.RawBidderResponsePayload], err error) { - vastRequestContext, ok := miCtx.ModuleContext[RequestContext].(models.RequestCtx) - if !ok { - result.DebugMessages = append(result.DebugMessages, "error: request-ctx not found in handleRawBidderResponseHook()") - return result, nil - } - vastUnwrapEnabled := vastRequestContext.VastUnwrapEnabled - if !vastRequestContext.Redirect { - pubId, _ := strconv.Atoi(miCtx.AccountID) - vastRequestContext.PubID = pubId - vastUnwrapEnabled = m.getVastUnwrapEnabled(vastRequestContext, m.TrafficPercentage) - result.DebugMessages = append(result.DebugMessages, - fmt.Sprintf("found request without sshb=1 in handleRawBidderResponseHook() for pubid:[%d]", vastRequestContext.PubID)) - } - - vastRequestContext.VastUnwrapEnabled = vastUnwrapEnabled - if vastRequestContext.VastUnwrapEnabled { - // Do Unwrap and Update Adm - wg := new(sync.WaitGroup) - for _, bid := range payload.Bids { - if string(bid.BidType) == MediaTypeVideo { - wg.Add(1) - go func(bid *adapters.TypedBid) { - defer wg.Done() - m.doUnwrapandUpdateBid(vastRequestContext.VastUnwrapStatsEnabled, bid, vastRequestContext.UA, vastRequestContext.IP, unwrapURL, miCtx.AccountID, payload.Bidder) - }(bid) - } - } - wg.Wait() - changeSet := hookstage.ChangeSet[hookstage.RawBidderResponsePayload]{} - changeSet.RawBidderResponse().Bids().Update(payload.Bids) - result.ChangeSet = changeSet - } else { - vastRequestContext.VastUnwrapStatsEnabled = openwrap.GetRandomNumberIn1To100() <= m.StatTrafficPercentage - if vastRequestContext.VastUnwrapStatsEnabled { - // Do Unwrap and Collect stats only - for _, bid := range payload.Bids { - if string(bid.BidType) == MediaTypeVideo { - go func(bid *adapters.TypedBid) { - m.doUnwrapandUpdateBid(vastRequestContext.VastUnwrapStatsEnabled, bid, vastRequestContext.UA, vastRequestContext.IP, unwrapURL, miCtx.AccountID, payload.Bidder) - }(bid) - } - } - } - } - - if vastRequestContext.VastUnwrapEnabled || vastRequestContext.VastUnwrapStatsEnabled { - result.DebugMessages = append(result.DebugMessages, - fmt.Sprintf("For pubid:[%d] VastUnwrapEnabled: [%v] VastUnwrapStatsEnabled:[%v] ", - vastRequestContext.PubID, vastRequestContext.VastUnwrapEnabled, vastRequestContext.VastUnwrapStatsEnabled)) - } - - return result, nil -} diff --git a/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go b/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go deleted file mode 100644 index 3b61a75990d..00000000000 --- a/modules/pubmatic/vastunwrap/hook_raw_bidder_response_test.go +++ /dev/null @@ -1,389 +0,0 @@ -package vastunwrap - -import ( - "fmt" - "net/http" - - "testing" - - "git.pubmatic.com/vastunwrap/config" - unWrapCfg "git.pubmatic.com/vastunwrap/config" - "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap" - "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/models" - mock_stats "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/stats/mock" - "github.com/stretchr/testify/assert" -) - -func TestHandleRawBidderResponseHook(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - mockMetricsEngine := mock_stats.NewMockMetricsEngine(ctrl) - VastUnWrapModule := VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1500}}, MetricsEngine: mockMetricsEngine} - type args struct { - module VastUnwrapModule - payload hookstage.RawBidderResponsePayload - moduleInvocationCtx hookstage.ModuleInvocationContext - unwrapTimeout int - url string - wantAdM bool - randomNumber int - statTrafficPercentage int - } - tests := []struct { - name string - args args - wantResult hookstage.HookResult[hookstage.RawBidderResponsePayload] - expectedBids []*adapters.TypedBid - setup func() - wantErr bool - unwrapRequest func(w http.ResponseWriter, req *http.Request) - getVastUnwrapEnabled func(rctx models.RequestCtx, vastunwraptraffic int) bool - }{ - { - name: "Empty Request Context", - args: args{ - module: VastUnWrapModule, - }, - wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{DebugMessages: []string{"error: request-ctx not found in handleRawBidderResponseHook()"}}, - wantErr: false, - }, - { - name: "Set Vast Unwrapper to false in request context with type video", - args: args{ - module: VastUnWrapModule, - payload: hookstage.RawBidderResponsePayload{ - Bids: []*adapters.TypedBid{ - { - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: "
This is an Ad
", - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }}}, - moduleInvocationCtx: hookstage.ModuleInvocationContext{ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false, Redirect: true}}}, - randomNumber: 1, - }, - wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, - wantErr: false, - }, - { - name: "Set Vast Unwrapper to false in request context with type video, stats enabled true", - args: args{ - module: VastUnWrapModule, - payload: hookstage.RawBidderResponsePayload{ - Bids: []*adapters.TypedBid{ - { - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: vastXMLAdM, - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }}, - Bidder: "pubmatic", - }, - moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: false, VastUnwrapStatsEnabled: true, Redirect: true}}}, - statTrafficPercentage: 100, - randomNumber: 10, - }, - wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, - setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() - mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes() - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() - mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()).AnyTimes() - }, - unwrapRequest: func(w http.ResponseWriter, req *http.Request) { - w.Header().Add("unwrap-status", "0") - w.Header().Add("unwrap-count", "1") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(inlineXMLAdM)) - }, - wantErr: false, - }, - { - name: "Set Vast Unwrapper to true in request context with invalid vast xml", - args: args{ - module: VastUnWrapModule, - payload: hookstage.RawBidderResponsePayload{ - Bids: []*adapters.TypedBid{ - { - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: invalidVastXMLAdM, - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }, - }, - Bidder: "pubmatic", - }, - moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: true, Redirect: true}}}, - url: UnwrapURL, - }, - wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, - setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "1").AnyTimes() - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() - }, - unwrapRequest: func(w http.ResponseWriter, req *http.Request) { - w.Header().Add("unwrap-status", "1") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(invalidVastXMLAdM)) - }, - wantErr: true, - }, - { - name: "Set Vast Unwrapper to true in request context with type video", - args: args{ - module: VastUnWrapModule, - payload: hookstage.RawBidderResponsePayload{ - Bids: []*adapters.TypedBid{ - { - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: vastXMLAdM, - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }, - }, - Bidder: "pubmatic", - }, - moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: true, Redirect: true}}}, - url: UnwrapURL, - }, - wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, - setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() - mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes() - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() - mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()).AnyTimes() - }, - unwrapRequest: func(w http.ResponseWriter, req *http.Request) { - w.Header().Add("unwrap-status", "0") - w.Header().Add("unwrap-count", "1") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(inlineXMLAdM)) - }, - wantErr: false, - }, - { - name: "Set Vast Unwrapper to true in request context for multiple bids with type video", - args: args{ - module: VastUnWrapModule, - payload: hookstage.RawBidderResponsePayload{ - Bids: []*adapters.TypedBid{ - { - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: vastXMLAdM, - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }, - { - Bid: &openrtb2.Bid{ - ID: "Bid-456", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: vastXMLAdM, - CrID: "Cr-789", - W: 100, - H: 50, - }, - BidType: "video", - }}, - Bidder: "pubmatic", - }, - moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: true, Redirect: true}}}, - url: UnwrapURL, - wantAdM: true, - randomNumber: 10, - }, - wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, - setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() - mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes() - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() - mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()).AnyTimes() - }, - unwrapRequest: func(w http.ResponseWriter, req *http.Request) { - w.Header().Add("unwrap-status", "0") - w.Header().Add("unwrap-count", "1") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(inlineXMLAdM)) - }, - wantErr: false, - }, - { - name: "Set Vast Unwrapper to true in request context for multiple bids with different type", - args: args{ - module: VastUnWrapModule, - payload: hookstage.RawBidderResponsePayload{ - Bids: []*adapters.TypedBid{ - { - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: vastXMLAdM, - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }, - { - Bid: &openrtb2.Bid{ - ID: "Bid-456", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: vastXMLAdM, - CrID: "Cr-789", - W: 100, - H: 50, - }, - BidType: "banner", - }}, - Bidder: "pubmatic", - }, - moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{VastUnwrapEnabled: true, Redirect: true}}}, - url: UnwrapURL, - }, - wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, - expectedBids: []*adapters.TypedBid{{ - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: inlineXMLAdM, - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }, - { - Bid: &openrtb2.Bid{ - ID: "Bid-456", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: vastXMLAdM, - CrID: "Cr-789", - W: 100, - H: 50, - }, - BidType: "banner", - }, - }, - setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() - mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "0").AnyTimes() - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() - mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "0", gomock.Any()).AnyTimes() - }, - unwrapRequest: func(w http.ResponseWriter, req *http.Request) { - w.Header().Add("unwrap-status", "0") - w.Header().Add("unwrap-count", "0") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(inlineXMLAdM)) - }, - wantErr: false, - }, - - { - name: "Set Vast Unwrapper to true in request context with type video and source owsdk", - args: args{ - module: VastUnWrapModule, - payload: hookstage.RawBidderResponsePayload{ - Bids: []*adapters.TypedBid{ - { - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: vastXMLAdM, - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }, - }, - Bidder: "pubmatic", - }, - moduleInvocationCtx: hookstage.ModuleInvocationContext{AccountID: "5890", ModuleContext: hookstage.ModuleContext{"rctx": models.RequestCtx{Redirect: false, ProfileID: 5890, DisplayID: 1, Endpoint: "/openrtb/video"}}}, - url: UnwrapURL, - }, - wantResult: hookstage.HookResult[hookstage.RawBidderResponsePayload]{Reject: false}, - setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0").AnyTimes() - mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1").AnyTimes() - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()).AnyTimes() - mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()).AnyTimes() - }, - unwrapRequest: func(w http.ResponseWriter, req *http.Request) { - w.Header().Add("unwrap-status", "0") - w.Header().Add("unwrap-count", "1") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(inlineXMLAdM)) - }, - getVastUnwrapEnabled: func(rctx models.RequestCtx, vastinwraptraffic int) bool { - return true - }, - - wantErr: false, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.setup != nil { - tt.setup() - } - openwrap.GetRandomNumberIn1To100 = func() int { - return tt.args.randomNumber - } - m := VastUnwrapModule{ - Cfg: tt.args.module.Cfg, - Enabled: true, - MetricsEngine: mockMetricsEngine, - unwrapRequest: tt.unwrapRequest, - getVastUnwrapEnabled: tt.getVastUnwrapEnabled, - StatTrafficPercentage: tt.args.statTrafficPercentage, - } - _, err := m.handleRawBidderResponseHook(tt.args.moduleInvocationCtx, tt.args.payload, "test") - if !assert.NoError(t, err, tt.wantErr) { - return - } - if tt.args.moduleInvocationCtx.ModuleContext != nil && tt.args.wantAdM { - assert.Equal(t, inlineXMLAdM, tt.args.payload.Bids[0].Bid.AdM, "AdM is not updated correctly after executing RawBidderResponse hook.") - } - }) - } -} diff --git a/modules/pubmatic/vastunwrap/models/request.go b/modules/pubmatic/vastunwrap/models/request.go deleted file mode 100644 index 0417219ed63..00000000000 --- a/modules/pubmatic/vastunwrap/models/request.go +++ /dev/null @@ -1,11 +0,0 @@ -package models - -type RequestCtx struct { - UA string - IP string - VastUnwrapEnabled bool - PubID, ProfileID, DisplayID int - Endpoint string - VastUnwrapStatsEnabled bool - Redirect bool -} diff --git a/modules/pubmatic/vastunwrap/module.go b/modules/pubmatic/vastunwrap/module.go deleted file mode 100644 index 16e2e2d102f..00000000000 --- a/modules/pubmatic/vastunwrap/module.go +++ /dev/null @@ -1,85 +0,0 @@ -package vastunwrap - -import ( - "context" - "encoding/json" - "fmt" - "net/http" - "time" - - vastunwrap "git.pubmatic.com/vastunwrap" - - unWrapCfg "git.pubmatic.com/vastunwrap/config" - "github.com/golang/glog" - "github.com/prebid/prebid-server/hooks/hookstage" - "github.com/prebid/prebid-server/modules/moduledeps" - openwrap "github.com/prebid/prebid-server/modules/pubmatic/openwrap" - "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/models" - metrics "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/stats" -) - -type VastUnwrapModule struct { - Cfg unWrapCfg.VastUnWrapCfg `mapstructure:"vastunwrap_cfg" json:"vastunwrap_cfg"` - TrafficPercentage int `mapstructure:"traffic_percentage" json:"traffic_percentage"` - StatTrafficPercentage int `mapstructure:"stat_traffic_percentage" json:"stat_traffic_percentage"` - Enabled bool `mapstructure:"enabled" json:"enabled"` - MetricsEngine metrics.MetricsEngine - unwrapRequest func(w http.ResponseWriter, r *http.Request) - getVastUnwrapEnabled func(rctx models.RequestCtx, vastunwraptraffic int) bool -} - -func Builder(rawCfg json.RawMessage, deps moduledeps.ModuleDeps) (interface{}, error) { - return initVastUnwrap(rawCfg, deps) -} - -func initVastUnwrap(rawCfg json.RawMessage, deps moduledeps.ModuleDeps) (VastUnwrapModule, error) { - t := time.Now() - defer glog.Infof("Time taken by initVastUnwrap---%v", time.Since(t).Milliseconds()) - vastUnwrapModuleCfg := VastUnwrapModule{} - err := json.Unmarshal(rawCfg, &vastUnwrapModuleCfg) - if err != nil { - return vastUnwrapModuleCfg, fmt.Errorf("invalid vastunwrap config: %v", err) - } - - if vastUnwrapModuleCfg.Cfg.StatConfig.UseHostName { - vastUnwrapModuleCfg.Cfg.ServerConfig.ServerName = openwrap.GetHostName() - } - vastunwrap.InitUnWrapperConfig(vastUnwrapModuleCfg.Cfg) - metricEngine, err := metrics.NewMetricsEngine(deps) - if err != nil { - return vastUnwrapModuleCfg, fmt.Errorf("Prometheus registry is nil") - } - return VastUnwrapModule{ - Cfg: vastUnwrapModuleCfg.Cfg, - TrafficPercentage: vastUnwrapModuleCfg.TrafficPercentage, - StatTrafficPercentage: vastUnwrapModuleCfg.StatTrafficPercentage, - Enabled: vastUnwrapModuleCfg.Enabled, - MetricsEngine: metricEngine, - unwrapRequest: vastunwrap.UnwrapRequest, - getVastUnwrapEnabled: openwrap.GetVastUnwrapEnabled, - }, nil -} - -// HandleRawBidderResponseHook fetches rCtx and check for vast unwrapper flag to enable/disable vast unwrapping feature -func (m VastUnwrapModule) HandleRawBidderResponseHook( - _ context.Context, - miCtx hookstage.ModuleInvocationContext, - payload hookstage.RawBidderResponsePayload, -) (hookstage.HookResult[hookstage.RawBidderResponsePayload], error) { - if m.Enabled { - return m.handleRawBidderResponseHook(miCtx, payload, UnwrapURL) - } - return hookstage.HookResult[hookstage.RawBidderResponsePayload]{}, nil -} - -// HandleEntrypointHook retrieves vast un-wrapper flag and User-agent provided in request context -func (m VastUnwrapModule) HandleEntrypointHook( - ctx context.Context, - miCtx hookstage.ModuleInvocationContext, - payload hookstage.EntrypointPayload, -) (hookstage.HookResult[hookstage.EntrypointPayload], error) { - if m.Enabled { - return handleEntrypointHook(ctx, miCtx, payload) - } - return hookstage.HookResult[hookstage.EntrypointPayload]{}, nil -} diff --git a/modules/pubmatic/vastunwrap/stats/metrics.go b/modules/pubmatic/vastunwrap/stats/metrics.go deleted file mode 100644 index 926cb458e98..00000000000 --- a/modules/pubmatic/vastunwrap/stats/metrics.go +++ /dev/null @@ -1,126 +0,0 @@ -package metrics - -import ( - "errors" - "time" - - "github.com/prebid/prebid-server/config" - metrics_cfg "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/modules/moduledeps" - - "github.com/prometheus/client_golang/prometheus" -) - -const ( - bidderLabel = "bidder" - pubIdLabel = "pub_id" - statusLabel = "status" - wrapperCountLabel = "wrapper_count" -) - -// MetricsEngine is a generic interface to record metrics into the desired backend -type MetricsEngine interface { - RecordRequestStatus(accountId, bidder, status string) - RecordWrapperCount(accountId, bidder string, wrapper_count string) - RecordRequestTime(accountId, bidder string, readTime time.Duration) - RecordUnwrapRespTime(accountId, wraperCnt string, respTime time.Duration) -} - -// Metrics defines the datatype which will implement MetricsEngine -type Metrics struct { - Registry *prometheus.Registry - requests *prometheus.CounterVec - wrapperCount *prometheus.CounterVec - requestTime *prometheus.HistogramVec - unwrapRespTime *prometheus.HistogramVec -} - -// NewMetricsEngine reads the configuration and returns the appropriate metrics engine -// for this instance. -func NewMetricsEngine(cfg moduledeps.ModuleDeps) (*Metrics, error) { - metrics := Metrics{} - // Set up the Prometheus metrics engine. - if cfg.MetricsCfg != nil && cfg.MetricsRegistry != nil && cfg.MetricsRegistry[metrics_cfg.PrometheusRegistry] != nil { - prometheusRegistry, _ := cfg.MetricsRegistry[metrics_cfg.PrometheusRegistry].(*prometheus.Registry) - metrics.Registry = prometheusRegistry - } - if metrics.Registry == nil { - return &metrics, errors.New("Prometheus registry is nil") - } - metrics.requests = newCounter(cfg.MetricsCfg.Prometheus, metrics.Registry, - "vastunwrap_status", - "Count of vast unwrap requests labeled by status", - []string{pubIdLabel, bidderLabel, statusLabel}) - metrics.wrapperCount = newCounter(cfg.MetricsCfg.Prometheus, metrics.Registry, - "vastunwrap_wrapper_count", - "Count of vast unwrap levels labeled by bidder", - []string{pubIdLabel, bidderLabel, wrapperCountLabel}) - metrics.requestTime = newHistogramVec(cfg.MetricsCfg.Prometheus, metrics.Registry, - "vastunwrap_request_time", - "Time taken to serve the vast unwrap request in Milliseconds", []string{pubIdLabel, bidderLabel}, - []float64{50, 100, 200, 300, 500}) - metrics.unwrapRespTime = newHistogramVec(cfg.MetricsCfg.Prometheus, metrics.Registry, - "vastunwrap_resp_time", - "Time taken to serve the vast unwrap request in Milliseconds at wrapper count level", []string{pubIdLabel, wrapperCountLabel}, - []float64{50, 100, 150, 200}) - return &metrics, nil -} - -func newCounter(cfg config.PrometheusMetrics, registry *prometheus.Registry, name, help string, labels []string) *prometheus.CounterVec { - opts := prometheus.CounterOpts{ - Namespace: cfg.Namespace, - Subsystem: cfg.Subsystem, - Name: name, - Help: help, - } - counter := prometheus.NewCounterVec(opts, labels) - registry.MustRegister(counter) - return counter -} - -func newHistogramVec(cfg config.PrometheusMetrics, registry *prometheus.Registry, name, help string, labels []string, buckets []float64) *prometheus.HistogramVec { - opts := prometheus.HistogramOpts{ - Namespace: cfg.Namespace, - Subsystem: cfg.Subsystem, - Name: name, - Help: help, - Buckets: buckets, - } - histogram := prometheus.NewHistogramVec(opts, labels) - registry.MustRegister(histogram) - return histogram -} - -// RecordRequest record counter with vast unwrap status -func (m *Metrics) RecordRequestStatus(accountId, bidder, status string) { - m.requests.With(prometheus.Labels{ - pubIdLabel: accountId, - bidderLabel: bidder, - statusLabel: status, - }).Inc() -} - -// RecordWrapperCount record counter of wrapper levels -func (m *Metrics) RecordWrapperCount(accountId, bidder, wrapper_count string) { - m.wrapperCount.With(prometheus.Labels{ - pubIdLabel: accountId, - bidderLabel: bidder, - wrapperCountLabel: wrapper_count, - }).Inc() -} - -// RecordRequestReadTime records time takent to complete vast unwrap -func (m *Metrics) RecordRequestTime(accountId, bidder string, requestTime time.Duration) { - m.requestTime.With(prometheus.Labels{ - pubIdLabel: accountId, - bidderLabel: bidder, - }).Observe(float64(requestTime.Milliseconds())) -} - -// RecordUnwrapRespTime records time takent to complete vast unwrap per wrapper count level -func (m *Metrics) RecordUnwrapRespTime(accountId, wraperCnt string, respTime time.Duration) { - m.unwrapRespTime.With(prometheus.Labels{ - pubIdLabel: accountId, - wrapperCountLabel: wraperCnt, - }).Observe(float64(respTime.Milliseconds())) -} diff --git a/modules/pubmatic/vastunwrap/stats/metrics_test.go b/modules/pubmatic/vastunwrap/stats/metrics_test.go deleted file mode 100644 index c7a2e67545d..00000000000 --- a/modules/pubmatic/vastunwrap/stats/metrics_test.go +++ /dev/null @@ -1,119 +0,0 @@ -package metrics - -import ( - "testing" - "time" - - "github.com/prebid/prebid-server/config" - metrics_cfg "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/modules/moduledeps" - "github.com/prometheus/client_golang/prometheus" - dto "github.com/prometheus/client_model/go" - "github.com/stretchr/testify/assert" -) - -func createMetricsForTesting() *Metrics { - cfg := moduledeps.ModuleDeps{ - MetricsRegistry: metrics_cfg.MetricsRegistry{ - metrics_cfg.PrometheusRegistry: prometheus.NewRegistry(), - }, - MetricsCfg: &config.Metrics{ - Prometheus: config.PrometheusMetrics{ - Port: 14404, - Namespace: "ow", - Subsystem: "pbs", - TimeoutMillisRaw: 10, - }, - }, - } - metrics_engine, err := NewMetricsEngine(cfg) - if err != nil { - return &Metrics{} - } - return metrics_engine -} - -func TestRecordRequestTime(t *testing.T) { - m := createMetricsForTesting() - - m.RecordRequestTime("1234", "pubmatic", time.Millisecond*250) - - result := getHistogramFromHistogramVec(m.requestTime, "bidder", "pubmatic") - assertHistogram(t, result, 1, 250) -} - -func TestRecordRespTime(t *testing.T) { - m := createMetricsForTesting() - - m.RecordUnwrapRespTime("1234", "1", time.Millisecond*100) - - result := getHistogramFromHistogramVec(m.unwrapRespTime, "pub_id", "1234") - assertHistogram(t, result, 1, 100) -} - -func TestRecordRequestStatus(t *testing.T) { - m := createMetricsForTesting() - - m.RecordRequestStatus("1234", "pubmatic", "0") - - assertCounterVecValue(t, "Record_Request_Status", "Record_Request_Status_Success", m.requests, float64(1), prometheus.Labels{ - "pub_id": "1234", - "bidder": "pubmatic", - "status": "0", - }) -} - -func TestRecordWrapperCount(t *testing.T) { - m := createMetricsForTesting() - - m.RecordWrapperCount("1234", "pubmatic", "1") - - assertCounterVecValue(t, "Record_Wrapper_Count", "Record_Wrapper_Count", m.wrapperCount, float64(1), prometheus.Labels{ - "pub_id": "1234", - "bidder": "pubmatic", - "wrapper_count": "1", - }) -} - -func assertCounterValue(t *testing.T, description, name string, counter prometheus.Counter, expected float64) { - m := dto.Metric{} - counter.Write(&m) - actual := *m.GetCounter().Value - - assert.Equal(t, expected, actual, description) -} - -func assertCounterVecValue(t *testing.T, description, name string, counterVec *prometheus.CounterVec, expected float64, labels prometheus.Labels) { - counter := counterVec.With(labels) - assertCounterValue(t, description, name, counter, expected) -} - -func assertHistogram(t *testing.T, histogram dto.Histogram, expectedCount uint64, expectedSum float64) { - assert.Equal(t, expectedCount, histogram.GetSampleCount()) - assert.Equal(t, expectedSum, histogram.GetSampleSum()) -} -func getHistogramFromHistogramVec(histogram *prometheus.HistogramVec, labelKey, labelValue string) dto.Histogram { - var result dto.Histogram - processMetrics(histogram, func(m dto.Metric) { - for _, label := range m.GetLabel() { - if label.GetName() == labelKey && label.GetValue() == labelValue { - result = *m.GetHistogram() - } - } - }) - return result -} - -func processMetrics(collector prometheus.Collector, handler func(m dto.Metric)) { - collectorChan := make(chan prometheus.Metric) - go func() { - collector.Collect(collectorChan) - close(collectorChan) - }() - - for metric := range collectorChan { - dtoMetric := dto.Metric{} - metric.Write(&dtoMetric) - handler(dtoMetric) - } -} diff --git a/modules/pubmatic/vastunwrap/stats/mock/mock.go b/modules/pubmatic/vastunwrap/stats/mock/mock.go deleted file mode 100644 index 6fa4d69fab1..00000000000 --- a/modules/pubmatic/vastunwrap/stats/mock/mock.go +++ /dev/null @@ -1,82 +0,0 @@ -// Code generated by MockGen. DO NOT EDIT. -// Source: github.com/PubMatic-OpenWrap/prebid-server/modules/pubmatic/vastunwrap/stats (interfaces: MetricsEngine) - -// Package mock_stats is a generated GoMock package. -package mock - -import ( - gomock "github.com/golang/mock/gomock" - reflect "reflect" - time "time" -) - -// MockMetricsEngine is a mock of MetricsEngine interface -type MockMetricsEngine struct { - ctrl *gomock.Controller - recorder *MockMetricsEngineMockRecorder -} - -// MockMetricsEngineMockRecorder is the mock recorder for MockMetricsEngine -type MockMetricsEngineMockRecorder struct { - mock *MockMetricsEngine -} - -// NewMockMetricsEngine creates a new mock instance -func NewMockMetricsEngine(ctrl *gomock.Controller) *MockMetricsEngine { - mock := &MockMetricsEngine{ctrl: ctrl} - mock.recorder = &MockMetricsEngineMockRecorder{mock} - return mock -} - -// EXPECT returns an object that allows the caller to indicate expected use -func (m *MockMetricsEngine) EXPECT() *MockMetricsEngineMockRecorder { - return m.recorder -} - -// RecordRequestStatus mocks base method -func (m *MockMetricsEngine) RecordRequestStatus(arg0, arg1, arg2 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordRequestStatus", arg0, arg1, arg2) -} - -// RecordRequestStatus indicates an expected call of RecordRequestStatus -func (mr *MockMetricsEngineMockRecorder) RecordRequestStatus(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordRequestStatus", reflect.TypeOf((*MockMetricsEngine)(nil).RecordRequestStatus), arg0, arg1, arg2) -} - -// RecordWrapperCount mocks base method -func (m *MockMetricsEngine) RecordWrapperCount(arg0, arg1, arg2 string) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordWrapperCount", arg0, arg1, arg2) -} - -// RecordWrapperCount indicates an expected call of RecordRequestStatus -func (mr *MockMetricsEngineMockRecorder) RecordWrapperCount(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordWrapperCount", reflect.TypeOf((*MockMetricsEngine)(nil).RecordWrapperCount), arg0, arg1, arg2) -} - -// RecordUnwrapRespTime mocks base method -func (m *MockMetricsEngine) RecordUnwrapRespTime(arg0, arg1 string, arg2 time.Duration) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordUnwrapRespTime", arg0, arg1, arg2) -} - -// RecordUnwrapRespTime indicates an expected call of RecordRequestStatus -func (mr *MockMetricsEngineMockRecorder) RecordUnwrapRespTime(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordUnwrapRespTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordUnwrapRespTime), arg0, arg1, arg2) -} - -// RecordRequestTime mocks base method -func (m *MockMetricsEngine) RecordRequestTime(arg0, arg1 string, arg2 time.Duration) { - m.ctrl.T.Helper() - m.ctrl.Call(m, "RecordRequestTime", arg0, arg1, arg2) -} - -// RecordRequestTime indicates an expected call of RecordRequestTime -func (mr *MockMetricsEngineMockRecorder) RecordRequestTime(arg0, arg1, arg2 interface{}) *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordRequestTime", reflect.TypeOf((*MockMetricsEngine)(nil).RecordRequestTime), arg0, arg1, arg2) -} diff --git a/modules/pubmatic/vastunwrap/unwrap_service.go b/modules/pubmatic/vastunwrap/unwrap_service.go deleted file mode 100644 index 3d2fd14e97a..00000000000 --- a/modules/pubmatic/vastunwrap/unwrap_service.go +++ /dev/null @@ -1,59 +0,0 @@ -package vastunwrap - -import ( - "net/http" - "runtime/debug" - "strconv" - "strings" - "time" - - "github.com/golang/glog" - "github.com/prebid/prebid-server/adapters" -) - -func (m VastUnwrapModule) doUnwrapandUpdateBid(isStatsEnabled bool, bid *adapters.TypedBid, userAgent string, ip string, unwrapURL string, accountID string, bidder string) { - startTime := time.Now() - var wrapperCnt int64 - var respStatus string - if bid == nil || bid.Bid == nil || bid.Bid.AdM == "" { - return - } - defer func() { - if r := recover(); r != nil { - glog.Errorf("AdM:[%s] Error:[%v] stacktrace:[%s]", bid.Bid.AdM, r, string(debug.Stack())) - } - respTime := time.Since(startTime) - m.MetricsEngine.RecordRequestTime(accountID, bidder, respTime) - m.MetricsEngine.RecordRequestStatus(accountID, bidder, respStatus) - if respStatus == "0" { - m.MetricsEngine.RecordWrapperCount(accountID, bidder, strconv.Itoa(int(wrapperCnt))) - m.MetricsEngine.RecordUnwrapRespTime(accountID, strconv.Itoa(int(wrapperCnt)), respTime) - } - }() - headers := http.Header{} - headers.Add(ContentType, "application/xml; charset=utf-8") - headers.Add(UserAgent, userAgent) - headers.Add(XUserAgent, userAgent) - headers.Add(XUserIP, ip) - headers.Add(CreativeID, bid.Bid.ID) - headers.Add(UnwrapTimeout, strconv.Itoa(m.Cfg.APPConfig.UnwrapDefaultTimeout)) - - unwrapURL = unwrapURL + "?" + PubID + "=" + accountID + "&" + ImpressionID + "=" + bid.Bid.ImpID - httpReq, err := http.NewRequest(http.MethodPost, unwrapURL, strings.NewReader(bid.Bid.AdM)) - if err != nil { - return - } - httpReq.Header = headers - httpResp := NewCustomRecorder() - m.unwrapRequest(httpResp, httpReq) - respStatus = httpResp.Header().Get(UnwrapStatus) - wrapperCnt, _ = strconv.ParseInt(httpResp.Header().Get(UnwrapCount), 10, 0) - if !isStatsEnabled && httpResp.Code == http.StatusOK { - respBody := httpResp.Body.Bytes() - bid.Bid.AdM = string(respBody) - } - - glog.V(3).Infof("[VAST_UNWRAPPER] pubid:[%v] bidder:[%v] impid:[%v] bidid:[%v] status_code:[%v] httpRespCode= [%v] statsEnabled:[%v]", - accountID, bidder, bid.Bid.ImpID, bid.Bid.ID, respStatus, httpResp.Code, isStatsEnabled) - -} diff --git a/modules/pubmatic/vastunwrap/unwrap_service_test.go b/modules/pubmatic/vastunwrap/unwrap_service_test.go deleted file mode 100644 index e176625daef..00000000000 --- a/modules/pubmatic/vastunwrap/unwrap_service_test.go +++ /dev/null @@ -1,179 +0,0 @@ -package vastunwrap - -import ( - "fmt" - "net/http" - "testing" - - "git.pubmatic.com/vastunwrap/config" - unWrapCfg "git.pubmatic.com/vastunwrap/config" - "github.com/golang/mock/gomock" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/adapters" - mock_stats "github.com/prebid/prebid-server/modules/pubmatic/vastunwrap/stats/mock" - - "github.com/stretchr/testify/assert" -) - -func TestDoUnwrap(t *testing.T) { - ctrl := gomock.NewController(t) - defer ctrl.Finish() - mockMetricsEngine := mock_stats.NewMockMetricsEngine(ctrl) - type args struct { - module VastUnwrapModule - statsEnabled bool - bid *adapters.TypedBid - userAgent string - ip string - unwrapDefaultTimeout int - url string - wantAdM bool - } - tests := []struct { - name string - args args - setup func() - unwrapRequest func(w http.ResponseWriter, r *http.Request) - }{ - { - name: "doUnwrap for adtype video with Empty Bid", - args: args{ - module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1000}}, MetricsEngine: mockMetricsEngine}, - bid: &adapters.TypedBid{ - Bid: &openrtb2.Bid{}, - }, - userAgent: "testUA", - url: UnwrapURL, - }, - }, - { - name: "doUnwrap for adtype video with Empty ADM", - args: args{ - module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1000}}, MetricsEngine: mockMetricsEngine}, - bid: &adapters.TypedBid{ - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }, - userAgent: "testUA", - url: UnwrapURL, - }, - }, - { - name: "doUnwrap for adtype video with invalid URL and timeout", - args: args{ - module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 2}}, MetricsEngine: mockMetricsEngine}, - bid: &adapters.TypedBid{ - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: vastXMLAdM, - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }, - userAgent: "testUA", - url: "testURL", - }, - setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "2") - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) - }, - unwrapRequest: func(w http.ResponseWriter, req *http.Request) { - w.Header().Add("unwrap-status", "2") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(vastXMLAdM)) - }, - }, - { - name: "doUnwrap for adtype video", - args: args{ - module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1500}}, MetricsEngine: mockMetricsEngine}, - bid: &adapters.TypedBid{ - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: vastXMLAdM, - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }, - userAgent: "testUA", - url: UnwrapURL, - wantAdM: true, - }, - setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "0") - mockMetricsEngine.EXPECT().RecordWrapperCount("5890", "pubmatic", "1") - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) - mockMetricsEngine.EXPECT().RecordUnwrapRespTime("5890", "1", gomock.Any()) - }, - unwrapRequest: func(w http.ResponseWriter, req *http.Request) { - w.Header().Add("unwrap-status", "0") - w.Header().Add("unwrap-count", "1") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(inlineXMLAdM)) - }, - }, - { - name: "doUnwrap for adtype video with invalid vast xml", - args: args{ - module: VastUnwrapModule{Cfg: config.VastUnWrapCfg{MaxWrapperSupport: 5, StatConfig: unWrapCfg.StatConfig{Endpoint: "http://10.172.141.13:8080", PublishInterval: 1}, APPConfig: config.AppConfig{UnwrapDefaultTimeout: 1000}}, MetricsEngine: mockMetricsEngine}, - bid: &adapters.TypedBid{ - Bid: &openrtb2.Bid{ - ID: "Bid-123", - ImpID: fmt.Sprintf("div-adunit-%d", 123), - Price: 2.1, - AdM: invalidVastXMLAdM, - CrID: "Cr-234", - W: 100, - H: 50, - }, - BidType: "video", - }, - userAgent: "testUA", - url: UnwrapURL, - wantAdM: false, - }, - setup: func() { - mockMetricsEngine.EXPECT().RecordRequestStatus("5890", "pubmatic", "1") - mockMetricsEngine.EXPECT().RecordRequestTime("5890", "pubmatic", gomock.Any()) - }, - unwrapRequest: func(w http.ResponseWriter, req *http.Request) { - w.Header().Add("unwrap-status", "1") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(invalidVastXMLAdM)) - }, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if tt.setup != nil { - tt.setup() - } - m := VastUnwrapModule{ - Cfg: tt.args.module.Cfg, - Enabled: true, - MetricsEngine: mockMetricsEngine, - unwrapRequest: tt.unwrapRequest, - } - m.doUnwrapandUpdateBid(tt.args.statsEnabled, tt.args.bid, tt.args.userAgent, tt.args.ip, tt.args.url, "5890", "pubmatic") - if tt.args.bid.Bid.AdM != "" && tt.args.wantAdM { - assert.Equal(t, inlineXMLAdM, tt.args.bid.Bid.AdM, "AdM is not updated correctly after executing RawBidderResponse hook.") - } - }) - } -} diff --git a/openrtb_ext/alternatebiddercodes.go b/openrtb_ext/alternatebiddercodes.go index 49291a3e5e8..3dcb6c781fa 100644 --- a/openrtb_ext/alternatebiddercodes.go +++ b/openrtb_ext/alternatebiddercodes.go @@ -1,6 +1,9 @@ package openrtb_ext -import "fmt" +import ( + "fmt" + "strings" +) // ExtAlternateBidderCodes defines list of alternate bidder codes allowed by adatpers. This overrides host level configs. type ExtAlternateBidderCodes struct { @@ -14,7 +17,7 @@ type ExtAdapterAlternateBidderCodes struct { } func (bidderCodes *ExtAlternateBidderCodes) IsValidBidderCode(bidder, alternateBidder string) (bool, error) { - if alternateBidder == "" || bidder == alternateBidder { + if alternateBidder == "" || strings.EqualFold(bidder, alternateBidder) { return true, nil } @@ -26,8 +29,8 @@ func (bidderCodes *ExtAlternateBidderCodes) IsValidBidderCode(bidder, alternateB return false, alternateBidderNotDefinedError(bidder, alternateBidder) } - adapterCfg, ok := bidderCodes.Bidders[bidder] - if !ok { + adapterCfg, found := bidderCodes.IsBidderInAlternateBidderCodes(bidder) + if !found { return false, alternateBidderNotDefinedError(bidder, alternateBidder) } @@ -56,3 +59,23 @@ func alternateBidderDisabledError(bidder, alternateBidder string) error { func alternateBidderNotDefinedError(bidder, alternateBidder string) error { return fmt.Errorf("alternateBidderCodes not defined for adapter %q, rejecting bids for %q", bidder, alternateBidder) } + +// IsBidderInAlternateBidderCodes tries to find bidder in the altBidderCodes.Bidders map in a case sensitive +// manner first. If no match is found it'll try it in a case insensitive way in linear time +func (bidderCodes *ExtAlternateBidderCodes) IsBidderInAlternateBidderCodes(bidder string) (ExtAdapterAlternateBidderCodes, bool) { + if len(bidder) > 0 && bidderCodes != nil && len(bidderCodes.Bidders) > 0 { + // try constant time exact match + if adapterCfg, found := bidderCodes.Bidders[bidder]; found { + return adapterCfg, true + } + + // check if we can find with a case insensitive comparison + for bidderName, adapterCfg := range bidderCodes.Bidders { + if strings.EqualFold(bidder, bidderName) { + return adapterCfg, true + } + } + } + + return ExtAdapterAlternateBidderCodes{}, false +} diff --git a/openrtb_ext/alternatebiddercodes_test.go b/openrtb_ext/alternatebiddercodes_test.go index 438aebad559..f9229b13d2a 100644 --- a/openrtb_ext/alternatebiddercodes_test.go +++ b/openrtb_ext/alternatebiddercodes_test.go @@ -35,6 +35,14 @@ func TestAlternateBidderCodes_IsValidBidderCode(t *testing.T) { }, wantIsValid: true, }, + { + name: "alternateBidder and bidder are the same under Unicode case-folding (default non-extra bid case with seat's alternateBidder explicitly set)", + args: args{ + bidder: "pubmatic", + alternateBidder: "pubmatic", + }, + wantIsValid: true, + }, { name: "account.alternatebiddercodes config not defined (default, reject bid)", args: args{ @@ -98,6 +106,20 @@ func TestAlternateBidderCodes_IsValidBidderCode(t *testing.T) { }, wantIsValid: true, }, + { + name: "bidder is different in casing than the entry in account.alternatebiddercodes but they match because our case insensitive comparison", + args: args{ + bidder: "PUBmatic", + alternateBidder: "groupm", + }, + fields: fields{ + Enabled: true, + Bidders: map[string]ExtAdapterAlternateBidderCodes{ + "pubmatic": {Enabled: true}, + }, + }, + wantIsValid: true, + }, { name: "allowedBidderCodes is *", args: args{ @@ -178,3 +200,129 @@ func TestAlternateBidderCodes_IsValidBidderCode(t *testing.T) { }) } } + +func TestIsBidderInAlternateBidderCodes(t *testing.T) { + type testInput struct { + bidder string + bidderCodes *ExtAlternateBidderCodes + } + type testOutput struct { + adapterCfg ExtAdapterAlternateBidderCodes + found bool + } + testCases := []struct { + desc string + in testInput + expected testOutput + }{ + { + desc: "empty bidder", + in: testInput{ + bidderCodes: &ExtAlternateBidderCodes{}, + }, + expected: testOutput{ + adapterCfg: ExtAdapterAlternateBidderCodes{}, + found: false, + }, + }, + { + desc: "nil ExtAlternateBidderCodes", + in: testInput{ + bidder: "appnexus", + bidderCodes: nil, + }, + expected: testOutput{ + adapterCfg: ExtAdapterAlternateBidderCodes{}, + found: false, + }, + }, + { + desc: "nil ExtAlternateBidderCodes.Bidder map", + in: testInput{ + bidder: "appnexus", + bidderCodes: &ExtAlternateBidderCodes{}, + }, + expected: testOutput{ + adapterCfg: ExtAdapterAlternateBidderCodes{}, + found: false, + }, + }, + { + desc: "nil ExtAlternateBidderCodes.Bidder map", + in: testInput{ + bidder: "appnexus", + bidderCodes: &ExtAlternateBidderCodes{ + Bidders: nil, + }, + }, + expected: testOutput{ + adapterCfg: ExtAdapterAlternateBidderCodes{}, + found: false, + }, + }, + { + desc: "bidder arg identical to entry in Bidders map", + in: testInput{ + bidder: "appnexus", + bidderCodes: &ExtAlternateBidderCodes{ + Bidders: map[string]ExtAdapterAlternateBidderCodes{ + "appnexus": { + Enabled: true, + AllowedBidderCodes: []string{"abcCode"}, + }, + }, + }, + }, + expected: testOutput{ + adapterCfg: ExtAdapterAlternateBidderCodes{ + Enabled: true, + AllowedBidderCodes: []string{"abcCode"}, + }, + found: true, + }, + }, + { + desc: "bidder arg matches an entry in Bidders map with case insensitive comparisson", + in: testInput{ + bidder: "appnexus", + bidderCodes: &ExtAlternateBidderCodes{ + Bidders: map[string]ExtAdapterAlternateBidderCodes{ + "AppNexus": {AllowedBidderCodes: []string{"adnxsCode"}}, + "PubMatic": {AllowedBidderCodes: []string{"pubCode"}}, + "Rubicon": {AllowedBidderCodes: []string{"rCode"}}, + }, + }, + }, + expected: testOutput{ + adapterCfg: ExtAdapterAlternateBidderCodes{ + AllowedBidderCodes: []string{"adnxsCode"}, + }, + found: true, + }, + }, + { + desc: "bidder arg doesn't match any entry in map", + in: testInput{ + bidder: "unknown", + bidderCodes: &ExtAlternateBidderCodes{ + Bidders: map[string]ExtAdapterAlternateBidderCodes{ + "AppNexus": {AllowedBidderCodes: []string{"adnxsCode"}}, + "PubMatic": {AllowedBidderCodes: []string{"pubCode"}}, + "Rubicon": {AllowedBidderCodes: []string{"rCode"}}, + }, + }, + }, + expected: testOutput{ + adapterCfg: ExtAdapterAlternateBidderCodes{}, + found: false, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + adapterCfg, found := tc.in.bidderCodes.IsBidderInAlternateBidderCodes(tc.in.bidder) + assert.Equal(t, tc.expected.adapterCfg, adapterCfg) + assert.Equal(t, tc.expected.found, found) + }) + } +} diff --git a/openrtb_ext/bid.go b/openrtb_ext/bid.go index aec4219e7df..8805a9f2350 100644 --- a/openrtb_ext/bid.go +++ b/openrtb_ext/bid.go @@ -7,8 +7,8 @@ import ( // ExtBid defines the contract for bidresponse.seatbid.bid[i].ext type ExtBid struct { - Prebid *ExtBidPrebid `json:"prebid,omitempty"` - Bidder json.RawMessage `json:"bidder,omitempty"` + DSA *ExtBidDSA `json:"dsa,omitempty"` + Prebid *ExtBidPrebid `json:"prebid,omitempty"` } // ExtBidPrebid defines the contract for bidresponse.seatbid.bid[i].ext.prebid @@ -86,6 +86,13 @@ type ExtBidPrebidEvents struct { Imp string `json:"imp,omitempty"` } +// ExtBidDSA defines the contract for bidresponse.seatbid.bid[i].ext.dsa +type ExtBidDSA struct { + AdRender *int8 `json:"adrender,omitempty"` + Behalf string `json:"behalf,omitempty"` + Paid string `json:"paid,omitempty"` +} + // BidType describes the allowed values for bidresponse.seatbid.bid[i].ext.prebid.type type BidType string diff --git a/openrtb_ext/bid_request_video.go b/openrtb_ext/bid_request_video.go index d9505fb6257..fbaf1dbb54c 100644 --- a/openrtb_ext/bid_request_video.go +++ b/openrtb_ext/bid_request_video.go @@ -1,6 +1,6 @@ package openrtb_ext -import "github.com/prebid/openrtb/v19/openrtb2" +import "github.com/prebid/openrtb/v20/openrtb2" type BidRequestVideo struct { // Attribute: diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 9d2cd68a4a7..163c893415d 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -22,8 +22,8 @@ var coreBidderNames []BidderName = []BidderName{ BidderAax, BidderAceex, BidderAcuityAds, + BidderAdelement, BidderAdf, - BidderAdform, BidderAdgeneration, BidderAdhese, BidderAdkernel, @@ -39,7 +39,6 @@ var coreBidderNames []BidderName = []BidderName{ BidderAdquery, BidderAdrino, BidderAdsinteractive, - BidderAdsyield, BidderAdtarget, BidderAdtrgtme, BidderAdtelligent, @@ -50,11 +49,10 @@ var coreBidderNames []BidderName = []BidderName{ BidderAidem, BidderAJA, BidderAlgorix, + BidderAlkimi, BidderAMX, BidderApacdex, - BidderApplogy, BidderAppnexus, - BidderAppstock, BidderAppush, BidderAudienceNetwork, BidderAutomatad, @@ -77,6 +75,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderBmtm, BidderBoldwin, BidderBrave, + BidderBWX, BidderCadentApertureMX, BidderCcx, BidderCoinzilla, @@ -85,7 +84,6 @@ var coreBidderNames []BidderName = []BidderName{ BidderConnectAd, BidderConsumable, BidderConversant, - BidderCopper6, BidderCpmstar, BidderCriteo, BidderCWire, @@ -94,28 +92,24 @@ var coreBidderNames []BidderName = []BidderName{ BidderDeepintent, BidderDefinemedia, BidderDianomi, + BidderEdge226, BidderDmx, + BidderDXKulture, BidderEmtv, BidderEmxDigital, - BidderEngageBDR, BidderEPlanning, BidderEpom, - BidderEpsilon, BidderEVolution, - BidderEvtech, BidderFlipp, BidderFreewheelSSP, - BidderFreewheelSSPOld, BidderFRVRAdNetwork, BidderGamma, BidderGamoshi, BidderGlobalsun, BidderGothamads, - BidderGreedygame, BidderGrid, BidderGumGum, BidderHuaweiAds, - BidderIionads, BidderImds, BidderImpactify, BidderImprovedigital, @@ -123,9 +117,9 @@ var coreBidderNames []BidderName = []BidderName{ BidderInMobi, BidderInteractiveoffers, BidderInvibes, + BidderIQX, BidderIQZone, BidderIx, - BidderJANet, BidderJixie, BidderKargo, BidderKayzen, @@ -133,7 +127,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderKiviads, BidderLmKiviads, BidderKrushmedia, - BidderKubient, + BidderLemmadigital, BidderLiftoff, BidderLimelightDigital, BidderLockerDome, @@ -147,13 +141,13 @@ var coreBidderNames []BidderName = []BidderName{ BidderMedianet, BidderMgid, BidderMgidX, + BidderMinuteMedia, BidderMobfoxpb, BidderMobileFuse, BidderMotorik, - BidderNanoInteractive, BidderNextMillennium, - BidderNinthDecimal, BidderNoBid, + BidderOms, BidderOneTag, BidderOpenWeb, BidderOpenx, @@ -162,15 +156,13 @@ var coreBidderNames []BidderName = []BidderName{ BidderOutbrain, BidderOwnAdx, BidderPangle, - BidderPGAM, BidderPGAMSsp, BidderPubmatic, BidderPubnative, BidderPulsepoint, BidderPWBid, - BidderQuantumdex, + BidderRelevantDigital, BidderRevcontent, - BidderRhythmone, BidderRichaudience, BidderRise, BidderRTBHouse, @@ -185,49 +177,45 @@ var coreBidderNames []BidderName = []BidderName{ BidderSmartAdserver, BidderSmartHub, BidderSmartRTB, + BidderSmartx, BidderSmartyAds, BidderSmileWanted, BidderSonobi, BidderSovrn, + BidderSovrnXsp, BidderSspBC, BidderSpotX, - BidderStreamkey, BidderStroeerCore, - BidderSuntContent, - BidderSynacormedia, BidderTaboola, BidderTappx, + BidderTeads, BidderTelaria, BidderTpmn, BidderTrafficGate, BidderTriplelift, BidderTripleliftNative, - BidderTrustX, BidderUcfunnel, BidderUndertone, BidderUnicorn, BidderUnruly, - BidderValueImpression, BidderVASTBidder, BidderVideoByte, BidderVideoHeroes, BidderVidoomy, - BidderViewdeos, BidderVisibleMeasures, BidderVisx, BidderVox, BidderVrtcal, BidderXeworks, - BidderXtrmqb, BidderYahooAds, - BidderYahooAdvertising, - BidderYahooSSP, + BidderYandex, BidderYeahmobi, BidderYieldlab, BidderYieldmo, BidderYieldone, BidderZeroClickFraud, BidderZetaGlobalSsp, + BidderZmaticoo, } func GetAliasBidderToParent() map[BidderName]BidderName { @@ -241,13 +229,10 @@ func SetAliasBidderName(aliasBidderName string, parentBidderName BidderName) err aliasBidder := BidderName(aliasBidderName) coreBidderNames = append(coreBidderNames, aliasBidder) aliasBidderToParent[aliasBidder] = parentBidderName + bidderNameLookup[strings.ToLower(aliasBidderName)] = aliasBidder return nil } -func (name BidderName) MarshalJSON() ([]byte, error) { - return []byte(name), nil -} - func (name *BidderName) String() string { if name == nil { return "" @@ -319,8 +304,8 @@ const ( BidderAax BidderName = "aax" BidderAceex BidderName = "aceex" BidderAcuityAds BidderName = "acuityads" + BidderAdelement BidderName = "adelement" BidderAdf BidderName = "adf" - BidderAdform BidderName = "adform" BidderAdgeneration BidderName = "adgeneration" BidderAdhese BidderName = "adhese" BidderAdkernel BidderName = "adkernel" @@ -336,7 +321,6 @@ const ( BidderAdquery BidderName = "adquery" BidderAdrino BidderName = "adrino" BidderAdsinteractive BidderName = "adsinteractive" - BidderAdsyield BidderName = "adsyield" BidderAdtarget BidderName = "adtarget" BidderAdtrgtme BidderName = "adtrgtme" BidderAdtelligent BidderName = "adtelligent" @@ -347,11 +331,10 @@ const ( BidderAidem BidderName = "aidem" BidderAJA BidderName = "aja" BidderAlgorix BidderName = "algorix" + BidderAlkimi BidderName = "alkimi" BidderAMX BidderName = "amx" BidderApacdex BidderName = "apacdex" - BidderApplogy BidderName = "applogy" BidderAppnexus BidderName = "appnexus" - BidderAppstock BidderName = "appstock" BidderAppush BidderName = "appush" BidderAudienceNetwork BidderName = "audienceNetwork" BidderAutomatad BidderName = "automatad" @@ -374,6 +357,7 @@ const ( BidderBmtm BidderName = "bmtm" BidderBoldwin BidderName = "boldwin" BidderBrave BidderName = "brave" + BidderBWX BidderName = "bwx" BidderCadentApertureMX BidderName = "cadent_aperture_mx" BidderCcx BidderName = "ccx" BidderCoinzilla BidderName = "coinzilla" @@ -382,7 +366,6 @@ const ( BidderConnectAd BidderName = "connectad" BidderConsumable BidderName = "consumable" BidderConversant BidderName = "conversant" - BidderCopper6 BidderName = "copper6" BidderCpmstar BidderName = "cpmstar" BidderCriteo BidderName = "criteo" BidderCWire BidderName = "cwire" @@ -391,28 +374,24 @@ const ( BidderDeepintent BidderName = "deepintent" BidderDefinemedia BidderName = "definemedia" BidderDianomi BidderName = "dianomi" + BidderEdge226 BidderName = "edge226" BidderDmx BidderName = "dmx" + BidderDXKulture BidderName = "dxkulture" BidderEmtv BidderName = "emtv" BidderEmxDigital BidderName = "emx_digital" - BidderEngageBDR BidderName = "engagebdr" BidderEPlanning BidderName = "eplanning" - BidderEpsilon BidderName = "epsilon" BidderEpom BidderName = "epom" BidderEVolution BidderName = "e_volution" - BidderEvtech BidderName = "evtech" BidderFlipp BidderName = "flipp" BidderFreewheelSSP BidderName = "freewheelssp" - BidderFreewheelSSPOld BidderName = "freewheel-ssp" BidderFRVRAdNetwork BidderName = "frvradn" BidderGamma BidderName = "gamma" BidderGamoshi BidderName = "gamoshi" BidderGlobalsun BidderName = "globalsun" BidderGothamads BidderName = "gothamads" - BidderGreedygame BidderName = "greedygame" BidderGrid BidderName = "grid" BidderGumGum BidderName = "gumgum" BidderHuaweiAds BidderName = "huaweiads" - BidderIionads BidderName = "iionads" BidderImds BidderName = "imds" BidderImpactify BidderName = "impactify" BidderImprovedigital BidderName = "improvedigital" @@ -420,9 +399,9 @@ const ( BidderInMobi BidderName = "inmobi" BidderInteractiveoffers BidderName = "interactiveoffers" BidderInvibes BidderName = "invibes" + BidderIQX BidderName = "iqx" BidderIQZone BidderName = "iqzone" BidderIx BidderName = "ix" - BidderJANet BidderName = "janet" BidderJixie BidderName = "jixie" BidderKargo BidderName = "kargo" BidderKayzen BidderName = "kayzen" @@ -430,7 +409,7 @@ const ( BidderKiviads BidderName = "kiviads" BidderLmKiviads BidderName = "lm_kiviads" BidderKrushmedia BidderName = "krushmedia" - BidderKubient BidderName = "kubient" + BidderLemmadigital BidderName = "lemmadigital" BidderLiftoff BidderName = "liftoff" BidderLimelightDigital BidderName = "limelightDigital" BidderLockerDome BidderName = "lockerdome" @@ -444,13 +423,13 @@ const ( BidderMedianet BidderName = "medianet" BidderMgid BidderName = "mgid" BidderMgidX BidderName = "mgidX" + BidderMinuteMedia BidderName = "minutemedia" BidderMobfoxpb BidderName = "mobfoxpb" BidderMobileFuse BidderName = "mobilefuse" BidderMotorik BidderName = "motorik" - BidderNanoInteractive BidderName = "nanointeractive" BidderNextMillennium BidderName = "nextmillennium" - BidderNinthDecimal BidderName = "ninthdecimal" BidderNoBid BidderName = "nobid" + BidderOms BidderName = "oms" BidderOneTag BidderName = "onetag" BidderOpenWeb BidderName = "openweb" BidderOpenx BidderName = "openx" @@ -459,15 +438,13 @@ const ( BidderOutbrain BidderName = "outbrain" BidderOwnAdx BidderName = "ownadx" BidderPangle BidderName = "pangle" - BidderPGAM BidderName = "pgam" BidderPGAMSsp BidderName = "pgamssp" BidderPubmatic BidderName = "pubmatic" BidderPubnative BidderName = "pubnative" BidderPulsepoint BidderName = "pulsepoint" BidderPWBid BidderName = "pwbid" - BidderQuantumdex BidderName = "quantumdex" + BidderRelevantDigital BidderName = "relevantdigital" BidderRevcontent BidderName = "revcontent" - BidderRhythmone BidderName = "rhythmone" BidderRichaudience BidderName = "richaudience" BidderRise BidderName = "rise" BidderRTBHouse BidderName = "rtbhouse" @@ -482,24 +459,24 @@ const ( BidderSmartAdserver BidderName = "smartadserver" BidderSmartHub BidderName = "smarthub" BidderSmartRTB BidderName = "smartrtb" + BidderSmartx BidderName = "smartx" BidderSmartyAds BidderName = "smartyads" BidderSmileWanted BidderName = "smilewanted" BidderSonobi BidderName = "sonobi" BidderSovrn BidderName = "sovrn" + BidderSovrnXsp BidderName = "sovrnXsp" BidderSspBC BidderName = "sspBC" BidderSpotX BidderName = "spotx" BidderStreamkey BidderName = "streamkey" BidderStroeerCore BidderName = "stroeerCore" - BidderSuntContent BidderName = "suntContent" - BidderSynacormedia BidderName = "synacormedia" BidderTaboola BidderName = "taboola" BidderTappx BidderName = "tappx" + BidderTeads BidderName = "teads" BidderTelaria BidderName = "telaria" BidderTpmn BidderName = "tpmn" BidderTrafficGate BidderName = "trafficgate" BidderTriplelift BidderName = "triplelift" BidderTripleliftNative BidderName = "triplelift_native" - BidderTrustX BidderName = "trustx" BidderUcfunnel BidderName = "ucfunnel" BidderUndertone BidderName = "undertone" BidderUnicorn BidderName = "unicorn" @@ -509,22 +486,20 @@ const ( BidderVideoByte BidderName = "videobyte" BidderVideoHeroes BidderName = "videoheroes" BidderVidoomy BidderName = "vidoomy" - BidderViewdeos BidderName = "viewdeos" BidderVisibleMeasures BidderName = "visiblemeasures" BidderVisx BidderName = "visx" BidderVox BidderName = "vox" BidderVrtcal BidderName = "vrtcal" BidderXeworks BidderName = "xeworks" - BidderXtrmqb BidderName = "xtrmqb" BidderYahooAds BidderName = "yahooAds" - BidderYahooAdvertising BidderName = "yahooAdvertising" - BidderYahooSSP BidderName = "yahoossp" + BidderYandex BidderName = "yandex" BidderYeahmobi BidderName = "yeahmobi" BidderYieldlab BidderName = "yieldlab" BidderYieldmo BidderName = "yieldmo" BidderYieldone BidderName = "yieldone" BidderZeroClickFraud BidderName = "zeroclickfraud" BidderZetaGlobalSsp BidderName = "zeta_global_ssp" + BidderZmaticoo BidderName = "zmaticoo" ) // CoreBidderNames returns a slice of all core bidders. @@ -568,14 +543,23 @@ var bidderNameLookup = func() map[string]BidderName { lookup[bidderNameLower] = name } return lookup -} +}() func NormalizeBidderName(name string) (BidderName, bool) { nameLower := strings.ToLower(name) - bidderName, exists := bidderNameLookup()[nameLower] + bidderName, exists := bidderNameLookup[nameLower] return bidderName, exists } +// NormalizeBidderNameOrUnchanged returns the normalized name of known bidders, otherwise returns +// the name exactly as provided. +func NormalizeBidderNameOrUnchanged(name string) BidderName { + if normalized, exists := NormalizeBidderName(name); exists { + return normalized + } + return BidderName(name) +} + // The BidderParamValidator is used to enforce bidrequest.imp[i].ext.prebid.bidder.{anyBidder} values. // // This is treated differently from the other types because we rely on JSON-schemas to validate bidder params. @@ -634,6 +618,7 @@ func NewBidderParamsValidator(schemaDirectory string) (BidderParamValidator, err if _, ok := bidderMap[bidderName]; !ok { return nil, fmt.Errorf("File %s/%s does not match a valid BidderName.", schemaDirectory, fileInfo.Name()) } + toOpen, err := paramsValidator.abs(filepath.Join(schemaDirectory, fileInfo.Name())) if err != nil { return nil, fmt.Errorf("Failed to get an absolute representation of the path: %s, %v", toOpen, err) @@ -653,7 +638,7 @@ func NewBidderParamsValidator(schemaDirectory string) (BidderParamValidator, err schemaContents[BidderName(bidderName)] = string(fileBytes) } - //set alias bidder params schema to its parent + // set alias bidder params schema to its parent for alias, parent := range aliasBidderToParent { parentSchema := schemas[parent] schemas[alias] = parentSchema diff --git a/openrtb_ext/bidders_test.go b/openrtb_ext/bidders_test.go index 2176c28e184..98404a205b1 100644 --- a/openrtb_ext/bidders_test.go +++ b/openrtb_ext/bidders_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "errors" "os" + "strings" "testing" "testing/fstest" @@ -148,6 +149,7 @@ func TestSetAliasBidderName(t *testing.T) { } else { assert.Contains(t, CoreBidderNames(), BidderName(test.aliasBidderName)) assert.Contains(t, aliasBidderToParent, BidderName(test.aliasBidderName)) + assert.Contains(t, bidderNameLookup, strings.ToLower(test.aliasBidderName)) } } diff --git a/openrtb_ext/bidders_validate_test.go b/openrtb_ext/bidders_validate_test.go index 33148d1ed41..66de818311d 100644 --- a/openrtb_ext/bidders_validate_test.go +++ b/openrtb_ext/bidders_validate_test.go @@ -50,7 +50,7 @@ func TestBidderUniquenessGatekeeping(t *testing.T) { // - Exclude duplicates of adapters for the same bidder, as it's unlikely a publisher will use both. var bidders []string for _, bidder := range CoreBidderNames() { - if bidder != BidderSilverPush && bidder != BidderTripleliftNative && bidder != BidderAdkernelAdn && bidder != BidderFreewheelSSPOld && bidder != BidderYahooAdvertising { + if bidder != BidderSilverPush && bidder != BidderTripleliftNative && bidder != BidderAdkernelAdn && bidder != "freewheel-ssp" && bidder != "yahooAdvertising" { bidders = append(bidders, string(bidder)) } } diff --git a/openrtb_ext/convert_down.go b/openrtb_ext/convert_down.go index 573ef64fdc0..e0842978551 100644 --- a/openrtb_ext/convert_down.go +++ b/openrtb_ext/convert_down.go @@ -36,6 +36,7 @@ func ConvertDownTo25(r *RequestWrapper) error { clear26Fields(r) clear202211Fields(r) clear202303Fields(r) + clear202309Fields(r) return nil } @@ -308,3 +309,27 @@ func clear202303Fields(r *RequestWrapper) { } } } + +// clear202309Fields sets all fields introduced in OpenRTB 2.6-202309 to default values +// which will cause them to be omitted during json marshal. +func clear202309Fields(r *RequestWrapper) { + r.ACat = nil + + for _, imp := range r.GetImp() { + if audio := imp.Audio; audio != nil { + audio.DurFloors = nil + } + + if video := imp.Video; video != nil { + video.DurFloors = nil + } + + if pmp := imp.PMP; pmp != nil { + for i := range pmp.Deals { + pmp.Deals[i].Guar = 0 + pmp.Deals[i].MinCPMPerSec = 0 + pmp.Deals[i].DurFloors = nil + } + } + } +} diff --git a/openrtb_ext/convert_down_test.go b/openrtb_ext/convert_down_test.go index a7761a0a18e..1bf112dcb3a 100644 --- a/openrtb_ext/convert_down_test.go +++ b/openrtb_ext/convert_down_test.go @@ -4,8 +4,10 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -14,7 +16,7 @@ func TestConvertDownTo25(t *testing.T) { name string givenRequest openrtb2.BidRequest expectedRequest openrtb2.BidRequest - expectedErr string + expectedErrType error }{ { name: "2.6-to-2.5", @@ -60,13 +62,23 @@ func TestConvertDownTo25(t *testing.T) { name: "2.6-202303-dropped", // integration with clear202303Fields givenRequest: openrtb2.BidRequest{ ID: "anyID", - Imp: []openrtb2.Imp{{ID: "1", Refresh: &openrtb2.Refresh{Count: 1}}}, + Imp: []openrtb2.Imp{{ID: "1", Refresh: &openrtb2.Refresh{Count: ptrutil.ToPtr(1)}}}, }, expectedRequest: openrtb2.BidRequest{ ID: "anyID", Imp: []openrtb2.Imp{{ID: "1"}}, }, }, + { + name: "2.6-202309-dropped", // integration with clear202309Fields + givenRequest: openrtb2.BidRequest{ + ID: "anyID", + ACat: []string{"anyACat"}, + }, + expectedRequest: openrtb2.BidRequest{ + ID: "anyID", + }, + }, { name: "2.6-to-2.5-OtherExtFields", givenRequest: openrtb2.BidRequest{ @@ -87,12 +99,12 @@ func TestConvertDownTo25(t *testing.T) { }, }, { - name: "malformed-shhain", + name: "malformed-schain", givenRequest: openrtb2.BidRequest{ ID: "anyID", Source: &openrtb2.Source{SChain: &openrtb2.SupplyChain{Complete: 1, Nodes: []openrtb2.SupplyChainNode{}, Ver: "2"}, Ext: json.RawMessage(`malformed`)}, }, - expectedErr: "invalid character 'm' looking for beginning of value", + expectedErrType: &errortypes.FailedToUnmarshal{}, }, { name: "malformed-gdpr", @@ -100,7 +112,7 @@ func TestConvertDownTo25(t *testing.T) { ID: "anyID", Regs: &openrtb2.Regs{GDPR: openrtb2.Int8Ptr(1), Ext: json.RawMessage(`malformed`)}, }, - expectedErr: "invalid character 'm' looking for beginning of value", + expectedErrType: &errortypes.FailedToUnmarshal{}, }, { name: "malformed-consent", @@ -108,7 +120,7 @@ func TestConvertDownTo25(t *testing.T) { ID: "anyID", User: &openrtb2.User{Consent: "1", Ext: json.RawMessage(`malformed`)}, }, - expectedErr: "invalid character 'm' looking for beginning of value", + expectedErrType: &errortypes.FailedToUnmarshal{}, }, { name: "malformed-usprivacy", @@ -116,7 +128,7 @@ func TestConvertDownTo25(t *testing.T) { ID: "anyID", Regs: &openrtb2.Regs{USPrivacy: "3", Ext: json.RawMessage(`malformed`)}, }, - expectedErr: "invalid character 'm' looking for beginning of value", + expectedErrType: &errortypes.FailedToUnmarshal{}, }, { name: "malformed-eid", @@ -124,7 +136,7 @@ func TestConvertDownTo25(t *testing.T) { ID: "anyID", User: &openrtb2.User{EIDs: []openrtb2.EID{{Source: "42"}}, Ext: json.RawMessage(`malformed`)}, }, - expectedErr: "invalid character 'm' looking for beginning of value", + expectedErrType: &errortypes.FailedToUnmarshal{}, }, { name: "malformed-imp", @@ -132,7 +144,7 @@ func TestConvertDownTo25(t *testing.T) { ID: "anyID", Imp: []openrtb2.Imp{{Rwdd: 1, Ext: json.RawMessage(`malformed`)}}, }, - expectedErr: "invalid character 'm' looking for beginning of value", + expectedErrType: &errortypes.FailedToUnmarshal{}, }, } @@ -141,8 +153,8 @@ func TestConvertDownTo25(t *testing.T) { w := &RequestWrapper{BidRequest: &test.givenRequest} err := ConvertDownTo25(w) - if len(test.expectedErr) > 0 { - assert.EqualError(t, err, test.expectedErr, "error") + if test.expectedErrType != nil { + assert.IsType(t, test.expectedErrType, err) } else { assert.NoError(t, w.RebuildRequest(), "error") assert.Equal(t, test.expectedRequest, *w.BidRequest, "result") @@ -162,7 +174,7 @@ func TestMoveSupplyChainFrom26To25(t *testing.T) { name string givenRequest openrtb2.BidRequest expectedRequest openrtb2.BidRequest - expectedErr string + expectedErrType error }{ { name: "notpresent-source", @@ -185,9 +197,9 @@ func TestMoveSupplyChainFrom26To25(t *testing.T) { expectedRequest: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: schain1Json}}, }, { - name: "malformed", - givenRequest: openrtb2.BidRequest{Source: &openrtb2.Source{SChain: schain1, Ext: json.RawMessage(`malformed`)}}, - expectedErr: "invalid character 'm' looking for beginning of value", + name: "malformed", + givenRequest: openrtb2.BidRequest{Source: &openrtb2.Source{SChain: schain1, Ext: json.RawMessage(`malformed`)}}, + expectedErrType: &errortypes.FailedToUnmarshal{}, }, } @@ -196,8 +208,8 @@ func TestMoveSupplyChainFrom26To25(t *testing.T) { w := &RequestWrapper{BidRequest: &test.givenRequest} err := moveSupplyChainFrom26To25(w) - if len(test.expectedErr) > 0 { - assert.EqualError(t, err, test.expectedErr, "error") + if test.expectedErrType != nil { + assert.IsType(t, test.expectedErrType, err) } else { assert.NoError(t, w.RebuildRequest(), "error") assert.Equal(t, test.expectedRequest, *w.BidRequest, "result") @@ -211,7 +223,7 @@ func TestMoveGDPRFrom26To25(t *testing.T) { name string givenRequest openrtb2.BidRequest expectedRequest openrtb2.BidRequest - expectedErr string + expectedErrType error }{ { name: "notpresent-regs", @@ -234,9 +246,9 @@ func TestMoveGDPRFrom26To25(t *testing.T) { expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gdpr":0}`)}}, }, { - name: "malformed", - givenRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{GDPR: openrtb2.Int8Ptr(0), Ext: json.RawMessage(`malformed`)}}, - expectedErr: "invalid character 'm' looking for beginning of value", + name: "malformed", + givenRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{GDPR: openrtb2.Int8Ptr(0), Ext: json.RawMessage(`malformed`)}}, + expectedErrType: &errortypes.FailedToUnmarshal{}, }, } @@ -245,8 +257,8 @@ func TestMoveGDPRFrom26To25(t *testing.T) { w := &RequestWrapper{BidRequest: &test.givenRequest} err := moveGDPRFrom26To25(w) - if len(test.expectedErr) > 0 { - assert.EqualError(t, err, test.expectedErr, "error") + if test.expectedErrType != nil { + assert.IsType(t, test.expectedErrType, err) } else { assert.NoError(t, w.RebuildRequest(), "error") assert.Equal(t, test.expectedRequest, *w.BidRequest, "result") @@ -260,7 +272,7 @@ func TestMoveConsentFrom26To25(t *testing.T) { name string givenRequest openrtb2.BidRequest expectedRequest openrtb2.BidRequest - expectedErr string + expectedErrType error }{ { name: "notpresent-user", @@ -283,9 +295,9 @@ func TestMoveConsentFrom26To25(t *testing.T) { expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage(`{"consent":"1"}`)}}, }, { - name: "malformed", - givenRequest: openrtb2.BidRequest{User: &openrtb2.User{Consent: "1", Ext: json.RawMessage(`malformed`)}}, - expectedErr: "invalid character 'm' looking for beginning of value", + name: "malformed", + givenRequest: openrtb2.BidRequest{User: &openrtb2.User{Consent: "1", Ext: json.RawMessage(`malformed`)}}, + expectedErrType: &errortypes.FailedToUnmarshal{}, }, } @@ -294,8 +306,8 @@ func TestMoveConsentFrom26To25(t *testing.T) { w := &RequestWrapper{BidRequest: &test.givenRequest} err := moveConsentFrom26To25(w) - if len(test.expectedErr) > 0 { - assert.EqualError(t, err, test.expectedErr, "error") + if test.expectedErrType != nil { + assert.IsType(t, test.expectedErrType, err) } else { assert.NoError(t, w.RebuildRequest(), "error") assert.Equal(t, test.expectedRequest, *w.BidRequest, "result") @@ -309,7 +321,7 @@ func TestMoveUSPrivacyFrom26To25(t *testing.T) { name string givenRequest openrtb2.BidRequest expectedRequest openrtb2.BidRequest - expectedErr string + expectedErrType error }{ { name: "notpresent-regs", @@ -332,9 +344,9 @@ func TestMoveUSPrivacyFrom26To25(t *testing.T) { expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"1"}`)}}, }, { - name: "malformed", - givenRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{USPrivacy: "1", Ext: json.RawMessage(`malformed`)}}, - expectedErr: "invalid character 'm' looking for beginning of value", + name: "malformed", + givenRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{USPrivacy: "1", Ext: json.RawMessage(`malformed`)}}, + expectedErrType: &errortypes.FailedToUnmarshal{}, }, } @@ -343,8 +355,8 @@ func TestMoveUSPrivacyFrom26To25(t *testing.T) { w := &RequestWrapper{BidRequest: &test.givenRequest} err := moveUSPrivacyFrom26To25(w) - if len(test.expectedErr) > 0 { - assert.EqualError(t, err, test.expectedErr, "error") + if test.expectedErrType != nil { + assert.IsType(t, test.expectedErrType, err) } else { assert.NoError(t, w.RebuildRequest(), "error") assert.Equal(t, test.expectedRequest, *w.BidRequest, "result") @@ -364,7 +376,7 @@ func TestMoveEIDFrom26To25(t *testing.T) { name string givenRequest openrtb2.BidRequest expectedRequest openrtb2.BidRequest - expectedErr string + expectedErrType error }{ { name: "notpresent-user", @@ -392,9 +404,9 @@ func TestMoveEIDFrom26To25(t *testing.T) { expectedRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: eid1Json}}, }, { - name: "malformed", - givenRequest: openrtb2.BidRequest{User: &openrtb2.User{EIDs: eid1, Ext: json.RawMessage(`malformed`)}}, - expectedErr: "invalid character 'm' looking for beginning of value", + name: "malformed", + givenRequest: openrtb2.BidRequest{User: &openrtb2.User{EIDs: eid1, Ext: json.RawMessage(`malformed`)}}, + expectedErrType: &errortypes.FailedToUnmarshal{}, }, } @@ -403,8 +415,8 @@ func TestMoveEIDFrom26To25(t *testing.T) { w := &RequestWrapper{BidRequest: &test.givenRequest} err := moveEIDFrom26To25(w) - if len(test.expectedErr) > 0 { - assert.EqualError(t, err, test.expectedErr, "error") + if test.expectedErrType != nil { + assert.IsType(t, test.expectedErrType, err) } else { assert.NoError(t, w.RebuildRequest(), "error") assert.Equal(t, test.expectedRequest, *w.BidRequest, "result") @@ -415,10 +427,10 @@ func TestMoveEIDFrom26To25(t *testing.T) { func TestMoveRewardedFrom26ToPrebidExt(t *testing.T) { testCases := []struct { - name string - givenImp openrtb2.Imp - expectedImp openrtb2.Imp - expectedErr string + name string + givenImp openrtb2.Imp + expectedImp openrtb2.Imp + expectedErrType error }{ { name: "notpresent-prebid", @@ -436,9 +448,9 @@ func TestMoveRewardedFrom26ToPrebidExt(t *testing.T) { expectedImp: openrtb2.Imp{Ext: json.RawMessage(`{"prebid":{"is_rewarded_inventory":1}}`)}, }, { - name: "Malformed", - givenImp: openrtb2.Imp{Rwdd: 1, Ext: json.RawMessage(`malformed`)}, - expectedErr: "invalid character 'm' looking for beginning of value", + name: "Malformed", + givenImp: openrtb2.Imp{Rwdd: 1, Ext: json.RawMessage(`malformed`)}, + expectedErrType: &errortypes.FailedToUnmarshal{}, }, } @@ -447,10 +459,10 @@ func TestMoveRewardedFrom26ToPrebidExt(t *testing.T) { w := &ImpWrapper{Imp: &test.givenImp} err := moveRewardedFrom26ToPrebidExt(w) - if len(test.expectedErr) > 0 { - assert.EqualError(t, err, test.expectedErr, "error") + if test.expectedErrType != nil { + assert.IsType(t, test.expectedErrType, err) } else { - assert.NoError(t, w.RebuildImpressionExt(), "error") + assert.NoError(t, w.RebuildImp(), "error") assert.Equal(t, test.expectedImp, *w.Imp, "result") } }) @@ -682,7 +694,7 @@ func TestClear202303Fields(t *testing.T) { { ID: "imp1", Video: &openrtb2.Video{PodID: "1", Plcmt: adcom1.VideoPlcmtInstream}, - Refresh: &openrtb2.Refresh{Count: 1}, + Refresh: &openrtb2.Refresh{Count: ptrutil.ToPtr(1)}, }, }, } @@ -701,3 +713,50 @@ func TestClear202303Fields(t *testing.T) { clear202303Fields(r) assert.Equal(t, expected, given) } + +func TestClear202309Fields(t *testing.T) { + givenDurFloors := []openrtb2.DurFloors{{MinDur: 15, MaxDur: 30, BidFloor: 100}} + + given := openrtb2.BidRequest{ + ID: "anyID", + ACat: []string{"acat1", "acat2"}, + Imp: []openrtb2.Imp{ + { + ID: "imp1", + Audio: &openrtb2.Audio{PodID: "1", DurFloors: givenDurFloors}, + }, + { + ID: "imp2", + Video: &openrtb2.Video{PodID: "2", DurFloors: givenDurFloors}, + PMP: &openrtb2.PMP{ + PrivateAuction: 1, + Deals: []openrtb2.Deal{ + {ID: "deal1", BidFloor: 200, Guar: 1, MinCPMPerSec: 2, DurFloors: givenDurFloors}}, + }, + }, + }, + } + + expected := openrtb2.BidRequest{ + ID: "anyID", + Imp: []openrtb2.Imp{ + { + ID: "imp1", + Audio: &openrtb2.Audio{PodID: "1"}, + }, + { + ID: "imp2", + Video: &openrtb2.Video{PodID: "2"}, + PMP: &openrtb2.PMP{ + PrivateAuction: 1, + Deals: []openrtb2.Deal{ + {ID: "deal1", BidFloor: 200}}, + }, + }, + }, + } + + r := &RequestWrapper{BidRequest: &given} + clear202309Fields(r) + assert.Equal(t, expected, given) +} diff --git a/openrtb_ext/convert_up.go b/openrtb_ext/convert_up.go index e8116cad11c..2cfb07fb071 100644 --- a/openrtb_ext/convert_up.go +++ b/openrtb_ext/convert_up.go @@ -3,7 +3,7 @@ package openrtb_ext import ( "fmt" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" ) func ConvertUpTo26(r *RequestWrapper) error { diff --git a/openrtb_ext/convert_up_test.go b/openrtb_ext/convert_up_test.go index 942df166cd1..f83b85be897 100644 --- a/openrtb_ext/convert_up_test.go +++ b/openrtb_ext/convert_up_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" ) @@ -20,7 +20,7 @@ func TestConvertUpTo26(t *testing.T) { givenRequest: openrtb2.BidRequest{ Ext: json.RawMessage(`malformed`), }, - expectedErr: "req.ext is invalid: invalid character 'm' looking for beginning of value", + expectedErr: "req.ext is invalid: expect { or n, but found m", }, { description: "2.4 -> 2.6", @@ -120,27 +120,27 @@ func TestConvertUpEnsureExt(t *testing.T) { { description: "Ext", givenRequest: openrtb2.BidRequest{Ext: json.RawMessage("malformed")}, - expectedErr: "req.ext is invalid: invalid character 'm' looking for beginning of value", + expectedErr: "req.ext is invalid: expect { or n, but found m", }, { description: "Source.Ext", givenRequest: openrtb2.BidRequest{Source: &openrtb2.Source{Ext: json.RawMessage("malformed")}}, - expectedErr: "req.source.ext is invalid: invalid character 'm' looking for beginning of value", + expectedErr: "req.source.ext is invalid: expect { or n, but found m", }, { description: "Regs.Ext", givenRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage("malformed")}}, - expectedErr: "req.regs.ext is invalid: invalid character 'm' looking for beginning of value", + expectedErr: "req.regs.ext is invalid: expect { or n, but found m", }, { description: "User.Ext", givenRequest: openrtb2.BidRequest{User: &openrtb2.User{Ext: json.RawMessage("malformed")}}, - expectedErr: "req.user.ext is invalid: invalid character 'm' looking for beginning of value", + expectedErr: "req.user.ext is invalid: expect { or n, but found m", }, { description: "Imp.Ext", givenRequest: openrtb2.BidRequest{Imp: []openrtb2.Imp{{Ext: json.RawMessage("malformed")}}}, - expectedErr: "imp[0].imp.ext is invalid: invalid character 'm' looking for beginning of value", + expectedErr: "imp[0].imp.ext is invalid: expect { or n, but found m", }, } @@ -444,7 +444,7 @@ func TestMoveRewardedFromPrebidExtTo26(t *testing.T) { for _, test := range testCases { w := &ImpWrapper{Imp: &test.givenImp} moveRewardedFromPrebidExtTo26(w) - assert.NoError(t, w.RebuildImpressionExt(), test.description) + assert.NoError(t, w.RebuildImp(), test.description) assert.Equal(t, test.expectedImp, *w.Imp, test.description) } } diff --git a/openrtb_ext/deal_tier.go b/openrtb_ext/deal_tier.go index 45285d21663..e96ca4b8473 100644 --- a/openrtb_ext/deal_tier.go +++ b/openrtb_ext/deal_tier.go @@ -1,9 +1,8 @@ package openrtb_ext import ( - "encoding/json" - - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) // DealTier defines the configuration of a deal tier. @@ -34,12 +33,16 @@ func ReadDealTiersFromImp(imp openrtb2.Imp) (DealTierBidderMap, error) { } `json:"bidder"` } `json:"prebid"` } - if err := json.Unmarshal(imp.Ext, &impPrebidExt); err != nil { + if err := jsonutil.Unmarshal(imp.Ext, &impPrebidExt); err != nil { return nil, err } for bidder, param := range impPrebidExt.Prebid.Bidders { if param.DealTier != nil { - dealTiers[BidderName(bidder)] = *param.DealTier + if bidderNormalized, bidderFound := NormalizeBidderName(bidder); bidderFound { + dealTiers[bidderNormalized] = *param.DealTier + } else { + dealTiers[BidderName(bidder)] = *param.DealTier + } } } diff --git a/openrtb_ext/deal_tier_test.go b/openrtb_ext/deal_tier_test.go index 0046b788ece..5407dd2af38 100644 --- a/openrtb_ext/deal_tier_test.go +++ b/openrtb_ext/deal_tier_test.go @@ -4,90 +4,118 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/errortypes" "github.com/stretchr/testify/assert" ) func TestReadDealTiersFromImp(t *testing.T) { testCases := []struct { - description string - impExt json.RawMessage - expectedResult DealTierBidderMap - expectedError string + description string + impExt json.RawMessage + expectedResult DealTierBidderMap + expectedErrorType error }{ { - description: "Nil", + description: "nil", impExt: nil, expectedResult: DealTierBidderMap{}, }, { - description: "None", + description: "none", impExt: json.RawMessage(``), expectedResult: DealTierBidderMap{}, }, { - description: "Empty Object", + description: "empty_object", impExt: json.RawMessage(`{}`), expectedResult: DealTierBidderMap{}, }, { - description: "imp.ext - no prebid but with other params", + description: "imp.ext_no_prebid_but_with_other_params", impExt: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "anyPrefix"}, "placementId": 12345}, "tid": "1234"}`), expectedResult: DealTierBidderMap{}, }, { - description: "imp.ext.prebid - nil", + description: "imp.ext.prebid_nil", impExt: json.RawMessage(`{"prebid": null}`), expectedResult: DealTierBidderMap{}, }, { - description: "imp.ext.prebid - empty", + description: "imp.ext.prebid_empty", impExt: json.RawMessage(`{"prebid": {}}`), expectedResult: DealTierBidderMap{}, }, { - description: "imp.ext.prebid - no bidder but with other params", + description: "imp.ext.prebid_no bidder but with other params", impExt: json.RawMessage(`{"prebid": {"supportdeals": true}}`), expectedResult: DealTierBidderMap{}, }, { - description: "imp.ext.prebid.bidder - one", + description: "imp.ext.prebid.bidder_one", impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "anyPrefix"}, "placementId": 12345}}}}`), expectedResult: DealTierBidderMap{BidderAppnexus: {Prefix: "anyPrefix", MinDealTier: 5}}, }, { - description: "imp.ext.prebid.bidder - one with other params", + description: "imp.ext.prebid.bidder_one_but_not_found_in_the_adapter_bidder_list", + impExt: json.RawMessage(`{"prebid": {"bidder": {"unknown": {"dealTier": {"minDealTier": 5, "prefix": "anyPrefix"}, "placementId": 12345}}}}`), + expectedResult: DealTierBidderMap{"unknown": {Prefix: "anyPrefix", MinDealTier: 5}}, + }, + { + description: "imp.ext.prebid.bidder_one_but_not_found_in_the_adapter_bidder_list_with_case_insensitive", + impExt: json.RawMessage(`{"prebid": {"bidder": {"UnKnOwn": {"dealTier": {"minDealTier": 5, "prefix": "anyPrefix"}, "placementId": 12345}}}}`), + expectedResult: DealTierBidderMap{"UnKnOwn": {Prefix: "anyPrefix", MinDealTier: 5}}, + }, + { + description: "imp.ext.prebid.bidder_one_but_case_is_different_from_the_adapter_bidder_list", + impExt: json.RawMessage(`{"prebid": {"bidder": {"APpNExUS": {"dealTier": {"minDealTier": 5, "prefix": "anyPrefix"}, "placementId": 12345}}}}`), + expectedResult: DealTierBidderMap{BidderAppnexus: {Prefix: "anyPrefix", MinDealTier: 5}}, + }, + { + description: "imp.ext.prebid.bidder_one_with_other_params", impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "anyPrefix"}, "placementId": 12345}}, "supportdeals": true}, "tid": "1234"}`), expectedResult: DealTierBidderMap{BidderAppnexus: {Prefix: "anyPrefix", MinDealTier: 5}}, }, { - description: "imp.ext.prebid.bidder - multiple", + description: "imp.ext.prebid.bidder_multiple", impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "appnexusPrefix"}, "placementId": 12345}, "rubicon": {"dealTier": {"minDealTier": 8, "prefix": "rubiconPrefix"}, "placementId": 12345}}}}`), expectedResult: DealTierBidderMap{BidderAppnexus: {Prefix: "appnexusPrefix", MinDealTier: 5}, BidderRubicon: {Prefix: "rubiconPrefix", MinDealTier: 8}}, }, { - description: "imp.ext.prebid.bidder - one without deal tier", + description: "imp.ext.prebid.bidder_one_without_deal_tier", impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"placementId": 12345}}}}`), expectedResult: DealTierBidderMap{}, }, { - description: "imp.ext.prebid.bidder - error", - impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": "wrong type", "placementId": 12345}}}}`), - expectedError: "json: cannot unmarshal string into Go struct field .prebid.bidder.dealTier of type openrtb_ext.DealTier", + description: "imp.ext.prebid.bidder_error", + impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": "wrong type", "placementId": 12345}}}}`), + expectedErrorType: &errortypes.FailedToUnmarshal{}, }, } for _, test := range testCases { - imp := openrtb2.Imp{Ext: test.impExt} + t.Run(test.description, func(t *testing.T) { - result, err := ReadDealTiersFromImp(imp) + imp := openrtb2.Imp{Ext: test.impExt} + result, err := ReadDealTiersFromImp(imp) - assert.Equal(t, test.expectedResult, result, test.description+":result") + assert.Equal(t, test.expectedResult, result) - if len(test.expectedError) == 0 { - assert.NoError(t, err, test.description+":error") - } else { - assert.EqualError(t, err, test.expectedError, test.description+":error") - } + if test.expectedErrorType != nil { + assert.IsType(t, test.expectedErrorType, err) + } else { + assert.NoError(t, err) + } + }) } + + t.Run("imp.ext.prebid.bidder_dedupe", func(t *testing.T) { + impExt := json.RawMessage(`{"prebid": {"bidder": {"APPNEXUS": {"dealTier": {"minDealTier": 100}},"APpNExUS": {"dealTier": {"minDealTier": 5}}}}}`) + imp := openrtb2.Imp{Ext: impExt} + result, err := ReadDealTiersFromImp(imp) + + assert.Len(t, result, 1) + assert.NotNil(t, result["appnexus"]) + assert.NoError(t, err) + }) } diff --git a/openrtb_ext/device.go b/openrtb_ext/device.go index f5b0556980b..f8be08eef78 100644 --- a/openrtb_ext/device.go +++ b/openrtb_ext/device.go @@ -6,7 +6,7 @@ import ( "strconv" "github.com/buger/jsonparser" - "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/v2/errortypes" ) // PrebidExtKey represents the prebid extension key used in requests diff --git a/openrtb_ext/device_test.go b/openrtb_ext/device_test.go index 1a3dbe8e2f4..f40e9650061 100644 --- a/openrtb_ext/device_test.go +++ b/openrtb_ext/device_test.go @@ -4,30 +4,31 @@ import ( "encoding/json" "testing" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" ) func TestInvalidDeviceExt(t *testing.T) { var s ExtDevice - assert.EqualError(t, json.Unmarshal([]byte(`{"prebid":{"interstitial":{"minheightperc":0}}}`), &s), "request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100") - assert.EqualError(t, json.Unmarshal([]byte(`{"prebid":{"interstitial":{"minwidthperc":105}}}`), &s), "request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100") - assert.EqualError(t, json.Unmarshal([]byte(`{"prebid":{"interstitial":{"minwidthperc":true,"minheightperc":0}}}`), &s), "request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100") - assert.EqualError(t, json.Unmarshal([]byte(`{"prebid":{"interstitial":{"minwidthperc":null,"minheightperc":0}}}`), &s), "request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100") - assert.EqualError(t, json.Unmarshal([]byte(`{"prebid":{"interstitial":{"minwidthperc":"75","minheightperc":0}}}`), &s), "request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"prebid":{"interstitial":{"minheightperc":0}}}`), &s), "request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"prebid":{"interstitial":{"minwidthperc":105}}}`), &s), "request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"prebid":{"interstitial":{"minwidthperc":true,"minheightperc":0}}}`), &s), "request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"prebid":{"interstitial":{"minwidthperc":null,"minheightperc":0}}}`), &s), "request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"prebid":{"interstitial":{"minwidthperc":"75","minheightperc":0}}}`), &s), "request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100") - assert.EqualError(t, json.Unmarshal([]byte(`{"prebid":{"interstitial":{"minwidthperc":85}}}`), &s), "request.device.ext.prebid.interstitial.minheightperc must be a number between 0 and 100") - assert.EqualError(t, json.Unmarshal([]byte(`{"prebid":{"interstitial":{"minwidthperc":85,"minheightperc":-5}}}`), &s), "request.device.ext.prebid.interstitial.minheightperc must be a number between 0 and 100") - assert.EqualError(t, json.Unmarshal([]byte(`{"prebid":{"interstitial":{"minwidthperc":85,"minheightperc":false}}}`), &s), "request.device.ext.prebid.interstitial.minheightperc must be a number between 0 and 100") - assert.EqualError(t, json.Unmarshal([]byte(`{"prebid":{"interstitial":{"minwidthperc":85,"minheightperc":"75"}}}`), &s), "request.device.ext.prebid.interstitial.minheightperc must be a number between 0 and 100") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"prebid":{"interstitial":{"minwidthperc":85}}}`), &s), "request.device.ext.prebid.interstitial.minheightperc must be a number between 0 and 100") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"prebid":{"interstitial":{"minwidthperc":85,"minheightperc":-5}}}`), &s), "request.device.ext.prebid.interstitial.minheightperc must be a number between 0 and 100") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"prebid":{"interstitial":{"minwidthperc":85,"minheightperc":false}}}`), &s), "request.device.ext.prebid.interstitial.minheightperc must be a number between 0 and 100") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"prebid":{"interstitial":{"minwidthperc":85,"minheightperc":"75"}}}`), &s), "request.device.ext.prebid.interstitial.minheightperc must be a number between 0 and 100") } func TestValidDeviceExt(t *testing.T) { var s ExtDevice - assert.NoError(t, json.Unmarshal([]byte(`{"prebid":{}}`), &s)) + assert.NoError(t, jsonutil.UnmarshalValid([]byte(`{"prebid":{}}`), &s)) assert.Nil(t, s.Prebid.Interstitial) - assert.NoError(t, json.Unmarshal([]byte(`{}`), &s)) + assert.NoError(t, jsonutil.UnmarshalValid([]byte(`{}`), &s)) assert.Nil(t, s.Prebid.Interstitial) - assert.NoError(t, json.Unmarshal([]byte(`{"prebid":{"interstitial":{"minwidthperc":75,"minheightperc":60}}}`), &s)) + assert.NoError(t, jsonutil.UnmarshalValid([]byte(`{"prebid":{"interstitial":{"minwidthperc":75,"minheightperc":60}}}`), &s)) assert.EqualValues(t, 75, s.Prebid.Interstitial.MinWidthPerc) assert.EqualValues(t, 60, s.Prebid.Interstitial.MinHeightPerc) } diff --git a/openrtb_ext/floors.go b/openrtb_ext/floors.go index aeec3647736..a63b60e32cd 100644 --- a/openrtb_ext/floors.go +++ b/openrtb_ext/floors.go @@ -1,5 +1,11 @@ package openrtb_ext +import ( + "github.com/prebid/prebid-server/v2/util/maputil" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/prebid/prebid-server/v2/util/sliceutil" +) + // Defines strings for FetchStatus const ( FetchSuccess = "success" @@ -82,9 +88,8 @@ type PriceFloorData struct { ModelTimestamp int `json:"modeltimestamp,omitempty"` ModelGroups []PriceFloorModelGroup `json:"modelgroups,omitempty"` FloorProvider string `json:"floorprovider,omitempty"` - UseFetchDataRate *int `json:"usefetchdatarate,omitempty"` + FetchRate *int `json:"fetchrate,omitempty"` } - type PriceFloorModelGroup struct { Currency string `json:"currency,omitempty"` ModelWeight *int `json:"modelweight,omitempty"` @@ -146,3 +151,59 @@ type ExtImp struct { type ImpExtPrebid struct { Floors Price `json:"floors,omitempty"` } + +func (pf *PriceFloorRules) DeepCopy() *PriceFloorRules { + if pf == nil { + return nil + } + + newRules := *pf + newRules.Enabled = ptrutil.Clone(pf.Enabled) + newRules.Skipped = ptrutil.Clone(pf.Skipped) + newRules.Location = ptrutil.Clone(pf.Location) + newRules.Data = pf.Data.DeepCopy() + newRules.Enforcement = pf.Enforcement.DeepCopy() + + return &newRules +} + +func (data *PriceFloorData) DeepCopy() *PriceFloorData { + if data == nil { + return nil + } + + newData := *data + newModelGroups := make([]PriceFloorModelGroup, len(data.ModelGroups)) + + for i := range data.ModelGroups { + var eachGroup PriceFloorModelGroup + eachGroup.Currency = data.ModelGroups[i].Currency + eachGroup.ModelWeight = ptrutil.Clone(data.ModelGroups[i].ModelWeight) + eachGroup.ModelVersion = data.ModelGroups[i].ModelVersion + eachGroup.SkipRate = data.ModelGroups[i].SkipRate + eachGroup.Values = maputil.Clone(data.ModelGroups[i].Values) + eachGroup.Default = data.ModelGroups[i].Default + eachGroup.Schema = PriceFloorSchema{ + Fields: sliceutil.Clone(data.ModelGroups[i].Schema.Fields), + Delimiter: data.ModelGroups[i].Schema.Delimiter, + } + newModelGroups[i] = eachGroup + } + newData.ModelGroups = newModelGroups + + return &newData +} + +func (enforcement *PriceFloorEnforcement) DeepCopy() *PriceFloorEnforcement { + if enforcement == nil { + return nil + } + + newEnforcement := *enforcement + newEnforcement.EnforceJS = ptrutil.Clone(enforcement.EnforceJS) + newEnforcement.EnforcePBS = ptrutil.Clone(enforcement.EnforcePBS) + newEnforcement.FloorDeals = ptrutil.Clone(enforcement.FloorDeals) + newEnforcement.BidAdjustment = ptrutil.Clone(enforcement.BidAdjustment) + + return &newEnforcement +} diff --git a/openrtb_ext/floors_test.go b/openrtb_ext/floors_test.go index 20247736768..687101bfa3a 100644 --- a/openrtb_ext/floors_test.go +++ b/openrtb_ext/floors_test.go @@ -1,8 +1,10 @@ package openrtb_ext import ( + "reflect" "testing" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -218,3 +220,263 @@ func TestPriceFloorRulesGetEnabled(t *testing.T) { }) } } + +func TestPriceFloorRulesDeepCopy(t *testing.T) { + type fields struct { + FloorMin float64 + FloorMinCur string + SkipRate int + Location *PriceFloorEndpoint + Data *PriceFloorData + Enforcement *PriceFloorEnforcement + Enabled *bool + Skipped *bool + FloorProvider string + FetchStatus string + PriceFloorLocation string + } + tests := []struct { + name string + fields fields + }{ + { + name: "DeepCopy does not share same reference", + fields: fields{ + FloorMin: 10, + FloorMinCur: "INR", + SkipRate: 0, + Location: &PriceFloorEndpoint{ + URL: "https://test/floors", + }, + Data: &PriceFloorData{ + Currency: "INR", + SkipRate: 0, + ModelGroups: []PriceFloorModelGroup{ + { + Currency: "INR", + ModelWeight: ptrutil.ToPtr(1), + SkipRate: 0, + Values: map[string]float64{ + "banner|300x600|www.website5.com": 20, + "*|*|*": 50, + }, + Schema: PriceFloorSchema{ + Fields: []string{"mediaType", "size", "domain"}, + Delimiter: "|", + }, + }, + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pf := &PriceFloorRules{ + FloorMin: tt.fields.FloorMin, + FloorMinCur: tt.fields.FloorMinCur, + SkipRate: tt.fields.SkipRate, + Location: tt.fields.Location, + Data: tt.fields.Data, + Enforcement: tt.fields.Enforcement, + Enabled: tt.fields.Enabled, + Skipped: tt.fields.Skipped, + FloorProvider: tt.fields.FloorProvider, + FetchStatus: tt.fields.FetchStatus, + PriceFloorLocation: tt.fields.PriceFloorLocation, + } + got := pf.DeepCopy() + if got == pf { + t.Errorf("Rules reference are same") + } + if got.Data == pf.Data { + t.Errorf("Floor data reference is same") + } + }) + } +} + +func TestFloorRulesDeepCopy(t *testing.T) { + type fields struct { + FloorMin float64 + FloorMinCur string + SkipRate int + Location *PriceFloorEndpoint + Data *PriceFloorData + Enforcement *PriceFloorEnforcement + Enabled *bool + Skipped *bool + FloorProvider string + FetchStatus string + PriceFloorLocation string + } + tests := []struct { + name string + fields fields + want *PriceFloorRules + }{ + { + name: "Copy entire floors object", + fields: fields{ + FloorMin: 10, + FloorMinCur: "INR", + SkipRate: 0, + Location: &PriceFloorEndpoint{ + URL: "http://prebid.com/floor", + }, + Data: &PriceFloorData{ + Currency: "INR", + SkipRate: 0, + FloorsSchemaVersion: 2, + ModelTimestamp: 123, + ModelGroups: []PriceFloorModelGroup{ + { + Currency: "INR", + ModelWeight: ptrutil.ToPtr(50), + ModelVersion: "version 1", + SkipRate: 0, + Schema: PriceFloorSchema{ + Fields: []string{"a", "b", "c"}, + Delimiter: "|", + }, + Values: map[string]float64{ + "*|*|*": 20, + }, + Default: 1, + }, + }, + FloorProvider: "prebid", + }, + Enforcement: &PriceFloorEnforcement{ + EnforceJS: ptrutil.ToPtr(true), + EnforcePBS: ptrutil.ToPtr(true), + FloorDeals: ptrutil.ToPtr(true), + BidAdjustment: ptrutil.ToPtr(true), + EnforceRate: 100, + }, + Enabled: ptrutil.ToPtr(true), + Skipped: ptrutil.ToPtr(false), + FloorProvider: "Prebid", + FetchStatus: "success", + PriceFloorLocation: "fetch", + }, + want: &PriceFloorRules{ + FloorMin: 10, + FloorMinCur: "INR", + SkipRate: 0, + Location: &PriceFloorEndpoint{ + URL: "http://prebid.com/floor", + }, + Data: &PriceFloorData{ + Currency: "INR", + SkipRate: 0, + FloorsSchemaVersion: 2, + ModelTimestamp: 123, + ModelGroups: []PriceFloorModelGroup{ + { + Currency: "INR", + ModelWeight: ptrutil.ToPtr(50), + ModelVersion: "version 1", + SkipRate: 0, + Schema: PriceFloorSchema{ + Fields: []string{"a", "b", "c"}, + Delimiter: "|", + }, + Values: map[string]float64{ + "*|*|*": 20, + }, + Default: 1, + }, + }, + FloorProvider: "prebid", + }, + Enforcement: &PriceFloorEnforcement{ + EnforceJS: ptrutil.ToPtr(true), + EnforcePBS: ptrutil.ToPtr(true), + FloorDeals: ptrutil.ToPtr(true), + BidAdjustment: ptrutil.ToPtr(true), + EnforceRate: 100, + }, + Enabled: ptrutil.ToPtr(true), + Skipped: ptrutil.ToPtr(false), + FloorProvider: "Prebid", + FetchStatus: "success", + PriceFloorLocation: "fetch", + }, + }, + { + name: "Copy entire floors object", + fields: fields{ + FloorMin: 10, + FloorMinCur: "INR", + SkipRate: 0, + Location: &PriceFloorEndpoint{ + URL: "http://prebid.com/floor", + }, + Data: nil, + Enforcement: &PriceFloorEnforcement{ + EnforceJS: ptrutil.ToPtr(true), + EnforcePBS: ptrutil.ToPtr(true), + FloorDeals: ptrutil.ToPtr(true), + BidAdjustment: ptrutil.ToPtr(true), + EnforceRate: 100, + }, + Enabled: ptrutil.ToPtr(true), + Skipped: ptrutil.ToPtr(false), + FloorProvider: "Prebid", + FetchStatus: "success", + PriceFloorLocation: "fetch", + }, + want: &PriceFloorRules{ + FloorMin: 10, + FloorMinCur: "INR", + SkipRate: 0, + Location: &PriceFloorEndpoint{ + URL: "http://prebid.com/floor", + }, + Data: nil, + Enforcement: &PriceFloorEnforcement{ + EnforceJS: ptrutil.ToPtr(true), + EnforcePBS: ptrutil.ToPtr(true), + FloorDeals: ptrutil.ToPtr(true), + BidAdjustment: ptrutil.ToPtr(true), + EnforceRate: 100, + }, + Enabled: ptrutil.ToPtr(true), + Skipped: ptrutil.ToPtr(false), + FloorProvider: "Prebid", + FetchStatus: "success", + PriceFloorLocation: "fetch", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + pf := &PriceFloorRules{ + FloorMin: tt.fields.FloorMin, + FloorMinCur: tt.fields.FloorMinCur, + SkipRate: tt.fields.SkipRate, + Location: tt.fields.Location, + Data: tt.fields.Data, + Enforcement: tt.fields.Enforcement, + Enabled: tt.fields.Enabled, + Skipped: tt.fields.Skipped, + FloorProvider: tt.fields.FloorProvider, + FetchStatus: tt.fields.FetchStatus, + PriceFloorLocation: tt.fields.PriceFloorLocation, + } + if got := pf.DeepCopy(); !reflect.DeepEqual(got, tt.want) { + t.Errorf("PriceFloorRules.DeepCopy() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestFloorRuleDeepCopyNil(t *testing.T) { + var priceFloorRule *PriceFloorRules + got := priceFloorRule.DeepCopy() + + if got != nil { + t.Errorf("PriceFloorRules.DeepCopy() = %v, want %v", got, nil) + } +} diff --git a/openrtb_ext/imp_adelement.go b/openrtb_ext/imp_adelement.go new file mode 100644 index 00000000000..7f5316c943f --- /dev/null +++ b/openrtb_ext/imp_adelement.go @@ -0,0 +1,5 @@ +package openrtb_ext + +type ExtAdelement struct { + SupplyId string `json:"supply_id"` +} diff --git a/openrtb_ext/imp_adnuntius.go b/openrtb_ext/imp_adnuntius.go index 86023c48231..49833e90f1d 100644 --- a/openrtb_ext/imp_adnuntius.go +++ b/openrtb_ext/imp_adnuntius.go @@ -5,4 +5,5 @@ type ImpExtAdnunitus struct { Network string `json:"network"` NoCookies bool `json:"noCookies"` MaxDeals int `json:"maxDeals"` + BidType string `json:"bidType,omitempty"` } diff --git a/openrtb_ext/imp_aidem.go b/openrtb_ext/imp_aidem.go index 59457f1eb4a..3fe00b404ff 100644 --- a/openrtb_ext/imp_aidem.go +++ b/openrtb_ext/imp_aidem.go @@ -1,8 +1,8 @@ package openrtb_ext -type ImpExtFoo struct { - SiteID string `json:"siteId"` - PublisherID string `json:"publisherId"` - PlacementID string `json:"placementId"` +type ExtImpAidem struct { + PlacementId string `json:"placementId"` + SiteId string `json:"siteId"` + PublisherId string `json:"publisherId"` RateLimit string `json:"rateLimit"` } diff --git a/openrtb_ext/imp_alkimi.go b/openrtb_ext/imp_alkimi.go new file mode 100644 index 00000000000..640528f212f --- /dev/null +++ b/openrtb_ext/imp_alkimi.go @@ -0,0 +1,9 @@ +package openrtb_ext + +type ExtImpAlkimi struct { + Token string `json:"token"` + BidFloor float64 `json:"bidFloor"` + Instl int8 `json:"instl"` + Exp int64 `json:"exp"` + AdUnitCode string `json:"adUnitCode"` +} diff --git a/openrtb_ext/imp_applogy.go b/openrtb_ext/imp_applogy.go deleted file mode 100644 index 45774a05afb..00000000000 --- a/openrtb_ext/imp_applogy.go +++ /dev/null @@ -1,5 +0,0 @@ -package openrtb_ext - -type ExtImpApplogy struct { - Token string `json:"token"` -} diff --git a/openrtb_ext/imp_appnexus.go b/openrtb_ext/imp_appnexus.go index d9549e74750..db949f661fd 100644 --- a/openrtb_ext/imp_appnexus.go +++ b/openrtb_ext/imp_appnexus.go @@ -5,7 +5,7 @@ import ( "fmt" "strings" - "github.com/prebid/prebid-server/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) // ExtImpAppnexus defines the contract for bidrequest.imp[i].ext.prebid.bidder.appnexus @@ -45,7 +45,7 @@ func (ks *ExtImpAppnexusKeywords) UnmarshalJSON(b []byte) error { switch b[0] { case '{': var results map[string][]string - if err := json.Unmarshal(b, &results); err != nil { + if err := jsonutil.UnmarshalValid(b, &results); err != nil { return err } @@ -64,7 +64,7 @@ func (ks *ExtImpAppnexusKeywords) UnmarshalJSON(b []byte) error { } case '[': var results []extImpAppnexusKeyVal - if err := json.Unmarshal(b, &results); err != nil { + if err := jsonutil.UnmarshalValid(b, &results); err != nil { return err } var kvs strings.Builder @@ -82,7 +82,7 @@ func (ks *ExtImpAppnexusKeywords) UnmarshalJSON(b []byte) error { } case '"': var keywords string - if err := json.Unmarshal(b, &keywords); err != nil { + if err := jsonutil.UnmarshalValid(b, &keywords); err != nil { return err } *ks = ExtImpAppnexusKeywords(keywords) diff --git a/openrtb_ext/imp_appnexus_test.go b/openrtb_ext/imp_appnexus_test.go index cbd6779d5da..9d65f3be3ad 100644 --- a/openrtb_ext/imp_appnexus_test.go +++ b/openrtb_ext/imp_appnexus_test.go @@ -1,9 +1,9 @@ package openrtb_ext import ( - "encoding/json" "testing" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" ) @@ -31,7 +31,7 @@ func TestKeywordsUnmarshalJSON(t *testing.T) { for _, test := range validTestCases { var keywords keywords - assert.NoError(t, json.Unmarshal(test.input, &keywords), test.desc) + assert.NoError(t, jsonutil.UnmarshalValid(test.input, &keywords), test.desc) assert.Equal(t, test.expected, keywords.Keywords.String()) } @@ -42,6 +42,6 @@ func TestKeywordsUnmarshalJSON(t *testing.T) { for _, test := range invalidTestCases { var keywords keywords - assert.Error(t, json.Unmarshal(test.input, &keywords), test.desc) + assert.Error(t, jsonutil.UnmarshalValid(test.input, &keywords), test.desc) } } diff --git a/openrtb_ext/imp_bizzclick.go b/openrtb_ext/imp_bizzclick.go index 5090a822ec5..15a8c2d0d46 100644 --- a/openrtb_ext/imp_bizzclick.go +++ b/openrtb_ext/imp_bizzclick.go @@ -2,5 +2,7 @@ package openrtb_ext type ExtBizzclick struct { AccountID string `json:"accountId"` + SourceID string `json:"sourceId"` + Host string `json:"host"` PlacementID string `json:"placementId"` } diff --git a/openrtb_ext/imp_bwx.go b/openrtb_ext/imp_bwx.go new file mode 100644 index 00000000000..9181beb05b0 --- /dev/null +++ b/openrtb_ext/imp_bwx.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ExtBWX struct { + Env string `json:"env"` + Pid string `json:"pid"` +} diff --git a/openrtb_ext/imp_consumable.go b/openrtb_ext/imp_consumable.go index fe916b09972..8158b41d2cc 100644 --- a/openrtb_ext/imp_consumable.go +++ b/openrtb_ext/imp_consumable.go @@ -6,5 +6,6 @@ type ExtImpConsumable struct { SiteId int `json:"siteId,omitempty"` UnitId int `json:"unitId,omitempty"` /* UnitName gets used as a classname and in the URL when building the ad markup */ - UnitName string `json:"unitName,omitempty"` + UnitName string `json:"unitName,omitempty"` + PlacementId string `json:"placementid,omitempty"` } diff --git a/openrtb_ext/imp_dxkulture.go b/openrtb_ext/imp_dxkulture.go new file mode 100644 index 00000000000..4b507c55248 --- /dev/null +++ b/openrtb_ext/imp_dxkulture.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtImpDXKulture defines the contract for bidrequest.imp[i].ext.prebid.bidder.dxkulture +type ExtImpDXKulture struct { + PublisherId string `json:"publisherId"` + PlacementId string `json:"placementId"` +} diff --git a/openrtb_ext/imp_edge226.go b/openrtb_ext/imp_edge226.go new file mode 100644 index 00000000000..44f3490ee42 --- /dev/null +++ b/openrtb_ext/imp_edge226.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ImpExtEdge226 struct { + PlacementID string `json:"placementId"` + EndpointID string `json:"endpointId"` +} diff --git a/openrtb_ext/imp_engagebdr.go b/openrtb_ext/imp_engagebdr.go deleted file mode 100644 index db500111a78..00000000000 --- a/openrtb_ext/imp_engagebdr.go +++ /dev/null @@ -1,6 +0,0 @@ -package openrtb_ext - -// ExtImpEngageBDR defines the contract for bidrequest.imp[i].ext.prebid.bidder.engagebdr -type ExtImpEngageBDR struct { - Sspid string `json:"sspid"` -} diff --git a/openrtb_ext/imp_freewheelssp.go b/openrtb_ext/imp_freewheelssp.go index 110f018f512..3d015d96722 100644 --- a/openrtb_ext/imp_freewheelssp.go +++ b/openrtb_ext/imp_freewheelssp.go @@ -1,7 +1,7 @@ package openrtb_ext import ( - "github.com/prebid/prebid-server/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) type ImpExtFreewheelSSP struct { diff --git a/openrtb_ext/imp_gumgum.go b/openrtb_ext/imp_gumgum.go index 96a1308d663..f54234fa394 100644 --- a/openrtb_ext/imp_gumgum.go +++ b/openrtb_ext/imp_gumgum.go @@ -3,10 +3,11 @@ package openrtb_ext // ExtImpGumGum defines the contract for bidrequest.imp[i].ext.prebid.bidder.gumgum // Either Zone or PubId must be present, others are optional parameters type ExtImpGumGum struct { - Zone string `json:"zone,omitempty"` - PubID float64 `json:"pubId,omitempty"` - IrisID string `json:"irisid,omitempty"` - Slot float64 `json:"slot,omitempty"` + Zone string `json:"zone,omitempty"` + PubID float64 `json:"pubId,omitempty"` + IrisID string `json:"irisid,omitempty"` + Slot float64 `json:"slot,omitempty"` + Product string `json:"product,omitempty"` } // ExtImpGumGumVideo defines the contract for bidresponse.seatbid.bid[i].ext.prebid.bidder.gumgum.video diff --git a/openrtb_ext/imp_iqx.go b/openrtb_ext/imp_iqx.go new file mode 100644 index 00000000000..0b0358b67e1 --- /dev/null +++ b/openrtb_ext/imp_iqx.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ExtIQX struct { + Env string `json:"env"` + Pid string `json:"pid"` +} diff --git a/openrtb_ext/imp_kubient.go b/openrtb_ext/imp_kubient.go deleted file mode 100644 index 59dd3d2aaab..00000000000 --- a/openrtb_ext/imp_kubient.go +++ /dev/null @@ -1,6 +0,0 @@ -package openrtb_ext - -// ExtImpKubient defines the contract for bidrequest.imp[i].ext.prebid.bidder.kubient -type ExtImpKubient struct { - ZoneID string `json:"zoneid"` -} diff --git a/openrtb_ext/imp_lemmadigital.go b/openrtb_ext/imp_lemmadigital.go new file mode 100644 index 00000000000..c691dd173d9 --- /dev/null +++ b/openrtb_ext/imp_lemmadigital.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ImpExtLemmaDigital struct { + PublisherId int `json:"pid"` + AdId int `json:"aid"` +} diff --git a/openrtb_ext/imp_minutemedia.go b/openrtb_ext/imp_minutemedia.go new file mode 100644 index 00000000000..197f3b0a85b --- /dev/null +++ b/openrtb_ext/imp_minutemedia.go @@ -0,0 +1,6 @@ +package openrtb_ext + +// ImpExtMinuteMedia defines the contract for bidrequest.imp[i].ext.prebid.bidder.minutemedia +type ImpExtMinuteMedia struct { + Org string `json:"org"` +} diff --git a/openrtb_ext/imp_nanointeractive.go b/openrtb_ext/imp_nanointeractive.go deleted file mode 100644 index b381fab8eb7..00000000000 --- a/openrtb_ext/imp_nanointeractive.go +++ /dev/null @@ -1,10 +0,0 @@ -package openrtb_ext - -// ExtImpNanoInteractive defines the contract for bidrequest.imp[i].ext.prebid.bidder.nanointeractive -type ExtImpNanoInteractive struct { - Pid string `json:"pid"` - Nq []string `json:"nq,omitempty"` - Category string `json:"category,omitempty"` - SubId string `json:"subId,omitempty"` - Ref string `json:"ref,omitempty"` -} diff --git a/openrtb_ext/imp_ninthdecimal.go b/openrtb_ext/imp_ninthdecimal.go deleted file mode 100755 index 8fb794dbdf2..00000000000 --- a/openrtb_ext/imp_ninthdecimal.go +++ /dev/null @@ -1,6 +0,0 @@ -package openrtb_ext - -type ExtImpNinthDecimal struct { - PublisherID string `json:"pubid"` - Placement string `json:"placement,omitempty"` -} diff --git a/openrtb_ext/imp_oms.go b/openrtb_ext/imp_oms.go new file mode 100644 index 00000000000..4e12da86264 --- /dev/null +++ b/openrtb_ext/imp_oms.go @@ -0,0 +1,6 @@ +package openrtb_ext + +// ExtImpOms defines the contract for bidrequest.imp[i].ext.prebid.bidder.oms +type ExtImpOms struct { + PublisherID string `json:"pid"` +} diff --git a/openrtb_ext/imp_relevantdigital.go b/openrtb_ext/imp_relevantdigital.go new file mode 100644 index 00000000000..ec250557c2b --- /dev/null +++ b/openrtb_ext/imp_relevantdigital.go @@ -0,0 +1,8 @@ +package openrtb_ext + +type ExtRelevantDigital struct { + AccountId string `json:"accountId"` + PlacementId string `json:"placementId"` + Host string `json:"pbsHost"` + PbsBufferMs int `json:"pbsBufferMs"` +} diff --git a/openrtb_ext/imp_rhythmone.go b/openrtb_ext/imp_rhythmone.go deleted file mode 100644 index 526a2843b53..00000000000 --- a/openrtb_ext/imp_rhythmone.go +++ /dev/null @@ -1,9 +0,0 @@ -package openrtb_ext - -// ExtImpRhythmone defines the contract for bidrequest.imp[i].ext.prebid.bidder.rhythmone -type ExtImpRhythmone struct { - PlacementId string `json:"placementId"` - Zone string `json:"zone"` - Path string `json:"path"` - S2S bool -} diff --git a/openrtb_ext/imp_seedingAlliance.go b/openrtb_ext/imp_seedingAlliance.go index 759683ad6b3..d383ad39d6e 100644 --- a/openrtb_ext/imp_seedingAlliance.go +++ b/openrtb_ext/imp_seedingAlliance.go @@ -1,5 +1,7 @@ package openrtb_ext type ImpExtSeedingAlliance struct { - AdUnitID string `json:"adUnitID"` + AdUnitID string `json:"adUnitId"` + SeatID string `json:"seatId"` + AccountID string `json:"accountId"` } diff --git a/openrtb_ext/imp_smartx.go b/openrtb_ext/imp_smartx.go new file mode 100644 index 00000000000..9a2975b01de --- /dev/null +++ b/openrtb_ext/imp_smartx.go @@ -0,0 +1,10 @@ +package openrtb_ext + +type ExtImpSmartclip struct { + TagID string `json:"tagId"` + PublisherID string `json:"publisherId"` + SiteID string `json:"siteId"` + AppID string `json:"appId"` + BundleID string `json:"bundleId"` + StoreURL string `json:"storeUrl"` +} diff --git a/openrtb_ext/imp_sovrnXsp.go b/openrtb_ext/imp_sovrnXsp.go new file mode 100644 index 00000000000..90c78d707e4 --- /dev/null +++ b/openrtb_ext/imp_sovrnXsp.go @@ -0,0 +1,8 @@ +package openrtb_ext + +type ExtImpSovrnXsp struct { + PubID string `json:"pub_id,omitempty"` + MedID string `json:"med_id,omitempty"` + ZoneID string `json:"zone_id,omitempty"` + ForceBid bool `json:"force_bid,omitempty"` +} diff --git a/openrtb_ext/imp_suntContent.go b/openrtb_ext/imp_suntContent.go deleted file mode 100644 index 5040df7e3b6..00000000000 --- a/openrtb_ext/imp_suntContent.go +++ /dev/null @@ -1,5 +0,0 @@ -package openrtb_ext - -type ImpExtSuntContent struct { - AdUnitID string `json:"adUnitID"` -} diff --git a/openrtb_ext/imp_teads.go b/openrtb_ext/imp_teads.go new file mode 100644 index 00000000000..50d11ae9192 --- /dev/null +++ b/openrtb_ext/imp_teads.go @@ -0,0 +1,5 @@ +package openrtb_ext + +type ExtImpTeads struct { + PlacementID string `json:"placementId"` +} diff --git a/openrtb_ext/imp_yandex.go b/openrtb_ext/imp_yandex.go new file mode 100644 index 00000000000..8439d95f845 --- /dev/null +++ b/openrtb_ext/imp_yandex.go @@ -0,0 +1,16 @@ +package openrtb_ext + +type ExtImpYandex struct { + /* + Possible formats + - `R-I-123456-2` + - `R-123456-1` + - `123456-789` + */ + PlacementID string `json:"placement_id"` + + // Deprecated: in favor of `PlacementID` + PageID int64 `json:"page_id"` + // Deprecated: in favor of `PlacementID` + ImpID int64 `json:"imp_id"` +} diff --git a/openrtb_ext/imp_zmaticoo.go b/openrtb_ext/imp_zmaticoo.go new file mode 100644 index 00000000000..6746493798b --- /dev/null +++ b/openrtb_ext/imp_zmaticoo.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtImpZmaticoo defines the contract for bidrequest.imp[i].ext.prebid.bidder.zmaticoo +type ExtImpZmaticoo struct { + PubId string `json:"pubId"` + ZoneId string `json:"zoneId"` +} diff --git a/openrtb_ext/multibid_test.go b/openrtb_ext/multibid_test.go index a27631d6891..926ab261b0f 100644 --- a/openrtb_ext/multibid_test.go +++ b/openrtb_ext/multibid_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/openrtb_ext/regs.go b/openrtb_ext/regs.go index 56a5179d051..eca5ff98e55 100644 --- a/openrtb_ext/regs.go +++ b/openrtb_ext/regs.go @@ -2,6 +2,8 @@ package openrtb_ext // ExtRegs defines the contract for bidrequest.regs.ext type ExtRegs struct { + // DSA is an object containing DSA transparency information, see https://github.com/InteractiveAdvertisingBureau/openrtb/blob/main/extensions/community_extensions/dsa_transparency.md + DSA *ExtRegsDSA `json:"dsa,omitempty"` // GDPR should be "1" if the caller believes the user is subject to GDPR laws, "0" if not, and undefined // if it's unknown. For more info on this parameter, see: https://iabtechlab.com/wp-content/uploads/2018/02/OpenRTB_Advisory_GDPR_2018-02.pdf @@ -10,3 +12,11 @@ type ExtRegs struct { // USPrivacy should be a four character string, see: https://iabtechlab.com/wp-content/uploads/2019/11/OpenRTB-Extension-U.S.-Privacy-IAB-Tech-Lab.pdf USPrivacy string `json:"us_privacy,omitempty"` } + +// ExtRegsDSA defines the contract for bidrequest.regs.ext.dsa +type ExtRegsDSA struct { + // Required should be a between 0 and 3 inclusive, see https://github.com/InteractiveAdvertisingBureau/openrtb/blob/main/extensions/community_extensions/dsa_transparency.md + Required *int8 `json:"dsarequired,omitempty"` + // PubRender should be between 0 and 2 inclusive, see https://github.com/InteractiveAdvertisingBureau/openrtb/blob/main/extensions/community_extensions/dsa_transparency.md + PubRender *int8 `json:"pubrender,omitempty"` +} diff --git a/openrtb_ext/request.go b/openrtb_ext/request.go index 6d74f37776f..2c5ac8a3650 100644 --- a/openrtb_ext/request.go +++ b/openrtb_ext/request.go @@ -4,10 +4,11 @@ import ( "encoding/json" "fmt" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/util/maputil" - "github.com/prebid/prebid-server/util/ptrutil" - "github.com/prebid/prebid-server/util/sliceutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/maputil" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/prebid/prebid-server/v2/util/sliceutil" ) // FirstPartyDataExtKey defines a field name within request.ext and request.imp.ext reserved for first party data. @@ -210,6 +211,7 @@ type ExtRequestTargeting struct { DurationRangeSec []int `json:"durationrangesec,omitempty"` PreferDeals bool `json:"preferdeals,omitempty"` AppendBidderNames bool `json:"appendbiddernames,omitempty"` + AlwaysIncludeDeals bool `json:"alwaysincludedeals,omitempty"` } type ExtIncludeBrandCategory struct { @@ -248,7 +250,7 @@ func (pg *PriceGranularity) UnmarshalJSON(b []byte) error { // price granularity used to be a string referencing a predefined value, try to parse // and map the legacy string before falling back to the modern custom model. legacyID := "" - if err := json.Unmarshal(b, &legacyID); err == nil { + if err := jsonutil.Unmarshal(b, &legacyID); err == nil { if legacyValue, ok := NewPriceGranularityFromLegacyID(legacyID); ok { *pg = legacyValue return nil @@ -257,7 +259,7 @@ func (pg *PriceGranularity) UnmarshalJSON(b []byte) error { // use a type-alias to avoid calling back into this UnmarshalJSON implementation modernValue := PriceGranularityRaw{} - err := json.Unmarshal(b, &modernValue) + err := jsonutil.Unmarshal(b, &modernValue) if err == nil { *pg = (PriceGranularity)(modernValue) } diff --git a/openrtb_ext/request_test.go b/openrtb_ext/request_test.go index a05bae3a6bf..81a12965307 100644 --- a/openrtb_ext/request_test.go +++ b/openrtb_ext/request_test.go @@ -5,8 +5,9 @@ import ( "fmt" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -37,7 +38,7 @@ func TestGranularityUnmarshal(t *testing.T) { for _, tg := range testGroups { for i, tc := range tg.in { var resolved PriceGranularity - err := json.Unmarshal(tc.json, &resolved) + err := jsonutil.UnmarshalValid(tc.json, &resolved) // Assert validation error if tg.expectError && !assert.Errorf(t, err, "%s test case %d", tg.desc, i) { diff --git a/openrtb_ext/request_wrapper.go b/openrtb_ext/request_wrapper.go index e28ee658b01..81fc6ba374f 100644 --- a/openrtb_ext/request_wrapper.go +++ b/openrtb_ext/request_wrapper.go @@ -4,10 +4,11 @@ import ( "encoding/json" "errors" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/util/maputil" - "github.com/prebid/prebid-server/util/ptrutil" - "github.com/prebid/prebid-server/util/sliceutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/maputil" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/prebid/prebid-server/v2/util/sliceutil" ) // RequestWrapper wraps the OpenRTB request to provide a storage location for unmarshalled ext fields, so they @@ -42,6 +43,7 @@ type RequestWrapper struct { appExt *AppExt regExt *RegExt siteExt *SiteExt + doohExt *DOOHExt sourceExt *SourceExt } @@ -51,12 +53,14 @@ const ( consentedProvidersSettingsListKey = "consented_providers_settings" consentKey = "consent" ampKey = "amp" + dsaKey = "dsa" eidsKey = "eids" gdprKey = "gdpr" prebidKey = "prebid" dataKey = "data" schainKey = "schain" us_privacyKey = "us_privacy" + cdepKey = "cdep" ) // LenImp returns the number of impressions without causing the creation of ImpWrapper objects. @@ -160,6 +164,17 @@ func (rw *RequestWrapper) GetSiteExt() (*SiteExt, error) { return rw.siteExt, rw.siteExt.unmarshal(rw.Site.Ext) } +func (rw *RequestWrapper) GetDOOHExt() (*DOOHExt, error) { + if rw.doohExt != nil { + return rw.doohExt, nil + } + rw.doohExt = &DOOHExt{} + if rw.BidRequest == nil || rw.DOOH == nil || rw.DOOH.Ext == nil { + return rw.doohExt, rw.doohExt.unmarshal(json.RawMessage{}) + } + return rw.doohExt, rw.doohExt.unmarshal(rw.DOOH.Ext) +} + func (rw *RequestWrapper) GetSourceExt() (*SourceExt, error) { if rw.sourceExt != nil { return rw.sourceExt, nil @@ -176,7 +191,7 @@ func (rw *RequestWrapper) RebuildRequest() error { return errors.New("Requestwrapper RebuildRequest called on a nil BidRequest") } - if err := rw.RebuildImp(); err != nil { + if err := rw.rebuildImp(); err != nil { return err } if err := rw.rebuildUserExt(); err != nil { @@ -197,6 +212,9 @@ func (rw *RequestWrapper) RebuildRequest() error { if err := rw.rebuildSiteExt(); err != nil { return err } + if err := rw.rebuildDOOHExt(); err != nil { + return err + } if err := rw.rebuildSourceExt(); err != nil { return err } @@ -204,7 +222,7 @@ func (rw *RequestWrapper) RebuildRequest() error { return nil } -func (rw *RequestWrapper) RebuildImp() error { +func (rw *RequestWrapper) rebuildImp() error { if !rw.impWrappersAccessed { return nil } @@ -216,7 +234,7 @@ func (rw *RequestWrapper) RebuildImp() error { rw.Imp = make([]openrtb2.Imp, len(rw.impWrappers)) for i := range rw.impWrappers { - if err := rw.impWrappers[i].RebuildImpressionExt(); err != nil { + if err := rw.impWrappers[i].RebuildImp(); err != nil { return err } rw.Imp[i] = *rw.impWrappers[i].Imp @@ -224,7 +242,6 @@ func (rw *RequestWrapper) RebuildImp() error { return nil } - func (rw *RequestWrapper) rebuildUserExt() error { if rw.userExt == nil || !rw.userExt.Dirty() { return nil @@ -297,6 +314,25 @@ func (rw *RequestWrapper) rebuildAppExt() error { return nil } +func (rw *RequestWrapper) rebuildDOOHExt() error { + if rw.doohExt == nil || !rw.doohExt.Dirty() { + return nil + } + + doohJson, err := rw.doohExt.marshal() + if err != nil { + return err + } + + if doohJson != nil && rw.DOOH == nil { + rw.DOOH = &openrtb2.DOOH{Ext: doohJson} + } else if rw.DOOH != nil { + rw.DOOH.Ext = doohJson + } + + return nil +} + func (rw *RequestWrapper) rebuildRegExt() error { if rw.regExt == nil || !rw.regExt.Dirty() { return nil @@ -370,6 +406,7 @@ func (rw *RequestWrapper) Clone() *RequestWrapper { clone.appExt = rw.appExt.Clone() clone.regExt = rw.regExt.Clone() clone.siteExt = rw.siteExt.Clone() + clone.doohExt = rw.doohExt.Clone() clone.sourceExt = rw.sourceExt.Clone() return &clone @@ -405,13 +442,13 @@ func (ue *UserExt) unmarshal(extJson json.RawMessage) error { return nil } - if err := json.Unmarshal(extJson, &ue.ext); err != nil { + if err := jsonutil.Unmarshal(extJson, &ue.ext); err != nil { return err } consentJson, hasConsent := ue.ext[consentKey] - if hasConsent { - if err := json.Unmarshal(consentJson, &ue.consent); err != nil { + if hasConsent && consentJson != nil { + if err := jsonutil.Unmarshal(consentJson, &ue.consent); err != nil { return err } } @@ -419,7 +456,9 @@ func (ue *UserExt) unmarshal(extJson json.RawMessage) error { prebidJson, hasPrebid := ue.ext[prebidKey] if hasPrebid { ue.prebid = &ExtUserPrebid{} - if err := json.Unmarshal(prebidJson, ue.prebid); err != nil { + } + if prebidJson != nil { + if err := jsonutil.Unmarshal(prebidJson, ue.prebid); err != nil { return err } } @@ -427,21 +466,29 @@ func (ue *UserExt) unmarshal(extJson json.RawMessage) error { eidsJson, hasEids := ue.ext[eidsKey] if hasEids { ue.eids = &[]openrtb2.EID{} - if err := json.Unmarshal(eidsJson, ue.eids); err != nil { + } + if eidsJson != nil { + if err := jsonutil.Unmarshal(eidsJson, ue.eids); err != nil { return err } } - if consentedProviderSettingsJson, hasCPSettings := ue.ext[consentedProvidersSettingsStringKey]; hasCPSettings { + consentedProviderSettingsInJson, hasCPSettingsIn := ue.ext[consentedProvidersSettingsStringKey] + if hasCPSettingsIn { ue.consentedProvidersSettingsIn = &ConsentedProvidersSettingsIn{} - if err := json.Unmarshal(consentedProviderSettingsJson, ue.consentedProvidersSettingsIn); err != nil { + } + if consentedProviderSettingsInJson != nil { + if err := jsonutil.Unmarshal(consentedProviderSettingsInJson, ue.consentedProvidersSettingsIn); err != nil { return err } } - if consentedProviderSettingsJson, hasCPSettings := ue.ext[consentedProvidersSettingsListKey]; hasCPSettings { + consentedProviderSettingsOutJson, hasCPSettingsOut := ue.ext[consentedProvidersSettingsListKey] + if hasCPSettingsOut { ue.consentedProvidersSettingsOut = &ConsentedProvidersSettingsOut{} - if err := json.Unmarshal(consentedProviderSettingsJson, ue.consentedProvidersSettingsOut); err != nil { + } + if consentedProviderSettingsOutJson != nil { + if err := jsonutil.Unmarshal(consentedProviderSettingsOutJson, ue.consentedProvidersSettingsOut); err != nil { return err } } @@ -452,7 +499,7 @@ func (ue *UserExt) unmarshal(extJson json.RawMessage) error { func (ue *UserExt) marshal() (json.RawMessage, error) { if ue.consentDirty { if ue.consent != nil && len(*ue.consent) > 0 { - consentJson, err := json.Marshal(ue.consent) + consentJson, err := jsonutil.Marshal(ue.consent) if err != nil { return nil, err } @@ -465,7 +512,7 @@ func (ue *UserExt) marshal() (json.RawMessage, error) { if ue.prebidDirty { if ue.prebid != nil { - prebidJson, err := json.Marshal(ue.prebid) + prebidJson, err := jsonutil.Marshal(ue.prebid) if err != nil { return nil, err } @@ -482,7 +529,7 @@ func (ue *UserExt) marshal() (json.RawMessage, error) { if ue.consentedProvidersSettingsInDirty { if ue.consentedProvidersSettingsIn != nil { - cpSettingsJson, err := json.Marshal(ue.consentedProvidersSettingsIn) + cpSettingsJson, err := jsonutil.Marshal(ue.consentedProvidersSettingsIn) if err != nil { return nil, err } @@ -499,7 +546,7 @@ func (ue *UserExt) marshal() (json.RawMessage, error) { if ue.consentedProvidersSettingsOutDirty { if ue.consentedProvidersSettingsOut != nil { - cpSettingsJson, err := json.Marshal(ue.consentedProvidersSettingsOut) + cpSettingsJson, err := jsonutil.Marshal(ue.consentedProvidersSettingsOut) if err != nil { return nil, err } @@ -516,7 +563,7 @@ func (ue *UserExt) marshal() (json.RawMessage, error) { if ue.eidsDirty { if ue.eids != nil && len(*ue.eids) > 0 { - eidsJson, err := json.Marshal(ue.eids) + eidsJson, err := jsonutil.Marshal(ue.eids) if err != nil { return nil, err } @@ -531,7 +578,7 @@ func (ue *UserExt) marshal() (json.RawMessage, error) { if len(ue.ext) == 0 { return nil, nil } - return json.Marshal(ue.ext) + return jsonutil.Marshal(ue.ext) } func (ue *UserExt) Dirty() bool { @@ -691,14 +738,16 @@ func (re *RequestExt) unmarshal(extJson json.RawMessage) error { return nil } - if err := json.Unmarshal(extJson, &re.ext); err != nil { + if err := jsonutil.Unmarshal(extJson, &re.ext); err != nil { return err } prebidJson, hasPrebid := re.ext[prebidKey] if hasPrebid { re.prebid = &ExtRequestPrebid{} - if err := json.Unmarshal(prebidJson, re.prebid); err != nil { + } + if prebidJson != nil { + if err := jsonutil.Unmarshal(prebidJson, re.prebid); err != nil { return err } } @@ -706,7 +755,9 @@ func (re *RequestExt) unmarshal(extJson json.RawMessage) error { schainJson, hasSChain := re.ext[schainKey] if hasSChain { re.schain = &openrtb2.SupplyChain{} - if err := json.Unmarshal(schainJson, re.schain); err != nil { + } + if schainJson != nil { + if err := jsonutil.Unmarshal(schainJson, re.schain); err != nil { return err } } @@ -717,7 +768,7 @@ func (re *RequestExt) unmarshal(extJson json.RawMessage) error { func (re *RequestExt) marshal() (json.RawMessage, error) { if re.prebidDirty { if re.prebid != nil { - prebidJson, err := json.Marshal(re.prebid) + prebidJson, err := jsonutil.Marshal(re.prebid) if err != nil { return nil, err } @@ -734,7 +785,7 @@ func (re *RequestExt) marshal() (json.RawMessage, error) { if re.schainDirty { if re.schain != nil { - schainJson, err := json.Marshal(re.schain) + schainJson, err := jsonutil.Marshal(re.schain) if err != nil { return nil, err } @@ -753,7 +804,7 @@ func (re *RequestExt) marshal() (json.RawMessage, error) { if len(re.ext) == 0 { return nil, nil } - return json.Marshal(re.ext) + return jsonutil.Marshal(re.ext) } func (re *RequestExt) Dirty() bool { @@ -832,6 +883,8 @@ type DeviceExt struct { extDirty bool prebid *ExtDevicePrebid prebidDirty bool + cdep string + cdepDirty bool } func (de *DeviceExt) unmarshal(extJson json.RawMessage) error { @@ -845,14 +898,23 @@ func (de *DeviceExt) unmarshal(extJson json.RawMessage) error { return nil } - if err := json.Unmarshal(extJson, &de.ext); err != nil { + if err := jsonutil.Unmarshal(extJson, &de.ext); err != nil { return err } prebidJson, hasPrebid := de.ext[prebidKey] if hasPrebid { de.prebid = &ExtDevicePrebid{} - if err := json.Unmarshal(prebidJson, de.prebid); err != nil { + } + if prebidJson != nil { + if err := jsonutil.Unmarshal(prebidJson, de.prebid); err != nil { + return err + } + } + + cdepJson, hasCDep := de.ext[cdepKey] + if hasCDep && cdepJson != nil { + if err := jsonutil.Unmarshal(cdepJson, &de.cdep); err != nil { return err } } @@ -863,7 +925,7 @@ func (de *DeviceExt) unmarshal(extJson json.RawMessage) error { func (de *DeviceExt) marshal() (json.RawMessage, error) { if de.prebidDirty { if de.prebid != nil { - prebidJson, err := json.Marshal(de.prebid) + prebidJson, err := jsonutil.Marshal(de.prebid) if err != nil { return nil, err } @@ -878,15 +940,28 @@ func (de *DeviceExt) marshal() (json.RawMessage, error) { de.prebidDirty = false } + if de.cdepDirty { + if len(de.cdep) > 0 { + rawjson, err := jsonutil.Marshal(de.cdep) + if err != nil { + return nil, err + } + de.ext[cdepKey] = rawjson + } else { + delete(de.ext, cdepKey) + } + de.cdepDirty = false + } + de.extDirty = false if len(de.ext) == 0 { return nil, nil } - return json.Marshal(de.ext) + return jsonutil.Marshal(de.ext) } func (de *DeviceExt) Dirty() bool { - return de.extDirty || de.prebidDirty + return de.extDirty || de.prebidDirty || de.cdepDirty } func (de *DeviceExt) GetExt() map[string]json.RawMessage { @@ -915,6 +990,15 @@ func (de *DeviceExt) SetPrebid(prebid *ExtDevicePrebid) { de.prebidDirty = true } +func (de *DeviceExt) GetCDep() string { + return de.cdep +} + +func (de *DeviceExt) SetCDep(cdep string) { + de.cdep = cdep + de.cdepDirty = true +} + func (de *DeviceExt) Clone() *DeviceExt { if de == nil { return nil @@ -957,14 +1041,16 @@ func (ae *AppExt) unmarshal(extJson json.RawMessage) error { return nil } - if err := json.Unmarshal(extJson, &ae.ext); err != nil { + if err := jsonutil.Unmarshal(extJson, &ae.ext); err != nil { return err } prebidJson, hasPrebid := ae.ext[prebidKey] if hasPrebid { ae.prebid = &ExtAppPrebid{} - if err := json.Unmarshal(prebidJson, ae.prebid); err != nil { + } + if prebidJson != nil { + if err := jsonutil.Unmarshal(prebidJson, ae.prebid); err != nil { return err } } @@ -975,7 +1061,7 @@ func (ae *AppExt) unmarshal(extJson json.RawMessage) error { func (ae *AppExt) marshal() (json.RawMessage, error) { if ae.prebidDirty { if ae.prebid != nil { - prebidJson, err := json.Marshal(ae.prebid) + prebidJson, err := jsonutil.Marshal(ae.prebid) if err != nil { return nil, err } @@ -994,7 +1080,7 @@ func (ae *AppExt) marshal() (json.RawMessage, error) { if len(ae.ext) == 0 { return nil, nil } - return json.Marshal(ae.ext) + return jsonutil.Marshal(ae.ext) } func (ae *AppExt) Dirty() bool { @@ -1040,6 +1126,70 @@ func (ae *AppExt) Clone() *AppExt { return &clone } +// --------------------------------------------------------------- +// DOOHExt provides an interface for request.dooh.ext +// This is currently a placeholder for consistency with others - no useful attributes and getters/setters exist yet +// --------------------------------------------------------------- + +type DOOHExt struct { + ext map[string]json.RawMessage + extDirty bool +} + +func (de *DOOHExt) unmarshal(extJson json.RawMessage) error { + if len(de.ext) != 0 || de.Dirty() { + return nil + } + + de.ext = make(map[string]json.RawMessage) + + if len(extJson) == 0 { + return nil + } + + if err := jsonutil.Unmarshal(extJson, &de.ext); err != nil { + return err + } + + return nil +} + +func (de *DOOHExt) marshal() (json.RawMessage, error) { + de.extDirty = false + if len(de.ext) == 0 { + return nil, nil + } + return jsonutil.Marshal(de.ext) +} + +func (de *DOOHExt) Dirty() bool { + return de.extDirty +} + +func (de *DOOHExt) GetExt() map[string]json.RawMessage { + ext := make(map[string]json.RawMessage) + for k, v := range de.ext { + ext[k] = v + } + return ext +} + +func (de *DOOHExt) SetExt(ext map[string]json.RawMessage) { + de.ext = ext + de.extDirty = true +} + +func (de *DOOHExt) Clone() *DOOHExt { + if de == nil { + return nil + } + + clone := *de + clone.ext = maputil.Clone(de.ext) + + return &clone +} + // --------------------------------------------------------------- // RegExt provides an interface for request.regs.ext // --------------------------------------------------------------- @@ -1047,6 +1197,8 @@ func (ae *AppExt) Clone() *AppExt { type RegExt struct { ext map[string]json.RawMessage extDirty bool + dsa *ExtRegsDSA + dsaDirty bool gdpr *int8 gdprDirty bool usPrivacy string @@ -1064,20 +1216,30 @@ func (re *RegExt) unmarshal(extJson json.RawMessage) error { return nil } - if err := json.Unmarshal(extJson, &re.ext); err != nil { + if err := jsonutil.Unmarshal(extJson, &re.ext); err != nil { return err } + dsaJson, hasDSA := re.ext[dsaKey] + if hasDSA { + re.dsa = &ExtRegsDSA{} + } + if dsaJson != nil { + if err := jsonutil.Unmarshal(dsaJson, re.dsa); err != nil { + return err + } + } + gdprJson, hasGDPR := re.ext[gdprKey] - if hasGDPR { - if err := json.Unmarshal(gdprJson, &re.gdpr); err != nil { + if hasGDPR && gdprJson != nil { + if err := jsonutil.Unmarshal(gdprJson, &re.gdpr); err != nil { return errors.New("gdpr must be an integer") } } uspJson, hasUsp := re.ext[us_privacyKey] - if hasUsp { - if err := json.Unmarshal(uspJson, &re.usPrivacy); err != nil { + if hasUsp && uspJson != nil { + if err := jsonutil.Unmarshal(uspJson, &re.usPrivacy); err != nil { return err } } @@ -1086,9 +1248,22 @@ func (re *RegExt) unmarshal(extJson json.RawMessage) error { } func (re *RegExt) marshal() (json.RawMessage, error) { + if re.dsaDirty { + if re.dsa != nil { + rawjson, err := jsonutil.Marshal(re.dsa) + if err != nil { + return nil, err + } + re.ext[dsaKey] = rawjson + } else { + delete(re.ext, dsaKey) + } + re.dsaDirty = false + } + if re.gdprDirty { if re.gdpr != nil { - rawjson, err := json.Marshal(re.gdpr) + rawjson, err := jsonutil.Marshal(re.gdpr) if err != nil { return nil, err } @@ -1101,7 +1276,7 @@ func (re *RegExt) marshal() (json.RawMessage, error) { if re.usPrivacyDirty { if len(re.usPrivacy) > 0 { - rawjson, err := json.Marshal(re.usPrivacy) + rawjson, err := jsonutil.Marshal(re.usPrivacy) if err != nil { return nil, err } @@ -1116,11 +1291,11 @@ func (re *RegExt) marshal() (json.RawMessage, error) { if len(re.ext) == 0 { return nil, nil } - return json.Marshal(re.ext) + return jsonutil.Marshal(re.ext) } func (re *RegExt) Dirty() bool { - return re.extDirty || re.gdprDirty || re.usPrivacyDirty + return re.extDirty || re.dsaDirty || re.gdprDirty || re.usPrivacyDirty } func (re *RegExt) GetExt() map[string]json.RawMessage { @@ -1136,9 +1311,25 @@ func (re *RegExt) SetExt(ext map[string]json.RawMessage) { re.extDirty = true } +func (re *RegExt) GetDSA() *ExtRegsDSA { + if re.dsa == nil { + return nil + } + dsa := *re.dsa + return &dsa +} + +func (re *RegExt) SetDSA(dsa *ExtRegsDSA) { + re.dsa = dsa + re.dsaDirty = true +} + func (re *RegExt) GetGDPR() *int8 { - gdpr := re.gdpr - return gdpr + if re.gdpr == nil { + return nil + } + gdpr := *re.gdpr + return &gdpr } func (re *RegExt) SetGDPR(gdpr *int8) { @@ -1191,13 +1382,13 @@ func (se *SiteExt) unmarshal(extJson json.RawMessage) error { return nil } - if err := json.Unmarshal(extJson, &se.ext); err != nil { + if err := jsonutil.Unmarshal(extJson, &se.ext); err != nil { return err } ampJson, hasAmp := se.ext[ampKey] - if hasAmp { - if err := json.Unmarshal(ampJson, &se.amp); err != nil { + if hasAmp && ampJson != nil { + if err := jsonutil.Unmarshal(ampJson, &se.amp); err != nil { return errors.New(`request.site.ext.amp must be either 1, 0, or undefined`) } } @@ -1208,7 +1399,7 @@ func (se *SiteExt) unmarshal(extJson json.RawMessage) error { func (se *SiteExt) marshal() (json.RawMessage, error) { if se.ampDirty { if se.amp != nil { - ampJson, err := json.Marshal(se.amp) + ampJson, err := jsonutil.Marshal(se.amp) if err != nil { return nil, err } @@ -1223,7 +1414,7 @@ func (se *SiteExt) marshal() (json.RawMessage, error) { if len(se.ext) == 0 { return nil, nil } - return json.Marshal(se.ext) + return jsonutil.Marshal(se.ext) } func (se *SiteExt) Dirty() bool { @@ -1286,13 +1477,13 @@ func (se *SourceExt) unmarshal(extJson json.RawMessage) error { return nil } - if err := json.Unmarshal(extJson, &se.ext); err != nil { + if err := jsonutil.Unmarshal(extJson, &se.ext); err != nil { return err } schainJson, hasSChain := se.ext[schainKey] - if hasSChain { - if err := json.Unmarshal(schainJson, &se.schain); err != nil { + if hasSChain && schainJson != nil { + if err := jsonutil.Unmarshal(schainJson, &se.schain); err != nil { return err } } @@ -1303,7 +1494,7 @@ func (se *SourceExt) unmarshal(extJson json.RawMessage) error { func (se *SourceExt) marshal() (json.RawMessage, error) { if se.schainDirty { if se.schain != nil { - schainJson, err := json.Marshal(se.schain) + schainJson, err := jsonutil.Marshal(se.schain) if err != nil { return nil, err } @@ -1322,7 +1513,7 @@ func (se *SourceExt) marshal() (json.RawMessage, error) { if len(se.ext) == 0 { return nil, nil } - return json.Marshal(se.ext) + return jsonutil.Marshal(se.ext) } func (se *SourceExt) Dirty() bool { @@ -1386,10 +1577,9 @@ func (w *ImpWrapper) GetImpExt() (*ImpExt, error) { } return w.impExt, w.impExt.unmarshal(w.Ext) } - -func (w *ImpWrapper) RebuildImpressionExt() error { +func (w *ImpWrapper) RebuildImp() error { if w.Imp == nil { - return errors.New("ImpWrapper RebuildImpressionExt called on a nil Imp") + return errors.New("ImpWrapper RebuildImp called on a nil Imp") } if err := w.rebuildImpExt(); err != nil { @@ -1451,14 +1641,16 @@ func (e *ImpExt) unmarshal(extJson json.RawMessage) error { return nil } - if err := json.Unmarshal(extJson, &e.ext); err != nil { + if err := jsonutil.Unmarshal(extJson, &e.ext); err != nil { return err } prebidJson, hasPrebid := e.ext[prebidKey] if hasPrebid { e.prebid = &ExtImpPrebid{} - if err := json.Unmarshal(prebidJson, e.prebid); err != nil { + } + if prebidJson != nil { + if err := jsonutil.Unmarshal(prebidJson, e.prebid); err != nil { return err } } @@ -1466,21 +1658,23 @@ func (e *ImpExt) unmarshal(extJson json.RawMessage) error { dataJson, hasData := e.ext[dataKey] if hasData { e.data = &ExtImpData{} - if err := json.Unmarshal(dataJson, e.data); err != nil { + } + if dataJson != nil { + if err := jsonutil.Unmarshal(dataJson, e.data); err != nil { return err } } tidJson, hasTid := e.ext["tid"] - if hasTid { - if err := json.Unmarshal(tidJson, &e.tid); err != nil { + if hasTid && tidJson != nil { + if err := jsonutil.Unmarshal(tidJson, &e.tid); err != nil { return err } } gpIdJson, hasGpId := e.ext["gpid"] - if hasGpId { - if err := json.Unmarshal(gpIdJson, &e.gpId); err != nil { + if hasGpId && gpIdJson != nil { + if err := jsonutil.Unmarshal(gpIdJson, &e.gpId); err != nil { return err } } @@ -1491,7 +1685,7 @@ func (e *ImpExt) unmarshal(extJson json.RawMessage) error { func (e *ImpExt) marshal() (json.RawMessage, error) { if e.prebidDirty { if e.prebid != nil { - prebidJson, err := json.Marshal(e.prebid) + prebidJson, err := jsonutil.Marshal(e.prebid) if err != nil { return nil, err } @@ -1508,7 +1702,7 @@ func (e *ImpExt) marshal() (json.RawMessage, error) { if e.tidDirty { if len(e.tid) > 0 { - tidJson, err := json.Marshal(e.tid) + tidJson, err := jsonutil.Marshal(e.tid) if err != nil { return nil, err } @@ -1523,7 +1717,7 @@ func (e *ImpExt) marshal() (json.RawMessage, error) { if len(e.ext) == 0 { return nil, nil } - return json.Marshal(e.ext) + return jsonutil.Marshal(e.ext) } func (e *ImpExt) Dirty() bool { diff --git a/openrtb_ext/request_wrapper_test.go b/openrtb_ext/request_wrapper_test.go index d55c4b71cc5..ffa925e46ac 100644 --- a/openrtb_ext/request_wrapper_test.go +++ b/openrtb_ext/request_wrapper_test.go @@ -4,8 +4,9 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -41,6 +42,7 @@ func TestCloneRequestWrapper(t *testing.T) { appExt: &AppExt{prebidDirty: true}, regExt: &RegExt{usPrivacy: "foo"}, siteExt: &SiteExt{amp: ptrutil.ToPtr[int8](1)}, + doohExt: &DOOHExt{}, sourceExt: &SourceExt{schainDirty: true}, }, reqWrapCopy: &RequestWrapper{ @@ -60,6 +62,7 @@ func TestCloneRequestWrapper(t *testing.T) { appExt: &AppExt{prebidDirty: true}, regExt: &RegExt{usPrivacy: "foo"}, siteExt: &SiteExt{amp: ptrutil.ToPtr[int8](1)}, + doohExt: &DOOHExt{}, sourceExt: &SourceExt{schainDirty: true}, }, mutator: func(t *testing.T, reqWrap *RequestWrapper) {}, @@ -83,6 +86,7 @@ func TestCloneRequestWrapper(t *testing.T) { appExt: &AppExt{prebidDirty: true}, regExt: &RegExt{usPrivacy: "foo"}, siteExt: &SiteExt{amp: ptrutil.ToPtr[int8](1)}, + doohExt: &DOOHExt{}, sourceExt: &SourceExt{schainDirty: true}, }, reqWrapCopy: &RequestWrapper{ @@ -102,6 +106,7 @@ func TestCloneRequestWrapper(t *testing.T) { appExt: &AppExt{prebidDirty: true}, regExt: &RegExt{usPrivacy: "foo"}, siteExt: &SiteExt{amp: ptrutil.ToPtr[int8](1)}, + doohExt: &DOOHExt{}, sourceExt: &SourceExt{schainDirty: true}, }, mutator: func(t *testing.T, reqWrap *RequestWrapper) { @@ -115,6 +120,7 @@ func TestCloneRequestWrapper(t *testing.T) { reqWrap.appExt = nil reqWrap.regExt = nil reqWrap.siteExt = nil + reqWrap.doohExt = nil reqWrap.sourceExt = nil }, }, @@ -216,7 +222,7 @@ func TestRebuildImp(t *testing.T) { description: "One - Accessed - Error", request: openrtb2.BidRequest{Imp: []openrtb2.Imp{{ID: "1"}}}, requestImpWrapper: []*ImpWrapper{{Imp: nil, impExt: &ImpExt{}}}, - expectedError: "ImpWrapper RebuildImpressionExt called on a nil Imp", + expectedError: "ImpWrapper RebuildImp called on a nil Imp", }, { description: "Many - Accessed - Dirty / Not Dirty", @@ -761,13 +767,13 @@ func TestRebuildDeviceExt(t *testing.T) { { description: "Nil - Dirty", request: openrtb2.BidRequest{}, - requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true}, - expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true, cdep: "1", cdepDirty: true}, + expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, }, { description: "Nil - Dirty - No Change", request: openrtb2.BidRequest{}, - requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true}, + requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true, cdep: "", cdepDirty: true}, expectedRequest: openrtb2.BidRequest{}, }, { @@ -779,37 +785,37 @@ func TestRebuildDeviceExt(t *testing.T) { { description: "Empty - Dirty", request: openrtb2.BidRequest{Device: &openrtb2.Device{}}, - requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true}, - expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true, cdep: "1", cdepDirty: true}, + expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, }, { description: "Empty - Dirty - No Change", request: openrtb2.BidRequest{Device: &openrtb2.Device{}}, - requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true}, + requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true, cdep: "", cdepDirty: true}, expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{}}, }, { description: "Populated - Not Dirty", - request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, requestDeviceExtWrapper: DeviceExt{}, - expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, }, { description: "Populated - Dirty", - request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, - requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent2, prebidDirty: true}, - expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":2,"minheightperc":0}}}`)}}, + request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent2, prebidDirty: true, cdep: "2", cdepDirty: true}, + expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"2","prebid":{"interstitial":{"minwidthperc":2,"minheightperc":0}}}`)}}, }, { description: "Populated - Dirty - No Change", - request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, - requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true}, - expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + requestDeviceExtWrapper: DeviceExt{prebid: &prebidContent1, prebidDirty: true, cdep: "1", cdepDirty: true}, + expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, }, { description: "Populated - Dirty - Cleared", - request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, - requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true}, + request: openrtb2.BidRequest{Device: &openrtb2.Device{Ext: json.RawMessage(`{"cdep":"1","prebid":{"interstitial":{"minwidthperc":1,"minheightperc":0}}}`)}}, + requestDeviceExtWrapper: DeviceExt{prebid: nil, prebidDirty: true, cdep: "", cdepDirty: true}, expectedRequest: openrtb2.BidRequest{Device: &openrtb2.Device{}}, }, } @@ -1008,6 +1014,8 @@ func TestCloneDeviceExt(t *testing.T) { prebid: &ExtDevicePrebid{ Interstitial: &ExtDeviceInt{MinWidthPerc: 65.0, MinHeightPerc: 75.0}, }, + cdep: "1", + cdepDirty: true, }, devExtCopy: &DeviceExt{ ext: map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)}, @@ -1015,6 +1023,8 @@ func TestCloneDeviceExt(t *testing.T) { prebid: &ExtDevicePrebid{ Interstitial: &ExtDeviceInt{MinWidthPerc: 65.0, MinHeightPerc: 75.0}, }, + cdep: "1", + cdepDirty: true, }, mutator: func(t *testing.T, devExt *DeviceExt) {}, }, @@ -1026,6 +1036,8 @@ func TestCloneDeviceExt(t *testing.T) { prebid: &ExtDevicePrebid{ Interstitial: &ExtDeviceInt{MinWidthPerc: 65.0, MinHeightPerc: 75.0}, }, + cdep: "1", + cdepDirty: true, }, devExtCopy: &DeviceExt{ ext: map[string]json.RawMessage{"A": json.RawMessage(`{}`), "B": json.RawMessage(`{"foo":"bar"}`)}, @@ -1033,6 +1045,8 @@ func TestCloneDeviceExt(t *testing.T) { prebid: &ExtDevicePrebid{ Interstitial: &ExtDeviceInt{MinWidthPerc: 65, MinHeightPerc: 75}, }, + cdep: "1", + cdepDirty: true, }, mutator: func(t *testing.T, devExt *DeviceExt) { devExt.ext["A"] = json.RawMessage(`"string"`) @@ -1041,6 +1055,8 @@ func TestCloneDeviceExt(t *testing.T) { devExt.prebid.Interstitial.MinHeightPerc = 55 devExt.prebid.Interstitial = &ExtDeviceInt{MinWidthPerc: 80} devExt.prebid = nil + devExt.cdep = "" + devExt.cdepDirty = true }, }, } @@ -1206,6 +1222,141 @@ func TestCloneAppExt(t *testing.T) { } } +func TestRebuildDOOHExt(t *testing.T) { + // These permutations look a bit wonky + // Since DOOHExt currently exists for consistency but there isn't a single field + // expected - hence unable to test dirty and variations + // Once one is established, updated the permutations below similar to TestRebuildAppExt example + testCases := []struct { + description string + request openrtb2.BidRequest + requestDOOHExtWrapper DOOHExt + expectedRequest openrtb2.BidRequest + }{ + { + description: "Nil - Not Dirty", + request: openrtb2.BidRequest{}, + requestDOOHExtWrapper: DOOHExt{}, + expectedRequest: openrtb2.BidRequest{}, + }, + { + description: "Nil - Dirty", + request: openrtb2.BidRequest{}, + requestDOOHExtWrapper: DOOHExt{}, + expectedRequest: openrtb2.BidRequest{DOOH: nil}, + }, + { + description: "Nil - Dirty - No Change", + request: openrtb2.BidRequest{}, + requestDOOHExtWrapper: DOOHExt{}, + expectedRequest: openrtb2.BidRequest{}, + }, + { + description: "Empty - Not Dirty", + request: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{}}, + requestDOOHExtWrapper: DOOHExt{}, + expectedRequest: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{}}, + }, + { + description: "Empty - Dirty", + request: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{}}, + requestDOOHExtWrapper: DOOHExt{}, + expectedRequest: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{}}, + }, + { + description: "Empty - Dirty - No Change", + request: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{}}, + requestDOOHExtWrapper: DOOHExt{}, + expectedRequest: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{}}, + }, + { + description: "Populated - Not Dirty", + request: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{Ext: json.RawMessage(`{}`)}}, + requestDOOHExtWrapper: DOOHExt{}, + expectedRequest: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{Ext: json.RawMessage(`{}`)}}, + }, + { + description: "Populated - Dirty", + request: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{Ext: json.RawMessage(`{}`)}}, + requestDOOHExtWrapper: DOOHExt{}, + expectedRequest: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{Ext: json.RawMessage(`{}`)}}, + }, + { + description: "Populated - Dirty - No Change", + request: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{Ext: json.RawMessage(`{}`)}}, + requestDOOHExtWrapper: DOOHExt{}, + expectedRequest: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{Ext: json.RawMessage(`{}`)}}, + }, + { + description: "Populated - Dirty - Cleared", + request: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{Ext: json.RawMessage(`{}`)}}, + requestDOOHExtWrapper: DOOHExt{}, + expectedRequest: openrtb2.BidRequest{DOOH: &openrtb2.DOOH{Ext: json.RawMessage(`{}`)}}, + }, + } + + for _, test := range testCases { + // create required filed in the test loop to keep test declaration easier to read + test.requestDOOHExtWrapper.ext = make(map[string]json.RawMessage) + + w := RequestWrapper{BidRequest: &test.request, doohExt: &test.requestDOOHExtWrapper} + w.RebuildRequest() + assert.Equal(t, test.expectedRequest, *w.BidRequest, test.description) + } +} + +func TestCloneDOOHExt(t *testing.T) { + testCases := []struct { + name string + DOOHExt *DOOHExt + DOOHExtCopy *DOOHExt // manual copy of above ext object to verify against + mutator func(t *testing.T, DOOHExt *DOOHExt) // function to modify the Ext object + }{ + { + name: "Nil", // Verify the nil case + DOOHExt: nil, + DOOHExtCopy: nil, + mutator: func(t *testing.T, DOOHExt *DOOHExt) {}, + }, + { + name: "NoMutate", + DOOHExt: &DOOHExt{ + ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, + extDirty: true, + }, + DOOHExtCopy: &DOOHExt{ + ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, + extDirty: true, + }, + mutator: func(t *testing.T, DOOHExt *DOOHExt) {}, + }, + { + name: "General", + DOOHExt: &DOOHExt{ + ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, + extDirty: true, + }, + DOOHExtCopy: &DOOHExt{ + ext: map[string]json.RawMessage{"A": json.RawMessage(`X`), "B": json.RawMessage(`Y`)}, + extDirty: true, + }, + mutator: func(t *testing.T, DOOHExt *DOOHExt) { + DOOHExt.ext["A"] = json.RawMessage(`"string"`) + DOOHExt.ext["C"] = json.RawMessage(`{}`) + DOOHExt.extDirty = false + }, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + clone := test.DOOHExt.Clone() + test.mutator(t, test.DOOHExt) + assert.Equal(t, test.DOOHExtCopy, clone) + }) + } +} + func TestCloneRegExt(t *testing.T) { testCases := []struct { name string @@ -1643,7 +1794,7 @@ func TestImpWrapperRebuildImp(t *testing.T) { test.impExtWrapper.ext = make(map[string]json.RawMessage) w := &ImpWrapper{Imp: &test.imp, impExt: &test.impExtWrapper} - w.RebuildImpressionExt() + w.RebuildImp() assert.Equal(t, test.expectedImp, *w.Imp, test.description) } } @@ -1652,10 +1803,10 @@ func TestImpWrapperGetImpExt(t *testing.T) { var isRewardedInventoryOne int8 = 1 testCases := []struct { - description string - givenWrapper ImpWrapper - expectedImpExt ImpExt - expectedError string + description string + givenWrapper ImpWrapper + expectedImpExt ImpExt + expectedErrorType error }{ { description: "Empty", @@ -1693,21 +1844,21 @@ func TestImpWrapperGetImpExt(t *testing.T) { expectedImpExt: ImpExt{ext: map[string]json.RawMessage{"foo": json.RawMessage("bar")}}, }, { - description: "Error - Ext", - givenWrapper: ImpWrapper{Imp: &openrtb2.Imp{Ext: json.RawMessage(`malformed`)}}, - expectedError: "invalid character 'm' looking for beginning of value", + description: "Error - Ext", + givenWrapper: ImpWrapper{Imp: &openrtb2.Imp{Ext: json.RawMessage(`malformed`)}}, + expectedErrorType: &errortypes.FailedToUnmarshal{}, }, { - description: "Error - Ext - Prebid", - givenWrapper: ImpWrapper{Imp: &openrtb2.Imp{Ext: json.RawMessage(`{"prebid":malformed}`)}}, - expectedError: "invalid character 'm' looking for beginning of value", + description: "Error - Ext - Prebid", + givenWrapper: ImpWrapper{Imp: &openrtb2.Imp{Ext: json.RawMessage(`{"prebid":malformed}`)}}, + expectedErrorType: &errortypes.FailedToUnmarshal{}, }, } for _, test := range testCases { impExt, err := test.givenWrapper.GetImpExt() - if test.expectedError != "" { - assert.EqualError(t, err, test.expectedError, test.description) + if test.expectedErrorType != nil { + assert.IsType(t, test.expectedErrorType, err) } else { assert.NoError(t, err, test.description) assert.Equal(t, test.expectedImpExt, *impExt, test.description) @@ -1908,3 +2059,292 @@ func TestCloneImpExt(t *testing.T) { }) } } + +func TestRebuildRegExt(t *testing.T) { + strA := "a" + strB := "b" + + tests := []struct { + name string + request openrtb2.BidRequest + regExt RegExt + expectedRequest openrtb2.BidRequest + }{ + { + name: "req_regs_nil_-_not_dirty_-_no_change", + request: openrtb2.BidRequest{}, + regExt: RegExt{}, + expectedRequest: openrtb2.BidRequest{}, + }, + { + name: "req_regs_nil_-_dirty_and_different_-_change", + request: openrtb2.BidRequest{}, + regExt: RegExt{dsa: &ExtRegsDSA{Required: ptrutil.ToPtr[int8](1)}, dsaDirty: true, gdpr: ptrutil.ToPtr[int8](1), gdprDirty: true, usPrivacy: strA, usPrivacyDirty: true}, + expectedRequest: openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa":{"dsarequired":1},"gdpr":1,"us_privacy":"a"}`), + }, + }, + }, + { + name: "req_regs_ext_nil_-_not_dirty_-_no_change", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{}}, + regExt: RegExt{}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{}}, + }, + { + name: "req_regs_ext_nil_-_dirty_and_different_-_change", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{}}, + regExt: RegExt{dsa: &ExtRegsDSA{Required: ptrutil.ToPtr[int8](1)}, dsaDirty: true, gdpr: ptrutil.ToPtr[int8](1), gdprDirty: true, usPrivacy: strA, usPrivacyDirty: true}, + expectedRequest: openrtb2.BidRequest{ + Regs: &openrtb2.Regs{ + Ext: json.RawMessage(`{"dsa":{"dsarequired":1},"gdpr":1,"us_privacy":"a"}`), + }, + }, + }, + { + name: "req_regs_dsa_populated_-_not_dirty_-_no_change", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"dsa":{"dsarequired":1}}`)}}, + regExt: RegExt{}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"dsa":{"dsarequired":1}}`)}}, + }, + { + name: "req_regs_dsa_populated_-_dirty_and_different-_change", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"dsa":{"dsarequired":1}}`)}}, + regExt: RegExt{dsa: &ExtRegsDSA{Required: ptrutil.ToPtr[int8](2)}, dsaDirty: true}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"dsa":{"dsarequired":2}}`)}}, + }, + { + name: "req_regs_dsa_populated_-_dirty_and_same_-_no_change", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"dsa":{"dsarequired":1}}`)}}, + regExt: RegExt{dsa: &ExtRegsDSA{Required: ptrutil.ToPtr[int8](1)}, dsaDirty: true}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"dsa":{"dsarequired":1}}`)}}, + }, + { + name: "req_regs_dsa_populated_-_dirty_and_nil_-_cleared", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{}`)}}, + regExt: RegExt{dsa: nil, dsaDirty: true}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{}}, + }, + { + name: "req_regs_gdpr_populated_-_not_dirty_-_no_change", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gdpr":1}`)}}, + regExt: RegExt{}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gdpr":1}`)}}, + }, + { + name: "req_regs_gdpr_populated_-_dirty_and_different-_change", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gdpr":1}`)}}, + regExt: RegExt{gdpr: ptrutil.ToPtr[int8](0), gdprDirty: true}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gdpr":0}`)}}, + }, + { + name: "req_regs_gdpr_populated_-_dirty_and_same_-_no_change", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gdpr":1}`)}}, + regExt: RegExt{gdpr: ptrutil.ToPtr[int8](1), gdprDirty: true}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"gdpr":1}`)}}, + }, + { + name: "req_regs_gdpr_populated_-_dirty_and_nil_-_cleared", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{}`)}}, + regExt: RegExt{gdpr: nil, gdprDirty: true}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{}}, + }, + { + name: "req_regs_usprivacy_populated_-_not_dirty_-_no_change", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"a"}`)}}, + regExt: RegExt{}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"a"}`)}}, + }, + { + name: "req_regs_usprivacy_populated_-_dirty_and_different-_change", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"a"}`)}}, + regExt: RegExt{usPrivacy: strB, usPrivacyDirty: true}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"b"}`)}}, + }, + { + name: "req_regs_usprivacy_populated_-_dirty_and_same_-_no_change", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"a"}`)}}, + regExt: RegExt{usPrivacy: strA, usPrivacyDirty: true}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"a"}`)}}, + }, + { + name: "req_regs_usprivacy_populated_-_dirty_and_nil_-_cleared", + request: openrtb2.BidRequest{Regs: &openrtb2.Regs{Ext: json.RawMessage(`{"us_privacy":"a"}`)}}, + regExt: RegExt{usPrivacy: "", usPrivacyDirty: true}, + expectedRequest: openrtb2.BidRequest{Regs: &openrtb2.Regs{}}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + tt.regExt.ext = make(map[string]json.RawMessage) + + w := RequestWrapper{BidRequest: &tt.request, regExt: &tt.regExt} + w.RebuildRequest() + assert.Equal(t, tt.expectedRequest, *w.BidRequest) + }) + } +} + +func TestRegExtUnmarshal(t *testing.T) { + tests := []struct { + name string + regExt *RegExt + extJson json.RawMessage + expectDSA *ExtRegsDSA + expectGDPR *int8 + expectUSPrivacy string + expectError bool + }{ + { + name: "RegExt.ext_not_empty_and_not_dirtyr", + regExt: &RegExt{ + ext: map[string]json.RawMessage{"dsa": json.RawMessage(`{}`)}, + }, + extJson: json.RawMessage{}, + expectError: false, + }, + { + name: "RegExt.ext_empty_and_dirty", + regExt: &RegExt{extDirty: true}, + extJson: json.RawMessage(`{"dsa":{"dsarequired":1}}`), + expectError: false, + }, + { + name: "nothing_to_unmarshal", + regExt: &RegExt{ + ext: map[string]json.RawMessage{}, + }, + extJson: json.RawMessage{}, + expectError: false, + }, + // DSA + { + name: "valid_dsa_json", + regExt: &RegExt{}, + extJson: json.RawMessage(`{"dsa":{"dsarequired":1}}`), + expectDSA: &ExtRegsDSA{ + Required: ptrutil.ToPtr[int8](1), + }, + expectError: false, + }, + { + name: "malformed_dsa_json", + regExt: &RegExt{}, + extJson: json.RawMessage(`{"dsa":{"dsarequired":""}}`), + expectDSA: &ExtRegsDSA{ + Required: ptrutil.ToPtr[int8](0), + }, + expectError: true, + }, + // GDPR + { + name: "valid_gdpr_json", + regExt: &RegExt{}, + extJson: json.RawMessage(`{"gdpr":1}`), + expectGDPR: ptrutil.ToPtr[int8](1), + expectError: false, + }, + { + name: "malformed_gdpr_json", + regExt: &RegExt{}, + extJson: json.RawMessage(`{"gdpr":""}`), + expectGDPR: ptrutil.ToPtr[int8](0), + expectError: true, + }, + // us_privacy + { + name: "valid_usprivacy_json", + regExt: &RegExt{}, + extJson: json.RawMessage(`{"us_privacy":"consent"}`), + expectUSPrivacy: "consent", + expectError: false, + }, + { + name: "malformed_usprivacy_json", + regExt: &RegExt{}, + extJson: json.RawMessage(`{"us_privacy":1}`), + expectError: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.regExt.unmarshal(tt.extJson) + if tt.expectError { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + assert.Equal(t, tt.expectDSA, tt.regExt.dsa) + assert.Equal(t, tt.expectGDPR, tt.regExt.gdpr) + assert.Equal(t, tt.expectUSPrivacy, tt.regExt.usPrivacy) + }) + } +} + +func TestRegExtGetExtSetExt(t *testing.T) { + regExt := &RegExt{} + regExtJSON := regExt.GetExt() + assert.Equal(t, regExtJSON, map[string]json.RawMessage{}) + assert.False(t, regExt.Dirty()) + + rawJSON := map[string]json.RawMessage{ + "dsa": json.RawMessage(`{}`), + "gdpr": json.RawMessage(`1`), + "usprivacy": json.RawMessage(`"consent"`), + } + regExt.SetExt(rawJSON) + assert.True(t, regExt.Dirty()) + + regExtJSON = regExt.GetExt() + assert.Equal(t, regExtJSON, rawJSON) + assert.NotSame(t, regExtJSON, rawJSON) +} + +func TestRegExtGetDSASetDSA(t *testing.T) { + regExt := &RegExt{} + regExtDSA := regExt.GetDSA() + assert.Nil(t, regExtDSA) + assert.False(t, regExt.Dirty()) + + dsa := &ExtRegsDSA{ + Required: ptrutil.ToPtr[int8](2), + } + regExt.SetDSA(dsa) + assert.True(t, regExt.Dirty()) + + regExtDSA = regExt.GetDSA() + assert.Equal(t, regExtDSA, dsa) + assert.NotSame(t, regExtDSA, dsa) +} + +func TestRegExtGetUSPrivacySetUSPrivacy(t *testing.T) { + regExt := &RegExt{} + regExtUSPrivacy := regExt.GetUSPrivacy() + assert.Equal(t, regExtUSPrivacy, "") + assert.False(t, regExt.Dirty()) + + usprivacy := "consent" + regExt.SetUSPrivacy(usprivacy) + assert.True(t, regExt.Dirty()) + + regExtUSPrivacy = regExt.GetUSPrivacy() + assert.Equal(t, regExtUSPrivacy, usprivacy) + assert.NotSame(t, regExtUSPrivacy, usprivacy) +} + +func TestRegExtGetGDPRSetGDPR(t *testing.T) { + regExt := &RegExt{} + regExtGDPR := regExt.GetGDPR() + assert.Nil(t, regExtGDPR) + assert.False(t, regExt.Dirty()) + + gdpr := ptrutil.ToPtr[int8](1) + regExt.SetGDPR(gdpr) + assert.True(t, regExt.Dirty()) + + regExtGDPR = regExt.GetGDPR() + assert.Equal(t, regExtGDPR, gdpr) + assert.NotSame(t, regExtGDPR, gdpr) +} diff --git a/openrtb_ext/response.go b/openrtb_ext/response.go index e91adaecad9..7b6eacf2830 100644 --- a/openrtb_ext/response.go +++ b/openrtb_ext/response.go @@ -3,9 +3,9 @@ package openrtb_ext import ( "encoding/json" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/openrtb/v19/openrtb3" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/openrtb/v20/openrtb3" ) type NonBidStatusCode openrtb3.LossReason @@ -113,9 +113,9 @@ const ( ) // NonBidObject is subset of Bid object with exact json signature -// defined at https://github.com/prebid/openrtb/blob/v19.0.0/openrtb2/bid.go // It also contains the custom fields type NonBidObject struct { + // SubSet Price float64 `json:"price,omitempty"` ADomain []string `json:"adomain,omitempty"` CatTax adcom1.CategoryTaxonomy `json:"cattax,omitempty"` @@ -126,6 +126,7 @@ type NonBidObject struct { Dur int64 `json:"dur,omitempty"` MType openrtb2.MarkupType `json:"mtype,omitempty"` + // Custom Fields OriginalBidCPM float64 `json:"origbidcpm,omitempty"` OriginalBidCur string `json:"origbidcur,omitempty"` diff --git a/openrtb_ext/site_test.go b/openrtb_ext/site_test.go index 67ec6cc4f99..f6fb04c50ee 100644 --- a/openrtb_ext/site_test.go +++ b/openrtb_ext/site_test.go @@ -1,28 +1,28 @@ package openrtb_ext_test import ( - "encoding/json" "testing" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" ) func TestInvalidSiteExt(t *testing.T) { var s openrtb_ext.ExtSite - assert.EqualError(t, json.Unmarshal([]byte(`{"amp":-1}`), &s), "request.site.ext.amp must be either 1, 0, or undefined") - assert.EqualError(t, json.Unmarshal([]byte(`{"amp":2}`), &s), "request.site.ext.amp must be either 1, 0, or undefined") - assert.EqualError(t, json.Unmarshal([]byte(`{"amp":true}`), &s), "request.site.ext.amp must be either 1, 0, or undefined") - assert.EqualError(t, json.Unmarshal([]byte(`{"amp":null}`), &s), "request.site.ext.amp must be either 1, 0, or undefined") - assert.EqualError(t, json.Unmarshal([]byte(`{"amp":"1"}`), &s), "request.site.ext.amp must be either 1, 0, or undefined") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"amp":-1}`), &s), "request.site.ext.amp must be either 1, 0, or undefined") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"amp":2}`), &s), "request.site.ext.amp must be either 1, 0, or undefined") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"amp":true}`), &s), "request.site.ext.amp must be either 1, 0, or undefined") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"amp":null}`), &s), "request.site.ext.amp must be either 1, 0, or undefined") + assert.EqualError(t, jsonutil.UnmarshalValid([]byte(`{"amp":"1"}`), &s), "request.site.ext.amp must be either 1, 0, or undefined") } func TestValidSiteExt(t *testing.T) { var s openrtb_ext.ExtSite - assert.NoError(t, json.Unmarshal([]byte(`{"amp":0}`), &s)) + assert.NoError(t, jsonutil.UnmarshalValid([]byte(`{"amp":0}`), &s)) assert.EqualValues(t, 0, s.AMP) - assert.NoError(t, json.Unmarshal([]byte(`{"amp":1}`), &s)) + assert.NoError(t, jsonutil.UnmarshalValid([]byte(`{"amp":1}`), &s)) assert.EqualValues(t, 1, s.AMP) - assert.NoError(t, json.Unmarshal([]byte(`{"amp": 1 }`), &s)) + assert.NoError(t, jsonutil.UnmarshalValid([]byte(`{"amp": 1 }`), &s)) assert.EqualValues(t, 1, s.AMP) } diff --git a/openrtb_ext/source.go b/openrtb_ext/source.go index 78112ec7668..dd2a4b0ddc8 100644 --- a/openrtb_ext/source.go +++ b/openrtb_ext/source.go @@ -1,6 +1,6 @@ package openrtb_ext -import "github.com/prebid/openrtb/v19/openrtb2" +import "github.com/prebid/openrtb/v20/openrtb2" // ExtSource defines the contract for bidrequest.source.ext type ExtSource struct { diff --git a/openrtb_ext/supplyChain.go b/openrtb_ext/supplyChain.go index 70299b50cc8..448f404ab28 100644 --- a/openrtb_ext/supplyChain.go +++ b/openrtb_ext/supplyChain.go @@ -5,8 +5,9 @@ import ( "net/url" "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + + "github.com/prebid/prebid-server/v2/util/ptrutil" ) func cloneSupplyChain(schain *openrtb2.SupplyChain) *openrtb2.SupplyChain { diff --git a/openrtb_ext/supplyChain_test.go b/openrtb_ext/supplyChain_test.go index 6b45df1fb09..b62a26aef44 100644 --- a/openrtb_ext/supplyChain_test.go +++ b/openrtb_ext/supplyChain_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) diff --git a/openrtb_ext/user.go b/openrtb_ext/user.go index 586c0aba74b..cd686a6ee75 100644 --- a/openrtb_ext/user.go +++ b/openrtb_ext/user.go @@ -5,7 +5,7 @@ import ( "strconv" "strings" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" ) // ExtUser defines the contract for bidrequest.user.ext diff --git a/ortb/clone.go b/ortb/clone.go index c0e5a4ddada..3023169bc8c 100644 --- a/ortb/clone.go +++ b/ortb/clone.go @@ -1,9 +1,9 @@ package ortb import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/util/ptrutil" - "github.com/prebid/prebid-server/util/sliceutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/util/ptrutil" + "github.com/prebid/prebid-server/v2/util/sliceutil" ) func CloneApp(s *openrtb2.App) *openrtb2.App { @@ -18,6 +18,8 @@ func CloneApp(s *openrtb2.App) *openrtb2.App { c.Cat = sliceutil.Clone(s.Cat) c.SectionCat = sliceutil.Clone(s.SectionCat) c.PageCat = sliceutil.Clone(s.PageCat) + c.PrivacyPolicy = ptrutil.Clone(s.PrivacyPolicy) + c.Paid = ptrutil.Clone(s.Paid) c.Publisher = ClonePublisher(s.Publisher) c.Content = CloneContent(s.Content) c.KwArray = sliceutil.Clone(s.KwArray) @@ -55,6 +57,9 @@ func CloneContent(s *openrtb2.Content) *openrtb2.Content { c.ProdQ = ptrutil.Clone(s.ProdQ) c.VideoQuality = ptrutil.Clone(s.VideoQuality) c.KwArray = sliceutil.Clone(s.KwArray) + c.LiveStream = ptrutil.Clone(s.LiveStream) + c.SourceRelationship = ptrutil.Clone(s.SourceRelationship) + c.Embeddable = ptrutil.Clone(s.Embeddable) c.Data = CloneDataSlice(s.Data) c.Network = CloneNetwork(s.Network) c.Channel = CloneChannel(s.Channel) @@ -93,6 +98,7 @@ func CloneDataSlice(s []openrtb2.Data) []openrtb2.Data { func CloneData(s openrtb2.Data) openrtb2.Data { // Shallow Copy (Value Fields) Occurred By Passing Argument By Value + // - Implicitly created by the cloned array. // Deep Copy (Pointers) s.Segment = CloneSegmentSlice(s.Segment) @@ -116,6 +122,7 @@ func CloneSegmentSlice(s []openrtb2.Segment) []openrtb2.Segment { func CloneSegment(s openrtb2.Segment) openrtb2.Segment { // Shallow Copy (Value Fields) Occurred By Passing Argument By Value + // - Implicitly created by the cloned array. // Deep Copy (Pointers) s.Ext = sliceutil.Clone(s.Ext) @@ -163,6 +170,8 @@ func CloneSite(s *openrtb2.Site) *openrtb2.Site { c.Cat = sliceutil.Clone(s.Cat) c.SectionCat = sliceutil.Clone(s.SectionCat) c.PageCat = sliceutil.Clone(s.PageCat) + c.Mobile = ptrutil.Clone(s.Mobile) + c.PrivacyPolicy = ptrutil.Clone(s.PrivacyPolicy) c.Publisher = ClonePublisher(s.Publisher) c.Content = CloneContent(s.Content) c.KwArray = sliceutil.Clone(s.KwArray) @@ -189,6 +198,132 @@ func CloneUser(s *openrtb2.User) *openrtb2.User { return &c } +func CloneDevice(s *openrtb2.Device) *openrtb2.Device { + if s == nil { + return nil + } + + // Shallow Copy (Value Fields) + c := *s + + // Deep Copy (Pointers) + c.Geo = CloneGeo(s.Geo) + c.DNT = ptrutil.Clone(s.DNT) + c.Lmt = ptrutil.Clone(s.Lmt) + c.SUA = CloneUserAgent(s.SUA) + c.JS = ptrutil.Clone(s.JS) + c.GeoFetch = ptrutil.Clone(s.GeoFetch) + c.ConnectionType = ptrutil.Clone(s.ConnectionType) + c.Ext = sliceutil.Clone(s.Ext) + + return &c +} + +func CloneUserAgent(s *openrtb2.UserAgent) *openrtb2.UserAgent { + if s == nil { + return nil + } + + // Shallow Copy (Value Fields) + c := *s + + // Deep Copy (Pointers) + c.Browsers = CloneBrandVersionSlice(s.Browsers) + c.Platform = CloneBrandVersion(s.Platform) + + if s.Mobile != nil { + mobileCopy := *s.Mobile + c.Mobile = &mobileCopy + } + s.Ext = sliceutil.Clone(s.Ext) + + return &c +} + +func CloneBrandVersionSlice(s []openrtb2.BrandVersion) []openrtb2.BrandVersion { + if s == nil { + return nil + } + + c := make([]openrtb2.BrandVersion, len(s)) + for i, d := range s { + bv := CloneBrandVersion(&d) + c[i] = *bv + } + + return c +} + +func CloneBrandVersion(s *openrtb2.BrandVersion) *openrtb2.BrandVersion { + if s == nil { + return nil + } + + // Shallow Copy (Value Fields) Occurred By Passing Argument By Value + c := *s + + // Deep Copy (Pointers) + c.Version = sliceutil.Clone(s.Version) + c.Ext = sliceutil.Clone(s.Ext) + + return &c +} + +func CloneSource(s *openrtb2.Source) *openrtb2.Source { + if s == nil { + return nil + } + + // Shallow Copy (Value Fields) + c := *s + + // Deep Copy (Pointers) + c.FD = ptrutil.Clone(s.FD) + c.SChain = CloneSChain(s.SChain) + c.Ext = sliceutil.Clone(s.Ext) + + return &c +} + +func CloneSChain(s *openrtb2.SupplyChain) *openrtb2.SupplyChain { + if s == nil { + return nil + } + + // Shallow Copy (Value Fields) + c := *s + + // Deep Copy (Pointers) + c.Nodes = CloneSupplyChainNodes(s.Nodes) + c.Ext = sliceutil.Clone(s.Ext) + + return &c +} + +func CloneSupplyChainNodes(s []openrtb2.SupplyChainNode) []openrtb2.SupplyChainNode { + if s == nil { + return nil + } + + c := make([]openrtb2.SupplyChainNode, len(s)) + for i, d := range s { + c[i] = CloneSupplyChainNode(d) + } + + return c +} + +func CloneSupplyChainNode(s openrtb2.SupplyChainNode) openrtb2.SupplyChainNode { + // Shallow Copy (Value Fields) Occurred By Passing Argument By Value + // - Implicitly created by the cloned array. + + // Deep Copy (Pointers) + s.HP = ptrutil.Clone(s.HP) + s.Ext = sliceutil.Clone(s.Ext) + + return s +} + func CloneGeo(s *openrtb2.Geo) *openrtb2.Geo { if s == nil { return nil @@ -198,6 +333,8 @@ func CloneGeo(s *openrtb2.Geo) *openrtb2.Geo { c := *s // Deep Copy (Pointers) + c.Lat = ptrutil.Clone(s.Lat) + c.Lon = ptrutil.Clone(s.Lon) c.Ext = sliceutil.Clone(s.Ext) return &c @@ -218,6 +355,7 @@ func CloneEIDSlice(s []openrtb2.EID) []openrtb2.EID { func CloneEID(s openrtb2.EID) openrtb2.EID { // Shallow Copy (Value Fields) Occurred By Passing Argument By Value + // - Implicitly created by the cloned array. // Deep Copy (Pointers) s.UIDs = CloneUIDSlice(s.UIDs) @@ -241,6 +379,7 @@ func CloneUIDSlice(s []openrtb2.UID) []openrtb2.UID { func CloneUID(s openrtb2.UID) openrtb2.UID { // Shallow Copy (Value Fields) Occurred By Passing Argument By Value + // - Implicitly created by the cloned array. // Deep Copy (Pointers) s.Ext = sliceutil.Clone(s.Ext) @@ -265,3 +404,20 @@ func CloneDOOH(s *openrtb2.DOOH) *openrtb2.DOOH { return &c } + +// CloneBidRequestPartial performs a deep clone of just the bid request device, user, and source fields. +func CloneBidRequestPartial(s *openrtb2.BidRequest) *openrtb2.BidRequest { + if s == nil { + return nil + } + + // Shallow Copy (Value Fields) + c := *s + + // Deep Copy (Pointers) - PARTIAL CLONE + c.Device = CloneDevice(s.Device) + c.User = CloneUser(s.User) + c.Source = CloneSource(s.Source) + + return &c +} diff --git a/ortb/clone_test.go b/ortb/clone_test.go index 24e43bda1e5..73d03614db4 100644 --- a/ortb/clone_test.go +++ b/ortb/clone_test.go @@ -5,9 +5,9 @@ import ( "reflect" "testing" - "github.com/prebid/openrtb/v19/adcom1" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/openrtb/v20/adcom1" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -36,8 +36,8 @@ func TestCloneApp(t *testing.T) { SectionCat: []string{"sectionCat1"}, PageCat: []string{"pageCat1"}, Ver: "anyVer", - PrivacyPolicy: 1, - Paid: 2, + PrivacyPolicy: ptrutil.ToPtr[int8](1), + Paid: ptrutil.ToPtr[int8](2), Publisher: &openrtb2.Publisher{ID: "anyPublisher", Ext: json.RawMessage(`{"publisher":1}`)}, Content: &openrtb2.Content{ID: "anyContent", Ext: json.RawMessage(`{"content":1}`)}, Keywords: "anyKeywords", @@ -51,6 +51,8 @@ func TestCloneApp(t *testing.T) { assert.NotSame(t, given.Cat, result.Cat, "cat") assert.NotSame(t, given.SectionCat, result.SectionCat, "sectioncat") assert.NotSame(t, given.PageCat, result.PageCat, "pagecat") + assert.NotSame(t, given.PrivacyPolicy, result.PrivacyPolicy, "privacypolicy") + assert.NotSame(t, given.Paid, result.Paid, "paid") assert.NotSame(t, given.Publisher, result.Publisher, "publisher") assert.NotSame(t, given.Publisher.Ext, result.Publisher.Ext, "publisher-ext") assert.NotSame(t, given.Content, result.Content, "content") @@ -65,6 +67,8 @@ func TestCloneApp(t *testing.T) { "Cat", "SectionCat", "PageCat", + "PrivacyPolicy", + "Paid", "Publisher", "Content", "KwArray", @@ -147,12 +151,12 @@ func TestCloneContent(t *testing.T) { QAGMediaRating: adcom1.MediaRatingAll, Keywords: "anyKeywords", KwArray: []string{"key1"}, - LiveStream: 2, - SourceRelationship: 3, + LiveStream: ptrutil.ToPtr[int8](2), + SourceRelationship: ptrutil.ToPtr[int8](3), Len: 4, Language: "anyLanguage", LangB: "anyLangB", - Embeddable: 5, + Embeddable: ptrutil.ToPtr[int8](5), Data: []openrtb2.Data{{ID: "1", Ext: json.RawMessage(`{"data":1}`)}}, Network: &openrtb2.Network{ID: "anyNetwork", Ext: json.RawMessage(`{"network":1}`)}, Channel: &openrtb2.Channel{ID: "anyChannel", Ext: json.RawMessage(`{"channel":1}`)}, @@ -167,6 +171,9 @@ func TestCloneContent(t *testing.T) { assert.NotSame(t, given.ProdQ, result.ProdQ, "prodq") assert.NotSame(t, given.VideoQuality, result.VideoQuality, "videoquality") assert.NotSame(t, given.KwArray, result.KwArray, "kwarray") + assert.NotSame(t, given.LiveStream, result.LiveStream, "livestream") + assert.NotSame(t, given.SourceRelationship, result.SourceRelationship, "sourcerelationship") + assert.NotSame(t, given.Embeddable, result.Embeddable, "embeddable") assert.NotSame(t, given.Data, result.Data, "data") assert.NotSame(t, given.Data[0], result.Data[0], "data-item") assert.NotSame(t, given.Data[0].Ext, result.Data[0].Ext, "data-item-ext") @@ -185,6 +192,9 @@ func TestCloneContent(t *testing.T) { "ProdQ", "VideoQuality", "KwArray", + "LiveStream", + "SourceRelationship", + "Embeddable", "Data", "Network", "Channel", @@ -446,8 +456,8 @@ func TestCloneSite(t *testing.T) { Page: "anyPage", Ref: "anyRef", Search: "anySearch", - Mobile: 1, - PrivacyPolicy: 2, + Mobile: ptrutil.ToPtr[int8](1), + PrivacyPolicy: ptrutil.ToPtr[int8](2), Publisher: &openrtb2.Publisher{ID: "anyPublisher", Ext: json.RawMessage(`{"publisher":1}`)}, Content: &openrtb2.Content{ID: "anyContent", Ext: json.RawMessage(`{"content":1}`)}, Keywords: "anyKeywords", @@ -461,6 +471,8 @@ func TestCloneSite(t *testing.T) { assert.NotSame(t, given.Cat, result.Cat, "cat") assert.NotSame(t, given.SectionCat, result.SectionCat, "sectioncat") assert.NotSame(t, given.PageCat, result.PageCat, "pagecat") + assert.NotSame(t, given.Mobile, result.Mobile, "mobile") + assert.NotSame(t, given.PrivacyPolicy, result.PrivacyPolicy, "privacypolicy") assert.NotSame(t, given.Publisher, result.Publisher, "publisher") assert.NotSame(t, given.Publisher.Ext, result.Publisher.Ext, "publisher-ext") assert.NotSame(t, given.Content, result.Content, "content") @@ -475,6 +487,8 @@ func TestCloneSite(t *testing.T) { "Cat", "SectionCat", "PageCat", + "Mobile", + "PrivacyPolicy", "Publisher", "Content", "KwArray", @@ -505,7 +519,7 @@ func TestCloneUser(t *testing.T) { Keywords: "anyKeywords", KwArray: []string{"key1"}, CustomData: "anyCustomData", - Geo: &openrtb2.Geo{Lat: 1.2, Lon: 2.3, Ext: json.RawMessage(`{"geo":1}`)}, + Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(1.2), Lon: ptrutil.ToPtr(2.3), Ext: json.RawMessage(`{"geo":1}`)}, Data: []openrtb2.Data{{ID: "1", Ext: json.RawMessage(`{"data":1}`)}}, Consent: "anyConsent", EIDs: []openrtb2.EID{{Source: "1", Ext: json.RawMessage(`{"eid":1}`)}}, @@ -536,6 +550,368 @@ func TestCloneUser(t *testing.T) { }) } +func TestCloneDevice(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneDevice(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := &openrtb2.Device{} + result := CloneDevice(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("populated", func(t *testing.T) { + var n int8 = 1 + np := &n + ct := adcom1.ConnectionWIFI + + given := &openrtb2.Device{ + Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(1.2), Lon: ptrutil.ToPtr(2.3), Ext: json.RawMessage(`{"geo":1}`)}, + DNT: np, + Lmt: np, + UA: "UserAgent", + SUA: &openrtb2.UserAgent{Mobile: np, Model: "iPad"}, + IP: "127.0.0.1", + IPv6: "2001::", + DeviceType: adcom1.DeviceTablet, + Make: "Apple", + Model: "iPad", + OS: "macOS", + OSV: "1.2.3", + HWV: "mini", + H: 20, + W: 30, + PPI: 100, + PxRatio: 200, + JS: ptrutil.ToPtr[int8](2), + GeoFetch: ptrutil.ToPtr[int8](4), + FlashVer: "1.22.33", + Language: "En", + LangB: "ENG", + Carrier: "AT&T", + MCCMNC: "111-222", + ConnectionType: &ct, + IFA: "IFA", + DIDSHA1: "DIDSHA1", + DIDMD5: "DIDMD5", + DPIDSHA1: "DPIDSHA1", + DPIDMD5: "DPIDMD5", + MACSHA1: "MACSHA1", + MACMD5: "MACMD5", + Ext: json.RawMessage(`{"anyField":1}`), + } + result := CloneDevice(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.Geo, result.Geo, "geo") + assert.NotSame(t, given.Geo.Ext, result.Geo.Ext, "geo-ext") + assert.NotSame(t, given.DNT, result.DNT, "dnt") + assert.NotSame(t, given.Lmt, result.Lmt, "lmt") + assert.NotSame(t, given.SUA, result.SUA, "sua") + assert.NotSame(t, given.JS, result.JS, "js") + assert.NotSame(t, given.GeoFetch, result.GeoFetch, "geofetch") + assert.NotSame(t, given.ConnectionType, result.ConnectionType, "connectionType") + assert.NotSame(t, given.Ext, result.Ext, "ext") + }) + + t.Run("assumptions", func(t *testing.T) { + assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.Device{})), + []string{ + "Geo", + "DNT", + "Lmt", + "SUA", + "JS", + "GeoFetch", + "ConnectionType", + "Ext", + }) + }) +} + +func TestCloneUserAgent(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneUserAgent(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := &openrtb2.UserAgent{} + result := CloneUserAgent(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("populated", func(t *testing.T) { + var n int8 = 1 + np := &n + + given := &openrtb2.UserAgent{ + Browsers: []openrtb2.BrandVersion{{Brand: "Apple"}}, + Platform: &openrtb2.BrandVersion{Brand: "Apple"}, + Mobile: np, + Architecture: "X86", + Bitness: "64", + Model: "iPad", + Source: adcom1.UASourceLowEntropy, + Ext: json.RawMessage(`{"anyField":1}`), + } + result := CloneUserAgent(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.Browsers, result.Browsers, "browsers") + assert.NotSame(t, given.Platform, result.Platform, "platform") + assert.NotSame(t, given.Mobile, result.Mobile, "mobile") + assert.NotSame(t, given.Architecture, result.Architecture, "architecture") + assert.NotSame(t, given.Ext, result.Ext, "ext") + }) + + t.Run("assumptions", func(t *testing.T) { + assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.UserAgent{})), + []string{ + "Browsers", + "Platform", + "Mobile", + "Ext", + }) + }) +} + +func TestCloneBrandVersionSlice(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneBrandVersionSlice(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := []openrtb2.BrandVersion{} + result := CloneBrandVersionSlice(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("one", func(t *testing.T) { + given := []openrtb2.BrandVersion{ + {Brand: "1", Version: []string{"s1", "s2"}, Ext: json.RawMessage(`{"anyField":1}`)}, + } + result := CloneBrandVersionSlice(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given[0], result[0], "item-pointer") + assert.NotSame(t, given[0].Ext, result[0].Ext, "item-pointer-ext") + }) + + t.Run("many", func(t *testing.T) { + given := []openrtb2.BrandVersion{ + {Brand: "1", Version: []string{"s1", "s2"}, Ext: json.RawMessage(`{"anyField":1}`)}, + {Brand: "2", Version: []string{"s3", "s4"}, Ext: json.RawMessage(`{"anyField":1}`)}, + {Brand: "3", Version: []string{"s5", "s6"}, Ext: json.RawMessage(`{"anyField":1}`)}, + } + result := CloneBrandVersionSlice(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given[0], result[0], "item0-pointer") + assert.NotSame(t, given[0].Ext, result[0].Ext, "item0-pointer-ext") + assert.NotSame(t, given[1], result[1], "item1-pointer") + assert.NotSame(t, given[1].Ext, result[1].Ext, "item1-pointer-ext") + assert.NotSame(t, given[2], result[2], "item1-pointer") + assert.NotSame(t, given[2].Ext, result[2].Ext, "item1-pointer-ext") + }) +} + +func TestCloneBrandVersion(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneBrandVersion(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := &openrtb2.BrandVersion{} + result := CloneBrandVersion(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("populated", func(t *testing.T) { + given := &openrtb2.BrandVersion{ + Brand: "Apple", + Version: []string{"s1"}, + Ext: json.RawMessage(`{"anyField":1}`), + } + result := CloneBrandVersion(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.Ext, result.Ext, "ext") + }) + + t.Run("assumptions", func(t *testing.T) { + assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.BrandVersion{})), + []string{ + "Version", + "Ext", + }) + }) +} + +func TestCloneSource(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneSource(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := &openrtb2.Source{} + result := CloneSource(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("populated", func(t *testing.T) { + + given := &openrtb2.Source{ + FD: ptrutil.ToPtr[int8](1), + TID: "Tid", + PChain: "PChain", + SChain: &openrtb2.SupplyChain{ + Complete: 1, + Nodes: []openrtb2.SupplyChainNode{ + {ASI: "asi", Ext: json.RawMessage(`{"anyField":1}`)}, + }, + Ext: json.RawMessage(`{"anyField":2}`), + }, + Ext: json.RawMessage(`{"anyField":1}`), + } + result := CloneSource(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.FD, result.FD, "fd") + assert.NotSame(t, given.SChain, result.SChain, "schain") + assert.NotSame(t, given.SChain.Ext, result.SChain.Ext, "schain.ext") + assert.NotSame(t, given.Ext, result.Ext, "ext") + assert.NotSame(t, given.SChain.Nodes[0].Ext, result.SChain.Nodes[0].Ext, "schain.nodes.ext") + }) + + t.Run("assumptions", func(t *testing.T) { + assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.Source{})), + []string{ + "FD", + "SChain", + "Ext", + }) + }) +} + +func TestCloneSChain(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneSource(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := &openrtb2.SupplyChain{} + result := CloneSChain(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("populated", func(t *testing.T) { + given := &openrtb2.SupplyChain{ + Complete: 1, + Nodes: []openrtb2.SupplyChainNode{ + {ASI: "asi", Ext: json.RawMessage(`{"anyField":1}`)}, + }, + Ext: json.RawMessage(`{"anyField":1}`), + } + result := CloneSChain(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.Nodes, result.Nodes, "nodes") + assert.NotSame(t, given.Nodes[0].Ext, result.Nodes[0].Ext, "nodes.ext") + assert.NotSame(t, given.Ext, result.Ext, "ext") + }) + + t.Run("assumptions", func(t *testing.T) { + assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.SupplyChain{})), + []string{ + "Nodes", + "Ext", + }) + }) +} + +func TestCloneSupplyChainNodes(t *testing.T) { + var n int8 = 1 + np := &n + t.Run("nil", func(t *testing.T) { + result := CloneSupplyChainNodes(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := []openrtb2.SupplyChainNode{} + result := CloneSupplyChainNodes(given) + assert.Empty(t, result) + assert.NotSame(t, given, result) + }) + + t.Run("one", func(t *testing.T) { + given := []openrtb2.SupplyChainNode{ + {ASI: "asi", HP: np, Ext: json.RawMessage(`{"anyField":1}`)}, + } + result := CloneSupplyChainNodes(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given[0], result[0], "item-pointer") + assert.NotSame(t, given[0].HP, result[0].HP, "item-pointer-hp") + assert.NotSame(t, given[0].Ext, result[0].Ext, "item-pointer-ext") + }) + + t.Run("many", func(t *testing.T) { + given := []openrtb2.SupplyChainNode{ + {ASI: "asi", HP: np, Ext: json.RawMessage(`{"anyField":1}`)}, + {ASI: "asi", HP: np, Ext: json.RawMessage(`{"anyField":1}`)}, + {ASI: "asi", HP: np, Ext: json.RawMessage(`{"anyField":1}`)}, + } + result := CloneSupplyChainNodes(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given[0], result[0], "item0-pointer") + assert.NotSame(t, given[0].Ext, result[0].Ext, "item0-pointer-ext") + assert.NotSame(t, given[0].HP, result[0].HP, "item0-pointer-hp") + assert.NotSame(t, given[1], result[1], "item1-pointer") + assert.NotSame(t, given[1].Ext, result[1].Ext, "item1-pointer-ext") + assert.NotSame(t, given[1].HP, result[1].HP, "item1-pointer-hp") + assert.NotSame(t, given[2], result[2], "item2-pointer") + assert.NotSame(t, given[2].Ext, result[2].Ext, "item2-pointer-ext") + assert.NotSame(t, given[2].HP, result[2].HP, "item2-pointer-hp") + }) +} + +func TestCloneSupplyChainNode(t *testing.T) { + t.Run("populated", func(t *testing.T) { + var n int8 = 1 + np := &n + + given := openrtb2.SupplyChainNode{ + ASI: "asi", + HP: np, + Ext: json.RawMessage(`{"anyField":1}`), + } + result := CloneSupplyChainNode(given) + assert.Equal(t, given, result, "equality") + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.Ext, result.Ext, "ext") + assert.NotSame(t, given.HP, result.HP, "hp") + }) + + t.Run("assumptions", func(t *testing.T) { + assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.SupplyChainNode{})), + []string{ + "HP", + "Ext", + }) + }) +} + func TestCloneGeo(t *testing.T) { t.Run("nil", func(t *testing.T) { result := CloneGeo(nil) @@ -551,8 +927,8 @@ func TestCloneGeo(t *testing.T) { t.Run("populated", func(t *testing.T) { given := &openrtb2.Geo{ - Lat: 1.234, - Lon: 5.678, + Lat: ptrutil.ToPtr(1.234), + Lon: ptrutil.ToPtr(5.678), Type: adcom1.LocationGPS, Accuracy: 1, LastFix: 2, @@ -569,12 +945,16 @@ func TestCloneGeo(t *testing.T) { result := CloneGeo(given) assert.Equal(t, given, result, "equality") assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.Lat, result.Lat, "lat") + assert.NotSame(t, given.Lon, result.Lon, "lon") assert.NotSame(t, given.Ext, result.Ext, "ext") }) t.Run("assumptions", func(t *testing.T) { assert.ElementsMatch(t, discoverPointerFields(reflect.TypeOf(openrtb2.Geo{})), []string{ + "Lat", + "Lon", "Ext", }) }) @@ -749,6 +1129,37 @@ func TestCloneDOOH(t *testing.T) { }) } +func TestCloneBidderReq(t *testing.T) { + t.Run("nil", func(t *testing.T) { + result := CloneBidRequestPartial(nil) + assert.Nil(t, result) + }) + + t.Run("empty", func(t *testing.T) { + given := &openrtb2.BidRequest{} + result := CloneBidRequestPartial(given) + assert.Equal(t, given, result) + assert.NotSame(t, given, result) + }) + + t.Run("populated", func(t *testing.T) { + given := &openrtb2.BidRequest{ + ID: "anyID", + User: &openrtb2.User{ID: "testUserId"}, + Device: &openrtb2.Device{Carrier: "testCarrier"}, + Source: &openrtb2.Source{TID: "testTID"}, + } + result := CloneBidRequestPartial(given) + assert.Equal(t, given, result) + assert.NotSame(t, given, result, "pointer") + assert.NotSame(t, given.Device, result.Device, "device") + assert.NotSame(t, given.User, result.User, "user") + assert.NotSame(t, given.Source, result.Source, "source") + }) + + // TODO: Implement a full bid request clone and track changes using an 'assumptions' test. +} + // discoverPointerFields returns the names of all fields of an object that are // pointers and would need to be cloned. This method is specific to types which can // appear within an OpenRTB data model object. diff --git a/ortb/default.go b/ortb/default.go index cd9d8c24759..c5e43e2e770 100644 --- a/ortb/default.go +++ b/ortb/default.go @@ -1,8 +1,8 @@ package ortb import ( - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) const ( diff --git a/ortb/default_test.go b/ortb/default_test.go index 04eeeebdcb6..7017f5f2114 100644 --- a/ortb/default_test.go +++ b/ortb/default_test.go @@ -4,12 +4,14 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/ptrutil" ) func TestSetDefaults(t *testing.T) { @@ -31,7 +33,7 @@ func TestSetDefaults(t *testing.T) { name: "malformed request.ext", givenRequest: openrtb2.BidRequest{Ext: json.RawMessage(`malformed`)}, expectedRequest: openrtb2.BidRequest{Ext: json.RawMessage(`malformed`)}, - expectedErr: "invalid character 'm' looking for beginning of value", + expectedErr: "expect { or n, but found m", }, { name: "targeting", // tests integration with setDefaultsTargeting @@ -55,6 +57,7 @@ func TestSetDefaults(t *testing.T) { // assert error if len(test.expectedErr) > 0 { assert.EqualError(t, err, test.expectedErr, "Error") + assert.IsType(t, &errortypes.FailedToUnmarshal{}, err) } // rebuild request @@ -66,10 +69,10 @@ func TestSetDefaults(t *testing.T) { assert.Equal(t, &test.expectedRequest, wrapper.BidRequest, "Request") } else { // assert request as json to ignore order in ext fields - expectedRequestJSON, err := json.Marshal(test.expectedRequest) + expectedRequestJSON, err := jsonutil.Marshal(test.expectedRequest) require.NoError(t, err, "Marshal Expected Request") - actualRequestJSON, err := json.Marshal(wrapper.BidRequest) + actualRequestJSON, err := jsonutil.Marshal(wrapper.BidRequest) require.NoError(t, err, "Marshal Actual Request") assert.JSONEq(t, string(expectedRequestJSON), string(actualRequestJSON), "Request") diff --git a/pbs/usersync.go b/pbs/usersync.go index a5b49f6db03..bfe12689177 100644 --- a/pbs/usersync.go +++ b/pbs/usersync.go @@ -10,9 +10,9 @@ import ( "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/server/ssl" - "github.com/prebid/prebid-server/usersync" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/server/ssl" + "github.com/prebid/prebid-server/v2/usersync" ) // Recaptcha code from https://github.com/haisum/recaptcha/blob/master/recaptcha.go diff --git a/prebid_cache_client/client.go b/prebid_cache_client/client.go index 872420001ea..fb3fb24d9cc 100644 --- a/prebid_cache_client/client.go +++ b/prebid_cache_client/client.go @@ -12,8 +12,8 @@ import ( "strings" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" "github.com/buger/jsonparser" "github.com/golang/glog" diff --git a/prebid_cache_client/client_test.go b/prebid_cache_client/client_test.go index ec390364849..f3ee3065ff1 100644 --- a/prebid_cache_client/client_test.go +++ b/prebid_cache_client/client_test.go @@ -10,9 +10,10 @@ import ( "strconv" "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - metricsConf "github.com/prebid/prebid-server/metrics/config" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + metricsConf "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -297,7 +298,7 @@ func newHandler(numResponses int) http.HandlerFunc { resp.Responses[i].UUID = strconv.Itoa(i) } - respBytes, _ := json.Marshal(resp) + respBytes, _ := jsonutil.Marshal(resp) w.Write(respBytes) }) } diff --git a/privacy/activitycontrol.go b/privacy/activitycontrol.go index 9d6668b3e44..7d1e16e99a5 100644 --- a/privacy/activitycontrol.go +++ b/privacy/activitycontrol.go @@ -1,8 +1,8 @@ package privacy import ( - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) type ActivityResult int @@ -37,7 +37,9 @@ func (r ActivityRequest) IsBidRequest() bool { } type ActivityControl struct { - plans map[Activity]ActivityPlan + plans map[Activity]ActivityPlan + IPv6Config config.IPv6 + IPv4Config config.IPv4 } func NewActivityControl(cfg *config.AccountPrivacy) ActivityControl { @@ -58,6 +60,9 @@ func NewActivityControl(cfg *config.AccountPrivacy) ActivityControl { plans[ActivityTransmitTIDs] = buildPlan(cfg.AllowActivities.TransmitTids) ac.plans = plans + ac.IPv4Config = cfg.IPv4Config + ac.IPv6Config = cfg.IPv6Config + return ac } diff --git a/privacy/activitycontrol_test.go b/privacy/activitycontrol_test.go index 743888df029..5cd9f38b011 100644 --- a/privacy/activitycontrol_test.go +++ b/privacy/activitycontrol_test.go @@ -3,9 +3,9 @@ package privacy import ( "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) @@ -33,17 +33,23 @@ func TestNewActivityControl(t *testing.T) { TransmitUniqueRequestIds: getTestActivityConfig(true), TransmitTids: getTestActivityConfig(true), }, + IPv6Config: config.IPv6{AnonKeepBits: 32}, + IPv4Config: config.IPv4{AnonKeepBits: 16}, + }, + activityControl: ActivityControl{ + plans: map[Activity]ActivityPlan{ + ActivitySyncUser: getTestActivityPlan(ActivityAllow), + ActivityFetchBids: getTestActivityPlan(ActivityAllow), + ActivityEnrichUserFPD: getTestActivityPlan(ActivityAllow), + ActivityReportAnalytics: getTestActivityPlan(ActivityAllow), + ActivityTransmitUserFPD: getTestActivityPlan(ActivityAllow), + ActivityTransmitPreciseGeo: getTestActivityPlan(ActivityDeny), + ActivityTransmitUniqueRequestIDs: getTestActivityPlan(ActivityAllow), + ActivityTransmitTIDs: getTestActivityPlan(ActivityAllow), + }, + IPv6Config: config.IPv6{AnonKeepBits: 32}, + IPv4Config: config.IPv4{AnonKeepBits: 16}, }, - activityControl: ActivityControl{plans: map[Activity]ActivityPlan{ - ActivitySyncUser: getTestActivityPlan(ActivityAllow), - ActivityFetchBids: getTestActivityPlan(ActivityAllow), - ActivityEnrichUserFPD: getTestActivityPlan(ActivityAllow), - ActivityReportAnalytics: getTestActivityPlan(ActivityAllow), - ActivityTransmitUserFPD: getTestActivityPlan(ActivityAllow), - ActivityTransmitPreciseGeo: getTestActivityPlan(ActivityDeny), - ActivityTransmitUniqueRequestIDs: getTestActivityPlan(ActivityAllow), - ActivityTransmitTIDs: getTestActivityPlan(ActivityAllow), - }}, }, } diff --git a/privacy/ccpa/consentwriter.go b/privacy/ccpa/consentwriter.go index 1d65a272f90..265d47e8595 100644 --- a/privacy/ccpa/consentwriter.go +++ b/privacy/ccpa/consentwriter.go @@ -1,8 +1,8 @@ package ccpa import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // ConsentWriter implements the old PolicyWriter interface for CCPA. diff --git a/privacy/ccpa/consentwriter_test.go b/privacy/ccpa/consentwriter_test.go index 015f1328f61..a92400dce53 100644 --- a/privacy/ccpa/consentwriter_test.go +++ b/privacy/ccpa/consentwriter_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/privacy/ccpa/parsedpolicy.go b/privacy/ccpa/parsedpolicy.go index 7b9c2d1fa7c..056cc99ee1b 100644 --- a/privacy/ccpa/parsedpolicy.go +++ b/privacy/ccpa/parsedpolicy.go @@ -4,7 +4,7 @@ import ( "errors" "fmt" - "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/v2/errortypes" ) const ( diff --git a/privacy/ccpa/policy.go b/privacy/ccpa/policy.go index fbafd8a8a2e..0b719bf1455 100644 --- a/privacy/ccpa/policy.go +++ b/privacy/ccpa/policy.go @@ -6,10 +6,10 @@ import ( gpplib "github.com/prebid/go-gpp" gppConstants "github.com/prebid/go-gpp/constants" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/openrtb_ext" - gppPolicy "github.com/prebid/prebid-server/privacy/gpp" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/openrtb_ext" + gppPolicy "github.com/prebid/prebid-server/v2/privacy/gpp" ) // Policy represents the CCPA regulatory information from an OpenRTB bid request. diff --git a/privacy/ccpa/policy_test.go b/privacy/ccpa/policy_test.go index 3a1433333c0..20d9f680ba1 100644 --- a/privacy/ccpa/policy_test.go +++ b/privacy/ccpa/policy_test.go @@ -7,8 +7,8 @@ import ( gpplib "github.com/prebid/go-gpp" gppConstants "github.com/prebid/go-gpp/constants" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -922,3 +922,7 @@ func (ms mockGPPSection) GetID() gppConstants.SectionID { func (ms mockGPPSection) GetValue() string { return ms.value } + +func (ms mockGPPSection) Encode(bool) []byte { + return nil +} diff --git a/privacy/enforcement.go b/privacy/enforcement.go deleted file mode 100644 index 8074d96acf3..00000000000 --- a/privacy/enforcement.go +++ /dev/null @@ -1,92 +0,0 @@ -package privacy - -import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/config" -) - -// Enforcement represents the privacy policies to enforce for an OpenRTB bid request. -type Enforcement struct { - CCPA bool - COPPA bool - GDPRGeo bool - GDPRID bool - LMT bool - - // activities - UFPD bool - Eids bool - PreciseGeo bool - TID bool -} - -// Any returns true if at least one privacy policy requires enforcement. -func (e Enforcement) AnyLegacy() bool { - return e.CCPA || e.COPPA || e.GDPRGeo || e.GDPRID || e.LMT -} - -func (e Enforcement) AnyActivities() bool { - return e.UFPD || e.PreciseGeo || e.Eids || e.TID -} - -// Apply cleans personally identifiable information from an OpenRTB bid request. -func (e Enforcement) Apply(bidRequest *openrtb2.BidRequest, privacy config.AccountPrivacy) { - e.apply(bidRequest, NewScrubber(privacy.IPv6Config, privacy.IPv4Config)) -} - -func (e Enforcement) apply(bidRequest *openrtb2.BidRequest, scrubber Scrubber) { - if bidRequest != nil { - if e.AnyActivities() { - bidRequest = scrubber.ScrubRequest(bidRequest, e) - } - if e.AnyLegacy() && !(e.UFPD && e.PreciseGeo && e.Eids) { - bidRequest.User = scrubber.ScrubUser(bidRequest.User, e.getUserScrubStrategy(), e.getGeoScrubStrategy()) - } - if e.AnyLegacy() && !(e.UFPD && e.PreciseGeo) { - bidRequest.Device = scrubber.ScrubDevice(bidRequest.Device, e.getDeviceIDScrubStrategy(), e.getIPv4ScrubStrategy(), e.getIPv6ScrubStrategy(), e.getGeoScrubStrategy()) - } - } -} - -func (e Enforcement) getDeviceIDScrubStrategy() ScrubStrategyDeviceID { - if e.COPPA || e.GDPRID || e.CCPA || e.LMT { - return ScrubStrategyDeviceIDAll - } - - return ScrubStrategyDeviceIDNone -} - -func (e Enforcement) getIPv4ScrubStrategy() ScrubStrategyIPV4 { - if e.COPPA || e.GDPRGeo || e.CCPA || e.LMT { - return ScrubStrategyIPV4Subnet - } - - return ScrubStrategyIPV4None -} - -func (e Enforcement) getIPv6ScrubStrategy() ScrubStrategyIPV6 { - if e.GDPRGeo || e.CCPA || e.LMT || e.COPPA { - return ScrubStrategyIPV6Subnet - } - return ScrubStrategyIPV6None -} - -func (e Enforcement) getGeoScrubStrategy() ScrubStrategyGeo { - if e.COPPA { - return ScrubStrategyGeoFull - } - - if e.GDPRGeo || e.CCPA || e.LMT { - return ScrubStrategyGeoReducedPrecision - } - - return ScrubStrategyGeoNone -} - -func (e Enforcement) getUserScrubStrategy() ScrubStrategyUser { - if e.COPPA || e.CCPA || e.LMT || e.GDPRID { - return ScrubStrategyUserIDAndDemographic - } - - return ScrubStrategyUserNone -} diff --git a/privacy/enforcement_test.go b/privacy/enforcement_test.go deleted file mode 100644 index a97779eb903..00000000000 --- a/privacy/enforcement_test.go +++ /dev/null @@ -1,393 +0,0 @@ -package privacy - -import ( - "testing" - - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/mock" -) - -func TestAnyLegacy(t *testing.T) { - testCases := []struct { - enforcement Enforcement - expected bool - description string - }{ - { - description: "All False", - enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPRGeo: false, - GDPRID: false, - LMT: false, - }, - expected: false, - }, - { - description: "All True", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - }, - expected: true, - }, - { - description: "Mixed", - enforcement: Enforcement{ - CCPA: false, - COPPA: true, - GDPRGeo: false, - GDPRID: false, - LMT: true, - }, - expected: true, - }, - } - - for _, test := range testCases { - result := test.enforcement.AnyLegacy() - assert.Equal(t, test.expected, result, test.description) - } -} - -func TestApplyGDPR(t *testing.T) { - testCases := []struct { - description string - enforcement Enforcement - expectedDeviceID ScrubStrategyDeviceID - expectedDeviceIPv4 ScrubStrategyIPV4 - expectedDeviceIPv6 ScrubStrategyIPV6 - expectedDeviceGeo ScrubStrategyGeo - expectedUser ScrubStrategyUser - expectedUserGeo ScrubStrategyGeo - }{ - { - description: "All Enforced", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoFull, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoFull, - }, - { - description: "CCPA Only", - enforcement: Enforcement{ - CCPA: true, - COPPA: false, - GDPRGeo: false, - GDPRID: false, - LMT: false, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "COPPA Only", - enforcement: Enforcement{ - CCPA: false, - COPPA: true, - GDPRGeo: false, - GDPRID: false, - LMT: false, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoFull, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoFull, - }, - { - description: "GDPR Only - Full", - enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPRGeo: true, - GDPRID: true, - LMT: false, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "GDPR Only - ID Only", - enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPRGeo: false, - GDPRID: true, - LMT: false, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4None, - expectedDeviceIPv6: ScrubStrategyIPV6None, - expectedDeviceGeo: ScrubStrategyGeoNone, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoNone, - }, - { - description: "GDPR Only - Geo Only", - enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPRGeo: true, - GDPRID: false, - LMT: false, - }, - expectedDeviceID: ScrubStrategyDeviceIDNone, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserNone, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "LMT Only", - enforcement: Enforcement{ - CCPA: false, - COPPA: false, - GDPRGeo: false, - GDPRID: false, - LMT: true, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoReducedPrecision, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "Interactions: COPPA + GDPR Full", - enforcement: Enforcement{ - CCPA: false, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: false, - }, - expectedDeviceID: ScrubStrategyDeviceIDAll, - expectedDeviceIPv4: ScrubStrategyIPV4Subnet, - expectedDeviceIPv6: ScrubStrategyIPV6Subnet, - expectedDeviceGeo: ScrubStrategyGeoFull, - expectedUser: ScrubStrategyUserIDAndDemographic, - expectedUserGeo: ScrubStrategyGeoFull, - }, - } - - for _, test := range testCases { - req := &openrtb2.BidRequest{ - Device: &openrtb2.Device{}, - User: &openrtb2.User{}, - } - replacedDevice := &openrtb2.Device{} - replacedUser := &openrtb2.User{} - - m := &mockScrubber{} - m.On("ScrubDevice", req.Device, test.expectedDeviceID, test.expectedDeviceIPv4, test.expectedDeviceIPv6, test.expectedDeviceGeo).Return(replacedDevice).Once() - m.On("ScrubUser", req.User, test.expectedUser, test.expectedUserGeo).Return(replacedUser).Once() - - test.enforcement.apply(req, m) - - m.AssertExpectations(t) - assert.Same(t, replacedDevice, req.Device, "Device") - assert.Same(t, replacedUser, req.User, "User") - } -} - -func TestApplyToggle(t *testing.T) { - testCases := []struct { - description string - enforcement Enforcement - expectedScrubRequestExecuted bool - expectedScrubUserExecuted bool - expectedScrubDeviceExecuted bool - }{ - { - description: "All enforced - only ScrubRequest execution expected", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - UFPD: true, - Eids: true, - PreciseGeo: true, - TID: true, - }, - expectedScrubRequestExecuted: true, - expectedScrubUserExecuted: false, - expectedScrubDeviceExecuted: false, - }, - { - description: "All Legacy and no activities - ScrubUser and ScrubDevice execution expected", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - UFPD: false, - Eids: false, - PreciseGeo: false, - TID: false, - }, - expectedScrubRequestExecuted: false, - expectedScrubUserExecuted: true, - expectedScrubDeviceExecuted: true, - }, - { - description: "Some Legacy and some activities - ScrubRequest, ScrubUser and ScrubDevice execution expected", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - UFPD: true, - Eids: false, - PreciseGeo: false, - TID: false, - }, - expectedScrubRequestExecuted: true, - expectedScrubUserExecuted: true, - expectedScrubDeviceExecuted: true, - }, - { - description: "Some Legacy and some activities - ScrubRequest execution expected", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - UFPD: true, - Eids: true, - PreciseGeo: true, - TID: false, - }, - expectedScrubRequestExecuted: true, - expectedScrubUserExecuted: false, - expectedScrubDeviceExecuted: false, - }, - { - description: "Some Legacy and some activities overlap - ScrubRequest and ScrubUser execution expected", - enforcement: Enforcement{ - CCPA: true, - COPPA: true, - GDPRGeo: true, - GDPRID: true, - LMT: true, - UFPD: true, - Eids: false, - PreciseGeo: true, - TID: false, - }, - expectedScrubRequestExecuted: true, - expectedScrubUserExecuted: true, - expectedScrubDeviceExecuted: false, - }, - } - - for _, test := range testCases { - t.Run(test.description, func(t *testing.T) { - req := &openrtb2.BidRequest{ - Device: &openrtb2.Device{}, - User: &openrtb2.User{}, - } - replacedDevice := &openrtb2.Device{} - replacedUser := &openrtb2.User{} - - m := &mockScrubber{} - - if test.expectedScrubRequestExecuted { - m.On("ScrubRequest", req, test.enforcement).Return(req).Once() - } - if test.expectedScrubUserExecuted { - m.On("ScrubUser", req.User, ScrubStrategyUserIDAndDemographic, ScrubStrategyGeoFull).Return(replacedUser).Once() - } - if test.expectedScrubDeviceExecuted { - m.On("ScrubDevice", req.Device, ScrubStrategyDeviceIDAll, ScrubStrategyIPV4Subnet, ScrubStrategyIPV6Subnet, ScrubStrategyGeoFull).Return(replacedDevice).Once() - } - - test.enforcement.apply(req, m) - - m.AssertExpectations(t) - - }) - } -} - -func TestApplyNoneApplicable(t *testing.T) { - req := &openrtb2.BidRequest{} - - m := &mockScrubber{} - - enforcement := Enforcement{ - CCPA: false, - COPPA: false, - GDPRGeo: false, - GDPRID: false, - LMT: false, - - UFPD: false, - PreciseGeo: false, - TID: false, - Eids: false, - } - enforcement.apply(req, m) - - m.AssertNotCalled(t, "ScrubDevice") - m.AssertNotCalled(t, "ScrubUser") -} - -func TestApplyNil(t *testing.T) { - m := &mockScrubber{} - - enforcement := Enforcement{} - enforcement.apply(nil, m) - - m.AssertNotCalled(t, "ScrubDevice") - m.AssertNotCalled(t, "ScrubUser") -} - -type mockScrubber struct { - mock.Mock -} - -func (m *mockScrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest { - args := m.Called(bidRequest, enforcement) - return args.Get(0).(*openrtb2.BidRequest) -} - -func (m *mockScrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device { - args := m.Called(device, id, ipv4, ipv6, geo) - return args.Get(0).(*openrtb2.Device) -} - -func (m *mockScrubber) ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb2.User { - args := m.Called(user, strategy, geo) - return args.Get(0).(*openrtb2.User) -} diff --git a/privacy/gdpr/consentwriter.go b/privacy/gdpr/consentwriter.go index 00e3558fd40..243a6cf79e9 100644 --- a/privacy/gdpr/consentwriter.go +++ b/privacy/gdpr/consentwriter.go @@ -1,8 +1,8 @@ package gdpr import ( - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // ConsentWriter implements the PolicyWriter interface for GDPR TCF. diff --git a/privacy/gdpr/consentwriter_test.go b/privacy/gdpr/consentwriter_test.go index d90de4a2405..47f24bc9ecc 100644 --- a/privacy/gdpr/consentwriter_test.go +++ b/privacy/gdpr/consentwriter_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" ) diff --git a/privacy/lmt/ios.go b/privacy/lmt/ios.go index 0b308a9ce32..b7cb836cfe4 100644 --- a/privacy/lmt/ios.go +++ b/privacy/lmt/ios.go @@ -3,9 +3,9 @@ package lmt import ( "strings" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/util/iosutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/iosutil" ) var ( diff --git a/privacy/lmt/ios_test.go b/privacy/lmt/ios_test.go index 2a679bfbd99..83b59b287fd 100644 --- a/privacy/lmt/ios_test.go +++ b/privacy/lmt/ios_test.go @@ -4,8 +4,8 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/util/iosutil" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/util/iosutil" "github.com/stretchr/testify/assert" ) diff --git a/privacy/lmt/policy.go b/privacy/lmt/policy.go index 0272a4f96b9..b4ab10394fc 100644 --- a/privacy/lmt/policy.go +++ b/privacy/lmt/policy.go @@ -1,6 +1,6 @@ package lmt -import "github.com/prebid/openrtb/v19/openrtb2" +import "github.com/prebid/openrtb/v20/openrtb2" const ( trackingUnrestricted = 0 diff --git a/privacy/lmt/policy_test.go b/privacy/lmt/policy_test.go index 222f7aca0d1..29e9370b8c1 100644 --- a/privacy/lmt/policy_test.go +++ b/privacy/lmt/policy_test.go @@ -3,7 +3,7 @@ package lmt import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" ) diff --git a/privacy/rule_condition_test.go b/privacy/rule_condition_test.go index bb1d81c00d2..a0b3f855951 100644 --- a/privacy/rule_condition_test.go +++ b/privacy/rule_condition_test.go @@ -3,8 +3,8 @@ package privacy import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/privacy/scrubber.go b/privacy/scrubber.go index 59b74a2532b..7a67737f028 100644 --- a/privacy/scrubber.go +++ b/privacy/scrubber.go @@ -4,249 +4,146 @@ import ( "encoding/json" "net" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/util/iputil" - "github.com/prebid/prebid-server/util/ptrutil" + "github.com/prebid/prebid-server/v2/util/jsonutil" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/iputil" ) -// ScrubStrategyIPV4 defines the approach to scrub PII from an IPV4 address. -type ScrubStrategyIPV4 int - -const ( - // ScrubStrategyIPV4None does not remove any part of an IPV4 address. - ScrubStrategyIPV4None ScrubStrategyIPV4 = iota - - // ScrubStrategyIPV4Subnet zeroes out the last 8 bits of an IPV4 address. - ScrubStrategyIPV4Subnet -) - -// ScrubStrategyIPV6 defines the approach to scrub PII from an IPV6 address. -type ScrubStrategyIPV6 int - -const ( - // ScrubStrategyIPV6None does not remove any part of an IPV6 address. - ScrubStrategyIPV6None ScrubStrategyIPV6 = iota - - // ScrubStrategyIPV6Subnet zeroes out the last 16 bits of an IPV6 sub net address. - ScrubStrategyIPV6Subnet -) - -// ScrubStrategyGeo defines the approach to scrub PII from geographical data. -type ScrubStrategyGeo int - -const ( - // ScrubStrategyGeoNone does not remove any geographical data. - ScrubStrategyGeoNone ScrubStrategyGeo = iota - - // ScrubStrategyGeoFull removes all geographical data. - ScrubStrategyGeoFull - - // ScrubStrategyGeoReducedPrecision anonymizes geographical data with rounding. - ScrubStrategyGeoReducedPrecision -) - -// ScrubStrategyUser defines the approach to scrub PII from user data. -type ScrubStrategyUser int - -const ( - // ScrubStrategyUserNone does not remove non-location data. - ScrubStrategyUserNone ScrubStrategyUser = iota - - // ScrubStrategyUserIDAndDemographic removes the user's buyer id, exchange id year of birth, and gender. - ScrubStrategyUserIDAndDemographic -) - -// ScrubStrategyDeviceID defines the approach to remove hardware id and device id data. -type ScrubStrategyDeviceID int - -const ( - // ScrubStrategyDeviceIDNone does not remove hardware id and device id data. - ScrubStrategyDeviceIDNone ScrubStrategyDeviceID = iota - - // ScrubStrategyDeviceIDAll removes all hardware and device id data (ifa, mac hashes device id hashes) - ScrubStrategyDeviceIDAll -) - -// Scrubber removes PII from parts of an OpenRTB request. -type Scrubber interface { - ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest - ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device - ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb2.User +type IPConf struct { + IPV6 config.IPv6 + IPV4 config.IPv4 } -type scrubber struct { - ipV6 config.IPv6 - ipV4 config.IPv4 +func scrubDeviceIDs(reqWrapper *openrtb_ext.RequestWrapper) { + if reqWrapper.Device != nil { + reqWrapper.Device.DIDMD5 = "" + reqWrapper.Device.DIDSHA1 = "" + reqWrapper.Device.DPIDMD5 = "" + reqWrapper.Device.DPIDSHA1 = "" + reqWrapper.Device.IFA = "" + reqWrapper.Device.MACMD5 = "" + reqWrapper.Device.MACSHA1 = "" + } } -// NewScrubber returns an OpenRTB scrubber. -func NewScrubber(ipV6 config.IPv6, ipV4 config.IPv4) Scrubber { - return scrubber{ - ipV6: ipV6, - ipV4: ipV4, +func scrubUserIDs(reqWrapper *openrtb_ext.RequestWrapper) { + if reqWrapper.User != nil { + reqWrapper.User.Data = nil + reqWrapper.User.ID = "" + reqWrapper.User.BuyerUID = "" + reqWrapper.User.Yob = 0 + reqWrapper.User.Gender = "" + reqWrapper.User.Keywords = "" + reqWrapper.User.KwArray = nil } } -func (s scrubber) ScrubRequest(bidRequest *openrtb2.BidRequest, enforcement Enforcement) *openrtb2.BidRequest { - var userExtParsed map[string]json.RawMessage - userExtModified := false - - // expressed in two lines because IntelliJ cannot infer the generic type - var userCopy *openrtb2.User - userCopy = ptrutil.Clone(bidRequest.User) - - // expressed in two lines because IntelliJ cannot infer the generic type - var deviceCopy *openrtb2.Device - deviceCopy = ptrutil.Clone(bidRequest.Device) - - if userCopy != nil && (enforcement.UFPD || enforcement.Eids) { - if len(userCopy.Ext) != 0 { - json.Unmarshal(userCopy.Ext, &userExtParsed) - } +func scrubUserDemographics(reqWrapper *openrtb_ext.RequestWrapper) { + if reqWrapper.User != nil { + reqWrapper.User.BuyerUID = "" + reqWrapper.User.ID = "" + reqWrapper.User.Yob = 0 + reqWrapper.User.Gender = "" } +} - if enforcement.UFPD { - // transmitUfpd covers user.ext.data, user.data, user.id, user.buyeruid, user.yob, user.gender, user.keywords, user.kwarray - // and device.{ifa, macsha1, macmd5, dpidsha1, dpidmd5, didsha1, didmd5} - if deviceCopy != nil { - deviceCopy.DIDMD5 = "" - deviceCopy.DIDSHA1 = "" - deviceCopy.DPIDMD5 = "" - deviceCopy.DPIDSHA1 = "" - deviceCopy.IFA = "" - deviceCopy.MACMD5 = "" - deviceCopy.MACSHA1 = "" - } - if userCopy != nil { - userCopy.Data = nil - userCopy.ID = "" - userCopy.BuyerUID = "" - userCopy.Yob = 0 - userCopy.Gender = "" - userCopy.Keywords = "" - userCopy.KwArray = nil - - _, hasField := userExtParsed["data"] - if hasField { - delete(userExtParsed, "data") - userExtModified = true - } +func scrubUserExt(reqWrapper *openrtb_ext.RequestWrapper, fieldName string) error { + if reqWrapper.User != nil { + userExt, err := reqWrapper.GetUserExt() + if err != nil { + return err } - } - if enforcement.Eids { - //transmitEids covers user.eids and user.ext.eids - if userCopy != nil { - userCopy.EIDs = nil - _, hasField := userExtParsed["eids"] - if hasField { - delete(userExtParsed, "eids") - userExtModified = true - } + ext := userExt.GetExt() + _, hasField := ext[fieldName] + if hasField { + delete(ext, fieldName) + userExt.SetExt(ext) } } + return nil +} - if userExtModified { - userExt, _ := json.Marshal(userExtParsed) - userCopy.Ext = userExt +func ScrubEIDs(reqWrapper *openrtb_ext.RequestWrapper) error { + //transmitEids removes user.eids and user.ext.eids + if reqWrapper.User != nil { + reqWrapper.User.EIDs = nil } + return scrubUserExt(reqWrapper, "eids") +} - if enforcement.TID { - //remove source.tid and imp.ext.tid - if bidRequest.Source != nil { - sourceCopy := ptrutil.Clone(bidRequest.Source) - sourceCopy.TID = "" - bidRequest.Source = sourceCopy - } - for ind, imp := range bidRequest.Imp { - impExt := scrubExtIDs(imp.Ext, "tid") - bidRequest.Imp[ind].Ext = impExt - } +func ScrubTID(reqWrapper *openrtb_ext.RequestWrapper) { + if reqWrapper.Source != nil { + reqWrapper.Source.TID = "" } - - if enforcement.PreciseGeo { - //round user's geographic location by rounding off IP address and lat/lng data. - //this applies to both device.geo and user.geo - if userCopy != nil && userCopy.Geo != nil { - userCopy.Geo = scrubGeoPrecision(userCopy.Geo) - } - - if deviceCopy != nil { - if deviceCopy.Geo != nil { - deviceCopy.Geo = scrubGeoPrecision(deviceCopy.Geo) - } - deviceCopy.IP = scrubIP(deviceCopy.IP, s.ipV4.AnonKeepBits, iputil.IPv4BitSize) - deviceCopy.IPv6 = scrubIP(deviceCopy.IPv6, s.ipV6.AnonKeepBits, iputil.IPv6BitSize) - } + impWrapper := reqWrapper.GetImp() + for ind, imp := range impWrapper { + impExt := scrubExtIDs(imp.Ext, "tid") + impWrapper[ind].Ext = impExt } - - bidRequest.Device = deviceCopy - bidRequest.User = userCopy - return bidRequest + reqWrapper.SetImp(impWrapper) } -func (s scrubber) ScrubDevice(device *openrtb2.Device, id ScrubStrategyDeviceID, ipv4 ScrubStrategyIPV4, ipv6 ScrubStrategyIPV6, geo ScrubStrategyGeo) *openrtb2.Device { - if device == nil { - return nil - } - - deviceCopy := *device - - switch id { - case ScrubStrategyDeviceIDAll: - deviceCopy.DIDMD5 = "" - deviceCopy.DIDSHA1 = "" - deviceCopy.DPIDMD5 = "" - deviceCopy.DPIDSHA1 = "" - deviceCopy.IFA = "" - deviceCopy.MACMD5 = "" - deviceCopy.MACSHA1 = "" +func scrubGEO(reqWrapper *openrtb_ext.RequestWrapper) { + //round user's geographic location by rounding off IP address and lat/lng data. + //this applies to both device.geo and user.geo + if reqWrapper.User != nil && reqWrapper.User.Geo != nil { + reqWrapper.User.Geo = scrubGeoPrecision(reqWrapper.User.Geo) } - switch ipv4 { - case ScrubStrategyIPV4Subnet: - deviceCopy.IP = scrubIP(device.IP, s.ipV4.AnonKeepBits, iputil.IPv4BitSize) + if reqWrapper.Device != nil && reqWrapper.Device.Geo != nil { + reqWrapper.Device.Geo = scrubGeoPrecision(reqWrapper.Device.Geo) } +} - switch ipv6 { - case ScrubStrategyIPV6Subnet: - deviceCopy.IPv6 = scrubIP(device.IPv6, s.ipV6.AnonKeepBits, iputil.IPv6BitSize) +func scrubGeoFull(reqWrapper *openrtb_ext.RequestWrapper) { + if reqWrapper.User != nil && reqWrapper.User.Geo != nil { + reqWrapper.User.Geo = &openrtb2.Geo{} } - - switch geo { - case ScrubStrategyGeoFull: - deviceCopy.Geo = scrubGeoFull(device.Geo) - case ScrubStrategyGeoReducedPrecision: - deviceCopy.Geo = scrubGeoPrecision(device.Geo) + if reqWrapper.Device != nil && reqWrapper.Device.Geo != nil { + reqWrapper.Device.Geo = &openrtb2.Geo{} } - return &deviceCopy } -func (scrubber) ScrubUser(user *openrtb2.User, strategy ScrubStrategyUser, geo ScrubStrategyGeo) *openrtb2.User { - if user == nil { - return nil +func scrubDeviceIP(reqWrapper *openrtb_ext.RequestWrapper, ipConf IPConf) { + if reqWrapper.Device != nil { + reqWrapper.Device.IP = scrubIP(reqWrapper.Device.IP, ipConf.IPV4.AnonKeepBits, iputil.IPv4BitSize) + reqWrapper.Device.IPv6 = scrubIP(reqWrapper.Device.IPv6, ipConf.IPV6.AnonKeepBits, iputil.IPv6BitSize) } +} - userCopy := *user +func ScrubDeviceIDsIPsUserDemoExt(reqWrapper *openrtb_ext.RequestWrapper, ipConf IPConf, fieldName string, scrubFullGeo bool) { + scrubDeviceIDs(reqWrapper) + scrubDeviceIP(reqWrapper, ipConf) + scrubUserDemographics(reqWrapper) + scrubUserExt(reqWrapper, fieldName) - if strategy == ScrubStrategyUserIDAndDemographic { - userCopy.BuyerUID = "" - userCopy.ID = "" - userCopy.Ext = scrubExtIDs(userCopy.Ext, "eids") - userCopy.Yob = 0 - userCopy.Gender = "" + if scrubFullGeo { + scrubGeoFull(reqWrapper) + } else { + scrubGEO(reqWrapper) } +} - switch geo { - case ScrubStrategyGeoFull: - userCopy.Geo = scrubGeoFull(user.Geo) - case ScrubStrategyGeoReducedPrecision: - userCopy.Geo = scrubGeoPrecision(user.Geo) - } +func ScrubUserFPD(reqWrapper *openrtb_ext.RequestWrapper) { + scrubDeviceIDs(reqWrapper) + scrubUserIDs(reqWrapper) + scrubUserExt(reqWrapper, "data") + reqWrapper.User.EIDs = nil +} - return &userCopy +func ScrubGdprID(reqWrapper *openrtb_ext.RequestWrapper) { + scrubDeviceIDs(reqWrapper) + scrubUserDemographics(reqWrapper) + scrubUserExt(reqWrapper, "eids") +} + +func ScrubGeoAndDeviceIP(reqWrapper *openrtb_ext.RequestWrapper, ipConf IPConf) { + scrubDeviceIP(reqWrapper, ipConf) + scrubGEO(reqWrapper) } func scrubIP(ip string, ones, bits int) string { @@ -258,22 +155,25 @@ func scrubIP(ip string, ones, bits int) string { return ipMasked.String() } -func scrubGeoFull(geo *openrtb2.Geo) *openrtb2.Geo { +func scrubGeoPrecision(geo *openrtb2.Geo) *openrtb2.Geo { if geo == nil { return nil } - return &openrtb2.Geo{} -} + geoCopy := *geo -func scrubGeoPrecision(geo *openrtb2.Geo) *openrtb2.Geo { - if geo == nil { - return nil + if geoCopy.Lat != nil { + lat := *geo.Lat + lat = float64(int(lat*100.0+0.5)) / 100.0 + geoCopy.Lat = &lat + } + + if geoCopy.Lon != nil { + lon := *geo.Lon + lon = float64(int(lon*100.0+0.5)) / 100.0 + geoCopy.Lon = &lon } - geoCopy := *geo - geoCopy.Lat = float64(int(geo.Lat*100.0+0.5)) / 100.0 // Round Latitude - geoCopy.Lon = float64(int(geo.Lon*100.0+0.5)) / 100.0 // Round Longitude return &geoCopy } @@ -283,7 +183,7 @@ func scrubExtIDs(ext json.RawMessage, fieldName string) json.RawMessage { } var userExtParsed map[string]json.RawMessage - err := json.Unmarshal(ext, &userExtParsed) + err := jsonutil.Unmarshal(ext, &userExtParsed) if err != nil { return ext } @@ -291,7 +191,7 @@ func scrubExtIDs(ext json.RawMessage, fieldName string) json.RawMessage { _, hasField := userExtParsed[fieldName] if hasField { delete(userExtParsed, fieldName) - result, err := json.Marshal(userExtParsed) + result, err := jsonutil.Marshal(userExtParsed) if err == nil { return result } diff --git a/privacy/scrubber_test.go b/privacy/scrubber_test.go index 59e593fc167..d06e0c9842e 100644 --- a/privacy/scrubber_test.go +++ b/privacy/scrubber_test.go @@ -2,454 +2,350 @@ package privacy import ( "encoding/json" - "github.com/prebid/prebid-server/config" "testing" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" ) -func TestScrubDevice(t *testing.T) { - device := getTestDevice() - +func TestScrubDeviceIDs(t *testing.T) { testCases := []struct { - description string - expected *openrtb2.Device - id ScrubStrategyDeviceID - ipv4 ScrubStrategyIPV4 - ipv6 ScrubStrategyIPV6 - geo ScrubStrategyGeo + name string + deviceIn *openrtb2.Device + expectedDevice *openrtb2.Device }{ { - description: "All Strategies - None", - expected: device, - id: ScrubStrategyDeviceIDNone, - ipv4: ScrubStrategyIPV4None, - ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoNone, - }, - { - description: "All Strategies - Strictest", - expected: &openrtb2.Device{ - DIDMD5: "", - DIDSHA1: "", - DPIDMD5: "", - DPIDSHA1: "", - MACSHA1: "", - MACMD5: "", - IFA: "", - IP: "1.2.3.0", - IPv6: "2001:1db8:2233:4400::", - Geo: &openrtb2.Geo{}, - }, - id: ScrubStrategyDeviceIDAll, - ipv4: ScrubStrategyIPV4Subnet, - ipv6: ScrubStrategyIPV6Subnet, - geo: ScrubStrategyGeoFull, - }, - { - description: "Isolated - ID - All", - expected: &openrtb2.Device{ - DIDMD5: "", - DIDSHA1: "", - DPIDMD5: "", - DPIDSHA1: "", - MACSHA1: "", - MACMD5: "", - IFA: "", - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: device.Geo, - }, - id: ScrubStrategyDeviceIDAll, - ipv4: ScrubStrategyIPV4None, - ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoNone, - }, - { - description: "Isolated - IPv4 - Lowest 8", - expected: &openrtb2.Device{ - DIDMD5: "anyDIDMD5", - DIDSHA1: "anyDIDSHA1", - DPIDMD5: "anyDPIDMD5", - DPIDSHA1: "anyDPIDSHA1", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IFA: "anyIFA", - IP: "1.2.3.0", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: device.Geo, - }, - id: ScrubStrategyDeviceIDNone, - ipv4: ScrubStrategyIPV4Subnet, - ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoNone, - }, - { - description: "Isolated - IPv6", - expected: &openrtb2.Device{ - DIDMD5: "anyDIDMD5", - DIDSHA1: "anyDIDSHA1", - DPIDMD5: "anyDPIDMD5", - DPIDSHA1: "anyDPIDSHA1", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IFA: "anyIFA", - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4400::", - Geo: device.Geo, - }, - id: ScrubStrategyDeviceIDNone, - ipv4: ScrubStrategyIPV4None, - ipv6: ScrubStrategyIPV6Subnet, - geo: ScrubStrategyGeoNone, - }, - { - description: "Isolated - Geo - Reduced Precision", - expected: &openrtb2.Device{ - DIDMD5: "anyDIDMD5", - DIDSHA1: "anyDIDSHA1", - DPIDMD5: "anyDPIDMD5", - DPIDSHA1: "anyDPIDSHA1", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IFA: "anyIFA", - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: &openrtb2.Geo{ - Lat: 123.46, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - id: ScrubStrategyDeviceIDNone, - ipv4: ScrubStrategyIPV4None, - ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "Isolated - Geo - Full", - expected: &openrtb2.Device{ - DIDMD5: "anyDIDMD5", - DIDSHA1: "anyDIDSHA1", - DPIDMD5: "anyDPIDMD5", - DPIDSHA1: "anyDPIDSHA1", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IFA: "anyIFA", - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: &openrtb2.Geo{}, - }, - id: ScrubStrategyDeviceIDNone, - ipv4: ScrubStrategyIPV4None, - ipv6: ScrubStrategyIPV6None, - geo: ScrubStrategyGeoFull, + name: "all", + deviceIn: &openrtb2.Device{DIDMD5: "MD5", DIDSHA1: "SHA1", DPIDMD5: "MD5", DPIDSHA1: "SHA1", IFA: "IFA", MACMD5: "MD5", MACSHA1: "SHA1"}, + expectedDevice: &openrtb2.Device{DIDMD5: "", DIDSHA1: "", DPIDMD5: "", DPIDSHA1: "", IFA: "", MACMD5: "", MACSHA1: ""}, + }, + { + name: "nil", + deviceIn: nil, + expectedDevice: nil, }, } - testIPMasking := getTestIPMasking() for _, test := range testCases { - result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubDevice(device, test.id, test.ipv4, test.ipv6, test.geo) - assert.Equal(t, test.expected, result, test.description) + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Device: test.deviceIn}} + scrubDeviceIDs(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedDevice, brw.Device) + }) } } -func TestScrubDeviceNil(t *testing.T) { - testIPMasking := getTestIPMasking() - result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubDevice(nil, ScrubStrategyDeviceIDNone, ScrubStrategyIPV4None, ScrubStrategyIPV6None, ScrubStrategyGeoNone) - assert.Nil(t, result) +func TestScrubUserIDs(t *testing.T) { + testCases := []struct { + name string + userIn *openrtb2.User + expectedUser *openrtb2.User + }{ + { + name: "all", + userIn: &openrtb2.User{Data: []openrtb2.Data{}, ID: "ID", BuyerUID: "bID", Yob: 2000, Gender: "M", Keywords: "keywords", KwArray: nil}, + expectedUser: &openrtb2.User{Data: nil, ID: "", BuyerUID: "", Yob: 0, Gender: "", Keywords: "", KwArray: nil}, + }, + { + name: "nil", + userIn: nil, + expectedUser: nil, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn}} + scrubUserIDs(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedUser, brw.User) + }) + } } -func TestScrubUser(t *testing.T) { - user := getTestUser() - +func TestScrubUserDemographics(t *testing.T) { testCases := []struct { - description string - expected *openrtb2.User - scrubUser ScrubStrategyUser - scrubGeo ScrubStrategyGeo + name string + userIn *openrtb2.User + expectedUser *openrtb2.User }{ { - description: "User ID And Demographic & Geo Full", - expected: &openrtb2.User{ - ID: "", - BuyerUID: "", - Yob: 0, - Gender: "", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{}, - }, - scrubUser: ScrubStrategyUserIDAndDemographic, - scrubGeo: ScrubStrategyGeoFull, - }, - { - description: "User ID And Demographic & Geo Reduced", - expected: &openrtb2.User{ - ID: "", - BuyerUID: "", - Yob: 0, - Gender: "", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{ - Lat: 123.46, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - scrubUser: ScrubStrategyUserIDAndDemographic, - scrubGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "User ID And Demographic & Geo None", - expected: &openrtb2.User{ - ID: "", - BuyerUID: "", - Yob: 0, - Gender: "", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{ - Lat: 123.456, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - scrubUser: ScrubStrategyUserIDAndDemographic, - scrubGeo: ScrubStrategyGeoNone, - }, - { - description: "User None & Geo Full", - expected: &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{}, - }, - scrubUser: ScrubStrategyUserNone, - scrubGeo: ScrubStrategyGeoFull, - }, - { - description: "User None & Geo Reduced", - expected: &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{ - Lat: 123.46, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - scrubUser: ScrubStrategyUserNone, - scrubGeo: ScrubStrategyGeoReducedPrecision, - }, - { - description: "User None & Geo None", - expected: &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{ - Lat: 123.456, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - scrubUser: ScrubStrategyUserNone, - scrubGeo: ScrubStrategyGeoNone, + name: "all", + userIn: &openrtb2.User{ID: "ID", BuyerUID: "bID", Yob: 2000, Gender: "M"}, + expectedUser: &openrtb2.User{ID: "", BuyerUID: "", Yob: 0, Gender: ""}, + }, + { + name: "nil", + userIn: nil, + expectedUser: nil, }, } - - testIPMasking := getTestIPMasking() for _, test := range testCases { - result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubUser(user, test.scrubUser, test.scrubGeo) - assert.Equal(t, test.expected, result, test.description) + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn}} + scrubUserDemographics(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedUser, brw.User) + }) } } -func TestScrubUserNil(t *testing.T) { - testIPMasking := getTestIPMasking() - result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubUser(nil, ScrubStrategyUserNone, ScrubStrategyGeoNone) - assert.Nil(t, result) +func TestScrubUserExt(t *testing.T) { + testCases := []struct { + name string + userIn *openrtb2.User + fieldName string + expectedUser *openrtb2.User + }{ + { + name: "nil_user", + userIn: nil, + expectedUser: nil, + }, + { + name: "nil_ext", + userIn: &openrtb2.User{ID: "ID", Ext: nil}, + expectedUser: &openrtb2.User{ID: "ID", Ext: nil}, + }, + { + name: "empty_ext", + userIn: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{}`)}, + expectedUser: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{}`)}, + }, + { + name: "ext_with_field", + userIn: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{"data":"123","test":1}`)}, + fieldName: "data", + expectedUser: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{"test":1}`)}, + }, + { + name: "ext_without_field", + userIn: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{"data":"123","test":1}`)}, + fieldName: "noData", + expectedUser: &openrtb2.User{ID: "ID", Ext: json.RawMessage(`{"data":"123","test":1}`)}, + }, + { + name: "nil", + userIn: nil, + expectedUser: nil, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn}} + scrubUserExt(brw, test.fieldName) + brw.RebuildRequest() + assert.Equal(t, test.expectedUser, brw.User) + }) + } } -func TestScrubRequest(t *testing.T) { - - imps := []openrtb2.Imp{ - {ID: "testId", Ext: json.RawMessage(`{"test": 1, "tid": 2}`)}, +func TestScrubEids(t *testing.T) { + testCases := []struct { + name string + userIn *openrtb2.User + expectedUser *openrtb2.User + }{ + { + name: "eids", + userIn: &openrtb2.User{ID: "ID", EIDs: []openrtb2.EID{}}, + expectedUser: &openrtb2.User{ID: "ID", EIDs: nil}, + }, + { + name: "nil_eids", + userIn: &openrtb2.User{ID: "ID", EIDs: nil}, + expectedUser: &openrtb2.User{ID: "ID", EIDs: nil}, + }, + { + name: "nil", + userIn: nil, + expectedUser: nil, + }, } - source := &openrtb2.Source{ - TID: "testTid", + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn}} + ScrubEIDs(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedUser, brw.User) + }) } - device := getTestDevice() - user := getTestUser() - user.Ext = json.RawMessage(`{"data": 1, "eids": 2}`) - user.EIDs = []openrtb2.EID{{Source: "test"}} +} +func TestScrubTID(t *testing.T) { testCases := []struct { - description string - enforcement Enforcement - userExtPresent bool - expected *openrtb2.BidRequest + name string + sourceIn *openrtb2.Source + impIn []openrtb2.Imp + expectedSource *openrtb2.Source + expectedImp []openrtb2.Imp }{ { - description: "enforce transmitUFPD with user.ext", - enforcement: Enforcement{UFPD: true}, - userExtPresent: true, - expected: &openrtb2.BidRequest{ - Imp: imps, - Source: source, - User: &openrtb2.User{ - EIDs: []openrtb2.EID{{Source: "test"}}, - Geo: user.Geo, - Ext: json.RawMessage(`{"eids":2}`), - }, - Device: &openrtb2.Device{ - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: device.Geo, - }, - }, - }, - { - description: "enforce transmitUFPD without user.ext", - enforcement: Enforcement{UFPD: true}, - userExtPresent: false, - expected: &openrtb2.BidRequest{ - Imp: imps, - Source: source, - User: &openrtb2.User{ - EIDs: []openrtb2.EID{{Source: "test"}}, - Geo: user.Geo, - }, - Device: &openrtb2.Device{ - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: device.Geo, - }, - }, - }, - { - description: "enforce transmitEids", - enforcement: Enforcement{Eids: true}, - userExtPresent: true, - expected: &openrtb2.BidRequest{ - Imp: imps, - Source: source, - Device: device, - User: &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Geo: user.Geo, - EIDs: nil, - Ext: json.RawMessage(`{"data":1}`), - }, - }, - }, - { - description: "enforce transmitTid", - enforcement: Enforcement{TID: true}, - userExtPresent: true, - expected: &openrtb2.BidRequest{ - Imp: []openrtb2.Imp{ - {ID: "testId", Ext: json.RawMessage(`{"test":1}`)}, - }, - Source: &openrtb2.Source{ - TID: "", - }, - Device: device, - User: &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Geo: user.Geo, - EIDs: []openrtb2.EID{{Source: "test"}}, - Ext: json.RawMessage(`{"data": 1, "eids": 2}`), - }, - }, - }, - { - description: "enforce precise Geo", - enforcement: Enforcement{PreciseGeo: true}, - userExtPresent: true, - expected: &openrtb2.BidRequest{ - Imp: imps, - Source: source, - User: &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Geo: &openrtb2.Geo{ - Lat: 123.46, Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - EIDs: []openrtb2.EID{{Source: "test"}}, - Ext: json.RawMessage(`{"data": 1, "eids": 2}`), - }, - Device: &openrtb2.Device{ - IFA: "anyIFA", - DIDSHA1: "anyDIDSHA1", - DIDMD5: "anyDIDMD5", - DPIDSHA1: "anyDPIDSHA1", - DPIDMD5: "anyDPIDMD5", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IP: "1.2.3.0", - IPv6: "2001:1db8:2233:4400::", - Geo: &openrtb2.Geo{ - Lat: 123.46, Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - }, - }, + name: "nil", + sourceIn: nil, + expectedSource: nil, }, + { + name: "nil_imp_ext", + sourceIn: &openrtb2.Source{TID: "tid"}, + impIn: []openrtb2.Imp{{ID: "impID", Ext: nil}}, + expectedSource: &openrtb2.Source{TID: ""}, + expectedImp: []openrtb2.Imp{{ID: "impID", Ext: nil}}, + }, + { + name: "empty_imp_ext", + sourceIn: &openrtb2.Source{TID: "tid"}, + impIn: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{}`)}}, + expectedSource: &openrtb2.Source{TID: ""}, + expectedImp: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{}`)}}, + }, + { + name: "ext_with_tid", + sourceIn: &openrtb2.Source{TID: "tid"}, + impIn: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{"tid":"123","test":1}`)}}, + expectedSource: &openrtb2.Source{TID: ""}, + expectedImp: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{"test":1}`)}}, + }, + { + name: "ext_without_tid", + sourceIn: &openrtb2.Source{TID: "tid"}, + impIn: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{"data":"123","test":1}`)}}, + expectedSource: &openrtb2.Source{TID: ""}, + expectedImp: []openrtb2.Imp{{ID: "impID", Ext: json.RawMessage(`{"data":"123","test":1}`)}}, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{Source: test.sourceIn, Imp: test.impIn}} + ScrubTID(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedSource, brw.Source) + assert.Equal(t, test.expectedImp, brw.Imp) + }) } +} - testIPMasking := getTestIPMasking() +func TestScrubGEO(t *testing.T) { + testCases := []struct { + name string + userIn *openrtb2.User + expectedUser *openrtb2.User + deviceIn *openrtb2.Device + expectedDevice *openrtb2.Device + }{ + { + name: "nil", + userIn: nil, + expectedUser: nil, + deviceIn: nil, + expectedDevice: nil, + }, + { + name: "nil_user_geo", + userIn: &openrtb2.User{ID: "ID", Geo: nil}, + expectedUser: &openrtb2.User{ID: "ID", Geo: nil}, + deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, + expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.12)}}, + }, + { + name: "with_user_geo", + userIn: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, + expectedUser: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.12)}}, + deviceIn: &openrtb2.Device{}, + expectedDevice: &openrtb2.Device{}, + }, + { + name: "nil_device_geo", + userIn: &openrtb2.User{}, + expectedUser: &openrtb2.User{}, + deviceIn: &openrtb2.Device{Geo: nil}, + expectedDevice: &openrtb2.Device{Geo: nil}, + }, + { + name: "with_device_geo", + userIn: &openrtb2.User{}, + expectedUser: &openrtb2.User{}, + deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, + expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.12)}}, + }, + { + name: "with_user_and_device_geo", + userIn: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, + expectedUser: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.12)}}, + deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, + expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.12)}}, + }, + } for _, test := range testCases { - t.Run(test.description, func(t *testing.T) { - bidRequest := &openrtb2.BidRequest{ - Imp: []openrtb2.Imp{ - {ID: "testId", Ext: json.RawMessage(`{"test": 1, "tid": 2}`)}, - }, - Source: &openrtb2.Source{ - TID: "testTid", - }, - User: getTestUser(), - Device: getTestDevice(), - } - if test.userExtPresent { - bidRequest.User.Ext = json.RawMessage(`{"data": 1, "eids": 2}`) - } else { - bidRequest.User.Ext = nil - } - bidRequest.User.EIDs = []openrtb2.EID{{Source: "test"}} + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn, Device: test.deviceIn}} + scrubGEO(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedUser, brw.User) + assert.Equal(t, test.expectedDevice, brw.Device) + }) + } +} - result := NewScrubber(testIPMasking.IPv6Config, testIPMasking.IPv4Config).ScrubRequest(bidRequest, test.enforcement) - assert.Equal(t, test.expected, result, test.description) +func TestScrubGeoFull(t *testing.T) { + testCases := []struct { + name string + userIn *openrtb2.User + expectedUser *openrtb2.User + deviceIn *openrtb2.Device + expectedDevice *openrtb2.Device + }{ + { + name: "nil", + userIn: nil, + expectedUser: nil, + deviceIn: nil, + expectedDevice: nil, + }, + { + name: "nil_user_geo", + userIn: &openrtb2.User{ID: "ID", Geo: nil}, + expectedUser: &openrtb2.User{ID: "ID", Geo: nil}, + deviceIn: &openrtb2.Device{}, + expectedDevice: &openrtb2.Device{}, + }, + { + name: "with_user_geo", + userIn: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, + expectedUser: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{}}, + deviceIn: &openrtb2.Device{}, + expectedDevice: &openrtb2.Device{}, + }, + { + name: "nil_device_geo", + userIn: &openrtb2.User{}, + expectedUser: &openrtb2.User{}, + deviceIn: &openrtb2.Device{Geo: nil}, + expectedDevice: &openrtb2.Device{Geo: nil}, + }, + { + name: "with_device_geo", + userIn: &openrtb2.User{}, + expectedUser: &openrtb2.User{}, + deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, + expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{}}, + }, + { + name: "with_user_and_device_geo", + userIn: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, + expectedUser: &openrtb2.User{ID: "ID", Geo: &openrtb2.Geo{}}, + deviceIn: &openrtb2.Device{Geo: &openrtb2.Geo{Lat: ptrutil.ToPtr(123.123)}}, + expectedDevice: &openrtb2.Device{Geo: &openrtb2.Geo{}}, + }, + } + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + brw := &openrtb_ext.RequestWrapper{BidRequest: &openrtb2.BidRequest{User: test.userIn, Device: test.deviceIn}} + scrubGeoFull(brw) + brw.RebuildRequest() + assert.Equal(t, test.expectedUser, brw.User) + assert.Equal(t, test.expectedDevice, brw.Device) }) } } @@ -509,42 +405,6 @@ func TestScrubIP(t *testing.T) { bits: 128, maskBits: 96, }, - { - IP: "2001:1db8:0000:0000:0000:ff00:0042:8329", - cleanedIP: "2001:1db8::ff00:42:0", - bits: 128, - maskBits: 112, - }, - { - IP: "2001:1db8:0000:0000:0000:ff00:0042:0", - cleanedIP: "2001:1db8::ff00:42:0", - bits: 128, - maskBits: 112, - }, - { - IP: "127.0.0.1", - cleanedIP: "127.0.0.0", - bits: 32, - maskBits: 24, - }, - { - IP: "0.0.0.0", - cleanedIP: "0.0.0.0", - bits: 32, - maskBits: 24, - }, - { - IP: "192.127.111.134", - cleanedIP: "192.127.111.0", - bits: 32, - maskBits: 24, - }, - { - IP: "192.127.111.0", - cleanedIP: "192.127.111.0", - bits: 32, - maskBits: 24, - }, } for _, test := range testCases { t.Run(test.IP, func(t *testing.T) { @@ -555,43 +415,17 @@ func TestScrubIP(t *testing.T) { } } -func TestScrubGeoFull(t *testing.T) { - geo := &openrtb2.Geo{ - Lat: 123.456, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - } - geoExpected := &openrtb2.Geo{ - Lat: 0, - Lon: 0, - Metro: "", - City: "", - ZIP: "", - } - - result := scrubGeoFull(geo) - - assert.Equal(t, geoExpected, result) -} - -func TestScrubGeoFullWhenNil(t *testing.T) { - result := scrubGeoFull(nil) - assert.Nil(t, result) -} - func TestScrubGeoPrecision(t *testing.T) { geo := &openrtb2.Geo{ - Lat: 123.456, - Lon: 678.89, + Lat: ptrutil.ToPtr(123.456), + Lon: ptrutil.ToPtr(678.89), Metro: "some metro", City: "some city", ZIP: "some zip", } geoExpected := &openrtb2.Geo{ - Lat: 123.46, - Lon: 678.89, + Lat: ptrutil.ToPtr(123.46), + Lon: ptrutil.ToPtr(678.89), Metro: "some metro", City: "some city", ZIP: "some zip", @@ -653,26 +487,11 @@ func TestScrubUserExtIDs(t *testing.T) { userExt: json.RawMessage(`{"anyExisting":{"existing":42},"eids":[{"source":"anySource","id":"anyId","uids":[{"id":"anyId","ext":{"id":42}}],"ext":{"id":42}}]}`), expected: json.RawMessage(`{"anyExisting":{"existing":42}}`), }, - { - description: "Remove eids Only", - userExt: json.RawMessage(`{"eids":[{"source":"anySource","id":"anyId","uids":[{"id":"anyId","ext":{"id":42}}],"ext":{"id":42}}]}`), - expected: json.RawMessage(`{}`), - }, { description: "Remove eids Only - Empty Array", userExt: json.RawMessage(`{"eids":[]}`), expected: json.RawMessage(`{}`), }, - { - description: "Remove eids Only - With Other Data", - userExt: json.RawMessage(`{"anyExisting":42,"eids":[{"source":"anySource","id":"anyId","uids":[{"id":"anyId","ext":{"id":42}}],"ext":{"id":42}}]}`), - expected: json.RawMessage(`{"anyExisting":42}`), - }, - { - description: "Remove eids Only - With Other Nested Data", - userExt: json.RawMessage(`{"anyExisting":{"existing":42},"eids":[{"source":"anySource","id":"anyId","uids":[{"id":"anyId","ext":{"id":42}}],"ext":{"id":42}}]}`), - expected: json.RawMessage(`{"anyExisting":{"existing":42}}`), - }, } for _, test := range testCases { @@ -680,52 +499,3 @@ func TestScrubUserExtIDs(t *testing.T) { assert.Equal(t, test.expected, result, test.description) } } - -func getTestUser() *openrtb2.User { - return &openrtb2.User{ - ID: "anyID", - BuyerUID: "anyBuyerUID", - Yob: 42, - Gender: "anyGender", - Ext: json.RawMessage(`{}`), - Geo: &openrtb2.Geo{ - Lat: 123.456, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - } -} - -func getTestDevice() *openrtb2.Device { - return &openrtb2.Device{ - DIDMD5: "anyDIDMD5", - DIDSHA1: "anyDIDSHA1", - DPIDMD5: "anyDPIDMD5", - DPIDSHA1: "anyDPIDSHA1", - MACSHA1: "anyMACSHA1", - MACMD5: "anyMACMD5", - IFA: "anyIFA", - IP: "1.2.3.4", - IPv6: "2001:1db8:2233:4455:6677:ff00:0042:8329", - Geo: &openrtb2.Geo{ - Lat: 123.456, - Lon: 678.89, - Metro: "some metro", - City: "some city", - ZIP: "some zip", - }, - } -} - -func getTestIPMasking() config.AccountPrivacy { - return config.AccountPrivacy{ - IPv6Config: config.IPv6{ - 54, - }, - IPv4Config: config.IPv4{ - 24, - }, - } -} diff --git a/privacy/writer.go b/privacy/writer.go index 88b7c6ee5d6..1f4a13cd6a4 100644 --- a/privacy/writer.go +++ b/privacy/writer.go @@ -1,6 +1,6 @@ package privacy -import "github.com/prebid/openrtb/v19/openrtb2" +import "github.com/prebid/openrtb/v20/openrtb2" // PolicyWriter mutates an OpenRTB bid request with a policy's regulatory information. type PolicyWriter interface { diff --git a/privacy/writer_test.go b/privacy/writer_test.go index 1ee94abaf22..47b4365d6c0 100644 --- a/privacy/writer_test.go +++ b/privacy/writer_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" ) diff --git a/router/admin.go b/router/admin.go index 29cdbbe5e23..1be7c8656da 100644 --- a/router/admin.go +++ b/router/admin.go @@ -5,9 +5,9 @@ import ( "net/http/pprof" "time" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/endpoints" - "github.com/prebid/prebid-server/version" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/endpoints" + "github.com/prebid/prebid-server/v2/version" ) func Admin(rateConverter *currency.RateConverter, rateConverterFetchingInterval time.Duration) *http.ServeMux { diff --git a/router/aspects/request_timeout_handler.go b/router/aspects/request_timeout_handler.go index 39a4341f995..7b94c96b11b 100644 --- a/router/aspects/request_timeout_handler.go +++ b/router/aspects/request_timeout_handler.go @@ -6,8 +6,8 @@ import ( "time" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" ) func QueuedRequestTimeout(f httprouter.Handle, reqTimeoutHeaders config.RequestTimeoutHeaders, metricsEngine metrics.MetricsEngine, requestType metrics.RequestType) httprouter.Handle { diff --git a/router/aspects/request_timeout_handler_test.go b/router/aspects/request_timeout_handler_test.go index 26e546dcd40..f77d48aa6e6 100644 --- a/router/aspects/request_timeout_handler_test.go +++ b/router/aspects/request_timeout_handler_test.go @@ -8,8 +8,8 @@ import ( "time" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" "github.com/stretchr/testify/assert" ) @@ -101,7 +101,7 @@ func ExecuteAspectRequest(t *testing.T, timeInQueue string, reqTimeout string, s req.Header.Set(reqTimeoutHeaderName, reqTimeout) } - customHeaders := config.RequestTimeoutHeaders{reqTimeInQueueHeaderName, reqTimeoutHeaderName} + customHeaders := config.RequestTimeoutHeaders{RequestTimeInQueue: reqTimeInQueueHeaderName, RequestTimeoutInQueue: reqTimeoutHeaderName} metrics := &metrics.MetricsEngineMock{} diff --git a/router/bidder_params_tests/appnexus.json b/router/bidder_params_tests/appnexus.json new file mode 100644 index 00000000000..11dedd41e49 --- /dev/null +++ b/router/bidder_params_tests/appnexus.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Sample schema", + "description": "A sample schema to test the bidder/params endpoint", + "type": "object", + "properties": { + "integer_param": { + "type": "integer", + "minimum": 1, + "description": "A customer id" + }, + "string_param_1": { + "type": "string", + "minLength": 1, + "description": "Text with blanks in between" + }, + "string_param_2": { + "type": "string", + "minLength": 1, + "description": "Text_with_no_blanks_in_between" + } + }, + "required": [ + "integer_param", + "string_param_2" + ] +} diff --git a/router/router.go b/router/router.go index a17fb327747..ddc84bd3a4c 100644 --- a/router/router.go +++ b/router/router.go @@ -11,34 +11,35 @@ import ( "strings" "time" - analyticsConf "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/endpoints" - "github.com/prebid/prebid-server/endpoints/events" - infoEndpoints "github.com/prebid/prebid-server/endpoints/info" - "github.com/prebid/prebid-server/endpoints/openrtb2" - "github.com/prebid/prebid-server/errortypes" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/experiment/adscert" - "github.com/prebid/prebid-server/floors" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/hooks" - "github.com/prebid/prebid-server/macros" - "github.com/prebid/prebid-server/metrics" - metricsConf "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/modules" - "github.com/prebid/prebid-server/modules/moduledeps" - "github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/pbs" - pbc "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/router/aspects" - "github.com/prebid/prebid-server/server/ssl" - storedRequestsConf "github.com/prebid/prebid-server/stored_requests/config" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/uuidutil" - "github.com/prebid/prebid-server/version" + analyticsBuild "github.com/prebid/prebid-server/v2/analytics/build" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/endpoints" + "github.com/prebid/prebid-server/v2/endpoints/events" + infoEndpoints "github.com/prebid/prebid-server/v2/endpoints/info" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/experiment/adscert" + "github.com/prebid/prebid-server/v2/floors" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/hooks" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/metrics" + metricsConf "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/modules" + "github.com/prebid/prebid-server/v2/modules/moduledeps" + "github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/pbs" + pbc "github.com/prebid/prebid-server/v2/prebid_cache_client" + "github.com/prebid/prebid-server/v2/router/aspects" + "github.com/prebid/prebid-server/v2/server/ssl" + storedRequestsConf "github.com/prebid/prebid-server/v2/stored_requests/config" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/prebid/prebid-server/v2/util/uuidutil" + "github.com/prebid/prebid-server/v2/version" _ "github.com/go-sql-driver/mysql" "github.com/golang/glog" @@ -94,7 +95,7 @@ func newJsonDirectoryServer(schemaDirectory string, validator openrtb_ext.Bidder data[aliasName] = bidderData } - response, err := json.Marshal(data) + response, err := jsonutil.Marshal(data) if err != nil { glog.Fatalf("Failed to marshal bidder param JSON-schema: %v", err) } @@ -172,6 +173,16 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R }, } + floorFechterHttpClient := &http.Client{ + Transport: &http.Transport{ + Proxy: http.ProxyFromEnvironment, + MaxConnsPerHost: cfg.PriceFloors.Fetcher.HttpClient.MaxConnsPerHost, + MaxIdleConns: cfg.PriceFloors.Fetcher.HttpClient.MaxIdleConns, + MaxIdleConnsPerHost: cfg.PriceFloors.Fetcher.HttpClient.MaxIdleConnsPerHost, + IdleConnTimeout: time.Duration(cfg.PriceFloors.Fetcher.HttpClient.IdleConnTimeout) * time.Second, + }, + } + if err := checkSupportedUserSyncEndpoints(cfg.BidderInfos); err != nil { return nil, err } @@ -205,11 +216,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R // todo(zachbadgett): better shutdown r.Shutdown = shutdown - //Price Floor Fetcher - priceFloorFetcher := floors.NewPriceFloorFetcher(cfg.PriceFloorFetcher.Worker, cfg.PriceFloorFetcher.Capacity, - cfg.AccountDefaults.PriceFloors.Fetch.Period, cfg.AccountDefaults.PriceFloors.Fetch.MaxAge, r.MetricsEngine) - - pbsAnalytics := analyticsConf.NewPBSAnalytics(&cfg.Analytics) + analyticsRunner := analyticsBuild.New(&cfg.Analytics) paramsValidator, err := openrtb_ext.NewBidderParamsValidator(schemaDirectory) if err != nil { @@ -249,22 +256,24 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R glog.Fatalf("Failed to create ads cert signer: %v", err) } + priceFloorFetcher := floors.NewPriceFloorFetcher(cfg.PriceFloors, floorFechterHttpClient, r.MetricsEngine) + tmaxAdjustments := exchange.ProcessTMaxAdjustments(cfg.TmaxAdjustments) planBuilder := hooks.NewExecutionPlanBuilder(cfg.Hooks, repo) macroReplacer := macros.NewStringIndexBasedReplacer() theExchange := exchange.NewExchange(adapters, cacheClient, cfg, syncersByBidder, r.MetricsEngine, cfg.BidderInfos, gdprPermsBuilder, rateConvertor, categoriesFetcher, adsCertSigner, macroReplacer, priceFloorFetcher) var uuidGenerator uuidutil.UUIDRandomGenerator - openrtbEndpoint, err := openrtb2.NewEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, storedRespFetcher, planBuilder, tmaxAdjustments) + openrtbEndpoint, err := openrtb2.NewEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, accounts, cfg, r.MetricsEngine, analyticsRunner, disabledBidders, defReqJSON, activeBidders, storedRespFetcher, planBuilder, tmaxAdjustments) if err != nil { glog.Fatalf("Failed to create the openrtb2 endpoint handler. %v", err) } - ampEndpoint, err := openrtb2.NewAmpEndpoint(uuidGenerator, theExchange, paramsValidator, ampFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, storedRespFetcher, planBuilder, tmaxAdjustments) + ampEndpoint, err := openrtb2.NewAmpEndpoint(uuidGenerator, theExchange, paramsValidator, ampFetcher, accounts, cfg, r.MetricsEngine, analyticsRunner, disabledBidders, defReqJSON, activeBidders, storedRespFetcher, planBuilder, tmaxAdjustments) if err != nil { glog.Fatalf("Failed to create the amp endpoint handler. %v", err) } - videoEndpoint, err := openrtb2.NewVideoEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, videoFetcher, accounts, cfg, r.MetricsEngine, pbsAnalytics, disabledBidders, defReqJSON, activeBidders, cacheClient, tmaxAdjustments) + videoEndpoint, err := openrtb2.NewVideoEndpoint(uuidGenerator, theExchange, paramsValidator, fetcher, videoFetcher, accounts, cfg, r.MetricsEngine, analyticsRunner, disabledBidders, defReqJSON, activeBidders, cacheClient, tmaxAdjustments) if err != nil { glog.Fatalf("Failed to create the video endpoint handler. %v", err) } @@ -280,7 +289,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R r.GET("/info/bidders", infoEndpoints.NewBiddersEndpoint(cfg.BidderInfos, defaultAliases)) r.GET("/info/bidders/:bidderName", infoEndpoints.NewBiddersDetailEndpoint(cfg.BidderInfos, defaultAliases)) r.GET("/bidders/params", NewJsonDirectoryServer(schemaDirectory, paramsValidator, defaultAliases)) - r.POST("/cookie_sync", endpoints.NewCookieSyncEndpoint(syncersByBidder, cfg, gdprPermsBuilder, tcf2CfgBuilder, r.MetricsEngine, pbsAnalytics, accounts, activeBidders).Handle) + r.POST("/cookie_sync", endpoints.NewCookieSyncEndpoint(syncersByBidder, cfg, gdprPermsBuilder, tcf2CfgBuilder, r.MetricsEngine, analyticsRunner, accounts, activeBidders).Handle) r.GET("/status", endpoints.NewStatusEndpoint(cfg.StatusResponse)) r.GET("/", serveIndex) r.Handler("GET", "/version", endpoints.NewVersionEndpoint(version.Ver, version.Rev)) @@ -293,7 +302,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R } // event endpoint - eventEndpoint := events.NewEventEndpoint(cfg, accounts, pbsAnalytics, r.MetricsEngine) + eventEndpoint := events.NewEventEndpoint(cfg, accounts, analyticsRunner, r.MetricsEngine) r.GET("/event", eventEndpoint) userSyncDeps := &pbs.UserSyncDeps{ @@ -303,7 +312,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R PriorityGroups: cfg.UserSync.PriorityGroups, } - r.GET("/setuid", endpoints.NewSetUIDEndpoint(cfg, syncersByBidder, gdprPermsBuilder, tcf2CfgBuilder, pbsAnalytics, accounts, r.MetricsEngine)) + r.GET("/setuid", endpoints.NewSetUIDEndpoint(cfg, syncersByBidder, gdprPermsBuilder, tcf2CfgBuilder, analyticsRunner, accounts, r.MetricsEngine)) r.GET("/getuids", endpoints.NewGetUIDsEndpoint(cfg.HostCookie)) r.POST("/optout", userSyncDeps.OptOut) r.GET("/optout", userSyncDeps.OptOut) @@ -317,7 +326,7 @@ func New(cfg *config.Configuration, rateConvertor *currency.RateConverter) (r *R g_accounts = &accounts g_videoFetcher = &videoFetcher g_storedRespFetcher = &storedRespFetcher - g_analytics = &pbsAnalytics + g_analytics = &analyticsRunner g_paramsValidator = ¶msValidator g_activeBidders = activeBidders g_disabledBidders = disabledBidders @@ -406,7 +415,7 @@ func readDefaultRequest(defReqConfig config.DefReqConfig) (map[string]string, [] return aliases, []byte{} } - if err := json.Unmarshal(defReqJSON, defReq); err != nil { + if err := jsonutil.UnmarshalValid(defReqJSON, defReq); err != nil { // we might not have aliases defined, but will atleast show that the JSON file is parsable. glog.Fatalf("error parsing alias json in file %s: %v", defReqConfig.FileSystem.FileName, err) return aliases, []byte{} diff --git a/router/router_sshb.go b/router/router_sshb.go index 26b37868026..a3f9e4a6945 100644 --- a/router/router_sshb.go +++ b/router/router_sshb.go @@ -5,24 +5,24 @@ import ( "net/http" "strconv" - "github.com/prebid/openrtb/v19/openrtb3" - "github.com/prebid/prebid-server/analytics" - "github.com/prebid/prebid-server/currency" - "github.com/prebid/prebid-server/hooks" - - analyticCfg "github.com/prebid/prebid-server/analytics/config" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/endpoints" - "github.com/prebid/prebid-server/endpoints/openrtb2" - "github.com/prebid/prebid-server/exchange" - "github.com/prebid/prebid-server/gdpr" - "github.com/prebid/prebid-server/metrics" - metricsConf "github.com/prebid/prebid-server/metrics/config" - "github.com/prebid/prebid-server/openrtb_ext" - pbc "github.com/prebid/prebid-server/prebid_cache_client" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/usersync" - "github.com/prebid/prebid-server/util/uuidutil" + "github.com/prebid/openrtb/v20/openrtb3" + "github.com/prebid/prebid-server/v2/analytics" + "github.com/prebid/prebid-server/v2/currency" + "github.com/prebid/prebid-server/v2/hooks" + + analyticsBuild "github.com/prebid/prebid-server/v2/analytics/build" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/endpoints" + "github.com/prebid/prebid-server/v2/endpoints/openrtb2" + "github.com/prebid/prebid-server/v2/exchange" + "github.com/prebid/prebid-server/v2/gdpr" + "github.com/prebid/prebid-server/v2/metrics" + metricsConf "github.com/prebid/prebid-server/v2/metrics/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + pbc "github.com/prebid/prebid-server/v2/prebid_cache_client" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/usersync" + "github.com/prebid/prebid-server/v2/util/uuidutil" "github.com/prometheus/client_golang/prometheus" ) @@ -37,7 +37,7 @@ var ( g_storedReqFetcher *stored_requests.Fetcher g_storedRespFetcher *stored_requests.Fetcher g_metrics metrics.MetricsEngine - g_analytics *analytics.PBSAnalyticsModule + g_analytics *analytics.Runner g_disabledBidders map[string]string g_videoFetcher *stored_requests.Fetcher g_activeBidders map[string]openrtb_ext.BidderName @@ -59,11 +59,11 @@ func GetPrebidCacheURL() string { } // RegisterAnalyticsModule function registers the PBSAnalyticsModule -func RegisterAnalyticsModule(anlt analytics.PBSAnalyticsModule) error { +func RegisterAnalyticsModule(anlt analytics.Module) error { if g_analytics == nil { return fmt.Errorf("g_analytics is nil") } - modules, err := analyticCfg.EnableAnalyticsModule(anlt, *g_analytics) + modules, err := analyticsBuild.EnableAnalyticsModule(anlt, *g_analytics) if err != nil { return err } diff --git a/router/router_test.go b/router/router_test.go index 39398e700ea..866a8440f3f 100644 --- a/router/router_test.go +++ b/router/router_test.go @@ -7,9 +7,11 @@ import ( "os" "testing" - _ "github.com/lib/pq" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + jsoniter "github.com/json-iterator/go" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" + "github.com/stretchr/testify/assert" ) @@ -17,6 +19,11 @@ const adapterDirectory = "../adapters" type testValidator struct{} +func TestMain(m *testing.M) { + jsoniter.RegisterExtension(&jsonutil.RawMessageExtension{}) + os.Exit(m.Run()) +} + func (validator *testValidator) Validate(name openrtb_ext.BidderName, ext json.RawMessage) error { return nil } @@ -45,7 +52,7 @@ func TestNewJsonDirectoryServer(t *testing.T) { handler(recorder, request, nil) var data map[string]json.RawMessage - json.Unmarshal(recorder.Body.Bytes(), &data) + jsonutil.UnmarshalValid(recorder.Body.Bytes(), &data) // Make sure that every adapter has a json schema by the same name associated with it. adapterFiles, err := os.ReadDir(adapterDirectory) @@ -274,3 +281,23 @@ func TestValidateDefaultAliases(t *testing.T) { } } } + +func TestBidderParamsCompactedOutput(t *testing.T) { + expectedFormattedResponse := `{"appnexus":{"$schema":"http://json-schema.org/draft-04/schema#","title":"Sample schema","description":"A sample schema to test the bidder/params endpoint","type":"object","properties":{"integer_param":{"type":"integer","minimum":1,"description":"A customer id"},"string_param_1":{"type":"string","minLength":1,"description":"Text with blanks in between"},"string_param_2":{"type":"string","minLength":1,"description":"Text_with_no_blanks_in_between"}},"required":["integer_param","string_param_2"]}}` + + // Setup + inSchemaDirectory := "bidder_params_tests" + paramsValidator, err := openrtb_ext.NewBidderParamsValidator(inSchemaDirectory) + assert.NoError(t, err, "Error initialing validator") + + handler := newJsonDirectoryServer(inSchemaDirectory, paramsValidator, nil, nil) + recorder := httptest.NewRecorder() + request, err := http.NewRequest("GET", "/bidder/params", nil) + assert.NoError(t, err, "Error creating request") + + // Run + handler(recorder, request, nil) + + // Assertions + assert.Equal(t, expectedFormattedResponse, recorder.Body.String()) +} diff --git a/schain/schain.go b/schain/schain.go index 6f084a65a2a..088e5a6099f 100644 --- a/schain/schain.go +++ b/schain/schain.go @@ -3,9 +3,9 @@ package schain import ( "fmt" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) // BidderToPrebidSChains organizes the ORTB 2.5 multiple root schain nodes into a map of schain nodes by bidder diff --git a/schain/schain_test.go b/schain/schain_test.go index dbe38d4014b..e35396bf2bd 100644 --- a/schain/schain_test.go +++ b/schain/schain_test.go @@ -3,8 +3,8 @@ package schain import ( "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/schain/schainwriter.go b/schain/schainwriter.go index 7e2161adb3b..b7ff0b52e95 100644 --- a/schain/schainwriter.go +++ b/schain/schainwriter.go @@ -1,10 +1,9 @@ package schain import ( - "encoding/json" - - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) // NewSChainWriter creates an ORTB 2.5 schain writer instance @@ -70,7 +69,7 @@ func (w SChainWriter) Write(req *openrtb2.BidRequest, bidder string) { schain.SChain.Nodes = append(schain.SChain.Nodes, *w.hostSChainNode) } - sourceExt, err := json.Marshal(schain) + sourceExt, err := jsonutil.Marshal(schain) if err == nil { req.Source.Ext = sourceExt } diff --git a/schain/schainwriter_test.go b/schain/schainwriter_test.go index e98c962b4fa..d9b1358ffe6 100644 --- a/schain/schainwriter_test.go +++ b/schain/schainwriter_test.go @@ -4,8 +4,10 @@ import ( "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" + + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" ) @@ -76,7 +78,7 @@ func TestSChainWriter(t *testing.T) { giveRequest: openrtb2.BidRequest{ Ext: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],` + seller1SChain + `}]}}`), Source: &openrtb2.Source{ - FD: 1, + FD: openrtb2.Int8Ptr(1), TID: "tid data", PChain: "pchain data", Ext: json.RawMessage(`{` + seller2SChain + `}`), @@ -86,7 +88,7 @@ func TestSChainWriter(t *testing.T) { wantRequest: openrtb2.BidRequest{ Ext: json.RawMessage(`{"prebid":{"schains":[{"bidders":["appnexus"],` + seller1SChain + `}]}}`), Source: &openrtb2.Source{ - FD: 1, + FD: openrtb2.Int8Ptr(1), TID: "tid data", PChain: "pchain data", Ext: json.RawMessage(`{` + seller1SChain + `}`), @@ -197,7 +199,7 @@ func TestSChainWriter(t *testing.T) { var reqExt *openrtb_ext.ExtRequest if tt.giveRequest.Ext != nil { reqExt = &openrtb_ext.ExtRequest{} - err := json.Unmarshal(tt.giveRequest.Ext, reqExt) + err := jsonutil.UnmarshalValid(tt.giveRequest.Ext, reqExt) if err != nil { t.Error("Unable to unmarshal request.ext") } diff --git a/scripts/check_coverage.sh b/scripts/check_coverage.sh index 0dd6235b96b..63e89297c42 100755 --- a/scripts/check_coverage.sh +++ b/scripts/check_coverage.sh @@ -25,8 +25,7 @@ while IFS= read -r LINE; do if [[ $LINE =~ "%" ]]; then PERCENT=$(echo "$LINE"|cut -d: -f2-|cut -d% -f1|cut -d. -f1|tr -d ' ') if [[ $PERCENT -lt $COV_MIN ]]; then - echo "Package has less than ${COV_MIN}% code coverage. Run ./scripts/coverage.sh --html to see a detailed coverage report, and add tests to improve your coverage" - exit 1 + echo "WARNING: Package has less than ${COV_MIN}% code coverage. Run ./scripts/coverage.sh --html to see a detailed coverage report, and add tests to improve your coverage" fi fi done <<< "$OUTPUT" diff --git a/scripts/coverage.sh b/scripts/coverage.sh index 3b3e2a95dd5..2f4c09fb7be 100755 --- a/scripts/coverage.sh +++ b/scripts/coverage.sh @@ -27,48 +27,48 @@ generate_cover_data() { fi # util/task uses _test package name if [[ "$pkg" =~ ^github\.com\/PubMatic\-OpenWrap\/prebid\-server\/util\/task$ ]]; then - cover+=" -coverpkg=github.com/prebid/prebid-server/util/task" + cover+=" -coverpkg=github.com/prebid/prebid-server/v2/util/task" fi if [[ "$pkg" =~ ^github\.com\/PubMatic\-OpenWrap\/prebid\-server\/router$ ]]; then - cover+=" -coverpkg=github.com/prebid/prebid-server/router" + cover+=" -coverpkg=github.com/prebid/prebid-server/v2/router" fi # temporarily disable openwrap, remove as we add full support to each package if [[ "$pkg" =~ ^github\.com\/PubMatic\-OpenWrap\/prebid\-server\/modules\/pubmatic\/openwrap$ ]]; then - cover+=" -coverpkg=github.com/prebid/prebid-server/modules/pubmatic/openwrap" + cover+=" -coverpkg=github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap" fi if [[ "$pkg" =~ ^github\.com\/PubMatic\-OpenWrap\/prebid\-server\/modules\/pubmatic\/openwrap\/adapters$ ]]; then - cover+=" -coverpkg=github.com/prebid/prebid-server/modules/pubmatic/openwrap/adapters" + cover+=" -coverpkg=github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adapters" fi if [[ "$pkg" =~ ^github\.com\/PubMatic\-OpenWrap\/prebid\-server\/modules\/pubmatic\/openwrap\/adunitconfig$ ]]; then - cover+=" -coverpkg=github.com/prebid/prebid-server/modules/pubmatic/openwrap/adunitconfig" + cover+=" -coverpkg=github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/adunitconfig" fi if [[ "$pkg" =~ ^github\.com\/PubMatic\-OpenWrap\/prebid\-server\/modules\/pubmatic\/openwrap\/bidderparams$ ]]; then - cover+=" -coverpkg=github.com/prebid/prebid-server/modules/pubmatic/openwrap/bidderparams" + cover+=" -coverpkg=github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/bidderparams" fi if [[ "$pkg" =~ ^github\.com\/PubMatic\-OpenWrap\/prebid\-server\/modules\/pubmatic\/openwrap\/config$ ]]; then - cover+=" -coverpkg=github.com/prebid/prebid-server/modules/pubmatic/openwrap/config" + cover+=" -coverpkg=github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/config" fi if [[ "$pkg" =~ ^github\.com\/PubMatic\-OpenWrap\/prebid\-server\/modules\/pubmatic\/openwrap\/database$ ]]; then - cover+=" -coverpkg=github.com/prebid/prebid-server/modules/pubmatic/openwrap/database" + cover+=" -coverpkg=github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/database" fi if [[ "$pkg" =~ ^github\.com\/PubMatic\-OpenWrap\/prebid\-server\/modules\/pubmatic\/openwrap\/metrics$ ]]; then - cover+=" -coverpkg=github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics" + cover+=" -coverpkg=github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics" fi if [[ "$pkg" =~ ^github\.com\/PubMatic\-OpenWrap\/prebid\-server\/modules\/pubmatic\/openwrap\/metrics\/stats$ ]]; then - cover+=" -coverpkg=github.com/prebid/prebid-server/modules/pubmatic/openwrap/metrics/stats" + cover+=" -coverpkg=github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/metrics/stats" fi if [[ "$pkg" =~ ^github\.com\/PubMatic\-OpenWrap\/prebid\-server\/modules\/pubmatic\/openwrap\/models$ ]]; then - cover+=" -coverpkg=github.com/prebid/prebid-server/modules/pubmatic/openwrap/models" + cover+=" -coverpkg=github.com/prebid/prebid-server/v2/modules/pubmatic/openwrap/models" fi go test ${cover} "$pkg" diff --git a/scripts/upgrade-pbs.sh b/scripts/upgrade-pbs.sh index e17eb6b99e8..4f3b71fe924 100755 --- a/scripts/upgrade-pbs.sh +++ b/scripts/upgrade-pbs.sh @@ -1,10 +1,5 @@ #!/bin/bash -e -prefix="v" -to_major=0 -to_minor=259 -to_patch=0 -upgrade_version="$prefix$to_major.$to_minor.$to_patch" attempt=1 @@ -45,19 +40,14 @@ log () { } clear_log() { - major=0 - minor=0 - patch=0 - get_current_tag_version major minor patch - current_fork_at_version="$major.$minor.$patch" - + current_fork_at_version=$(git describe --tags --abbrev=0) if [ "$current_fork_at_version" == "$upgrade_version" ] ; then log "Upgraded to $current_fork_at_version" rm -f "$CHECKLOG" log "Last validation before creating PR" go_mod - checkpoint_run "./validate.sh --race 5" + checkpoint_run "./validate.sh --race 5 --nofmt" go_discard set +e @@ -70,36 +60,6 @@ clear_log() { fi } -get_current_tag_version() { - log "get_current_tag_version $*" - - local -n _major=$1 - local -n _minor=$2 - local -n _patch=$3 - - # script will always start from start if origin/master is used. - # common_commit=$(git merge-base prebid-upstream/master origin/master) - # log "Common commit b/w prebid-upstream/master origin/master: $common_commit" - - # remove origin for master to continue from last fixed tag's rebase. - common_commit=$(git merge-base prebid-upstream/master master) - log "Common commit b/w prebid-upstream/master master: $common_commit" - - current_version=$(git tag --points-at $common_commit) - if [[ $current_version == v* ]] ; then - log "Current Version: $current_version" - else - log "Failed to detected current version. Abort." - exit 1 - # abort - # cd prebid-server; git rebase --abort;cd - - fi - - IFS='.' read -r -a _current_version <<< "$current_version" - _major=${_current_version[0]} - _minor=${_current_version[1]} - _patch=${_current_version[2]} -} clone_repo() { if [ -d "/tmp/prebid-server" ]; then @@ -122,6 +82,7 @@ checkout_branch() { # git push origin $tag_base_branch_name git checkout -b $upgrade_branch_name + git checkout $upgrade_branch_name # git push origin $upgrade_branch_name set -e @@ -146,9 +107,10 @@ checkpoint_run() { if [ -f $CHECKLOG ] ; then if grep -q "$cmd" "$CHECKLOG"; then log "Retry this checkpoint: $cmd" + cmd_exe $cmd rm "$CHECKLOG" - elif grep -q "./validate.sh --race 5" "$CHECKLOG"; then - log "Special checkpoint. ./validate.sh --race 5 failed for last tag update. Hence, only fixes are expected in successfully upgraded branch. (change in func() def, wrong conflict resolve, etc)" + elif grep -q "./validate.sh --race 5 --nofmt" "$CHECKLOG"; then + log "Special checkpoint. ./validate.sh --race 5 --nofmt failed for last tag update. Hence, only fixes are expected in successfully upgraded branch. (change in func() def, wrong conflict resolve, etc)" cmd_exe $cmd rm "$CHECKLOG" else @@ -167,9 +129,6 @@ go_mod() { } go_discard() { - # discard local changes if any. manual validate, compile, etc - # git checkout master go.mod - # git checkout master go.sum git checkout go.mod go.sum } @@ -188,43 +147,35 @@ checkpoint_run clone_repo cd /tmp/prebid-server log "At $(pwd)" -# code merged in master -# if [ "$RESTART" -eq "1" ]; then -# # TODO: commit this in origin/master,ci and remove it from here. -# git merge --squash origin/UOE-7610-1-upgrade.sh -# git commit --no-edit -# fi - -major=0 -minor=0 -patch=0 - -get_current_tag_version major minor patch -current_fork_at_version="$major.$minor.$patch" -git diff tags/$current_fork_at_version..origin/master > /tmp/pbs-patch/current_ow_patch-$current_fork_at_version-origin_master-$attempt.diff - -((minor++)) -log "Starting with version split major:$major, minor:$minor, patch:$patch" - -# how to validate with this code -# if [ "$RESTART" -eq "1" ]; then -# # Solving go.mod and go.sum conflicts would be easy at last as we would need to only pick the OW-patch entries rather than resolving conflict for every version -# log "Using latest go.mod and go.sum. Patch OW changes at last" -# git checkout tags/$current_fork_at_version go.mod -# git checkout tags/$current_fork_at_version go.sum -# git commit go.mod go.sum -m "[upgrade-start-checkpoint] tags/$current_fork_at_version go.mod go.sum" -# fi +# Get the latest tag +latest_tag=$(git describe --tags --abbrev=0) + +git diff tags/$latest_tag..origin/master > /tmp/pbs-patch/current_ow_patch-$latest_tag-origin_master-$attempt.diff + +log "Starting with version :$latest_tag" log "Checking if last failure was for test case. Need this to pick correct" go_mod -checkpoint_run "./validate.sh --race 5" +checkpoint_run "./validate.sh --race 5 --nofmt" go_discard +# Loop through each tag and merge it +tags=$(git tag --merged prebid-upstream/master --sort=v:refname) + log "Starting upgrade loop..." -while [ "$minor" -le "$to_minor" ]; do - # _upgrade_version="$prefix$major.$minor.$patch" - _upgrade_version="$major.$minor.$patch" - ((minor++)) +for tag in $tags + do + if [[ "$tag" == "$latest_tag" ]]; then + found_latest_tag=true + if [[ -f $CHECKLOG ]]; then + log "At tag: $tag but $CHECKLOG exists. Continue last failed checkpoint." + else + continue + fi + fi + + if [[ "$found_latest_tag" = true && "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + _upgrade_version=$tag log "Starting upgrade to version $_upgrade_version" @@ -235,11 +186,12 @@ while [ "$minor" -le "$to_minor" ]; do checkpoint_run checkout_branch + log "Merging master in $tag_base_branch_name" checkpoint_run git merge master --no-edit # Use `git commit --amend --no-edit` if you had to fix test cases, etc for wrong merge conflict resolve, etc. log "Validating the master merge into current tag. Fix and commit changes if required. Use 'git commit --amend --no-edit' for consistency" go_mod - checkpoint_run "./validate.sh --race 5" + checkpoint_run "./validate.sh --race 5 --nofmt" go_discard checkpoint_run git checkout master @@ -247,16 +199,5 @@ while [ "$minor" -le "$to_minor" ]; do log "Generating patch file at /tmp/pbs-patch/ for $_upgrade_version" git diff tags/$_upgrade_version..master > /tmp/pbs-patch/new_ow_patch_$upgrade_version-master-1.diff + fi done - -# TODO: -# diff tags/v0.192.0..origin/master -# diff tags/v0.207.0..prebid_v0.207.0 - -# TODO: UPDATE HEADER-BIDDING GO-MOD - - -# TODO: automate go.mod conflicts -# go mod edit -replace github.com/prebid/prebid-server=./ -# go mod edit -replace github.com/mxmCherry/openrtb/v16=github.com/PubMatic-OpenWrap/openrtb/v15@v15.0.0 -# go mod edit -replace github.com/beevik/etree=github.com/PubMatic-OpenWrap/etree@latest diff --git a/server/listener.go b/server/listener.go index 43917ac0a05..a10aef6441e 100644 --- a/server/listener.go +++ b/server/listener.go @@ -6,7 +6,7 @@ import ( "time" "github.com/golang/glog" - "github.com/prebid/prebid-server/metrics" + "github.com/prebid/prebid-server/v2/metrics" ) // monitorableListener tracks any opened connections in the metrics. diff --git a/server/listener_test.go b/server/listener_test.go index d10a3bdfbf9..c729f2ba55e 100644 --- a/server/listener_test.go +++ b/server/listener_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" gometrics "github.com/rcrowley/go-metrics" ) diff --git a/server/prometheus.go b/server/prometheus.go index 33114c86a0b..8b841f5151a 100644 --- a/server/prometheus.go +++ b/server/prometheus.go @@ -7,8 +7,8 @@ import ( "github.com/golang/glog" "github.com/prometheus/client_golang/prometheus/promhttp" - "github.com/prebid/prebid-server/config" - metricsconfig "github.com/prebid/prebid-server/metrics/config" + "github.com/prebid/prebid-server/v2/config" + metricsconfig "github.com/prebid/prebid-server/v2/metrics/config" ) func newPrometheusServer(cfg *config.Configuration, metrics *metricsconfig.DetailedMetricsEngine) *http.Server { diff --git a/server/prometheus_ow.go b/server/prometheus_ow.go new file mode 100644 index 00000000000..8766f43ab15 --- /dev/null +++ b/server/prometheus_ow.go @@ -0,0 +1,33 @@ +package server + +import ( + "net/http" + + "github.com/prebid/prebid-server/v2/config" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" +) + +func initPrometheusStatsEndpoint(cfg *config.Configuration, gatherer *prometheus.Registry, promMux *http.ServeMux) { + promMux.Handle("/stats", promhttp.HandlerFor(gatherer, promhttp.HandlerOpts{ + ErrorLog: loggerForPrometheus{}, + MaxRequestsInFlight: 5, + Timeout: cfg.Metrics.Prometheus.Timeout(), + })) +} + +func initPrometheusMetricsEndpoint(cfg *config.Configuration, promMux *http.ServeMux) { + promMux.Handle("/metrics", promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{ + ErrorLog: loggerForPrometheus{}, + MaxRequestsInFlight: 5, + Timeout: cfg.Metrics.Prometheus.Timeout(), + })) +} + +func getOpenWrapPrometheusServer(cfg *config.Configuration, gatherer *prometheus.Registry, server *http.Server) *http.Server { + promMux := http.NewServeMux() + initPrometheusStatsEndpoint(cfg, gatherer, promMux) + initPrometheusMetricsEndpoint(cfg, promMux) + server.Handler = promMux + return server +} diff --git a/server/prometheus_ow_test.go b/server/prometheus_ow_test.go new file mode 100644 index 00000000000..d4e1b6f6e37 --- /dev/null +++ b/server/prometheus_ow_test.go @@ -0,0 +1,219 @@ +package server + +import ( + "context" + "net/http" + "net/http/httptest" + "testing" + + "github.com/magiconair/properties/assert" + "github.com/prebid/prebid-server/v2/config" + "github.com/prometheus/client_golang/prometheus" +) + +func TestInitPrometheusStatsEndpoint(t *testing.T) { + type args struct { + endpoint string + cfg *config.Configuration + gatherer *prometheus.Registry + promMux *http.ServeMux + } + tests := []struct { + name string + args args + want int + }{ + { + name: "valid request for /stats endpoint", + args: args{ + endpoint: "/stats", + cfg: &config.Configuration{ + Metrics: config.Metrics{ + Prometheus: config.PrometheusMetrics{ + Port: 9090, + TimeoutMillisRaw: 12, + }, + }, + }, + gatherer: &prometheus.Registry{}, + promMux: http.NewServeMux(), + }, + + want: http.StatusOK, + }, + { + name: "invalid request for /stats endpoint", + args: args{ + endpoint: "/stat", + cfg: &config.Configuration{ + Metrics: config.Metrics{ + Prometheus: config.PrometheusMetrics{ + Port: 9090, + TimeoutMillisRaw: 12, + }, + }, + }, + gatherer: &prometheus.Registry{}, + promMux: http.NewServeMux(), + }, + + want: http.StatusNotFound, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + initPrometheusStatsEndpoint(tt.args.cfg, tt.args.gatherer, tt.args.promMux) + req := httptest.NewRequest("GET", tt.args.endpoint, nil) + rec := httptest.NewRecorder() + tt.args.promMux.ServeHTTP(rec, req) + assert.Equal(t, rec.Code, tt.want) + }) + } +} + +func TestInitPrometheusMetricsEndpoint(t *testing.T) { + type args struct { + endpoint string + cfg *config.Configuration + promMux *http.ServeMux + } + tests := []struct { + name string + args args + want int + }{ + { + name: "valid request for /metrics endpoint", + args: args{ + endpoint: "/metrics", + cfg: &config.Configuration{ + Metrics: config.Metrics{ + Prometheus: config.PrometheusMetrics{ + Port: 9091, + TimeoutMillisRaw: 12, + }, + }, + }, + promMux: http.NewServeMux(), + }, + want: http.StatusOK, + }, + { + name: "invalid request for /metrics endpoint", + args: args{ + endpoint: "/endpoint", + cfg: &config.Configuration{ + Metrics: config.Metrics{ + Prometheus: config.PrometheusMetrics{ + Port: 9091, + TimeoutMillisRaw: 12, + }, + }, + }, + promMux: http.NewServeMux(), + }, + want: http.StatusNotFound, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + initPrometheusMetricsEndpoint(tt.args.cfg, tt.args.promMux) + req := httptest.NewRequest("GET", tt.args.endpoint, nil) + rec := httptest.NewRecorder() + tt.args.promMux.ServeHTTP(rec, req) + assert.Equal(t, rec.Code, tt.want) + }) + } +} + +func TestGetOpenWrapPrometheusServer(t *testing.T) { + type args struct { + endpoint string + cfg *config.Configuration + gatherer *prometheus.Registry + } + + server := &http.Server{ + Addr: ":" + "9092", + } + tests := []struct { + name string + args args + want int + }{ + { + name: "valid request for /stats endpoint", + args: args{ + endpoint: "/stats", + cfg: &config.Configuration{ + Metrics: config.Metrics{ + Prometheus: config.PrometheusMetrics{ + Port: 9092, + TimeoutMillisRaw: 12, + }, + }, + }, + gatherer: prometheus.NewRegistry(), + }, + want: http.StatusOK, + }, + { + name: "valid request for /metrics endpoint", + args: args{ + endpoint: "/stats", + cfg: &config.Configuration{ + Metrics: config.Metrics{ + Prometheus: config.PrometheusMetrics{ + Port: 9092, + TimeoutMillisRaw: 12, + }, + }, + }, + gatherer: prometheus.NewRegistry(), + }, + want: http.StatusOK, + }, + { + name: "invalid request for /stats endpoint", + args: args{ + endpoint: "/abc", + cfg: &config.Configuration{ + Metrics: config.Metrics{ + Prometheus: config.PrometheusMetrics{ + Port: 9092, + TimeoutMillisRaw: 12, + }, + }, + }, + gatherer: prometheus.NewRegistry(), + }, + want: http.StatusNotFound, + }, + { + name: "valid request for /metrics endpoint", + args: args{ + endpoint: "/xyz", + cfg: &config.Configuration{ + Metrics: config.Metrics{ + Prometheus: config.PrometheusMetrics{ + Port: 9092, + TimeoutMillisRaw: 12, + }, + }, + }, + gatherer: prometheus.NewRegistry(), + }, + want: http.StatusNotFound, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + promServer := getOpenWrapPrometheusServer(tt.args.cfg, tt.args.gatherer, server) + defer promServer.Shutdown(context.Background()) + req := httptest.NewRequest("GET", tt.args.endpoint, nil) + rec := httptest.NewRecorder() + promServer.Handler.ServeHTTP(rec, req) + assert.Equal(t, rec.Code, tt.want) + }) + } +} diff --git a/server/server.go b/server/server.go index 9282f0fcf15..fd377517f59 100644 --- a/server/server.go +++ b/server/server.go @@ -13,9 +13,9 @@ import ( "github.com/NYTimes/gziphandler" "github.com/golang/glog" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - metricsconfig "github.com/prebid/prebid-server/metrics/config" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + metricsconfig "github.com/prebid/prebid-server/v2/metrics/config" ) // Listen blocks forever, serving PBS requests on the given port. This will block forever, until the process is shut down. @@ -68,6 +68,7 @@ func Listen(cfg *config.Configuration, handler http.Handler, adminHandler http.H prometheusListener net.Listener prometheusServer = newPrometheusServer(cfg, metrics) ) + prometheusServer = getOpenWrapPrometheusServer(cfg, metrics.PrometheusMetrics.Gatherer, prometheusServer) go shutdownAfterSignals(prometheusServer, stopPrometheus, done) if prometheusListener, err = newTCPListener(prometheusServer.Addr, nil); err != nil { glog.Errorf("Error listening for TCP connections on %s: %v for prometheus server", adminServer.Addr, err) diff --git a/server/server_test.go b/server/server_test.go index 23b45656d7d..03a2fc911b5 100644 --- a/server/server_test.go +++ b/server/server_test.go @@ -9,8 +9,8 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - metricsconfig "github.com/prebid/prebid-server/metrics/config" + "github.com/prebid/prebid-server/v2/config" + metricsconfig "github.com/prebid/prebid-server/v2/metrics/config" "github.com/stretchr/testify/assert" ) @@ -192,7 +192,6 @@ func TestListen(t *testing.T) { Port: 8000, UnixSocketEnable: false, UnixSocketName: "prebid_socket", - EnableGzip: false, } ) diff --git a/static/bidder-info/33across.yaml b/static/bidder-info/33across.yaml index 902db6b362b..cbbdedb2193 100644 --- a/static/bidder-info/33across.yaml +++ b/static/bidder-info/33across.yaml @@ -1,4 +1,7 @@ endpoint: "https://ssc.33across.com/api/v1/s2s" +# This bidder does not operate globally. Please consider setting "disabled: true" in European datacenters. +geoscope: + - "!EEA" maintainer: email: "headerbidding@33across.com" gvlVendorID: 58 @@ -10,4 +13,4 @@ capabilities: userSync: iframe: url: "https://ssc-cms.33across.com/ps/?m=xch&rt=html&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&ru={{.RedirectURL}}&id=zzz000000000002zzz" - userMacro: "33XUSERID33X" \ No newline at end of file + userMacro: "33XUSERID33X" diff --git a/static/bidder-info/adelement.yaml b/static/bidder-info/adelement.yaml new file mode 100644 index 00000000000..3b852283c02 --- /dev/null +++ b/static/bidder-info/adelement.yaml @@ -0,0 +1,18 @@ +endpoint: "https://amp.adelement.com/openrtb2/auction?supply_id={{.SupplyId}}" +endpointCompression: gzip +maintainer: + email: "tech@adelement.com" +gvlVendorID: 196 +capabilities: + app: + mediaTypes: + - banner + - native + - video + - audio + site: + mediaTypes: + - banner + - native + - video + - audio \ No newline at end of file diff --git a/static/bidder-info/adf.yaml b/static/bidder-info/adf.yaml index 2310f346c25..d7d9eccf986 100644 --- a/static/bidder-info/adf.yaml +++ b/static/bidder-info/adf.yaml @@ -1,4 +1,12 @@ +# Please uncomment the appropriate endpoint URL for your datacenter +# Europe endpoint: "https://adx.adform.net/adx/openrtb" +# North/South America +# endpoint: "https://adx2.adform.net/adx/openrtb" +# APAC +# endpoint: "https://adx3.adform.net/adx/openrtb" +geoscope: + - global maintainer: email: "scope.sspp@adform.com" gvlVendorID: 50 diff --git a/static/bidder-info/adform.yaml b/static/bidder-info/adform.yaml index 2310f346c25..2ad892785ce 100644 --- a/static/bidder-info/adform.yaml +++ b/static/bidder-info/adform.yaml @@ -1,18 +1,4 @@ -endpoint: "https://adx.adform.net/adx/openrtb" -maintainer: - email: "scope.sspp@adform.com" -gvlVendorID: 50 -capabilities: - app: - mediaTypes: - - banner - - native - - video - site: - mediaTypes: - - banner - - native - - video +aliasOf: adf userSync: redirect: url: "https://cm.adform.net/cookie?redirect_url={{.RedirectURL}}" diff --git a/static/bidder-info/adkernel.yaml b/static/bidder-info/adkernel.yaml index 1afbdc8d590..22be7bee809 100644 --- a/static/bidder-info/adkernel.yaml +++ b/static/bidder-info/adkernel.yaml @@ -1,15 +1,20 @@ -endpoint: "https://pbs.adksrv.com/hb?zone={{.ZoneID}}" +endpoint: "http://pbs.adksrv.com/hb?zone={{.ZoneID}}" maintainer: email: "prebid-dev@adkernel.com" gvlVendorID: 14 capabilities: app: mediaTypes: - - banner + - banner + - video + - audio + - native site: mediaTypes: - banner - video + - audio + - native userSync: redirect: url: "https://sync.adkernel.com/user-sync?t=image&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&r={{.RedirectURL}}" diff --git a/static/bidder-info/adquery.yaml b/static/bidder-info/adquery.yaml index 98b6b0ea432..035bd6fc1cb 100644 --- a/static/bidder-info/adquery.yaml +++ b/static/bidder-info/adquery.yaml @@ -1,16 +1,13 @@ -endpoint: "https://bidder.adquery.io/prebid/bid" +endpoint: https://bidder2.adquery.io/prebid/bid maintainer: email: prebid@adquery.io -#endpointCompression: gzip # disabled because otherwise bidder responds with {data:null} gvlVendorID: 902 capabilities: -# app: # disabled because currently it's only a site, not an app (?) -# mediaTypes: -# - banner site: mediaTypes: - banner userSync: - redirect: - url: https://bidder.adquery.io/prebid/userSync?1=1&gdpr={{.GDPR}}&consent={{.GDPRConsent}}&ccpa_consent={{.USPrivacy}}&redirect={{.RedirectURL}} - userMacro: $UID \ No newline at end of file + iframe: + url: https://api.adquery.io/storage?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&ccpa_consent={{.USPrivacy}}&redirect={{.RedirectURL}} + userMacro: $UID + \ No newline at end of file diff --git a/static/bidder-info/adsyield.yaml b/static/bidder-info/adsyield.yaml index dadd9cc3d13..2be1395b851 100644 --- a/static/bidder-info/adsyield.yaml +++ b/static/bidder-info/adsyield.yaml @@ -1,16 +1,2 @@ endpoint: "http://ads-pbs.open-adsyield.com/openrtb/{{.PublisherID}}?host={{.Host}}" -maintainer: - email: "engineering@project-limelight.com" -capabilities: - app: - mediaTypes: - - banner - - video - - audio - - native - site: - mediaTypes: - - banner - - video - - audio - - native +aliasOf: "limelightDigital" diff --git a/static/bidder-info/aidem.yaml b/static/bidder-info/aidem.yaml index 35ea898aa8a..70f63526879 100644 --- a/static/bidder-info/aidem.yaml +++ b/static/bidder-info/aidem.yaml @@ -1,4 +1,4 @@ -endpoint: "https://zero.aidemsrv.com/ortb/v2.6/bid/request" +endpoint: "https://zero.aidemsrv.com/ortb/v2.6/bid/request?billing_id={{.PublisherID}}" maintainer: email: prebid@aidem.com modifyingVastXmlAllowed: true @@ -15,5 +15,5 @@ capabilities: - native userSync: redirect: - url: https://gum.aidemsrv.com/prebid_sync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}} + url: https://gum.aidemsrv.com/prebid_sync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}} userMacro: "$UID" diff --git a/static/bidder-info/alkimi.yaml b/static/bidder-info/alkimi.yaml new file mode 100644 index 00000000000..2e36aebbcb5 --- /dev/null +++ b/static/bidder-info/alkimi.yaml @@ -0,0 +1,19 @@ +endpoint: https://exchange.alkimi-onboarding.com/server/bid +maintainer: + email: support@alkimi.org +gvlVendorID: 1169 +capabilities: + app: + mediaTypes: + - banner + - video + - audio + site: + mediaTypes: + - banner + - video + - audio +userSync: + redirect: + url: "https://user-sync.alkimi-onboarding.com/ssp-sync?gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}" + userMacro: "${UID}" diff --git a/static/bidder-info/appstock.yaml b/static/bidder-info/appstock.yaml index 28f420e9e88..73b54aa7144 100644 --- a/static/bidder-info/appstock.yaml +++ b/static/bidder-info/appstock.yaml @@ -1,16 +1,2 @@ endpoint: "http://ads-pbs.pre.vr-tb.com/openrtb/{{.PublisherID}}?host={{.Host}}" -maintainer: - email: "engineering@project-limelight.com" -capabilities: - app: - mediaTypes: - - banner - - video - - audio - - native - site: - mediaTypes: - - banner - - video - - audio - - native +aliasOf: "limelightDigital" diff --git a/static/bidder-info/bizzclick.yaml b/static/bidder-info/bizzclick.yaml index 28ff0d44285..9d0103bcd81 100644 --- a/static/bidder-info/bizzclick.yaml +++ b/static/bidder-info/bizzclick.yaml @@ -1,6 +1,14 @@ -endpoint: "http://us-e-node1.bizzclick.com/bid?rtb_seat_id={{.SourceId}}&secret_key={{.AccountID}}" +# Contact support@bizzclick.com to connect with Bizzclick exchange. +# We have the following regional endpoint sub-domains: US East - us-e-node1, EU: n1, APAC: n3 +# Use Host macro to resolve the endpoint based on the datacenter. +# By default, we will use the US East sub-domain. +# Please deploy this config in each of your datacenters with the appropriate regional subdomain +endpoint: 'http://{{.Host}}.bizzclick.com/bid?rtb_seat_id={{.SourceId}}&secret_key={{.AccountID}}' maintainer: - email: "support@bizzclick.com" + email: 'support@bizzclick.com' +geoscope: + - global +endpointCompression: "GZIP" capabilities: app: mediaTypes: diff --git a/static/bidder-info/bliink.yaml b/static/bidder-info/bliink.yaml index bcdacc42f07..4650e9f8441 100644 --- a/static/bidder-info/bliink.yaml +++ b/static/bidder-info/bliink.yaml @@ -15,6 +15,9 @@ capabilities: - video - native userSync: + iframe: + url: "https://tag.bliink.io/usersync.html?gdpr={{.GDPR}}&gdprConsent={{.GDPRConsent}}&uspConsent={{.USPrivacy}}&redirect={{.RedirectURL}}" + userMacro: "$UID" redirect: url: "https://cookiesync.api.bliink.io/getuid?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url={{.RedirectURL}}" userMacro: "$UID" diff --git a/static/bidder-info/bmtm.yaml b/static/bidder-info/bmtm.yaml index 2992d0fbc7b..27452195479 100644 --- a/static/bidder-info/bmtm.yaml +++ b/static/bidder-info/bmtm.yaml @@ -1,8 +1,12 @@ endpoint: "https://one.elitebidder.com/api/pbs" maintainer: - email: dev@brightmountainmedia.com + email: product@brightmountainmedia.com modifyingVastXmlAllowed: false capabilities: + app: + mediaTypes: + - banner + - video site: mediaTypes: - banner diff --git a/static/bidder-info/boldwin.yaml b/static/bidder-info/boldwin.yaml index 39c1caf34f8..f35d08ca197 100644 --- a/static/bidder-info/boldwin.yaml +++ b/static/bidder-info/boldwin.yaml @@ -13,3 +13,7 @@ capabilities: - banner - video - native +userSync: + redirect: + url: "https://sync.videowalldirect.com/pbserver?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&ccpa={{.USPrivacy}}&redir={{.RedirectURL}}" + userMacro: "[UID]" \ No newline at end of file diff --git a/static/bidder-info/bwx.yaml b/static/bidder-info/bwx.yaml new file mode 100644 index 00000000000..cf716c3890d --- /dev/null +++ b/static/bidder-info/bwx.yaml @@ -0,0 +1,19 @@ +endpoint: "http://rtb.boldwin.live?pid={{.SourceId}}&host={{.Host}}&pbs=1" +maintainer: + email: "prebid@bold-win.com" +capabilities: + app: + mediaTypes: + - banner + - video + - native + site: + mediaTypes: + - banner + - video + - native +userSync: + # BoldwinX supports user syncing, but requires configuration by the host. contact this + # bidder directly at the email address in this file to ask about enabling user sync. + supports: + - redirect diff --git a/static/bidder-info/consumable.yaml b/static/bidder-info/consumable.yaml index cc290149be2..0f240e87942 100644 --- a/static/bidder-info/consumable.yaml +++ b/static/bidder-info/consumable.yaml @@ -2,10 +2,13 @@ endpoint: "https://e.serverbid.com/api/v2" maintainer: email: "prebid@consumable.com" gvlVendorID: 591 +endpointCompression: gzip capabilities: app: mediaTypes: - banner + - video + - audio site: mediaTypes: - banner diff --git a/static/bidder-info/copper6.yaml b/static/bidder-info/copper6.yaml index 213cbe3624d..b4e259ef08a 100644 --- a/static/bidder-info/copper6.yaml +++ b/static/bidder-info/copper6.yaml @@ -1,15 +1,7 @@ +aliasOf: adtelligent endpoint: "http://ghb.app.copper6.com/pbs/ortb" maintainer: email: "info@copper6.com" -capabilities: - app: - mediaTypes: - - banner - - video - site: - mediaTypes: - - banner - - video userSync: # Copper6 ssp supports user syncing, but requires configuration by the host. contact this # bidder directly at the email address in this file to ask about enabling user sync. diff --git a/static/bidder-info/criteo.yaml b/static/bidder-info/criteo.yaml index 7d58c68d198..d6419d463de 100644 --- a/static/bidder-info/criteo.yaml +++ b/static/bidder-info/criteo.yaml @@ -15,8 +15,8 @@ userSync: # criteo supports user syncing, but requires configuration by the host. contact this # bidder directly at the email address in this file to ask about enabling user sync. redirect: - url: "https://ssp-sync.criteo.com/user-sync/redirect?gdprapplies={{.GDPR}}&gdpr={{.GDPRConsent}}&ccpa={{.USPrivacy}}&redir={{.RedirectURL}}&profile=230" + url: "https://ssp-sync.criteo.com/user-sync/redirect?gdprapplies={{.GDPR}}&gdpr={{.GDPRConsent}}&ccpa={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redir={{.RedirectURL}}&profile=230" userMacro: "${CRITEO_USER_ID}" iframe: - url: "https://ssp-sync.criteo.com/user-sync/iframe?gdprapplies={{.GDPR}}&gdpr={{.GDPRConsent}}&ccpa={{.USPrivacy}}&redir={{.RedirectURL}}&profile=230" + url: "https://ssp-sync.criteo.com/user-sync/iframe?gdprapplies={{.GDPR}}&gdpr={{.GDPRConsent}}&ccpa={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redir={{.RedirectURL}}&profile=230" userMacro: "${CRITEO_USER_ID}" \ No newline at end of file diff --git a/static/bidder-info/definemedia.yaml b/static/bidder-info/definemedia.yaml index d79c3835a58..a7be6fb9d23 100644 --- a/static/bidder-info/definemedia.yaml +++ b/static/bidder-info/definemedia.yaml @@ -1,6 +1,6 @@ endpoint: "https://rtb.conative.network/openrtb2/auction" maintainer: - email: "d.joest@definemedia.de" + email: "development@definemedia.de" gvlVendorID: 440 # GDPR vendor list ID capabilities: diff --git a/static/bidder-info/dxkulture.yaml b/static/bidder-info/dxkulture.yaml new file mode 100644 index 00000000000..d6f03044fda --- /dev/null +++ b/static/bidder-info/dxkulture.yaml @@ -0,0 +1,16 @@ +endpoint: "https://ads.dxkulture.com/pbs" +maintainer: + email: "devops@dxkulture.com" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video +userSync: + redirect: + url: "https://ads.dxkulture.com/usync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&cb={{.RedirectURL}}" + userMacro: "$UID" diff --git a/static/bidder-info/edge226.yaml b/static/bidder-info/edge226.yaml new file mode 100644 index 00000000000..c4fbdefc29b --- /dev/null +++ b/static/bidder-info/edge226.yaml @@ -0,0 +1,16 @@ +endpoint: "http://ssp.dauup.com/pserver" +maintainer: + email: "audit@edge226.com" +gvlVendorID: 1202 +capabilities: + site: + mediaTypes: + - banner + - video + - native + + app: + mediaTypes: + - banner + - video + - native diff --git a/static/bidder-info/embimedia.yaml b/static/bidder-info/embimedia.yaml new file mode 100644 index 00000000000..9cfa1546bbe --- /dev/null +++ b/static/bidder-info/embimedia.yaml @@ -0,0 +1,2 @@ +endpoint: "http://ads-pbs.bidder-embi.media/openrtb/{{.PublisherID}}?host={{.Host}}" +aliasOf: "limelightDigital" diff --git a/static/bidder-info/engagebdr.yaml b/static/bidder-info/engagebdr.yaml deleted file mode 100644 index 8218040c605..00000000000 --- a/static/bidder-info/engagebdr.yaml +++ /dev/null @@ -1,14 +0,0 @@ -endpoint: "http://dsp.bnmla.com/hb" -maintainer: - email: "tech@engagebdr.com" -capabilities: - app: - mediaTypes: - - banner - - video - - native -userSync: - iframe: - url: "https://match.bnmla.com/usersync/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}}" - userMacro: "${UUID}" - diff --git a/static/bidder-info/epsilon.yaml b/static/bidder-info/epsilon.yaml index 856d82a6f3f..828bac6cfc4 100644 --- a/static/bidder-info/epsilon.yaml +++ b/static/bidder-info/epsilon.yaml @@ -1,16 +1,4 @@ -endpoint: "http://api.hb.ad.cpe.dotomi.com/cvx/server/hb/ortb/25" -maintainer: - email: "PublisherIntegration@epsilon.com" -gvlVendorID: 24 -capabilities: - app: - mediaTypes: - - banner - - video - site: - mediaTypes: - - banner - - video +aliasOf: conversant userSync: redirect: url: "https://prebid-match.dotomi.com/match/bounce/current?version=1&networkId=72582&rurl={{.RedirectURL}}" diff --git a/static/bidder-info/evtech.yaml b/static/bidder-info/evtech.yaml index 4277a5c46c8..59134e04523 100644 --- a/static/bidder-info/evtech.yaml +++ b/static/bidder-info/evtech.yaml @@ -1,19 +1,5 @@ endpoint: "http://ads-pbs.direct.e-volution.ai/openrtb/{{.PublisherID}}?host={{.Host}}" -maintainer: - email: "engineering@project-limelight.com" -capabilities: - app: - mediaTypes: - - banner - - video - - audio - - native - site: - mediaTypes: - - banner - - video - - audio - - native +aliasOf: "limelightDigital" userSync: iframe: url: https://tracker.direct.e-volution.ai/sync.html?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}} diff --git a/static/bidder-info/finative.yaml b/static/bidder-info/finative.yaml new file mode 100644 index 00000000000..e1136c3220d --- /dev/null +++ b/static/bidder-info/finative.yaml @@ -0,0 +1,2 @@ +endpoint: "https://b.finative.cloud/cds/rtb/bid?ssp={{.AccountID}}" +aliasOf: "seedingAlliance" \ No newline at end of file diff --git a/static/bidder-info/freewheel-ssp.yaml b/static/bidder-info/freewheel-ssp.yaml index 0c0a11edfce..43c1ca166a2 100644 --- a/static/bidder-info/freewheel-ssp.yaml +++ b/static/bidder-info/freewheel-ssp.yaml @@ -1,16 +1,5 @@ -endpoint: "https://ads.stickyadstv.com/openrtb/dsp" -maintainer: - email: prebid-maintainer@freewheel.com -gvlVendorID: 285 -modifyingVastXmlAllowed: true -capabilities: - app: - mediaTypes: - - video - site: - mediaTypes: - - video +aliasOf: freewheelssp userSync: iframe: url: "https://ads.stickyadstv.com/pbs-user-sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&r={{.RedirectURL}}" - userMacro: "{viewerid}" + userMacro: "{viewerid}" \ No newline at end of file diff --git a/static/bidder-info/greedygame.yaml b/static/bidder-info/greedygame.yaml index 5e73e1fe950..330a7debd9d 100644 --- a/static/bidder-info/greedygame.yaml +++ b/static/bidder-info/greedygame.yaml @@ -1,16 +1,2 @@ endpoint: "http://ads-pbs.rtb-greedygame.com/openrtb/{{.PublisherID}}?host={{.Host}}" -maintainer: - email: "engineering@project-limelight.com" -capabilities: - app: - mediaTypes: - - banner - - video - - audio - - native - site: - mediaTypes: - - banner - - video - - audio - - native +aliasOf: "limelightDigital" diff --git a/static/bidder-info/grid.yaml b/static/bidder-info/grid.yaml index bf1832c9590..9d9e7aa58f5 100644 --- a/static/bidder-info/grid.yaml +++ b/static/bidder-info/grid.yaml @@ -15,5 +15,5 @@ capabilities: - native userSync: redirect: - url: "https://x.bidswitch.net/check_uuid/{{.RedirectURL}}?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}" + url: "https://x.bidswitch.net/check_uuid/{{.RedirectURL}}?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&us_privacy={{.USPrivacy}}" userMacro: "${BSW_UUID}" diff --git a/static/bidder-info/iionads.yaml b/static/bidder-info/iionads.yaml index da2f494bb30..1dc154a358d 100644 --- a/static/bidder-info/iionads.yaml +++ b/static/bidder-info/iionads.yaml @@ -1,16 +1,2 @@ endpoint: "http://ads-pbs.iionads.com/openrtb/{{.PublisherID}}?host={{.Host}}" -maintainer: - email: "engineering@project-limelight.com" -capabilities: - app: - mediaTypes: - - banner - - video - - audio - - native - site: - mediaTypes: - - banner - - video - - audio - - native +aliasOf: "limelightDigital" \ No newline at end of file diff --git a/static/bidder-info/imds.yaml b/static/bidder-info/imds.yaml index 491a5bd0ac6..135b67fc1f9 100644 --- a/static/bidder-info/imds.yaml +++ b/static/bidder-info/imds.yaml @@ -1,4 +1,8 @@ endpoint: "https://pbs.technoratimedia.com/openrtb/bids/{{.AccountID}}?src={{.SourceId}}&adapter=imds" +# This bidder does not operate globally. Please consider setting "disabled: true" outside of the following regions: +geoscope: + - USA + - CAN maintainer: email: "eng-demand@imds.tv" capabilities: diff --git a/static/bidder-info/improvedigital.yaml b/static/bidder-info/improvedigital.yaml index d14356cde0e..4cd30d7c6b2 100644 --- a/static/bidder-info/improvedigital.yaml +++ b/static/bidder-info/improvedigital.yaml @@ -7,14 +7,16 @@ capabilities: mediaTypes: - banner - video + - audio - native site: mediaTypes: - banner - video + - audio - native userSync: redirect: - url: "https://ad.360yield.com/server_match?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}}" + url: "https://ad.360yield.com/server_match?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&r={{.RedirectURL}}" userMacro: "{PUB_USER_ID}" endpointCompression: "GZIP" \ No newline at end of file diff --git a/static/bidder-info/indicue.yaml b/static/bidder-info/indicue.yaml new file mode 100644 index 00000000000..1353424dfed --- /dev/null +++ b/static/bidder-info/indicue.yaml @@ -0,0 +1,9 @@ +aliasOf: adtelligent +endpoint: "http://ghb.console.indicue.com/pbs/ortb" +maintainer: + email: "ops@indicue.com" +userSync: + # Indicue ssp supports user syncing, but requires configuration by the host. contact this + # bidder directly at the email address in this file to ask about enabling user sync. + supports: + - iframe diff --git a/static/bidder-info/iqx.yaml b/static/bidder-info/iqx.yaml new file mode 100644 index 00000000000..3569c3accf8 --- /dev/null +++ b/static/bidder-info/iqx.yaml @@ -0,0 +1,19 @@ +endpoint: "http://rtb.iqzone.com?pid={{.SourceId}}&host={{.Host}}&pbs=1" +maintainer: + email: "adops@iqzone.com" +capabilities: + app: + mediaTypes: + - banner + - video + - native + site: + mediaTypes: + - banner + - video + - native +userSync: + # IQX supports user syncing, but requires configuration by the host. contact this + # bidder directly at the email address in this file to ask about enabling user sync. + supports: + - redirect diff --git a/static/bidder-info/iqzone.yaml b/static/bidder-info/iqzone.yaml index 3465cfabcb0..620ea01e43c 100644 --- a/static/bidder-info/iqzone.yaml +++ b/static/bidder-info/iqzone.yaml @@ -1,6 +1,6 @@ endpoint: "http://smartssp-us-east.iqzone.com/pserver" maintainer: - email: "smartssp@iqzone.com" + email: "adops@iqzone.com" capabilities: site: mediaTypes: diff --git a/static/bidder-info/janet.yaml b/static/bidder-info/janet.yaml index 0a3abb53af4..97f44a2bd56 100644 --- a/static/bidder-info/janet.yaml +++ b/static/bidder-info/janet.yaml @@ -1,15 +1,7 @@ +aliasOf: adtelligent endpoint: "http://ghb.bidder.jmgads.com/pbs/ortb" maintainer: email: "info@thejmg.com" -capabilities: - app: - mediaTypes: - - banner - - video - site: - mediaTypes: - - banner - - video userSync: # JANet supports user syncing, but requires configuration by the host. contact this # bidder directly at the email address in this file to ask about enabling user sync. diff --git a/static/bidder-info/kargo.yaml b/static/bidder-info/kargo.yaml index e73eabe9cdf..1a7a77eb8bb 100644 --- a/static/bidder-info/kargo.yaml +++ b/static/bidder-info/kargo.yaml @@ -11,6 +11,8 @@ capabilities: - native userSync: redirect: - url: "https://crb.kargo.com/api/v1/dsync/PrebidServer?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}}" + url: "https://crb.kargo.com/api/v1/dsync/PrebidServer?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&r={{.RedirectURL}}" userMacro: "$UID" -endpointCompression: "GZIP" \ No newline at end of file +endpointCompression: "GZIP" +openrtb: + gpp-supported: true diff --git a/static/bidder-info/lemmadigital.yaml b/static/bidder-info/lemmadigital.yaml new file mode 100644 index 00000000000..535c91ffa77 --- /dev/null +++ b/static/bidder-info/lemmadigital.yaml @@ -0,0 +1,14 @@ +endpoint: "https://sg.ads.lemmatechnologies.com/lemma/servad?pid={{.PublisherID}}&aid={{.AdUnit}}" +maintainer: + email: support@lemmatechnologies.com +endpointCompression: gzip +modifyingVastXmlAllowed: true +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video \ No newline at end of file diff --git a/static/bidder-info/liftoff.yaml b/static/bidder-info/liftoff.yaml index 577439dbb97..43470bc7020 100644 --- a/static/bidder-info/liftoff.yaml +++ b/static/bidder-info/liftoff.yaml @@ -1,9 +1,12 @@ endpoint: "https://rtb.ads.vungle.com/bid/t/c770f32" maintainer: - email: platform-ssp@liftoff.io + email: vxssp@liftoff.io endpointCompression: gzip modifyingVastXmlAllowed: true capabilities: app: mediaTypes: - video + site: + mediaTypes: + - video diff --git a/static/bidder-info/lockerdome.yaml b/static/bidder-info/lockerdome.yaml index cfefb2f995b..530c54eaaff 100644 --- a/static/bidder-info/lockerdome.yaml +++ b/static/bidder-info/lockerdome.yaml @@ -1,4 +1,7 @@ endpoint: "https://lockerdome.com/ladbid/prebidserver/openrtb2" +# This bidder does not operate globally. Please consider setting "disabled: true" outside of the following regions: +geoscope: + - USA maintainer: email: "bidding@lockerdome.com" capabilities: diff --git a/static/bidder-info/lunamedia.yaml b/static/bidder-info/lunamedia.yaml index ef34143eb40..6f450382c0e 100644 --- a/static/bidder-info/lunamedia.yaml +++ b/static/bidder-info/lunamedia.yaml @@ -1,6 +1,8 @@ endpoint: "http://rtb.lunamedia.live/?pid={{.PublisherID}}" +geoscope: + - USA maintainer: - email: "josh@lunamedia.io" + email: "cs@lunamedia.io" capabilities: site: mediaTypes: diff --git a/static/bidder-info/magnite.yaml b/static/bidder-info/magnite.yaml new file mode 100644 index 00000000000..109c20d03bf --- /dev/null +++ b/static/bidder-info/magnite.yaml @@ -0,0 +1 @@ +aliasOf: "rubicon" \ No newline at end of file diff --git a/static/bidder-info/medianet.yaml b/static/bidder-info/medianet.yaml index ea47de2b11d..1b23d5aec96 100644 --- a/static/bidder-info/medianet.yaml +++ b/static/bidder-info/medianet.yaml @@ -3,6 +3,9 @@ extra_info: "https://medianet.golang.pbs.com" maintainer: email: "prebid-support@media.net" gvlVendorID: 142 +endpointCompression: gzip +openrtb: + version: 2.6 modifyingVastXmlAllowed: true capabilities: app: @@ -17,5 +20,5 @@ capabilities: - native userSync: redirect: - url: https://hbx.media.net/cksync.php?cs=1&type=pbs&ovsid=setstatuscode&bidder=medianet&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}} + url: https://hbx.media.net/cksync.php?cs=1&type=pbs&ovsid=setstatuscode&bidder=medianet&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}} userMacro: "" \ No newline at end of file diff --git a/static/bidder-info/minutemedia.yaml b/static/bidder-info/minutemedia.yaml new file mode 100644 index 00000000000..55f5a2b7cd3 --- /dev/null +++ b/static/bidder-info/minutemedia.yaml @@ -0,0 +1,18 @@ +endpoint: "https://pbs.minutemedia-prebid.com/pbs-mm" +maintainer: + email: hb@minutemedia.com +gvlVendorID: 918 +modifyingVastXmlAllowed: true +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video +userSync: + iframe: + url: https://pbs-cs.minutemedia-prebid.com/pbs-iframe?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirect={{.RedirectURL}} + userMacro: "[PBS_UID]" diff --git a/static/bidder-info/mobilefuse.yaml b/static/bidder-info/mobilefuse.yaml index e1474f775fc..1d6b323c3a6 100644 --- a/static/bidder-info/mobilefuse.yaml +++ b/static/bidder-info/mobilefuse.yaml @@ -1,4 +1,8 @@ endpoint: "http://mfx.mobilefuse.com/openrtb?pub_id={{.PublisherID}}" +# This bidder does not operate globally. Please consider setting "disabled: true" outside of the following regions: +geoscope: + - USA + - CAN maintainer: email: prebid@mobilefuse.com gvlVendorID: 909 @@ -8,4 +12,4 @@ capabilities: - banner - video - native -endpointCompression: "GZIP" \ No newline at end of file +endpointCompression: "GZIP" diff --git a/static/bidder-info/nanointeractive.yaml b/static/bidder-info/nanointeractive.yaml deleted file mode 100644 index 639c5450d2e..00000000000 --- a/static/bidder-info/nanointeractive.yaml +++ /dev/null @@ -1,15 +0,0 @@ -endpoint: "https://ad.audiencemanager.de/hbs" -maintainer: - email: "development@nanointeractive.com" -gvlVendorID: 72 -capabilities: - app: - mediaTypes: - - banner - site: - mediaTypes: - - banner -userSync: - redirect: - url: "https://ad.audiencemanager.de/hbs/cookie_sync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri={{.RedirectURL}}" - userMacro: "$UID" diff --git a/static/bidder-info/nextmillennium.yaml b/static/bidder-info/nextmillennium.yaml index a6712846a41..11182ac271c 100644 --- a/static/bidder-info/nextmillennium.yaml +++ b/static/bidder-info/nextmillennium.yaml @@ -1,13 +1,16 @@ endpoint: "https://pbs.nextmillmedia.com/openrtb2/auction" maintainer: email: "accountmanagers@nextmillennium.io" +gvlVendorID: 1060 capabilities: app: mediaTypes: - banner + - video site: mediaTypes: - banner + - video userSync: iframe: url: "https://cookies.nextmillmedia.com/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}" diff --git a/static/bidder-info/ninthdecimal.yaml b/static/bidder-info/ninthdecimal.yaml deleted file mode 100755 index 8a4d7b8d299..00000000000 --- a/static/bidder-info/ninthdecimal.yaml +++ /dev/null @@ -1,18 +0,0 @@ -endpoint: "http://rtb.ninthdecimal.com/xp/get?pubid={{.PublisherID}}" -maintainer: - email: "abudig@ninthdecimal.com" -capabilities: - site: - mediaTypes: - - banner - - video - - app: - mediaTypes: - - banner - - video -userSync: - iframe: - url: "https://rtb.ninthdecimal.com/xp/user-sync?acctid={aid}&&redirect={{.RedirectURL}}" - userMacro: "$UID" - diff --git a/static/bidder-info/kubient.yaml b/static/bidder-info/oms.yaml similarity index 51% rename from static/bidder-info/kubient.yaml rename to static/bidder-info/oms.yaml index 15c2708bcb3..8bb9299d6e9 100644 --- a/static/bidder-info/kubient.yaml +++ b/static/bidder-info/oms.yaml @@ -1,13 +1,11 @@ -endpoint: "https://kssp.kbntx.ch/prebid" +endpoint: "http://rt.marphezis.com/pbs" maintainer: - email: "prebid@kubient.com" + email: "prebid@onlinemediasolutions.com" capabilities: app: mediaTypes: - banner - - video site: mediaTypes: - banner - - video diff --git a/static/bidder-info/onetag.yaml b/static/bidder-info/onetag.yaml index 3677921ee5c..bc52faffe1b 100644 --- a/static/bidder-info/onetag.yaml +++ b/static/bidder-info/onetag.yaml @@ -1,4 +1,6 @@ endpoint: "https://prebid-server.onetag-sys.com/prebid-server/{{.PublisherID}}" +openrtb: + version: 2.6 maintainer: email: devops@onetag.com gvlVendorID: 241 diff --git a/static/bidder-info/orbidder.yaml b/static/bidder-info/orbidder.yaml index 705d35a5a20..c42fb91de44 100644 --- a/static/bidder-info/orbidder.yaml +++ b/static/bidder-info/orbidder.yaml @@ -10,3 +10,8 @@ capabilities: mediaTypes: - banner - native +userSync: + supportCors: true + redirect: + url: "https://orbidder.otto.de/pbs-usersync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&redirect={{.RedirectURL}}" + userMacro: "[ODN_ID]" \ No newline at end of file diff --git a/static/bidder-info/pgam.yaml b/static/bidder-info/pgam.yaml index 0c9b2f008b6..e0bf4388d4b 100644 --- a/static/bidder-info/pgam.yaml +++ b/static/bidder-info/pgam.yaml @@ -1,15 +1,7 @@ +aliasOf: adtelligent endpoint: "http://ghb.pgamssp.com/pbs/ortb" maintainer: email: "ppatel@pgammedia.com" -capabilities: - app: - mediaTypes: - - banner - - video - site: - mediaTypes: - - banner - - video userSync: # PGAM ssp supports user syncing, but requires configuration by the host. contact this # bidder directly at the email address in this file to ask about enabling user sync. diff --git a/static/bidder-info/quantumdex.yaml b/static/bidder-info/quantumdex.yaml index 590737ac28b..fae2a987dd7 100644 --- a/static/bidder-info/quantumdex.yaml +++ b/static/bidder-info/quantumdex.yaml @@ -1,16 +1,4 @@ -endpoint: "http://useast.quantumdex.io/auction/pbs" -maintainer: - email: "support@apacdex.com" -modifyingVastXmlAllowed: false -capabilities: - app: - mediaTypes: - - banner - - video - site: - mediaTypes: - - banner - - video +aliasOf: "apacdex" userSync: iframe: url: https://sync.quantumdex.io/usersync/pbs?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}} diff --git a/static/bidder-info/applogy.yaml b/static/bidder-info/relevantdigital.yaml similarity index 51% rename from static/bidder-info/applogy.yaml rename to static/bidder-info/relevantdigital.yaml index 62a70a17f28..cc873f2804a 100644 --- a/static/bidder-info/applogy.yaml +++ b/static/bidder-info/relevantdigital.yaml @@ -1,14 +1,17 @@ -endpoint: "http://rtb.applogy.com/v1/prebid" +endpoint: "https://{{.Host}}.relevant-digital.com/openrtb2/auction" maintainer: - email: work@applogy.com + email: "support@relevant-digital.com" +gvlVendorID: 1100 capabilities: app: mediaTypes: - banner - video - native + - audio site: mediaTypes: - banner - video - native + - audio diff --git a/static/bidder-info/rhythmone.yaml b/static/bidder-info/rhythmone.yaml deleted file mode 100644 index 529eae12628..00000000000 --- a/static/bidder-info/rhythmone.yaml +++ /dev/null @@ -1,17 +0,0 @@ -endpoint: "http://tag.1rx.io/rmp" -maintainer: - email: "support@rhythmone.com" -gvlVendorID: 36 -capabilities: - app: - mediaTypes: - - banner - - video - site: - mediaTypes: - - banner - - video -userSync: - redirect: - url: "https://sync.1rx.io/usersync2/rmphb?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir={{.RedirectURL}}" - userMacro: "[RX_UUID]" \ No newline at end of file diff --git a/static/bidder-info/rise.yaml b/static/bidder-info/rise.yaml index d429e8780a7..79b64d5a4e7 100644 --- a/static/bidder-info/rise.yaml +++ b/static/bidder-info/rise.yaml @@ -2,7 +2,7 @@ endpoint: "https://pbs.yellowblue.io/pbs" maintainer: email: rise-prog-dev@risecodes.com gvlVendorID: 1043 -modifyingVastXmlAllowed: false +modifyingVastXmlAllowed: true capabilities: app: mediaTypes: @@ -14,5 +14,5 @@ capabilities: - video userSync: iframe: - url: https://pbs-cs.yellowblue.io/pbs-iframe?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}} + url: https://pbs-cs.yellowblue.io/pbs-iframe?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirect={{.RedirectURL}} userMacro: "[PBS_UID]" diff --git a/static/bidder-info/rtbhouse.yaml b/static/bidder-info/rtbhouse.yaml index ad2fbfcbc95..1f8b131678d 100644 --- a/static/bidder-info/rtbhouse.yaml +++ b/static/bidder-info/rtbhouse.yaml @@ -4,9 +4,14 @@ maintainer: endpointCompression: gzip gvlVendorID: 16 capabilities: + app: + mediaTypes: + - banner + - native site: mediaTypes: - banner + - native userSync: # rtbhouse supports user syncing, but requires configuration by the host. contact this # bidder directly at the email address in this file to ask about enabling user sync. diff --git a/static/bidder-info/rubicon.yaml b/static/bidder-info/rubicon.yaml index b3de838c9fd..c3943058511 100644 --- a/static/bidder-info/rubicon.yaml +++ b/static/bidder-info/rubicon.yaml @@ -1,5 +1,15 @@ -endpoint: "http://exapi-us-east.rubiconproject.com/a/api/exchange.json" +# Contact global-support@magnite.com to ask about enabling a connection to the Magnite (formerly Rubicon) exchange. +# We have the following regional endpoint domains: exapi-us-east, exapi-us-west, exapi-apac, exapi-eu +# Please deploy this config in each of your datacenters with the appropriate regional subdomain +endpoint: "https://REGION.rubiconproject.com/a/api/exchange" +endpointCompression: GZIP +geoscope: + - global disabled: true +xapi: + username: GET_FROM_MAGNITE + password: GET_FROM_MAGNITE + tracker: SAME_AS_USERNAME maintainer: email: "header-bidding@rubiconproject.com" gvlVendorID: 52 @@ -15,7 +25,6 @@ capabilities: - video - native userSync: - # rubicon supports user syncing, but requires configuration by the host. contact this - # bidder directly at the email address in this file to ask about enabling user sync. + # rubicon supports user syncing, but requires configuration. Please contact global-support@magnite.com. supports: - redirect diff --git a/static/bidder-info/seedingAlliance.yaml b/static/bidder-info/seedingAlliance.yaml index dee9fcd6026..9dcb7701b34 100644 --- a/static/bidder-info/seedingAlliance.yaml +++ b/static/bidder-info/seedingAlliance.yaml @@ -1,4 +1,4 @@ -endpoint: "https://b.nativendo.de/cds/rtb/bid?ssp=pbs" +endpoint: "https://b.nativendo.de/cds/rtb/bid?ssp={{.AccountID}}" maintainer: email: tech@seeding-alliance.de gvlVendorID: 371 diff --git a/static/bidder-info/smaato.yaml b/static/bidder-info/smaato.yaml index 59a734c90fd..2b1b5c3fe17 100644 --- a/static/bidder-info/smaato.yaml +++ b/static/bidder-info/smaato.yaml @@ -15,6 +15,8 @@ capabilities: - video - native userSync: + # This bidder does not sync when GDPR is in-scope. Please consider removing the usersync + # block when deploying to European datacenters redirect: url: "https://s.ad.smaato.net/c/?adExInit=p&redir={{.RedirectURL}}&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}" userMacro: "$UID" diff --git a/static/bidder-info/smartx.yaml b/static/bidder-info/smartx.yaml new file mode 100644 index 00000000000..9a387ecfbd2 --- /dev/null +++ b/static/bidder-info/smartx.yaml @@ -0,0 +1,12 @@ +endpoint: "https://bid.smartclip.net/bid/1005" +maintainer: + email: "bidding@smartclip.tv" +gvlVendorID: 115 +modifyingVastXmlAllowed: false +capabilities: + site: + mediaTypes: + - video + app: + mediaTypes: + - video diff --git a/static/bidder-info/smartyads.yaml b/static/bidder-info/smartyads.yaml index b36983bc7b1..21b8a0a11d6 100644 --- a/static/bidder-info/smartyads.yaml +++ b/static/bidder-info/smartyads.yaml @@ -1,6 +1,7 @@ endpoint: "http://{{.Host}}.smartyads.com/bid?rtb_seat_id={{.SourceId}}&secret_key={{.AccountID}}" maintainer: email: "support@smartyads.com" +gvlVendorID: 534 capabilities: app: mediaTypes: diff --git a/static/bidder-info/sovrnXsp.yaml b/static/bidder-info/sovrnXsp.yaml new file mode 100644 index 00000000000..3cce11a551c --- /dev/null +++ b/static/bidder-info/sovrnXsp.yaml @@ -0,0 +1,12 @@ +endpoint: "http://xsp.lijit.com/json/rtb/prebid/server" +maintainer: + email: "sovrnoss@sovrn.com" +endpointCompression: gzip +gvlVendorID: 13 +modifyingVastXmlAllowed: true +capabilities: + app: + mediaTypes: + - banner + - video + - native diff --git a/static/bidder-info/streamkey.yaml b/static/bidder-info/streamkey.yaml index 9e5d05abaec..20510ceac69 100644 --- a/static/bidder-info/streamkey.yaml +++ b/static/bidder-info/streamkey.yaml @@ -1,3 +1,4 @@ +aliasOf: adtelligent endpoint: "http://ghb.hb.streamkey.net/pbs/ortb" maintainer: email: "contact@streamkey.tv" diff --git a/static/bidder-info/stroeerCore.yaml b/static/bidder-info/stroeerCore.yaml index 32c78590bb8..9c0904508ac 100644 --- a/static/bidder-info/stroeerCore.yaml +++ b/static/bidder-info/stroeerCore.yaml @@ -7,9 +7,11 @@ capabilities: app: mediaTypes: - banner + - video site: mediaTypes: - banner + - video userSync: # for both user syncs, stroeerCore appends the user id to end of the redirect url and does not utilize a macro iframe: diff --git a/static/bidder-info/suntContent.yaml b/static/bidder-info/suntContent.yaml index e46cc4086e0..bb5eb2ee977 100644 --- a/static/bidder-info/suntContent.yaml +++ b/static/bidder-info/suntContent.yaml @@ -1,13 +1,7 @@ -endpoint: "https://b.suntcontent.se/cds/rtb/bid?ssp=pbs" -maintainer: - email: tech@seeding-alliance.de +endpoint: "https://b.suntcontent.se/cds/rtb/bid?ssp={{.AccountID}}" +aliasOf: "seedingAlliance" gvlVendorID: 1097 -capabilities: - site: - mediaTypes: - - native - - banner userSync: redirect: url: "https://dmp.suntcontent.se/set-uuid?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect_url={{.RedirectURL}}" - userMacro: "$UID" + userMacro: "$UID" \ No newline at end of file diff --git a/static/bidder-info/synacormedia.yaml b/static/bidder-info/synacormedia.yaml index 2a796ae839f..ccbba0700a4 100644 --- a/static/bidder-info/synacormedia.yaml +++ b/static/bidder-info/synacormedia.yaml @@ -1,15 +1,3 @@ # DEPRECATED: Use imds bidder instead +aliasOf: imds endpoint: "https://pbs.technoratimedia.com/openrtb/bids/{{.AccountID}}?src={{.SourceId}}&adapter=synacormedia" -maintainer: - email: "eng-demand@imds.tv" -capabilities: - app: - mediaTypes: - - banner - - video - site: - mediaTypes: - - banner - - video -userSync: - key: "imds" diff --git a/static/bidder-info/taboola.yaml b/static/bidder-info/taboola.yaml index 436f746959a..0fccee145bc 100644 --- a/static/bidder-info/taboola.yaml +++ b/static/bidder-info/taboola.yaml @@ -13,8 +13,8 @@ capabilities: - native userSync: redirect: - url: https://trc.taboola.com/sg/ps/1/cm?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}} + url: "https://trc.taboola.com/sg/ps/1/cm?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirect={{.RedirectURL}}" userMacro: "" iframe: - url: https://cdn.taboola.com/scripts/ps-sync.html?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}} + url: "https://cdn.taboola.com/scripts/ps-sync.html?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirect={{.RedirectURL}}" userMacro: "" diff --git a/static/bidder-info/teads.yaml b/static/bidder-info/teads.yaml new file mode 100644 index 00000000000..307b631efb4 --- /dev/null +++ b/static/bidder-info/teads.yaml @@ -0,0 +1,9 @@ +endpoint: "https://psrv.teads.tv/prebid-server/bid-request" +maintainer: + email: "innov-ssp@teads.tv" +gvlVendorID: 132 +capabilities: + app: + mediaTypes: + - banner + - video \ No newline at end of file diff --git a/static/bidder-info/trustx.yaml b/static/bidder-info/trustx.yaml index 5e407782e6a..1c60ac34c2f 100644 --- a/static/bidder-info/trustx.yaml +++ b/static/bidder-info/trustx.yaml @@ -1,17 +1,9 @@ -endpoint: "https://grid.bidswitch.net/sp_bid?sp=trustx" +aliasOf: grid maintainer: email: "grid-tech@themediagrid.com" gvlVendorID: 686 -capabilities: - app: - mediaTypes: - - banner - - video - site: - mediaTypes: - - banner - - video userSync: redirect: url: "https://x.bidswitch.net/check_uuid/{{.RedirectURL}}?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}" - userMacro: "${BSW_UUID}" \ No newline at end of file + userMacro: "${BSW_UUID}" + \ No newline at end of file diff --git a/static/bidder-info/valueimpression.yaml b/static/bidder-info/valueimpression.yaml index 9e6e583967c..fae2a987dd7 100644 --- a/static/bidder-info/valueimpression.yaml +++ b/static/bidder-info/valueimpression.yaml @@ -1,20 +1,8 @@ -endpoint: "http://useast.quantumdex.io/auction/pbs" -maintainer: - email: "support@apacdex.com" -modifyingVastXmlAllowed: false -capabilities: - app: - mediaTypes: - - banner - - video - site: - mediaTypes: - - banner - - video +aliasOf: "apacdex" userSync: iframe: url: https://sync.quantumdex.io/usersync/pbs?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}} userMacro: "[UID]" redirect: url: "https://sync.quantumdex.io/getuid?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}}" - userMacro: "[UID]" \ No newline at end of file + userMacro: "[UID]" diff --git a/static/bidder-info/viewdeos.yaml b/static/bidder-info/viewdeos.yaml index c5e1e8ef02c..8d14cb961d8 100644 --- a/static/bidder-info/viewdeos.yaml +++ b/static/bidder-info/viewdeos.yaml @@ -1,16 +1,8 @@ +aliasOf: adtelligent endpoint: "http://ghb.sync.viewdeos.com/pbs/ortb" maintainer: email: "contact@viewdeos.com" gvlVendorID: 924 -capabilities: - app: - mediaTypes: - - banner - - video - site: - mediaTypes: - - banner - - video userSync: # viewdeos supports user syncing, but requires configuration by the host. contact this # bidder directly at the email address in this file to ask about enabling user sync. diff --git a/static/bidder-info/vrtcal.yaml b/static/bidder-info/vrtcal.yaml index 2aeb292a7ed..1ce005cd223 100644 --- a/static/bidder-info/vrtcal.yaml +++ b/static/bidder-info/vrtcal.yaml @@ -14,7 +14,11 @@ capabilities: - video - native userSync: - # vrtcal supports user syncing, but requires configuration by the host. contact this - # bidder directly at the email address in this file to ask about enabling user sync. - supports: - - redirect + iframe: + url: "https://usync.vrtcal.com/i?ssp=1804&synctype=iframe&us_privacy={{.USPrivacy}}&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&surl={{.RedirectURL}}" + userMacro: "$$VRTCALUSER$$" + redirect: + url: "https://usync.vrtcal.com/i?ssp=1804&synctype=redirect&us_privacy={{.USPrivacy}}&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&surl={{.RedirectURL}}" + userMacro: "$$VRTCALUSER$$" +openrtb: + gpp-supported: true diff --git a/static/bidder-info/xtrmqb.yaml b/static/bidder-info/xtrmqb.yaml index c1f0da3fc1f..b7a068c3889 100644 --- a/static/bidder-info/xtrmqb.yaml +++ b/static/bidder-info/xtrmqb.yaml @@ -1,16 +1,2 @@ endpoint: "http://ads-pbs.ortb.tech/openrtb/{{.PublisherID}}?host={{.Host}}" -maintainer: - email: "engineering@project-limelight.com" -capabilities: - app: - mediaTypes: - - banner - - video - - audio - - native - site: - mediaTypes: - - banner - - video - - audio - - native +aliasOf: "limelightDigital" diff --git a/static/bidder-info/yahooAds.yaml b/static/bidder-info/yahooAds.yaml index a2581387152..c53d1b14e0c 100644 --- a/static/bidder-info/yahooAds.yaml +++ b/static/bidder-info/yahooAds.yaml @@ -1,6 +1,6 @@ endpoint: "https://s2shb.ssp.yahoo.com/admax/bid/partners/PBS" maintainer: - email: "hb-fe-tech@yahooinc.com" + email: "prebid-tech-team@yahooinc.com" gvlVendorID: 25 capabilities: app: diff --git a/static/bidder-info/yahooAdvertising.yaml b/static/bidder-info/yahooAdvertising.yaml index 3798adf7ca2..f9f34fbfbd0 100644 --- a/static/bidder-info/yahooAdvertising.yaml +++ b/static/bidder-info/yahooAdvertising.yaml @@ -1,16 +1,4 @@ -endpoint: "https://s2shb.ssp.yahoo.com/admax/bid/partners/PBS" -maintainer: - email: "hb-fe-tech@yahooinc.com" -gvlVendorID: 25 -capabilities: - app: - mediaTypes: - - banner - - video - site: - mediaTypes: - - banner - - video +aliasOf: yahooAds userSync: # yahooAdvertising supports user syncing, but requires configuration by the host. contact this # bidder directly at the email address in this file to ask about enabling user sync. diff --git a/static/bidder-info/yahoossp.yaml b/static/bidder-info/yahoossp.yaml index 8c23c01e181..81695624dfa 100644 --- a/static/bidder-info/yahoossp.yaml +++ b/static/bidder-info/yahoossp.yaml @@ -1,16 +1,6 @@ -endpoint: "https://s2shb.ssp.yahoo.com/admax/bid/partners/PBS" +aliasOf: yahooAds maintainer: email: "hb-fe-tech@oath.com" -gvlVendorID: 25 -capabilities: - app: - mediaTypes: - - banner - - video - site: - mediaTypes: - - banner - - video userSync: # yahoossp supports user syncing, but requires configuration by the host. contact this # bidder directly at the email address in this file to ask about enabling user sync. diff --git a/static/bidder-info/yandex.yaml b/static/bidder-info/yandex.yaml new file mode 100644 index 00000000000..8c5be2f70d4 --- /dev/null +++ b/static/bidder-info/yandex.yaml @@ -0,0 +1,13 @@ +endpoint: "https://bs-metadsp.yandex.ru/prebid/{{.PageID}}?ssp-id=10500" +endpointCompression: gzip +maintainer: + email: "prebid@yandex-team.ru" +capabilities: + site: + mediaTypes: + - banner + - native +userSync: + redirect: + url: https://yandex.ru/an/mapuid/yandex/?ssp-id=10500&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&location={{.RedirectURL}} + userMacro: "{YANDEXUID}" diff --git a/static/bidder-info/yeahmobi.yaml b/static/bidder-info/yeahmobi.yaml index ae41464f6fa..975bf044c5b 100644 --- a/static/bidder-info/yeahmobi.yaml +++ b/static/bidder-info/yeahmobi.yaml @@ -1,6 +1,6 @@ endpoint: "https://{{.Host}}/prebid/bid" maintainer: - email: "junping.zhao@yeahmobi.com" + email: "developer@yeahmobi.com" capabilities: app: mediaTypes: diff --git a/static/bidder-info/yieldmo.yaml b/static/bidder-info/yieldmo.yaml index 9356a541f60..02ab8721a16 100644 --- a/static/bidder-info/yieldmo.yaml +++ b/static/bidder-info/yieldmo.yaml @@ -13,5 +13,5 @@ capabilities: - video userSync: redirect: - url: "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri={{.RedirectURL}}" + url: "https://ads.yieldmo.com/pbsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirectUri={{.RedirectURL}}" userMacro: "$UID" \ No newline at end of file diff --git a/static/bidder-info/zeta_global_ssp.yaml b/static/bidder-info/zeta_global_ssp.yaml index 3cc2ecb5297..479f024c1b7 100644 --- a/static/bidder-info/zeta_global_ssp.yaml +++ b/static/bidder-info/zeta_global_ssp.yaml @@ -1,4 +1,4 @@ -endpoint: https://ssp.disqus.com/bid/prebid-server?sid=GET_SID_FROM_ZETA&shortname=GET_SHORTNAME_FROM_ZETA +endpoint: https://ssp.disqus.com/bid/prebid-server?sid=GET_SID_FROM_ZETA endpointCompression: gzip maintainer: email: DL-Zeta-SSP@zetaglobal.com @@ -15,6 +15,6 @@ capabilities: - video userSync: redirect: - url: https://ssp.disqus.com/redirectuser?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}}&partner=GET_SHORTNAME_FROM_ZETA + url: https://ssp.disqus.com/redirectuser?sid=GET_SID_FROM_ZETA&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}} userMacro: 'BUYERUID' diff --git a/static/bidder-info/zmaticoo.yaml b/static/bidder-info/zmaticoo.yaml new file mode 100644 index 00000000000..ae7d3eb0100 --- /dev/null +++ b/static/bidder-info/zmaticoo.yaml @@ -0,0 +1,9 @@ +endpoint: "https://bid.zmaticoo.com/prebid/bid" +maintainer: + email: "adam.li@eclicktech.com.cn" +capabilities: + app: + mediaTypes: + - banner + - video + - native \ No newline at end of file diff --git a/static/bidder-params/adelement.json b/static/bidder-params/adelement.json new file mode 100644 index 00000000000..d293fb6d7fd --- /dev/null +++ b/static/bidder-params/adelement.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Adelement Params", + "description": "A schema which validates params accepted by the Adelement", + "type": "object", + "properties": { + "supply_id": { + "type": "string", + "description": "Supply id", + "minLength": 1 + } + }, + "required": ["supply_id"] +} diff --git a/static/bidder-params/adform.json b/static/bidder-params/adform.json deleted file mode 100644 index e112f122e49..00000000000 --- a/static/bidder-params/adform.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Adform Adapter Params", - "description": "A schema which validates params accepted by the adf adapter", - "type": "object", - "properties": { - "mid": { - "type": ["integer", "string"], - "pattern": "^\\d+$", - "description": "An ID which identifies the placement selling the impression" - }, - "inv": { - "type": ["integer"], - "description": "An ID which identifies the Adform inventory source id" - }, - "mname": { - "type": ["string"], - "description": "A Name which identifies the placement selling the impression" - }, - "priceType": { - "type": ["string"], - "description": "gross or net. Default is net.", - "pattern": "gross|net" - } - }, - "anyOf":[ - { - "required": ["mid"] - }, { - "required": ["inv", "mname"] - } - ] -} diff --git a/static/bidder-params/adnuntius.json b/static/bidder-params/adnuntius.json index ff975501edb..f7ab32d2f48 100644 --- a/static/bidder-params/adnuntius.json +++ b/static/bidder-params/adnuntius.json @@ -9,6 +9,10 @@ "type": "string", "description": "Placement ID" }, + "maxDeals": { + "type": "integer", + "description": "Specify how many deals that you want to return from the auction." + }, "network": { "type": "string", "description": "Network if required" @@ -16,6 +20,10 @@ "noCookies": { "type": "boolean", "description": "Disable cookies being set by the ad server." + }, + "bidType": { + "type": "string", + "description": "Allows you to specify Net or Gross bids." } }, diff --git a/static/bidder-params/adsyield.json b/static/bidder-params/adsyield.json deleted file mode 100644 index c7c5308890f..00000000000 --- a/static/bidder-params/adsyield.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Adsyield Adapter Params", - "description": "A schema which validates params accepted by the Adsyield adapter", - "type": "object", - - "properties": { - "host": { - "type": "string", - "description": "Ad network's RTB host", - "format": "hostname", - "pattern": "^.+\\..+$" - }, - "publisherId": { - "type": ["integer", "string"], - "description": "Publisher ID", - "minimum": 1, - "pattern": "^[1-9][0-9]*$" - } - }, - - "required": ["host", "publisherId"] -} diff --git a/static/bidder-params/alkimi.json b/static/bidder-params/alkimi.json new file mode 100644 index 00000000000..93e09022469 --- /dev/null +++ b/static/bidder-params/alkimi.json @@ -0,0 +1,28 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Alkimi Adapter Params", + "description": "A schema which validates params accepted by the Alkimi adapter", + "type": "object", + + "properties": { + "token": { + "type": "string", + "description": "Publisher Token provided by Alkimi" + }, + "bidFloor": { + "type": "number", + "description": "The minimum CPM price in USD", + "minimum": 0 + }, + "instl": { + "type": "number", + "description": "Set to 1 if using interstitial (default: 0)" + }, + "exp": { + "type": "number", + "description": "Advisory as to the number of seconds that may elapse between the auction and the actual impression" + } + }, + + "required": ["token"] +} diff --git a/static/bidder-params/appstock.json b/static/bidder-params/appstock.json deleted file mode 100644 index eb2251e4e47..00000000000 --- a/static/bidder-params/appstock.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Appstock Adapter Params", - "description": "A schema which validates params accepted by the Appstock adapter", - "type": "object", - - "properties": { - "host": { - "type": "string", - "description": "Ad network's RTB host", - "format": "hostname", - "pattern": "^.+\\..+$" - }, - "publisherId": { - "type": ["integer", "string"], - "description": "Publisher ID", - "minimum": 1, - "pattern": "^[1-9][0-9]*$" - } - }, - - "required": ["host", "publisherId"] -} diff --git a/static/bidder-params/bizzclick.json b/static/bidder-params/bizzclick.json index 429e2948f8a..3f16e990be1 100644 --- a/static/bidder-params/bizzclick.json +++ b/static/bidder-params/bizzclick.json @@ -9,11 +9,34 @@ "description": "Account id", "minLength": 1 }, + "sourceId": { + "type": "string", + "description": "Source id", + "minLength": 1 + }, + "host": { + "type": "string", + "description": "Server region", + "minLength": 1 + }, "placementId": { "type": "string", - "description": "PlacementId id", + "description": "Deprecated, use sourceId instead", "minLength": 1 } }, - "required": ["accountId", "placementId"] + "oneOf": [ + { + "required": [ + "accountId", + "placementId" + ] + }, + { + "required": [ + "accountId", + "sourceId" + ] + } + ] } \ No newline at end of file diff --git a/static/bidder-params/bwx.json b/static/bidder-params/bwx.json new file mode 100644 index 00000000000..873aadad40c --- /dev/null +++ b/static/bidder-params/bwx.json @@ -0,0 +1,21 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "BoldwinX Adapter Params", + "description": "A schema which validates params accepted by the BoldwinX adapter", + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "BoldwinX environment", + "minLength": 1 + }, + "pid": { + "type": "string", + "description": "Unique placement ID", + "minLength": 1 + } + }, + "required": [ + "pid" + ] + } diff --git a/static/bidder-params/consumable.json b/static/bidder-params/consumable.json index b1db53568b9..7709ab09d37 100644 --- a/static/bidder-params/consumable.json +++ b/static/bidder-params/consumable.json @@ -24,7 +24,15 @@ "type": "string", "description": "The unit name from Consumable (expected to be a valid CSS class name)", "pattern": "^-?[_a-zA-Z]+[_a-zA-Z0-9-]*$" + }, + "placementId": { + "type": "string", + "description": "The placementID from Consumable, Required for non-site requests", + "pattern": "^[a-zA-Z0-9]+$" } }, - "required": ["siteId", "networkId","unitId"] + "oneOf": [ + {"required": ["siteId", "networkId","unitId"] }, + {"required": ["placementId"] } + ] } diff --git a/static/bidder-params/copper6.json b/static/bidder-params/copper6.json deleted file mode 100644 index fa4050f6c84..00000000000 --- a/static/bidder-params/copper6.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Copper6 Adapter Params", - "description": "A schema which validates params accepted by the Copper6 ssp adapter", - - "type": "object", - "properties": { - "placementId": { - "type": "integer", - "description": "An ID which identifies this placement of the impression" - }, - "siteId": { - "type": "integer", - "description": "An ID which identifies the site selling the impression" - }, - "aid": { - "type": "integer", - "description": "An ID which identifies the channel" - }, - "bidFloor": { - "type": "number", - "description": "BidFloor, US Dollars" - } - }, - "required": ["aid"] -} diff --git a/static/bidder-params/dxkulture.json b/static/bidder-params/dxkulture.json new file mode 100644 index 00000000000..858eefd22e4 --- /dev/null +++ b/static/bidder-params/dxkulture.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "DXKulture Adapter Params", + "description": "A schema which validates params accepted by the DXKulture adapter", + "type": "object", + "properties": { + "publisherId": { + "type": "string", + "description": "The publisher id" + }, + "placementId": { + "type": "string", + "description": "The placement id" + } + }, + "required": [ + "publisherId", + "placementId" + ] +} diff --git a/static/bidder-params/edge226.json b/static/bidder-params/edge226.json new file mode 100644 index 00000000000..95b0fbdabb8 --- /dev/null +++ b/static/bidder-params/edge226.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Edge226 Adapter Params", + "description": "A schema which validates params accepted by the Edge226 adapter", + "type": "object", + + "properties": { + "placementId": { + "type": "string", + "minLength": 1, + "description": "Placement ID" + }, + "endpointId": { + "type": "string", + "minLength": 1, + "description": "Endpoint ID" + } + }, + "oneOf": [ + { "required": ["placementId"] }, + { "required": ["endpointId"] } + ] + } \ No newline at end of file diff --git a/static/bidder-params/engagebdr.json b/static/bidder-params/engagebdr.json deleted file mode 100644 index 4f987004045..00000000000 --- a/static/bidder-params/engagebdr.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "EngageBDR Adapter Params", - "description": "A schema which validates params accepted by the EngageBDR adapter", - "type": "object", - "properties": { - "sspid": { - "type": "string", - "description": "SSPID parameter", - "pattern": "^[0-9]+$" - } - }, - "required": ["sspid"] -} diff --git a/static/bidder-params/epsilon.json b/static/bidder-params/epsilon.json deleted file mode 100644 index a33a68325e4..00000000000 --- a/static/bidder-params/epsilon.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Epsilon Adapter Params", - "description": "A schema which validates params accepted by the Epsilon adapter.", - "type": "object", - "properties": { - "site_id": { - "type": "string", - "description": "An Epsilon specific ID which identifies the site." - }, - "secure": { - "type": "integer", - "description": "Override http/https context on ad markup." - }, - "bidfloor" : { - "type": "number", - "description": "Minimum bid price that will be considered." - }, - "tag_id": { - "type": "string", - "description": "Identifies specific ad placement." - }, - "position": { - "type": "integer", - "description": "Ad position on screen." - }, - "mimes": { - "type": "array", - "description": "Array of content MIME types. For videos only.", - "items": { - "type": "string" - } - }, - "maxduration": { - "type": "integer", - "description": "Maximum duration in seconds. For videos only." - }, - "api": { - "type": "array", - "description": "Array of supported API frameworks. For videos only.", - "items": { - "type": "integer" - } - }, - "protocols": { - "type": "array", - "description": "Array of supported video protocols. For videos only.", - "items": { - "type": "integer" - } - } - }, - "required": ["site_id"] -} \ No newline at end of file diff --git a/static/bidder-params/evtech.json b/static/bidder-params/evtech.json deleted file mode 100644 index 5e78b7de171..00000000000 --- a/static/bidder-params/evtech.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Evolution Technologies Adapter Params", - "description": "A schema which validates params accepted by the Evolution Technologies adapter", - "type": "object", - - "properties": { - "host": { - "type": "string", - "description": "Ad network's RTB host", - "format": "hostname", - "pattern": "^.+\\..+$" - }, - "publisherId": { - "type": ["integer", "string"], - "description": "Publisher ID", - "minimum": 1, - "pattern": "^[1-9][0-9]*$" - } - }, - - "required": ["host", "publisherId"] -} diff --git a/static/bidder-params/freewheel-ssp.json b/static/bidder-params/freewheel-ssp.json deleted file mode 100644 index 23375f7603c..00000000000 --- a/static/bidder-params/freewheel-ssp.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "FreewheelSSP old Adapter Params", - "description": "A schema which validates params accepted by the FreewheelSSP adapter", - "type": "object", - - "properties": { - "zoneId": { - "type": ["integer", "string"], - "description": "Zone ID" - } - }, - - "required": ["zoneId"] -} diff --git a/static/bidder-params/greedygame.json b/static/bidder-params/greedygame.json deleted file mode 100644 index 2057c147d44..00000000000 --- a/static/bidder-params/greedygame.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "GreedyGame Adapter Params", - "description": "A schema which validates params accepted by the GreedyGame adapter", - "type": "object", - - "properties": { - "host": { - "type": "string", - "description": "Ad network's RTB host", - "format": "hostname", - "pattern": "^.+\\..+$" - }, - "publisherId": { - "type": ["integer", "string"], - "description": "Publisher ID", - "minimum": 1, - "pattern": "^[1-9][0-9]*$" - } - }, - - "required": ["host", "publisherId"] -} diff --git a/static/bidder-params/gumgum.json b/static/bidder-params/gumgum.json index 95f05e7d517..c9972713f87 100644 --- a/static/bidder-params/gumgum.json +++ b/static/bidder-params/gumgum.json @@ -20,6 +20,10 @@ "slot": { "type": "integer", "description": "A slot id used to identify a slot placement mapped to a GumGum zone or publisher" + }, + "product": { + "type": "string", + "description": "Product param that allow support for Desktop Skins - display and video" } }, "anyOf": [ @@ -34,4 +38,4 @@ ] } ] -} +} \ No newline at end of file diff --git a/static/bidder-params/iionads.json b/static/bidder-params/iionads.json deleted file mode 100644 index b9196445acc..00000000000 --- a/static/bidder-params/iionads.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Iion Adapter Params", - "description": "A schema which validates params accepted by the Iion adapter", - "type": "object", - - "properties": { - "host": { - "type": "string", - "description": "Ad network's RTB host", - "format": "hostname", - "pattern": "^.+\\..+$" - }, - "publisherId": { - "type": ["integer", "string"], - "description": "Publisher ID", - "minimum": 1, - "pattern": "^[1-9][0-9]*$" - } - }, - - "required": ["host", "publisherId"] -} diff --git a/static/bidder-params/iqx.json b/static/bidder-params/iqx.json new file mode 100644 index 00000000000..447c92beeb8 --- /dev/null +++ b/static/bidder-params/iqx.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "IQX Adapter Params", + "description": "A schema which validates params accepted by the iqzonex adapter", + "type": "object", + "properties": { + "env": { + "type": "string", + "description": "IQX environment", + "minLength": 1 + }, + "pid": { + "type": "string", + "description": "Unique placement ID", + "minLength": 1 + } + }, + "required": [ + "env", + "pid" + ] +} diff --git a/static/bidder-params/janet.json b/static/bidder-params/janet.json deleted file mode 100644 index 38c2c2eb044..00000000000 --- a/static/bidder-params/janet.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "JANet Adapter Params", - "description": "A schema which validates params accepted by the JANet adapter", - - "type": "object", - "properties": { - "placementId": { - "type": "integer", - "description": "An ID which identifies this placement of the impression" - }, - "siteId": { - "type": "integer", - "description": "An ID which identifies the site selling the impression" - }, - "aid": { - "type": "integer", - "description": "An ID which identifies the channel" - }, - "bidFloor": { - "type": "number", - "description": "BidFloor, US Dollars" - } - }, - "required": ["aid"] -} diff --git a/static/bidder-params/lemmadigital.json b/static/bidder-params/lemmadigital.json new file mode 100644 index 00000000000..be4a89edd7a --- /dev/null +++ b/static/bidder-params/lemmadigital.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Lemma Adapter Params", + "description": "A schema which validates params accepted by the Lemma adapter", + "type": "object", + + "properties": { + "pid": { + "type": "integer", + "description": "Publisher ID" + }, + "aid": { + "type": "integer", + "description": "Ad ID" + } + }, + + "required": ["pid", "aid"] +} \ No newline at end of file diff --git a/static/bidder-params/kubient.json b/static/bidder-params/minutemedia.json similarity index 58% rename from static/bidder-params/kubient.json rename to static/bidder-params/minutemedia.json index 9b975289a7b..c4c475b132f 100644 --- a/static/bidder-params/kubient.json +++ b/static/bidder-params/minutemedia.json @@ -1,13 +1,14 @@ { "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Kubient Adapter Params", - "description": "A schema which validates params accepted by the Kubient adapter", + "title": "MinuteMedia Adapter Params", + "description": "A schema which validates params accepted by the MinuteMedia adapter", "type": "object", "properties": { - "zoneid": { + "org": { "type": "string", - "description": "Zone ID identifies Kubient placement ID.", + "description": "The organization ID.", "minLength": 1 } - } -} + }, + "required": ["org"] +} \ No newline at end of file diff --git a/static/bidder-params/nanointeractive.json b/static/bidder-params/nanointeractive.json deleted file mode 100644 index aacd3154a92..00000000000 --- a/static/bidder-params/nanointeractive.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "NanoInteractive Adapter Params", - "description": "A schema which validates params accepted by the NanoInteractive adapter", - "type": "object", - "properties": { - "pid": { - "type": "string", - "description": "Placement id" - }, - "nq": { - "type": "array", - "items": { - "type": "string" - }, - "description": "search queries" - }, - "category": { - "type": "string", - "description": "IAB Category" - }, - "subId": { - "type": "string", - "description": "any segment value provided by publisher" - }, - "ref" : { - "type": "string", - "description": "referer" - } - }, - "required": ["pid"] -} diff --git a/static/bidder-params/ninthdecimal.json b/static/bidder-params/ninthdecimal.json deleted file mode 100755 index f230361d77e..00000000000 --- a/static/bidder-params/ninthdecimal.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "NinthDecimal Adapter Params", - "description": "A schema which validates params accepted by the NinthDecimal adapter", - "type": "object", - "properties": { - "pubid": { - "type": "string", - "description": "An id used to identify NinthDecimal publisher.", - "minLength": 8 - }, - "placement": { - "type": "string", - "description": "A placement created on adserver." - } - }, - "required": ["pubid"] -} diff --git a/static/bidder-params/applogy.json b/static/bidder-params/oms.json similarity index 51% rename from static/bidder-params/applogy.json rename to static/bidder-params/oms.json index 2650640c115..f33286d10d9 100644 --- a/static/bidder-params/applogy.json +++ b/static/bidder-params/oms.json @@ -1,13 +1,14 @@ { "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Applogy Adapter Params", - "description": "A schema which validates params accepted by the Applogy adapter", + "title": "Online Media Solutions Adapter Params", + "description": "A schema which validates params accepted by the OMS adapter", "type": "object", "properties": { - "token": { + "pid": { "type": "string", - "description": "Applogy token" + "description": "An id used to identify OMS publisher.", + "minLength": 5 } }, - "required": ["token"] + "required": ["pid"] } diff --git a/static/bidder-params/pgam.json b/static/bidder-params/pgam.json deleted file mode 100644 index c0a016a7d24..00000000000 --- a/static/bidder-params/pgam.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "PGAM Adapter Params", - "description": "A schema which validates params accepted by the PGAM ssp adapter", - - "type": "object", - "properties": { - "placementId": { - "type": "integer", - "description": "An ID which identifies this placement of the impression" - }, - "siteId": { - "type": "integer", - "description": "An ID which identifies the site selling the impression" - }, - "aid": { - "type": "integer", - "description": "An ID which identifies the channel" - }, - "bidFloor": { - "type": "number", - "description": "BidFloor, US Dollars" - } - }, - "required": ["aid"] -} diff --git a/static/bidder-params/quantumdex.json b/static/bidder-params/quantumdex.json deleted file mode 100644 index 3ef9abe34c6..00000000000 --- a/static/bidder-params/quantumdex.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Quantumdex Adapter Params", - "description": "A schema which validates params accepted by the Quantumdex adapter", - "type": "object", - "properties": { - "placementId": { - "type": "string", - "description": "Placement ID provided by Quantumdex" - }, - "siteId": { - "type": "string", - "description": "Publisher site ID from Quantumdex" - }, - "floorPrice": { - "type": "number", - "description": "CPM bidfloor in USD" - } - }, - "oneOf": [ - { - "required": [ - "placementId" - ] - }, - { - "required": [ - "siteId" - ] - } - ] -} \ No newline at end of file diff --git a/static/bidder-params/relevantdigital.json b/static/bidder-params/relevantdigital.json new file mode 100644 index 00000000000..5f88d85fe58 --- /dev/null +++ b/static/bidder-params/relevantdigital.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Relevant Digital Adapter Params", + "description": "A schema which validates params accepted by the Relevant Digital adapter", + "type": "object", + "properties": { + "accountId": { + "type": "string", + "description": "An ID which identifies the Relevant Digital account ID" + }, + "placementId": { + "type": "string", + "description": "An ID which identifies the Relevant Digital placement ID" + }, + "pbsHost": { + "type": "string", + "description": "Prebid Server Host supplied by Relevant Digital" + }, + "pbsBufferMs": { + "type": "number", + "description": "TMax buffer, default is 250" + } + }, + "required": [ + "accountId", + "placementId", + "pbsHost" + ] +} diff --git a/static/bidder-params/rhythmone.json b/static/bidder-params/rhythmone.json deleted file mode 100644 index 01366b45607..00000000000 --- a/static/bidder-params/rhythmone.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Rhythmone Adapter Params", - "description": "A schema which validates params accepted by the Rhythmone adapter", - "type": "object", - "properties": { - "placementId": { - "type": "string", - "description": "An ID which is used to frame Rhythmone ad tag", - "minLength": 1 - }, - "path": { - "type": "string", - "description": "An ID which is used to frame Rhythmone ad tag", - "minLength": 1 - }, - "zone": { - "type": "string", - "description": "An ID which is used to frame Rhythmone ad tag", - "minLength": 1 - } - }, - "required": ["placementId", "path", "zone"] -} diff --git a/static/bidder-params/seedingAlliance.json b/static/bidder-params/seedingAlliance.json index cf9aa375eb4..d72086230aa 100644 --- a/static/bidder-params/seedingAlliance.json +++ b/static/bidder-params/seedingAlliance.json @@ -8,6 +8,14 @@ "type": "string", "description": "Ad Unit ID", "minLength": 1 + }, + "seatId": { + "type": "string", + "description": "Deprecated, please use accountId" + }, + "accountId": { + "type": "string", + "description": "Account ID of partner" } }, "required": [ diff --git a/static/bidder-params/smartx.json b/static/bidder-params/smartx.json new file mode 100644 index 00000000000..3bd97456770 --- /dev/null +++ b/static/bidder-params/smartx.json @@ -0,0 +1,37 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "smartclip.tv Adapter Params", + "description": "A schema which validates params accepted by the smartclip.tv adapter", + "type": "object", + "properties": { + "tagId": { + "type": "string", + "description": "Ad tag ID" + }, + "publisherId": { + "type": "string", + "description": "Publisher ID" + }, + "siteId": { + "type": "string", + "description": "Site ID" + }, + "appId": { + "type": "string", + "description": "App ID" + }, + "bundleId": { + "type": "string", + "description": "Bundle ID" + }, + "storeUrl": { + "type": "string", + "description": "AppStore URL" + } + }, + "oneOf": [ + { "required": ["siteId"] }, + { "required": ["appId"] } + ], + "required": ["tagId", "publisherId"] +} \ No newline at end of file diff --git a/static/bidder-params/sovrnXsp.json b/static/bidder-params/sovrnXsp.json new file mode 100644 index 00000000000..beea3f588df --- /dev/null +++ b/static/bidder-params/sovrnXsp.json @@ -0,0 +1,27 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Sovrn XSP Adapter Params", + "description": "Schema validating params accepted by the Sovrn XSP adapter", + "type": "object", + "properties": { + "pub_id": { + "type": "string", + "description": "Assigned publisher ID", + "minLength": 4 + }, + "med_id": { + "type": "string", + "description": "Property ID not zone ID not provided" + }, + "zone_id": { + "type": "string", + "description": "Specific zone ID for this placement, belonging to app/site", + "minLength": 20 + }, + "force_bid": { + "type": "boolean", + "description": "Force bids with a test creative" + } + }, + "required": [ "pub_id" ] + } diff --git a/static/bidder-params/streamkey.json b/static/bidder-params/streamkey.json deleted file mode 100644 index ec8e5b1b643..00000000000 --- a/static/bidder-params/streamkey.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Streamkey Adapter Params", - "description": "A schema which validates params accepted by the Streamkey adapter", - - "type": "object", - "properties": { - "placementId": { - "type": "integer", - "description": "An ID which identifies this placement of the impression" - }, - "siteId": { - "type": "integer", - "description": "An ID which identifies the site selling the impression" - }, - "aid": { - "type": "integer", - "description": "An ID which identifies the channel" - }, - "bidFloor": { - "type": "number", - "description": "BidFloor, US Dollars" - } - }, - "required": ["aid"] -} diff --git a/static/bidder-params/suntContent.json b/static/bidder-params/suntContent.json deleted file mode 100644 index e85ad3f5de5..00000000000 --- a/static/bidder-params/suntContent.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "SUNT Content Adapter Params", - "description": "A schema which validates params accepted by the SUNT Content adapter", - "type": "object", - "properties": { - "adUnitId": { - "type": "string", - "description": "Ad Unit ID", - "minLength": 1 - } - }, - "required": [ - "adUnitId" - ] -} \ No newline at end of file diff --git a/static/bidder-params/synacormedia.json b/static/bidder-params/synacormedia.json deleted file mode 100644 index 21eb06cc7ce..00000000000 --- a/static/bidder-params/synacormedia.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Synacormedia Adapter Params", - "description": "DEPRECATED: Use imds bidder instead. A schema which validates params accepted by the Synacormedia adapter", - - "type": "object", - "properties": { - "seatId": { - "type": "string", - "description": "The seat id." - }, - "tagId": { - "type": "string", - "description": "The tag id." - } - }, - - "required": ["seatId"] -} diff --git a/static/bidder-params/teads.json b/static/bidder-params/teads.json new file mode 100644 index 00000000000..2b3f47d8bc2 --- /dev/null +++ b/static/bidder-params/teads.json @@ -0,0 +1,15 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Teads Adapter Params", + "description": "A schema which validates params accepted by the Teads adapter", + + "type": "object", + "properties": { + "placementId": { + "type": "integer", + "description": "The placement id.", + "minimum": 1 + } + }, + "required": ["placementId"] + } \ No newline at end of file diff --git a/static/bidder-params/trustx.json b/static/bidder-params/trustx.json deleted file mode 100644 index efedf9de537..00000000000 --- a/static/bidder-params/trustx.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "TrustX Adapter Params", - "description": "A schema which validates params accepted by TrustX adapter", - "type": "object", - "properties": { - "uid": { - "type": "integer", - "description": "An ID which identifies this placement of the impression" - } - }, - "required": [] -} diff --git a/static/bidder-params/valueimpression.json b/static/bidder-params/valueimpression.json deleted file mode 100644 index 793e940eb11..00000000000 --- a/static/bidder-params/valueimpression.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Valueimpression Adapter Params", - "description": "A schema which validates params accepted by the Valueimpression adapter", - "type": "object", - "properties": { - "placementId": { - "type": "string", - "description": "Placement ID provided by Valueimpression" - }, - "siteId": { - "type": "string", - "description": "Publisher site ID from Valueimpression" - }, - "floorPrice": { - "type": "number", - "description": "CPM bidfloor in USD" - } - }, - "oneOf": [ - { - "required": [ - "placementId" - ] - }, - { - "required": [ - "siteId" - ] - } - ] -} \ No newline at end of file diff --git a/static/bidder-params/viewdeos.json b/static/bidder-params/viewdeos.json deleted file mode 100644 index 3e309e4b77a..00000000000 --- a/static/bidder-params/viewdeos.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "Viewdeos Adapter Params", - "description": "A schema which validates params accepted by the Viewdeos adapter", - - "type": "object", - "properties": { - "placementId": { - "type": "integer", - "description": "An ID which identifies this placement of the impression" - }, - "siteId": { - "type": "integer", - "description": "An ID which identifies the site selling the impression" - }, - "aid": { - "type": "integer", - "description": "An ID which identifies the channel" - }, - "bidFloor": { - "type": "number", - "description": "BidFloor, US Dollars" - } - }, - "required": ["aid"] -} diff --git a/static/bidder-params/xtrmqb.json b/static/bidder-params/xtrmqb.json deleted file mode 100644 index 59e711e9ad3..00000000000 --- a/static/bidder-params/xtrmqb.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "XTRM QB Adapter Params", - "description": "A schema which validates params accepted by the XTRM QB adapter", - "type": "object", - - "properties": { - "host": { - "type": "string", - "description": "Ad network's RTB host", - "format": "hostname", - "pattern": "^.+\\..+$" - }, - "publisherId": { - "type": ["integer", "string"], - "description": "Publisher ID", - "minimum": 1, - "pattern": "^[1-9][0-9]*$" - } - }, - - "required": ["host", "publisherId"] -} diff --git a/static/bidder-params/yahooAdvertising.json b/static/bidder-params/yahooAdvertising.json deleted file mode 100644 index 4778f0778c7..00000000000 --- a/static/bidder-params/yahooAdvertising.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "YahooAdvertising Adapter Params", - "description": "A schema which validates params accepted by the YahooAdvertising adapter", - "type": "object", - "properties": { - "dcn": { - "type": "string", - "minLength": 1, - "description": "Site ID provided by One Mobile" - }, - "pos": { - "type": "string", - "minLength": 1, - "description": "Placement ID" - } - }, - "required": ["dcn", "pos"] -} diff --git a/static/bidder-params/yahoossp.json b/static/bidder-params/yahoossp.json deleted file mode 100644 index 9d971149339..00000000000 --- a/static/bidder-params/yahoossp.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-04/schema#", - "title": "YahooSSP Adapter Params", - "description": "A schema which validates params accepted by the YahooSSP adapter", - "type": "object", - "properties": { - "dcn": { - "type": "string", - "minLength": 1, - "description": "Site ID provided by One Mobile" - }, - "pos": { - "type": "string", - "minLength": 1, - "description": "Placement ID" - } - }, - "required": ["dcn", "pos"] -} diff --git a/static/bidder-params/yandex.json b/static/bidder-params/yandex.json new file mode 100644 index 00000000000..ca777077611 --- /dev/null +++ b/static/bidder-params/yandex.json @@ -0,0 +1,43 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Yandex Adapter Params", + "description": "A schema which validates params accepted by the Yandex adapter", + "type": "object", + "oneOf": [ + { + "type": "object", + "description": "Deprecated composite ad placement identifier", + "properties": { + "page_id": { + "type": "integer", + "minLength": 1, + "minimum": 1, + "description": "Special Page Id provided by Yandex Manager" + }, + "imp_id": { + "type": "integer", + "minLength": 1, + "minimum": 1, + "description": "Special identifier provided by Yandex Manager" + } + }, + "required": [ + "page_id", + "imp_id" + ] + }, + { + "type": "object", + "properties": { + "placement_id": { + "type": "string", + "description": "Ad placement identifier", + "pattern": "(\\S+-)?\\d+-\\d+" + } + }, + "required": [ + "placement_id" + ] + } + ] +} \ No newline at end of file diff --git a/static/bidder-params/zmaticoo.json b/static/bidder-params/zmaticoo.json new file mode 100644 index 00000000000..fc89e5bedf9 --- /dev/null +++ b/static/bidder-params/zmaticoo.json @@ -0,0 +1,22 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "zMaticoo Adapter Params", + "description": "A schema which validates params accepted by the zMaticoo adapter", + "type": "object", + "properties": { + "pubId": { + "type": "string", + "description": "Publisher ID", + "minLength": 1 + }, + "zoneId": { + "type": "string", + "description": "Zone Id", + "minLength": 1 + } + }, + "required": [ + "pubId", + "zoneId" + ] +} \ No newline at end of file diff --git a/static/index.html b/static/index.html index a3518ce27ae..ac79079f34f 100644 --- a/static/index.html +++ b/static/index.html @@ -3,7 +3,7 @@ Prebid Server - Prebid Server is a server-to-server proxy for
Prebid.js users. + Prebid Server is a server-to-server proxy for Prebid.js users. The host is not responsible for the content or advertising delivered through this proxy. For more information, please contact prebid-server - at - prebid.org. diff --git a/stored_requests/backends/db_fetcher/fetcher.go b/stored_requests/backends/db_fetcher/fetcher.go index 3ea36bc0fc7..d6fade0e866 100644 --- a/stored_requests/backends/db_fetcher/fetcher.go +++ b/stored_requests/backends/db_fetcher/fetcher.go @@ -7,8 +7,8 @@ import ( "github.com/lib/pq" "github.com/golang/glog" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/db_provider" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests/backends/db_provider" ) func NewFetcher( @@ -152,7 +152,7 @@ func (fetcher *dbFetcher) FetchResponses(ctx context.Context, ids []string) (dat } func (fetcher *dbFetcher) FetchAccount(ctx context.Context, accountDefaultsJSON json.RawMessage, accountID string) (json.RawMessage, []error) { - return nil, []error{stored_requests.NotFoundError{accountID, "Account"}} + return nil, []error{stored_requests.NotFoundError{ID: accountID, DataType: "Account"}} } func (fetcher *dbFetcher) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) { diff --git a/stored_requests/backends/db_fetcher/fetcher_test.go b/stored_requests/backends/db_fetcher/fetcher_test.go index 04753fb8af5..f736e1bea15 100644 --- a/stored_requests/backends/db_fetcher/fetcher_test.go +++ b/stored_requests/backends/db_fetcher/fetcher_test.go @@ -11,7 +11,7 @@ import ( "time" "github.com/DATA-DOG/go-sqlmock" - "github.com/prebid/prebid-server/stored_requests/backends/db_provider" + "github.com/prebid/prebid-server/v2/stored_requests/backends/db_provider" "github.com/stretchr/testify/assert" ) diff --git a/stored_requests/backends/db_provider/db_provider.go b/stored_requests/backends/db_provider/db_provider.go index 0f79a7737e0..6e623356ed8 100644 --- a/stored_requests/backends/db_provider/db_provider.go +++ b/stored_requests/backends/db_provider/db_provider.go @@ -5,7 +5,7 @@ import ( "database/sql" "github.com/golang/glog" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" ) type DbProvider interface { diff --git a/stored_requests/backends/db_provider/db_provider_mock.go b/stored_requests/backends/db_provider/db_provider_mock.go index 3d4cfda76c3..c0ec3458eee 100644 --- a/stored_requests/backends/db_provider/db_provider_mock.go +++ b/stored_requests/backends/db_provider/db_provider_mock.go @@ -6,7 +6,7 @@ import ( "reflect" "github.com/DATA-DOG/go-sqlmock" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" ) func NewDbProviderMock() (*DbProviderMock, sqlmock.Sqlmock, error) { diff --git a/stored_requests/backends/db_provider/mysql_dbprovider.go b/stored_requests/backends/db_provider/mysql_dbprovider.go index 6301a119c45..38ad0836024 100644 --- a/stored_requests/backends/db_provider/mysql_dbprovider.go +++ b/stored_requests/backends/db_provider/mysql_dbprovider.go @@ -15,7 +15,7 @@ import ( "strings" "github.com/go-sql-driver/mysql" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" ) const customTLSKey = "prebid-tls" diff --git a/stored_requests/backends/db_provider/mysql_dbprovider_test.go b/stored_requests/backends/db_provider/mysql_dbprovider_test.go index e47280ef26b..b91352d08d6 100644 --- a/stored_requests/backends/db_provider/mysql_dbprovider_test.go +++ b/stored_requests/backends/db_provider/mysql_dbprovider_test.go @@ -6,7 +6,7 @@ import ( "runtime" "testing" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" "github.com/stretchr/testify/assert" ) diff --git a/stored_requests/backends/db_provider/postgres_dbprovider.go b/stored_requests/backends/db_provider/postgres_dbprovider.go index ef945faebc9..e2081f27df4 100644 --- a/stored_requests/backends/db_provider/postgres_dbprovider.go +++ b/stored_requests/backends/db_provider/postgres_dbprovider.go @@ -10,7 +10,7 @@ import ( "strconv" "strings" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" ) type PostgresDbProvider struct { diff --git a/stored_requests/backends/db_provider/postgres_dbprovider_test.go b/stored_requests/backends/db_provider/postgres_dbprovider_test.go index 9e98c0b5763..4b31e6f8ec3 100644 --- a/stored_requests/backends/db_provider/postgres_dbprovider_test.go +++ b/stored_requests/backends/db_provider/postgres_dbprovider_test.go @@ -4,7 +4,7 @@ import ( "errors" "testing" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" "github.com/stretchr/testify/assert" ) diff --git a/stored_requests/backends/empty_fetcher/fetcher.go b/stored_requests/backends/empty_fetcher/fetcher.go index 0246990c02e..65e5de90480 100644 --- a/stored_requests/backends/empty_fetcher/fetcher.go +++ b/stored_requests/backends/empty_fetcher/fetcher.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" - "github.com/prebid/prebid-server/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests" ) // EmptyFetcher is a nil-object which has no Stored Requests. @@ -33,7 +33,7 @@ func (fetcher EmptyFetcher) FetchResponses(ctx context.Context, ids []string) (d } func (fetcher EmptyFetcher) FetchAccount(ctx context.Context, accountDefaultJSON json.RawMessage, accountID string) (json.RawMessage, []error) { - return nil, []error{stored_requests.NotFoundError{accountID, "Account"}} + return nil, []error{stored_requests.NotFoundError{ID: accountID, DataType: "Account"}} } func (fetcher EmptyFetcher) FetchCategories(ctx context.Context, primaryAdServer, publisherId, iabCategory string) (string, error) { diff --git a/stored_requests/backends/file_fetcher/fetcher.go b/stored_requests/backends/file_fetcher/fetcher.go index 56f5bdf853c..21a54039cda 100644 --- a/stored_requests/backends/file_fetcher/fetcher.go +++ b/stored_requests/backends/file_fetcher/fetcher.go @@ -7,7 +7,8 @@ import ( "os" "strings" - "github.com/prebid/prebid-server/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/util/jsonutil" jsonpatch "gopkg.in/evanphx/json-patch.v4" ) @@ -78,7 +79,7 @@ func (fetcher *eagerFetcher) FetchCategories(ctx context.Context, primaryAdServe tmp := make(map[string]stored_requests.Category) - if err := json.Unmarshal(file, &tmp); err != nil { + if err := jsonutil.UnmarshalValid(file, &tmp); err != nil { return "", fmt.Errorf("Unable to unmarshal categories for adserver: '%s', publisherId: '%s'", primaryAdServer, publisherId) } fetcher.Categories[fileName] = tmp diff --git a/stored_requests/backends/file_fetcher/fetcher_test.go b/stored_requests/backends/file_fetcher/fetcher_test.go index 3c585f9f456..b4a73870392 100644 --- a/stored_requests/backends/file_fetcher/fetcher_test.go +++ b/stored_requests/backends/file_fetcher/fetcher_test.go @@ -6,7 +6,8 @@ import ( "fmt" "testing" - "github.com/prebid/prebid-server/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" ) @@ -35,7 +36,7 @@ func TestAccountFetcher(t *testing.T) { _, errs = fetcher.FetchAccount(context.Background(), json.RawMessage(`{"events_enabled":true}`), "nonexistent") assertErrorCount(t, 1, errs) assert.Error(t, errs[0]) - assert.Equal(t, stored_requests.NotFoundError{"nonexistent", "Account"}, errs[0]) + assert.Equal(t, stored_requests.NotFoundError{ID: "nonexistent", DataType: "Account"}, errs[0]) _, errs = fetcher.FetchAccount(context.Background(), json.RawMessage(`{"events_enabled"}`), "valid") assertErrorCount(t, 1, errs) @@ -58,7 +59,7 @@ func validateStoredReqOne(t *testing.T, storedRequests map[string]json.RawMessag } var req1Val map[string]string - if err := json.Unmarshal(value, &req1Val); err != nil { + if err := jsonutil.UnmarshalValid(value, &req1Val); err != nil { t.Errorf("Failed to unmarshal 1: %v", err) } if len(req1Val) != 1 { @@ -80,7 +81,7 @@ func validateStoredReqTwo(t *testing.T, storedRequests map[string]json.RawMessag } var req2Val string - if err := json.Unmarshal(value, &req2Val); err != nil { + if err := jsonutil.UnmarshalValid(value, &req2Val); err != nil { t.Errorf("Failed to unmarshal %d: %v", 2, err) } if req2Val != `esca"ped` { @@ -95,7 +96,7 @@ func validateImp(t *testing.T, storedImps map[string]json.RawMessage) { } var impVal map[string]bool - if err := json.Unmarshal(value, &impVal); err != nil { + if err := jsonutil.UnmarshalValid(value, &impVal); err != nil { t.Errorf("Failed to unmarshal some-imp: %v", err) } if len(impVal) != 1 { diff --git a/stored_requests/backends/http_fetcher/fetcher.go b/stored_requests/backends/http_fetcher/fetcher.go index 326b63fce71..75aa1090ce4 100644 --- a/stored_requests/backends/http_fetcher/fetcher.go +++ b/stored_requests/backends/http_fetcher/fetcher.go @@ -1,7 +1,6 @@ package http_fetcher import ( - "bytes" "context" "encoding/json" "fmt" @@ -10,7 +9,8 @@ import ( "net/url" "strings" - "github.com/prebid/prebid-server/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/util/jsonutil" jsonpatch "gopkg.in/evanphx/json-patch.v4" "github.com/golang/glog" @@ -142,7 +142,7 @@ func (fetcher *HttpFetcher) FetchAccounts(ctx context.Context, accountIDs []stri } } var responseData accountsResponseContract - if err = json.Unmarshal(respBytes, &responseData); err != nil { + if err = jsonutil.UnmarshalValid(respBytes, &responseData); err != nil { return nil, []error{ fmt.Errorf(`Error fetching accounts %v via http: failed to parse response: %v`, accountIDs, err), } @@ -209,7 +209,7 @@ func (fetcher *HttpFetcher) FetchCategories(ctx context.Context, primaryAdServer respBytes, err := io.ReadAll(httpResp.Body) tmp := make(map[string]stored_requests.Category) - if err := json.Unmarshal(respBytes, &tmp); err != nil { + if err := jsonutil.UnmarshalValid(respBytes, &tmp); err != nil { return "", fmt.Errorf("Unable to unmarshal categories for adserver: '%s', publisherId: '%s'", primaryAdServer, publisherId) } fetcher.Categories[dataName] = tmp @@ -240,7 +240,7 @@ func unpackResponse(resp *http.Response) (requestData map[string]json.RawMessage if resp.StatusCode == http.StatusOK { var responseObj responseContract - if err := json.Unmarshal(respBytes, &responseObj); err != nil { + if err := jsonutil.UnmarshalValid(respBytes, &responseObj); err != nil { errs = append(errs, err) return } @@ -260,7 +260,7 @@ func unpackResponse(resp *http.Response) (requestData map[string]json.RawMessage func convertNullsToErrs(m map[string]json.RawMessage, dataType string, errs []error) []error { for id, val := range m { - if bytes.Equal(val, []byte("null")) { + if val == nil { delete(m, id) errs = append(errs, stored_requests.NotFoundError{ ID: id, diff --git a/stored_requests/backends/http_fetcher/fetcher_test.go b/stored_requests/backends/http_fetcher/fetcher_test.go index 3c8ca7cb070..1358ce413c1 100644 --- a/stored_requests/backends/http_fetcher/fetcher_test.go +++ b/stored_requests/backends/http_fetcher/fetcher_test.go @@ -10,6 +10,7 @@ import ( "testing" "time" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" ) @@ -233,7 +234,7 @@ func newHandler(t *testing.T, expectReqIDs []string, expectImpIDs []string, json Imps: impIDResponse, } - if respBytes, err := json.Marshal(respObj); err != nil { + if respBytes, err := jsonutil.Marshal(respObj); err != nil { t.Errorf("failed to marshal responseContract in test: %v", err) w.WriteHeader(http.StatusInternalServerError) } else { @@ -267,7 +268,7 @@ func newAccountHandler(t *testing.T, expectAccIDs []string) func(w http.Response Accounts: accIDResponse, } - if respBytes, err := json.Marshal(respObj); err != nil { + if respBytes, err := jsonutil.Marshal(respObj); err != nil { t.Errorf("failed to marshal responseContract in test: %v", err) w.WriteHeader(http.StatusInternalServerError) } else { @@ -327,7 +328,7 @@ func richSplit(queryVal string) []string { } func jsonifyID(id string) json.RawMessage { - if b, err := json.Marshal(id); err != nil { + if b, err := jsonutil.Marshal(id); err != nil { return json.RawMessage([]byte("\"error encoding ID=" + id + "\"")) } else { return json.RawMessage(b) diff --git a/stored_requests/caches/cachestest/reliable.go b/stored_requests/caches/cachestest/reliable.go index 7fbaf7238af..517668318c7 100644 --- a/stored_requests/caches/cachestest/reliable.go +++ b/stored_requests/caches/cachestest/reliable.go @@ -5,7 +5,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests" ) const ( diff --git a/stored_requests/caches/memory/cache.go b/stored_requests/caches/memory/cache.go index 5939c26ddec..eb6317a3487 100644 --- a/stored_requests/caches/memory/cache.go +++ b/stored_requests/caches/memory/cache.go @@ -7,7 +7,7 @@ import ( "github.com/coocood/freecache" "github.com/golang/glog" - "github.com/prebid/prebid-server/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests" ) // NewCache returns an in-memory Cache which evicts items if: diff --git a/stored_requests/caches/memory/cache_test.go b/stored_requests/caches/memory/cache_test.go index b89bd5af26f..67ff661c7c5 100644 --- a/stored_requests/caches/memory/cache_test.go +++ b/stored_requests/caches/memory/cache_test.go @@ -7,8 +7,8 @@ import ( "strconv" "testing" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/caches/cachestest" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests/caches/cachestest" ) func TestLRURobustness(t *testing.T) { diff --git a/stored_requests/config/config.go b/stored_requests/config/config.go index 9cb349d1f72..112b11122f9 100644 --- a/stored_requests/config/config.go +++ b/stored_requests/config/config.go @@ -5,24 +5,24 @@ import ( "net/http" "time" - "github.com/prebid/prebid-server/metrics" + "github.com/prebid/prebid-server/v2/metrics" "github.com/golang/glog" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/db_fetcher" - "github.com/prebid/prebid-server/stored_requests/backends/db_provider" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/stored_requests/backends/file_fetcher" - "github.com/prebid/prebid-server/stored_requests/backends/http_fetcher" - "github.com/prebid/prebid-server/stored_requests/caches/memory" - "github.com/prebid/prebid-server/stored_requests/caches/nil_cache" - "github.com/prebid/prebid-server/stored_requests/events" - apiEvents "github.com/prebid/prebid-server/stored_requests/events/api" - databaseEvents "github.com/prebid/prebid-server/stored_requests/events/database" - httpEvents "github.com/prebid/prebid-server/stored_requests/events/http" - "github.com/prebid/prebid-server/util/task" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests/backends/db_fetcher" + "github.com/prebid/prebid-server/v2/stored_requests/backends/db_provider" + "github.com/prebid/prebid-server/v2/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/v2/stored_requests/backends/file_fetcher" + "github.com/prebid/prebid-server/v2/stored_requests/backends/http_fetcher" + "github.com/prebid/prebid-server/v2/stored_requests/caches/memory" + "github.com/prebid/prebid-server/v2/stored_requests/caches/nil_cache" + "github.com/prebid/prebid-server/v2/stored_requests/events" + apiEvents "github.com/prebid/prebid-server/v2/stored_requests/events/api" + databaseEvents "github.com/prebid/prebid-server/v2/stored_requests/events/database" + httpEvents "github.com/prebid/prebid-server/v2/stored_requests/events/http" + "github.com/prebid/prebid-server/v2/util/task" ) // CreateStoredRequests returns three things: diff --git a/stored_requests/config/config_test.go b/stored_requests/config/config_test.go index b06feea7d31..2e5a8c2a079 100644 --- a/stored_requests/config/config_test.go +++ b/stored_requests/config/config_test.go @@ -13,14 +13,14 @@ import ( sqlmock "github.com/DATA-DOG/go-sqlmock" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/backends/db_provider" - "github.com/prebid/prebid-server/stored_requests/backends/empty_fetcher" - "github.com/prebid/prebid-server/stored_requests/backends/http_fetcher" - "github.com/prebid/prebid-server/stored_requests/events" - httpEvents "github.com/prebid/prebid-server/stored_requests/events/http" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests/backends/db_provider" + "github.com/prebid/prebid-server/v2/stored_requests/backends/empty_fetcher" + "github.com/prebid/prebid-server/v2/stored_requests/backends/http_fetcher" + "github.com/prebid/prebid-server/v2/stored_requests/events" + httpEvents "github.com/prebid/prebid-server/v2/stored_requests/events/http" "github.com/stretchr/testify/mock" ) diff --git a/stored_requests/events/api/api.go b/stored_requests/events/api/api.go index bf8edd2d849..2d489bded38 100644 --- a/stored_requests/events/api/api.go +++ b/stored_requests/events/api/api.go @@ -1,12 +1,12 @@ package api import ( - "encoding/json" "io" "net/http" "github.com/julienschmidt/httprouter" - "github.com/prebid/prebid-server/stored_requests/events" + "github.com/prebid/prebid-server/v2/stored_requests/events" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) type eventsAPI struct { @@ -43,7 +43,7 @@ func (api *eventsAPI) HandleEvent(w http.ResponseWriter, r *http.Request, _ http } var save events.Save - if err := json.Unmarshal(body, &save); err != nil { + if err := jsonutil.UnmarshalValid(body, &save); err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Invalid update.\n")) return @@ -59,7 +59,7 @@ func (api *eventsAPI) HandleEvent(w http.ResponseWriter, r *http.Request, _ http } var invalidation events.Invalidation - if err := json.Unmarshal(body, &invalidation); err != nil { + if err := jsonutil.UnmarshalValid(body, &invalidation); err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte("Invalid invalidation.\n")) return diff --git a/stored_requests/events/api/api_test.go b/stored_requests/events/api/api_test.go index 67ba836cb00..536afe820af 100644 --- a/stored_requests/events/api/api_test.go +++ b/stored_requests/events/api/api_test.go @@ -9,9 +9,9 @@ import ( "strings" "testing" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/caches/memory" - "github.com/prebid/prebid-server/stored_requests/events" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests/caches/memory" + "github.com/prebid/prebid-server/v2/stored_requests/events" ) func TestGoodRequests(t *testing.T) { diff --git a/stored_requests/events/database/database.go b/stored_requests/events/database/database.go index 24eddf214eb..260a58029cb 100644 --- a/stored_requests/events/database/database.go +++ b/stored_requests/events/database/database.go @@ -9,11 +9,11 @@ import ( "time" "github.com/golang/glog" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests/backends/db_provider" - "github.com/prebid/prebid-server/stored_requests/events" - "github.com/prebid/prebid-server/util/timeutil" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/stored_requests/backends/db_provider" + "github.com/prebid/prebid-server/v2/stored_requests/events" + "github.com/prebid/prebid-server/v2/util/timeutil" ) func bytesNull() []byte { diff --git a/stored_requests/events/database/database_test.go b/stored_requests/events/database/database_test.go index 8ce21bfde95..27a56b67fe3 100644 --- a/stored_requests/events/database/database_test.go +++ b/stored_requests/events/database/database_test.go @@ -7,10 +7,10 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests/backends/db_provider" - "github.com/prebid/prebid-server/stored_requests/events" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/stored_requests/backends/db_provider" + "github.com/prebid/prebid-server/v2/stored_requests/events" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" diff --git a/stored_requests/events/events.go b/stored_requests/events/events.go index 725df4279d8..e042b7c3513 100644 --- a/stored_requests/events/events.go +++ b/stored_requests/events/events.go @@ -4,7 +4,7 @@ import ( "context" "encoding/json" - "github.com/prebid/prebid-server/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests" ) // Save represents a bulk save diff --git a/stored_requests/events/events_test.go b/stored_requests/events/events_test.go index 580f1eddf37..165e6a6beb0 100644 --- a/stored_requests/events/events_test.go +++ b/stored_requests/events/events_test.go @@ -7,8 +7,8 @@ import ( "reflect" "testing" - "github.com/prebid/prebid-server/stored_requests" - "github.com/prebid/prebid-server/stored_requests/caches/memory" + "github.com/prebid/prebid-server/v2/stored_requests" + "github.com/prebid/prebid-server/v2/stored_requests/caches/memory" ) func TestListen(t *testing.T) { diff --git a/stored_requests/events/http/http.go b/stored_requests/events/http/http.go index 1c4f8fdff73..6c2145da2f3 100644 --- a/stored_requests/events/http/http.go +++ b/stored_requests/events/http/http.go @@ -12,7 +12,8 @@ import ( "golang.org/x/net/context/ctxhttp" "github.com/buger/jsonparser" - "github.com/prebid/prebid-server/stored_requests/events" + "github.com/prebid/prebid-server/v2/stored_requests/events" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/golang/glog" ) @@ -184,7 +185,7 @@ func (e *HTTPEvents) parse(endpoint string, resp *httpCore.Response, err error) } var respObj responseContract - if err := json.Unmarshal(respBytes, &respObj); err != nil { + if err := jsonutil.UnmarshalValid(respBytes, &respObj); err != nil { glog.Errorf("Failed to unmarshal body of GET %s for Stored Requests: %v", endpoint, err) return nil, false } diff --git a/stored_requests/events/http/http_test.go b/stored_requests/events/http/http_test.go index a185a3c2360..fa7f64f227f 100644 --- a/stored_requests/events/http/http_test.go +++ b/stored_requests/events/http/http_test.go @@ -2,13 +2,13 @@ package http import ( "context" - "encoding/json" "fmt" httpCore "net/http" "net/http/httptest" "testing" "time" + "github.com/prebid/prebid-server/v2/util/jsonutil" "github.com/stretchr/testify/assert" ) @@ -151,14 +151,14 @@ func TestStartup(t *testing.T) { t.Run(fmt.Sprintf("Step %d", i+1), func(t *testing.T) { // Check expected Saves if len(test.saves) > 0 { - saves, err := json.Marshal(<-ev.Saves()) + saves, err := jsonutil.Marshal(<-ev.Saves()) assert.NoError(t, err, `Failed to marshal event.Save object: %v`, err) assert.JSONEq(t, test.saves, string(saves)) } assert.Empty(t, ev.Saves(), "Unexpected additional messages in save channel") // Check expected Invalidations if len(test.invalidations) > 0 { - invalidations, err := json.Marshal(<-ev.Invalidations()) + invalidations, err := jsonutil.Marshal(<-ev.Invalidations()) assert.NoError(t, err, `Failed to marshal event.Invalidation object: %v`, err) assert.JSONEq(t, test.invalidations, string(invalidations)) } diff --git a/stored_requests/fetcher.go b/stored_requests/fetcher.go index 433b33427b8..cd3855c9eb8 100644 --- a/stored_requests/fetcher.go +++ b/stored_requests/fetcher.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" - "github.com/prebid/prebid-server/metrics" + "github.com/prebid/prebid-server/v2/metrics" ) // Fetcher knows how to fetch Stored Request data by id. diff --git a/stored_requests/fetcher_test.go b/stored_requests/fetcher_test.go index 684b867165c..34c15f61f96 100644 --- a/stored_requests/fetcher_test.go +++ b/stored_requests/fetcher_test.go @@ -6,8 +6,8 @@ import ( "errors" "testing" - "github.com/prebid/prebid-server/metrics" - "github.com/prebid/prebid-server/stored_requests/caches/nil_cache" + "github.com/prebid/prebid-server/v2/metrics" + "github.com/prebid/prebid-server/v2/stored_requests/caches/nil_cache" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" diff --git a/stored_responses/stored_responses.go b/stored_responses/stored_responses.go index 19b010fb12d..04dc3decf5a 100644 --- a/stored_responses/stored_responses.go +++ b/stored_responses/stored_responses.go @@ -4,11 +4,11 @@ import ( "context" "encoding/json" "fmt" + "strings" - "github.com/buger/jsonparser" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" - "github.com/prebid/prebid-server/stored_requests" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/stored_requests" ) type ImpsWithAuctionResponseIDs map[string]string @@ -56,8 +56,7 @@ func buildStoredResp(storedBidResponses ImpBidderStoredResp) BidderImpsWithBidRe return bidderToImpToResponses } -func extractStoredResponsesIds(impInfo []ImpExtPrebidData, - bidderMap map[string]openrtb_ext.BidderName) ( +func extractStoredResponsesIds(impInfo []*openrtb_ext.ImpWrapper) ( StoredResponseIDs, ImpBiddersWithBidResponseIDs, ImpsWithAuctionResponseIDs, @@ -75,48 +74,62 @@ func extractStoredResponsesIds(impInfo []ImpExtPrebidData, impBidderReplaceImp := ImpBidderReplaceImpID{} for index, impData := range impInfo { - impId, err := jsonparser.GetString(impData.Imp, "id") + impId := impData.ID + impExt, err := impData.GetImpExt() if err != nil { - return nil, nil, nil, nil, fmt.Errorf("request.imp[%d] missing required field: \"id\"", index) + return nil, nil, nil, nil, err + } + impExtPrebid := impExt.GetPrebid() + if impExtPrebid == nil { + continue } - if impData.ImpExtPrebid.StoredAuctionResponse != nil { - if len(impData.ImpExtPrebid.StoredAuctionResponse.ID) == 0 { + if impExtPrebid.StoredAuctionResponse != nil { + if len(impExtPrebid.StoredAuctionResponse.ID) == 0 { return nil, nil, nil, nil, fmt.Errorf("request.imp[%d] has ext.prebid.storedauctionresponse specified, but \"id\" field is missing ", index) } - allStoredResponseIDs = append(allStoredResponseIDs, impData.ImpExtPrebid.StoredAuctionResponse.ID) + allStoredResponseIDs = append(allStoredResponseIDs, impExtPrebid.StoredAuctionResponse.ID) - impAuctionResponseIDs[impId] = impData.ImpExtPrebid.StoredAuctionResponse.ID + impAuctionResponseIDs[impId] = impExtPrebid.StoredAuctionResponse.ID } - if len(impData.ImpExtPrebid.StoredBidResponse) > 0 { + if len(impExtPrebid.StoredBidResponse) > 0 { + + // bidders can be specified in imp.ext and in imp.ext.prebid.bidders + allBidderNames := make([]string, 0) + for bidderName := range impExtPrebid.Bidder { + allBidderNames = append(allBidderNames, bidderName) + } + for extData := range impExt.GetExt() { + // no bidders will not be processed + allBidderNames = append(allBidderNames, extData) + } bidderStoredRespId := make(map[string]string) bidderReplaceImpId := make(map[string]bool) - for _, bidderResp := range impData.ImpExtPrebid.StoredBidResponse { + for _, bidderResp := range impExtPrebid.StoredBidResponse { if len(bidderResp.ID) == 0 || len(bidderResp.Bidder) == 0 { return nil, nil, nil, nil, fmt.Errorf("request.imp[%d] has ext.prebid.storedbidresponse specified, but \"id\" or/and \"bidder\" fields are missing ", index) } - //check if bidder is valid/exists - if _, isValid := bidderMap[bidderResp.Bidder]; !isValid { - return nil, nil, nil, nil, fmt.Errorf("request.imp[impId: %s].ext.prebid.bidder contains unknown bidder: %s. Did you forget an alias in request.ext.prebid.aliases?", impId, bidderResp.Bidder) - } - // bidder is unique per one bid stored response - // if more than one bidder specified the last defined bidder id will take precedence - bidderStoredRespId[bidderResp.Bidder] = bidderResp.ID - impBiddersWithBidResponseIDs[impId] = bidderStoredRespId - - // stored response config can specify if imp id should be replaced with imp id from request - replaceImpId := true - if bidderResp.ReplaceImpId != nil { - // replaceimpid is true if not specified - replaceImpId = *bidderResp.ReplaceImpId - } - bidderReplaceImpId[bidderResp.Bidder] = replaceImpId - impBidderReplaceImp[impId] = bidderReplaceImpId - //storedAuctionResponseIds are not unique, but fetch will return single data for repeated ids - allStoredResponseIDs = append(allStoredResponseIDs, bidderResp.ID) + for _, bidderName := range allBidderNames { + if _, found := bidderStoredRespId[bidderName]; !found && strings.EqualFold(bidderName, bidderResp.Bidder) { + bidderStoredRespId[bidderName] = bidderResp.ID + impBiddersWithBidResponseIDs[impId] = bidderStoredRespId + + // stored response config can specify if imp id should be replaced with imp id from request + replaceImpId := true + if bidderResp.ReplaceImpId != nil { + // replaceimpid is true if not specified + replaceImpId = *bidderResp.ReplaceImpId + } + bidderReplaceImpId[bidderName] = replaceImpId + impBidderReplaceImp[impId] = bidderReplaceImpId + + //storedAuctionResponseIds are not unique, but fetch will return single data for repeated ids + allStoredResponseIDs = append(allStoredResponseIDs, bidderResp.ID) + } + } } } } @@ -129,14 +142,11 @@ func extractStoredResponsesIds(impInfo []ImpExtPrebidData, // Note that processStoredResponses must be called after processStoredRequests // because stored imps and stored requests can contain stored auction responses and stored bid responses // so the stored requests/imps have to be merged into the incoming request prior to processing stored auction responses. -func ProcessStoredResponses(ctx context.Context, requestJson []byte, storedRespFetcher stored_requests.Fetcher, bidderMap map[string]openrtb_ext.BidderName) (ImpsWithBidResponses, ImpBidderStoredResp, BidderImpReplaceImpID, []error) { - impInfo, errs := parseImpInfo(requestJson) - if len(errs) > 0 { - return nil, nil, nil, errs - } - storedResponsesIds, impBidderToStoredBidResponseId, impIdToRespId, impBidderReplaceImp, err := extractStoredResponsesIds(impInfo, bidderMap) +func ProcessStoredResponses(ctx context.Context, requestWrapper *openrtb_ext.RequestWrapper, storedRespFetcher stored_requests.Fetcher) (ImpsWithBidResponses, ImpBidderStoredResp, BidderImpReplaceImpID, []error) { + + storedResponsesIds, impBidderToStoredBidResponseId, impIdToRespId, impBidderReplaceImp, err := extractStoredResponsesIds(requestWrapper.GetImp()) if err != nil { - return nil, nil, nil, append(errs, err) + return nil, nil, nil, []error{err} } if len(storedResponsesIds) > 0 { @@ -195,28 +205,3 @@ func buildStoredResponsesMaps(storedResponses StoredResponseIdToStoredResponse, } return impIdToStoredResp, impBidderToStoredBidResponse, errs } - -// parseImpInfo parses the request JSON and returns the impressions with their unmarshalled imp.ext.prebid -// copied from exchange to isolate stored responses code from auction dependencies -func parseImpInfo(requestJson []byte) (impData []ImpExtPrebidData, errs []error) { - - if impArray, dataType, _, err := jsonparser.Get(requestJson, "imp"); err == nil && dataType == jsonparser.Array { - _, err = jsonparser.ArrayEach(impArray, func(imp []byte, _ jsonparser.ValueType, _ int, err error) { - impExtData, _, _, err := jsonparser.Get(imp, "ext", "prebid") - var impExtPrebid openrtb_ext.ExtImpPrebid - if impExtData != nil { - if err := json.Unmarshal(impExtData, &impExtPrebid); err != nil { - errs = append(errs, err) - } - } - newImpData := ImpExtPrebidData{imp, impExtPrebid} - impData = append(impData, newImpData) - }) - } - return -} - -type ImpExtPrebidData struct { - Imp json.RawMessage - ImpExtPrebid openrtb_ext.ExtImpPrebid -} diff --git a/stored_responses/stored_responses_test.go b/stored_responses/stored_responses_test.go index c4ddea278a7..49ce19d3414 100644 --- a/stored_responses/stored_responses_test.go +++ b/stored_responses/stored_responses_test.go @@ -6,8 +6,8 @@ import ( "errors" "testing" - "github.com/prebid/openrtb/v19/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) @@ -163,136 +163,109 @@ func TestBuildStoredBidResponses(t *testing.T) { } func TestProcessStoredAuctionAndBidResponsesErrors(t *testing.T) { - bidderMap := map[string]openrtb_ext.BidderName{"testBidder": "testBidder"} - testCases := []struct { description string - requestJson []byte + request openrtb2.BidRequest expectedErrorList []error }{ { description: "Invalid stored auction response format: empty stored Auction Response Id", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ "prebid": { - "storedauctionresponse": { - } - } - } - } - ]}`), + "storedauctionresponse": {} + }}`)}, + }, + }, expectedErrorList: []error{errors.New("request.imp[0] has ext.prebid.storedauctionresponse specified, but \"id\" field is missing ")}, }, { description: "Invalid stored bid response format: empty storedbidresponse.bidder", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ "prebid": { "storedbidresponse": [ { "id": "123abc"}] - } - } - } - ]}`), + }}`)}, + }, + }, expectedErrorList: []error{errors.New("request.imp[0] has ext.prebid.storedbidresponse specified, but \"id\" or/and \"bidder\" fields are missing ")}, }, { description: "Invalid stored bid response format: empty storedbidresponse.id", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ "prebid": { "storedbidresponse": [ { "bidder": "testbidder"}] - } - } - } - ]}`), + }}`)}, + }, + }, expectedErrorList: []error{errors.New("request.imp[0] has ext.prebid.storedbidresponse specified, but \"id\" or/and \"bidder\" fields are missing ")}, }, - { - description: "Invalid stored bid response: storedbidresponse.bidder not found", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { - "prebid": { - "storedbidresponse": [ - { "bidder": "testBidder123", "id": "123abc"}] - } - } - } - ]}`), - expectedErrorList: []error{errors.New("request.imp[impId: imp-id1].ext.prebid.bidder contains unknown bidder: testBidder123. Did you forget an alias in request.ext.prebid.aliases?")}, - }, { description: "Invalid stored auction response format: empty stored Auction Response Id in second imp", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ "prebid": { "storedauctionresponse": { "id":"123" } - } - } - }, - { - "id": "imp-id2", - "ext": { + }}`)}, + {ID: "imp-id2", + Ext: json.RawMessage(`{ "prebid": { - "storedauctionresponse": { + "storedauctionresponse": { "id":"" } - } - } - } - ]}`), + }}`)}, + }, + }, expectedErrorList: []error{errors.New("request.imp[1] has ext.prebid.storedauctionresponse specified, but \"id\" field is missing ")}, }, { description: "Invalid stored bid response format: empty stored bid Response Id in second imp", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ "prebid": { - "storedbidresponse": [ + "storedbidresponse": [ {"bidder":"testBidder", "id": "123abc"} ] - } - } - }, - { - "id": "imp-id2", - "ext": { + }}`)}, + {ID: "imp-id2", + Ext: json.RawMessage(`{ "prebid": { - "storedbidresponse": [ + "storedbidresponse": [ {"bidder":"testBidder", "id": ""} ] - } - } - } - ]}`), + }}`)}, + }, + }, expectedErrorList: []error{errors.New("request.imp[1] has ext.prebid.storedbidresponse specified, but \"id\" or/and \"bidder\" fields are missing ")}, }, } for _, test := range testCases { - _, _, _, errorList := ProcessStoredResponses(nil, test.requestJson, nil, bidderMap) - assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description) + t.Run(test.description, func(t *testing.T) { + rw := &openrtb_ext.RequestWrapper{BidRequest: &test.request} + _, _, _, errorList := ProcessStoredResponses(nil, rw, nil) + assert.Equalf(t, test.expectedErrorList, errorList, "Error doesn't match: %s\n", test.description) + }) } } func TestProcessStoredAuctionAndBidResponses(t *testing.T) { - bidderMap := map[string]openrtb_ext.BidderName{"bidderA": "bidderA", "bidderB": "bidderB"} bidStoredResp1 := json.RawMessage(`[{"bid": [{"id": "bid_id1"],"seat": "bidderA"}]`) bidStoredResp2 := json.RawMessage(`[{"bid": [{"id": "bid_id2"],"seat": "bidderB"}]`) bidStoredResp3 := json.RawMessage(`[{"bid": [{"id": "bid_id3"],"seat": "bidderA"}]`) @@ -305,33 +278,31 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { testCases := []struct { description string - requestJson []byte + request openrtb2.BidRequest expectedStoredAuctionResponses ImpsWithBidResponses expectedStoredBidResponses ImpBidderStoredResp expectedBidderImpReplaceImpID BidderImpReplaceImpID }{ { description: "No stored responses", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { - "prebid": { - - } - } - } - ]}`), + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ + "prebid": {} + }`)}, + }, + }, expectedStoredAuctionResponses: nil, expectedStoredBidResponses: nil, expectedBidderImpReplaceImpID: nil, }, { description: "Stored auction response one imp", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ "appnexus": { "placementId": 123 }, @@ -340,9 +311,9 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { "id": "1" } } - } - } - ]}`), + }`)}, + }, + }, expectedStoredAuctionResponses: ImpsWithBidResponses{ "imp-id1": bidStoredResp1, }, @@ -351,11 +322,11 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { }, { description: "Stored bid response one imp", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { - "appnexus": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ + "bidderA": { "placementId": 123 }, "prebid": { @@ -363,9 +334,9 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { {"bidder":"bidderA", "id": "1"} ] } - } - } - ]}`), + }`)}, + }, + }, expectedStoredAuctionResponses: ImpsWithBidResponses{}, expectedStoredBidResponses: ImpBidderStoredResp{ "imp-id1": {"bidderA": bidStoredResp1}, @@ -376,11 +347,14 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { }, { description: "Stored bid responses two bidders one imp", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { - "appnexus": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ + "bidderA": { + "placementId": 123 + }, + "bidderB": { "placementId": 123 }, "prebid": { @@ -389,9 +363,9 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { {"bidder":"bidderB", "id": "2", "replaceimpid": false} ] } - } - } - ]}`), + }`)}, + }, + }, expectedStoredAuctionResponses: ImpsWithBidResponses{}, expectedStoredBidResponses: ImpBidderStoredResp{ "imp-id1": {"bidderA": bidStoredResp1, "bidderB": bidStoredResp2}, @@ -401,14 +375,120 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { "bidderB": map[string]bool{"imp-id1": false}, }, }, + { + description: "Stored bid responses two same mixed case bidders one imp", + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ + "bidderA": { + "placementId": 123 + }, + "BIDDERa": { + "placementId": 123 + }, + "prebid": { + "storedbidresponse": [ + {"bidder":"bidderA", "id": "1", "replaceimpid": true}, + {"bidder":"bidderB", "id": "2", "replaceimpid": false} + ] + } + }`)}, + }, + }, + expectedStoredAuctionResponses: ImpsWithBidResponses{}, + expectedStoredBidResponses: ImpBidderStoredResp{ + "imp-id1": {"bidderA": bidStoredResp1, "BIDDERa": bidStoredResp1}, + }, + expectedBidderImpReplaceImpID: BidderImpReplaceImpID{ + "BIDDERa": map[string]bool{"imp-id1": true}, + "bidderA": map[string]bool{"imp-id1": true}, + }, + }, + { + description: "Stored bid responses 3 same mixed case bidders in imp.ext and imp.ext.prebid.bidders one imp", + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ + "bidderA": { + "placementId": 123 + }, + "BIDDERa": { + "placementId": 123 + }, + "prebid": { + "bidder": { + "BiddeRa": { + "placementId": 12883451 + } + }, + "storedbidresponse": [ + {"bidder":"bidderA", "id": "1", "replaceimpid": true}, + {"bidder":"bidderB", "id": "2", "replaceimpid": false} + ] + } + }`)}, + }, + }, + expectedStoredAuctionResponses: ImpsWithBidResponses{}, + expectedStoredBidResponses: ImpBidderStoredResp{ + "imp-id1": {"bidderA": bidStoredResp1, "BIDDERa": bidStoredResp1, "BiddeRa": bidStoredResp1}, + }, + expectedBidderImpReplaceImpID: BidderImpReplaceImpID{ + "BIDDERa": map[string]bool{"imp-id1": true}, + "bidderA": map[string]bool{"imp-id1": true}, + "BiddeRa": map[string]bool{"imp-id1": true}, + }, + }, + { + description: "Stored bid responses 3 same mixed case bidders in imp.ext and imp.ext.prebid.bidders one imp, duplicated stored response", + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ + "bidderA": { + "placementId": 123 + }, + "BIDDERa": { + "placementId": 123 + }, + "prebid": { + "bidder": { + "BiddeRa": { + "placementId": 12883451 + } + }, + "storedbidresponse": [ + {"bidder":"bidderA", "id": "1", "replaceimpid": true}, + {"bidder":"bidderA", "id": "2", "replaceimpid": true}, + {"bidder":"bidderB", "id": "2", "replaceimpid": false} + ] + } + }`)}, + }, + }, + expectedStoredAuctionResponses: ImpsWithBidResponses{}, + expectedStoredBidResponses: ImpBidderStoredResp{ + "imp-id1": {"bidderA": bidStoredResp1, "BIDDERa": bidStoredResp1, "BiddeRa": bidStoredResp1}, + }, + expectedBidderImpReplaceImpID: BidderImpReplaceImpID{ + "BIDDERa": map[string]bool{"imp-id1": true}, + "bidderA": map[string]bool{"imp-id1": true}, + "BiddeRa": map[string]bool{"imp-id1": true}, + }, + }, { //This is not a valid scenario for real auction request, added for testing purposes description: "Stored auction and bid responses one imp", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { - "appnexus": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ + "bidderA": { + "placementId": 123 + }, + "bidderB": { "placementId": 123 }, "prebid": { @@ -420,9 +500,9 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { {"bidder":"bidderB", "id": "2"} ] } - } - } - ]}`), + }`)}, + }, + }, expectedStoredAuctionResponses: ImpsWithBidResponses{ "imp-id1": bidStoredResp1, }, @@ -436,10 +516,10 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { }, { description: "Stored auction response three imps", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ "appnexus": { "placementId": 123 }, @@ -448,11 +528,9 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { "id": "1" } } - } - }, - { - "id": "imp-id2", - "ext": { + }`)}, + {ID: "imp-id2", + Ext: json.RawMessage(`{ "appnexus": { "placementId": 123 }, @@ -461,11 +539,10 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { "id": "2" } } - } - }, + }`)}, { - "id": "imp-id3", - "ext": { + ID: "imp-id3", + Ext: json.RawMessage(`{ "appnexus": { "placementId": 123 }, @@ -474,9 +551,10 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { "id": "3" } } - } - } - ]}`), + }`), + }, + }, + }, expectedStoredAuctionResponses: ImpsWithBidResponses{ "imp-id1": bidStoredResp1, "imp-id2": bidStoredResp2, @@ -487,10 +565,10 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { }, { description: "Stored auction response three imps duplicated stored auction response", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ "appnexus": { "placementId": 123 }, @@ -499,11 +577,9 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { "id": "1" } } - } - }, - { - "id": "imp-id2", - "ext": { + }`)}, + {ID: "imp-id2", + Ext: json.RawMessage(`{ "appnexus": { "placementId": 123 }, @@ -512,11 +588,10 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { "id": "2" } } - } - }, + }`)}, { - "id": "imp-id3", - "ext": { + ID: "imp-id3", + Ext: json.RawMessage(`{ "appnexus": { "placementId": 123 }, @@ -525,9 +600,10 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { "id": "2" } } - } - } - ]}`), + }`), + }, + }, + }, expectedStoredAuctionResponses: ImpsWithBidResponses{ "imp-id1": bidStoredResp1, "imp-id2": bidStoredResp2, @@ -538,11 +614,14 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { }, { description: "Stored bid responses two bidders two imp", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { - "appnexus": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ + "bidderA": { + "placementId": 123 + }, + "bidderB": { "placementId": 123 }, "prebid": { @@ -551,12 +630,13 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { {"bidder":"bidderB", "id": "2"} ] } - } - }, - { - "id": "imp-id2", - "ext": { - "appnexus": { + }`)}, + {ID: "imp-id2", + Ext: json.RawMessage(`{ + "bidderA": { + "placementId": 123 + }, + "bidderB": { "placementId": 123 }, "prebid": { @@ -565,9 +645,9 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { {"bidder":"bidderB", "id": "2", "replaceimpid": false} ] } - } - } - ]}`), + }`)}, + }, + }, expectedStoredAuctionResponses: ImpsWithBidResponses{}, expectedStoredBidResponses: ImpBidderStoredResp{ "imp-id1": {"bidderA": bidStoredResp1, "bidderB": bidStoredResp2}, @@ -581,17 +661,19 @@ func TestProcessStoredAuctionAndBidResponses(t *testing.T) { } for _, test := range testCases { - storedAuctionResponses, storedBidResponses, bidderImpReplaceImpId, errorList := ProcessStoredResponses(nil, test.requestJson, fetcher, bidderMap) - assert.Equal(t, test.expectedStoredAuctionResponses, storedAuctionResponses, "storedAuctionResponses doesn't match: %s\n", test.description) - assert.Equalf(t, test.expectedStoredBidResponses, storedBidResponses, "storedBidResponses doesn't match: %s\n", test.description) - assert.Equal(t, test.expectedBidderImpReplaceImpID, bidderImpReplaceImpId, "bidderImpReplaceImpId doesn't match: %s\n", test.description) - assert.Nil(t, errorList, "Error should be nil") + t.Run(test.description, func(t *testing.T) { + rw := openrtb_ext.RequestWrapper{BidRequest: &test.request} + storedAuctionResponses, storedBidResponses, bidderImpReplaceImpId, errorList := ProcessStoredResponses(nil, &rw, fetcher) + assert.Equal(t, test.expectedStoredAuctionResponses, storedAuctionResponses) + assert.Equal(t, test.expectedStoredBidResponses, storedBidResponses) + assert.Equal(t, test.expectedBidderImpReplaceImpID, bidderImpReplaceImpId) + assert.Nil(t, errorList, "Error should be nil") + }) } } func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { - bidderMap := map[string]openrtb_ext.BidderName{"bidderA": "bidderA", "bidderB": "bidderB"} bidStoredResp1 := json.RawMessage(`[{"bid": [{"id": "bid_id1"],"seat": "bidderA"}]`) bidStoredResp2 := json.RawMessage(`[{"bid": [{"id": "bid_id2"],"seat": "bidderB"}]`) mockStoredResponses := map[string]json.RawMessage{ @@ -604,16 +686,16 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { testCases := []struct { description string - requestJson []byte + request openrtb2.BidRequest expectedErrors []error }{ { description: "Stored bid response with nil data, one bidder one imp", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { - "appnexus": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ + "bidderB": { "placementId": 123 }, "prebid": { @@ -621,20 +703,20 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { {"bidder":"bidderB", "id": "3"} ] } - } - } - ]}`), + }`)}, + }, + }, expectedErrors: []error{ errors.New("failed to fetch stored bid response for impId = imp-id1, bidder = bidderB and storedBidResponse id = 3"), }, }, { description: "Stored bid response with nil data, one bidder, two imps, one with correct stored response", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { - "appnexus": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ + "bidderB": { "placementId": 123 }, "prebid": { @@ -642,12 +724,10 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { {"bidder":"bidderB", "id": "1"} ] } - } - }, - { - "id": "imp-id2", - "ext": { - "appnexus": { + }`)}, + {ID: "imp-id2", + Ext: json.RawMessage(`{ + "bidderB": { "placementId": 123 }, "prebid": { @@ -655,20 +735,20 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { {"bidder":"bidderB", "id": "3"} ] } - } - } - ]}`), + }`)}, + }, + }, expectedErrors: []error{ errors.New("failed to fetch stored bid response for impId = imp-id2, bidder = bidderB and storedBidResponse id = 3"), }, }, { description: "Stored bid response with nil data, one bidder, two imps, both with correct stored response", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { - "appnexus": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ + "bidderB": { "placementId": 123 }, "prebid": { @@ -676,12 +756,10 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { {"bidder":"bidderB", "id": "4"} ] } - } - }, - { - "id": "imp-id2", - "ext": { - "appnexus": { + }`)}, + {ID: "imp-id2", + Ext: json.RawMessage(`{ + "bidderB": { "placementId": 123 }, "prebid": { @@ -689,9 +767,9 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { {"bidder":"bidderB", "id": "3"} ] } - } - } - ]}`), + }`)}, + }, + }, expectedErrors: []error{ errors.New("failed to fetch stored bid response for impId = imp-id1, bidder = bidderB and storedBidResponse id = 4"), errors.New("failed to fetch stored bid response for impId = imp-id2, bidder = bidderB and storedBidResponse id = 3"), @@ -699,10 +777,10 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { }, { description: "Stored auction response with nil data and one imp", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ "appnexus": { "placementId": 123 }, @@ -711,19 +789,19 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { "id": "4" } } - } - } - ]}`), + }`)}, + }, + }, expectedErrors: []error{ errors.New("failed to fetch stored auction response for impId = imp-id1 and storedAuctionResponse id = 4"), }, }, { description: "Stored auction response with nil data, and two imps with nil responses", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ "appnexus": { "placementId": 123 }, @@ -732,11 +810,9 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { "id": "4" } } - } - }, - { - "id": "imp-id2", - "ext": { + }`)}, + {ID: "imp-id2", + Ext: json.RawMessage(`{ "appnexus": { "placementId": 123 }, @@ -745,9 +821,9 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { "id": "3" } } - } - } - ]}`), + }`)}, + }, + }, expectedErrors: []error{ errors.New("failed to fetch stored auction response for impId = imp-id1 and storedAuctionResponse id = 4"), errors.New("failed to fetch stored auction response for impId = imp-id2 and storedAuctionResponse id = 3"), @@ -755,10 +831,10 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { }, { description: "Stored auction response with nil data, two imps, one with nil responses", - requestJson: []byte(`{"imp": [ - { - "id": "imp-id1", - "ext": { + request: openrtb2.BidRequest{ + Imp: []openrtb2.Imp{ + {ID: "imp-id1", + Ext: json.RawMessage(`{ "appnexus": { "placementId": 123 }, @@ -767,11 +843,9 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { "id": "2" } } - } - }, - { - "id": "imp-id2", - "ext": { + }`)}, + {ID: "imp-id2", + Ext: json.RawMessage(`{ "appnexus": { "placementId": 123 }, @@ -780,9 +854,9 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { "id": "3" } } - } - } - ]}`), + }`)}, + }, + }, expectedErrors: []error{ errors.New("failed to fetch stored auction response for impId = imp-id2 and storedAuctionResponse id = 3"), }, @@ -790,10 +864,13 @@ func TestProcessStoredResponsesNotFoundResponse(t *testing.T) { } for _, test := range testCases { - _, _, _, errorList := ProcessStoredResponses(nil, test.requestJson, fetcher, bidderMap) - for _, err := range test.expectedErrors { - assert.Contains(t, errorList, err, "incorrect errors returned: %s", test.description) - } + t.Run(test.description, func(t *testing.T) { + rw := openrtb_ext.RequestWrapper{BidRequest: &test.request} + _, _, _, errorList := ProcessStoredResponses(nil, &rw, fetcher) + for _, err := range test.expectedErrors { + assert.Contains(t, errorList, err) + } + }) } } @@ -847,8 +924,10 @@ func TestFlipMap(t *testing.T) { } for _, test := range testCases { - actualResult := flipMap(test.inImpBidderReplaceImpID) - assert.Equal(t, test.outBidderImpReplaceImpID, actualResult, "Incorrect flipped map for test case %s\n", test.description) + t.Run(test.description, func(t *testing.T) { + actualResult := flipMap(test.inImpBidderReplaceImpID) + assert.Equal(t, test.outBidderImpReplaceImpID, actualResult) + }) } } diff --git a/usersync/bidderfilter.go b/usersync/bidderfilter.go index 2d7d16ffe2b..2e4df476f26 100644 --- a/usersync/bidderfilter.go +++ b/usersync/bidderfilter.go @@ -1,5 +1,9 @@ package usersync +import ( + "strings" +) + // BidderFilter determines if a bidder has permission to perform a user sync activity. type BidderFilter interface { // Allowed returns true if the filter determines the bidder has permission and false if either @@ -40,7 +44,7 @@ func (f SpecificBidderFilter) Allowed(bidder string) bool { func NewSpecificBidderFilter(bidders []string, mode BidderFilterMode) BidderFilter { biddersLookup := make(map[string]struct{}, len(bidders)) for _, bidder := range bidders { - biddersLookup[bidder] = struct{}{} + biddersLookup[strings.ToLower(bidder)] = struct{}{} } return SpecificBidderFilter{biddersLookup: biddersLookup, mode: mode} diff --git a/usersync/bidderfilter_test.go b/usersync/bidderfilter_test.go index ddc757339a2..009c051bacc 100644 --- a/usersync/bidderfilter_test.go +++ b/usersync/bidderfilter_test.go @@ -1,6 +1,7 @@ package usersync import ( + "strings" "testing" "github.com/stretchr/testify/assert" @@ -69,6 +70,12 @@ func TestSpecificBidderFilter(t *testing.T) { mode: BidderFilterMode(-1), expected: false, }, + { + description: "Case Insensitive Include - One", + bidders: []string{strings.ToUpper(bidder)}, + mode: BidderFilterModeInclude, + expected: true, + }, } for _, test := range testCases { diff --git a/usersync/chooser.go b/usersync/chooser.go index 4f808950fd0..d8bf731f693 100644 --- a/usersync/chooser.go +++ b/usersync/chooser.go @@ -1,5 +1,12 @@ package usersync +import ( + "strings" + + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + // Chooser determines which syncers are eligible for a given request. type Chooser interface { // Choose considers bidders to sync, filters the bidders, and returns the result of the @@ -8,16 +15,20 @@ type Chooser interface { } // NewChooser returns a new instance of the standard chooser implementation. -func NewChooser(bidderSyncerLookup map[string]Syncer) Chooser { +func NewChooser(bidderSyncerLookup map[string]Syncer, biddersKnown map[string]struct{}, bidderInfo map[string]config.BidderInfo) Chooser { bidders := make([]string, 0, len(bidderSyncerLookup)) + for k := range bidderSyncerLookup { bidders = append(bidders, k) } return standardChooser{ - bidderSyncerLookup: bidderSyncerLookup, - biddersAvailable: bidders, - bidderChooser: standardBidderChooser{shuffler: randomShuffler{}}, + bidderSyncerLookup: bidderSyncerLookup, + biddersAvailable: bidders, + bidderChooser: standardBidderChooser{shuffler: randomShuffler{}}, + normalizeValidBidderName: openrtb_ext.NormalizeBidderName, + biddersKnown: biddersKnown, + bidderInfo: bidderInfo, } } @@ -28,6 +39,8 @@ type Request struct { Limit int Privacy Privacy SyncTypeFilter SyncTypeFilter + GPPSID string + Debug bool } // Cooperative specifies the settings for cooperative syncing for a given request, where bidders @@ -67,13 +80,6 @@ const ( // StatusBlockedByUserOptOut specifies a user's cookie explicitly signals an opt-out. StatusBlockedByUserOptOut - // StatusBlockedByGDPR specifies a user's GDPR TCF consent explicitly forbids host cookies - // or specific bidder syncing. - StatusBlockedByGDPR - - // StatusBlockedByCCPA specifies a user's CCPA consent explicitly forbids bidder syncing. - StatusBlockedByCCPA - // StatusAlreadySynced specifies a user's cookie has an existing non-expired sync for a specific bidder. StatusAlreadySynced @@ -88,11 +94,21 @@ const ( // StatusBlockedByPrivacy specifies a bidder sync url is not allowed by privacy activities StatusBlockedByPrivacy + + // StatusBlockedByRegulationScope specifies the bidder chose to not sync given GDPR being in scope or because of a GPPSID + StatusBlockedByRegulationScope + + // StatusUnconfiguredBidder refers to a bidder who hasn't been configured to have a syncer key, but is known by Prebid Server + StatusUnconfiguredBidder + + // StatusBlockedByDisabledUsersync refers to a bidder who won't be synced because it's been disabled in its config by the host + StatusBlockedByDisabledUsersync ) // Privacy determines which privacy policies will be enforced for a user sync request. type Privacy interface { GDPRAllowsHostCookie() bool + GDPRInScope() bool GDPRAllowsBidderSync(bidder string) bool CCPAAllowsBidderSync(bidder string) bool ActivityAllowsUserSync(bidder string) bool @@ -100,9 +116,12 @@ type Privacy interface { // standardChooser implements the user syncer algorithm per official Prebid specification. type standardChooser struct { - bidderSyncerLookup map[string]Syncer - biddersAvailable []string - bidderChooser bidderChooser + bidderSyncerLookup map[string]Syncer + biddersAvailable []string + bidderChooser bidderChooser + normalizeValidBidderName func(name string) (openrtb_ext.BidderName, bool) + biddersKnown map[string]struct{} + bidderInfo map[string]config.BidderInfo } // Choose randomly selects user syncers which are permitted by the user's privacy settings and @@ -113,10 +132,11 @@ func (c standardChooser) Choose(request Request, cookie *Cookie) Result { } if !request.Privacy.GDPRAllowsHostCookie() { - return Result{Status: StatusBlockedByGDPR} + return Result{Status: StatusBlockedByPrivacy} } syncersSeen := make(map[string]struct{}) + biddersSeen := make(map[string]struct{}) limitDisabled := request.Limit <= 0 biddersEvaluated := make([]BidderEvaluation, 0) @@ -124,34 +144,43 @@ func (c standardChooser) Choose(request Request, cookie *Cookie) Result { bidders := c.bidderChooser.choose(request.Bidders, c.biddersAvailable, request.Cooperative) for i := 0; i < len(bidders) && (limitDisabled || len(syncersChosen) < request.Limit); i++ { - syncer, evaluation := c.evaluate(bidders[i], syncersSeen, request.SyncTypeFilter, request.Privacy, cookie) + if _, ok := biddersSeen[bidders[i]]; ok { + continue + } + syncer, evaluation := c.evaluate(bidders[i], syncersSeen, request.SyncTypeFilter, request.Privacy, cookie, request.GPPSID) biddersEvaluated = append(biddersEvaluated, evaluation) if evaluation.Status == StatusOK { syncersChosen = append(syncersChosen, SyncerChoice{Bidder: bidders[i], Syncer: syncer}) } + biddersSeen[bidders[i]] = struct{}{} } return Result{Status: StatusOK, BiddersEvaluated: biddersEvaluated, SyncersChosen: syncersChosen} } -func (c standardChooser) evaluate(bidder string, syncersSeen map[string]struct{}, syncTypeFilter SyncTypeFilter, privacy Privacy, cookie *Cookie) (Syncer, BidderEvaluation) { - bidderName := bidder - if bidderName == "indexExchange" { - bidderName = "ix" - } - syncer, exists := c.bidderSyncerLookup[bidderName] +func (c standardChooser) evaluate(bidder string, syncersSeen map[string]struct{}, syncTypeFilter SyncTypeFilter, privacy Privacy, cookie *Cookie, GPPSID string) (Syncer, BidderEvaluation) { + bidderNormalized, exists := c.normalizeValidBidderName(bidder) if !exists { return nil, BidderEvaluation{Status: StatusUnknownBidder, Bidder: bidder} } + syncer, exists := c.bidderSyncerLookup[bidderNormalized.String()] + if !exists { + if _, ok := c.biddersKnown[bidder]; !ok { + return nil, BidderEvaluation{Status: StatusUnknownBidder, Bidder: bidder} + } else { + return nil, BidderEvaluation{Status: StatusUnconfiguredBidder, Bidder: bidder} + } + } + _, seen := syncersSeen[syncer.Key()] if seen { return nil, BidderEvaluation{Status: StatusDuplicate, Bidder: bidder, SyncerKey: syncer.Key()} } syncersSeen[syncer.Key()] = struct{}{} - if !syncer.SupportsType(syncTypeFilter.ForBidder(bidder)) { + if !syncer.SupportsType(syncTypeFilter.ForBidder(strings.ToLower(bidder))) { return nil, BidderEvaluation{Status: StatusTypeNotSupported, Bidder: bidder, SyncerKey: syncer.Key()} } @@ -164,12 +193,24 @@ func (c standardChooser) evaluate(bidder string, syncersSeen map[string]struct{} return nil, BidderEvaluation{Status: StatusBlockedByPrivacy, Bidder: bidder, SyncerKey: syncer.Key()} } - if !privacy.GDPRAllowsBidderSync(bidder) { - return nil, BidderEvaluation{Status: StatusBlockedByGDPR, Bidder: bidder, SyncerKey: syncer.Key()} + if !privacy.GDPRAllowsBidderSync(bidderNormalized.String()) { + return nil, BidderEvaluation{Status: StatusBlockedByPrivacy, Bidder: bidder, SyncerKey: syncer.Key()} + } + + if c.bidderInfo[bidder].Syncer != nil && c.bidderInfo[bidder].Syncer.Enabled != nil && !*c.bidderInfo[bidder].Syncer.Enabled { + return nil, BidderEvaluation{Status: StatusBlockedByDisabledUsersync, Bidder: bidder, SyncerKey: syncer.Key()} } - if !privacy.CCPAAllowsBidderSync(bidder) { - return nil, BidderEvaluation{Status: StatusBlockedByCCPA, Bidder: bidder, SyncerKey: syncer.Key()} + if privacy.GDPRInScope() && c.bidderInfo[bidder].Syncer != nil && c.bidderInfo[bidder].Syncer.SkipWhen != nil && c.bidderInfo[bidder].Syncer.SkipWhen.GDPR { + return nil, BidderEvaluation{Status: StatusBlockedByRegulationScope, Bidder: bidder, SyncerKey: syncer.Key()} + } + + if c.bidderInfo[bidder].Syncer != nil && c.bidderInfo[bidder].Syncer.SkipWhen != nil { + for _, gppSID := range c.bidderInfo[bidder].Syncer.SkipWhen.GPPSID { + if gppSID == GPPSID { + return nil, BidderEvaluation{Status: StatusBlockedByRegulationScope, Bidder: bidder, SyncerKey: syncer.Key()} + } + } } return syncer, BidderEvaluation{Status: StatusOK, Bidder: bidder, SyncerKey: syncer.Key()} diff --git a/usersync/chooser_test.go b/usersync/chooser_test.go index 4f38002c988..f48dbeff9f1 100644 --- a/usersync/chooser_test.go +++ b/usersync/chooser_test.go @@ -4,15 +4,20 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/macros" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/ptrutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" + + "github.com/prebid/prebid-server/v2/macros" ) func TestNewChooser(t *testing.T) { testCases := []struct { description string bidderSyncerLookup map[string]Syncer + bidderInfo map[string]config.BidderInfo expectedBiddersAvailable []string }{ { @@ -38,7 +43,7 @@ func TestNewChooser(t *testing.T) { } for _, test := range testCases { - chooser, _ := NewChooser(test.bidderSyncerLookup).(standardChooser) + chooser, _ := NewChooser(test.bidderSyncerLookup, make(map[string]struct{}), test.bidderInfo).(standardChooser) assert.ElementsMatch(t, test.expectedBiddersAvailable, chooser.biddersAvailable, test.description) } } @@ -47,30 +52,45 @@ func TestChooserChoose(t *testing.T) { fakeSyncerA := fakeSyncer{key: "keyA", supportsIFrame: true} fakeSyncerB := fakeSyncer{key: "keyB", supportsIFrame: true} fakeSyncerC := fakeSyncer{key: "keyC", supportsIFrame: false} - bidderSyncerLookup := map[string]Syncer{"a": fakeSyncerA, "b": fakeSyncerB, "c": fakeSyncerC} + + duplicateSyncer := fakeSyncer{key: "syncerForDuplicateTest", supportsIFrame: true} + bidderSyncerLookup := map[string]Syncer{"a": fakeSyncerA, "b": fakeSyncerB, "c": fakeSyncerC, "appnexus": fakeSyncerA, "d": duplicateSyncer, "e": duplicateSyncer} + biddersKnown := map[string]struct{}{"a": {}, "b": {}, "c": {}} + + normalizedBidderNamesLookup := func(name string) (openrtb_ext.BidderName, bool) { + return openrtb_ext.BidderName(name), true + } + syncerChoiceA := SyncerChoice{Bidder: "a", Syncer: fakeSyncerA} syncerChoiceB := SyncerChoice{Bidder: "b", Syncer: fakeSyncerB} + syncTypeFilter := SyncTypeFilter{ IFrame: NewUniformBidderFilter(BidderFilterModeInclude), - Redirect: NewUniformBidderFilter(BidderFilterModeExclude)} + Redirect: NewUniformBidderFilter(BidderFilterModeExclude), + } cooperativeConfig := Cooperative{Enabled: true} + usersyncDisabled := ptrutil.ToPtr(false) + testCases := []struct { description string givenRequest Request givenChosenBidders []string givenCookie Cookie + givenBidderInfo map[string]config.BidderInfo + bidderNamesLookup func(name string) (openrtb_ext.BidderName, bool) expected Result }{ { description: "Cookie Opt Out", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{"a"}, givenCookie: Cookie{optOut: true}, + bidderNamesLookup: normalizedBidderNamesLookup, expected: Result{ Status: StatusBlockedByUserOptOut, BiddersEvaluated: nil, @@ -80,13 +100,14 @@ func TestChooserChoose(t *testing.T) { { description: "GDPR Host Cookie Not Allowed", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: false, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Privacy: &fakePrivacy{gdprAllowsHostCookie: false, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{"a"}, givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, expected: Result{ - Status: StatusBlockedByGDPR, + Status: StatusBlockedByPrivacy, BiddersEvaluated: nil, SyncersChosen: nil, }, @@ -94,11 +115,12 @@ func TestChooserChoose(t *testing.T) { { description: "No Bidders", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{}, givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, expected: Result{ Status: StatusOK, BiddersEvaluated: []BidderEvaluation{}, @@ -108,11 +130,12 @@ func TestChooserChoose(t *testing.T) { { description: "One Bidder - Sync", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{"a"}, givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, expected: Result{ Status: StatusOK, BiddersEvaluated: []BidderEvaluation{{Bidder: "a", SyncerKey: "keyA", Status: StatusOK}}, @@ -122,25 +145,42 @@ func TestChooserChoose(t *testing.T) { { description: "One Bidder - No Sync", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{"c"}, givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, expected: Result{ Status: StatusOK, BiddersEvaluated: []BidderEvaluation{{Bidder: "c", SyncerKey: "keyC", Status: StatusTypeNotSupported}}, SyncersChosen: []SyncerChoice{}, }, }, + { + description: "One Bidder - No Sync - Unknown", + givenRequest: Request{ + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Limit: 0, + }, + givenChosenBidders: []string{"unknown"}, + givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, + expected: Result{ + Status: StatusOK, + BiddersEvaluated: []BidderEvaluation{{Bidder: "unknown", Status: StatusUnknownBidder}}, + SyncersChosen: []SyncerChoice{}, + }, + }, { description: "Many Bidders - All Sync - Limit Disabled With 0", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{"a", "b"}, givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, expected: Result{ Status: StatusOK, BiddersEvaluated: []BidderEvaluation{{Bidder: "a", SyncerKey: "keyA", Status: StatusOK}, {Bidder: "b", SyncerKey: "keyB", Status: StatusOK}}, @@ -150,11 +190,12 @@ func TestChooserChoose(t *testing.T) { { description: "Many Bidders - All Sync - Limit Disabled With Negative Value", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: -1, }, givenChosenBidders: []string{"a", "b"}, givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, expected: Result{ Status: StatusOK, BiddersEvaluated: []BidderEvaluation{{Bidder: "a", SyncerKey: "keyA", Status: StatusOK}, {Bidder: "b", SyncerKey: "keyB", Status: StatusOK}}, @@ -164,11 +205,12 @@ func TestChooserChoose(t *testing.T) { { description: "Many Bidders - Limited Sync", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 1, }, givenChosenBidders: []string{"a", "b"}, givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, expected: Result{ Status: StatusOK, BiddersEvaluated: []BidderEvaluation{{Bidder: "a", SyncerKey: "keyA", Status: StatusOK}}, @@ -178,11 +220,12 @@ func TestChooserChoose(t *testing.T) { { description: "Many Bidders - Limited Sync - Disqualified Syncers Don't Count Towards Limit", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 1, }, givenChosenBidders: []string{"c", "a", "b"}, givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, expected: Result{ Status: StatusOK, BiddersEvaluated: []BidderEvaluation{{Bidder: "c", SyncerKey: "keyC", Status: StatusTypeNotSupported}, {Bidder: "a", SyncerKey: "keyA", Status: StatusOK}}, @@ -192,17 +235,189 @@ func TestChooserChoose(t *testing.T) { { description: "Many Bidders - Some Sync, Some Don't", givenRequest: Request{ - Privacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, Limit: 0, }, givenChosenBidders: []string{"a", "c"}, givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, expected: Result{ Status: StatusOK, BiddersEvaluated: []BidderEvaluation{{Bidder: "a", SyncerKey: "keyA", Status: StatusOK}, {Bidder: "c", SyncerKey: "keyC", Status: StatusTypeNotSupported}}, SyncersChosen: []SyncerChoice{syncerChoiceA}, }, }, + { + description: "Chosen bidders have duplicate syncer keys, the one that comes first should be labled OK", + givenRequest: Request{ + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Limit: 0, + }, + givenChosenBidders: []string{"d", "e"}, + givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, + expected: Result{ + Status: StatusOK, + BiddersEvaluated: []BidderEvaluation{ + {Bidder: "d", SyncerKey: "syncerForDuplicateTest", Status: StatusOK}, + {Bidder: "e", SyncerKey: "syncerForDuplicateTest", Status: StatusDuplicate}, + }, + SyncersChosen: []SyncerChoice{{Bidder: "d", Syncer: duplicateSyncer}}, + }, + }, + { + description: "Chosen bidders have duplicate syncer keys, the one that comes first should be labled OK (reverse)", + givenRequest: Request{ + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Limit: 0, + }, + givenChosenBidders: []string{"e", "d"}, + givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, + expected: Result{ + Status: StatusOK, + BiddersEvaluated: []BidderEvaluation{ + {Bidder: "e", SyncerKey: "syncerForDuplicateTest", Status: StatusOK}, + {Bidder: "d", SyncerKey: "syncerForDuplicateTest", Status: StatusDuplicate}, + }, + SyncersChosen: []SyncerChoice{{Bidder: "e", Syncer: duplicateSyncer}}, + }, + }, + { + description: "Same bidder name, no duplicate warning", + givenRequest: Request{ + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Limit: 0, + }, + givenChosenBidders: []string{"a", "a"}, + givenCookie: Cookie{}, + bidderNamesLookup: normalizedBidderNamesLookup, + expected: Result{ + Status: StatusOK, + BiddersEvaluated: []BidderEvaluation{ + {Bidder: "a", SyncerKey: fakeSyncerA.key, Status: StatusOK}, + }, + SyncersChosen: []SyncerChoice{{Bidder: "a", Syncer: fakeSyncerA}}, + }, + }, + { + description: "Unknown Bidder", + givenRequest: Request{ + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Limit: 0, + }, + givenChosenBidders: []string{"a"}, + givenCookie: Cookie{}, + bidderNamesLookup: func(name string) (openrtb_ext.BidderName, bool) { + return openrtb_ext.BidderName(name), false + }, + expected: Result{ + Status: StatusOK, + BiddersEvaluated: []BidderEvaluation{{Bidder: "a", Status: StatusUnknownBidder}}, + SyncersChosen: []SyncerChoice{}, + }, + }, + { + description: "Case insensitive bidder name", + givenRequest: Request{ + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Limit: 0, + }, + givenChosenBidders: []string{"AppNexus"}, + givenCookie: Cookie{}, + bidderNamesLookup: openrtb_ext.NormalizeBidderName, + expected: Result{ + Status: StatusOK, + BiddersEvaluated: []BidderEvaluation{{Bidder: "AppNexus", SyncerKey: "keyA", Status: StatusOK}}, + SyncersChosen: []SyncerChoice{{Bidder: "AppNexus", Syncer: fakeSyncerA}}, + }, + }, + { + description: "Duplicate bidder name", + givenRequest: Request{ + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Limit: 0, + }, + givenChosenBidders: []string{"AppNexus", "appNexus"}, + givenCookie: Cookie{}, + bidderNamesLookup: openrtb_ext.NormalizeBidderName, + expected: Result{ + Status: StatusOK, + BiddersEvaluated: []BidderEvaluation{{Bidder: "AppNexus", SyncerKey: "keyA", Status: StatusOK}, {Bidder: "appNexus", SyncerKey: "keyA", Status: StatusDuplicate}}, + SyncersChosen: []SyncerChoice{{Bidder: "AppNexus", Syncer: fakeSyncerA}}, + }, + }, + { + description: "Disabled Usersync", + givenRequest: Request{ + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Limit: 0, + }, + givenChosenBidders: []string{"a"}, + givenCookie: Cookie{}, + givenBidderInfo: map[string]config.BidderInfo{ + "a": { + Syncer: &config.Syncer{ + Enabled: usersyncDisabled, + }, + }, + }, + bidderNamesLookup: normalizedBidderNamesLookup, + expected: Result{ + Status: StatusOK, + BiddersEvaluated: []BidderEvaluation{{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByDisabledUsersync}}, + SyncersChosen: []SyncerChoice{}, + }, + }, + { + description: "Regulation Scope GDPR", + givenRequest: Request{ + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true, gdprInScope: true}, + Limit: 0, + }, + givenChosenBidders: []string{"a"}, + givenCookie: Cookie{}, + givenBidderInfo: map[string]config.BidderInfo{ + "a": { + Syncer: &config.Syncer{ + SkipWhen: &config.SkipWhen{ + GDPR: true, + }, + }, + }, + }, + bidderNamesLookup: normalizedBidderNamesLookup, + expected: Result{ + Status: StatusOK, + BiddersEvaluated: []BidderEvaluation{{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByRegulationScope}}, + SyncersChosen: []SyncerChoice{}, + }, + }, + { + description: "Regulation Scope GPP", + givenRequest: Request{ + Privacy: &fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + Limit: 0, + GPPSID: "2", + }, + givenChosenBidders: []string{"a"}, + givenCookie: Cookie{}, + givenBidderInfo: map[string]config.BidderInfo{ + "a": { + Syncer: &config.Syncer{ + SkipWhen: &config.SkipWhen{ + GPPSID: []string{"2", "3"}, + }, + }, + }, + }, + bidderNamesLookup: normalizedBidderNamesLookup, + expected: Result{ + Status: StatusOK, + BiddersEvaluated: []BidderEvaluation{{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByRegulationScope}}, + SyncersChosen: []SyncerChoice{}, + }, + }, } bidders := []string{"anyRequested"} @@ -218,10 +433,17 @@ func TestChooserChoose(t *testing.T) { On("choose", test.givenRequest.Bidders, biddersAvailable, cooperativeConfig). Return(test.givenChosenBidders) + if test.givenBidderInfo == nil { + test.givenBidderInfo = map[string]config.BidderInfo{} + } + chooser := standardChooser{ - bidderSyncerLookup: bidderSyncerLookup, - biddersAvailable: biddersAvailable, - bidderChooser: mockBidderChooser, + bidderSyncerLookup: bidderSyncerLookup, + biddersAvailable: biddersAvailable, + bidderChooser: mockBidderChooser, + normalizeValidBidderName: test.bidderNamesLookup, + biddersKnown: biddersKnown, + bidderInfo: test.givenBidderInfo, } result := chooser.Choose(test.givenRequest, &test.givenCookie) @@ -232,113 +454,256 @@ func TestChooserChoose(t *testing.T) { func TestChooserEvaluate(t *testing.T) { fakeSyncerA := fakeSyncer{key: "keyA", supportsIFrame: true} fakeSyncerB := fakeSyncer{key: "keyB", supportsIFrame: false} - bidderSyncerLookup := map[string]Syncer{"a": fakeSyncerA, "b": fakeSyncerB} + + biddersKnown := map[string]struct{}{"a": {}, "b": {}, "unconfigured": {}} + bidderSyncerLookup := map[string]Syncer{"a": fakeSyncerA, "b": fakeSyncerB, "appnexus": fakeSyncerA, "seedingAlliance": fakeSyncerA} + syncTypeFilter := SyncTypeFilter{ IFrame: NewUniformBidderFilter(BidderFilterModeInclude), - Redirect: NewUniformBidderFilter(BidderFilterModeExclude)} - + Redirect: NewUniformBidderFilter(BidderFilterModeExclude), + } + normalizedBidderNamesLookup := func(name string) (openrtb_ext.BidderName, bool) { + return openrtb_ext.BidderName(name), true + } cookieNeedsSync := Cookie{} cookieAlreadyHasSyncForA := Cookie{uids: map[string]UIDEntry{"keyA": {Expires: time.Now().Add(time.Duration(24) * time.Hour)}}} cookieAlreadyHasSyncForB := Cookie{uids: map[string]UIDEntry{"keyB": {Expires: time.Now().Add(time.Duration(24) * time.Hour)}}} + usersyncDisabled := ptrutil.ToPtr(false) + testCases := []struct { - description string - givenBidder string - givenSyncersSeen map[string]struct{} - givenPrivacy Privacy - givenCookie Cookie - expectedSyncer Syncer - expectedEvaluation BidderEvaluation + description string + givenBidder string + normalisedBidderName string + givenSyncersSeen map[string]struct{} + givenPrivacy fakePrivacy + givenCookie Cookie + givenGPPSID string + givenBidderInfo map[string]config.BidderInfo + givenSyncTypeFilter SyncTypeFilter + normalizedBidderNamesLookup func(name string) (openrtb_ext.BidderName, bool) + expectedSyncer Syncer + expectedEvaluation BidderEvaluation }{ { - description: "Valid", - givenBidder: "a", - givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, - givenCookie: cookieNeedsSync, - expectedSyncer: fakeSyncerA, - expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusOK}, - }, - { - description: "Unknown Bidder", - givenBidder: "unknown", - givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, - givenCookie: cookieNeedsSync, - expectedSyncer: nil, - expectedEvaluation: BidderEvaluation{Bidder: "unknown", Status: StatusUnknownBidder}, - }, - { - description: "Duplicate Syncer", - givenBidder: "a", - givenSyncersSeen: map[string]struct{}{"keyA": {}}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, - givenCookie: cookieNeedsSync, - expectedSyncer: nil, - expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusDuplicate}, - }, - { - description: "Incompatible Kind", - givenBidder: "b", - givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, - givenCookie: cookieNeedsSync, - expectedSyncer: nil, - expectedEvaluation: BidderEvaluation{Bidder: "b", SyncerKey: "keyB", Status: StatusTypeNotSupported}, - }, - { - description: "Already Synced", - givenBidder: "a", - givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, - givenCookie: cookieAlreadyHasSyncForA, - expectedSyncer: nil, - expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusAlreadySynced}, - }, - { - description: "Different Bidder Already Synced", - givenBidder: "a", - givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, - givenCookie: cookieAlreadyHasSyncForB, - expectedSyncer: fakeSyncerA, - expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusOK}, - }, - { - description: "Blocked By GDPR", - givenBidder: "a", - givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: false, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, - givenCookie: cookieNeedsSync, - expectedSyncer: nil, - expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByGDPR}, - }, - { - description: "Blocked By CCPA", - givenBidder: "a", - givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: false, activityAllowUserSync: true}, - givenCookie: cookieNeedsSync, - expectedSyncer: nil, - expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByCCPA}, - }, - { - description: "Blocked By activity control", - givenBidder: "a", - givenSyncersSeen: map[string]struct{}{}, - givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: false}, - givenCookie: cookieNeedsSync, - expectedSyncer: nil, - expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByPrivacy}, + description: "Valid", + givenBidder: "a", + normalisedBidderName: "a", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieNeedsSync, + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: normalizedBidderNamesLookup, + expectedSyncer: fakeSyncerA, + expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusOK}, + }, + { + description: "Unknown Bidder", + givenBidder: "unknown", + normalisedBidderName: "", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieNeedsSync, + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: normalizedBidderNamesLookup, + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "unknown", Status: StatusUnknownBidder}, + }, + { + description: "Duplicate Syncer", + givenBidder: "a", + normalisedBidderName: "", + givenSyncersSeen: map[string]struct{}{"keyA": {}}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieNeedsSync, + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: normalizedBidderNamesLookup, + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusDuplicate}, + }, + { + description: "Incompatible Kind", + givenBidder: "b", + normalisedBidderName: "", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieNeedsSync, + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: normalizedBidderNamesLookup, + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "b", SyncerKey: "keyB", Status: StatusTypeNotSupported}, + }, + { + description: "Already Synced", + givenBidder: "a", + normalisedBidderName: "", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieAlreadyHasSyncForA, + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: normalizedBidderNamesLookup, + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusAlreadySynced}, + }, + { + description: "Different Bidder Already Synced", + givenBidder: "a", + normalisedBidderName: "a", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieAlreadyHasSyncForB, + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: normalizedBidderNamesLookup, + expectedSyncer: fakeSyncerA, + expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusOK}, + }, + { + description: "Blocked By GDPR", + givenBidder: "a", + normalisedBidderName: "a", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: false, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieNeedsSync, + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: normalizedBidderNamesLookup, + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByPrivacy}, + }, + { + description: "Blocked By activity control", + givenBidder: "a", + normalisedBidderName: "", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: false}, + givenCookie: cookieNeedsSync, + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: normalizedBidderNamesLookup, + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByPrivacy}, + }, + { + description: "Case insensitive bidder name", + givenBidder: "AppNexus", + normalisedBidderName: "appnexus", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieNeedsSync, + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: openrtb_ext.NormalizeBidderName, + expectedSyncer: fakeSyncerA, + expectedEvaluation: BidderEvaluation{Bidder: "AppNexus", SyncerKey: "keyA", Status: StatusOK}, + }, + { + description: "Case insensitivity check for sync type filter", + givenBidder: "SeedingAlliance", + normalisedBidderName: "seedingAlliance", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieNeedsSync, + givenSyncTypeFilter: SyncTypeFilter{ + IFrame: NewSpecificBidderFilter([]string{"SeedingAlliance"}, BidderFilterModeInclude), + Redirect: NewSpecificBidderFilter([]string{"SeedingAlliance"}, BidderFilterModeExclude), + }, + normalizedBidderNamesLookup: openrtb_ext.NormalizeBidderName, + expectedSyncer: fakeSyncerA, + expectedEvaluation: BidderEvaluation{Bidder: "SeedingAlliance", SyncerKey: "keyA", Status: StatusOK}, + }, + { + description: "Case Insensitivity Check For Blocked By GDPR", + givenBidder: "AppNexus", + normalisedBidderName: "appnexus", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: false, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieNeedsSync, + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: openrtb_ext.NormalizeBidderName, + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "AppNexus", SyncerKey: "keyA", Status: StatusBlockedByPrivacy}, + }, + { + description: "Unconfigured Bidder", + givenBidder: "unconfigured", + normalizedBidderNamesLookup: normalizedBidderNamesLookup, + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieNeedsSync, + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "unconfigured", Status: StatusUnconfiguredBidder}, + }, + { + description: "Disabled Usersync", + givenBidder: "a", + normalisedBidderName: "a", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieNeedsSync, + givenBidderInfo: map[string]config.BidderInfo{ + "a": { + Syncer: &config.Syncer{ + Enabled: usersyncDisabled, + }, + }, + }, + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: normalizedBidderNamesLookup, + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByDisabledUsersync}, + }, + { + description: "Blocked By Regulation Scope - GDPR", + givenBidder: "a", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true, gdprInScope: true}, + givenCookie: cookieNeedsSync, + givenBidderInfo: map[string]config.BidderInfo{ + "a": { + Syncer: &config.Syncer{ + SkipWhen: &config.SkipWhen{ + GDPR: true, + }, + }, + }, + }, + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: normalizedBidderNamesLookup, + normalisedBidderName: "a", + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByRegulationScope}, + }, + { + description: "Blocked By Regulation Scope - GPP", + givenBidder: "a", + givenSyncersSeen: map[string]struct{}{}, + givenPrivacy: fakePrivacy{gdprAllowsHostCookie: true, gdprAllowsBidderSync: true, ccpaAllowsBidderSync: true, activityAllowUserSync: true}, + givenCookie: cookieNeedsSync, + givenBidderInfo: map[string]config.BidderInfo{ + "a": { + Syncer: &config.Syncer{ + SkipWhen: &config.SkipWhen{ + GPPSID: []string{"2", "3"}, + }, + }, + }, + }, + givenGPPSID: "2", + givenSyncTypeFilter: syncTypeFilter, + normalizedBidderNamesLookup: normalizedBidderNamesLookup, + normalisedBidderName: "a", + expectedSyncer: nil, + expectedEvaluation: BidderEvaluation{Bidder: "a", SyncerKey: "keyA", Status: StatusBlockedByRegulationScope}, }, } for _, test := range testCases { - chooser, _ := NewChooser(bidderSyncerLookup).(standardChooser) - sync, evaluation := chooser.evaluate(test.givenBidder, test.givenSyncersSeen, syncTypeFilter, test.givenPrivacy, &test.givenCookie) + t.Run(test.description, func(t *testing.T) { + chooser, _ := NewChooser(bidderSyncerLookup, biddersKnown, test.givenBidderInfo).(standardChooser) + chooser.normalizeValidBidderName = test.normalizedBidderNamesLookup + sync, evaluation := chooser.evaluate(test.givenBidder, test.givenSyncersSeen, test.givenSyncTypeFilter, &test.givenPrivacy, &test.givenCookie, test.givenGPPSID) - assert.Equal(t, test.expectedSyncer, sync, test.description+":syncer") - assert.Equal(t, test.expectedEvaluation, evaluation, test.description+":evaluation") + assert.Equal(t, test.normalisedBidderName, test.givenPrivacy.inputBidderName) + assert.Equal(t, test.expectedSyncer, sync, test.description+":syncer") + assert.Equal(t, test.expectedEvaluation, evaluation, test.description+":evaluation") + }) } } @@ -355,13 +720,14 @@ type fakeSyncer struct { key string supportsIFrame bool supportsRedirect bool + formatOverride string } func (s fakeSyncer) Key() string { return s.key } -func (s fakeSyncer) DefaultSyncType() SyncType { +func (s fakeSyncer) DefaultResponseFormat() SyncType { return SyncTypeIFrame } @@ -386,20 +752,28 @@ type fakePrivacy struct { gdprAllowsBidderSync bool ccpaAllowsBidderSync bool activityAllowUserSync bool + gdprInScope bool + inputBidderName string } -func (p fakePrivacy) GDPRAllowsHostCookie() bool { +func (p *fakePrivacy) GDPRAllowsHostCookie() bool { return p.gdprAllowsHostCookie } -func (p fakePrivacy) GDPRAllowsBidderSync(bidder string) bool { +func (p *fakePrivacy) GDPRAllowsBidderSync(bidder string) bool { + p.inputBidderName = bidder return p.gdprAllowsBidderSync } -func (p fakePrivacy) CCPAAllowsBidderSync(bidder string) bool { +func (p *fakePrivacy) CCPAAllowsBidderSync(bidder string) bool { + p.inputBidderName = bidder return p.ccpaAllowsBidderSync } -func (p fakePrivacy) ActivityAllowsUserSync(bidder string) bool { +func (p *fakePrivacy) ActivityAllowsUserSync(bidder string) bool { return p.activityAllowUserSync } + +func (p *fakePrivacy) GDPRInScope() bool { + return p.gdprInScope +} diff --git a/usersync/cookie.go b/usersync/cookie.go index 48ccac560fb..879a338ee64 100644 --- a/usersync/cookie.go +++ b/usersync/cookie.go @@ -1,13 +1,13 @@ package usersync import ( - "encoding/json" "errors" "net/http" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) const uidCookieName = "uids" @@ -227,7 +227,7 @@ type cookieJson struct { } func (cookie *Cookie) MarshalJSON() ([]byte, error) { - return json.Marshal(cookieJson{ + return jsonutil.Marshal(cookieJson{ UIDs: cookie.uids, OptOut: cookie.optOut, }) @@ -235,7 +235,7 @@ func (cookie *Cookie) MarshalJSON() ([]byte, error) { func (cookie *Cookie) UnmarshalJSON(b []byte) error { var cookieContract cookieJson - if err := json.Unmarshal(b, &cookieContract); err != nil { + if err := jsonutil.Unmarshal(b, &cookieContract); err != nil { return err } diff --git a/usersync/cookie_test.go b/usersync/cookie_test.go index 340069767b1..1ecfe51b5b5 100644 --- a/usersync/cookie_test.go +++ b/usersync/cookie_test.go @@ -7,8 +7,8 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" "github.com/stretchr/testify/assert" ) diff --git a/usersync/decoder.go b/usersync/decoder.go index 3ff13aa3242..a73e37fc560 100644 --- a/usersync/decoder.go +++ b/usersync/decoder.go @@ -2,12 +2,12 @@ package usersync import ( "encoding/base64" - "encoding/json" + + "github.com/prebid/prebid-server/v2/util/jsonutil" ) type Decoder interface { - // Decode takes an encoded string and decodes it into a cookie - Decode(v string) *Cookie + Decode(encodedValue string) *Cookie } type Base64Decoder struct{} @@ -19,7 +19,7 @@ func (d Base64Decoder) Decode(encodedValue string) *Cookie { } var cookie Cookie - if err = json.Unmarshal(jsonValue, &cookie); err != nil { + if err = jsonutil.UnmarshalValid(jsonValue, &cookie); err != nil { return NewCookie() } diff --git a/usersync/encoder.go b/usersync/encoder.go index eef7e2ef34f..2baf5d86524 100644 --- a/usersync/encoder.go +++ b/usersync/encoder.go @@ -2,7 +2,8 @@ package usersync import ( "encoding/base64" - "encoding/json" + + "github.com/prebid/prebid-server/v2/util/jsonutil" ) type Encoder interface { @@ -13,7 +14,7 @@ type Encoder interface { type Base64Encoder struct{} func (e Base64Encoder) Encode(c *Cookie) (string, error) { - j, err := json.Marshal(c) + j, err := jsonutil.Marshal(c) if err != nil { return "", err } diff --git a/usersync/syncer.go b/usersync/syncer.go index e561614f4a2..50985eca5be 100644 --- a/usersync/syncer.go +++ b/usersync/syncer.go @@ -9,8 +9,8 @@ import ( "text/template" validator "github.com/asaskevich/govalidator" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/macros" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/macros" ) var ( @@ -26,8 +26,8 @@ type Syncer interface { // necessarily, a one-to-one mapping with a bidder. Key() string - // DefaultSyncType is the default SyncType for this syncer. - DefaultSyncType() SyncType + // DefaultResponseFormat is the default SyncType for this syncer. + DefaultResponseFormat() SyncType // SupportsType returns true if the syncer supports at least one of the specified sync types. SupportsType(syncTypes []SyncType) bool @@ -50,13 +50,9 @@ type standardSyncer struct { iframe *template.Template redirect *template.Template supportCORS bool + formatOverride string } -const ( - setuidSyncTypeIFrame = "b" // b = blank HTML response - setuidSyncTypeRedirect = "i" // i = image response -) - // NewSyncer creates a new Syncer from the provided configuration, or return an error if macro substition // fails or an endpoint url is invalid. func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer, bidder string) (Syncer, error) { @@ -72,11 +68,12 @@ func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer, bidder st key: syncerConfig.Key, defaultSyncType: resolveDefaultSyncType(syncerConfig), supportCORS: syncerConfig.SupportCORS != nil && *syncerConfig.SupportCORS, + formatOverride: syncerConfig.FormatOverride, } if syncerConfig.IFrame != nil { var err error - syncer.iframe, err = buildTemplate(bidder, setuidSyncTypeIFrame, hostConfig, syncerConfig.ExternalURL, *syncerConfig.IFrame) + syncer.iframe, err = buildTemplate(bidder, config.SyncResponseFormatIFrame, hostConfig, syncerConfig.ExternalURL, *syncerConfig.IFrame, syncerConfig.FormatOverride) if err != nil { return nil, fmt.Errorf("iframe %v", err) } @@ -87,7 +84,7 @@ func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer, bidder st if syncerConfig.Redirect != nil { var err error - syncer.redirect, err = buildTemplate(bidder, setuidSyncTypeRedirect, hostConfig, syncerConfig.ExternalURL, *syncerConfig.Redirect) + syncer.redirect, err = buildTemplate(bidder, config.SyncResponseFormatRedirect, hostConfig, syncerConfig.ExternalURL, *syncerConfig.Redirect, syncerConfig.FormatOverride) if err != nil { return nil, fmt.Errorf("redirect %v", err) } @@ -117,12 +114,16 @@ var ( macroRegex = regexp.MustCompile(`{{\s*\..*?\s*}}`) ) -func buildTemplate(bidderName, syncTypeValue string, hostConfig config.UserSync, syncerExternalURL string, syncerEndpoint config.SyncerEndpoint) (*template.Template, error) { +func buildTemplate(bidderName, syncTypeValue string, hostConfig config.UserSync, syncerExternalURL string, syncerEndpoint config.SyncerEndpoint, formatOverride string) (*template.Template, error) { redirectTemplate := syncerEndpoint.RedirectURL if redirectTemplate == "" { redirectTemplate = hostConfig.RedirectURL } + if formatOverride != "" { + syncTypeValue = formatOverride + } + externalURL := chooseExternalURL(syncerEndpoint.ExternalURL, syncerExternalURL, hostConfig.ExternalURL) redirectURL := macroRegexSyncerKey.ReplaceAllLiteralString(redirectTemplate, bidderName) @@ -189,8 +190,15 @@ func (s standardSyncer) Key() string { return s.key } -func (s standardSyncer) DefaultSyncType() SyncType { - return s.defaultSyncType +func (s standardSyncer) DefaultResponseFormat() SyncType { + switch s.formatOverride { + case config.SyncResponseFormatIFrame: + return SyncTypeIFrame + case config.SyncResponseFormatRedirect: + return SyncTypeRedirect + default: + return s.defaultSyncType + } } func (s standardSyncer) SupportsType(syncTypes []SyncType) bool { diff --git a/usersync/syncer_test.go b/usersync/syncer_test.go index 72167addae5..c9c568e299a 100644 --- a/usersync/syncer_test.go +++ b/usersync/syncer_test.go @@ -4,8 +4,8 @@ import ( "testing" "text/template" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/macros" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/macros" "github.com/stretchr/testify/assert" ) @@ -27,6 +27,7 @@ func TestNewSyncer(t *testing.T) { givenIFrameConfig *config.SyncerEndpoint givenRedirectConfig *config.SyncerEndpoint givenExternalURL string + givenForceType string expectedError string expectedDefault SyncType expectedIFrame string @@ -322,7 +323,7 @@ func TestBuildTemplate(t *testing.T) { } for _, test := range testCases { - result, err := buildTemplate(key, syncTypeValue, hostConfig, test.givenSyncerExternalURL, test.givenSyncerEndpoint) + result, err := buildTemplate(key, syncTypeValue, hostConfig, test.givenSyncerExternalURL, test.givenSyncerEndpoint, "") if test.expectedError == "" { assert.NoError(t, err, test.description+":err") @@ -480,7 +481,36 @@ func TestSyncerKey(t *testing.T) { func TestSyncerDefaultSyncType(t *testing.T) { syncer := standardSyncer{defaultSyncType: SyncTypeRedirect} - assert.Equal(t, SyncTypeRedirect, syncer.DefaultSyncType()) + assert.Equal(t, SyncTypeRedirect, syncer.DefaultResponseFormat()) +} + +func TestSyncerDefaultResponseFormat(t *testing.T) { + testCases := []struct { + description string + givenSyncer standardSyncer + expectedSyncType SyncType + }{ + { + description: "IFrame", + givenSyncer: standardSyncer{formatOverride: config.SyncResponseFormatIFrame}, + expectedSyncType: SyncTypeIFrame, + }, + { + description: "Default with Redirect Override", + givenSyncer: standardSyncer{defaultSyncType: SyncTypeIFrame, formatOverride: config.SyncResponseFormatRedirect}, + expectedSyncType: SyncTypeRedirect, + }, + { + description: "Default with no override", + givenSyncer: standardSyncer{defaultSyncType: SyncTypeRedirect}, + expectedSyncType: SyncTypeRedirect, + }, + } + + for _, test := range testCases { + syncType := test.givenSyncer.DefaultResponseFormat() + assert.Equal(t, test.expectedSyncType, syncType, test.description) + } } func TestSyncerSupportsType(t *testing.T) { diff --git a/usersync/syncersbuilder.go b/usersync/syncersbuilder.go index 9a52a740f31..9c916b821b4 100644 --- a/usersync/syncersbuilder.go +++ b/usersync/syncersbuilder.go @@ -5,7 +5,7 @@ import ( "sort" "strings" - "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/v2/config" ) type namedSyncerConfig struct { diff --git a/usersync/syncersbuilder_test.go b/usersync/syncersbuilder_test.go index 15c53dba2a4..a8f396aa714 100644 --- a/usersync/syncersbuilder_test.go +++ b/usersync/syncersbuilder_test.go @@ -4,8 +4,8 @@ import ( "errors" "testing" - "github.com/prebid/prebid-server/config" - "github.com/prebid/prebid-server/macros" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/macros" "github.com/stretchr/testify/assert" ) diff --git a/util/httputil/httputil.go b/util/httputil/httputil.go index 28334a54b87..cabb197fa37 100644 --- a/util/httputil/httputil.go +++ b/util/httputil/httputil.go @@ -5,7 +5,7 @@ import ( "net/http" "strings" - "github.com/prebid/prebid-server/util/iputil" + "github.com/prebid/prebid-server/v2/util/iputil" ) var ( diff --git a/util/httputil/httputil_test.go b/util/httputil/httputil_test.go index 5da3b0ab735..d056db245f3 100644 --- a/util/httputil/httputil_test.go +++ b/util/httputil/httputil_test.go @@ -5,7 +5,7 @@ import ( "net/http" "testing" - "github.com/prebid/prebid-server/util/iputil" + "github.com/prebid/prebid-server/v2/util/iputil" "github.com/stretchr/testify/assert" ) diff --git a/util/jsonutil/jsonutil.go b/util/jsonutil/jsonutil.go index 3b468731cad..695ccd8a5c1 100644 --- a/util/jsonutil/jsonutil.go +++ b/util/jsonutil/jsonutil.go @@ -4,14 +4,18 @@ import ( "bytes" "encoding/json" "io" + "strings" + "unsafe" + + jsoniter "github.com/json-iterator/go" + "github.com/modern-go/reflect2" + "github.com/prebid/prebid-server/v2/errortypes" ) -var comma = []byte(",")[0] -var colon = []byte(":")[0] -var sqBracket = []byte("]")[0] -var openCurlyBracket = []byte("{")[0] -var closingCurlyBracket = []byte("}")[0] -var quote = []byte(`"`)[0] +var comma = byte(',') +var colon = byte(':') +var sqBracket = byte(']') +var closingCurlyBracket = byte('}') // Finds element in json byte array with any level of nesting func FindElement(extension []byte, elementNames ...string) (bool, int64, int64, error) { @@ -110,3 +114,137 @@ func DropElement(extension []byte, elementNames ...string) ([]byte, error) { } return extension, nil } + +// jsonConfigValidationOn attempts to maintain compatibility with the standard library which +// includes enabling validation +var jsonConfigValidationOn = jsoniter.ConfigCompatibleWithStandardLibrary + +// jsonConfigValidationOff disables validation +var jsonConfigValidationOff = jsoniter.Config{ + EscapeHTML: true, + SortMapKeys: true, + ValidateJsonRawMessage: false, +}.Froze() + +// Unmarshal unmarshals a byte slice into the specified data structure without performing +// any validation on the data. An unmarshal error is returned if a non-validation error occurs. +func Unmarshal(data []byte, v interface{}) error { + err := jsonConfigValidationOff.Unmarshal(data, v) + if err != nil { + return &errortypes.FailedToUnmarshal{ + Message: tryExtractErrorMessage(err), + } + } + return nil +} + +// UnmarshalValid validates and unmarshals a byte slice into the specified data structure +// returning an error if validation fails +func UnmarshalValid(data []byte, v interface{}) error { + if err := jsonConfigValidationOn.Unmarshal(data, v); err != nil { + return &errortypes.FailedToUnmarshal{ + Message: tryExtractErrorMessage(err), + } + } + return nil +} + +// Marshal marshals a data structure into a byte slice without performing any validation +// on the data. A marshal error is returned if a non-validation error occurs. +func Marshal(v interface{}) ([]byte, error) { + data, err := jsonConfigValidationOn.Marshal(v) + if err != nil { + return nil, &errortypes.FailedToMarshal{ + Message: err.Error(), + } + } + return data, nil +} + +// tryExtractErrorMessage attempts to extract a sane error message from the json-iter package. The errors +// returned from that library are not types and include a lot of extra information we don't want to respond with. +// This is hacky, but it's the only downside to the json-iter library. +func tryExtractErrorMessage(err error) string { + msg := err.Error() + + msgEndIndex := strings.LastIndex(msg, ", error found in #") + if msgEndIndex == -1 { + return msg + } + + msgStartIndex := strings.Index(msg, ": ") + if msgStartIndex == -1 { + return msg + } + + operationStack := []string{msg[0:msgStartIndex]} + for { + msgStartIndexNext := strings.Index(msg[msgStartIndex+2:], ": ") + + // no more matches + if msgStartIndexNext == -1 { + break + } + + // matches occur after the end message marker (sanity check) + if (msgStartIndex + msgStartIndexNext) >= msgEndIndex { + break + } + + // match should not contain a space, indicates operation is really an error message + match := msg[msgStartIndex+2 : msgStartIndex+2+msgStartIndexNext] + if strings.Contains(match, " ") { + break + } + + operationStack = append(operationStack, match) + msgStartIndex += msgStartIndexNext + 2 + } + + if len(operationStack) > 1 && isLikelyDetailedErrorMessage(msg[msgStartIndex+2:]) { + return "cannot unmarshal " + operationStack[len(operationStack)-2] + ": " + msg[msgStartIndex+2:msgEndIndex] + } + + return msg[msgStartIndex+2 : msgEndIndex] +} + +// isLikelyDetailedErrorMessage checks if the json unmarshal error contains enough information such +// that the caller clearly understands the context, where the structure name is not needed. +func isLikelyDetailedErrorMessage(msg string) bool { + return !strings.HasPrefix(msg, "request.") +} + +// RawMessageExtension will call json.Compact() on every json.RawMessage field when getting marshalled. +type RawMessageExtension struct { + jsoniter.DummyExtension +} + +// CreateEncoder substitutes the default jsoniter encoder of the json.RawMessage type with ours, that +// calls json.Compact() before writting to the stream +func (e *RawMessageExtension) CreateEncoder(typ reflect2.Type) jsoniter.ValEncoder { + if typ == jsonRawMessageType { + return &rawMessageCodec{} + } + return nil +} + +var jsonRawMessageType = reflect2.TypeOfPtr(&json.RawMessage{}).Elem() + +// rawMessageCodec implements jsoniter.ValEncoder interface so we can override the default json.RawMessage Encode() +// function with our implementation +type rawMessageCodec struct{} + +func (codec *rawMessageCodec) Encode(ptr unsafe.Pointer, stream *jsoniter.Stream) { + if ptr != nil { + jsonRawMsg := *(*[]byte)(ptr) + + dst := bytes.NewBuffer(make([]byte, 0, len(jsonRawMsg))) + if err := json.Compact(dst, jsonRawMsg); err == nil { + stream.Write(dst.Bytes()) + } + } +} + +func (codec *rawMessageCodec) IsEmpty(ptr unsafe.Pointer) bool { + return ptr == nil || len(*((*json.RawMessage)(ptr))) == 0 +} diff --git a/util/jsonutil/jsonutil_test.go b/util/jsonutil/jsonutil_test.go index 12b1fd5e803..96632d62548 100644 --- a/util/jsonutil/jsonutil_test.go +++ b/util/jsonutil/jsonutil_test.go @@ -1,13 +1,19 @@ package jsonutil import ( - "github.com/stretchr/testify/assert" + "bytes" + "encoding/json" + "errors" "strings" "testing" + "unsafe" + + jsoniter "github.com/json-iterator/go" + "github.com/modern-go/reflect2" + "github.com/stretchr/testify/assert" ) func TestDropElement(t *testing.T) { - tests := []struct { description string input []byte @@ -183,3 +189,136 @@ func TestDropElement(t *testing.T) { } } } + +func TestTryExtractErrorMessage(t *testing.T) { + tests := []struct { + name string + givenErr string + expectedMsg string + }{ + { + name: "level-1", + givenErr: "readObjectStart: expect { or n, but found m, error found in #1 byte of ...|malformed|..., bigger context ...|malformed|..", + expectedMsg: "expect { or n, but found m", + }, + { + name: "level-2", + givenErr: "openrtb_ext.ExtRequestPrebidCache.Bids: readObjectStart: expect { or n, but found t, error found in #10 byte of ...|:{\"bids\":true}}|..., bigger context ...|{\"cache\":{\"bids\":true}}|...", + expectedMsg: "cannot unmarshal openrtb_ext.ExtRequestPrebidCache.Bids: expect { or n, but found t", + }, + { + name: "level-3+", + givenErr: "openrtb_ext.ExtRequestPrebid.Cache: openrtb_ext.ExtRequestPrebidCache.Bids: readObjectStart: expect { or n, but found t, error found in #10 byte of ...|:{\"bids\":true}}|..., bigger context ...|{\"cache\":{\"bids\":true}}|...", + expectedMsg: "cannot unmarshal openrtb_ext.ExtRequestPrebidCache.Bids: expect { or n, but found t", + }, + { + name: "error-msg", + givenErr: "Skip: do not know how to skip: 109, error found in #10 byte of ...|prebid\": malformed}|..., bigger context ...|{\"prebid\": malformed}|...", + expectedMsg: "do not know how to skip: 109", + }, + { + name: "specific", + givenErr: "openrtb_ext.ExtDevicePrebid.Interstitial: unmarshalerDecoder: request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100, error found in #10 byte of ...| }\n }|..., bigger context ...|: 120,\n \"minheightperc\": 60\n }\n }|...", + expectedMsg: "request.device.ext.prebid.interstitial.minwidthperc must be a number between 0 and 100", + }, + { + name: "normal", + givenErr: "normal error message", + expectedMsg: "normal error message", + }, + { + name: "norma-false-start", + givenErr: "false: normal error message", + expectedMsg: "false: normal error message", + }, + { + name: "norma-false-end", + givenErr: "normal error message, error found in #10 but doesn't follow format", + expectedMsg: "normal error message, error found in #10 but doesn't follow format", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + result := tryExtractErrorMessage(errors.New(test.givenErr)) + assert.Equal(t, test.expectedMsg, result) + }) + } +} + +func TestCreateEncoder(t *testing.T) { + testCases := []struct { + desc string + inType reflect2.Type + expectedValEncoder jsoniter.ValEncoder + }{ + { + desc: "With_extension", + inType: reflect2.TypeOfPtr((*jsoniter.Any)(nil)).Elem(), + expectedValEncoder: nil, + }, + { + desc: "No_extension", + inType: reflect2.TypeOfPtr(&json.RawMessage{}).Elem(), + expectedValEncoder: &rawMessageCodec{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.desc, func(t *testing.T) { + extension := &RawMessageExtension{} + encoder := extension.CreateEncoder(tc.inType) + assert.IsType(t, encoder, tc.expectedValEncoder) + }) + } +} + +func TestEncode(t *testing.T) { + jsonBlob := json.RawMessage(`{ + "properties": { + "string": "Blanks spaces in between words to not be removed if compacted", + "integer": 5, + "string_array": [ + "string array elem one", + "string array elem two" + ] + } +}`) + + t.Run( + "Nil_pointer", + func(t *testing.T) { + // set test + encoder := &rawMessageCodec{} + output := bytes.NewBuffer([]byte{}) + stream := jsoniter.NewStream(jsonConfigValidationOn, output, len(jsonBlob)) + + // run + encoder.Encode(nil, stream) + + // assertions + assert.Equal(t, "", output.String()) + assert.Equal(t, true, encoder.IsEmpty(nil)) + }, + ) + t.Run( + "json.RawMessage_compact_JSON", + func(t *testing.T) { + // set test + encoder := &rawMessageCodec{} + output := bytes.NewBuffer([]byte{}) + stream := jsoniter.NewStream(jsonConfigValidationOn, output, len(jsonBlob)) + + // run + encoder.Encode(unsafe.Pointer(&jsonBlob), stream) + + // assertions + assert.Equal( + t, + `{"properties":{"string":"Blanks spaces in between words to not be removed if compacted","integer":5,"string_array":["string array elem one","string array elem two"]}}`, + output.String(), + ) + assert.Equal(t, false, encoder.IsEmpty(unsafe.Pointer(&jsonBlob))) + }, + ) +} diff --git a/util/jsonutil/stringInt_test.go b/util/jsonutil/stringInt_test.go index e8639c7acee..cd2f3476e46 100644 --- a/util/jsonutil/stringInt_test.go +++ b/util/jsonutil/stringInt_test.go @@ -1,7 +1,6 @@ package jsonutil import ( - "encoding/json" "testing" "github.com/buger/jsonparser" @@ -16,27 +15,27 @@ func TestStringIntUnmarshalJSON(t *testing.T) { t.Run("string", func(t *testing.T) { jsonData := []byte(`{"item_id":"30"}`) var item Item - assert.NoError(t, json.Unmarshal(jsonData, &item)) + assert.NoError(t, UnmarshalValid(jsonData, &item)) assert.Equal(t, 30, int(item.ItemId)) }) t.Run("int", func(t *testing.T) { jsonData := []byte(`{"item_id":30}`) var item Item - assert.NoError(t, json.Unmarshal(jsonData, &item)) + assert.NoError(t, UnmarshalValid(jsonData, &item)) assert.Equal(t, 30, int(item.ItemId)) }) t.Run("empty_id", func(t *testing.T) { jsonData := []byte(`{"item_id": ""}`) var item Item - assert.NoError(t, json.Unmarshal(jsonData, &item)) + assert.NoError(t, UnmarshalValid(jsonData, &item)) }) t.Run("invalid_input", func(t *testing.T) { jsonData := []byte(`{"item_id":true}`) var item Item - err := json.Unmarshal(jsonData, &item) - assert.Equal(t, jsonparser.MalformedValueError, err) + err := UnmarshalValid(jsonData, &item) + assert.EqualError(t, err, "cannot unmarshal jsonutil.Item.ItemId: "+jsonparser.MalformedValueError.Error()) }) } diff --git a/util/ptrutil/ptrutil.go b/util/ptrutil/ptrutil.go index 90888f3eef8..27616a0a4b5 100644 --- a/util/ptrutil/ptrutil.go +++ b/util/ptrutil/ptrutil.go @@ -12,3 +12,12 @@ func Clone[T any](v *T) *T { clone := *v return &clone } + +func ValueOrDefault[T any](v *T) T { + if v != nil { + return *v + } + + var def T + return def +} diff --git a/util/ptrutil/ptrutil_test.go b/util/ptrutil/ptrutil_test.go new file mode 100644 index 00000000000..85671e31b44 --- /dev/null +++ b/util/ptrutil/ptrutil_test.go @@ -0,0 +1,45 @@ +package ptrutil + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestValueOrDefault(t *testing.T) { + t.Run("int-nil", func(t *testing.T) { + var v *int = nil + r := ValueOrDefault(v) + assert.Equal(t, 0, r) + }) + + t.Run("int-0", func(t *testing.T) { + var v *int = ToPtr[int](0) + r := ValueOrDefault(v) + assert.Equal(t, 0, r) + }) + + t.Run("int-42", func(t *testing.T) { + var v *int = ToPtr[int](42) + r := ValueOrDefault(v) + assert.Equal(t, 42, r) + }) + + t.Run("string-nil", func(t *testing.T) { + var v *string = nil + r := ValueOrDefault(v) + assert.Equal(t, "", r) + }) + + t.Run("string-empty", func(t *testing.T) { + var v *string = ToPtr[string]("") + r := ValueOrDefault(v) + assert.Equal(t, "", r) + }) + + t.Run("string-something", func(t *testing.T) { + var v *string = ToPtr[string]("something") + r := ValueOrDefault(v) + assert.Equal(t, "something", r) + }) +} diff --git a/util/stringutil/stringutil_test.go b/util/stringutil/stringutil_test.go index 94988ee41c9..a7aa0010995 100644 --- a/util/stringutil/stringutil_test.go +++ b/util/stringutil/stringutil_test.go @@ -30,7 +30,7 @@ func TestStrToInt8Slice(t *testing.T) { in: "malformed", expected: testOutput{ arr: nil, - err: &strconv.NumError{"ParseInt", "malformed", strconv.ErrSyntax}, + err: &strconv.NumError{Func: "ParseInt", Num: "malformed", Err: strconv.ErrSyntax}, }, }, { @@ -38,7 +38,7 @@ func TestStrToInt8Slice(t *testing.T) { in: "malformed,2,malformed", expected: testOutput{ arr: nil, - err: &strconv.NumError{"ParseInt", "malformed", strconv.ErrSyntax}, + err: &strconv.NumError{Func: "ParseInt", Num: "malformed", Err: strconv.ErrSyntax}, }, }, { @@ -46,7 +46,7 @@ func TestStrToInt8Slice(t *testing.T) { in: "128", expected: testOutput{ arr: nil, - err: &strconv.NumError{"ParseInt", "128", strconv.ErrRange}, + err: &strconv.NumError{Func: "ParseInt", Num: "128", Err: strconv.ErrRange}, }, }, { diff --git a/util/task/ticker_task_test.go b/util/task/ticker_task_test.go index c02eec158a1..3ca0280d23e 100644 --- a/util/task/ticker_task_test.go +++ b/util/task/ticker_task_test.go @@ -5,7 +5,7 @@ import ( "testing" "time" - "github.com/prebid/prebid-server/util/task" + "github.com/prebid/prebid-server/v2/util/task" "github.com/stretchr/testify/assert" ) diff --git a/validate.sh b/validate.sh index d263df0ab51..ca4fbc1dfd7 100755 --- a/validate.sh +++ b/validate.sh @@ -36,5 +36,5 @@ fi if $VET; then echo "Running go vet check" - go vet -composites=false ./... + go vet ./... fi diff --git a/version/xprebidheader.go b/version/xprebidheader.go index 103642feb6a..c65fdbe6389 100644 --- a/version/xprebidheader.go +++ b/version/xprebidheader.go @@ -3,9 +3,9 @@ package version import ( "strings" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" ) const xPrebidHeaderVersionPrefix = "owpbs-go" diff --git a/version/xprebidheader_test.go b/version/xprebidheader_test.go index 1843d3ea296..3b71bacc679 100644 --- a/version/xprebidheader_test.go +++ b/version/xprebidheader_test.go @@ -1,13 +1,13 @@ package version import ( - "encoding/json" "testing" - "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/openrtb/v20/openrtb2" "github.com/stretchr/testify/assert" - "github.com/prebid/prebid-server/openrtb_ext" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/prebid/prebid-server/v2/util/jsonutil" ) func TestBuildXPrebidHeader(t *testing.T) { @@ -134,12 +134,12 @@ func TestBuildXPrebidHeaderForRequest(t *testing.T) { for _, test := range testCases { req := &openrtb2.BidRequest{} if test.requestExt != nil { - reqExt, err := json.Marshal(test.requestExt) + reqExt, err := jsonutil.Marshal(test.requestExt) assert.NoError(t, err, test.description+":err marshalling reqExt") req.Ext = reqExt } if test.requestAppExt != nil { - reqAppExt, err := json.Marshal(test.requestAppExt) + reqAppExt, err := jsonutil.Marshal(test.requestAppExt) assert.NoError(t, err, test.description+":err marshalling reqAppExt") req.App = &openrtb2.App{Ext: reqAppExt} }