Skip to content

Commit 6a26430

Browse files
add admixer adapter (#1195)
1 parent 7be0a4d commit 6a26430

34 files changed

+1623
-6
lines changed

adapters/admixer/admixer.go

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
package admixer
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"github.com/mxmCherry/openrtb"
7+
"github.com/prebid/prebid-server/adapters"
8+
"github.com/prebid/prebid-server/errortypes"
9+
"github.com/prebid/prebid-server/openrtb_ext"
10+
"net/http"
11+
)
12+
13+
type AdmixerAdapter struct {
14+
endpoint string
15+
}
16+
17+
func NewAdmixerBidder(endpoint string) *AdmixerAdapter {
18+
return &AdmixerAdapter{endpoint: endpoint}
19+
}
20+
21+
type admixerImpExt struct {
22+
CustomParams map[string]interface{} `json:"customParams"`
23+
}
24+
25+
func (a *AdmixerAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) (requests []*adapters.RequestData, errors []error) {
26+
rq, errs := a.makeRequest(request)
27+
28+
if len(errs) > 0 {
29+
errors = append(errors, errs...)
30+
return
31+
}
32+
33+
if rq != nil {
34+
requests = append(requests, rq)
35+
}
36+
37+
return
38+
}
39+
40+
func (a *AdmixerAdapter) makeRequest(request *openrtb.BidRequest) (*adapters.RequestData, []error) {
41+
var errs []error
42+
var validImps []openrtb.Imp
43+
44+
if len(request.Imp) == 0 {
45+
return nil, []error{&errortypes.BadInput{
46+
Message: "No impressions in request",
47+
}}
48+
}
49+
50+
for _, imp := range request.Imp {
51+
if err := preprocess(&imp); err != nil {
52+
errs = append(errs, err)
53+
continue
54+
}
55+
validImps = append(validImps, imp)
56+
}
57+
58+
if len(validImps) == 0 {
59+
return nil, errs
60+
}
61+
62+
request.Imp = validImps
63+
64+
reqJSON, err := json.Marshal(request)
65+
if err != nil {
66+
errs = append(errs, err)
67+
return nil, errs
68+
}
69+
70+
headers := http.Header{}
71+
headers.Add("Content-Type", "application/json;charset=utf-8")
72+
headers.Add("Accept", "application/json")
73+
return &adapters.RequestData{
74+
Method: "POST",
75+
Uri: a.endpoint,
76+
Body: reqJSON,
77+
Headers: headers,
78+
}, errs
79+
}
80+
81+
func preprocess(imp *openrtb.Imp) error {
82+
var bidderExt adapters.ExtImpBidder
83+
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
84+
return &errortypes.BadInput{
85+
Message: err.Error(),
86+
}
87+
}
88+
89+
var admixerExt openrtb_ext.ExtImpAdmixer
90+
if err := json.Unmarshal(bidderExt.Bidder, &admixerExt); err != nil {
91+
return &errortypes.BadInput{
92+
Message: "Wrong Admixer bidder ext",
93+
}
94+
}
95+
96+
//don't use regexp due to possible performance reduce
97+
if len(admixerExt.ZoneId) != 36 {
98+
return &errortypes.BadInput{
99+
Message: "ZoneId must be UUID/GUID",
100+
}
101+
}
102+
103+
imp.TagID = admixerExt.ZoneId
104+
imp.BidFloor = admixerExt.CustomBidFloor
105+
106+
imp.Ext = nil
107+
108+
if admixerExt.CustomParams != nil {
109+
impExt := admixerImpExt{
110+
CustomParams: admixerExt.CustomParams,
111+
}
112+
var err error
113+
if imp.Ext, err = json.Marshal(impExt); err != nil {
114+
return &errortypes.BadInput{
115+
Message: err.Error(),
116+
}
117+
}
118+
}
119+
120+
return nil
121+
}
122+
123+
func (a *AdmixerAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
124+
if response.StatusCode == http.StatusNoContent {
125+
return nil, nil
126+
}
127+
128+
if response.StatusCode >= http.StatusInternalServerError {
129+
return nil, []error{&errortypes.BadServerResponse{
130+
Message: fmt.Sprintf("Unexpected status code: %d. Dsp server internal error", response.StatusCode),
131+
}}
132+
}
133+
134+
if response.StatusCode >= http.StatusBadRequest {
135+
return nil, []error{&errortypes.BadInput{
136+
Message: fmt.Sprintf("Unexpected status code: %d. Bad request to dsp", response.StatusCode),
137+
}}
138+
}
139+
140+
if response.StatusCode != http.StatusOK {
141+
return nil, []error{&errortypes.BadServerResponse{
142+
Message: fmt.Sprintf("Unexpected status code: %d", response.StatusCode),
143+
}}
144+
}
145+
146+
var bidResp openrtb.BidResponse
147+
if err := json.Unmarshal(response.Body, &bidResp); err != nil {
148+
return nil, []error{err}
149+
}
150+
151+
//additional no content check
152+
if len(bidResp.SeatBid) == 0 || len(bidResp.SeatBid[0].Bid) == 0 {
153+
return nil, nil
154+
}
155+
156+
bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid))
157+
158+
for _, sb := range bidResp.SeatBid {
159+
for i := range sb.Bid {
160+
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
161+
Bid: &sb.Bid[i],
162+
BidType: getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp),
163+
})
164+
}
165+
}
166+
return bidResponse, nil
167+
}
168+
169+
func getMediaTypeForImp(impID string, imps []openrtb.Imp) openrtb_ext.BidType {
170+
for _, imp := range imps {
171+
if imp.ID == impID {
172+
if imp.Banner != nil {
173+
return openrtb_ext.BidTypeBanner
174+
} else if imp.Video != nil {
175+
return openrtb_ext.BidTypeVideo
176+
} else if imp.Native != nil {
177+
return openrtb_ext.BidTypeNative
178+
} else if imp.Audio != nil {
179+
return openrtb_ext.BidTypeAudio
180+
}
181+
}
182+
}
183+
return openrtb_ext.BidTypeBanner
184+
}

adapters/admixer/admixer_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package admixer
2+
3+
import (
4+
"github.com/prebid/prebid-server/adapters/adapterstest"
5+
"testing"
6+
)
7+
8+
func TestJsonSamples(t *testing.T) {
9+
adapterstest.RunJSONBidderTest(t, "admixertest", NewAdmixerBidder("http://inv-nets.admixer.net/pbs.aspx"))
10+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
{
2+
"mockBidRequest": {
3+
"id": "test-request-id",
4+
"imp": [
5+
{
6+
"id": "test-imp-id",
7+
"banner": {
8+
"format": [
9+
{
10+
"w": 728,
11+
"h": 90
12+
}
13+
]
14+
},
15+
"ext": {
16+
"bidder": {
17+
"zone": "2eb6bd58-865c-47ce-af7f-a918108c3fd2",
18+
"customFloor": 0.1,
19+
"customParams": {
20+
"foo": "bar"
21+
}
22+
}
23+
}
24+
},
25+
{
26+
"id": "test-imp-id",
27+
"banner": {
28+
"format": [
29+
{
30+
"w": 728,
31+
"h": 90
32+
}
33+
]
34+
},
35+
"ext": {
36+
"bidder": {
37+
"zone": "2eb6bd58-865c-47ce-af7f-a918108c3fd2",
38+
"customFloor": 0.1,
39+
"customParams": {
40+
"foo": [
41+
"bar",
42+
"baz"
43+
]
44+
}
45+
}
46+
}
47+
}
48+
]
49+
},
50+
"httpCalls": [
51+
{
52+
"expectedRequest": {
53+
"uri": "http://inv-nets.admixer.net/pbs.aspx",
54+
"body": {
55+
"id": "test-request-id",
56+
"imp": [
57+
{
58+
"id": "test-imp-id",
59+
"banner": {
60+
"format": [
61+
{
62+
"w": 728,
63+
"h": 90
64+
}
65+
]
66+
},
67+
"tagid": "2eb6bd58-865c-47ce-af7f-a918108c3fd2",
68+
"bidfloor": 0.1,
69+
"ext": {
70+
"customParams": {
71+
"foo": "bar"
72+
}
73+
}
74+
},
75+
{
76+
"id": "test-imp-id",
77+
"banner": {
78+
"format": [
79+
{
80+
"w": 728,
81+
"h": 90
82+
}
83+
]
84+
},
85+
"tagid": "2eb6bd58-865c-47ce-af7f-a918108c3fd2",
86+
"bidfloor": 0.1,
87+
"ext": {
88+
"customParams": {
89+
"foo": [
90+
"bar",
91+
"baz"
92+
]
93+
}
94+
}
95+
}
96+
]
97+
}
98+
},
99+
"mockResponse": {
100+
"status": 204
101+
}
102+
}
103+
]
104+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
"mockBidRequest": {
3+
"id": "test-request-id",
4+
"app": {
5+
"bundle": "com.prebid"
6+
},
7+
"device": {
8+
"ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3"
9+
},
10+
"imp": [
11+
{
12+
"id": "test-imp-id",
13+
"audio": {
14+
"mimes": ["audio/mp4"],
15+
"protocols": [9,10]
16+
},
17+
"ext": {
18+
"bidder": {
19+
"zone": "473e443c-43d0-423d-a8d7-a302637a01d8"
20+
}
21+
}
22+
}
23+
]
24+
},
25+
26+
"httpCalls": [
27+
{
28+
"expectedRequest": {
29+
"uri": "http://inv-nets.admixer.net/pbs.aspx",
30+
"body": {
31+
"id": "test-request-id",
32+
"app": {
33+
"bundle": "com.prebid"
34+
},
35+
"device": {
36+
"ifa":"ec943cb9-61ec-460f-a925-6489c3fcc4e3"
37+
},
38+
"imp": [
39+
{
40+
"id": "test-imp-id",
41+
"audio": {
42+
"mimes": ["audio/mp4"],
43+
"protocols": [9,10]
44+
},
45+
"tagid": "473e443c-43d0-423d-a8d7-a302637a01d8"
46+
}
47+
]
48+
}
49+
},
50+
"mockResponse": {
51+
"status": 200,
52+
"body": {
53+
"id": "test-request-id",
54+
"seatbid": [
55+
{
56+
"seat": "admixer",
57+
"bid": [{
58+
"id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
59+
"impid": "test-imp-id",
60+
"price": 0.500000,
61+
"adm": "some-test-ad",
62+
"crid": "test-crid"
63+
}]
64+
}
65+
],
66+
"cur": "USD"
67+
}
68+
}
69+
}
70+
],
71+
72+
"expectedBidResponses": [
73+
{
74+
"currency": "USD",
75+
"bids": [
76+
{
77+
"bid": {
78+
"id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
79+
"impid": "test-imp-id",
80+
"price": 0.5,
81+
"adm": "some-test-ad",
82+
"crid": "test-crid"
83+
},
84+
"type": "audio"
85+
}
86+
]
87+
}
88+
]
89+
}

0 commit comments

Comments
 (0)