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 admixer adapter #1195

Merged
merged 5 commits into from
Feb 27, 2020
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
184 changes: 184 additions & 0 deletions adapters/admixer/admixer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package admixer

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"
)

type AdmixerAdapter struct {
endpoint string
}

func NewAdmixerBidder(endpoint string) *AdmixerAdapter {
return &AdmixerAdapter{endpoint: endpoint}
}

type admixerImpExt struct {
CustomParams map[string]interface{} `json:"customParams"`
}

func (a *AdmixerAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) (requests []*adapters.RequestData, errors []error) {
rq, errs := a.makeRequest(request)

if len(errs) > 0 {
errors = append(errors, errs...)
return
}

if rq != nil {
requests = append(requests, rq)
}

return
}

func (a *AdmixerAdapter) makeRequest(request *openrtb.BidRequest) (*adapters.RequestData, []error) {
var errs []error
var validImps []openrtb.Imp

if len(request.Imp) == 0 {
return nil, []error{&errortypes.BadInput{
Message: "No impressions in request",
}}
}

for _, imp := range request.Imp {
if err := preprocess(&imp); err != nil {
errs = append(errs, err)
continue
}
validImps = append(validImps, imp)
}

if len(validImps) == 0 {
return nil, errs
}

request.Imp = validImps

reqJSON, err := json.Marshal(request)
if err != nil {
errs = append(errs, err)
return nil, errs
}

headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")
return &adapters.RequestData{
Method: "POST",
Uri: a.endpoint,
Body: reqJSON,
Headers: headers,
}, errs
}

func preprocess(imp *openrtb.Imp) error {
var bidderExt adapters.ExtImpBidder
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
return &errortypes.BadInput{
Message: err.Error(),
}
}

var admixerExt openrtb_ext.ExtImpAdmixer
if err := json.Unmarshal(bidderExt.Bidder, &admixerExt); err != nil {
return &errortypes.BadInput{
Message: "Wrong Admixer bidder ext",
}
}

//don't use regexp due to possible performance reduce
SyntaxNode marked this conversation as resolved.
Show resolved Hide resolved
if len(admixerExt.ZoneId) != 36 {
return &errortypes.BadInput{
Message: "ZoneId must be UUID/GUID",
}
}

imp.TagID = admixerExt.ZoneId
imp.BidFloor = admixerExt.CustomBidFloor

imp.Ext = nil

if admixerExt.CustomParams != nil {
impExt := admixerImpExt{
CustomParams: admixerExt.CustomParams,
}
var err error
if imp.Ext, err = json.Marshal(impExt); err != nil {
return &errortypes.BadInput{
Message: err.Error(),
}
}
}

return nil
}

func (a *AdmixerAdapter) 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.StatusInternalServerError {
return nil, []error{&errortypes.BadServerResponse{
Message: fmt.Sprintf("Unexpected status code: %d. Dsp server internal error", response.StatusCode),
}}
}

if response.StatusCode >= http.StatusBadRequest {
return nil, []error{&errortypes.BadInput{
Message: fmt.Sprintf("Unexpected status code: %d. Bad request to dsp", response.StatusCode),
}}
}

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

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

//additional no content check
if len(bidResp.SeatBid) == 0 || len(bidResp.SeatBid[0].Bid) == 0 {
return nil, nil
}

bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid))

for _, sb := range bidResp.SeatBid {
for i := range sb.Bid {
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
Bid: &sb.Bid[i],
BidType: getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp),
})
}
}
return bidResponse, nil
}

func getMediaTypeForImp(impID string, imps []openrtb.Imp) openrtb_ext.BidType {
for _, imp := range imps {
if imp.ID == impID {
if imp.Banner != nil {
return openrtb_ext.BidTypeBanner
} else if imp.Video != nil {
return openrtb_ext.BidTypeVideo
} else if imp.Native != nil {
return openrtb_ext.BidTypeNative
} else if imp.Audio != nil {
return openrtb_ext.BidTypeAudio
}
}
}
return openrtb_ext.BidTypeBanner
}
10 changes: 10 additions & 0 deletions adapters/admixer/admixer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package admixer

import (
"github.com/prebid/prebid-server/adapters/adapterstest"
"testing"
)

func TestJsonSamples(t *testing.T) {
adapterstest.RunJSONBidderTest(t, "admixertest", NewAdmixerBidder("http://inv-nets.admixer.net/pbs.aspx"))
Copy link
Contributor

Choose a reason for hiding this comment

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

The url does not need to be real here. It just needs to match the json examples. Consider using an obviously fake stub so you don't have to worry about this address becoming outdated.

}
104 changes: 104 additions & 0 deletions adapters/admixer/admixertest/exemplary/optional-params.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
{
"mockBidRequest": {
"id": "test-request-id",
"imp": [
{
"id": "test-imp-id",
"banner": {
"format": [
{
"w": 728,
"h": 90
}
]
},
"ext": {
"bidder": {
"zone": "2eb6bd58-865c-47ce-af7f-a918108c3fd2",
"customFloor": 0.1,
"customParams": {
"foo": "bar"
}
}
}
},
{
"id": "test-imp-id",
"banner": {
"format": [
{
"w": 728,
"h": 90
}
]
},
"ext": {
"bidder": {
"zone": "2eb6bd58-865c-47ce-af7f-a918108c3fd2",
"customFloor": 0.1,
"customParams": {
"foo": [
"bar",
"baz"
]
}
}
}
}
]
},
"httpCalls": [
{
"expectedRequest": {
"uri": "http://inv-nets.admixer.net/pbs.aspx",
"body": {
"id": "test-request-id",
"imp": [
{
"id": "test-imp-id",
"banner": {
"format": [
{
"w": 728,
"h": 90
}
]
},
"tagid": "2eb6bd58-865c-47ce-af7f-a918108c3fd2",
"bidfloor": 0.1,
"ext": {
"customParams": {
"foo": "bar"
}
}
},
{
"id": "test-imp-id",
"banner": {
"format": [
{
"w": 728,
"h": 90
}
]
},
"tagid": "2eb6bd58-865c-47ce-af7f-a918108c3fd2",
"bidfloor": 0.1,
"ext": {
"customParams": {
"foo": [
"bar",
"baz"
]
}
}
}
]
}
},
"mockResponse": {
"status": 204
}
}
]
}
89 changes: 89 additions & 0 deletions adapters/admixer/admixertest/exemplary/simple-app-audio.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{
"mockBidRequest": {
"id": "test-request-id",
"app": {
"bundle": "com.prebid"
},
"device": {
"ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3"
},
"imp": [
{
"id": "test-imp-id",
"audio": {
"mimes": ["audio/mp4"],
"protocols": [9,10]
},
"ext": {
"bidder": {
"zone": "473e443c-43d0-423d-a8d7-a302637a01d8"
}
}
}
]
},

"httpCalls": [
{
"expectedRequest": {
"uri": "http://inv-nets.admixer.net/pbs.aspx",
"body": {
"id": "test-request-id",
"app": {
"bundle": "com.prebid"
},
"device": {
"ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3"
},
"imp": [
{
"id": "test-imp-id",
"audio": {
"mimes": ["audio/mp4"],
"protocols": [9,10]
},
"tagid": "473e443c-43d0-423d-a8d7-a302637a01d8"
}
]
}
},
"mockResponse": {
"status": 200,
"body": {
"id": "test-request-id",
"seatbid": [
{
"seat": "admixer",
"bid": [{
"id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
"impid": "test-imp-id",
"price": 0.500000,
"adm": "some-test-ad",
"crid": "test-crid"
}]
}
],
"cur": "USD"
}
}
}
],

"expectedBidResponses": [
{
"currency": "USD",
"bids": [
{
"bid": {
"id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
"impid": "test-imp-id",
"price": 0.5,
"adm": "some-test-ad",
"crid": "test-crid"
},
"type": "audio"
}
]
}
]
}
Loading