Skip to content

Commit

Permalink
Add Datablocks Adapter (prebid#1095)
Browse files Browse the repository at this point in the history
* datablocks bid adapter

* ttx

* add test json

* add coverage

* redo ttx

* formatted

* better error handling

* additional tests and recomended fixes
  • Loading branch information
htang555 authored and mansinahar committed Apr 15, 2020
1 parent 6a7d562 commit 07ae3c4
Show file tree
Hide file tree
Showing 27 changed files with 1,229 additions and 0 deletions.
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),
}}
}

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}
}
}

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}
}
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

0 comments on commit 07ae3c4

Please sign in to comment.