-
Notifications
You must be signed in to change notification settings - Fork 752
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
New Adapter: ResetDigital #3766
base: master
Are you sure you want to change the base?
Changes from 18 commits
70de190
dd48470
32551fc
125124b
5c56e12
fe4ff39
664614f
0b44384
0775c72
20fa856
33f4603
e468217
b8ca566
2de7781
eb067f7
86206d7
3fc5455
3762676
989c565
39905ed
4482bc9
d426e8a
ac2fc01
52a6794
9626103
dea6cf7
5cfe0ae
ece8152
baa553c
ecc90bb
0d54a8d
660dba7
5f70f11
613317a
a083c03
bc7caaf
4569e97
0bc2aeb
d212d91
cff2442
44bee69
96fde76
6e08b5d
ec4005d
fd3ec0d
8bca6ad
278be3f
56a77a0
f0e2574
0e365ce
f2d3afc
d6e24d0
eceef87
e86d017
baa974f
73c3fe0
246010f
cff5817
5ab5517
d3df8f2
c1795b8
1665bc9
0f6f5b1
27863db
f0da5d7
6c745fb
56b03f1
21eecdd
dcd9dad
2584c6b
123cb13
4950da1
cc06d05
330d84f
17cf6c8
5b44280
ee3546c
2ecc9a8
0a4d9bc
b93dca3
ef65f7b
6bac6cd
62f74c1
4a0ec3f
11c824f
905485d
0623316
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,274 @@ | ||
package resetdigital | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"strconv" | ||
"text/template" | ||
|
||
"github.com/prebid/openrtb/v20/openrtb2" | ||
"github.com/prebid/prebid-server/v2/adapters" | ||
"github.com/prebid/prebid-server/v2/config" | ||
"github.com/prebid/prebid-server/v2/openrtb_ext" | ||
) | ||
|
||
// MaximumBids is the maximum number of bids that can be returned by this adapter. | ||
const ( | ||
MaxBids = 1 | ||
) | ||
|
||
type adapter struct { | ||
endpoint *template.Template | ||
endpointUri string | ||
} | ||
|
||
type resetDigitalRequest struct { | ||
Site resetDigitalRequestSite `json:"site"` | ||
Imps []resetDigitalRequesImps `json:"imps"` | ||
} | ||
|
||
type resetDigitalRequestSite struct { | ||
Domain string `json:"domain"` | ||
Referrer string `json:"referrer"` | ||
} | ||
|
||
type resetDigitalRequesImps struct { | ||
ForceBid bool `json:"force_bid"` | ||
ZoneID struct { | ||
PlacementID string `json:"placementId"` | ||
} `json:"zone_id"` | ||
BidID string `json:"bid_id"` | ||
ImpID string `json:"imp_id"` | ||
Ext struct { | ||
Gpid string `json:"gpid"` | ||
} `json:"ext"` | ||
Sizes [][]int64 `json:"sizes"` | ||
MediaTypes struct { | ||
Banner struct { | ||
Sizes [][]int64 `json:"sizes"` | ||
} `json:"banner"` | ||
Video struct { | ||
Sizes [][]int64 `json:"sizes"` | ||
} `json:"video"` | ||
} `json:"media_types"` | ||
} | ||
|
||
func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { | ||
template, err := template.New("endpointTemplate").Parse(config.Endpoint) | ||
if err != nil { | ||
return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) | ||
} | ||
|
||
bidder := &adapter{ | ||
endpoint: template, | ||
} | ||
|
||
return bidder, nil | ||
} | ||
func getHeaders(request *openrtb2.BidRequest) http.Header { | ||
headers := http.Header{} | ||
|
||
if request.Device != nil && request.Site != nil { | ||
addNonEmptyHeaders(&headers, map[string]string{ | ||
"Referer": request.Site.Page, | ||
"Accept-Language": request.Device.Language, | ||
"User-Agent": request.Device.UA, | ||
"X-Forwarded-For": request.Device.IP, | ||
"X-Real-Ip": request.Device.IP, | ||
"Content-Type": "application/json;charset=utf-8", | ||
"Accept": "application/json", | ||
}) | ||
} | ||
|
||
return headers | ||
} | ||
func addNonEmptyHeaders(headers *http.Header, headerValues map[string]string) { | ||
for key, value := range headerValues { | ||
if len(value) > 0 { | ||
headers.Add(key, value) | ||
} | ||
} | ||
} | ||
|
||
func getReferer(request *openrtb2.BidRequest) string { | ||
if request.Site == nil { | ||
return "" | ||
} | ||
|
||
return request.Site.Domain | ||
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. code path is not covered. should add json tests to improve coverage |
||
} | ||
|
||
func getCurrency(request *openrtb2.BidRequest) string { | ||
if len(request.Cur) == 0 { | ||
return "USD" | ||
} | ||
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. code path is not covered. should add json tests to improve coverage |
||
|
||
return request.Cur[0] | ||
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. @bruno-siira should update in bidder docs that if multiple currencies are present in request then only first is considered 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. @onkarvhanumante where should this bidder docs go? Do you have some example? 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. @bruno-siira refer this prebid/prebid.github.io#5486 PR as example |
||
} | ||
|
||
func (a *adapter) MakeRequests(requestData *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { | ||
var ( | ||
requests []*adapters.RequestData | ||
errors []error | ||
) | ||
|
||
referer := getReferer(requestData) | ||
currency := getCurrency(requestData) | ||
|
||
if referer == currency { | ||
return nil, nil | ||
} | ||
bruno-siira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
for i := range requestData.Imp { | ||
imp := requestData.Imp[i] | ||
bidType, err := getBidType(imp) | ||
|
||
if err != nil { | ||
bruno-siira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
errors = append(errors, err) | ||
continue | ||
Comment on lines
+126
to
+127
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. code path is not covered. should add json tests to improve coverage |
||
} | ||
|
||
splittedRequestData := processDataFromRequest(requestData, imp, bidType) | ||
|
||
requestBody, err := json.Marshal(splittedRequestData) | ||
if err != nil { | ||
errors = append(errors, err) | ||
continue | ||
} | ||
|
||
requests = append(requests, &adapters.RequestData{ | ||
Method: "POST", | ||
Uri: a.endpointUri, | ||
Body: requestBody, | ||
Headers: getHeaders(requestData), | ||
}) | ||
} | ||
|
||
return requests, errors | ||
} | ||
|
||
func processDataFromRequest(requestData *openrtb2.BidRequest, imp openrtb2.Imp, bidType openrtb_ext.BidType) resetDigitalRequest { | ||
|
||
var resetDigitalRequestData resetDigitalRequest | ||
resetDigitalRequestData.Site.Domain = requestData.Site.Domain | ||
resetDigitalRequestData.Site.Referrer = requestData.Site.Page | ||
bruno-siira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
resetDigitalRequestData.Imps = append(resetDigitalRequestData.Imps, resetDigitalRequesImps{}) | ||
resetDigitalRequestData.Imps[0].BidID = requestData.ID | ||
resetDigitalRequestData.Imps[0].ImpID = imp.ID | ||
|
||
var err error | ||
|
||
if bidType == openrtb_ext.BidTypeBanner { | ||
resetDigitalRequestData.Imps[0].MediaTypes.Banner.Sizes = append(resetDigitalRequestData.Imps[0].MediaTypes.Banner.Sizes, []int64{imp.Banner.Format[0].W, imp.Banner.Format[0].H}) | ||
} | ||
if bidType == openrtb_ext.BidTypeVideo { | ||
resetDigitalRequestData.Imps[0].MediaTypes.Video.Sizes = append(resetDigitalRequestData.Imps[0].MediaTypes.Banner.Sizes, []int64{*imp.Video.W, *imp.Video.H}) | ||
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. code path is not covered. should add json tests to improve coverage |
||
} | ||
|
||
var extData = make(map[string]interface{}) | ||
err = json.Unmarshal(imp.Ext, &extData) | ||
if err != nil { | ||
|
||
} else { | ||
bruno-siira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
resetDigitalRequestData.Imps[0].ZoneID.PlacementID = extData["bidder"].(map[string]interface{})["placement_id"].(string) | ||
if resetDigitalRequestData.Imps[0].ZoneID.PlacementID == "test" { | ||
resetDigitalRequestData.Imps[0].ForceBid = true | ||
} | ||
|
||
} | ||
return resetDigitalRequestData | ||
|
||
} | ||
|
||
func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { | ||
if response.StatusCode == http.StatusNoContent { | ||
return nil, nil | ||
} | ||
if response.StatusCode == http.StatusBadRequest { | ||
return nil, nil | ||
} | ||
if response.StatusCode != http.StatusOK { | ||
return nil, nil | ||
} | ||
|
||
if err := json.Unmarshal(response.Body, &response); err != nil { | ||
return nil, []error{err} | ||
} | ||
bidResponse := adapters.NewBidderResponseWithBidsCapacity(MaxBids) | ||
//check no bids | ||
jsonData := make(map[string]interface{}) | ||
|
||
json.Unmarshal([]byte(response.Body), &jsonData) | ||
//Always one bid | ||
bid := getBidFromResponse(jsonData) | ||
|
||
bidType, err := getBidType(internalRequest.Imp[0]) | ||
if err != nil { | ||
// handle error | ||
return nil, []error{err} | ||
} | ||
bidResponse.Currency = getCurrency(internalRequest) | ||
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ | ||
Bid: bid, | ||
BidType: bidType, | ||
}) | ||
|
||
return bidResponse, nil | ||
} | ||
|
||
func getBidFromResponse(requestData map[string]interface{}) *openrtb2.Bid { | ||
processData := requestData["bids"].([]interface{})[0].(map[string]interface{}) | ||
|
||
bid := &openrtb2.Bid{ | ||
ID: processData["bid_id"].(string), | ||
Price: getBidPrice(processData), | ||
ImpID: processData["imp_id"].(string), | ||
CrID: processData["crid"].(string), | ||
} | ||
//if HTML is filled on jsonData then fill ADM with it | ||
if value, ok := processData["html"].(string); ok { | ||
bid.AdM = value | ||
} | ||
//if Width and Height are filled on jsonData then fill W and H with it | ||
if value, ok := processData["w"].(string); ok { | ||
|
||
i, _ := strconv.ParseInt(value, 10, 64) | ||
if i > 0 { | ||
bid.W = i | ||
} | ||
} | ||
if value, ok := processData["h"].(string); ok { | ||
i, _ := strconv.ParseInt(value, 10, 64) | ||
if i > 0 { | ||
bid.H = i | ||
} | ||
} | ||
//if Bid Price is 0 then return nil | ||
if bid.Price == 0 { | ||
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. code path is not covered. should add json tests to improve coverage |
||
return nil | ||
} | ||
|
||
return bid | ||
} | ||
|
||
func getBidPrice(requestData map[string]interface{}) float64 { | ||
if value, ok := requestData["cpm"].(float64); ok { | ||
return value | ||
} | ||
return 0.0 // Default value if "cpm" doesn't exist or is not a float64 | ||
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. code path is not covered. should add json tests to improve coverage |
||
} | ||
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. Refer to https://github.com/prebid/prebid-server/pull/3766/files#r1672009993 comment |
||
|
||
func getBidType(imp openrtb2.Imp) (openrtb_ext.BidType, error) { | ||
if imp.Banner != nil { | ||
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. Consider this as a suggestion. The current implementation follows an anti-pattern, assumes that if there is a multi-format request, the media type defaults to openrtb_ext.BidTypeBanner, nil. Prebid server expects the media type to be explicitly set in the adapter response. Therefore, we strongly recommend implementing a pattern where the adapter server sets the MType field in the response to accurately determine the media type for the impression. 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. Implemented as suggested 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.
could you point out or link where 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. Sorry, it was addressed on the point that we support only single format bids, so we could assume the anti pattern. Anyway, it would be more advisable to change to the normal pattern? 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.
Prebid team recommends using MType field. But if it's not doable then current change suffices single format bid. @bruno-siira should mention in Bidder docs that adapter expects only single format bids in the incoming request 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. When we're talking about the Bidder Docs what is this file exacly @onkarvhanumante 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. |
||
return openrtb_ext.BidTypeBanner, nil | ||
} else if imp.Video != nil { | ||
bruno-siira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return openrtb_ext.BidTypeVideo, nil | ||
} else if imp.Audio != nil { | ||
bruno-siira marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return openrtb_ext.BidTypeAudio, nil | ||
} | ||
|
||
return "", fmt.Errorf("failed to find matching imp for bid %s", imp.ID) | ||
Comment on lines
+286
to
+291
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. code path is not covered. should add json tests to improve coverage |
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package resetdigital | ||
|
||
import ( | ||
"encoding/json" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/prebid/openrtb/v20/openrtb2" | ||
"github.com/prebid/prebid-server/v2/adapters" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestMakeRequests(t *testing.T) { | ||
bidder := new(adapter) | ||
|
||
request := &openrtb2.BidRequest{ | ||
ID: "12345", | ||
|
||
Imp: []openrtb2.Imp{{ | ||
ID: "001", | ||
Banner: &openrtb2.Banner{ | ||
Format: []openrtb2.Format{ | ||
{W: 300, H: 250}, | ||
}, | ||
}, | ||
|
||
Ext: json.RawMessage(`{"bidder": {"placement_id": "test"}}`), //Placement_id test is used to get the force bid true | ||
}}, | ||
Site: &openrtb2.Site{ | ||
Domain: "https://test.com", | ||
Page: "https://test.com/2016/06/12", | ||
}, | ||
Cur: []string{"USD"}, | ||
Device: &openrtb2.Device{ | ||
UA: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", | ||
IP: "127.0.0.1", | ||
Language: "EN", | ||
}, | ||
} | ||
|
||
reqs, errs := bidder.MakeRequests(request, &adapters.ExtraRequestInfo{}) | ||
|
||
assert.Empty(t, errs, "Got unexpected errors while building HTTP requests: %v", errs) | ||
assert.Equal(t, 1, len(reqs), "Unexpected number of HTTP requests. Got %d. Expected %d", len(reqs), 1) | ||
} | ||
|
||
func TestMakeBids(t *testing.T) { | ||
request := &openrtb2.BidRequest{ | ||
ID: "12345", | ||
|
||
Imp: []openrtb2.Imp{{ | ||
ID: "001", | ||
Banner: &openrtb2.Banner{ | ||
Format: []openrtb2.Format{ | ||
{W: 300, H: 250}, | ||
}, | ||
}, | ||
|
||
Ext: json.RawMessage(`{"bidder": {"placement_id": "test"}}`), //Placement_id test is used to get the force bid true | ||
}}, | ||
Site: &openrtb2.Site{ | ||
Domain: "https://test.com", | ||
Page: "https://test.com/2016/06/12", | ||
}, | ||
Cur: []string{"USD"}, | ||
Device: &openrtb2.Device{ | ||
UA: "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/72.0.3626.121 Safari/537.36", | ||
IP: "127.0.0.1", | ||
Language: "EN", | ||
}, | ||
} | ||
|
||
requestJson, _ := json.Marshal(request) | ||
reqData := &adapters.RequestData{ | ||
Method: "POST", | ||
Uri: "test-uri", | ||
Body: requestJson, | ||
} | ||
|
||
httpResp := &adapters.ResponseData{ | ||
StatusCode: http.StatusOK, | ||
Body: []byte(`{"bids":[{"bid_id":"01","imp_id":"001","cpm":10.00,"cid":"1002088","crid":"1000763-1002088","adid":"1002088","w":"300","h":"250","seat":"resetdigital","html":"<scriptsrc=\"https://data.resetdigital.co/evts?S0B=1&R0E=1&R0M=3_3&testad=US-HEADER-15&R0A=1000048_1001096_1001117_1627360746&R0P=resetio_1234_muscleandfitness.com_Site_1_Banner&R0L=*_*_*_*_*&R0D=*_*_*_*_*_*&R0B=*_*_*\"type=\"text/javascript\"></script><imagesrc='https://adsreq.resetdigital.co?brid=0000000000000001'/><imagesrc='https://sync2.resetdigital.co/hbsync?ck=0000000000000001'/>"}]}`), | ||
} | ||
|
||
bidder := new(adapter) | ||
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) | ||
} |
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.
code path is not covered. should add json tests to improve coverage