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 Datablocks Adapter #1095

Merged
merged 9 commits into from
Nov 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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
188 changes: 188 additions & 0 deletions adapters/datablocks/datablocks.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package datablocks

import (
"encoding/json"
"fmt"
"github.com/golang/glog"
"github.com/mxmCherry/openrtb"
"github.com/prebid/prebid-server/adapters"
"github.com/prebid/prebid-server/errortypes"
"github.com/prebid/prebid-server/macros"
"github.com/prebid/prebid-server/openrtb_ext"
"net/http"
"strconv"
"text/template"
)

type DatablocksAdapter struct {
EndpointTemplate template.Template
}

func (a *DatablocksAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {

errs := make([]error, 0, len(request.Imp))
headers := http.Header{
"Content-Type": {"application/json"},
"Accept": {"application/json"},
}

// Pull the host and source ID info from the bidder params.
reqImps, err := splitImpressions(request.Imp)

if err != nil {
errs = append(errs, err)
}

requests := []*adapters.RequestData{}

for reqExt, reqImp := range reqImps {
request.Imp = reqImp
reqJson, err := json.Marshal(request)

if err != nil {
errs = append(errs, err)
continue
}

urlParams := macros.EndpointTemplateParams{Host: reqExt.Host, SourceId: strconv.Itoa(reqExt.SourceId)}
url, err := macros.ResolveMacros(a.EndpointTemplate, urlParams)

if err != nil {
errs = append(errs, err)
continue
}

request := adapters.RequestData{
Method: "POST",
Uri: url,
Body: reqJson,
Headers: headers}

requests = append(requests, &request)
}

return requests, errs
}

/*
internal original request in OpenRTB, external = result of us having converted it (what comes out of MakeRequests)
*/
func (a *DatablocksAdapter) MakeBids(
internalRequest *openrtb.BidRequest,
externalRequest *adapters.RequestData,
response *adapters.ResponseData,
) (*adapters.BidderResponse, []error) {

if response.StatusCode == http.StatusNoContent {
return nil, nil
}

if response.StatusCode != http.StatusOK {
return nil, []error{&errortypes.BadServerResponse{
Message: fmt.Sprintf("ERR, response with status %d", response.StatusCode),
}}
}
Copy link
Contributor

@guscarreon guscarreon Nov 7, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When response.StatusCode == http.StatusNoContent other adapters don't return an error, they just return nil, nil. Does it make sense to throw an error in the context of this adapter? Do we risk of flooding the logs with errors everytime the response.StatusCode == http.StatusNoContent condition is met?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our server (right now) always responds with a 200 request with an empty seat bid for no-bid cases. I added the 204 handling and test in my latest commit anyways for future proofing of our server.


var bidResp openrtb.BidResponse

if err := json.Unmarshal(response.Body, &bidResp); err != nil {
return nil, []error{err}
}

bidResponse := adapters.NewBidderResponse()
bidResponse.Currency = bidResp.Cur

for _, seatBid := range bidResp.SeatBid {
for i := 0; i < len(seatBid.Bid); i++ {
bid := seatBid.Bid[i]
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
Bid: &bid,
BidType: getMediaType(bid.ImpID, internalRequest.Imp),
})
}
}

return bidResponse, nil
}

func splitImpressions(imps []openrtb.Imp) (map[openrtb_ext.ExtImpDatablocks][]openrtb.Imp, error) {

var m = make(map[openrtb_ext.ExtImpDatablocks][]openrtb.Imp)

for _, imp := range imps {
bidderParams, err := getBidderParams(&imp)
if err != nil {
return nil, err
}

v, ok := m[*bidderParams]
if ok {
m[*bidderParams] = append(v, imp)
} else {
m[*bidderParams] = []openrtb.Imp{imp}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick:

103   func splitImpressions(imps []openrtb.Imp) (map[openrtb_ext.ExtImpDatablocks][]openrtb.Imp, error) {
104
105       var m = make(map[openrtb_ext.ExtImpDatablocks][]openrtb.Imp)
106
107       for _, imp := range imps {
108           bidderParams, err := getBidderParams(&imp)
109           if err != nil {
110               return nil, err
111           }
112
113 -         _, ok := m[*bidderParams]
    +         v, ok := m[*bidderParams]
114           if ok {
115 -             v, _ := m[*bidderParams]
116               m[*bidderParams] = append(v, imp)
117           } else {
118               m[*bidderParams] = []openrtb.Imp{imp}
119           }
120       }
121
122       return m, nil
123   }
adapters/datablocks/datablocks.go

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thats a good nitpick ;)


return m, nil
}

func getBidderParams(imp *openrtb.Imp) (*openrtb_ext.ExtImpDatablocks, error) {
var bidderExt adapters.ExtImpBidder
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
return nil, &errortypes.BadInput{
Message: fmt.Sprintf("Missing bidder ext: %s", err.Error()),
}
}
var datablocksExt openrtb_ext.ExtImpDatablocks
if err := json.Unmarshal(bidderExt.Bidder, &datablocksExt); err != nil {
return nil, &errortypes.BadInput{
Message: fmt.Sprintf("Cannot Resolve host or sourceId: %s", err.Error()),
}
}

if datablocksExt.SourceId < 1 {
return nil, &errortypes.BadInput{
Message: "Invalid/Missing SourceId",
}
}

if len(datablocksExt.Host) < 1 {
return nil, &errortypes.BadInput{
Message: "Invalid/Missing Host",
}
}

return &datablocksExt, nil
}

func getMediaType(impID string, imps []openrtb.Imp) openrtb_ext.BidType {

bidType := openrtb_ext.BidTypeBanner

for _, imp := range imps {
if imp.ID == impID {
if imp.Video != nil {
bidType = openrtb_ext.BidTypeVideo
break
} else if imp.Native != nil {
bidType = openrtb_ext.BidTypeNative
break
} else {
bidType = openrtb_ext.BidTypeBanner
break
}
}
}

return bidType
}

func NewDatablocksBidder(endpoint string) *DatablocksAdapter {
template, err := template.New("endpointTemplate").Parse(endpoint)
if err != nil {
glog.Fatal("Unable to parse endpoint url template")
return nil
}

return &DatablocksAdapter{EndpointTemplate: *template}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some other adapters log a message when the template couldn't be parsed. I believe it'd make sense to add one in our context.

176   func NewDatablocksBidder(endpoint string) *DatablocksAdapter {
177       template, err := template.New("endpointTemplate").Parse(endpoint)
178       if err != nil {
	+ 	      glog.Fatal("Unable to parse endpoint url template")
179           return nil
180       }
181
182       return &DatablocksAdapter{EndpointTemplate: *template}
183   }
adapters/datablocks/datablocks.go

11 changes: 11 additions & 0 deletions adapters/datablocks/datablocks_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package datablocks

import (
"testing"

"github.com/prebid/prebid-server/adapters/adapterstest"
)

func TestJsonSamples(t *testing.T) {
adapterstest.RunJSONBidderTest(t, "datablockstest", NewDatablocksBidder("http://{{.Host}}/openrtb2?sid={{.SourceId}}"))
}
160 changes: 160 additions & 0 deletions adapters/datablocks/datablockstest/exemplary/multi-request.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
{
"mockBidRequest":
{
"id": "some-request-id",
"imp": [
{
"id": "some-impression-id",
"banner":
{
"format": [
{
"w": 300,
"h": 250
}]
},
"ext":
{
"bidder":
{
"host": "search.nutella.datablocks.net",
"sourceId": 906295
}
}
},{
"id": "some-impression-id2",
"banner":
{
"format": [{
"w": 300,
"h": 600
}]
},
"ext":
{
"bidder":
{
"host": "search.nutella.datablocks.net",
"sourceId": 906295
}
}
}],
"site":
{
"page": "prebid.org"
},
"device":
{
"ip": "8.8.8.10"
},
"at": 1,
"tmax": 500
},
"httpCalls": [
{
"expectedRequest":
{
"uri": "http://search.nutella.datablocks.net/openrtb2?sid=906295",
"body":
{
"id": "some-request-id",
"imp": [
{
"id": "some-impression-id",
"banner":
{
"format": [
{
"w": 300,
"h": 250
}]
},
"ext":
{
"bidder":
{
"host": "search.nutella.datablocks.net",
"sourceId": 906295
}
}
},{
"id": "some-impression-id2",
"banner":
{
"format": [
{
"w": 300,
"h": 600
}]
},
"ext":
{
"bidder":
{
"host": "search.nutella.datablocks.net",
"sourceId": 906295
}
}
}],
"site":
{
"page": "prebid.org"
},
"device":
{
"ip": "8.8.8.10"
},
"at": 1,
"tmax": 500
}
},
"mockResponse":
{
"status": 200,
"body":
{
"id": "some-request-id",
"bidid": "183975330-5-29038-2",
"seatbid": [
{
"seat": "906295",
"bid": [
{
"id": "2181314349",
"impid": "some-impression-id",
"adm": "<style>.text_preview{padding:14px;overflow:hidden;display:flex;flex-direction:column;justify-content:center;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;margin:0;padding:0;color:#333;background:#fff}.text_decription{min-width:150px;padding:5px;flex-grow:0;}.title_preview a{color:#00f;font-weight:700;text-decoration:none}.description_preview{padding:0 0 20px;overflow:hidden;text-overflow:ellipsis}.click_url_preview{color:green}.image_container{position:relative;width:160;height:120}.image_container img{max-height:100%;max-width:100%}.loading{background:url() center no-repeat #eee;height:100%;width:100%;top:0;left:0;position:absolute;border-radius:0}</style><style>.text_boundary {width:300px;height:250px;}</style><div class=\"text_preview text_boundary\"><div class=\"text_decription\"><div class=\"title_preview\"><a href=\"https://track.nutella.datablocks.net/c/267237/?fcid=2181314349\" target=\"_top\">Datablocks Inc.</a></div><div class=\"description_preview\">Datablocks provides world class \"Software as a Service\" (SaaS) solutions to its clients.</div><a class=\"click_url_preview\" target=\"_top\" href=\"https://track.nutella.datablocks.net/c/267237/?fcid=2181314349\">www.datablocks.net</a></div></div><img alt=\"\" src=\"https://impression.nutella.datablocks.net/i/267237/?fcid=2181314349&pixel=1\" width=\"1\" height=\"1\" ></div>",
"price": 13.37,
"cid": "906293",
"adid": "906297",
"crid": "906299",
"w": 300,
"h": 250
}]
}],
"cur": "USD",
"ext":
{}
}
}
}],
"expectedBidResponses": [
{
"currency": "USD",
"bids": [
{
"bid":
{
"id": "2181314349",
"impid": "some-impression-id",
"adm": "<style>.text_preview{padding:14px;overflow:hidden;display:flex;flex-direction:column;justify-content:center;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;font-size:14px;line-height:20px;margin:0;padding:0;color:#333;background:#fff}.text_decription{min-width:150px;padding:5px;flex-grow:0;}.title_preview a{color:#00f;font-weight:700;text-decoration:none}.description_preview{padding:0 0 20px;overflow:hidden;text-overflow:ellipsis}.click_url_preview{color:green}.image_container{position:relative;width:160;height:120}.image_container img{max-height:100%;max-width:100%}.loading{background:url() center no-repeat #eee;height:100%;width:100%;top:0;left:0;position:absolute;border-radius:0}</style><style>.text_boundary {width:300px;height:250px;}</style><div class=\"text_preview text_boundary\"><div class=\"text_decription\"><div class=\"title_preview\"><a href=\"https://track.nutella.datablocks.net/c/267237/?fcid=2181314349\" target=\"_top\">Datablocks Inc.</a></div><div class=\"description_preview\">Datablocks provides world class \"Software as a Service\" (SaaS) solutions to its clients.</div><a class=\"click_url_preview\" target=\"_top\" href=\"https://track.nutella.datablocks.net/c/267237/?fcid=2181314349\">www.datablocks.net</a></div></div><img alt=\"\" src=\"https://impression.nutella.datablocks.net/i/267237/?fcid=2181314349&pixel=1\" width=\"1\" height=\"1\" ></div>",
"price": 13.37,
"cid": "906293",
"adid": "906297",
"crid": "906299",
"w": 300,
"h": 250
},
"type": "banner"
}]
}]
}
Loading