-
Notifications
You must be signed in to change notification settings - Fork 748
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
Add Support For SkAdN + Refactor Split Imps #1741
Changes from all commits
e8ddcdd
257a5a9
043d65c
531702e
5e6b012
e6c6e64
60fe865
418fb3a
3b4760d
d371b12
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
{ | ||
"description": "The imp.ext.skadn field is valid for Apple's SKAdNetwork and should be exempt from bidder name validation.", | ||
|
||
"mockBidRequest": { | ||
"id": "some-request-id", | ||
"site": { | ||
"page": "test.somepage.com" | ||
}, | ||
"imp": [{ | ||
"id": "some-imp-id", | ||
"banner": { | ||
"format": [{ | ||
"w": 600, | ||
"h": 500 | ||
}, { | ||
"w": 300, | ||
"h": 600 | ||
}] | ||
}, | ||
"ext": { | ||
"prebid": { | ||
"bidder": { | ||
"appnexus": { | ||
"placementId": 12883451 | ||
} | ||
} | ||
}, | ||
"skadn": { | ||
"anyMember": "anyValue" | ||
} | ||
} | ||
}] | ||
}, | ||
"expectedBidResponse": { | ||
"id": "some-request-id", | ||
"seatbid": [{ | ||
"bid": [{ | ||
"id": "appnexus-bid", | ||
"impid": "", | ||
"price": 0 | ||
}], | ||
"seat": "appnexus-bids" | ||
}], | ||
"bidid": "test bid id", | ||
"nbr": 0 | ||
}, | ||
"expectedReturnCode": 200 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -61,7 +61,6 @@ | |
"bidder": { | ||
"placementId": 1 | ||
}, | ||
"prebid": {}, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously, |
||
"context": { | ||
"data": { | ||
"keywords": "prebid server example" | ||
|
@@ -112,7 +111,6 @@ | |
"siteId": 2, | ||
"zoneId": 3 | ||
}, | ||
"prebid": {}, | ||
"context": { | ||
"data": { | ||
"keywords": "prebid server example" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -59,8 +59,9 @@ func cleanOpenRTBRequests(ctx context.Context, | |
usersyncIfAmbiguous bool, | ||
privacyConfig config.Privacy) (bidderRequests []BidderRequest, privacyLabels metrics.PrivacyLabels, errs []error) { | ||
|
||
impsByBidder, errs := splitImps(req.BidRequest.Imp) | ||
if len(errs) > 0 { | ||
impsByBidder, err := splitImps(req.BidRequest.Imp) | ||
if err != nil { | ||
errs = []error{err} | ||
return | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This now returns the first error encountered instead of an array of all errors. The only errors possible here are from marshalling, which is very likely to be impossible at this stage in request processing. Current work on request wrappers to cut down on marshalling will remove the possibility for errors here completely. |
||
|
||
|
@@ -327,106 +328,99 @@ func extractBuyerUIDs(user *openrtb.User) (map[string]string, error) { | |
// The "imp.ext" value of the rubicon Imp will only contain the "prebid" values, and "rubicon" value at the "bidder" key. | ||
// | ||
// The goal here is so that Bidders only get Imps and Imp.Ext values which are intended for them. | ||
func splitImps(imps []openrtb.Imp) (map[string][]openrtb.Imp, []error) { | ||
impExts, err := parseImpExts(imps) | ||
if err != nil { | ||
return nil, []error{err} | ||
} | ||
func splitImps(imps []openrtb.Imp) (map[string][]openrtb.Imp, error) { | ||
bidderImps := make(map[string][]openrtb.Imp) | ||
|
||
splitImps := make(map[string][]openrtb.Imp, len(imps)) | ||
var errList []error | ||
for i, imp := range imps { | ||
var impExt map[string]json.RawMessage | ||
if err := json.Unmarshal(imp.Ext, &impExt); err != nil { | ||
return nil, fmt.Errorf("invalid json for imp[%d]: %v", i, err) | ||
} | ||
|
||
for i := 0; i < len(imps); i++ { | ||
imp := imps[i] | ||
impExt := impExts[i] | ||
var impExtPrebid map[string]json.RawMessage | ||
if impExtPrebidJSON, exists := impExt[openrtb_ext.PrebidExtKey]; exists { | ||
// validation already performed by impExt unmarshal. no error is possible here, proven by tests. | ||
json.Unmarshal(impExtPrebidJSON, &impExtPrebid) | ||
} | ||
|
||
var firstPartyDataContext json.RawMessage | ||
if context, exists := impExt[openrtb_ext.FirstPartyDataContextExtKey]; exists { | ||
firstPartyDataContext = context | ||
var impExtPrebidBidder map[string]json.RawMessage | ||
if impExtPrebidBidderJSON, exists := impExtPrebid[openrtb_ext.PrebidExtBidderKey]; exists { | ||
// validation already performed by impExt unmarshal. no error is possible here, proven by tests. | ||
json.Unmarshal(impExtPrebidBidderJSON, &impExtPrebidBidder) | ||
} | ||
|
||
rawPrebidExt, ok := impExt[openrtb_ext.PrebidExtKey] | ||
sanitizedImpExt, err := createSanitizedImpExt(impExt, impExtPrebid) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to remove other bidder fields for imp[%d]: %v", i, err) | ||
} | ||
|
||
if ok { | ||
var prebidExt openrtb_ext.ExtImpPrebid | ||
for bidder, bidderExt := range extractBidderExts(impExt, impExtPrebidBidder) { | ||
impCopy := imp | ||
|
||
if err := json.Unmarshal(rawPrebidExt, &prebidExt); err == nil && prebidExt.Bidder != nil { | ||
if errs := sanitizedImpCopy(&imp, prebidExt.Bidder, rawPrebidExt, firstPartyDataContext, &splitImps); errs != nil { | ||
errList = append(errList, errs...) | ||
} | ||
sanitizedImpExt[openrtb_ext.PrebidExtBidderKey] = bidderExt | ||
|
||
continue | ||
impExtJSON, err := json.Marshal(sanitizedImpExt) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to remove other bidder fields for imp[%d]: cannot marshal ext: %v", i, err) | ||
} | ||
} | ||
impCopy.Ext = impExtJSON | ||
|
||
if errs := sanitizedImpCopy(&imp, impExt, rawPrebidExt, firstPartyDataContext, &splitImps); errs != nil { | ||
errList = append(errList, errs...) | ||
bidderImps[bidder] = append(bidderImps[bidder], impCopy) | ||
} | ||
} | ||
|
||
return splitImps, nil | ||
return bidderImps, nil | ||
} | ||
|
||
// sanitizedImpCopy returns a copy of imp with its ext filtered so that only "prebid", "context", and bidder params exist. | ||
// It will not mutate the input imp. | ||
// This function will write the new imps to the output map passed in | ||
func sanitizedImpCopy(imp *openrtb.Imp, | ||
bidderExts map[string]json.RawMessage, | ||
rawPrebidExt json.RawMessage, | ||
firstPartyDataContext json.RawMessage, | ||
out *map[string][]openrtb.Imp) []error { | ||
|
||
var prebidExt map[string]json.RawMessage | ||
var errs []error | ||
|
||
if err := json.Unmarshal(rawPrebidExt, &prebidExt); err == nil { | ||
// Remove the entire bidder field. We will already have the content we need in bidderExts. We | ||
// don't want to include other demand partners' bidder params in the sanitized imp. | ||
if _, hasBidderField := prebidExt["bidder"]; hasBidderField { | ||
delete(prebidExt, "bidder") | ||
func createSanitizedImpExt(impExt, impExtPrebid map[string]json.RawMessage) (map[string]json.RawMessage, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This refactor is definitely an improvement, however it is still a little confusing that this function takes the unmarshaled and marshaled versions of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It may go away with the wrapper work and it will certainly go away when I agree having to ignore |
||
sanitizedImpExt := make(map[string]json.RawMessage, 3) | ||
|
||
var err error | ||
if rawPrebidExt, err = json.Marshal(prebidExt); err != nil { | ||
errs = append(errs, err) | ||
} | ||
delete(impExtPrebid, openrtb_ext.PrebidExtBidderKey) | ||
if len(impExtPrebid) > 0 { | ||
if impExtPrebidJSON, err := json.Marshal(impExtPrebid); err == nil { | ||
sanitizedImpExt[openrtb_ext.PrebidExtKey] = impExtPrebidJSON | ||
} else { | ||
return nil, fmt.Errorf("cannot marshal ext.prebid: %v", err) | ||
} | ||
} | ||
|
||
for bidder, ext := range bidderExts { | ||
if bidder == openrtb_ext.PrebidExtKey || bidder == openrtb_ext.FirstPartyDataContextExtKey { | ||
continue | ||
} | ||
if v, exists := impExt[openrtb_ext.FirstPartyDataContextExtKey]; exists { | ||
sanitizedImpExt[openrtb_ext.FirstPartyDataContextExtKey] = v | ||
} | ||
|
||
impCopy := *imp | ||
newExt := make(map[string]json.RawMessage, 3) | ||
if v, exists := impExt[openrtb_ext.SKAdNExtKey]; exists { | ||
sanitizedImpExt[openrtb_ext.SKAdNExtKey] = v | ||
} | ||
|
||
newExt["bidder"] = ext | ||
return sanitizedImpExt, nil | ||
} | ||
|
||
if rawPrebidExt != nil { | ||
newExt[openrtb_ext.PrebidExtKey] = rawPrebidExt | ||
} | ||
func extractBidderExts(impExt, impExtPrebidBidders map[string]json.RawMessage) map[string]json.RawMessage { | ||
bidderExts := make(map[string]json.RawMessage) | ||
|
||
if len(firstPartyDataContext) > 0 { | ||
newExt[openrtb_ext.FirstPartyDataContextExtKey] = firstPartyDataContext | ||
} | ||
// prefer imp.ext.prebid.bidder.BIDDER | ||
for bidder, bidderExt := range impExtPrebidBidders { | ||
bidderExts[bidder] = bidderExt | ||
} | ||
|
||
rawExt, err := json.Marshal(newExt) | ||
if err != nil { | ||
errs = append(errs, err) | ||
// fallback to imp.BIDDER | ||
for bidder, bidderExt := range impExt { | ||
if isSpecialField(bidder) { | ||
continue | ||
} | ||
|
||
impCopy.Ext = rawExt | ||
|
||
otherImps, _ := (*out)[bidder] | ||
(*out)[bidder] = append(otherImps, impCopy) | ||
if _, exists := bidderExts[bidder]; !exists { | ||
bidderExts[bidder] = bidderExt | ||
} | ||
} | ||
|
||
if len(errs) > 0 { | ||
return errs | ||
} | ||
return bidderExts | ||
} | ||
|
||
return nil | ||
func isSpecialField(bidder string) bool { | ||
return bidder == openrtb_ext.FirstPartyDataContextExtKey || | ||
bidder == openrtb_ext.SKAdNExtKey || | ||
bidder == openrtb_ext.PrebidExtKey | ||
} | ||
|
||
// prepareUser changes req.User so that it's ready for the given bidder. | ||
|
@@ -564,21 +558,6 @@ func resolveBidder(bidder string, aliases map[string]string) openrtb_ext.BidderN | |
return openrtb_ext.BidderName(bidder) | ||
} | ||
|
||
// parseImpExts does a partial-unmarshal of the imp[].Ext field. | ||
// The keys in the returned map are expected to be "prebid", "context", CoreBidderNames, or Aliases for this request. | ||
func parseImpExts(imps []openrtb.Imp) ([]map[string]json.RawMessage, error) { | ||
exts := make([]map[string]json.RawMessage, len(imps)) | ||
// Loop over every impression in the request | ||
for i := 0; i < len(imps); i++ { | ||
// Unpack each set of extensions found in the Imp array | ||
err := json.Unmarshal(imps[i].Ext, &exts[i]) | ||
if err != nil { | ||
return nil, fmt.Errorf("Error unpacking extensions for Imp[%d]: %s", i, err.Error()) | ||
} | ||
} | ||
return exts, nil | ||
} | ||
|
||
// parseAliases parses the aliases from the BidRequest | ||
func parseAliases(orig *openrtb.BidRequest) (map[string]string, []error) { | ||
var aliases map[string]string | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is now. :)