Skip to content
This repository has been archived by the owner on Dec 22, 2022. It is now read-only.

Commit

Permalink
Moved bid validation into some adapter middleware. (prebid#772)
Browse files Browse the repository at this point in the history
* Moved bidd validation into some adapter middleware.

* Ran gofmt -s to fix the build.
  • Loading branch information
dbemiller authored Dec 14, 2018
1 parent 7e9d4e9 commit ff8bee1
Show file tree
Hide file tree
Showing 5 changed files with 387 additions and 354 deletions.
8 changes: 8 additions & 0 deletions exchange/adapter_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter

allBidders := make(map[openrtb_ext.BidderName]adaptedBidder, len(ortbBidders)+len(legacyBidders))

// Wrap legacy and openrtb Bidders behind a common interface, so that the Exchange doesn't need to concern
// itself with the differences.
for name, bidder := range legacyBidders {
// Clean out any disabled bidders
if isEnabledBidder(cfg.Adapters, string(name)) {
Expand All @@ -87,6 +89,12 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter
allBidders[name] = adaptBidder(adapters.EnforceBidderInfo(bidder, infos[string(name)]), client)
}
}

// Apply any middleware used for global Bidder logic.
for name, bidder := range allBidders {
allBidders[name] = ensureValidBids(bidder)
}

return allBidders
}

Expand Down
119 changes: 119 additions & 0 deletions exchange/bidder_validate_bids.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package exchange

import (
"context"
"errors"
"fmt"
"strings"

"github.com/mxmCherry/openrtb"
"github.com/prebid/prebid-server/openrtb_ext"
"golang.org/x/text/currency"
)

// ensureValidBids returns a bidder that removes invalid bids from the argument bidder's response.
// These will be converted into errors instead.
//
// The goal here is to make sure that the response contains Bids which are valid given the initial Request,
// so that Publishers can trust the Bids they get from Prebid Server.
func ensureValidBids(bidder adaptedBidder) adaptedBidder {
return &validatedBidder{
bidder: bidder,
}
}

type validatedBidder struct {
bidder adaptedBidder
}

func (v *validatedBidder) requestBid(ctx context.Context, request *openrtb.BidRequest, name openrtb_ext.BidderName, bidAdjustment float64) (*pbsOrtbSeatBid, []error) {
seatBid, errs := v.bidder.requestBid(ctx, request, name, bidAdjustment)
if validationErrors := removeInvalidBids(request, seatBid); len(validationErrors) > 0 {
errs = append(errs, validationErrors...)
}
return seatBid, errs
}

// validateBids will run some validation checks on the returned bids and excise any invalid bids
func removeInvalidBids(request *openrtb.BidRequest, seatBid *pbsOrtbSeatBid) []error {
// Exit early if there is nothing to do.
if seatBid == nil || len(seatBid.bids) == 0 {
return nil
}

// By design, default currency is USD.
if cerr := validateCurrency(request.Cur, seatBid.currency); cerr != nil {
seatBid.bids = nil
return []error{cerr}
}

errs := make([]error, 0, len(seatBid.bids))
validBids := make([]*pbsOrtbBid, 0, len(seatBid.bids))
for _, bid := range seatBid.bids {
if ok, berr := validateBid(bid); ok {
validBids = append(validBids, bid)
} else {
errs = append(errs, berr)
}
}
seatBid.bids = validBids
return errs
}

// validateCurrency will run currency validation checks and return true if it passes, false otherwise.
func validateCurrency(requestAllowedCurrencies []string, bidCurrency string) error {
// Default currency is `USD` by design.
defaultCurrency := "USD"
// Make sure bid currency is a valid ISO currency code
if bidCurrency == "" {
// If bid currency is not set, then consider it's default currency.
bidCurrency = defaultCurrency
}
currencyUnit, cerr := currency.ParseISO(bidCurrency)
if cerr != nil {
return cerr
}
// Make sure the bid currency is allowed from bid request via `cur` field.
// If `cur` field array from bid request is empty, then consider it accepts the default currency.
currencyAllowed := false
if len(requestAllowedCurrencies) == 0 {
requestAllowedCurrencies = []string{defaultCurrency}
}
for _, allowedCurrency := range requestAllowedCurrencies {
if strings.ToUpper(allowedCurrency) == currencyUnit.String() {
currencyAllowed = true
break
}
}
if currencyAllowed == false {
return fmt.Errorf(
"Bid currency is not allowed. Was '%s', wants: ['%s']",
currencyUnit.String(),
strings.Join(requestAllowedCurrencies, "', '"),
)
}

return nil
}

// validateBid will run the supplied bid through validation checks and return true if it passes, false otherwise.
func validateBid(bid *pbsOrtbBid) (bool, error) {
if bid.bid == nil {
return false, errors.New("Empty bid object submitted.")
}

if bid.bid.ID == "" {
return false, errors.New("Bid missing required field 'id'")
}
if bid.bid.ImpID == "" {
return false, fmt.Errorf("Bid \"%s\" missing required field 'impid'", bid.bid.ID)
}
if bid.bid.Price <= 0.0 {
return false, fmt.Errorf("Bid \"%s\" does not contain a positive 'price'", bid.bid.ID)
}
if bid.bid.CrID == "" {
return false, fmt.Errorf("Bid \"%s\" missing creative ID", bid.bid.ID)
}

return true, nil
}
Loading

0 comments on commit ff8bee1

Please sign in to comment.