Skip to content

Commit

Permalink
add ucfunnel adapter (#1192)
Browse files Browse the repository at this point in the history
  • Loading branch information
guscarreon authored Mar 5, 2020
1 parent f03dfa5 commit 8686f03
Show file tree
Hide file tree
Showing 16 changed files with 559 additions and 0 deletions.
47 changes: 47 additions & 0 deletions adapters/ucfunnel/params_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package ucfunnel

import (
"encoding/json"
"github.com/prebid/prebid-server/openrtb_ext"
"testing"
)

// This file actually intends to test static/bidder-params/ucfunnel.json
//
// These also validate the format of the external API: request.imp[i].ext.ucfunnel

// TestValidParams makes sure that the ucfunnel schema accepts all imp.ext fields which we intend to support.
func TestValidParams(t *testing.T) {
validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
if err != nil {
t.Fatalf("Failed to fetch the json-schemas. %v", err)
}

for _, validParam := range validParams {
if err := validator.Validate(openrtb_ext.BidderUcfunnel, json.RawMessage(validParam)); err != nil {
t.Errorf("Schema rejected ucfunnel params: %s", validParam)
}
}
}

// TestInvalidParams makes sure that the ucfunnel schema rejects all the imp.ext fields we don't support.
func TestInvalidParams(t *testing.T) {
validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
if err != nil {
t.Fatalf("Failed to fetch the json-schemas. %v", err)
}

for _, invalidParam := range invalidParams {
if err := validator.Validate(openrtb_ext.BidderUcfunnel, json.RawMessage(invalidParam)); err != nil {
t.Errorf("Schema allowed unexpected params: %s", invalidParam)
}
}
}

var validParams = []string{
`{"adunitid": "ad-83444226E44368D1E32E49EEBE6D29","partnerid": "par-2EDDB423AA24474188B843EE4842932"}`,
}

var invalidParams = []string{
`{"adunitid": "","partnerid": ""}`,
}
150 changes: 150 additions & 0 deletions adapters/ucfunnel/ucfunnel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package ucfunnel

import (
"encoding/json"
"fmt"
"github.com/mxmCherry/openrtb"
"github.com/prebid/prebid-server/adapters"
"github.com/prebid/prebid-server/errortypes"
"github.com/prebid/prebid-server/openrtb_ext"
"net/http"
"net/url"
)

type UcfunnelAdapter struct {
URI string
}

func NewUcfunnelBidder(endpoint string) *UcfunnelAdapter {
return &UcfunnelAdapter{
URI: endpoint}
}

func (a *UcfunnelAdapter) Name() string {
return "ucfunnel"
}

func (a *UcfunnelAdapter) SkipNoCookies() bool {
return false
}

func (a *UcfunnelAdapter) 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.StatusBadRequest {
return nil, []error{&errortypes.BadInput{
Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
}}
}

if response.StatusCode != http.StatusOK {
return nil, []error{&errortypes.BadServerResponse{
Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
}}
}

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

var bidReq openrtb.BidRequest
if err := json.Unmarshal(externalRequest.Body, &bidReq); err != nil {
return nil, []error{err}
}

bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid))
for _, sb := range bidResp.SeatBid {
for i := range sb.Bid {
bidType := getBidType(bidReq, sb.Bid[i].ImpID)
if (bidType == openrtb_ext.BidTypeBanner) || (bidType == openrtb_ext.BidTypeVideo) {
b := &adapters.TypedBid{
Bid: &sb.Bid[i],
BidType: bidType,
}
bidResponse.Bids = append(bidResponse.Bids, b)
}
}
}
return bidResponse, errs
}

func (a *UcfunnelAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
errs := make([]error, 0, len(request.Imp))

// If all the requests were malformed, don't bother making a server call with no impressions.
if len(request.Imp) == 0 {
return nil, []error{&errortypes.BadInput{
Message: fmt.Sprintf("No impression in the bid request\n"),
}}
}

partnerId, partnerErr := getPartnerId(request)
if partnerErr != nil {
return nil, partnerErr
}

reqJSON, err := json.Marshal(request)
if err != nil {
return nil, []error{err}
}

headers := http.Header{}
headers.Add("Content-Type", "application/json")

uri := a.URI + "/" + url.PathEscape(partnerId) + "/request"
return []*adapters.RequestData{{
Method: "POST",
Uri: uri,
Body: reqJSON,
Headers: headers,
}}, errs
}

func getPartnerId(request *openrtb.BidRequest) (string, []error) {
var ext ExtBidderUcfunnel
var errs = []error{}
err := json.Unmarshal(request.Imp[0].Ext, &ext)
if err != nil {
errs = append(errs, err)
return "", errs
}
errs = checkBidderParameter(ext)
if errs != nil {
return "", errs
}
return ext.Bidder.PartnerId, nil
}

func checkBidderParameter(ext ExtBidderUcfunnel) []error {
var errs = []error{}
if len(ext.Bidder.PartnerId) == 0 || len(ext.Bidder.AdUnitId) == 0 {
errs = append(errs, fmt.Errorf("No PartnerId or AdUnitId in the bid request\n"))
return errs
}
return nil
}

func getBidType(bidReq openrtb.BidRequest, impid string) openrtb_ext.BidType {
for i := range bidReq.Imp {
if bidReq.Imp[i].ID == impid {
if bidReq.Imp[i].Banner != nil {
return openrtb_ext.BidTypeBanner
} else if bidReq.Imp[i].Video != nil {
return openrtb_ext.BidTypeVideo
} else if bidReq.Imp[i].Audio != nil {
return openrtb_ext.BidTypeAudio
} else if bidReq.Imp[i].Native != nil {
return openrtb_ext.BidTypeNative
}
}
}
return openrtb_ext.BidTypeNative
}

type ExtBidderUcfunnel struct {
Bidder openrtb_ext.ExtImpUcfunnel `json:"bidder"`
}
163 changes: 163 additions & 0 deletions adapters/ucfunnel/ucfunnel_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
package ucfunnel

import (
"encoding/json"
"fmt"
"testing"

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

func TestUcfunnelAdapterNames(t *testing.T) {
adapter := NewUcfunnelBidder("http://localhost/bid")
adapterstest.VerifyStringValue(adapter.Name(), "ucfunnel", t)
}

func TestSkipNoCookies(t *testing.T) {
adapter := NewUcfunnelBidder("http://localhost/bid")
status := adapter.SkipNoCookies()
if status != false {
t.Errorf("actual = %t expected != %t", status, false)
}
}

func TestMakeRequests(t *testing.T) {

imp := openrtb.Imp{
ID: "1234",
Banner: &openrtb.Banner{},
}
imp2 := openrtb.Imp{
ID: "1235",
Video: &openrtb.Video{},
}

imp3 := openrtb.Imp{
ID: "1236",
Audio: &openrtb.Audio{},
}

imp4 := openrtb.Imp{
ID: "1237",
Native: &openrtb.Native{},
}
imp5 := openrtb.Imp{
ID: "1237",
Native: &openrtb.Native{},
}

internalRequest01 := openrtb.BidRequest{Imp: []openrtb.Imp{}}
internalRequest02 := openrtb.BidRequest{Imp: []openrtb.Imp{imp, imp2, imp3, imp4, imp5}}
internalRequest03 := openrtb.BidRequest{Imp: []openrtb.Imp{imp, imp2, imp3, imp4, imp5}}

internalRequest03.Imp[0].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`)
internalRequest03.Imp[1].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`)
internalRequest03.Imp[2].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`)
internalRequest03.Imp[3].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`)
internalRequest03.Imp[4].Ext = []byte(`{"bidder": {"adunitid": "aa","partnerid": ""}}`)

adapter := NewUcfunnelBidder("http://localhost/bid")

var testCases = []struct {
in []openrtb.BidRequest
out1 [](int)
out2 [](bool)
}{
{
in: []openrtb.BidRequest{internalRequest01, internalRequest02, internalRequest03},
out1: [](int){1, 1, 0},
out2: [](bool){false, false, true},
},
}

for idx := range testCases {
for i := range testCases[idx].in {
RequestData, err := adapter.MakeRequests(&testCases[idx].in[i], nil)
if ((RequestData == nil) == testCases[idx].out2[i]) && (len(err) == testCases[idx].out1[i]) {
t.Errorf("actual = %v expected = %v", len(err), testCases[idx].out1[i])
}
}
}
}

func TestMakeBids(t *testing.T) {
imp := openrtb.Imp{
ID: "1234",
Banner: &openrtb.Banner{},
}
imp2 := openrtb.Imp{
ID: "1235",
Video: &openrtb.Video{},
}

imp3 := openrtb.Imp{
ID: "1236",
Audio: &openrtb.Audio{},
}

imp4 := openrtb.Imp{
ID: "1237",
Native: &openrtb.Native{},
}
imp5 := openrtb.Imp{
ID: "1237",
Native: &openrtb.Native{},
}

internalRequest03 := openrtb.BidRequest{Imp: []openrtb.Imp{imp, imp2, imp3, imp4, imp5}}
internalRequest04 := openrtb.BidRequest{Imp: []openrtb.Imp{imp}}

internalRequest03.Imp[0].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`)
internalRequest03.Imp[1].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`)
internalRequest03.Imp[2].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`)
internalRequest03.Imp[3].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`)
internalRequest03.Imp[4].Ext = []byte(`{"bidder": {"adunitid": "aa","partnerid": ""}}`)
internalRequest04.Imp[0].Ext = []byte(`{"bidder": {"adunitid": "0"}}`)

mockResponse200 := adapters.ResponseData{StatusCode: 200, Body: json.RawMessage(`{"seatbid": [{"bid": [{"impid": "1234"}]},{"bid": [{"impid": "1235"}]},{"bid": [{"impid": "1236"}]},{"bid": [{"impid": "1237"}]}]}`)}
mockResponse203 := adapters.ResponseData{StatusCode: 203, Body: json.RawMessage(`{"seatbid":[{"bid":[{"impid":"1234"}]},{"bid":[{"impid":"1235"}]}]}`)}
mockResponse204 := adapters.ResponseData{StatusCode: 204, Body: json.RawMessage(`{"seatbid":[{"bid":[{"impid":"1234"}]},{"bid":[{"impid":"1235"}]}]}`)}
mockResponse400 := adapters.ResponseData{StatusCode: 400, Body: json.RawMessage(`{"seatbid":[{"bid":[{"impid":"1234"}]},{"bid":[{"impid":"1235"}]}]}`)}
mockResponseError := adapters.ResponseData{StatusCode: 200, Body: json.RawMessage(`{"seatbid":[{"bid":[{"im236"}],{"bid":[{"impid":"1237}]}`)}

RequestData01 := adapters.RequestData{Method: "POST", Body: []byte(`{"imp":[{"id":"1234","banner":{}},{"id":"1235","video":{}},{"id":"1236","audio":{}},{"id":"1237","native":{}}]}`)}
RequestData02 := adapters.RequestData{Method: "POST", Body: []byte(`{"imp":[{"id":"1234","banne"1235","video":{}},{"id":"1236","audio":{}},{"id":"1237","native":{}}]}`)}

adapter := NewUcfunnelBidder("http://localhost/bid")

var testCases = []struct {
in1 []openrtb.BidRequest
in2 []adapters.RequestData
in3 []adapters.ResponseData
out1 [](bool)
out2 [](bool)
}{
{
in1: []openrtb.BidRequest{internalRequest03, internalRequest03, internalRequest03, internalRequest03, internalRequest03, internalRequest04},
in2: []adapters.RequestData{RequestData01, RequestData01, RequestData01, RequestData01, RequestData01, RequestData02},
in3: []adapters.ResponseData{mockResponse200, mockResponse203, mockResponse204, mockResponse400, mockResponseError, mockResponse200},
out1: [](bool){true, false, false, false, false, false},
out2: [](bool){false, true, false, true, true, true},
},
}

for idx := range testCases {
for i := range testCases[idx].in1 {
BidderResponse, err := adapter.MakeBids(&testCases[idx].in1[i], &testCases[idx].in2[i], &testCases[idx].in3[i])

if (BidderResponse == nil) == testCases[idx].out1[i] {
fmt.Println(i)
fmt.Println("BidderResponse")
t.Errorf("actual = %t expected == %v", (BidderResponse == nil), testCases[idx].out1[i])
}

if (err == nil) == testCases[idx].out2[i] {
fmt.Println(i)
fmt.Println("error")
t.Errorf("actual = %t expected == %v", err, testCases[idx].out2[i])
}
}
}
}
Loading

0 comments on commit 8686f03

Please sign in to comment.