From 345168a990fbca967f6450c243db95bc7016f44b Mon Sep 17 00:00:00 2001 From: Peiling-Ding <65974486+Peiling-Ding@users.noreply.github.com> Date: Fri, 14 Jul 2023 15:00:16 -0700 Subject: [PATCH] Peiling/chore/sync to v0.263.0 (#48) * Fix: deal tiers no longer ignored due to presence of tid (#2829) * CAPT-787: GPP support for imds bidder. (#2867) Co-authored-by: Timothy M. Ace * Adsinteractive: change usersync endpoint to https (#2861) Co-authored-by: Balint Vargha * consumable adapter: add gpp support (#2883) * feat: IX Bid Adapter - gpp support for user sync urls (#2873) Co-authored-by: Chris Corbo * fix: update links in readme (#2888) authored by @akkapur * New Adapter: AIDEM (#2824) Co-authored-by: AndreaC <67786179+darkstarac@users.noreply.github.com> Co-authored-by: Andrea Tumbarello Co-authored-by: darkstar * Improve Digital adapter: Set currency in bid response (#2886) * Sharethrough: Support multiformat bid request impression (#2866) * Triplelift Bid Adapter: Adding GPP Support (#2887) * YahooAdvertising rebranding to Yahoo Ads. (#2872) Co-authored-by: oath-jac * IX: MultiImp Implementation (#2779) Co-authored-by: Chris Corbo Co-authored-by: Oronno Mamun * Exchange unit test fix (#2868) * Semgrep rules for adapters (#2833) * IX: Remove glog statement (#2909) * Activities framework (#2844) * PWBID: Update Default Endpoint (#2903) * script to run semgrep tests against adapter PRs (#2907) authored by @onkarvhanumante * semgrep rule to detect undesirable package imports in adapter code (#2911) * update package-import message (#2913) authored by @onkarvhanumante * Bump google.golang.org/grpc from 1.46.2 to 1.53.0 (#2905) --------- Co-authored-by: Brian Sardo <1168933+bsardo@users.noreply.github.com> Co-authored-by: Timothy Ace Co-authored-by: Timothy M. Ace Co-authored-by: balintvargha <122350182+balintvargha@users.noreply.github.com> Co-authored-by: Balint Vargha Co-authored-by: Jason Piros Co-authored-by: ccorbo Co-authored-by: Chris Corbo Co-authored-by: Ankush Co-authored-by: Giovanni Sollazzo Co-authored-by: AndreaC <67786179+darkstarac@users.noreply.github.com> Co-authored-by: Andrea Tumbarello Co-authored-by: darkstar Co-authored-by: Jozef Bartek <31618107+jbartek25@users.noreply.github.com> Co-authored-by: Max Dupuis <118775839+maxime-dupuis@users.noreply.github.com> Co-authored-by: Patrick Loughrey Co-authored-by: radubarbos Co-authored-by: oath-jac Co-authored-by: Oronno Mamun Co-authored-by: Veronika Solovei Co-authored-by: Onkar Hanumante Co-authored-by: Stephen Johnston Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../workflows/helpers/pull-request-utils.js | 337 ++++++++++++++ .github/workflows/semgrep.yml | 74 +++ .semgrep/adapter/builder-struct-name.go | 124 +++++ .semgrep/adapter/builder-struct-name.yml | 58 +++ .semgrep/adapter/package-import.go | 47 ++ .semgrep/adapter/package-import.yml | 13 + .semgrep/adapter/type-bid-assignment.go | 181 ++++++++ .semgrep/adapter/type-bid-assignment.yml | 64 +++ README.md | 8 +- adapters/aidem/aidem.go | 102 +++++ adapters/aidem/aidem_test.go | 30 ++ .../aidemtest/exemplary/multi-format.json | 119 +++++ .../exemplary/multi-imps-multi-bid.json | 154 +++++++ .../exemplary/multi-imps-single-bid.json | 131 ++++++ .../aidem/aidemtest/exemplary/no-bid.json | 58 +++ .../aidemtest/exemplary/optional-params.json | 60 +++ .../aidemtest/exemplary/simple-banner.json | 97 ++++ .../aidemtest/exemplary/simple-video.json | 103 +++++ ...valid-req-400-status-code-bad-request.json | 97 ++++ ...nvalid-res-200-status-code-empty-bids.json | 102 +++++ .../invalid-resp-multi-imp-type.json | 151 +++++++ ...valid-req-200-bid-response-from-aidem.json | 141 ++++++ .../valid-req-204-response-from-aidem.json | 66 +++ adapters/aidem/params_test.go | 57 +++ adapters/consumable/consumable.go | 10 + .../supplemental/simple-banner-gpp.json | 130 ++++++ adapters/improvedigital/improvedigital.go | 1 + .../supplemental/foreign-currency.json | 89 ++++ adapters/ix/ix.go | 197 ++++---- adapters/ix/ix_test.go | 10 +- .../multi-imp-multi-size-requests.json | 211 +++++++++ ...-requests.json => multi-imp-requests.json} | 167 +++---- .../ix/ixtest/exemplary/multiple-siteIds.json | 224 +++++++++ .../exemplary/simple-banner-multi-size.json | 110 +---- .../ix/ixtest/exemplary/structured-pod.json | 236 ++++++++++ .../multi-imp-requests-error.json | 189 ++++++++ adapters/ix/ixtest/supplemental/sid.json | 184 ++++++++ adapters/ix/params_test.go | 1 + adapters/pwbid/pwbid_test.go | 2 +- .../pwbid/pwbidtest/exemplary/banner.json | 4 +- .../pwbid/pwbidtest/exemplary/native.json | 4 +- .../pwbidtest/exemplary/optional-params.json | 4 +- adapters/pwbid/pwbidtest/exemplary/video.json | 2 +- .../response-200-without-body.json | 2 +- .../pwbidtest/supplemental/response-204.json | 2 +- .../pwbidtest/supplemental/response-400.json | 2 +- .../pwbidtest/supplemental/response-500.json | 2 +- adapters/sharethrough/sharethrough.go | 60 ++- .../supplemental/multiformat-impression.json | 362 +++++++++++++++ .../params_test.go | 16 +- .../yahooAds.go} | 12 +- .../yahooAds_test.go} | 14 +- .../exemplary/simple-app-banner.json | 2 +- .../exemplary/simple-banner.json | 2 +- .../yahooAdstest}/exemplary/simple-video.json | 2 +- .../supplemental/empty-banner-format.json | 0 .../supplemental/invalid-banner-height.json | 0 .../supplemental/invalid-banner-width.json | 0 .../non-supported-requests-bids-ignored.json | 2 +- .../supplemental/required-nobidder-info.json | 0 .../supplemental/server-error.json | 0 .../server-response-wrong-impid.json | 2 +- .../simple-banner-gpp-overwrite.json | 2 +- .../supplemental/simple-banner-gpp.json | 2 +- ...nner-ignore-width-when-height-missing.json | 2 +- config/account.go | 2 +- config/activity.go | 3 +- config/config.go | 4 + endpoints/openrtb2/amp_auction.go | 9 + endpoints/openrtb2/auction.go | 11 + exchange/adapter_builders.go | 9 +- exchange/adapter_util.go | 2 +- exchange/adapter_util_test.go | 8 +- exchange/exchange.go | 2 + exchange/exchange_test.go | 34 +- exchange/utils.go | 9 + exchange/utils_test.go | 72 ++- go.mod | 8 +- go.sum | 19 +- openrtb_ext/bidders.go | 4 + openrtb_ext/bidders_validate_test.go | 2 +- openrtb_ext/deal_tier.go | 14 - openrtb_ext/deal_tier_test.go | 45 +- openrtb_ext/imp_aidem.go | 8 + openrtb_ext/imp_ix.go | 1 + openrtb_ext/imp_yahooAds.go | 7 + privacy/activity.go | 3 + privacy/enforcer.go | 247 ++++++++-- privacy/enforcer_test.go | 427 +++++++++++++++++- privacy/policyenforcer.go | 45 ++ privacy/policyenforcer_test.go | 18 + static/bidder-info/adsinteractive.yaml | 2 +- static/bidder-info/aidem.yaml | 19 + static/bidder-info/consumable.yaml | 4 +- static/bidder-info/imds.yaml | 6 +- static/bidder-info/ix.yaml | 6 +- static/bidder-info/mediafuse.yaml | 4 +- static/bidder-info/pwbid.yaml | 4 +- static/bidder-info/triplelift.yaml | 8 +- static/bidder-info/triplelift_native.yaml | 8 +- static/bidder-info/yahooAds.yaml | 18 + static/bidder-params/aidem.json | 29 ++ static/bidder-params/ix.json | 5 + static/bidder-params/yahooAds.json | 19 + 104 files changed, 5344 insertions(+), 438 deletions(-) create mode 100644 .github/workflows/helpers/pull-request-utils.js create mode 100644 .github/workflows/semgrep.yml create mode 100644 .semgrep/adapter/builder-struct-name.go create mode 100644 .semgrep/adapter/builder-struct-name.yml create mode 100644 .semgrep/adapter/package-import.go create mode 100644 .semgrep/adapter/package-import.yml create mode 100644 .semgrep/adapter/type-bid-assignment.go create mode 100644 .semgrep/adapter/type-bid-assignment.yml create mode 100644 adapters/aidem/aidem.go create mode 100644 adapters/aidem/aidem_test.go create mode 100644 adapters/aidem/aidemtest/exemplary/multi-format.json create mode 100644 adapters/aidem/aidemtest/exemplary/multi-imps-multi-bid.json create mode 100644 adapters/aidem/aidemtest/exemplary/multi-imps-single-bid.json create mode 100644 adapters/aidem/aidemtest/exemplary/no-bid.json create mode 100644 adapters/aidem/aidemtest/exemplary/optional-params.json create mode 100644 adapters/aidem/aidemtest/exemplary/simple-banner.json create mode 100644 adapters/aidem/aidemtest/exemplary/simple-video.json create mode 100644 adapters/aidem/aidemtest/supplemental/invalid-req-400-status-code-bad-request.json create mode 100644 adapters/aidem/aidemtest/supplemental/invalid-res-200-status-code-empty-bids.json create mode 100644 adapters/aidem/aidemtest/supplemental/invalid-resp-multi-imp-type.json create mode 100644 adapters/aidem/aidemtest/supplemental/valid-req-200-bid-response-from-aidem.json create mode 100644 adapters/aidem/aidemtest/supplemental/valid-req-204-response-from-aidem.json create mode 100644 adapters/aidem/params_test.go create mode 100644 adapters/consumable/consumable/supplemental/simple-banner-gpp.json create mode 100644 adapters/improvedigital/improvedigitaltest/supplemental/foreign-currency.json create mode 100644 adapters/ix/ixtest/exemplary/multi-imp-multi-size-requests.json rename adapters/ix/ixtest/exemplary/{max-requests.json => multi-imp-requests.json} (69%) create mode 100644 adapters/ix/ixtest/exemplary/multiple-siteIds.json create mode 100644 adapters/ix/ixtest/exemplary/structured-pod.json create mode 100644 adapters/ix/ixtest/supplemental/multi-imp-requests-error.json create mode 100644 adapters/ix/ixtest/supplemental/sid.json create mode 100644 adapters/sharethrough/sharethroughtest/supplemental/multiformat-impression.json rename adapters/{yahooAdvertising => yahooAds}/params_test.go (63%) rename adapters/{yahooAdvertising/yahooAdvertising.go => yahooAds/yahooAds.go} (93%) rename adapters/{yahooAdvertising/yahooAdvertising_test.go => yahooAds/yahooAds_test.go} (51%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/exemplary/simple-app-banner.json (98%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/exemplary/simple-banner.json (98%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/exemplary/simple-video.json (98%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/supplemental/empty-banner-format.json (100%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/supplemental/invalid-banner-height.json (100%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/supplemental/invalid-banner-width.json (100%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/supplemental/non-supported-requests-bids-ignored.json (98%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/supplemental/required-nobidder-info.json (100%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/supplemental/server-error.json (100%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/supplemental/server-response-wrong-impid.json (98%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/supplemental/simple-banner-gpp-overwrite.json (98%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/supplemental/simple-banner-gpp.json (98%) rename adapters/{yahooAdvertising/yahooAdvertisingtest => yahooAds/yahooAdstest}/supplemental/simple-banner-ignore-width-when-height-missing.json (98%) create mode 100644 openrtb_ext/imp_aidem.go create mode 100644 openrtb_ext/imp_yahooAds.go create mode 100644 privacy/policyenforcer.go create mode 100644 privacy/policyenforcer_test.go create mode 100644 static/bidder-info/aidem.yaml create mode 100644 static/bidder-info/yahooAds.yaml create mode 100644 static/bidder-params/aidem.json create mode 100644 static/bidder-params/yahooAds.json diff --git a/.github/workflows/helpers/pull-request-utils.js b/.github/workflows/helpers/pull-request-utils.js new file mode 100644 index 00000000000..64ea538290f --- /dev/null +++ b/.github/workflows/helpers/pull-request-utils.js @@ -0,0 +1,337 @@ +const synchronizeEvent = "synchronize", + openedEvent = "opened", + completedStatus = "completed", + resultSize = 100 + +class diffHelper { + constructor(input) { + this.owner = input.context.repo.owner + this.repo = input.context.repo.repo + this.github = input.github + this.pullRequestNumber = input.context.payload.pull_request.number + this.pullRequestEvent = input.event + this.testName = input.testName + this.fileNameFilter = !input.fileNameFilter ? () => true : input.fileNameFilter + this.fileLineFilter = !input.fileLineFilter ? () => true : input.fileLineFilter + } + + /* + Checks whether the test defined by this.testName has been executed on the given commit + @param {string} commit - commit SHA to check for test execution + @returns {boolean} - returns true if the test has been executed on the commit, otherwise false + */ + async #isTestExecutedOnCommit(commit) { + const response = await this.github.rest.checks.listForRef({ + owner: this.owner, + repo: this.repo, + ref: commit, + }) + + return response.data.check_runs.some( + ({ status, name }) => status === completedStatus && name === this.testName + ) + } + + /* + Retrieves the line numbers of added or updated lines in the provided files + @param {Array} files - array of files containing their filename and patch + @returns {Object} - object mapping filenames to arrays of line numbers indicating the added or updated lines + */ + async #getDiffForFiles(files = []) { + let diff = {} + for (const { filename, patch } of files) { + if (this.fileNameFilter(filename)) { + const lines = patch.split("\n") + if (lines.length === 1) { + continue + } + + let lineNumber + for (const line of lines) { + // Check if line is diff header + // example: + // @@ -1,3 +1,3 @@ + // 1 var a + // 2 + // 3 - //test + // 3 +var b + // Here @@ -1,3 +1,3 @@ is diff header + if (line.match(/@@\s.*?@@/) != null) { + lineNumber = parseInt(line.match(/\+(\d+)/)[0]) + continue + } + + // "-" prefix indicates line was deleted. So do not consider deleted line + if (line.startsWith("-")) { + continue + } + + // "+"" prefix indicates line was added or updated. Include line number in diff details + if (line.startsWith("+") && this.fileLineFilter(line)) { + diff[filename] = diff[filename] || [] + diff[filename].push(lineNumber) + } + lineNumber++ + } + } + } + return diff + } + + /* + Retrieves a list of commits that have not been checked by the test defined by this.testName + @returns {Array} - array of commit SHAs that have not been checked by the test + */ + async #getNonScannedCommits() { + const { data } = await this.github.rest.pulls.listCommits({ + owner: this.owner, + repo: this.repo, + pull_number: this.pullRequestNumber, + per_page: resultSize, + }) + let nonScannedCommits = [] + + // API returns commits in ascending order. Loop in reverse to quickly retrieve unchecked commits + for (let i = data.length - 1; i >= 0; i--) { + const { sha, parents } = data[i] + + // Commit can be merged master commit. Such commit have multiple parents + // Do not consider such commit for building file diff + if (parents.length > 1) { + continue + } + + const isTestExecuted = await this.#isTestExecutedOnCommit(sha) + if (isTestExecuted) { + // Remaining commits have been tested in previous scans. Therefore, do not need to be considered again + break + } else { + nonScannedCommits.push(sha) + } + } + + // Reverse to return commits in ascending order. This is needed to build diff for commits in chronological order + return nonScannedCommits.reverse() + } + + /* + Filters the commit diff to include only the files that are part of the PR diff + @param {Array} commitDiff - array of line numbers representing lines added or updated in the commit + @param {Array} prDiff - array of line numbers representing lines added or updated in the pull request + @returns {Array} - filtered commit diff, including only the files that are part of the PR diff + */ + async #filterCommitDiff(commitDiff = [], prDiff = []) { + return commitDiff.filter((file) => prDiff.includes(file)) + } + + /* + Builds the diff for the pull request, including both the changes in the pull request and the changes in non-scanned commits + @returns {string} - json string representation of the pull request diff and the diff for non-scanned commits + */ + async buildDiff() { + const { data } = await this.github.rest.pulls.listFiles({ + owner: this.owner, + repo: this.repo, + pull_number: this.pullRequestNumber, + per_page: resultSize, + }) + + const pullRequestDiff = await this.#getDiffForFiles(data) + + const nonScannedCommitsDiff = + Object.keys(pullRequestDiff).length != 0 && this.pullRequestEvent === synchronizeEvent // The "synchronize" event implies that new commit are pushed after the pull request was opened + ? await this.getNonScannedCommitDiff(pullRequestDiff) + : {} + + const prDiffFiles = Object.keys(pullRequestDiff) + const pullRequest = { + hasChanges: prDiffFiles.length > 0, + files: prDiffFiles.join(" "), + diff: pullRequestDiff, + } + const uncheckedCommits = { diff: nonScannedCommitsDiff } + return JSON.stringify({ pullRequest, uncheckedCommits }) + } + + /* + Retrieves the diff for non-scanned commits by comparing their changes with the pull request diff + @param {Object} pullRequestDiff - The diff of files in the pull request + @returns {Object} - The diff of files in the non-scanned commits that are part of the pull request diff + */ + async getNonScannedCommitDiff(pullRequestDiff) { + let nonScannedCommitsDiff = {} + // Retrieves list of commits that have not been scanned by the PR check + const nonScannedCommits = await this.#getNonScannedCommits() + for (const commit of nonScannedCommits) { + const { data } = await this.github.rest.repos.getCommit({ + owner: this.owner, + repo: this.repo, + ref: commit, + }) + + const commitDiff = await this.#getDiffForFiles(data.files) + const files = Object.keys(commitDiff) + for (const file of files) { + // Consider scenario where the changes made to a file in the initial commit are completely undone by subsequent commits + // In such cases, the modifications from the initial commit should not be taken into account + // If the changes were entirely removed, there should be no entry for the file in the pullRequestStats + const filePRDiff = pullRequestDiff[file] + if (!filePRDiff) { + continue + } + + // Consider scenario where changes made in the commit were partially removed or modified by subsequent commits + // In such cases, include only those commit changes that are part of the pullRequestStats object + // This ensures that only the changes that are reflected in the pull request are considered + const changes = await this.#filterCommitDiff(commitDiff[file], filePRDiff) + + if (changes.length !== 0) { + // Check if nonScannedCommitsDiff[file] exists, if not assign an empty array to it + nonScannedCommitsDiff[file] = nonScannedCommitsDiff[file] || [] + // Combine the existing nonScannedCommitsDiff[file] array with the commit changes + // Remove any duplicate elements using the Set data structure + nonScannedCommitsDiff[file] = [ + ...new Set([...nonScannedCommitsDiff[file], ...changes]), + ] + } + } + } + return nonScannedCommitsDiff + } +} + +class semgrepHelper { + constructor(input) { + this.owner = input.context.repo.owner + this.repo = input.context.repo.repo + this.github = input.github + + this.pullRequestNumber = input.context.payload.pull_request.number + this.pullRequestEvent = input.event + + this.pullRequestDiff = input.diff.pullRequest.diff + this.newCommitsDiff = input.diff.uncheckedCommits.diff + + this.semgrepErrors = [] + this.semgrepWarnings = [] + input.semgrepResult.forEach((res) => { + res.severity === "High" ? this.semgrepErrors.push(res) : this.semgrepWarnings.push(res) + }) + + this.headSha = input.headSha + } + + /* + Retrieves the matching line number from the provided diff for a given file and range of lines + @param {Object} range - object containing the file, start line, and end line to find a match + @param {Object} diff - object containing file changes and corresponding line numbers + @returns {number|null} - line number that matches the range within the diff, or null if no match is found + */ + async #getMatchingLineFromDiff({ file, start, end }, diff) { + const fileDiff = diff[file] + if (!fileDiff) { + return null + } + if (fileDiff.includes(start)) { + return start + } + if (fileDiff.includes(end)) { + return end + } + return null + } + + /* + Splits the semgrep results into different categories based on the scan + @param {Array} semgrepResults - array of results reported by semgrep + @returns {Object} - object containing the categorized semgrep results i.e results reported in previous scans and new results found in the current scan + */ + async #splitSemgrepResultsByScan(semgrepResults = []) { + const result = { + nonDiff: [], // Errors or warnings found in files updated in pull request, but not part of sections that were modified in the pull request + previous: [], // Errors or warnings found in previous semgrep scans + current: [], // Errors or warnings found in current semgrep scan + } + + for (const se of semgrepResults) { + const prDiffLine = await this.#getMatchingLineFromDiff(se, this.pullRequestDiff) + if (!prDiffLine) { + result.nonDiff.push({ ...se }) + continue + } + + switch (this.pullRequestEvent) { + case openedEvent: + // "Opened" event implies that this is the first check + // Therefore, the error should be appended to the result.current + result.current.push({ ...se, line: prDiffLine }) + case synchronizeEvent: + const commitDiffLine = await this.#getMatchingLineFromDiff(se, this.newCommitsDiff) + // Check if error or warning is part of current commit diff + // If not then error or warning was reported in previous scans + commitDiffLine != null + ? result.current.push({ ...se, line: commitDiffLine }) + : result.previous.push({ + ...se, + line: prDiffLine, + }) + } + } + return result + } + + /* + Adds review comments based on the semgrep results to the current pull request + @returns {Object} - object containing the count of unaddressed comments from the previous scan and the count of new comments from the current scan + */ + async addReviewComments() { + let result = { + previousScan: { unAddressedComments: 0 }, + currentScan: { newComments: 0 }, + } + + if (this.semgrepErrors.length == 0 && this.semgrepWarnings.length == 0) { + return result + } + + const errors = await this.#splitSemgrepResultsByScan(this.semgrepErrors) + if (errors.previous.length == 0 && errors.current.length == 0) { + console.log("Semgrep did not find any errors in the current pull request changes") + } else { + for (const { message, file, line } of errors.current) { + await this.github.rest.pulls.createReviewComment({ + owner: this.owner, + repo: this.repo, + pull_number: this.pullRequestNumber, + commit_id: this.headSha, + body: message, + path: file, + line: line, + }) + } + result.currentScan.newComments = errors.current.length + if (this.pullRequestEvent == synchronizeEvent) { + result.previousScan.unAddressedComments = errors.previous.length + } + } + + const warnings = await this.#splitSemgrepResultsByScan(this.semgrepWarnings) + for (const { message, file, line } of warnings.current) { + await this.github.rest.pulls.createReviewComment({ + owner: this.owner, + repo: this.repo, + pull_number: this.pullRequestNumber, + commit_id: this.headSha, + body: "Consider this as a suggestion. " + message, + path: file, + line: line, + }) + } + return result + } +} + +module.exports = { + diffHelper: (input) => new diffHelper(input), + semgrepHelper: (input) => new semgrepHelper(input), +} diff --git a/.github/workflows/semgrep.yml b/.github/workflows/semgrep.yml new file mode 100644 index 00000000000..87c5573e671 --- /dev/null +++ b/.github/workflows/semgrep.yml @@ -0,0 +1,74 @@ +name: Adapter semgrep checks +on: + pull_request: + paths: ["adapters/*/*.go"] +permissions: write-all +jobs: + semgrep-check: + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Calculate diff + id: calculate_diff + uses: actions/github-script@v6 + with: + result-encoding: string + script: | + const utils = require('./.github/workflows/helpers/pull-request-utils.js') + // consider only non-test Go files that are part of the adapter code + function fileNameFilter(filename) { + return filename.startsWith("adapters/") && filename.split("/").length > 2 && filename.endsWith(".go") && !filename.endsWith("_test.go") + } + const helper = utils.diffHelper({github, context, fileNameFilter, event: "${{github.event.action}}", testName: "${{github.job}}"}) + return await helper.buildDiff() + + - name: Should run semgrep + id: should_run_semgrep + run: | + hasChanges=$(echo '${{ steps.calculate_diff.outputs.result }}' | jq .pullRequest.hasChanges) + echo "hasChanges=${hasChanges}" >> $GITHUB_OUTPUT + + - name: Install semgrep + if: contains(steps.should_run_semgrep.outputs.hasChanges, 'true') + run: | + pip3 install semgrep==1.22.0 + semgrep --version + + - name: Run semgrep tests + id: run_semgrep_tests + if: contains(steps.should_run_semgrep.outputs.hasChanges, 'true') + run: | + unqouted_string=$(echo '${{ steps.calculate_diff.outputs.result }}' | jq .pullRequest.files | tr -d '"') + outputs=$(semgrep --gitlab-sast --config=.semgrep/adapter $unqouted_string | jq '[.vulnerabilities[] | {"file": .location.file, "severity": .severity, "start": .location.start_line, "end": .location.end_line, "message": (.message | gsub("\\n"; "\n"))}]' | jq -c | jq -R) + echo "semgrep_result=${outputs}" >> "$GITHUB_OUTPUT" + + - 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 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + result-encoding: string + script: | + const utils = require('./.github/workflows/helpers/pull-request-utils.js') + const helper = utils.semgrepHelper({ + github, context, event: "${{github.event.action}}", + semgrepResult: JSON.parse(${{ steps.run_semgrep_tests.outputs.semgrep_result }}), + diff: ${{ steps.calculate_diff.outputs.result }}, headSha: "${{github.event.pull_request.head.sha}}" + }) + const { previousScan, currentScan } = await helper.addReviewComments() + return previousScan.unAddressedComments + currentScan.newComments + + - name: Adapter semgrep checks result + if: contains(steps.should_run_semgrep.outputs.hasChanges, 'true') + run: | + if [ "${{steps.add_pull_request_comment.outputs.result}}" -ne "0" ]; then + echo 'Semgrep has found "${{steps.add_pull_request_comment.outputs.result}}" errors' + exit 1 + else + echo 'Semgrep did not find any errors in the pull request changes' + fi diff --git a/.semgrep/adapter/builder-struct-name.go b/.semgrep/adapter/builder-struct-name.go new file mode 100644 index 00000000000..37a938ca748 --- /dev/null +++ b/.semgrep/adapter/builder-struct-name.go @@ -0,0 +1,124 @@ +/* + builder-struct-name tests + https://semgrep.dev/docs/writing-rules/testing-rules + "ruleid" prefix in comment indicates patterns that should be flagged by semgrep + "ok" prefix in comment indidcates patterns that should not be flagged by the semgrep +*/ + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + foo1 := foo{} + // ruleid: builder-struct-name-check + return &fooadapter{foo: foo1}, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + // ruleid: builder-struct-name-check + return &adapterbar{}, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + // ruleid: builder-struct-name-check + return &fooadapterbar{}, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + // ruleid: builder-struct-name-check + return &FooAdapter{}, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + // ruleid: builder-struct-name-check + return &AdapterBar{}, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + // ruleid: builder-struct-name-check + return &AdapterBar{}, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + // ruleid: builder-struct-name-check + return &FooAdapterBar{}, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + foo2 := foo{} + //ruleid: builder-struct-name-check + adpt1 := Adapter{foo: foo2} + return &adpt1, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + //ruleid: builder-struct-name-check + builder := &Adapter{foo{}} + return builder, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + foo3 := foo{} + if foo3.bar == "" { + foo3.bar = "bar" + } + //ruleid: builder-struct-name-check + adpt2 := Adapter{} + adpt2.foo = foo3 + return &adpt2, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + //ruleid: builder-struct-name-check + return &foo{}, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + //ruleid: builder-struct-name-check + var obj Adapter + obj.Foo = "foo" + if obj.Bar == "" { + obj.Bar = "bar" + } + return &obj, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + //ruleid: builder-struct-name-check + var obj *FooAdapterBar + obj.Foo = "foo" + if obj.Bar == "" { + obj.Bar = "bar" + } + return obj, nil +} + +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + // ok: builder-struct-name-check + return &adapter{endpoint: "www.foo.com"}, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + builder := &adapter{} + builder.endpoint = "www.foo.com" + // ok: builder-struct-name-check + return builder, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + builder := adapter{} + builder.endpoint = "www.foo.com" + // ok: builder-struct-name-check + return &builder, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + var builder adapter + builder.endpoint = "www.foo.com" + // ok: builder-struct-name-check + return &builder, nil +} + +func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { + var builder *adapter + builder.endpoint = "www.foo.com" + // ok: builder-struct-name-check + return builder, nil +} \ No newline at end of file diff --git a/.semgrep/adapter/builder-struct-name.yml b/.semgrep/adapter/builder-struct-name.yml new file mode 100644 index 00000000000..bc876ae1809 --- /dev/null +++ b/.semgrep/adapter/builder-struct-name.yml @@ -0,0 +1,58 @@ +rules: + - id: builder-struct-name-check + languages: + - go + message: | + You can call this simply "adapter", the `$BUILDER` identification is already supplied by the package name. As you have it, referencing your adapter from outside the package would be `$BUILDER.$BUILDER` which looks a little redundant. See example below: + + ``` + package foo + + type adapter struct { + endpoint string + } + + func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + return &adapter{endpoint: "https://www.foo.com"}, nil + } + ``` + severity: ERROR + patterns: + - pattern-either: + - pattern-inside: > + func Builder($BIDDER_NAME openrtb_ext.BidderName, $CONFIG config.Adapter, $SERVER config.Server) (adapters.Bidder, error) { + ... + $BUILDER_OBJ := &$BUILDER{...} + ... + return $BUILDER_OBJ, nil + } + - pattern-inside: > + func Builder($BIDDER_NAME openrtb_ext.BidderName, $CONFIG config.Adapter, $SERVER config.Server) (adapters.Bidder, error) { + ... + $BUILDER_OBJ := $BUILDER{...} + ... + return &$BUILDER_OBJ, nil + } + - pattern-inside: > + func Builder($BIDDER_NAME openrtb_ext.BidderName, $CONFIG config.Adapter, $SERVER config.Server) (adapters.Bidder, error) { + ... + return &$BUILDER{...}, ... + } + - pattern-inside: > + func Builder($BIDDER_NAME openrtb_ext.BidderName, $CONFIG config.Adapter, $SERVER config.Server) (adapters.Bidder, error) { + ... + var $BUILDER_OBJ $BUILDER + ... + return &$BUILDER_OBJ, ... + } + - pattern-inside: > + func Builder($BIDDER_NAME openrtb_ext.BidderName, $CONFIG config.Adapter, $SERVER config.Server) (adapters.Bidder, error) { + ... + var $BUILDER_OBJ *$BUILDER + ... + return $BUILDER_OBJ, ... + } + - focus-metavariable: $BUILDER + - metavariable-regex: + metavariable: $BUILDER + regex: (?!adapter$) diff --git a/.semgrep/adapter/package-import.go b/.semgrep/adapter/package-import.go new file mode 100644 index 00000000000..a34ce8789cd --- /dev/null +++ b/.semgrep/adapter/package-import.go @@ -0,0 +1,47 @@ +import ( + // ok: package-import-check + "fmt" + // ok: package-import-check + "os" + // ruleid: package-import-check + "github.com/mitchellh/copystructure" + // ruleid: package-import-check + "github.com/golang/glog" +) + +import ( + // ok: package-import-check + "fmt" + // ruleid: package-import-check + cs "github.com/mitchellh/copystructure" + // ok: package-import-check + "os" + // ruleid: package-import-check + log "github.com/golang/glog" +) + +import ( + // ok: package-import-check + "fmt" + // ruleid: package-import-check + cs "github.com/mitchellh/copystructure/subpackage" + // ok: package-import-check + "os" + // ruleid: package-import-check + log "github.com/golang/glog/subpackage" +) + +// ruleid: package-import-check +import "github.com/golang/glog" + +// ruleid: package-import-check +import "github.com/mitchellh/copystructure" + +// ruleid: package-import-check +import log "github.com/golang/glog" + +// ruleid: package-import-check +import copy "github.com/mitchellh/copystructure" + +// ok: package-import-check +import "fmt" diff --git a/.semgrep/adapter/package-import.yml b/.semgrep/adapter/package-import.yml new file mode 100644 index 00000000000..20de2d107da --- /dev/null +++ b/.semgrep/adapter/package-import.yml @@ -0,0 +1,13 @@ +rules: + - id: package-import-check + message: Usage of "$PKG" package is prohibited in adapter code + languages: + - go + severity: ERROR + pattern-either: + - patterns: + - pattern: import "$PKG" + - focus-metavariable: $PKG + - metavariable-regex: + metavariable: $PKG + regex: (^github\.com/mitchellh/copystructure(/.*)?$|^github\.com/golang/glog(/.*)?$) \ No newline at end of file diff --git a/.semgrep/adapter/type-bid-assignment.go b/.semgrep/adapter/type-bid-assignment.go new file mode 100644 index 00000000000..fb29a4912d5 --- /dev/null +++ b/.semgrep/adapter/type-bid-assignment.go @@ -0,0 +1,181 @@ +/* + type-bid-assignment tests + https://semgrep.dev/docs/writing-rules/testing-rules + "ruleid" prefix in comment indicates patterns that should be flagged by semgrep + "ok" prefix in comment indidcates patterns that should not be flagged by the semgrep +*/ + +func (a *adapter) 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 { + return nil, []error{err} + } + + for _, seatBid := range bidResp.SeatBid { + for _, sb := range seatBid.Bid { + bidType, err := getMediaTypeForImp(seatBid.Bid[i], internalRequest.Imp) + if err != nil { + errs = append(errs, err) + } else { + b := &adapters.TypedBid{ + // ruleid: type-bid-assignment-check + Bid: &sb, + BidType: bidType, + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + } +} + +func (a *adapter) 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 { + return nil, []error{err} + } + + for _, seatBid := range bidResp.SeatBid { + for _, sb := range seatBid.Bid { + sbcopy := sb + bidType, err := getMediaTypeForImp(seatBid.Bid[i], internalRequest.Imp) + if err != nil { + errs = append(errs, err) + } else { + b := &adapters.TypedBid{ + // ok: type-bid-assignment-check + Bid: &sbcopy, + BidType: bidType, + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + } +} + +func (a *adapter) 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 { + return nil, []error{err} + } + + for _, seatBid := range bidResp.SeatBid { + for _, sb := range seatBid.Bid { + bidType, err := getMediaTypeForImp(seatBid.Bid[i], internalRequest.Imp) + if err != nil { + return nil, err + } + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + // ruleid: type-bid-assignment-check + Bid: &sb, + BidType: bidType, + }) + + } + } +} + +func (a *adapter) 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 { + return nil, []error{err} + } + + for _, seatBid := range bidResp.SeatBid { + for _, sb := range seatBid.Bid { + bidType, err := getMediaTypeForImp(seatBid.Bid[i], internalRequest.Imp) + if err != nil { + return nil, err + } + // ruleid: type-bid-assignment-check + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{Bid: &sb, BidType: bidType}) + } + } +} + +func (a *adapter) 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 { + return nil, []error{err} + } + + var errors []error + for _, seatBid := range bidResp.SeatBid { + for _, bid := range seatBid.Bid { + var t adapters.TypedBid + // ruleid: type-bid-assignment-check + t.Bid = &bid + bidResponse.Bids = append(bidResponse.Bids, &t) + } + } + return bidResponse, errors +} + +func (a *adapter) 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 { + return nil, []error{err} + } + + var errors []error + for _, seatBid := range bidResp.SeatBid { + for _, bid := range seatBid.Bid { + var t adapters.TypedBid + t = adapters.TypedBid{ + // ruleid: type-bid-assignment-check + Bid: &bid, + } + + bidResponse.Bids = append(bidResponse.Bids, &t) + } + } + return bidResponse, errors +} + +func (a *adapter) 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 { + return nil, []error{err} + } + + for _, seatBid := range bidResp.SeatBid { + for idx, _ := range seatBid.Bid { + bidType, err := getMediaTypeForImp(seatBid.Bid[i], internalRequest.Imp) + if err != nil { + errs = append(errs, err) + } else { + b := &adapters.TypedBid{ + // ok: type-bid-assignment-check + Bid: &seatBid.Bid[idx], + BidType: bidType, + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + } +} + +func (a *adapter) 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 { + return nil, []error{err} + } + + for _, seatBid := range bidResp.SeatBid { + for idx := range seatBid.Bid { + bidType, err := getMediaTypeForImp(seatBid.Bid[i], internalRequest.Imp) + if err != nil { + return nil, err + } + // ok: type-bid-assignment-check + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{Bid: &seatBid.Bid[idx], BidType: bidType}) + } + } +} diff --git a/.semgrep/adapter/type-bid-assignment.yml b/.semgrep/adapter/type-bid-assignment.yml new file mode 100644 index 00000000000..95fb6811227 --- /dev/null +++ b/.semgrep/adapter/type-bid-assignment.yml @@ -0,0 +1,64 @@ +rules: + - id: type-bid-assignment-check + languages: + - go + message: > + Found incorrect assignment made to $KEY. $BID variable receives a new value in each iteration of range loop. Assigning the address of $BID `(&$BID)` to $KEY will result in a pointer that always points to the same memory address with the value of the last iteration. + This can lead to unexpected behavior or incorrect results. Refer https://go.dev/play/p/9ZS1f-5h4qS + + Consider using an index variable in the seatBids.Bid loop as shown below + + ``` + for _, seatBid := range response.SeatBid { + for i := range seatBids.Bid { + ... + responseBid := &adapters.TypedBid{ + Bid: &seatBids.Bid[i], + ... + } + ... + ... + } + } + ``` + severity: ERROR + patterns: + - pattern-either: + - pattern: > + for _, $BID := range ... { + ... + ... := &adapters.TypedBid{ + $KEY: &$BID, + ... + } + ... + } + - pattern: > + for _, $BID := range ... { + ... + ... = adapters.TypedBid{ + $KEY: &$BID, + ... + } + ... + } + - pattern: > + for _, $BID := range ... { + ... + ... = append(..., &adapters.TypedBid{ + $KEY: &$BID, + ... + }) + ... + } + - pattern: > + for _, $BID := range ... { + var $TYPEBID_OBJ adapters.TypedBid + ... + $TYPEBID_OBJ.$KEY = &$BID + ... + } + - focus-metavariable: $KEY + - metavariable-regex: + metavariable: $KEY + regex: Bid diff --git a/README.md b/README.md index 4ec6792c0c8..95e0262b46f 100644 --- a/README.md +++ b/README.md @@ -5,17 +5,17 @@ # Prebid Server Prebid Server is an open source implementation of Server-Side Header Bidding. -It is managed by [Prebid.org](http://prebid.org/overview/what-is-prebid-org.html), -and upholds the principles from the [Prebid Code of Conduct](http://prebid.org/wrapper_code_of_conduct.html). +It is managed by [Prebid.org](https://prebid.org/about/), +and upholds the principles from the [Prebid Code of Conduct](https://prebid.org/code-of-conduct/). 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). For more information, see: -- [What is Prebid?](https://prebid.org/overview/intro.html) +- [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](http://prebid.org/dev-docs/pbs-bidders.html) +- [Current Bidders](https://docs.prebid.org/dev-docs/pbs-bidders.html) 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. diff --git a/adapters/aidem/aidem.go b/adapters/aidem/aidem.go new file mode 100644 index 00000000000..9748f32c957 --- /dev/null +++ b/adapters/aidem/aidem.go @@ -0,0 +1,102 @@ +package aidem + +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" +) + +type adapter struct { + endpoint string +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + + reqJson, err := json.Marshal(request) + 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, + Body: reqJson, + Headers: headers, + }}, nil +} + +func (a *adapter) MakeBids(internalRequest *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{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + var bidResp openrtb2.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("JSON parsing error: %v", err), + }} + } + + if len(bidResp.SeatBid) == 0 { + return nil, []error{&errortypes.BadServerResponse{ + Message: "Empty SeatBid array", + }} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) + + for _, seatBid := range bidResp.SeatBid { + for i := range seatBid.Bid { + bidType, err := getMediaTypeForBid(seatBid.Bid[i]) + if err != nil { + errs = append(errs, err) + } else { + b := &adapters.TypedBid{ + Bid: &seatBid.Bid[i], + BidType: bidType, + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + } + return bidResponse, errs +} + +// 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 +} + +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.MarkupAudio: + return openrtb_ext.BidTypeAudio, 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/aidem/aidem_test.go b/adapters/aidem/aidem_test.go new file mode 100644 index 00000000000..03bcc7e0fb5 --- /dev/null +++ b/adapters/aidem/aidem_test.go @@ -0,0 +1,30 @@ +package aidem + +import ( + "testing" + + "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" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderAidem, config.Adapter{ + Endpoint: "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + }, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "aidemtest", bidder) +} + +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) +} diff --git a/adapters/aidem/aidemtest/exemplary/multi-format.json b/adapters/aidem/aidemtest/exemplary/multi-format.json new file mode 100644 index 00000000000..0c940d4ba59 --- /dev/null +++ b/adapters/aidem/aidemtest/exemplary/multi-format.json @@ -0,0 +1,119 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 320, + "h": 480 + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 320, + "h": 480 + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "aax", + "bid": [ + { + "id": "test-bid-id", + "impid": "1", + "price": 1.50, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 50, + "w": 320, + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "1", + "price": 1.50, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/aidem/aidemtest/exemplary/multi-imps-multi-bid.json b/adapters/aidem/aidemtest/exemplary/multi-imps-multi-bid.json new file mode 100644 index 00000000000..5c2b36948f7 --- /dev/null +++ b/adapters/aidem/aidemtest/exemplary/multi-imps-multi-bid.json @@ -0,0 +1,154 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + }, + { + "id": "2", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + }, + { + "id": "2", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "aax", + "bid": [ + { + "id": "test-bid-id", + "impid": "1", + "price": 1.50, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 50, + "w": 320, + "mtype": 1 + }, + { + "id": "test-bid-id-2", + "impid": "2", + "price": 1.10, + "adm": "some-test-ad-2", + "crid": "test-crid-2", + "h": 50, + "w": 300, + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "1", + "price": 1.50, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + }, + { + "bid": { + "id": "test-bid-id-2", + "impid": "2", + "price": 1.10, + "adm": "some-test-ad-2", + "crid": "test-crid-2", + "w": 300, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/aidem/aidemtest/exemplary/multi-imps-single-bid.json b/adapters/aidem/aidemtest/exemplary/multi-imps-single-bid.json new file mode 100644 index 00000000000..9eae7101a90 --- /dev/null +++ b/adapters/aidem/aidemtest/exemplary/multi-imps-single-bid.json @@ -0,0 +1,131 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + }, + { + "id": "2", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + }, + { + "id": "2", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "aax", + "bid": [ + { + "id": "test-bid-id", + "impid": "1", + "price": 1.50, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 50, + "w": 320, + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "1", + "price": 1.50, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/aidem/aidemtest/exemplary/no-bid.json b/adapters/aidem/aidemtest/exemplary/no-bid.json new file mode 100644 index 00000000000..7418425f10b --- /dev/null +++ b/adapters/aidem/aidemtest/exemplary/no-bid.json @@ -0,0 +1,58 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/aidem/aidemtest/exemplary/optional-params.json b/adapters/aidem/aidemtest/exemplary/optional-params.json new file mode 100644 index 00000000000..69511fb595c --- /dev/null +++ b/adapters/aidem/aidemtest/exemplary/optional-params.json @@ -0,0 +1,60 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234", + "placementId": "ext" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234", + "placementId": "ext" + } + } + } + ] + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/aidem/aidemtest/exemplary/simple-banner.json b/adapters/aidem/aidemtest/exemplary/simple-banner.json new file mode 100644 index 00000000000..73db297ee42 --- /dev/null +++ b/adapters/aidem/aidemtest/exemplary/simple-banner.json @@ -0,0 +1,97 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "banner": { + "format": [ + { + "w": 320, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "aax", + "bid": [ + { + "id": "test-bid-id", + "impid": "1", + "price": 1.50, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 50, + "w": 320, + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "1", + "price": 1.50, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/aidem/aidemtest/exemplary/simple-video.json b/adapters/aidem/aidemtest/exemplary/simple-video.json new file mode 100644 index 00000000000..0daaffaa8cf --- /dev/null +++ b/adapters/aidem/aidemtest/exemplary/simple-video.json @@ -0,0 +1,103 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 320, + "h": 480 + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "1", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 320, + "h": 480 + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "seat": "aax", + "bid": [ + { + "id": "test-bid-id", + "impid": "1", + "price": 2.50, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 320, + "h": 480, + "mtype": 2 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "1", + "price": 2.50, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 320, + "h": 480, + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} 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 new file mode 100644 index 00000000000..3dea13ef7c9 --- /dev/null +++ b/adapters/aidem/aidemtest/supplemental/invalid-req-400-status-code-bad-request.json @@ -0,0 +1,97 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "0000-000-000-0000" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ], + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "user": { + "buyeruid": "0000-000-000-0000" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 400 + } + } + ], + "expectedBidResponses": [], + "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/aidem/aidemtest/supplemental/invalid-res-200-status-code-empty-bids.json b/adapters/aidem/aidemtest/supplemental/invalid-res-200-status-code-empty-bids.json new file mode 100644 index 00000000000..dd64125f467 --- /dev/null +++ b/adapters/aidem/aidemtest/supplemental/invalid-res-200-status-code-empty-bids.json @@ -0,0 +1,102 @@ +{ + "mockBidRequest": { + "id": "some-request-id", + "tmax": 1000, + "user": { + "buyeruid": "0000-000-000-0000" + }, + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "body": { + "id": "some-request-id", + "imp": [ + { + "id": "some-impression-id", + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ], + "app": { + "publisher": { + "id": "123456789" + }, + "cat": [ + "IAB22-1" + ], + "bundle": "com.app.awesome", + "name": "Awesome App", + "domain": "awesomeapp.com", + "id": "123456789" + }, + "user": { + "buyeruid": "0000-000-000-0000" + }, + "tmax": 1000 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "tid", + "seatbid": [], + "bidid": "bid01" + } + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Empty SeatBid array", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/aidem/aidemtest/supplemental/invalid-resp-multi-imp-type.json b/adapters/aidem/aidemtest/supplemental/invalid-resp-multi-imp-type.json new file mode 100644 index 00000000000..95f97d31f72 --- /dev/null +++ b/adapters/aidem/aidemtest/supplemental/invalid-resp-multi-imp-type.json @@ -0,0 +1,151 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "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": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com", + "publisher": { + "domain": "example.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "userAgent", + "ip": "193.168.244.1" + }, + "at": 1, + "tmax": 5000, + "cur": [ + "USD" + ], + "regs": { + "ext": { + "gdpr": 0 + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "video": { + "mimes": [ + "video/mp4" + ], + "w": 640, + "h": 480, + "minduration": 120, + "maxduration": 150 + }, + "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": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com", + "publisher": { + "domain": "example.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "userAgent", + "ip": "193.168.244.1" + }, + "at": 1, + "tmax": 5000, + "cur": [ + "USD" + ], + "regs": { + "ext": { + "gdpr": 0 + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "tid", + "seatbid": [ + { + "seat": "aax", + "bid": [ + { + "id": "randomid", + "impid": "test-imp-id", + "price": 0.500000, + "adid": "12345678", + "adm": "some-test-ad", + "cid": "987", + "crid": "12345678", + "h": 250, + "w": 300 + } + ] + } + ], + "bidid": "bid01" + } + } + } + ], + "expectedBidResponses": [{"currency":"USD","bids":[]}], + "expectedMakeBidsErrors": [ + { + "value": "Unable to fetch mediaType in multi-format: test-imp-id", + "comparison": "literal" + } + ] +} \ No newline at end of file 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 new file mode 100644 index 00000000000..cbd534cd91c --- /dev/null +++ b/adapters/aidem/aidemtest/supplemental/valid-req-200-bid-response-from-aidem.json @@ -0,0 +1,141 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com", + "publisher": { + "domain": "example.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "userAgent", + "ip": "193.168.244.1" + }, + "at": 1, + "tmax": 5000, + "cur": [ + "USD" + ], + "regs": { + "ext": { + "gdpr": 0 + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + } + } + ], + "site": { + "domain": "www.example.com", + "page": "http://www.example.com", + "publisher": { + "domain": "example.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "userAgent", + "ip": "193.168.244.1" + }, + "at": 1, + "tmax": 5000, + "cur": [ + "USD" + ], + "regs": { + "ext": { + "gdpr": 0 + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "tid", + "seatbid": [ + { + "seat": "aax", + "bid": [ + { + "id": "randomid", + "impid": "test-imp-id", + "price": 0.500000, + "adid": "12345678", + "adm": "some-test-ad", + "cid": "987", + "crid": "12345678", + "h": 250, + "w": 300, + "mtype": 1 + } + ] + } + ], + "bidid": "bid01" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "randomid", + "impid": "test-imp-id", + "price": 0.500000, + "adid": "12345678", + "adm": "some-test-ad", + "cid": "987", + "crid": "12345678", + "h": 250, + "w": 300, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file 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 new file mode 100644 index 00000000000..ddd4e5a7735 --- /dev/null +++ b/adapters/aidem/aidemtest/supplemental/valid-req-204-response-from-aidem.json @@ -0,0 +1,66 @@ +{ + "mockBidRequest": { + "app": { + "bundle": "com.example.app" + }, + "id": "req-id", + "device": { + "ifa": "9d8fe0a9-c0dd-4482-b16b-5709b00c608d", + "ip": "1.1.1.1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + }, + "imp": [ + { + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + }, + "banner": { + "w": 320, + "h": 50 + }, + "id": "imp-id" + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakezero.aidemsrv.com/ortb/v2.6/bid/request", + "body": { + "app": { + "bundle": "com.example.app" + }, + "id": "req-id", + "device": { + "ifa": "9d8fe0a9-c0dd-4482-b16b-5709b00c608d", + "ip": "1.1.1.1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + }, + "imp": [ + { + "ext": { + "bidder": { + "siteId": "TCID", + "publisherId": "1234" + } + }, + "banner": { + "w": 320, + "h": 50 + }, + "id": "imp-id" + } + ] + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + "expectedBidResponses": [] +} \ No newline at end of file diff --git a/adapters/aidem/params_test.go b/adapters/aidem/params_test.go new file mode 100644 index 00000000000..36190c0bc9f --- /dev/null +++ b/adapters/aidem/params_test.go @@ -0,0 +1,57 @@ +package aidem + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// This file actually intends to test static/bidder-params/aidem.json TODO: MUST BE CREATED +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.BidderAidem, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected aidem params: %s", validParam) + } + } +} + +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.BidderAidem, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"siteId":"123", "publisherId":"1234"}`, + `{"siteId":"123", "publisherId":"1234", "placementId":"12345"}`, + `{"siteId":"123", "publisherId":"1234", "rateLimit":1}`, + `{"siteId":"123", "publisherId":"1234", "placementId":"12345", "rateLimit":1}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"siteId":"", "publisherId":""}`, + `{"siteId":"only siteId is present"}`, + `{"publisherId":"only publisherId is present"}`, + `{"ssiteId":"123","ppublisherId":"123"}`, + `{"aid":123, "placementId":"123", "siteId":"321"}`, +} diff --git a/adapters/consumable/consumable.go b/adapters/consumable/consumable.go index 9969ea3796e..0e16497774c 100644 --- a/adapters/consumable/consumable.go +++ b/adapters/consumable/consumable.go @@ -40,6 +40,8 @@ type bidRequest struct { Coppa bool `json:"coppa,omitempty"` SChain openrtb2.SupplyChain `json:"schain"` Content *openrtb2.Content `json:"content,omitempty"` + GPP string `json:"gpp,omitempty"` + GPPSID []int8 `json:"gpp_sid,omitempty"` } type placement struct { @@ -192,6 +194,14 @@ func (a *ConsumableAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo * body.Coppa = request.Regs != nil && request.Regs.COPPA > 0 + if request.Regs != nil && request.Regs.GPP != "" { + body.GPP = request.Regs.GPP + } + + if request.Regs != nil && request.Regs.GPPSID != nil { + body.GPPSID = request.Regs.GPPSID + } + if request.Site != nil && request.Site.Content != nil { body.Content = request.Site.Content } else if request.App != nil && request.App.Content != nil { diff --git a/adapters/consumable/consumable/supplemental/simple-banner-gpp.json b/adapters/consumable/consumable/supplemental/simple-banner-gpp.json new file mode 100644 index 00000000000..9b2e3d75031 --- /dev/null +++ b/adapters/consumable/consumable/supplemental/simple-banner-gpp.json @@ -0,0 +1,130 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 250}] + }, + "ext": { + "bidder": { + "networkId": 11, + "siteId": 32, + "unitId": 42 + } + } + } + ], + "device": { + "ua": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36", + "ip": "123.123.123.123" + }, + "site": { + "domain": "www.some.com", + "page": "http://www.some.com/page-where-ad-will-be-shown" + }, + "regs": { + "gpp": "gppString", + "gpp_sid": [7] + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://e.serverbid.com/api/v2", + "headers": { + "Accept": [ + "application/json" + ], + "Content-Type": [ + "application/json" + ], + "User-Agent": [ + "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.91 Safari/537.36" + ], + "X-Forwarded-For": [ + "123.123.123.123" + ], + "Forwarded": [ + "for=123.123.123.123" + ], + "Origin": [ + "http://www.some.com" + ], + "Referer": [ + "http://www.some.com/page-where-ad-will-be-shown" + ] + }, + "body": { + "placements": [ + { + "adTypes": [2730], + "divName": "test-imp-id", + "networkId": 11, + "siteId": 32, + "unitId": 42 + } + ], + "schain": { + "complete": 0, + "nodes": null, + "ver": "" + }, + "networkId": 11, + "siteId": 32, + "unitId": 42, + "time": 1451651415, + "url": "http://www.some.com/page-where-ad-will-be-shown", + "includePricingData": true, + "user":{}, + "enableBotFiltering": true, + "parallel": true, + "gpp": "gppString", + "gpp_sid": [7] + } + }, + "mockResponse": { + "status": 200, + "body": { + "decisions": { + "test-imp-id": { + "adId": 1234567890, + "pricing": { + "clearPrice": 0.5 + }, + "width": 728, + "height": 250, + "impressionUrl": "http://localhost:8080/shown", + "contents" : [ + { + "body": "Remember this: https://www.google.com/search?q=blink+tag ?" + } + ] + } + } + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "test-request-id", + "impid": "test-imp-id", + "price": 0.5, + "adm": "Remember this: https://www.google.com/search?q=blink+tag ?", + "crid": "1234567890", + "exp": 30, + "w": 728, + "h": 250 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/improvedigital/improvedigital.go b/adapters/improvedigital/improvedigital.go index 4c64451f247..b934ac753a0 100644 --- a/adapters/improvedigital/improvedigital.go +++ b/adapters/improvedigital/improvedigital.go @@ -140,6 +140,7 @@ func (a *ImprovedigitalAdapter) MakeBids(internalRequest *openrtb2.BidRequest, e } bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(seatBid.Bid)) + bidResponse.Currency = bidResp.Cur for i := range seatBid.Bid { bid := seatBid.Bid[i] diff --git a/adapters/improvedigital/improvedigitaltest/supplemental/foreign-currency.json b/adapters/improvedigital/improvedigitaltest/supplemental/foreign-currency.json new file mode 100644 index 00000000000..633e8b6b0aa --- /dev/null +++ b/adapters/improvedigital/improvedigitaltest/supplemental/foreign-currency.json @@ -0,0 +1,89 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "cur": ["EUR"], + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + }, + "ext": { + "bidder": { + "placementId": 13245 + } + } + }] + }, + + "httpCalls": [{ + "expectedRequest": { + "uri": "http://localhost/pbs", + "body": { + "id": "test-request-id", + "cur": ["EUR"], + "site": { + "page": "https://good.site/url" + }, + "imp": [{ + "id": "test-imp-id", + "banner": { + "format": [{ + "w": 300, + "h": 250 + }] + }, + "ext": { + "bidder": { + "placementId": 13245 + } + } + }] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "EUR", + "seatbid": [{ + "seat": "improvedigital", + "bid": [{ + "id": "randomid", + "impid": "test-imp-id", + "price": 0.500000, + "adid": "12345678", + "adm": "some-test-ad", + "cid": "987", + "crid": "12345678", + "h": 250, + "w": 300 + }] + }] + } + } + }], + + "expectedBidResponses": [{ + "currency": "EUR", + "bids": [{ + "bid": { + "id": "randomid", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-ad", + "adid": "12345678", + "cid": "987", + "crid": "12345678", + "w": 300, + "h": 250 + }, + "type": "banner" + }] + }] +} diff --git a/adapters/ix/ix.go b/adapters/ix/ix.go index b17d913f42d..796fefbbff0 100644 --- a/adapters/ix/ix.go +++ b/adapters/ix/ix.go @@ -19,8 +19,7 @@ import ( ) type IxAdapter struct { - URI string - maxRequests int + URI string } type ExtRequest struct { @@ -30,108 +29,127 @@ type ExtRequest struct { } type IxDiag struct { - PbsV string `json:"pbsv,omitempty"` - PbjsV string `json:"pbjsv,omitempty"` + PbsV string `json:"pbsv,omitempty"` + PbjsV string `json:"pbjsv,omitempty"` + MultipleSiteIds string `json:"multipleSiteIds,omitempty"` } func (a *IxAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { - nImp := len(request.Imp) - if nImp > a.maxRequests { - request.Imp = request.Imp[:a.maxRequests] - nImp = a.maxRequests - } - + requests := make([]*adapters.RequestData, 0, len(request.Imp)) errs := make([]error, 0) - if err := BuildIxDiag(request); err != nil { - errs = append(errs, err) - } - - // Multi-size banner imps are split into single-size requests. - // The first size imp requests are added to the first slice. - // Additional size requests are added to the second slice and are merged with the first at the end. - // Preallocate the max possible size to avoid reallocating arrays. - requests := make([]*adapters.RequestData, 0, a.maxRequests) - multiSizeRequests := make([]*adapters.RequestData, 0, a.maxRequests-nImp) - headers := http.Header{ "Content-Type": {"application/json;charset=utf-8"}, "Accept": {"application/json"}} - imps := request.Imp - for iImp := range imps { - request.Imp = imps[iImp : iImp+1] - if request.Site != nil { - if err := setSitePublisherId(request, iImp); err != nil { - errs = append(errs, err) - continue - } + uniqueSiteIDs := make(map[string]struct{}) + filteredImps := make([]openrtb2.Imp, 0, len(request.Imp)) + requestCopy := *request + + ixDiag := &IxDiag{} + + for _, imp := range requestCopy.Imp { + var err error + ixExt, err := unmarshalToIxExt(&imp) + + if err != nil { + errs = append(errs, err) + continue } - if request.Imp[0].Banner != nil { - banner := *request.Imp[0].Banner - request.Imp[0].Banner = &banner - formats := getBannerFormats(&banner) - for iFmt := range formats { - banner.Format = formats[iFmt : iFmt+1] - banner.W = openrtb2.Int64Ptr(banner.Format[0].W) - banner.H = openrtb2.Int64Ptr(banner.Format[0].H) - if requestData, err := createRequestData(a, request, &headers); err == nil { - if iFmt == 0 { - requests = append(requests, requestData) - } else { - multiSizeRequests = append(multiSizeRequests, requestData) - } - } else { - errs = append(errs, err) - } - if len(multiSizeRequests) == cap(multiSizeRequests) { - break - } + if err = parseSiteId(ixExt, uniqueSiteIDs); err != nil { + errs = append(errs, err) + continue + } + + if err := moveSid(&imp, ixExt); err != nil { + errs = append(errs, err) + } + + if imp.Banner != nil { + bannerCopy := *imp.Banner + + if len(bannerCopy.Format) == 0 && bannerCopy.W != nil && bannerCopy.H != nil { + bannerCopy.Format = []openrtb2.Format{{W: *bannerCopy.W, H: *bannerCopy.H}} } - } else if requestData, err := createRequestData(a, request, &headers); err == nil { + + if len(bannerCopy.Format) == 1 { + bannerCopy.W = openrtb2.Int64Ptr(bannerCopy.Format[0].W) + bannerCopy.H = openrtb2.Int64Ptr(bannerCopy.Format[0].H) + } + imp.Banner = &bannerCopy + } + filteredImps = append(filteredImps, imp) + } + requestCopy.Imp = filteredImps + + setSitePublisherId(&requestCopy, uniqueSiteIDs, ixDiag) + + err := setIxDiagIntoExtRequest(&requestCopy, ixDiag) + if err != nil { + errs = append(errs, err) + } + + if len(requestCopy.Imp) != 0 { + if requestData, err := createRequestData(a, &requestCopy, &headers); err == nil { requests = append(requests, requestData) } else { errs = append(errs, err) } } - request.Imp = imps - return append(requests, multiSizeRequests...), errs + return requests, errs } -func setSitePublisherId(request *openrtb2.BidRequest, iImp int) error { - if iImp == 0 { - // first impression - create a site and pub copy - site := *request.Site +func setSitePublisherId(requestCopy *openrtb2.BidRequest, uniqueSiteIDs map[string]struct{}, ixDiag *IxDiag) { + if requestCopy.Site != nil { + site := *requestCopy.Site if site.Publisher == nil { site.Publisher = &openrtb2.Publisher{} } else { publisher := *site.Publisher site.Publisher = &publisher } - request.Site = &site + + siteIDs := make([]string, 0, len(uniqueSiteIDs)) + for key := range uniqueSiteIDs { + siteIDs = append(siteIDs, key) + } + if len(siteIDs) == 1 { + site.Publisher.ID = siteIDs[0] + } + if len(siteIDs) > 1 { + // Sorting siteIDs for predictable output as Go maps don't guarantee order + sort.Strings(siteIDs) + multipleSiteIDs := strings.Join(siteIDs, ", ") + ixDiag.MultipleSiteIds = multipleSiteIDs + } + requestCopy.Site = &site } +} +func unmarshalToIxExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpIx, error) { var bidderExt adapters.ExtImpBidder - if err := json.Unmarshal(request.Imp[0].Ext, &bidderExt); err != nil { - return err + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, err } var ixExt openrtb_ext.ExtImpIx if err := json.Unmarshal(bidderExt.Bidder, &ixExt); err != nil { - return err + return nil, err } - request.Site.Publisher.ID = ixExt.SiteId - return nil + return &ixExt, nil } -func getBannerFormats(banner *openrtb2.Banner) []openrtb2.Format { - if len(banner.Format) == 0 && banner.W != nil && banner.H != nil { - banner.Format = []openrtb2.Format{{W: *banner.W, H: *banner.H}} +func parseSiteId(ixExt *openrtb_ext.ExtImpIx, uniqueSiteIDs map[string]struct{}) error { + if ixExt == nil { + return fmt.Errorf("Nil Ix Ext") + } + if ixExt.SiteId != "" { + uniqueSiteIDs[ixExt.SiteId] = struct{}{} } - return banner.Format + return nil } func createRequestData(a *IxAdapter, request *openrtb2.BidRequest, headers *http.Header) (*adapters.RequestData, error) { @@ -180,7 +198,8 @@ func (a *IxAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalReque } } - bidderResponse := adapters.NewBidderResponseWithBidsCapacity(5) + // capacity 0 will make channel unbuffered + bidderResponse := adapters.NewBidderResponseWithBidsCapacity(0) bidderResponse.Currency = bidResponse.Cur var errs []error @@ -275,8 +294,7 @@ func getMediaTypeForBid(bid openrtb2.Bid, impMediaTypeReq map[string]openrtb_ext // Builder builds a new instance of the Ix adapter for the given bidder with the given config. func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { bidder := &IxAdapter{ - URI: config.Endpoint, - maxRequests: 20, + URI: config.Endpoint, } return bidder, nil } @@ -328,29 +346,33 @@ func marshalJsonWithoutUnicode(v interface{}) (string, error) { return strings.TrimSuffix(sb.String(), "\n"), nil } -func BuildIxDiag(request *openrtb2.BidRequest) error { +func setIxDiagIntoExtRequest(request *openrtb2.BidRequest, ixDiag *IxDiag) error { extRequest := &ExtRequest{} if request.Ext != nil { if err := json.Unmarshal(request.Ext, &extRequest); err != nil { return err } } - ixdiag := &IxDiag{} if extRequest.Prebid != nil && extRequest.Prebid.Channel != nil { - ixdiag.PbjsV = extRequest.Prebid.Channel.Version + ixDiag.PbjsV = extRequest.Prebid.Channel.Version } - // Slice commit hash out of version if strings.Contains(version.Ver, "-") { - ixdiag.PbsV = version.Ver[:strings.Index(version.Ver, "-")] + ixDiag.PbsV = version.Ver[:strings.Index(version.Ver, "-")] } else if version.Ver != "" { - ixdiag.PbsV = version.Ver + ixDiag.PbsV = version.Ver } // Only set request.ext if ixDiag is not empty - if *ixdiag != (IxDiag{}) { - extRequest.IxDiag = ixdiag + if *ixDiag != (IxDiag{}) { + extRequest := &ExtRequest{} + if request.Ext != nil { + if err := json.Unmarshal(request.Ext, &extRequest); err != nil { + return err + } + } + extRequest.IxDiag = ixDiag extRequestJson, err := json.Marshal(extRequest) if err != nil { return err @@ -359,3 +381,24 @@ func BuildIxDiag(request *openrtb2.BidRequest) error { } return nil } + +// moves sid from imp[].ext.bidder.sid to imp[].ext.sid +func moveSid(imp *openrtb2.Imp, ixExt *openrtb_ext.ExtImpIx) error { + if ixExt == nil { + return fmt.Errorf("Nil Ix Ext") + } + + if ixExt.Sid != "" { + var m map[string]interface{} + if err := json.Unmarshal(imp.Ext, &m); err != nil { + return err + } + m["sid"] = ixExt.Sid + ext, err := json.Marshal(m) + if err != nil { + return err + } + imp.Ext = ext + } + return nil +} diff --git a/adapters/ix/ix_test.go b/adapters/ix/ix_test.go index 0f6b856dce4..a64e3d0c661 100644 --- a/adapters/ix/ix_test.go +++ b/adapters/ix/ix_test.go @@ -19,8 +19,6 @@ const endpoint string = "http://host/endpoint" func TestJsonSamples(t *testing.T) { if bidder, err := Builder(openrtb_ext.BidderIx, config.Adapter{Endpoint: endpoint}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}); err == nil { - ixBidder := bidder.(*IxAdapter) - ixBidder.maxRequests = 2 adapterstest.RunJSONBidderTest(t, "ixtest", bidder) } else { t.Fatalf("Builder returned unexpected error %v", err) @@ -44,7 +42,7 @@ func TestIxMakeBidsWithCategoryDuration(t *testing.T) { `{ "prebid": {}, "bidder": { - "siteID": 123456 + "siteID": "123456" } }`, )}, @@ -106,7 +104,6 @@ func TestIxMakeBidsWithCategoryDuration(t *testing.T) { func TestIxMakeRequestWithGppString(t *testing.T) { bidder := &IxAdapter{} - bidder.maxRequests = 2 testGppString := "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN" @@ -124,7 +121,7 @@ func TestIxMakeRequestWithGppString(t *testing.T) { `{ "prebid": {}, "bidder": { - "siteID": 123456 + "siteId": "123456" } }`, )}, @@ -256,7 +253,8 @@ func TestBuildIxDiag(t *testing.T) { for _, test := range testCases { t.Run(test.description, func(t *testing.T) { version.Ver = test.pbsVersion - err := BuildIxDiag(test.request) + ixDiag := &IxDiag{} + err := setIxDiagIntoExtRequest(test.request, ixDiag) if test.expectError { assert.NotNil(t, err) } else { diff --git a/adapters/ix/ixtest/exemplary/multi-imp-multi-size-requests.json b/adapters/ix/ixtest/exemplary/multi-imp-multi-size-requests.json new file mode 100644 index 00000000000..4decdaf985d --- /dev/null +++ b/adapters/ix/ixtest/exemplary/multi-imp-multi-size-requests.json @@ -0,0 +1,211 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id-1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + }, + { + "id": "test-imp-id-3", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + } + ], + "site": { + "page": "https://www.example.com/" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://host/endpoint", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id-1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + }, + { + "id": "test-imp-id-3", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ], + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + } + ], + "site": { + "page": "https://www.example.com/", + "publisher": { + "id": "569749" + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [ + { + "id": "7706636740145184841", + "impid": "test-imp-id-1", + "price": 0.5, + "adid": "29681110", + "adm": "some-test-ad", + "adomain": [ + "https://advertiser.example.com" + ], + "cid": "958", + "crid": "29681110", + "h": 600, + "w": 300, + "ext": { + "ix": {} + } + } + ] + } + ], + "bidid": "5778926625248726496", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id-1", + "price": 0.5, + "adm": "some-test-ad", + "adid": "29681110", + "adomain": [ + "https://advertiser.example.com" + ], + "cid": "958", + "crid": "29681110", + "w": 300, + "h": 600, + "ext": { + "ix": {} + } + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/ix/ixtest/exemplary/max-requests.json b/adapters/ix/ixtest/exemplary/multi-imp-requests.json similarity index 69% rename from adapters/ix/ixtest/exemplary/max-requests.json rename to adapters/ix/ixtest/exemplary/multi-imp-requests.json index 46d9ec5d6b7..d8d00aeea69 100644 --- a/adapters/ix/ixtest/exemplary/max-requests.json +++ b/adapters/ix/ixtest/exemplary/multi-imp-requests.json @@ -9,10 +9,6 @@ { "w": 300, "h": 250 - }, - { - "w": 300, - "h": 600 } ] }, @@ -62,6 +58,38 @@ "siteId": "569749" } } + }, + { + "id": "test-imp-id-4", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + }, + { + "id": "test-imp-id-5", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } } ] }, @@ -89,49 +117,7 @@ "siteId": "569749" } } - } - ] - } - }, - "mockResponse": { - "status": 200, - "body": { - "id": "test-request-id", - "seatbid": [ - { - "seat": "958", - "bid": [ - { - "id": "7706636740145184841", - "impid": "test-imp-id-1", - "price": 0.5, - "adid": "29681110", - "adm": "some-test-ad", - "adomain": [ - "https://advertiser.example.com" - ], - "cid": "958", - "crid": "29681110", - "h": 250, - "w": 300, - "ext": { - "ix": {} - } - } - ] - } - ], - "bidid": "5778926625248726496", - "cur": "USD" - } - } - }, - { - "expectedRequest": { - "uri": "http://host/endpoint", - "body": { - "id": "test-request-id", - "imp": [ + }, { "id": "test-imp-id-2", "video": { @@ -156,6 +142,60 @@ "siteId": "569749" } } + }, + { + "banner": { + "format": [ + { + "h": 600, + "w": 300 + } + ], + "h": 600, + "w": 300 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + }, + "id": "test-imp-id-3" + }, + { + "id": "test-imp-id-4", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + }, + { + "id": "test-imp-id-5", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } } ] } @@ -170,7 +210,7 @@ "bid": [ { "id": "7706636740145184841", - "impid": "test-imp-id-2", + "impid": "test-imp-id-1", "price": 0.5, "adid": "29681110", "adm": "some-test-ad", @@ -181,9 +221,6 @@ "crid": "29681110", "h": 250, "w": 300, - "cat": [ - "IAB9-1" - ], "ext": { "ix": {} } @@ -222,34 +259,6 @@ "type": "banner" } ] - }, - { - "currency": "USD", - "bids": [ - { - "bid": { - "id": "7706636740145184841", - "impid": "test-imp-id-2", - "price": 0.5, - "adm": "some-test-ad", - "adid": "29681110", - "adomain": [ - "https://advertiser.example.com" - ], - "cid": "958", - "crid": "29681110", - "w": 300, - "h": 250, - "cat": [ - "IAB9-1" - ], - "ext": { - "ix": {} - } - }, - "type": "video" - } - ] } ] } diff --git a/adapters/ix/ixtest/exemplary/multiple-siteIds.json b/adapters/ix/ixtest/exemplary/multiple-siteIds.json new file mode 100644 index 00000000000..7f4227ac4b2 --- /dev/null +++ b/adapters/ix/ixtest/exemplary/multiple-siteIds.json @@ -0,0 +1,224 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id-1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "siteId": "569750" + } + } + }, + { + "id": "test-imp-id-3", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "siteId": "569751" + } + } + } + ], + "site": { + "page": "https://www.example.com/" + }, + "ext": { + "prebid": { + "channel": { + "name": "web", + "version": "7.0.0" + } + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://host/endpoint", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id-1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "siteId": "569750" + } + } + }, + { + "banner": { + "format": [ + { + "h": 600, + "w": 300 + } + ], + "h": 600, + "w": 300 + }, + "ext": { + "bidder": { + "siteId": "569751" + } + }, + "id": "test-imp-id-3" + } + ], + "site": { + "page": "https://www.example.com/", + "publisher": { + } + }, + "ext": { + "ixdiag": { + "multipleSiteIds": "569749, 569750, 569751", + "pbjsv": "7.0.0" + }, + "prebid": { + "channel": { + "name": "web", + "version": "7.0.0" + } + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [ + { + "id": "7706636740145184841", + "impid": "test-imp-id-1", + "price": 0.5, + "adid": "29681110", + "adm": "some-test-ad", + "adomain": [ + "https://advertiser.example.com" + ], + "cid": "958", + "crid": "29681110", + "h": 250, + "w": 300, + "ext": { + "ix": {} + } + } + ] + } + ], + "bidid": "5778926625248726496", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id-1", + "price": 0.5, + "adm": "some-test-ad", + "adid": "29681110", + "adomain": [ + "https://advertiser.example.com" + ], + "cid": "958", + "crid": "29681110", + "w": 300, + "h": 250, + "ext": { + "ix": {} + } + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/ix/ixtest/exemplary/simple-banner-multi-size.json b/adapters/ix/ixtest/exemplary/simple-banner-multi-size.json index 5e0a311a91b..15dc3ecbb0c 100644 --- a/adapters/ix/ixtest/exemplary/simple-banner-multi-size.json +++ b/adapters/ix/ixtest/exemplary/simple-banner-multi-size.json @@ -13,6 +13,10 @@ { "w": 300, "h": 600 + }, + { + "w": 600, + "h": 800 } ] }, @@ -22,7 +26,10 @@ } } } - ] + ], + "site": { + "page": "https://www.example.com/" + } }, "httpCalls": [ { @@ -38,70 +45,16 @@ { "w": 300, "h": 250 - } - ], - "w": 300, - "h": 250 - }, - "ext": { - "bidder": { - "siteId": "569749" - } - } - } - ] - } - }, - "mockResponse": { - "status": 200, - "body": { - "id": "test-request-id", - "seatbid": [ - { - "seat": "958", - "bid": [ - { - "id": "7706636740145184841", - "impid": "test-imp-id", - "price": 0.5, - "adid": "29681110", - "adm": "some-test-ad", - "adomain": [ - "https://advertiser.example.com" - ], - "cid": "958", - "crid": "29681110", - "h": 250, - "w": 300, - "ext": { - "ix": {} - } - } - ] - } - ], - "bidid": "5778926625248726496", - "cur": "USD" - } - } - }, - { - "expectedRequest": { - "uri": "http://host/endpoint", - "body": { - "id": "test-request-id", - "imp": [ - { - "id": "test-imp-id", - "banner": { - "format": [ + }, { "w": 300, "h": 600 + }, + { + "w": 600, + "h": 800 } - ], - "w": 300, - "h": 600 + ] }, "ext": { "bidder": { @@ -109,7 +62,13 @@ } } } - ] + ], + "site": { + "page": "https://www.example.com/", + "publisher": { + "id": "569749" + } + } } }, "mockResponse": { @@ -131,7 +90,7 @@ ], "cid": "958", "crid": "29681110", - "h": 600, + "h": 250, "w": 300, "ext": { "ix": {} @@ -171,31 +130,6 @@ "type": "banner" } ] - }, - { - "currency": "USD", - "bids": [ - { - "bid": { - "id": "7706636740145184841", - "impid": "test-imp-id", - "price": 0.5, - "adm": "some-test-ad", - "adid": "29681110", - "adomain": [ - "https://advertiser.example.com" - ], - "cid": "958", - "crid": "29681110", - "w": 300, - "h": 600, - "ext": { - "ix": {} - } - }, - "type": "banner" - } - ] } ] } diff --git a/adapters/ix/ixtest/exemplary/structured-pod.json b/adapters/ix/ixtest/exemplary/structured-pod.json new file mode 100644 index 00000000000..a5ca9895554 --- /dev/null +++ b/adapters/ix/ixtest/exemplary/structured-pod.json @@ -0,0 +1,236 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id-1", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560, + "podid": "1", + "slotinpod": 1, + "podseq": 1 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560, + "podid": "1", + "slotinpod": -1, + "podseq": 1 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://host/endpoint", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id-1", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560, + "podid": "1", + "slotinpod": 1, + "podseq": 1 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560, + "podid": "1", + "slotinpod": -1, + "podseq": 1 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [ + { + "id": "7706636740145184841", + "impid": "test-imp-id-1", + "price": 0.5, + "adid": "29681110", + "adm": "some-test-ad", + "adomain": [ + "https://advertiser.example.com" + ], + "cid": "958", + "crid": "29681110", + "h": 560, + "w": 940, + "dur": 30, + "ext": { + "ix": {} + } + } + ] + }, + { + "seat": "958", + "bid": [ + { + "id": "7706636740145184841", + "impid": "test-imp-id-2", + "price": 0.75, + "adid": "29681110", + "adm": "some-test-ad", + "adomain": [ + "https://advertiser.example.com" + ], + "cid": "958", + "crid": "29681110", + "h": 560, + "w": 940, + "dur": 30, + "ext": { + "ix": {} + } + } + ] + } + ], + "bidid": "5778926625248726496", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id-1", + "price": 0.5, + "adm": "some-test-ad", + "adid": "29681110", + "adomain": [ + "https://advertiser.example.com" + ], + "cid": "958", + "crid": "29681110", + "h": 560, + "w": 940, + "dur": 30, + "ext": { + "ix": {} + } + }, + "type": "video" + }, + { + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id-2", + "price": 0.75, + "adm": "some-test-ad", + "adid": "29681110", + "adomain": [ + "https://advertiser.example.com" + ], + "cid": "958", + "crid": "29681110", + "h": 560, + "w": 940, + "dur": 30, + "ext": { + "ix": {} + } + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/ix/ixtest/supplemental/multi-imp-requests-error.json b/adapters/ix/ixtest/supplemental/multi-imp-requests-error.json new file mode 100644 index 00000000000..223e863284c --- /dev/null +++ b/adapters/ix/ixtest/supplemental/multi-imp-requests-error.json @@ -0,0 +1,189 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id-1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "siteId": "569749", + "sid": 12345 + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + }, + { + "id": "test-imp-id-3", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://host/endpoint", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + }, + { + "id": "test-imp-id-3", + "banner": { + "format": [ + { + "w": 300, + "h": 600 + } + ], + "w": 300, + "h": 600 + }, + "ext": { + "bidder": { + "siteId": "569749" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [ + { + "id": "7706636740145184841", + "impid": "test-imp-id-1", + "price": 0.5, + "adid": "29681110", + "adm": "some-test-ad", + "adomain": [ + "https://advertiser.example.com" + ], + "cid": "958", + "crid": "29681110", + "h": 600, + "w": 300, + "ext": { + "ix": {} + } + } + ] + } + ], + "bidid": "5778926625248726496", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id-1", + "price": 0.5, + "adm": "some-test-ad", + "adid": "29681110", + "adomain": [ + "https://advertiser.example.com" + ], + "cid": "958", + "crid": "29681110", + "w": 300, + "h": 600, + "ext": { + "ix": {} + } + }, + "type": "banner" + } + ] + } + ], + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal number into Go struct field ExtImpIx.sid of type string", + "comparison": "literal" + } + ] +} diff --git a/adapters/ix/ixtest/supplemental/sid.json b/adapters/ix/ixtest/supplemental/sid.json new file mode 100644 index 00000000000..c2156b6b20d --- /dev/null +++ b/adapters/ix/ixtest/supplemental/sid.json @@ -0,0 +1,184 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id-1", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "siteId": "569749", + "sid": "1234" + } + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "siteId": "569750", + "sid": "5678" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://host/endpoint", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id-1", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "siteId": "569749", + "sid": "1234" + }, + "sid": "1234" + } + }, + { + "id": "test-imp-id-2", + "video": { + "mimes": [ + "video/mp4" + ], + "minduration": 15, + "maxduration": 30, + "protocols": [ + 2, + 3, + 5, + 6, + 7, + 8 + ], + "w": 940, + "h": 560 + }, + "ext": { + "bidder": { + "siteId": "569750", + "sid": "5678" + }, + "sid": "5678" + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "958", + "bid": [ + { + "id": "7706636740145184841", + "impid": "test-imp-id-1", + "price": 0.5, + "adid": "29681110", + "adm": "some-test-ad", + "adomain": [ + "https://advertiser.example.com" + ], + "cid": "958", + "crid": "29681110", + "h": 250, + "w": 300, + "ext": { + "ix": {} + } + } + ] + } + ], + "bidid": "5778926625248726496", + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "7706636740145184841", + "impid": "test-imp-id-1", + "price": 0.5, + "adm": "some-test-ad", + "adid": "29681110", + "adomain": [ + "https://advertiser.example.com" + ], + "cid": "958", + "crid": "29681110", + "w": 300, + "h": 250, + "ext": { + "ix": {} + } + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/ix/params_test.go b/adapters/ix/params_test.go index 9246a43a725..8ba937c12f4 100644 --- a/adapters/ix/params_test.go +++ b/adapters/ix/params_test.go @@ -38,6 +38,7 @@ var validParams = []string{ `{"siteID":"12345"}`, `{"siteId":"123456"}`, `{"siteid":"1234567", "size": [640,480]}`, + `{"siteId":"123456", "sid":"12345"}`, } var invalidParams = []string{ diff --git a/adapters/pwbid/pwbid_test.go b/adapters/pwbid/pwbid_test.go index 21a4194249e..194e4bdea02 100644 --- a/adapters/pwbid/pwbid_test.go +++ b/adapters/pwbid/pwbid_test.go @@ -10,7 +10,7 @@ import ( func TestJsonSamples(t *testing.T) { bidder, buildErr := Builder(openrtb_ext.BidderPWBid, config.Adapter{ - Endpoint: "https://bid.pubwise.io/prebid"}, + Endpoint: "https://bidder.east2.pubwise.io/bid/pubwisedirect"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 842, DataCenter: "2"}) if buildErr != nil { diff --git a/adapters/pwbid/pwbidtest/exemplary/banner.json b/adapters/pwbid/pwbidtest/exemplary/banner.json index ba618cb8cf1..4cf93e1ab76 100644 --- a/adapters/pwbid/pwbidtest/exemplary/banner.json +++ b/adapters/pwbid/pwbidtest/exemplary/banner.json @@ -23,7 +23,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bid.pubwise.io/prebid", + "uri": "https://bidder.east2.pubwise.io/bid/pubwisedirect", "body": { "id": "test-request-id-banner", "imp": [ @@ -90,4 +90,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/adapters/pwbid/pwbidtest/exemplary/native.json b/adapters/pwbid/pwbidtest/exemplary/native.json index 907c16d467a..ff57752c5ea 100644 --- a/adapters/pwbid/pwbidtest/exemplary/native.json +++ b/adapters/pwbid/pwbidtest/exemplary/native.json @@ -18,7 +18,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bid.pubwise.io/prebid", + "uri": "https://bidder.east2.pubwise.io/bid/pubwisedirect", "body": { "id": "test-request-id-native", "imp": [ @@ -78,4 +78,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/adapters/pwbid/pwbidtest/exemplary/optional-params.json b/adapters/pwbid/pwbidtest/exemplary/optional-params.json index a080be90208..5ababb24bdc 100644 --- a/adapters/pwbid/pwbidtest/exemplary/optional-params.json +++ b/adapters/pwbid/pwbidtest/exemplary/optional-params.json @@ -25,7 +25,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bid.pubwise.io/prebid", + "uri": "https://bidder.east2.pubwise.io/bid/pubwisedirect", "body": { "id": "test-request-id-banner", "imp": [ @@ -94,4 +94,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/adapters/pwbid/pwbidtest/exemplary/video.json b/adapters/pwbid/pwbidtest/exemplary/video.json index b74c780d0a9..6257de632d4 100644 --- a/adapters/pwbid/pwbidtest/exemplary/video.json +++ b/adapters/pwbid/pwbidtest/exemplary/video.json @@ -20,7 +20,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bid.pubwise.io/prebid", + "uri": "https://bidder.east2.pubwise.io/bid/pubwisedirect", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/pwbid/pwbidtest/supplemental/response-200-without-body.json b/adapters/pwbid/pwbidtest/supplemental/response-200-without-body.json index 146ba93a27d..0d469893e0c 100644 --- a/adapters/pwbid/pwbidtest/supplemental/response-200-without-body.json +++ b/adapters/pwbid/pwbidtest/supplemental/response-200-without-body.json @@ -19,7 +19,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bid.pubwise.io/prebid", + "uri": "https://bidder.east2.pubwise.io/bid/pubwisedirect", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/pwbid/pwbidtest/supplemental/response-204.json b/adapters/pwbid/pwbidtest/supplemental/response-204.json index 5fff7ee32cc..4fc8961e0bb 100644 --- a/adapters/pwbid/pwbidtest/supplemental/response-204.json +++ b/adapters/pwbid/pwbidtest/supplemental/response-204.json @@ -19,7 +19,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bid.pubwise.io/prebid", + "uri": "https://bidder.east2.pubwise.io/bid/pubwisedirect", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/pwbid/pwbidtest/supplemental/response-400.json b/adapters/pwbid/pwbidtest/supplemental/response-400.json index d594e571243..a1517883243 100644 --- a/adapters/pwbid/pwbidtest/supplemental/response-400.json +++ b/adapters/pwbid/pwbidtest/supplemental/response-400.json @@ -19,7 +19,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bid.pubwise.io/prebid", + "uri": "https://bidder.east2.pubwise.io/bid/pubwisedirect", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/pwbid/pwbidtest/supplemental/response-500.json b/adapters/pwbid/pwbidtest/supplemental/response-500.json index fa3d4d063a8..c2b5649418b 100644 --- a/adapters/pwbid/pwbidtest/supplemental/response-500.json +++ b/adapters/pwbid/pwbidtest/supplemental/response-500.json @@ -19,7 +19,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bid.pubwise.io/prebid", + "uri": "https://bidder.east2.pubwise.io/bid/pubwisedirect", "body": { "id": "test-request-id", "imp": [ diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index f797bfe43d9..009ce0aa1d5 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -83,21 +83,29 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.E requestCopy.BCat = append(requestCopy.BCat, strImpParams.BCat...) requestCopy.BAdv = append(requestCopy.BAdv, strImpParams.BAdv...) - requestCopy.Imp = []openrtb2.Imp{imp} - - requestJSON, err := json.Marshal(requestCopy) + impressionsByMediaType, err := splitImpressionsByMediaType(&imp) if err != nil { errors = append(errors, err) continue } - requestData := &adapters.RequestData{ - Method: "POST", - Uri: a.endpoint, - Body: requestJSON, - Headers: headers, + for _, impression := range impressionsByMediaType { + requestCopy.Imp = []openrtb2.Imp{impression} + + requestJSON, err := json.Marshal(requestCopy) + if err != nil { + errors = append(errors, err) + continue + } + + requestData := &adapters.RequestData{ + Method: "POST", + Uri: a.endpoint, + Body: requestJSON, + Headers: headers, + } + requests = append(requests, requestData) } - requests = append(requests, requestData) } return requests, errors @@ -150,6 +158,40 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.R return bidderResponse, errors } +func splitImpressionsByMediaType(impression *openrtb2.Imp) ([]openrtb2.Imp, error) { + if impression.Banner == nil && impression.Video == nil && impression.Native == nil { + return nil, &errortypes.BadInput{Message: "Invalid MediaType. Sharethrough only supports Banner, Video and Native."} + } + + if impression.Audio != nil { + impression.Audio = nil + } + + impressions := make([]openrtb2.Imp, 0, 3) + + if impression.Banner != nil { + impCopy := *impression + impCopy.Video = nil + impCopy.Native = nil + impressions = append(impressions, impCopy) + } + + if impression.Video != nil { + impCopy := *impression + impCopy.Banner = nil + impCopy.Native = nil + impressions = append(impressions, impCopy) + } + + if impression.Native != nil { + impression.Banner = nil + impression.Video = nil + impressions = append(impressions, *impression) + } + + return impressions, nil +} + func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { if bid.Ext != nil { diff --git a/adapters/sharethrough/sharethroughtest/supplemental/multiformat-impression.json b/adapters/sharethrough/sharethroughtest/supplemental/multiformat-impression.json new file mode 100644 index 00000000000..fcebf79acc8 --- /dev/null +++ b/adapters/sharethrough/sharethroughtest/supplemental/multiformat-impression.json @@ -0,0 +1,362 @@ +{ + "mockBidRequest": { + "id": "parent-id", + "tmax": 3000, + "imp": [ + { + "id": "impression-id", + "ext": { + "bidder": { + "pkey": "pkey1" + } + }, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "placement": 1 + }, + "native": { + "ver": "1.2", + "request": "placeholder request" + }, + "audio": { + "mimes": [ + "audio/mp4" + ], + "protocols": [ + 1, + 2 + ] + } + } + ], + "site": { + "publisher": { + "id": "1" + }, + "page": "https://some-site.com", + "ref": "https://some-site.com" + }, + "device": { + "w": 1200, + "h": 900 + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://whatever.url", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "parent-id", + "tmax": 3000, + "imp": [ + { + "id": "impression-id", + "tagid": "pkey1", + "ext": { + "bidder": { + "pkey": "pkey1" + } + }, + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + } + } + ], + "site": { + "publisher": { + "id": "1" + }, + "page": "https://some-site.com", + "ref": "https://some-site.com" + }, + "device": { + "w": 1200, + "h": 900 + }, + "source": { + "ext": { + "version": "", + "str": "10.0" + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "parent-id", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "parent-id", + "impid": "impression-id", + "crid": "some-creative-id", + "adm": "
Ad
", + "price": 20, + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + } + ] + } + ] + } + } + }, + { + "expectedRequest": { + "uri": "http://whatever.url", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "parent-id", + "tmax": 3000, + "imp": [ + { + "id": "impression-id", + "tagid": "pkey1", + "ext": { + "bidder": { + "pkey": "pkey1" + } + }, + "video": { + "w": 640, + "h": 480, + "mimes": [ + "video/mp4" + ], + "placement": 1 + } + } + ], + "site": { + "publisher": { + "id": "1" + }, + "page": "https://some-site.com", + "ref": "https://some-site.com" + }, + "device": { + "w": 1200, + "h": 900 + }, + "source": { + "ext": { + "version": "", + "str": "10.0" + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "parent-id", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "parent-id", + "impid": "impression-id", + "crid": "some-creative-id", + "adm": "TAG", + "price": 20, + "w": 640, + "h": 480, + "ext": { + "prebid": { + "type": "video" + } + } + } + ] + } + ] + } + } + }, + { + "expectedRequest": { + "uri": "http://whatever.url", + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ] + }, + "body": { + "id": "parent-id", + "tmax": 3000, + "imp": [ + { + "id": "impression-id", + "tagid": "pkey1", + "ext": { + "bidder": { + "pkey": "pkey1" + } + }, + "native": { + "ver": "1.2", + "request": "placeholder request" + } + } + ], + "site": { + "publisher": { + "id": "1" + }, + "page": "https://some-site.com", + "ref": "https://some-site.com" + }, + "device": { + "w": 1200, + "h": 900 + }, + "source": { + "ext": { + "version": "", + "str": "10.0" + } + } + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "parent-id", + "cur": "USD", + "seatbid": [ + { + "bid": [ + { + "id": "parent-id", + "impid": "impression-id", + "crid": "some-creative-id", + "adm": "
Ad
", + "price": 20, + "w": 640, + "h": 480, + "ext": { + "prebid": { + "type": "native" + } + } + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "parent-id", + "impid": "impression-id", + "crid": "some-creative-id", + "adm": "
Ad
", + "price": 20, + "w": 300, + "h": 250, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + } + ] + }, + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "parent-id", + "impid": "impression-id", + "crid": "some-creative-id", + "adm": "TAG", + "price": 20, + "w": 640, + "h": 480, + "ext": { + "prebid": { + "type": "video" + } + } + }, + "type": "video" + } + ] + }, + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "parent-id", + "impid": "impression-id", + "crid": "some-creative-id", + "adm": "
Ad
", + "price": 20, + "w": 640, + "h": 480, + "ext": { + "prebid": { + "type": "native" + } + } + }, + "type": "native" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/yahooAdvertising/params_test.go b/adapters/yahooAds/params_test.go similarity index 63% rename from adapters/yahooAdvertising/params_test.go rename to adapters/yahooAds/params_test.go index 10d504cb291..c0deaaa32c9 100644 --- a/adapters/yahooAdvertising/params_test.go +++ b/adapters/yahooAds/params_test.go @@ -1,4 +1,4 @@ -package yahooAdvertising +package yahooAds import ( "encoding/json" @@ -7,11 +7,11 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" ) -// This file actually intends to test static/bidder-params/yahooAdvertising.json +// This file actually intends to test static/bidder-params/yahooAds.json // -// These also validate the format of the external API: request.imp[i].ext.yahooAdvertising +// These also validate the format of the external API: request.imp[i].ext.yahooAds -// TestValidParams makes sure that the yahooAdvertising schema accepts all imp.ext fields which we intend to support. +// TestValidParams makes sure that the yahooAds schema accepts all imp.ext fields which we intend to support. func TestValidParams(t *testing.T) { validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") if err != nil { @@ -19,13 +19,13 @@ func TestValidParams(t *testing.T) { } for _, validParam := range validParams { - if err := validator.Validate(openrtb_ext.BidderYahooAdvertising, json.RawMessage(validParam)); err != nil { - t.Errorf("Schema rejected yahooAdvertising params: %s", validParam) + if err := validator.Validate(openrtb_ext.BidderYahooAds, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected yahooAds params: %s", validParam) } } } -// TestInvalidParams makes sure that the yahooAdvertising schema rejects all the imp.ext fields we don't support. +// TestInvalidParams makes sure that the yahooAds 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 { @@ -33,7 +33,7 @@ func TestInvalidParams(t *testing.T) { } for _, invalidParam := range invalidParams { - if err := validator.Validate(openrtb_ext.BidderYahooAdvertising, json.RawMessage(invalidParam)); err == nil { + if err := validator.Validate(openrtb_ext.BidderYahooAds, json.RawMessage(invalidParam)); err == nil { t.Errorf("Schema allowed unexpected params: %s", invalidParam) } } diff --git a/adapters/yahooAdvertising/yahooAdvertising.go b/adapters/yahooAds/yahooAds.go similarity index 93% rename from adapters/yahooAdvertising/yahooAdvertising.go rename to adapters/yahooAds/yahooAds.go index d476adb4e8e..3597d0e359c 100644 --- a/adapters/yahooAdvertising/yahooAdvertising.go +++ b/adapters/yahooAds/yahooAds.go @@ -1,4 +1,4 @@ -package yahooAdvertising +package yahooAds import ( "encoding/json" @@ -41,8 +41,8 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.E continue } - var yahooAdvertisingExt openrtb_ext.ExtImpYahooAdvertising - err = json.Unmarshal(bidderExt.Bidder, &yahooAdvertisingExt) + var yahooAdsExt openrtb_ext.ExtImpYahooAds + err = json.Unmarshal(bidderExt.Bidder, &yahooAdsExt) if err != nil { err = &errortypes.BadInput{ Message: fmt.Sprintf("imp #%d: %s", idx, err.Error()), @@ -64,7 +64,7 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.E reqCopy.App = &appCopy } - if err := changeRequestForBidService(&reqCopy, &yahooAdvertisingExt); err != nil { + if err := changeRequestForBidService(&reqCopy, &yahooAdsExt); err != nil { errors = append(errors, err) continue } @@ -150,7 +150,7 @@ func getImpInfo(impId string, imps []openrtb2.Imp) (bool, openrtb_ext.BidType) { return exists, mediaType } -func changeRequestForBidService(request *openrtb2.BidRequest, extension *openrtb_ext.ExtImpYahooAdvertising) error { +func changeRequestForBidService(request *openrtb2.BidRequest, extension *openrtb_ext.ExtImpYahooAds) error { /* Always override the tag ID and (site ID or app ID) of the request */ request.Imp[0].TagID = extension.Pos if request.Site != nil { @@ -218,7 +218,7 @@ func validateBanner(banner *openrtb2.Banner) error { return nil } -// Builder builds a new instance of the YahooAdvertising adapter for the given bidder with the given config. +// Builder builds a new instance of the YahooAds adapter for the given bidder with the given config. func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { bidder := &adapter{ URI: config.Endpoint, diff --git a/adapters/yahooAdvertising/yahooAdvertising_test.go b/adapters/yahooAds/yahooAds_test.go similarity index 51% rename from adapters/yahooAdvertising/yahooAdvertising_test.go rename to adapters/yahooAds/yahooAds_test.go index e701c3335c1..924eabd5ec1 100644 --- a/adapters/yahooAdvertising/yahooAdvertising_test.go +++ b/adapters/yahooAds/yahooAds_test.go @@ -1,4 +1,4 @@ -package yahooAdvertising +package yahooAds import ( "testing" @@ -10,8 +10,8 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" ) -func TestYahooAdvertisingBidderEndpointConfig(t *testing.T) { - bidder, buildErr := Builder(openrtb_ext.BidderYahooAdvertising, config.Adapter{ +func TestYahooAdsBidderEndpointConfig(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderYahooAds, config.Adapter{ Endpoint: "http://localhost/bid", }, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) @@ -19,17 +19,17 @@ func TestYahooAdvertisingBidderEndpointConfig(t *testing.T) { t.Fatalf("Builder returned unexpected error %v", buildErr) } - bidderYahooAdvertising := bidder.(*adapter) + bidderYahooAds := bidder.(*adapter) - assert.Equal(t, "http://localhost/bid", bidderYahooAdvertising.URI) + assert.Equal(t, "http://localhost/bid", bidderYahooAds.URI) } func TestJsonSamples(t *testing.T) { - bidder, buildErr := Builder(openrtb_ext.BidderYahooAdvertising, config.Adapter{}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + bidder, buildErr := Builder(openrtb_ext.BidderYahooAds, config.Adapter{}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) if buildErr != nil { t.Fatalf("Builder returned unexpected error %v", buildErr) } - adapterstest.RunJSONBidderTest(t, "yahooAdvertisingtest", bidder) + adapterstest.RunJSONBidderTest(t, "yahooAdstest", bidder) } diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/exemplary/simple-app-banner.json b/adapters/yahooAds/yahooAdstest/exemplary/simple-app-banner.json similarity index 98% rename from adapters/yahooAdvertising/yahooAdvertisingtest/exemplary/simple-app-banner.json rename to adapters/yahooAds/yahooAdstest/exemplary/simple-app-banner.json index d5e77c71d3d..7ad41161915 100644 --- a/adapters/yahooAdvertising/yahooAdvertisingtest/exemplary/simple-app-banner.json +++ b/adapters/yahooAds/yahooAdstest/exemplary/simple-app-banner.json @@ -77,7 +77,7 @@ "id": "test-request-id", "seatbid": [ { - "seat": "yahooAdvertising", + "seat": "yahooAds", "bid": [{ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", "impid": "test-imp-id", diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/exemplary/simple-banner.json b/adapters/yahooAds/yahooAdstest/exemplary/simple-banner.json similarity index 98% rename from adapters/yahooAdvertising/yahooAdvertisingtest/exemplary/simple-banner.json rename to adapters/yahooAds/yahooAdstest/exemplary/simple-banner.json index d08bdf06019..7036664d4ad 100644 --- a/adapters/yahooAdvertising/yahooAdvertisingtest/exemplary/simple-banner.json +++ b/adapters/yahooAds/yahooAdstest/exemplary/simple-banner.json @@ -77,7 +77,7 @@ "id": "test-request-id", "seatbid": [ { - "seat": "yahooAdvertising", + "seat": "yahooAds", "bid": [{ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", "impid": "test-imp-id", diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/exemplary/simple-video.json b/adapters/yahooAds/yahooAdstest/exemplary/simple-video.json similarity index 98% rename from adapters/yahooAdvertising/yahooAdvertisingtest/exemplary/simple-video.json rename to adapters/yahooAds/yahooAdstest/exemplary/simple-video.json index ea95ec40306..ebf7af93d53 100644 --- a/adapters/yahooAdvertising/yahooAdvertisingtest/exemplary/simple-video.json +++ b/adapters/yahooAds/yahooAdstest/exemplary/simple-video.json @@ -92,7 +92,7 @@ "id": "test-request-id", "seatbid": [ { - "seat": "yahooAdvertising", + "seat": "yahooAds", "bid": [{ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", "impid": "test-imp-id", diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/empty-banner-format.json b/adapters/yahooAds/yahooAdstest/supplemental/empty-banner-format.json similarity index 100% rename from adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/empty-banner-format.json rename to adapters/yahooAds/yahooAdstest/supplemental/empty-banner-format.json diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/invalid-banner-height.json b/adapters/yahooAds/yahooAdstest/supplemental/invalid-banner-height.json similarity index 100% rename from adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/invalid-banner-height.json rename to adapters/yahooAds/yahooAdstest/supplemental/invalid-banner-height.json diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/invalid-banner-width.json b/adapters/yahooAds/yahooAdstest/supplemental/invalid-banner-width.json similarity index 100% rename from adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/invalid-banner-width.json rename to adapters/yahooAds/yahooAdstest/supplemental/invalid-banner-width.json diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/non-supported-requests-bids-ignored.json b/adapters/yahooAds/yahooAdstest/supplemental/non-supported-requests-bids-ignored.json similarity index 98% rename from adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/non-supported-requests-bids-ignored.json rename to adapters/yahooAds/yahooAdstest/supplemental/non-supported-requests-bids-ignored.json index a44020daf3f..c0d77fa496b 100644 --- a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/non-supported-requests-bids-ignored.json +++ b/adapters/yahooAds/yahooAdstest/supplemental/non-supported-requests-bids-ignored.json @@ -74,7 +74,7 @@ "id": "test-request-id", "seatbid": [ { - "seat": "yahooAdvertising", + "seat": "yahooAds", "bid": [{ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", "impid": "test-imp-id", diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/required-nobidder-info.json b/adapters/yahooAds/yahooAdstest/supplemental/required-nobidder-info.json similarity index 100% rename from adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/required-nobidder-info.json rename to adapters/yahooAds/yahooAdstest/supplemental/required-nobidder-info.json diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/server-error.json b/adapters/yahooAds/yahooAdstest/supplemental/server-error.json similarity index 100% rename from adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/server-error.json rename to adapters/yahooAds/yahooAdstest/supplemental/server-error.json diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/server-response-wrong-impid.json b/adapters/yahooAds/yahooAdstest/supplemental/server-response-wrong-impid.json similarity index 98% rename from adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/server-response-wrong-impid.json rename to adapters/yahooAds/yahooAdstest/supplemental/server-response-wrong-impid.json index 1fcf61e12b7..f40819497a8 100644 --- a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/server-response-wrong-impid.json +++ b/adapters/yahooAds/yahooAdstest/supplemental/server-response-wrong-impid.json @@ -76,7 +76,7 @@ "id": "test-request-id", "seatbid": [ { - "seat": "yahooAdvertising", + "seat": "yahooAds", "bid": [{ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", "impid": "wrong", diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/simple-banner-gpp-overwrite.json b/adapters/yahooAds/yahooAdstest/supplemental/simple-banner-gpp-overwrite.json similarity index 98% rename from adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/simple-banner-gpp-overwrite.json rename to adapters/yahooAds/yahooAdstest/supplemental/simple-banner-gpp-overwrite.json index 190f96bf0a6..94c895b996d 100644 --- a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/simple-banner-gpp-overwrite.json +++ b/adapters/yahooAds/yahooAdstest/supplemental/simple-banner-gpp-overwrite.json @@ -92,7 +92,7 @@ "id": "test-request-id", "seatbid": [ { - "seat": "yahooAdvertising", + "seat": "yahooAds", "bid": [{ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", "impid": "test-imp-id", diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/simple-banner-gpp.json b/adapters/yahooAds/yahooAdstest/supplemental/simple-banner-gpp.json similarity index 98% rename from adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/simple-banner-gpp.json rename to adapters/yahooAds/yahooAdstest/supplemental/simple-banner-gpp.json index 81c89f55c3d..3d5aff6c531 100644 --- a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/simple-banner-gpp.json +++ b/adapters/yahooAds/yahooAdstest/supplemental/simple-banner-gpp.json @@ -87,7 +87,7 @@ "id": "test-request-id", "seatbid": [ { - "seat": "yahooAdvertising", + "seat": "yahooAds", "bid": [{ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", "impid": "test-imp-id", diff --git a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/simple-banner-ignore-width-when-height-missing.json b/adapters/yahooAds/yahooAdstest/supplemental/simple-banner-ignore-width-when-height-missing.json similarity index 98% rename from adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/simple-banner-ignore-width-when-height-missing.json rename to adapters/yahooAds/yahooAdstest/supplemental/simple-banner-ignore-width-when-height-missing.json index 353d5e29e71..1206005970c 100644 --- a/adapters/yahooAdvertising/yahooAdvertisingtest/supplemental/simple-banner-ignore-width-when-height-missing.json +++ b/adapters/yahooAds/yahooAdstest/supplemental/simple-banner-ignore-width-when-height-missing.json @@ -78,7 +78,7 @@ "id": "test-request-id", "seatbid": [ { - "seat": "yahooAdvertising", + "seat": "yahooAds", "bid": [{ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", "impid": "test-imp-id", diff --git a/config/account.go b/config/account.go index 8beff9b6569..020402114de 100644 --- a/config/account.go +++ b/config/account.go @@ -40,7 +40,7 @@ type Account struct { Validations Validations `mapstructure:"validations" json:"validations"` DefaultBidLimit int `mapstructure:"default_bid_limit" json:"default_bid_limit"` BidAdjustments *openrtb_ext.ExtRequestPrebidBidAdjustments `mapstructure:"bidadjustments" json:"bidadjustments"` - Privacy AccountPrivacy `mapstructure:"privacy" json:"privacy"` + Privacy *AccountPrivacy `mapstructure:"privacy" json:"privacy"` } // CookieSync represents the account-level defaults for the cookie sync endpoint. diff --git a/config/activity.go b/config/activity.go index 987cbe84a2d..5bddc7c6405 100644 --- a/config/activity.go +++ b/config/activity.go @@ -8,16 +8,17 @@ type AllowActivities struct { TransmitUserFPD Activity `mapstructure:"transmitUfpd" json:"transmitUfpd"` TransmitPreciseGeo Activity `mapstructure:"transmitPreciseGeo" json:"transmitPreciseGeo"` TransmitUniqueRequestIds Activity `mapstructure:"transmitUniqueRequestIds" json:"transmitUniqueRequestIds"` + TransmitTids Activity `mapstructure:"transmitTid" json:"transmitTid"` } type Activity struct { Default *bool `mapstructure:"default" json:"default"` Rules []ActivityRule `mapstructure:"rules" json:"rules"` - Allow bool `mapstructure:"allow" json:"allow"` } type ActivityRule struct { Condition ActivityCondition `mapstructure:"condition" json:"condition"` + Allow bool `mapstructure:"allow" json:"allow"` } type ActivityCondition struct { diff --git a/config/config.go b/config/config.go index 12fd5263a6d..0ecd85c2ef9 100644 --- a/config/config.go +++ b/config/config.go @@ -155,6 +155,10 @@ func (cfg *Configuration) validate(v *viper.Viper) []error { cfg.TmaxAdjustments.Enabled = false } + if cfg.AccountDefaults.Privacy != nil { + glog.Warning("account_defaults.Privacy has no effect as the feature is under development.") + } + errs = cfg.Experiment.validate(errs) errs = cfg.BidderInfos.validate(errs) return errs diff --git a/endpoints/openrtb2/amp_auction.go b/endpoints/openrtb2/amp_auction.go index 73f4842ae83..8d328b7c51a 100644 --- a/endpoints/openrtb2/amp_auction.go +++ b/endpoints/openrtb2/amp_auction.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/prebid/prebid-server/privacy" "net/http" "net/url" "strings" @@ -222,6 +223,13 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR) + activities, activitiesErr := privacy.NewActivityControl(account.Privacy) + if activitiesErr != nil { + errL = append(errL, activitiesErr) + writeError(errL, w, &labels) + return + } + secGPC := r.Header.Get("Sec-GPC") auctionRequest := &exchange.AuctionRequest{ @@ -239,6 +247,7 @@ func (deps *endpointDeps) AmpAuction(w http.ResponseWriter, r *http.Request, _ h HookExecutor: hookExecutor, QueryParams: r.URL.Query(), TCF2Config: tcf2Config, + Activities: activities, } auctionResponse, err := deps.ex.HoldAuction(ctx, auctionRequest, nil) diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index bcd622dbff0..ad365a47fb0 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/prebid/prebid-server/privacy" "io" "io/ioutil" "net/http" @@ -191,6 +192,15 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http tcf2Config := gdpr.NewTCF2Config(deps.cfg.GDPR.TCF2, account.GDPR) + activities, activitiesErr := privacy.NewActivityControl(account.Privacy) + if activitiesErr != nil { + errL = append(errL, activitiesErr) + if errortypes.ContainsFatalError(errL) { + writeError(errL, w, &labels) + return + } + } + ctx := context.Background() timeout := deps.cfg.AuctionTimeouts.LimitAuctionTimeout(time.Duration(req.TMax) * time.Millisecond) @@ -236,6 +246,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http PubID: labels.PubID, HookExecutor: hookExecutor, TCF2Config: tcf2Config, + Activities: activities, } auctionResponse, err := deps.ex.HoldAuction(ctx, auctionRequest, nil) ao.RequestWrapper = req diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 171600c0260..3818a16a03a 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -28,6 +28,7 @@ import ( "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" @@ -169,7 +170,7 @@ import ( "github.com/prebid/prebid-server/adapters/visx" "github.com/prebid/prebid-server/adapters/vrtcal" "github.com/prebid/prebid-server/adapters/xeworks" - "github.com/prebid/prebid-server/adapters/yahooAdvertising" + "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" @@ -212,6 +213,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderAdView: adview.Builder, openrtb_ext.BidderAdxcg: adxcg.Builder, openrtb_ext.BidderAdyoulike: adyoulike.Builder, + openrtb_ext.BidderAidem: aidem.Builder, openrtb_ext.BidderAJA: aja.Builder, openrtb_ext.BidderAlgorix: algorix.Builder, openrtb_ext.BidderAMX: amx.Builder, @@ -367,8 +369,9 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderVisx: visx.Builder, openrtb_ext.BidderVrtcal: vrtcal.Builder, openrtb_ext.BidderXeworks: xeworks.Builder, - openrtb_ext.BidderYahooAdvertising: yahooAdvertising.Builder, - openrtb_ext.BidderYahooSSP: yahooAdvertising.Builder, + openrtb_ext.BidderYahooAds: yahooAds.Builder, + openrtb_ext.BidderYahooAdvertising: yahooAds.Builder, + openrtb_ext.BidderYahooSSP: yahooAds.Builder, openrtb_ext.BidderYeahmobi: yeahmobi.Builder, openrtb_ext.BidderYieldlab: yieldlab.Builder, openrtb_ext.BidderYieldmo: yieldmo.Builder, diff --git a/exchange/adapter_util.go b/exchange/adapter_util.go index d8f683b35d2..ad1866a63c8 100644 --- a/exchange/adapter_util.go +++ b/exchange/adapter_util.go @@ -88,7 +88,7 @@ func GetDisabledBiddersErrorMessages(infos config.BidderInfos) 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 "yahooAdvertising" in 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.`, diff --git a/exchange/adapter_util_test.go b/exchange/adapter_util_test.go index 9d2a5c3cd5f..2dd0fd04ab7 100644 --- a/exchange/adapter_util_test.go +++ b/exchange/adapter_util_test.go @@ -210,7 +210,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) { "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 "yahooAdvertising" in 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.`, @@ -224,7 +224,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) { "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 "yahooAdvertising" in 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.`, @@ -238,7 +238,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) { "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 "yahooAdvertising" in 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.`, "appnexus": `Bidder "appnexus" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`, "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.`, @@ -253,7 +253,7 @@ func TestGetDisabledBiddersErrorMessages(t *testing.T) { "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 "yahooAdvertising" in 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.`, "appnexus": `Bidder "appnexus" has been disabled on this instance of Prebid Server. Please work with the PBS host to enable this bidder again.`, "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.`, diff --git a/exchange/exchange.go b/exchange/exchange.go index 737091d4ba8..8fb1f45de42 100644 --- a/exchange/exchange.go +++ b/exchange/exchange.go @@ -6,6 +6,7 @@ import ( "encoding/json" "errors" "fmt" + "github.com/prebid/prebid-server/privacy" "math/rand" "net/url" "runtime/debug" @@ -195,6 +196,7 @@ type AuctionRequest struct { GlobalPrivacyControlHeader string ImpExtInfoMap map[string]ImpExtInfo TCF2Config gdpr.TCF2ConfigReader + Activities privacy.ActivityControl // LegacyLabels is included here for temporary compatibility with cleanOpenRTBRequests // in HoldAuction until we get to factoring it away. Do not use for anything new. diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go index 511fd84b71a..8306849a762 100644 --- a/exchange/exchange_test.go +++ b/exchange/exchange_test.go @@ -2085,7 +2085,7 @@ func getTestBuildRequest(t *testing.T) *openrtb2.BidRequest { H: 600, }}, }, - Ext: json.RawMessage(`{"ext_field":"value}"}`), + Ext: json.RawMessage(`{"prebid":{"bidder":{"appnexus": {"placementId": 1}}}}`), }, { Video: &openrtb2.Video{ MIMEs: []string{"video/mp4"}, @@ -2094,7 +2094,7 @@ func getTestBuildRequest(t *testing.T) *openrtb2.BidRequest { W: 300, H: 600, }, - Ext: json.RawMessage(`{"ext_field":"value}"}`), + Ext: json.RawMessage(`{"prebid":{"bidder":{"appnexus": {"placementId": 1}}}}`), }}, } } @@ -3592,7 +3592,7 @@ func TestApplyDealSupport(t *testing.T) { { description: "hb_pb_cat_dur should be modified", dealPriority: 5, - impExt: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + 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", }, @@ -3603,7 +3603,7 @@ func TestApplyDealSupport(t *testing.T) { { description: "hb_pb_cat_dur should not be modified due to priority not exceeding min", dealPriority: 9, - impExt: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 10, "prefix": "tier"}, "placementId": 10433394}}`), + 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", }, @@ -3614,7 +3614,7 @@ func TestApplyDealSupport(t *testing.T) { { description: "hb_pb_cat_dur should not be modified due to invalid config", dealPriority: 5, - impExt: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": ""}, "placementId": 10433394}}`), + impExt: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": ""}, "placementId": 10433394}}}}`), targ: map[string]string{ "hb_pb_cat_dur": "12.00_games_30s", }, @@ -3625,7 +3625,7 @@ func TestApplyDealSupport(t *testing.T) { { description: "hb_pb_cat_dur should not be modified due to deal priority of 0", dealPriority: 0, - impExt: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + 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", }, @@ -3695,11 +3695,11 @@ func TestApplyDealSupportMultiBid(t *testing.T) { Imp: []openrtb2.Imp{ { ID: "imp_id1", - Ext: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + Ext: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}}}`), }, { ID: "imp_id1", - Ext: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + Ext: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}}}`), }, }, }, @@ -3741,11 +3741,11 @@ func TestApplyDealSupportMultiBid(t *testing.T) { Imp: []openrtb2.Imp{ { ID: "imp_id1", - Ext: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + Ext: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}}}`), }, { ID: "imp_id1", - Ext: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + Ext: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}}}`), }, }, }, @@ -3792,11 +3792,11 @@ func TestApplyDealSupportMultiBid(t *testing.T) { Imp: []openrtb2.Imp{ { ID: "imp_id1", - Ext: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + Ext: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}}}`), }, { ID: "imp_id1", - Ext: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}`), + Ext: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}, "placementId": 10433394}}}}`), }, }, }, @@ -3869,7 +3869,7 @@ func TestGetDealTiers(t *testing.T) { description: "One", request: openrtb2.BidRequest{ Imp: []openrtb2.Imp{ - {ID: "imp1", Ext: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}}}`)}, + {ID: "imp1", Ext: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier"}}}}}`)}, }, }, expected: map[string]openrtb_ext.DealTierBidderMap{ @@ -3880,8 +3880,8 @@ func TestGetDealTiers(t *testing.T) { description: "Many", request: openrtb2.BidRequest{ Imp: []openrtb2.Imp{ - {ID: "imp1", Ext: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier1"}}}`)}, - {ID: "imp2", Ext: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 8, "prefix": "tier2"}}}`)}, + {ID: "imp1", Ext: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier1"}}}}}`)}, + {ID: "imp2", Ext: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 8, "prefix": "tier2"}}}}}`)}, }, }, expected: map[string]openrtb_ext.DealTierBidderMap{ @@ -3893,8 +3893,8 @@ func TestGetDealTiers(t *testing.T) { description: "Many - Skips Malformed", request: openrtb2.BidRequest{ Imp: []openrtb2.Imp{ - {ID: "imp1", Ext: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier1"}}}`)}, - {ID: "imp2", Ext: json.RawMessage(`{"appnexus": {"dealTier": "wrong type"}}`)}, + {ID: "imp1", Ext: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "tier1"}}}}}`)}, + {ID: "imp2", Ext: json.RawMessage(`{"prebid": {"bidder": {"appnexus": {"dealTier": "wrong type"}}}}`)}, }, }, expected: map[string]openrtb_ext.DealTierBidderMap{ diff --git a/exchange/utils.go b/exchange/utils.go index d43a4182451..5b29fd6292f 100644 --- a/exchange/utils.go +++ b/exchange/utils.go @@ -150,6 +150,15 @@ func (rs *requestSplitter) cleanOpenRTBRequests(ctx context.Context, for _, bidderRequest := range allBidderRequests { bidRequestAllowed := true + // fetchBids activity + fetchBidsActivityAllowed := auctionReq.Activities.Allow(privacy.ActivityFetchBids, + privacy.ScopedName{Scope: privacy.ScopeTypeBidder, Name: bidderRequest.BidderName.String()}) + if fetchBidsActivityAllowed == privacy.ActivityDeny { + // skip the call to a bidder if fetchBids activity is not allowed + // do not add this bidder to allowedBidderRequests + continue + } + // CCPA privacyEnforcement.CCPA = ccpaEnforcer.ShouldEnforce(bidderRequest.BidderName.String()) diff --git a/exchange/utils_test.go b/exchange/utils_test.go index 8bb82fe412f..973edc53355 100644 --- a/exchange/utils_test.go +++ b/exchange/utils_test.go @@ -17,6 +17,7 @@ import ( "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/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -505,7 +506,7 @@ func TestCleanOpenRTBRequestsWithFPD(t *testing.T) { App: &openrtb2.App{Name: "fpdApnApp"}, User: &openrtb2.User{Keywords: "fpdApnUser"}, } - fpd[openrtb_ext.BidderName("appnexus")] = &apnFpd + fpd[openrtb_ext.BidderName("rubicon")] = &apnFpd brightrollFpd := firstpartydata.ResolvedFirstPartyData{ Site: &openrtb2.Site{Name: "fpdBrightrollSite"}, @@ -4273,3 +4274,72 @@ func TestGetMediaTypeForBid(t *testing.T) { }) } } + +func TemporarilyDisabledTestCleanOpenRTBRequestsActivitiesFetchBids(t *testing.T) { + testCases := []struct { + name string + req *openrtb2.BidRequest + componentName string + allow bool + expectedReqNumber int + }{ + { + name: "request_with_one_bidder_allowed", + req: newBidRequest(t), + componentName: "appnexus", + allow: true, + expectedReqNumber: 1, + }, + { + name: "request_with_one_bidder_not_allowed", + req: newBidRequest(t), + componentName: "appnexus", + allow: false, + expectedReqNumber: 0, + }, + } + + for _, test := range testCases { + privacyConfig := getDefaultActivityConfig(test.componentName, test.allow) + activities, err := privacy.NewActivityControl(privacyConfig) + assert.NoError(t, err, "") + auctionReq := AuctionRequest{ + BidRequestWrapper: &openrtb_ext.RequestWrapper{BidRequest: test.req}, + UserSyncs: &emptyUsersync{}, + Activities: activities, + } + + 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) + assert.Empty(t, errs) + assert.Len(t, bidderRequests, test.expectedReqNumber) + }) + } +} + +func getDefaultActivityConfig(componentName string, allow bool) *config.AccountPrivacy { + return &config.AccountPrivacy{ + AllowActivities: config.AllowActivities{ + FetchBids: config.Activity{ + Default: ptrutil.ToPtr(true), + Rules: []config.ActivityRule{ + { + Allow: allow, + Condition: config.ActivityCondition{ + ComponentName: []string{componentName}, + ComponentType: []string{"bidder"}, + }, + }, + }, + }, + }, + } +} diff --git a/go.mod b/go.mod index cb3e942c2e8..06bfb9a265f 100644 --- a/go.mod +++ b/go.mod @@ -33,14 +33,14 @@ require ( github.com/yudai/gojsondiff v1.0.0 golang.org/x/net v0.7.0 golang.org/x/text v0.7.0 - google.golang.org/grpc v1.46.2 + google.golang.org/grpc v1.53.0 gopkg.in/evanphx/json-patch.v4 v4.12.0 gopkg.in/yaml.v3 v3.0.1 ) require ( github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/fsnotify/fsnotify v1.5.4 // indirect github.com/golang/protobuf v1.5.2 // indirect @@ -68,8 +68,8 @@ require ( github.com/yudai/pp v2.0.1+incompatible // indirect golang.org/x/crypto v0.0.0-20220411220226-7b82a4e95df4 // indirect golang.org/x/sys v0.5.0 // indirect - google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd // indirect - google.golang.org/protobuf v1.28.0 // indirect + google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect + google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.66.4 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect ) diff --git a/go.sum b/go.sum index 3a4af44cdf7..de28594620f 100644 --- a/go.sum +++ b/go.sum @@ -86,8 +86,9 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chasex/glog v0.0.0-20160217080310-c62392af379c h1:eXqCBUHfmjbeDqcuvzjsd+bM6A+bnwo5N9FVbV6m5/s= github.com/chasex/glog v0.0.0-20160217080310-c62392af379c/go.mod h1:omJZNg0Qu76bxJd+ExohVo8uXzNcGOk2bv7vel460xk= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -125,7 +126,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/go-control-plane v0.10.1/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -210,7 +210,7 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -909,8 +909,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-20220519153652-3a47de7e79bd h1:e0TwkXOdbnH/1x5rc5MZ/VYyiZ4v+RdVfrGMqEwT68I= -google.golang.org/genproto v0.0.0-20220519153652-3a47de7e79bd/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +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/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= @@ -941,9 +941,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.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2 h1:u+MLGgVf7vRdjEYZ8wDFhAVNmhkbJ5hmrA1LMWK1CAQ= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +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/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= @@ -958,8 +957,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.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= -google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/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= diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 03056123423..9906d12e1b8 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -114,6 +114,7 @@ const ( BidderAdView BidderName = "adview" BidderAdxcg BidderName = "adxcg" BidderAdyoulike BidderName = "adyoulike" + BidderAidem BidderName = "aidem" BidderAJA BidderName = "aja" BidderAlgorix BidderName = "algorix" BidderAMX BidderName = "amx" @@ -269,6 +270,7 @@ const ( BidderVisx BidderName = "visx" BidderVrtcal BidderName = "vrtcal" BidderXeworks BidderName = "xeworks" + BidderYahooAds BidderName = "yahooAds" BidderYahooAdvertising BidderName = "yahooAdvertising" BidderYahooSSP BidderName = "yahoossp" BidderYeahmobi BidderName = "yeahmobi" @@ -310,6 +312,7 @@ func CoreBidderNames() []BidderName { BidderAdView, BidderAdxcg, BidderAdyoulike, + BidderAidem, BidderAJA, BidderAlgorix, BidderAMX, @@ -465,6 +468,7 @@ func CoreBidderNames() []BidderName { BidderVisx, BidderVrtcal, BidderXeworks, + BidderYahooAds, BidderYahooAdvertising, BidderYahooSSP, BidderYeahmobi, diff --git a/openrtb_ext/bidders_validate_test.go b/openrtb_ext/bidders_validate_test.go index 530f260c761..87a8c96d6f8 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 != BidderTripleliftNative && bidder != BidderAdkernelAdn && bidder != BidderFreewheelSSPOld { + if bidder != BidderTripleliftNative && bidder != BidderAdkernelAdn && bidder != BidderFreewheelSSPOld && bidder != BidderYahooAdvertising { bidders = append(bidders, string(bidder)) } } diff --git a/openrtb_ext/deal_tier.go b/openrtb_ext/deal_tier.go index ef85b9b1df8..45285d21663 100644 --- a/openrtb_ext/deal_tier.go +++ b/openrtb_ext/deal_tier.go @@ -27,20 +27,6 @@ func ReadDealTiersFromImp(imp openrtb2.Imp) (DealTierBidderMap, error) { return dealTiers, nil } - // imp.ext.{bidder} - var impExt map[string]struct { - DealTier *DealTier `json:"dealTier"` - } - if err := json.Unmarshal(imp.Ext, &impExt); err != nil { - return nil, err - } - for bidder, param := range impExt { - if param.DealTier != nil { - dealTiers[BidderName(bidder)] = *param.DealTier - } - } - - // imp.ext.prebid.{bidder} var impPrebidExt struct { Prebid struct { Bidders map[string]struct { diff --git a/openrtb_ext/deal_tier_test.go b/openrtb_ext/deal_tier_test.go index 6aaebbab687..0046b788ece 100644 --- a/openrtb_ext/deal_tier_test.go +++ b/openrtb_ext/deal_tier_test.go @@ -31,55 +31,50 @@ func TestReadDealTiersFromImp(t *testing.T) { expectedResult: DealTierBidderMap{}, }, { - description: "imp.ext - with other params", - impExt: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "anyPrefix"}, "placementId": 12345}}`), - expectedResult: DealTierBidderMap{BidderAppnexus: {Prefix: "anyPrefix", MinDealTier: 5}}, + 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 - multiple", - impExt: json.RawMessage(`{"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 - nil", + impExt: json.RawMessage(`{"prebid": null}`), + expectedResult: DealTierBidderMap{}, }, { - description: "imp.ext - no deal tier", - impExt: json.RawMessage(`{"appnexus": {"placementId": 12345}}`), + description: "imp.ext.prebid - empty", + impExt: json.RawMessage(`{"prebid": {}}`), expectedResult: DealTierBidderMap{}, }, { - description: "imp.ext - error", - impExt: json.RawMessage(`{"appnexus": {"dealTier": "wrong type", "placementId": 12345}}`), - expectedError: "json: cannot unmarshal string into Go struct field .dealTier of type openrtb_ext.DealTier", + description: "imp.ext.prebid - no bidder but with other params", + impExt: json.RawMessage(`{"prebid": {"supportdeals": true}}`), + expectedResult: DealTierBidderMap{}, }, { - description: "imp.ext.prebid", + 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- multiple", + 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", 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 - no 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 - error", + 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 wins over imp.ext", - impExt: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "impExt"}, "placementId": 12345}, "prebid": {"bidder": {"appnexus": {"dealTier": {"minDealTier": 8, "prefix": "impExtPrebid"}, "placementId": 12345}}}}`), - expectedResult: DealTierBidderMap{BidderAppnexus: {Prefix: "impExtPrebid", MinDealTier: 8}}, - }, - { - description: "imp.ext.prebid coexists with imp.ext", - impExt: json.RawMessage(`{"appnexus": {"dealTier": {"minDealTier": 5, "prefix": "impExt"}, "placementId": 12345}, "prebid": {"bidder": {"rubicon": {"dealTier": {"minDealTier": 8, "prefix": "impExtPrebid"}, "placementId": 12345}}}}`), - expectedResult: DealTierBidderMap{BidderAppnexus: {Prefix: "impExt", MinDealTier: 5}, BidderRubicon: {Prefix: "impExtPrebid", MinDealTier: 8}}, - }, } for _, test := range testCases { diff --git a/openrtb_ext/imp_aidem.go b/openrtb_ext/imp_aidem.go new file mode 100644 index 00000000000..59457f1eb4a --- /dev/null +++ b/openrtb_ext/imp_aidem.go @@ -0,0 +1,8 @@ +package openrtb_ext + +type ImpExtFoo struct { + SiteID string `json:"siteId"` + PublisherID string `json:"publisherId"` + PlacementID string `json:"placementId"` + RateLimit string `json:"rateLimit"` +} diff --git a/openrtb_ext/imp_ix.go b/openrtb_ext/imp_ix.go index 9f977fb0dcd..40c3f51867f 100644 --- a/openrtb_ext/imp_ix.go +++ b/openrtb_ext/imp_ix.go @@ -4,4 +4,5 @@ package openrtb_ext type ExtImpIx struct { SiteId string `json:"siteId"` Size []int `json:"size"` + Sid string `json:"sid"` } diff --git a/openrtb_ext/imp_yahooAds.go b/openrtb_ext/imp_yahooAds.go new file mode 100644 index 00000000000..36a4a0f618b --- /dev/null +++ b/openrtb_ext/imp_yahooAds.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtImpYahooAds defines the contract for bidrequest.imp[i].ext.prebid.bidder.yahooAds +type ExtImpYahooAds struct { + Dcn string `json:"dcn"` + Pos string `json:"pos"` +} diff --git a/privacy/activity.go b/privacy/activity.go index 7cdef17590c..6ca48ae0b93 100644 --- a/privacy/activity.go +++ b/privacy/activity.go @@ -11,6 +11,7 @@ const ( ActivityTransmitUserFPD ActivityTransmitPreciseGeo ActivityTransmitUniqueRequestIds + ActivityTransmitTids ) func (a Activity) String() string { @@ -29,6 +30,8 @@ func (a Activity) String() string { return "transmitPreciseGeo" case ActivityTransmitUniqueRequestIds: return "transmitUniqueRequestIds" + case ActivityTransmitTids: + return "transmitTid" } return "" diff --git a/privacy/enforcer.go b/privacy/enforcer.go index 0d5ecad5309..d63cd8de31f 100644 --- a/privacy/enforcer.go +++ b/privacy/enforcer.go @@ -1,43 +1,236 @@ package privacy -// PolicyEnforcer determines if personally identifiable information (PII) should be removed or anonymized per the policy. -type PolicyEnforcer interface { - // CanEnforce returns true when policy information is specifically provided by the publisher. - CanEnforce() bool +import ( + "fmt" + "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/errortypes" + "strings" +) - // ShouldEnforce returns true when the OpenRTB request should have personally identifiable - // information (PII) removed or anonymized per the policy. - ShouldEnforce(bidder string) bool +type ActivityResult int + +const ( + ActivityAbstain ActivityResult = iota + ActivityAllow + ActivityDeny +) + +const ( + ScopeTypeBidder = "bidder" + ScopeTypeAnalytics = "analytics" + ScopeTypeRTD = "rtd" // real time data + ScopeTypeUserID = "userid" + ScopeTypeGeneral = "general" +) + +type ActivityControl struct { + plans map[Activity]ActivityPlan } -// NilPolicyEnforcer implements the PolicyEnforcer interface but will always return false. -type NilPolicyEnforcer struct{} +func NewActivityControl(privacyConf *config.AccountPrivacy) (ActivityControl, error) { + ac := ActivityControl{} + var err error -// CanEnforce is hardcoded to always return false. -func (NilPolicyEnforcer) CanEnforce() bool { - return false + if privacyConf == nil { + return ac, err + } else { + //temporarily disable Activities if they are specified at the account level + return ac, &errortypes.Warning{Message: "account.Privacy has no effect as the feature is under development."} + } + + plans := make(map[Activity]ActivityPlan) + + plans[ActivitySyncUser], err = buildEnforcementPlan(privacyConf.AllowActivities.SyncUser) + if err != nil { + return ac, err + } + plans[ActivityFetchBids], err = buildEnforcementPlan(privacyConf.AllowActivities.FetchBids) + if err != nil { + return ac, err + } + plans[ActivityEnrichUserFPD], err = buildEnforcementPlan(privacyConf.AllowActivities.EnrichUserFPD) + if err != nil { + return ac, err + } + plans[ActivityReportAnalytics], err = buildEnforcementPlan(privacyConf.AllowActivities.ReportAnalytics) + if err != nil { + return ac, err + } + plans[ActivityTransmitUserFPD], err = buildEnforcementPlan(privacyConf.AllowActivities.TransmitUserFPD) + if err != nil { + return ac, err + } + plans[ActivityTransmitPreciseGeo], err = buildEnforcementPlan(privacyConf.AllowActivities.TransmitPreciseGeo) + if err != nil { + return ac, err + } + plans[ActivityTransmitUniqueRequestIds], err = buildEnforcementPlan(privacyConf.AllowActivities.TransmitUniqueRequestIds) + if err != nil { + return ac, err + } + plans[ActivityTransmitTids], err = buildEnforcementPlan(privacyConf.AllowActivities.TransmitTids) + if err != nil { + return ac, err + } + + ac.plans = plans + + return ac, nil +} + +func buildEnforcementPlan(activity config.Activity) (ActivityPlan, error) { + ef := ActivityPlan{} + rules, err := activityRulesToEnforcementRules(activity.Rules) + if err != nil { + return ef, err + } + ef.defaultResult = activityDefaultToDefaultResult(activity.Default) + ef.rules = rules + return ef, nil } -// ShouldEnforce is hardcoded to always return false. -func (NilPolicyEnforcer) ShouldEnforce(bidder string) bool { - return false +func activityRulesToEnforcementRules(rules []config.ActivityRule) ([]ActivityRule, error) { + enfRules := make([]ActivityRule, 0) + for _, r := range rules { + cmpName, err := conditionToRuleComponentName(r.Condition.ComponentName) + if err != nil { + return nil, err + } + er := ComponentEnforcementRule{ + allowed: r.Allow, + componentName: cmpName, + componentType: r.Condition.ComponentType, + } + enfRules = append(enfRules, er) + } + return enfRules, nil } -// EnabledPolicyEnforcer decorates a PolicyEnforcer with an enabled flag. -type EnabledPolicyEnforcer struct { - Enabled bool - PolicyEnforcer PolicyEnforcer +func conditionToRuleComponentName(conditions []string) ([]ScopedName, error) { + sn := make([]ScopedName, 0) + for _, condition := range conditions { + scope, err := NewScopedName(condition) + if err != nil { + return sn, err + } + sn = append(sn, scope) + } + return sn, nil } -// CanEnforce returns true when the PolicyEnforcer can enforce. -func (p EnabledPolicyEnforcer) CanEnforce() bool { - return p.PolicyEnforcer.CanEnforce() +func activityDefaultToDefaultResult(activityDefault *bool) ActivityResult { + if activityDefault == nil { + // if default is unspecified, the hardcoded default-default is true. + return ActivityAllow + } else if *activityDefault { + return ActivityAllow + } + return ActivityDeny } -// ShouldEnforce returns true when the enforcer is enabled the PolicyEnforcer allows enforcement. -func (p EnabledPolicyEnforcer) ShouldEnforce(bidder string) bool { - if p.Enabled { - return p.PolicyEnforcer.ShouldEnforce(bidder) +func (e ActivityControl) Allow(activity Activity, target ScopedName) ActivityResult { + plan, planDefined := e.plans[activity] + + if !planDefined { + return ActivityAbstain } - return false + + return plan.Allow(target) +} + +type ActivityPlan struct { + defaultResult ActivityResult + rules []ActivityRule +} + +func (p ActivityPlan) Allow(target ScopedName) ActivityResult { + for _, rule := range p.rules { + result := rule.Allow(target) + if result == ActivityDeny || result == ActivityAllow { + return result + } + } + return p.defaultResult +} + +type ActivityRule interface { + Allow(target ScopedName) ActivityResult +} + +type ComponentEnforcementRule struct { + componentName []ScopedName + componentType []string + // include gppSectionId from 3.5 + // include geo from 3.5 + allowed bool +} + +func (r ComponentEnforcementRule) Allow(target ScopedName) ActivityResult { + if len(r.componentName) == 0 && len(r.componentType) == 0 { + return ActivityAbstain + } + + nameClauseExists := len(r.componentName) > 0 + typeClauseExists := len(r.componentType) > 0 + + componentNameFound := false + for _, scope := range r.componentName { + if strings.EqualFold(scope.Scope, target.Scope) && + (scope.Name == "*" || strings.EqualFold(scope.Name, target.Name)) { + componentNameFound = true + break + } + } + + componentTypeFound := false + for _, componentType := range r.componentType { + if strings.EqualFold(componentType, target.Scope) { + componentTypeFound = true + break + } + } + // behavior if rule matches: can be either true=allow or false=deny. result is abstain if the rule doesn't match + matchFound := (componentNameFound || !nameClauseExists) && (componentTypeFound || !typeClauseExists) + if matchFound { + if r.allowed { + return ActivityAllow + } else { + return ActivityDeny + } + } + return ActivityAbstain +} + +type ScopedName struct { + Scope string + Name string +} + +func NewScopedName(condition string) (ScopedName, error) { + if condition == "" { + return ScopedName{}, fmt.Errorf("unable to parse empty condition") + } + var scope, name string + split := strings.Split(condition, ".") + if len(split) == 2 { + s := strings.ToLower(split[0]) + if s == ScopeTypeBidder || s == ScopeTypeAnalytics || s == ScopeTypeUserID { + scope = s + } else if strings.Contains(s, ScopeTypeRTD) { + scope = ScopeTypeRTD + } else { + scope = ScopeTypeGeneral + } + name = split[1] + } else if len(split) == 1 { + scope = ScopeTypeBidder + name = split[0] + } else { + return ScopedName{}, fmt.Errorf("unable to parse condition: %s", condition) + } + + return ScopedName{ + Scope: scope, + Name: name, + }, nil } diff --git a/privacy/enforcer_test.go b/privacy/enforcer_test.go index b0c4032c714..e87a9eb2bff 100644 --- a/privacy/enforcer_test.go +++ b/privacy/enforcer_test.go @@ -1,18 +1,427 @@ package privacy import ( - "testing" - + "errors" + "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/util/ptrutil" "github.com/stretchr/testify/assert" + "testing" ) -func TestNilEnforcerCanEnforce(t *testing.T) { - nilEnforcer := &NilPolicyEnforcer{} - assert.False(t, nilEnforcer.CanEnforce()) +func TemporarilyDisabledTestNewActivityControl(t *testing.T) { + + testCases := []struct { + name string + privacyConf *config.AccountPrivacy + activityControl ActivityControl + err error + }{ + { + name: "privacy_config_is_nil", + privacyConf: nil, + activityControl: ActivityControl{plans: nil}, + err: nil, + }, + { + name: "privacy_config_is_specified_and_correct", + privacyConf: &config.AccountPrivacy{ + AllowActivities: config.AllowActivities{ + SyncUser: getDefaultActivityConfig(), + FetchBids: getDefaultActivityConfig(), + EnrichUserFPD: getDefaultActivityConfig(), + ReportAnalytics: getDefaultActivityConfig(), + TransmitUserFPD: getDefaultActivityConfig(), + TransmitPreciseGeo: getDefaultActivityConfig(), + TransmitUniqueRequestIds: getDefaultActivityConfig(), + TransmitTids: getDefaultActivityConfig(), + }, + }, + activityControl: ActivityControl{plans: map[Activity]ActivityPlan{ + ActivitySyncUser: getDefaultActivityPlan(), + ActivityFetchBids: getDefaultActivityPlan(), + ActivityEnrichUserFPD: getDefaultActivityPlan(), + ActivityReportAnalytics: getDefaultActivityPlan(), + ActivityTransmitUserFPD: getDefaultActivityPlan(), + ActivityTransmitPreciseGeo: getDefaultActivityPlan(), + ActivityTransmitUniqueRequestIds: getDefaultActivityPlan(), + ActivityTransmitTids: getDefaultActivityPlan(), + }}, + err: nil, + }, + { + name: "privacy_config_is_specified_and_SyncUser_is_incorrect", + privacyConf: &config.AccountPrivacy{ + AllowActivities: config.AllowActivities{ + SyncUser: getIncorrectActivityConfig(), + }, + }, + activityControl: ActivityControl{plans: nil}, + err: errors.New("unable to parse condition: bidder.bidderA.bidderB"), + }, + { + name: "privacy_config_is_specified_and_FetchBids_is_incorrect", + privacyConf: &config.AccountPrivacy{ + AllowActivities: config.AllowActivities{ + FetchBids: getIncorrectActivityConfig(), + }, + }, + activityControl: ActivityControl{plans: nil}, + err: errors.New("unable to parse condition: bidder.bidderA.bidderB"), + }, + { + name: "privacy_config_is_specified_and_EnrichUserFPD_is_incorrect", + privacyConf: &config.AccountPrivacy{ + AllowActivities: config.AllowActivities{ + EnrichUserFPD: getIncorrectActivityConfig(), + }, + }, + activityControl: ActivityControl{plans: nil}, + err: errors.New("unable to parse condition: bidder.bidderA.bidderB"), + }, + { + name: "privacy_config_is_specified_and_ReportAnalytics_is_incorrect", + privacyConf: &config.AccountPrivacy{ + AllowActivities: config.AllowActivities{ + ReportAnalytics: getIncorrectActivityConfig(), + }, + }, + activityControl: ActivityControl{plans: nil}, + err: errors.New("unable to parse condition: bidder.bidderA.bidderB"), + }, + { + name: "privacy_config_is_specified_and_TransmitUserFPD_is_incorrect", + privacyConf: &config.AccountPrivacy{ + AllowActivities: config.AllowActivities{ + TransmitUserFPD: getIncorrectActivityConfig(), + }, + }, + activityControl: ActivityControl{plans: nil}, + err: errors.New("unable to parse condition: bidder.bidderA.bidderB"), + }, + { + name: "privacy_config_is_specified_and_TransmitPreciseGeo_is_incorrect", + privacyConf: &config.AccountPrivacy{ + AllowActivities: config.AllowActivities{ + TransmitPreciseGeo: getIncorrectActivityConfig(), + }, + }, + activityControl: ActivityControl{plans: nil}, + err: errors.New("unable to parse condition: bidder.bidderA.bidderB"), + }, + { + name: "privacy_config_is_specified_and_TransmitUniqueRequestIds_is_incorrect", + privacyConf: &config.AccountPrivacy{ + AllowActivities: config.AllowActivities{ + TransmitUniqueRequestIds: getIncorrectActivityConfig(), + }, + }, + activityControl: ActivityControl{plans: nil}, + err: errors.New("unable to parse condition: bidder.bidderA.bidderB"), + }, + { + name: "privacy_config_is_specified_and_TransmitTids_is_incorrect", + privacyConf: &config.AccountPrivacy{ + AllowActivities: config.AllowActivities{ + TransmitTids: getIncorrectActivityConfig(), + }, + }, + activityControl: ActivityControl{plans: nil}, + err: errors.New("unable to parse condition: bidder.bidderA.bidderB"), + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + actualAC, actualErr := NewActivityControl(test.privacyConf) + if test.err == nil { + assert.Equal(t, test.activityControl, actualAC) + assert.NoError(t, actualErr) + } else { + assert.EqualError(t, actualErr, test.err.Error()) + } + }) + } +} + +func TestActivityDefaultToDefaultResult(t *testing.T) { + + testCases := []struct { + name string + activityDefault *bool + expectedResult ActivityResult + }{ + { + name: "activityDefault_is_nil", + activityDefault: nil, + expectedResult: ActivityAllow, + }, + { + name: "activityDefault_is_true", + activityDefault: ptrutil.ToPtr(true), + expectedResult: ActivityAllow, + }, + { + name: "activityDefault_is_false", + activityDefault: ptrutil.ToPtr(false), + expectedResult: ActivityDeny, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + actualResult := activityDefaultToDefaultResult(test.activityDefault) + assert.Equal(t, test.expectedResult, actualResult) + }) + } +} + +func TestAllowActivityControl(t *testing.T) { + + testCases := []struct { + name string + activityControl ActivityControl + activity Activity + target ScopedName + activityResult ActivityResult + }{ + { + name: "plans_is_nil", + activityControl: ActivityControl{plans: nil}, + activity: ActivityFetchBids, + target: ScopedName{Scope: "bidder", Name: "bidderA"}, + activityResult: ActivityAbstain, + }, + { + name: "activity_not_defined", + activityControl: ActivityControl{plans: map[Activity]ActivityPlan{ + ActivitySyncUser: getDefaultActivityPlan()}}, + activity: ActivityFetchBids, + target: ScopedName{Scope: "bidder", Name: "bidderA"}, + activityResult: ActivityAbstain, + }, + { + name: "activity_defined_but_not_found_default_returned", + activityControl: ActivityControl{plans: map[Activity]ActivityPlan{ + ActivityFetchBids: getDefaultActivityPlan()}}, + activity: ActivityFetchBids, + target: ScopedName{Scope: "bidder", Name: "bidderB"}, + activityResult: ActivityAllow, + }, + { + name: "activity_defined_and_allowed", + activityControl: ActivityControl{plans: map[Activity]ActivityPlan{ + ActivityFetchBids: getDefaultActivityPlan()}}, + activity: ActivityFetchBids, + target: ScopedName{Scope: "bidder", Name: "bidderA"}, + activityResult: ActivityAllow, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + actualResult := test.activityControl.Allow(test.activity, test.target) + assert.Equal(t, test.activityResult, actualResult) + + }) + } +} + +func TestAllowComponentEnforcementRule(t *testing.T) { + + testCases := []struct { + name string + componentRule ComponentEnforcementRule + target ScopedName + activityResult ActivityResult + }{ + { + name: "activity_is_allowed", + componentRule: ComponentEnforcementRule{ + allowed: true, + componentName: []ScopedName{ + {Scope: "bidder", Name: "bidderA"}, + }, + componentType: []string{"bidder"}, + }, + target: ScopedName{Scope: "bidder", Name: "bidderA"}, + activityResult: ActivityAllow, + }, + { + name: "activity_is_not_allowed", + componentRule: ComponentEnforcementRule{ + allowed: false, + componentName: []ScopedName{ + {Scope: "bidder", Name: "bidderA"}, + }, + componentType: []string{"bidder"}, + }, + target: ScopedName{Scope: "bidder", Name: "bidderA"}, + activityResult: ActivityDeny, + }, + { + name: "abstain_both_clauses_do_not_match", + componentRule: ComponentEnforcementRule{ + allowed: true, + componentName: []ScopedName{ + {Scope: "bidder", Name: "bidderA"}, + }, + componentType: []string{"bidder"}, + }, + target: ScopedName{Scope: "bidder", Name: "bidderB"}, + activityResult: ActivityAbstain, + }, + { + name: "activity_is_not_allowed_componentName_only", + componentRule: ComponentEnforcementRule{ + allowed: true, + componentName: []ScopedName{ + {Scope: "bidder", Name: "bidderA"}, + }, + }, + target: ScopedName{Scope: "bidder", Name: "bidderA"}, + activityResult: ActivityAllow, + }, + { + name: "activity_is_allowed_componentType_only", + componentRule: ComponentEnforcementRule{ + allowed: true, + componentType: []string{"bidder"}, + }, + target: ScopedName{Scope: "bidder", Name: "bidderB"}, + activityResult: ActivityAllow, + }, + { + name: "abstain_activity_no_componentType_and_no_componentName", + componentRule: ComponentEnforcementRule{ + allowed: true, + }, + target: ScopedName{Scope: "bidder", Name: "bidderB"}, + activityResult: ActivityAbstain, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + actualResult := test.componentRule.Allow(test.target) + assert.Equal(t, test.activityResult, actualResult) + + }) + } +} + +func TestNewScopedName(t *testing.T) { + + testCases := []struct { + name string + condition string + expectedScopeName ScopedName + err error + }{ + { + name: "condition_is_empty", + condition: "", + expectedScopeName: ScopedName{}, + err: errors.New("unable to parse empty condition"), + }, + { + name: "condition_is_incorrect", + condition: "bidder.bidderA.bidderB", + expectedScopeName: ScopedName{}, + err: errors.New("unable to parse condition: bidder.bidderA.bidderB"), + }, + { + name: "condition_is_scoped_to_bidder", + condition: "bidder.bidderA", + expectedScopeName: ScopedName{Scope: "bidder", Name: "bidderA"}, + err: nil, + }, + { + name: "condition_is_scoped_to_analytics", + condition: "analytics.bidderA", + expectedScopeName: ScopedName{Scope: "analytics", Name: "bidderA"}, + err: nil, + }, + { + name: "condition_is_scoped_to_userid", + condition: "userid.bidderA", + expectedScopeName: ScopedName{Scope: "userid", Name: "bidderA"}, + err: nil, + }, + { + name: "condition_is_bidder_name", + condition: "bidderA", + expectedScopeName: ScopedName{Scope: "bidder", Name: "bidderA"}, + err: nil, + }, + { + name: "condition_is_module_tag_rtd", + condition: "rtd.test", + expectedScopeName: ScopedName{Scope: "rtd", Name: "test"}, + err: nil, + }, + { + name: "condition_scope_defaults_to_genera", + condition: "test.test", + expectedScopeName: ScopedName{Scope: "general", Name: "test"}, + err: nil, + }, + } + + for _, test := range testCases { + t.Run(test.name, func(t *testing.T) { + actualSN, actualErr := NewScopedName(test.condition) + if test.err == nil { + assert.Equal(t, test.expectedScopeName, actualSN) + assert.NoError(t, actualErr) + } else { + assert.EqualError(t, actualErr, test.err.Error()) + } + }) + } +} + +// constants +func getDefaultActivityConfig() config.Activity { + return config.Activity{ + Default: ptrutil.ToPtr(true), + Rules: []config.ActivityRule{ + { + Allow: true, + Condition: config.ActivityCondition{ + ComponentName: []string{"bidderA"}, + ComponentType: []string{"bidder"}, + }, + }, + }, + } +} + +func getDefaultActivityPlan() ActivityPlan { + return ActivityPlan{ + defaultResult: ActivityAllow, + rules: []ActivityRule{ + ComponentEnforcementRule{ + allowed: true, + componentName: []ScopedName{ + {Scope: "bidder", Name: "bidderA"}, + }, + componentType: []string{"bidder"}, + }, + }, + } } -func TestNilEnforcerShouldEnforce(t *testing.T) { - nilEnforcer := &NilPolicyEnforcer{} - assert.False(t, nilEnforcer.ShouldEnforce("")) - assert.False(t, nilEnforcer.ShouldEnforce("anyBidder")) +func getIncorrectActivityConfig() config.Activity { + return config.Activity{ + Default: ptrutil.ToPtr(true), + Rules: []config.ActivityRule{ + { + Allow: true, + Condition: config.ActivityCondition{ + ComponentName: []string{"bidder.bidderA.bidderB"}, + ComponentType: []string{"bidder"}, + }, + }, + }, + } } diff --git a/privacy/policyenforcer.go b/privacy/policyenforcer.go new file mode 100644 index 00000000000..e70c0d3d190 --- /dev/null +++ b/privacy/policyenforcer.go @@ -0,0 +1,45 @@ +package privacy + +// NOTE: Reanme this package. Will eventually replace in its entirety with Activites. + +// PolicyEnforcer determines if personally identifiable information (PII) should be removed or anonymized per the policy. +type PolicyEnforcer interface { + // CanEnforce returns true when policy information is specifically provided by the publisher. + CanEnforce() bool + + // ShouldEnforce returns true when the OpenRTB request should have personally identifiable + // information (PII) removed or anonymized per the policy. + ShouldEnforce(bidder string) bool +} + +// NilPolicyEnforcer implements the PolicyEnforcer interface but will always return false. +type NilPolicyEnforcer struct{} + +// CanEnforce is hardcoded to always return false. +func (NilPolicyEnforcer) CanEnforce() bool { + return false +} + +// ShouldEnforce is hardcoded to always return false. +func (NilPolicyEnforcer) ShouldEnforce(bidder string) bool { + return false +} + +// EnabledPolicyEnforcer decorates a PolicyEnforcer with an enabled flag. +type EnabledPolicyEnforcer struct { + Enabled bool + PolicyEnforcer PolicyEnforcer +} + +// CanEnforce returns true when the PolicyEnforcer can enforce. +func (p EnabledPolicyEnforcer) CanEnforce() bool { + return p.PolicyEnforcer.CanEnforce() +} + +// ShouldEnforce returns true when the enforcer is enabled the PolicyEnforcer allows enforcement. +func (p EnabledPolicyEnforcer) ShouldEnforce(bidder string) bool { + if p.Enabled { + return p.PolicyEnforcer.ShouldEnforce(bidder) + } + return false +} diff --git a/privacy/policyenforcer_test.go b/privacy/policyenforcer_test.go new file mode 100644 index 00000000000..b0c4032c714 --- /dev/null +++ b/privacy/policyenforcer_test.go @@ -0,0 +1,18 @@ +package privacy + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNilEnforcerCanEnforce(t *testing.T) { + nilEnforcer := &NilPolicyEnforcer{} + assert.False(t, nilEnforcer.CanEnforce()) +} + +func TestNilEnforcerShouldEnforce(t *testing.T) { + nilEnforcer := &NilPolicyEnforcer{} + assert.False(t, nilEnforcer.ShouldEnforce("")) + assert.False(t, nilEnforcer.ShouldEnforce("anyBidder")) +} diff --git a/static/bidder-info/adsinteractive.yaml b/static/bidder-info/adsinteractive.yaml index ae60ec88f10..d430e63b2f0 100644 --- a/static/bidder-info/adsinteractive.yaml +++ b/static/bidder-info/adsinteractive.yaml @@ -12,6 +12,6 @@ capabilities: - banner userSync: redirect: - url: "http://bid.adsinteractive.com/getuid?{{.RedirectURL}}" + url: "https://bid.adsinteractive.com/getuid?{{.RedirectURL}}" userMacro: "$AUID" diff --git a/static/bidder-info/aidem.yaml b/static/bidder-info/aidem.yaml new file mode 100644 index 00000000000..35ea898aa8a --- /dev/null +++ b/static/bidder-info/aidem.yaml @@ -0,0 +1,19 @@ +endpoint: "https://zero.aidemsrv.com/ortb/v2.6/bid/request" +maintainer: + email: prebid@aidem.com +modifyingVastXmlAllowed: true +capabilities: + app: + mediaTypes: + - banner + - video + - native + site: + mediaTypes: + - banner + - video + - native +userSync: + redirect: + url: https://gum.aidemsrv.com/prebid_sync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}} + userMacro: "$UID" diff --git a/static/bidder-info/consumable.yaml b/static/bidder-info/consumable.yaml index e1c4fc9b986..cc290149be2 100644 --- a/static/bidder-info/consumable.yaml +++ b/static/bidder-info/consumable.yaml @@ -11,6 +11,8 @@ capabilities: - banner userSync: redirect: - url: "https://e.serverbid.com/udb/9969/match?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir={{.RedirectURL}}" + url: "https://e.serverbid.com/udb/9969/match?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redir={{.RedirectURL}}" userMacro: "" # consumable appends the user id to end of the redirect url and does not utilize a macro +openrtb: + gpp-supported: true diff --git a/static/bidder-info/imds.yaml b/static/bidder-info/imds.yaml index e608a6c6c8c..491a5bd0ac6 100644 --- a/static/bidder-info/imds.yaml +++ b/static/bidder-info/imds.yaml @@ -13,8 +13,10 @@ capabilities: userSync: supportCors: true iframe: - url: "https://ad-cdn.technoratimedia.com/html/usersync.html?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&cb={{.RedirectURL}}" + url: "https://ad-cdn.technoratimedia.com/html/usersync.html?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gppsid={{.GPPSID}}&cb={{.RedirectURL}}" userMacro: "[USER_ID]" redirect: - url: "https://sync.technoratimedia.com/services?srv=cs&gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&cb={{.RedirectURL}}" + url: "https://sync.technoratimedia.com/services?srv=cs&gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gppsid={{.GPPSID}}&cb={{.RedirectURL}}" userMacro: "[USER_ID]" +openrtb: + gpp-supported: true diff --git a/static/bidder-info/ix.yaml b/static/bidder-info/ix.yaml index 9ac7dba32dc..4ab520ad81c 100644 --- a/static/bidder-info/ix.yaml +++ b/static/bidder-info/ix.yaml @@ -17,8 +17,10 @@ capabilities: - audio userSync: redirect: - url: "https://ssum.casalemedia.com/usermatchredir?s=194962&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&cb={{.RedirectURL}}" + url: "https://ssum.casalemedia.com/usermatchredir?s=194962&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gppsid={{.GPPSID}}&cb={{.RedirectURL}}" userMacro: "" iframe: - url: "https://ssum-sec.casalemedia.com/usermatch?s=184674&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&cb={{.RedirectURL}}" + url: "https://ssum-sec.casalemedia.com/usermatch?s=184674&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gppsid={{.GPPSID}}&cb={{.RedirectURL}}" # ix appends the user id to end of the redirect url and does not utilize a macro +openrtb: + gpp-supported: true diff --git a/static/bidder-info/mediafuse.yaml b/static/bidder-info/mediafuse.yaml index b78b21e16ea..be19cc6c68a 100644 --- a/static/bidder-info/mediafuse.yaml +++ b/static/bidder-info/mediafuse.yaml @@ -12,5 +12,5 @@ capabilities: - banner - video - native -userSync: - key: "adnxs" +# userSync: +# key: "adnxs" diff --git a/static/bidder-info/pwbid.yaml b/static/bidder-info/pwbid.yaml index ac6872738b4..a172ff39c83 100644 --- a/static/bidder-info/pwbid.yaml +++ b/static/bidder-info/pwbid.yaml @@ -1,4 +1,4 @@ -endpoint: "https://bid.pubwise.io/prebid" +endpoint: "https://bidder.east2.pubwise.io/bid/pubwisedirect" maintainer: email: info@pubwise.io gvlVendorID: 842 @@ -17,4 +17,4 @@ userSync: # PubWise 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 \ No newline at end of file + - redirect diff --git a/static/bidder-info/triplelift.yaml b/static/bidder-info/triplelift.yaml index 45811c0c868..605bcc71e6e 100644 --- a/static/bidder-info/triplelift.yaml +++ b/static/bidder-info/triplelift.yaml @@ -16,9 +16,11 @@ userSync: # Contact this bidder directly at the email address above to ask about enabling user sync. # iframe: - url: "https://eb2.3lift.com/sync?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir={{.RedirectURL}}" + url: "https://eb2.3lift.com/sync?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redir={{.RedirectURL}}" userMacro: $UID redirect: - url: "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir={{.RedirectURL}}" + url: "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redir={{.RedirectURL}}" userMacro: "$UID" -endpointCompression: "GZIP" \ No newline at end of file +endpointCompression: "GZIP" +openrtb: + gpp-supported: true \ No newline at end of file diff --git a/static/bidder-info/triplelift_native.yaml b/static/bidder-info/triplelift_native.yaml index 85ebd1a52cc..ff93b544c4c 100644 --- a/static/bidder-info/triplelift_native.yaml +++ b/static/bidder-info/triplelift_native.yaml @@ -16,8 +16,10 @@ userSync: # Contact this bidder directly at the email address above to ask about enabling user sync. # iframe: - url: "https://eb2.3lift.com/sync?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir={{.RedirectURL}}" + url: "https://eb2.3lift.com/sync?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redir={{.RedirectURL}}" userMacro: $UID redirect: - url: "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir={{.RedirectURL}}" - userMacro: "$UID" \ No newline at end of file + url: "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redir={{.RedirectURL}}" + userMacro: "$UID" +openrtb: + gpp-supported: true \ No newline at end of file diff --git a/static/bidder-info/yahooAds.yaml b/static/bidder-info/yahooAds.yaml new file mode 100644 index 00000000000..a2581387152 --- /dev/null +++ b/static/bidder-info/yahooAds.yaml @@ -0,0 +1,18 @@ +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 +userSync: + # yahooAds 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 \ No newline at end of file diff --git a/static/bidder-params/aidem.json b/static/bidder-params/aidem.json new file mode 100644 index 00000000000..221e5ff7a92 --- /dev/null +++ b/static/bidder-params/aidem.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "AIDEM Adapter Params", + "description": "A schema which validates params accepted by the AIDEM adapter", + "type": "object", + "properties": { + "siteId": { + "type": "string", + "minLength": 1, + "description": "Unique site ID" + }, + "publisherId": { + "type": "string", + "minLength": 1, + "description": "Unique publisher ID" + }, + "placementId": { + "type": "string", + "minLength": 1, + "description": "Unique publisher ttag ID" + }, + "rateLimit": { + "type": "number", + "minimum": 0, + "maximum": 1 + } + }, + "required": ["siteId", "publisherId"] +} \ No newline at end of file diff --git a/static/bidder-params/ix.json b/static/bidder-params/ix.json index a7a5cb7308a..172690cca32 100644 --- a/static/bidder-params/ix.json +++ b/static/bidder-params/ix.json @@ -27,6 +27,11 @@ "minItems": 2, "maxItems": 2, "description": "An array of two integer containing the dimension" + }, + "sid": { + "type": "string", + "minLength": 1, + "description": "Slot ID" } }, "oneOf": [ diff --git a/static/bidder-params/yahooAds.json b/static/bidder-params/yahooAds.json new file mode 100644 index 00000000000..77e7107350c --- /dev/null +++ b/static/bidder-params/yahooAds.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "YahooAds Adapter Params", + "description": "A schema which validates params accepted by the YahooAds 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"] +}