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

Add cpmOverride #1289

Merged
merged 3 commits into from
May 14, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 102 additions & 9 deletions adapters/rubicon/rubicon.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@ type rubiconParams struct {
Video rubiconVideoParams `json:"video"`
}

type bidRequestExt struct {
Rubicon bidRequestExtRubicon `json:"rubicon,omitempty"`
}

type bidRequestExtRubicon struct {
Debug bidRequestExtRubiconDebug `json:"debug,omitempty"`
}

type bidRequestExtRubiconDebug struct {
CpmOverride float64 `json:"cpmOverride,omitempty"`
}

type rubiconImpExtRPTrack struct {
Mint string `json:"mint"`
MintVersion string `json:"mint_version"`
Expand Down Expand Up @@ -578,6 +590,7 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap

requestImpCopy := request.Imp

rubiconRequest := makeRubiconRequest(request)
for i := 0; i < numRequests; i++ {
thisImp := requestImpCopy[i]

Expand Down Expand Up @@ -677,14 +690,14 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap
errs = append(errs, err)
continue
}
request.User = &userCopy
rubiconRequest.User = &userCopy
}

if request.Device != nil {
deviceCopy := *request.Device
deviceExt := rubiconDeviceExt{RP: rubiconDeviceExtRP{PixelRatio: request.Device.PxRatio}}
deviceCopy.Ext, err = json.Marshal(&deviceExt)
request.Device = &deviceCopy
rubiconRequest.Device = &deviceCopy
}

isVideo := isVideo(thisImp)
Expand Down Expand Up @@ -732,28 +745,28 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap
siteCopy.Ext, err = json.Marshal(&siteExt)
siteCopy.Publisher = &openrtb.Publisher{}
siteCopy.Publisher.Ext, err = json.Marshal(&pubExt)
request.Site = &siteCopy
rubiconRequest.Site = &siteCopy
}
if request.App != nil {
appCopy := *request.App
appCopy.Ext, err = json.Marshal(&siteExt)
appCopy.Publisher = &openrtb.Publisher{}
appCopy.Publisher.Ext, err = json.Marshal(&pubExt)
request.App = &appCopy
rubiconRequest.App = &appCopy
}

reqBadv := request.BAdv
if reqBadv != nil {
if len(reqBadv) > badvLimitSize {
request.BAdv = reqBadv[:badvLimitSize]
rubiconRequest.BAdv = reqBadv[:badvLimitSize]
}
}

request.Imp = []openrtb.Imp{thisImp}
request.Cur = nil
request.Ext = nil
rubiconRequest.Imp = []openrtb.Imp{thisImp}
rubiconRequest.Cur = nil
rubiconRequest.Ext = nil

reqJSON, err := json.Marshal(request)
reqJSON, err := json.Marshal(rubiconRequest)
if err != nil {
errs = append(errs, err)
continue
Expand All @@ -772,6 +785,31 @@ func (a *RubiconAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adap
return requestData, errs
}

func makeRubiconRequest(internalRequest *openrtb.BidRequest) *openrtb.BidRequest {
return &openrtb.BidRequest{
DGarbar marked this conversation as resolved.
Show resolved Hide resolved
ID: internalRequest.ID,
Imp: internalRequest.Imp,
Site: internalRequest.Site,
App: internalRequest.App,
Device: internalRequest.Device,
User: internalRequest.User,
Test: internalRequest.Test,
AT: internalRequest.AT,
TMax: internalRequest.TMax,
WSeat: internalRequest.WSeat,
BSeat: internalRequest.BSeat,
AllImps: internalRequest.AllImps,
Cur: internalRequest.Cur,
WLang: internalRequest.WLang,
BCat: internalRequest.BCat,
BAdv: internalRequest.BAdv,
BApp: internalRequest.BApp,
Source: internalRequest.Source,
Regs: internalRequest.Regs,
Ext: internalRequest.Ext,
}
}

func getTpIdsAndSegments(eids []openrtb_ext.ExtUserEid) ([]rubiconExtUserTpID, []string, []error) {
tpIds := make([]rubiconExtUserTpID, 0)
segments := make([]string, 0)
Expand Down Expand Up @@ -900,9 +938,33 @@ func (a *RubiconAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalR
bidType = openrtb_ext.BidTypeVideo
}

impToCpmOverride, err := mapImpIdToCpmOverride(internalRequest.Imp)
if err != nil {
DGarbar marked this conversation as resolved.
Show resolved Hide resolved
return nil, []error{&errortypes.BadInput{
Message: err.Error(),
}}
}

cmpOverride, err := cmpOverrideFromBidRequest(internalRequest)
if err != nil {
DGarbar marked this conversation as resolved.
Show resolved Hide resolved
return nil, []error{&errortypes.BadInput{
Message: err.Error(),
}}
}

for _, sb := range bidResp.SeatBid {
for i := 0; i < len(sb.Bid); i++ {
bid := sb.Bid[i]

bidCmpOverride, ok := impToCpmOverride[bid.ImpID]
if !ok || bidCmpOverride == 0 {
bidCmpOverride = cmpOverride
}

if bidCmpOverride > 0 {
bid.Price = bidCmpOverride
}

if bid.Price != 0 {
// Since Rubicon XAPI returns only one bid per response
// copy response.bidid to openrtb_response.seatbid.bid.bidid
Expand All @@ -919,3 +981,34 @@ func (a *RubiconAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalR

return bidResponse, nil
}

func cmpOverrideFromBidRequest(bidRequest *openrtb.BidRequest) (float64, error) {
if bidRequest.Ext == nil {
return 0, nil
}

var bidRequestExt bidRequestExt
if err := json.Unmarshal(bidRequest.Ext, &bidRequestExt); err != nil {
return 0, fmt.Errorf("Error decoding Request.ext : %s ", err.Error())
}

return bidRequestExt.Rubicon.Debug.CpmOverride, nil
}

func mapImpIdToCpmOverride(imps []openrtb.Imp) (map[string]float64, error) {
impIdToCmpOverride := make(map[string]float64)
for _, imp := range imps {
var bidderExt adapters.ExtImpBidder
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
continue
}

var rubiconExt openrtb_ext.ExtImpRubicon
if err := json.Unmarshal(bidderExt.Bidder, &rubiconExt); err != nil {
continue
}

impIdToCmpOverride[imp.ID] = rubiconExt.Debug.CpmOverride
}
return impIdToCmpOverride, nil
}
97 changes: 95 additions & 2 deletions adapters/rubicon/rubicon_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -973,9 +973,9 @@ func TestOpenRTBRequest(t *testing.T) {
}

assert.Equal(t, request.ID, rpRequest.ID, "Bad Request ID. Expected %s, Got %s", request.ID, rpRequest.ID)
assert.Equal(t, len(request.Imp), len(rpRequest.Imp), "Wrong len(request.Imp). Expected %d, Got %d", len(request.Imp), len(rpRequest.Imp))
assert.Equal(t, 1, len(rpRequest.Imp), "Wrong len(request.Imp). Expected %d, Got %d", len(request.Imp), len(rpRequest.Imp))
assert.Nil(t, rpRequest.Cur, "Wrong request.Cur. Expected nil, Got %s", rpRequest.Cur)
assert.Nil(t, request.Ext, "Wrong request.ext. Expected nil, Got %v", request.Ext)
assert.Nil(t, rpRequest.Ext, "Wrong request.ext. Expected nil, Got %v", rpRequest.Ext)

if rpRequest.Imp[0].ID == "test-imp-banner-id" {
var rpExt rubiconBannerExt
Expand Down Expand Up @@ -1425,6 +1425,99 @@ func TestOpenRTBStandardResponse(t *testing.T) {
assert.Equal(t, "1234567890", theBid.ID, "Bad bid ID. Expected %s, got %s", "1234567890", theBid.ID)
}

func TestOpenRTBResponseOverridePriceFromBidRequest(t *testing.T) {
request := &openrtb.BidRequest{
ID: "test-request-id",
Imp: []openrtb.Imp{{
ID: "test-imp-id",
Banner: &openrtb.Banner{
Format: []openrtb.Format{{
W: 320,
H: 50,
}},
},
Ext: json.RawMessage(`{"bidder": {
"accountId": 2763,
"siteId": 68780,
"zoneId": 327642
}}`),
}},
Ext: json.RawMessage(`{"rubicon": {
"debug": {
"cpmOverride" : 10
}}}`),
}

requestJson, _ := json.Marshal(request)
reqData := &adapters.RequestData{
Method: "POST",
Uri: "test-uri",
Body: requestJson,
Headers: nil,
}

httpResp := &adapters.ResponseData{
StatusCode: http.StatusOK,
Body: []byte(`{"id":"test-request-id","seatbid":[{"bid":[{"id":"1234567890","impid":"test-imp-id","price": 2,"crid":"4122982","adm":"some ad","h": 50,"w": 320,"ext":{"bidder":{"rp":{"targeting": {"key": "rpfl_2763", "values":["43_tier0100"]},"mime": "text/html","size_id": 43}}}}]}]}`),
}

bidder := new(RubiconAdapter)
bidResponse, errs := bidder.MakeBids(request, reqData, httpResp)

assert.Empty(t, errs, "Expected 0 errors. Got %d", len(errs))

assert.Equal(t, float64(10), bidResponse.Bids[0].Bid.Price,
"Expected Price 10. Got: %s", bidResponse.Bids[0].Bid.Price)
}

func TestOpenRTBResponseOverridePriceFromCorrespondingImp(t *testing.T) {
request := &openrtb.BidRequest{
ID: "test-request-id",
Imp: []openrtb.Imp{{
ID: "test-imp-id",
Banner: &openrtb.Banner{
Format: []openrtb.Format{{
W: 320,
H: 50,
}},
},
Ext: json.RawMessage(`{"bidder": {
"accountId": 2763,
"siteId": 68780,
"zoneId": 327642,
"debug": {
"cpmOverride" : 20
}
}}`),
}},
Ext: json.RawMessage(`{"rubicon": {
"debug": {
"cpmOverride" : 10
}}}`),
}

requestJson, _ := json.Marshal(request)
reqData := &adapters.RequestData{
Method: "POST",
Uri: "test-uri",
Body: requestJson,
Headers: nil,
}

httpResp := &adapters.ResponseData{
StatusCode: http.StatusOK,
Body: []byte(`{"id":"test-request-id","seatbid":[{"bid":[{"id":"1234567890","impid":"test-imp-id","price": 2,"crid":"4122982","adm":"some ad","h": 50,"w": 320,"ext":{"bidder":{"rp":{"targeting": {"key": "rpfl_2763", "values":["43_tier0100"]},"mime": "text/html","size_id": 43}}}}]}]}`),
}

bidder := new(RubiconAdapter)
bidResponse, errs := bidder.MakeBids(request, reqData, httpResp)

assert.Empty(t, errs, "Expected 0 errors. Got %d", len(errs))

assert.Equal(t, float64(20), bidResponse.Bids[0].Bid.Price,
"Expected Price 20. Got: %s", bidResponse.Bids[0].Bid.Price)
}

func TestOpenRTBCopyBidIdFromResponseIfZero(t *testing.T) {
request := &openrtb.BidRequest{
ID: "test-request-id",
Expand Down
6 changes: 6 additions & 0 deletions openrtb_ext/imp_rubicon.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ type ExtImpRubicon struct {
Inventory json.RawMessage `json:"inventory,omitempty"`
Visitor json.RawMessage `json:"visitor,omitempty"`
Video rubiconVideoParams `json:"video"`
Debug impExtRubiconDebug `json:"debug,omitempty"`
}

// rubiconVideoParams defines the contract for bidrequest.imp[i].ext.rubicon.video
Expand All @@ -23,3 +24,8 @@ type rubiconVideoParams struct {
Skip int `json:"skip,omitempty"`
SkipDelay int `json:"skipdelay,omitempty"`
}

// rubiconVideoParams defines the contract for bidrequest.imp[i].ext.rubicon.debug
type impExtRubiconDebug struct {
CpmOverride float64 `json:"cpmOverride,omitempty"`
}