Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Conditional Impressions #2168

Closed
bretg opened this issue Feb 28, 2022 · 9 comments
Closed

Conditional Impressions #2168

bretg opened this issue Feb 28, 2022 · 9 comments

Comments

@bretg
Copy link
Contributor

bretg commented Feb 28, 2022

Prebid.js supports different ways of refining AdUnits (i.e. imps) based on parameters like size and custom labels

https://docs.prebid.org/dev-docs/conditional-ad-units.html
https://docs.prebid.org/dev-docs/modules/sizeMappingV2.html

Prebid Server should support these extensions to enable a broader shift to server-side bidding as well as supporting the mobile app scenario.

Feature: Labels

The 'labels' feature allows certain imp objects and certain bidders to be conditional based on the definition of a generic string in the request called a 'label'.

See the label examples in https://docs.prebid.org/dev-docs/conditional-ad-units.html for use cases.

Defining the Label Value

The proposal is to define the value of the label on ext.prebid.labels, which is an array of strings.

ext.prebid.labels: ["tablet", "brazil"]

Labels can also be defined through the conditional responsive logic described in the next feature.

Conditional Logic

New attributes are supported as extensions that may include the object:

  • imp[].ext.prebid.labelany[] - by default, skip this imp object unless there's an intersection between ext.prebid.labels and this array of strings.
  • imp[].ext.prebid.labelall[] - by default, skip this imp object unless every value in labelall is also present in ext.prebid.labels
  • imp[].ext.prebid.bidder.BIDDER.labelany[] - by default, skip this bidder in this imp unless there's an intersection between ext.prebid.labels and this array of strings.
  • imp[].ext.prebid.bidder.BIDDER.labelall[] - by default, skip this bidder in this imp object unless every value in labelall is also present in ext.prebid.labels

Feature: SizeConfig

Some publishers have complex rules for which imps, bidders, and mediatypes are relevant based on device viewport size.

Prebid.js supports two ways for publishers to define the responsive screen-size behavior:

  1. PBJS-core supports a relatively simple global SizeConfig setting
  2. The Advanced Size Mapping module super-charges sizeConfig, supporting it at both the AdUnit and Bidder level.

There are three features here all based on the size of the user's screen:

  1. the set of ad sizes used may differ
  2. which bidders are included may differ
  3. which mediatypes a given bidder utilizes may differ

Global sizeconfig

The proposed feature is to support ext.prebid.sizeconfig in a similar format as that used by Prebid.js:

"device": {                                                           // this is always specified in the request
   "w": WIDTH,
   "h": HEIGHT
},
ext.prebid.sizeconfig: [{                                   // this may be specified in a stored request
        'minViewPort': [0, 0],                                // note inside this block is camelCase
        'sizes': [
            {'w':300, 'h':250},
            {'w':300, 'h':100}
        ],
        'labels': ['phone']
    }, {
        minViewPort: [760, 0],
        'sizes': [
            {'w':728, 'h':90},
            {'w':300, 'h':250}
        ],
        'labels': ['tablet']
    }, {
        minViewPort: [1200, 0],
        'sizes': [
            {'w':970, 'h':90},
            {'w':728, 'h':90},
            {'w':300, 'h':250}
        ],
        'labels': ['desktop']
    }, {
        minViewPort: [1600, 0], 
        'sizes': [
            {'w':1000, 'h':300},
            {'w':970, 'h':90},
            {'w':728, 'h':90},
            {'w':300, 'h':250}
        ],
        'labels': ['desktop-hd']
    }]

PBS logic:

  1. If ext.prebid.sizeconfig is defined but device.w/h are not defined, reject the request with HTTP 400.
  2. Resolve the minViewPort blocks to determine the 'sizes' and 'labels'
  3. Process all imps in the request and remove any sizes not on the sizes array
  4. Add the 'labels' to any defined on ext.prebid.labels for use in processing the labelany/labelall

PBJS logic:
The pbsBidAdapter should not pass ext.prebid.sizeconfig because it would have to convert mediaQueries to this different format. Instead, it should resolve the PBJS mediaQuery locally and:

  • pass the labels on ext.prebid.labels
  • filter all imps to remove any sizes not on sizesSupported. If any imps are left without a size, they should be removed from the auction

AdUnit-Level SizeConfig

This is really just sizeConfig on a per-AdUnit basis rather than globally. But Prebid.js implemented it with a different (simplified) syntax.

"device": {                                                                                          // this is always specified in the request
   "w": WIDTH,
   "h": HEIGHT
},
imp[].ext.prebid.mediatypes.banner.sizeconfig: [                          // this may be specified in a stored request
          { minViewPort: [0, 0], sizes: [{'w':300, 'h':250}, {'w':300, 'h':100}] },
          { minViewPort: [768, 0], sizes: [{'w':728, 'h':90}, {'w':300, 'h':250}] },
          { minViewPort: [1200, 0], sizes: [{'w':970, 'h':90, {'w':728, 'h':90}, {'w':300, 'h':250}] },
          { minViewPort: [1600, 0], sizes: [{'w':1000, 'h':300}, {'w':970, 'h':90}, {'w':728, 'h':90}, {'w':300, 'h':250} }
        ],
imp[].ext.prebid.mediatypes.video.sizeconfig: [                            // this may be specified in a stored request
          { minViewPort: [0, 0], playerSize: {'w':640, 'h':400} },                   // dumb example, but you get the point
          { minViewPort: [768, 0], playerSize: {'w':640, 'h':400} },
          { minViewPort: [1200, 0], playerSize: {'w':640, 'h':400} }
        ],
imp[].ext.prebid.mediatypes.native.sizeconfig: [                          // this may be specified in a stored request
          { minViewPort: [0, 0], active: false },
          { minViewPort: [768, 0], active: true },
          { minViewPort: [1200, 0], active: true }
        ],

PBS logic:

  1. If imp[].ext.prebid.mediatypes.MEDIATYPE.sizeconfig is defined but device.w/h are not defined, reject the request with HTTP 400.
  2. Resolve the minViewPort blocks to determine the 'sizes'
  3. For MEDIATYPE banner:
    1. Overwrite this imp[].format[] array with the results from 'sizes'.
    2. Check imp[].w and imp[].h -- remove them if pair is not on the 'sizes' array.
    3. Reject the request with a 400 if any of the sizes is not a number.
  4. For MEDIATYPE video, overwrite this imp[].video.w/h with the values from 'playerSize'. If the values are not numbers, reject the request with a 400.
  5. For MEDIATYPE native, remove this imp[].native block if 'active' is false.
  6. Ignore global sizeConfig

PBJS logic:

  • the pbsBidAdapter should just pass the AdUnit sizeConfig on the relevant imp[].ext.prebid.mediatypes.TYPE.sizeconfig

Bidder-Level SizeConfig

Finally, at the bidder level, the responsive overrides can remove request mediaTypes and overwrite sizes. (Note: PBJS doesn't yet support the size override part, but there's an enhancement request in place, so we may as build it when we do this feature.)

"device": {                                                           // this is always specified in the request
   "w": WIDTH,
   "h": HEIGHT
},
imp[].ext.prebid.bidder.BIDDER.sizeconfig: [
            { minViewPort: [0, 0], relevantMediaTypes: ['none'] },
            { minViewPort: [700, 0], relevantMediaTypes: ['banner'], sizes: [{'w':728, 'h':90}, {'w':300, 'h':250}] },
            { minViewPort: [1300, 0], relevantMediaTypes: ['video'] }
        ]

PBS logic:

  1. If imp[].ext.prebid.bidder.BIDDER.sizeconfig is defined but device.w/h are not defined, reject the request with HTTP 400.
  2. Resolve the minViewPort blocks to determine the 'relevantMediaTypes' and 'sizes'
  3. If 'relevantMediaTypes' is defined, then before sending this request to the bid adapter, remove imp[].banner, imp[].video, and/or imp[].native as instructed. i.e. if that type isn't considered relevant for this bidder, remove it.
    1. If any value other than banner, video, audio, or native is defined in this array, reject the request with a 400 and a decent error message.
  4. If 'sizes' is defined, then before sending this request to the bid adapter:
    1. Overwrite this imp[].format[] array with the results from 'sizes'.
    2. Check imp[].w and imp[].h -- remove them if pair is not on the 'sizes' array.
    3. Reject the request with a 400 if any of the sizes is not a number.
  5. Ignore global sizeConfig

PBJS logic:

  • the pbsBidAdapter should just pass the Bidder-level sizeConfig on the relevant imp[].ext.prebid.bidder.BIDDER.sizeconfig

Resolving minViewPort

This is a simpler implementation than a mediaQuery -- it's basically just the min/max-width/height attributes.

minViewPort: [W1, H1]
minViewPort: [W2, H2]

Logic:

  1. Walk down the list of minViewPort entries until one of them is true.
  2. It's true if
    1. if minViewPort.W>0 and device.w <= minViewPort.W
    2. if minViewPort.H>0 and device.h <= minViewPort.H
@bretg
Copy link
Contributor Author

bretg commented Mar 4, 2022

Discussed in committee. Initial feedback is to not use mediaQuery, and perhaps to use an object for sizes instead of array-of-arrays. Will update the description above next week.

@bretg
Copy link
Contributor Author

bretg commented Mar 10, 2022

Feedback from the PBJS team was that converting size arrays to a size object is not a performance concern. Spec updated above.

@dgirardi
Copy link
Contributor

For bidder-level sizeConfig, the client may need to know which relevantMediaTypes and sizes were applied to any given bid in the response. Otherwise there's a potential issue with "extra" bids: if PBJS requests an imp with two bidders A and B, and PBS responds with bids from A, B, and C, we cannot resolve the correct mediaType for C client-side - which matters for the current implementation (although it's possible that it should be made to work without).

Also, from PBJS' point of view, if it's not going to use the global sizeConfig, server-side labels are not that useful either - they'd act as a pure yes/no filter that is entirely resolved before the request is made. If the intent is to harmonize the interfaces, it's worth noting that the current logic is "if any ad unit in a particular auction is using adUnit or bidder-level sizeConfig, ignore the global sizeConfig entirely".

@bretg
Copy link
Contributor Author

bretg commented Mar 29, 2022

we cannot resolve the correct mediaType for C client-side

mediaType is in seatbid.bid.ext.prebid.type

Adding @SyntaxNode and @SerhiiNahornyi to confirm this value is always present in both PBS-Go and PBS-Java. Based on the bid adapter instructions, I suspect it's always available?

      b := &adapters.TypedBid{
        Bid:     &seatBid.Bid[i],
        BidType: getMediaTypeForBid(bid),  // is this where seatbid.bid.ext.prebid.type comes from?
      }

current logic is "if any ad unit in a particular auction is using adUnit or bidder-level sizeConfig, ignore the global sizeConfig entirely"

Cool - thanks @dgirardi , updated the spec above to note this.

@dgirardi
Copy link
Contributor

mediaType is in seatbid.bid.ext.prebid.type

I am referring to the client-side mediaType object, which encapsulates sizes among other things. Currently PBJS expects to have that for every bid in the response, which is not a problem because it knows what it requested - but that would no longer be true for conditional "extra" bids.

@bretg
Copy link
Contributor Author

bretg commented Mar 29, 2022

Currently PBJS expects to have the client-side mediaType object for every bid in the response

Remind me why the pbsBidAdapter can't just copy the relevant mediaType object from the original AdUnit?

@dgirardi
Copy link
Contributor

Take this scenario:

pbjs.addAdUnits({
    code: 'conditional',
    mediaTypes: {
          banner: {
               sizeConfig: { // something irrelevant here, as bidder-level settings override this }
          }, 
          bids: [{
               bidder: 'A',
               sizeConfig: { minViewPort: [700, 0], relevantMediaTypes: ['banner'], sizes: [{'w':250, 'h':100}, {'w':400, 'h':300}] },
          }, {
               bidder: 'B',
               sizeConfig: { minViewPort: [700, 0], relevantMediaTypes: ['banner'], sizes: [{'w':400, 'h':300}, {'w':600, 'h':400}] }
          }]
    }
})

And say the response contains an "extra" bid from C for size 400x300. PBJS does not know what was actually requested from C, because it does not know what sizeConfig was applied; the adUnit-level sizeConfig may even resolve to something completely different. So it effectively cannot compute the requested mediaType.

I am sure it's possible to refactor client side logic so that we don't need to know that for everything in the response, but currently the codebase assumes that's always available.

@dgirardi
Copy link
Contributor

Looking quickly at the PBJS codebase, I believe the only thing that would need to be cut is banner size validation on the response - which does seem very optional to me. So it's probably OK for Prebid to not know precisely what was requested from extra bidders.

@bretg
Copy link
Contributor Author

bretg commented Oct 6, 2023

Discussed in committee. We've decided to let this one go. Conditional existence of an adunit or bidders can (and maybe should) be done in a module.

@bretg bretg closed this as completed Oct 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

No branches or pull requests

2 participants