Skip to content
This repository was archived by the owner on Dec 22, 2022. It is now read-only.

Commit d1969aa

Browse files
Kidoz adapter (prebid#1210)
Co-authored-by: Ryan Haksi <[email protected]>
1 parent 25ea035 commit d1969aa

35 files changed

+1394
-7
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ debug
3838
pbs.*
3939
inventory_url.yaml
4040

41+
# generated log files during tests
42+
analytics/config/testFiles/
43+
analytics/filesystem/testFiles/
44+
4145
# autogenerated version file
4246
# static/version.txt
4347

adapters/kidoz/kidoz.go

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
package kidoz
2+
3+
import (
4+
"encoding/json"
5+
"errors"
6+
"net/http"
7+
"strconv"
8+
9+
"github.com/mxmCherry/openrtb"
10+
"github.com/prebid/prebid-server/adapters"
11+
"github.com/prebid/prebid-server/errortypes"
12+
"github.com/prebid/prebid-server/openrtb_ext"
13+
)
14+
15+
type KidozAdapter struct {
16+
endpoint string
17+
}
18+
19+
func (a *KidozAdapter) MakeRequests(request *openrtb.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
20+
headers := http.Header{}
21+
headers.Add("Content-Type", "application/json;charset=utf-8")
22+
headers.Add("Accept", "application/json")
23+
headers.Add("x-openrtb-version", "2.5")
24+
25+
impressions := request.Imp
26+
result := make([]*adapters.RequestData, 0, len(impressions))
27+
errs := make([]error, 0, len(impressions))
28+
29+
for i, impression := range impressions {
30+
if impression.Banner == nil && impression.Video == nil {
31+
errs = append(errs, &errortypes.BadInput{
32+
Message: "Kidoz only supports banner or video ads",
33+
})
34+
continue
35+
}
36+
37+
if impression.Banner != nil {
38+
banner := impression.Banner
39+
if banner.Format == nil {
40+
errs = append(errs, &errortypes.BadInput{
41+
Message: "banner format required",
42+
})
43+
continue
44+
}
45+
if len(banner.Format) == 0 {
46+
errs = append(errs, &errortypes.BadInput{
47+
Message: "banner format array is empty",
48+
})
49+
continue
50+
}
51+
}
52+
53+
if len(impression.Ext) == 0 {
54+
errs = append(errs, errors.New("impression extensions required"))
55+
continue
56+
}
57+
var bidderExt adapters.ExtImpBidder
58+
err := json.Unmarshal(impression.Ext, &bidderExt)
59+
if err != nil {
60+
errs = append(errs, err)
61+
continue
62+
}
63+
if len(bidderExt.Bidder) == 0 {
64+
errs = append(errs, errors.New("bidder required"))
65+
continue
66+
}
67+
var impressionExt openrtb_ext.ExtImpKidoz
68+
err = json.Unmarshal(bidderExt.Bidder, &impressionExt)
69+
if err != nil {
70+
errs = append(errs, err)
71+
continue
72+
}
73+
if impressionExt.AccessToken == "" {
74+
errs = append(errs, errors.New("Kidoz access_token required"))
75+
continue
76+
}
77+
if impressionExt.PublisherID == "" {
78+
errs = append(errs, errors.New("Kidoz publisher_id required"))
79+
continue
80+
}
81+
82+
request.Imp = impressions[i : i+1]
83+
body, err := json.Marshal(request)
84+
if err != nil {
85+
errs = append(errs, err)
86+
continue
87+
}
88+
result = append(result, &adapters.RequestData{
89+
Method: "POST",
90+
Uri: a.endpoint,
91+
Body: body,
92+
Headers: headers,
93+
})
94+
}
95+
96+
request.Imp = impressions
97+
98+
if len(result) == 0 {
99+
return nil, errs
100+
}
101+
return result, errs
102+
}
103+
104+
func (a *KidozAdapter) MakeBids(request *openrtb.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
105+
var errs []error
106+
107+
switch responseData.StatusCode {
108+
case http.StatusNoContent:
109+
fallthrough
110+
case http.StatusServiceUnavailable:
111+
return nil, nil
112+
113+
case http.StatusBadRequest:
114+
fallthrough
115+
case http.StatusUnauthorized:
116+
fallthrough
117+
case http.StatusForbidden:
118+
return nil, []error{&errortypes.BadInput{
119+
Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode) + " " + string(responseData.Body),
120+
}}
121+
122+
case http.StatusOK:
123+
break
124+
125+
default:
126+
return nil, []error{&errortypes.BadServerResponse{
127+
Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode) + " " + string(responseData.Body),
128+
}}
129+
}
130+
131+
var bidResponse openrtb.BidResponse
132+
err := json.Unmarshal(responseData.Body, &bidResponse)
133+
if err != nil {
134+
return nil, []error{&errortypes.BadServerResponse{
135+
Message: err.Error(),
136+
}}
137+
}
138+
139+
response := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
140+
141+
for _, seatBid := range bidResponse.SeatBid {
142+
for _, bid := range seatBid.Bid {
143+
thisBid := bid
144+
bidType := GetMediaTypeForImp(bid.ImpID, request.Imp)
145+
if bidType == UndefinedMediaType {
146+
errs = append(errs, &errortypes.BadServerResponse{
147+
Message: "ignoring bid id=" + bid.ID + ", request doesn't contain any valid impression with id=" + bid.ImpID,
148+
})
149+
continue
150+
}
151+
response.Bids = append(response.Bids, &adapters.TypedBid{
152+
Bid: &thisBid,
153+
BidType: bidType,
154+
})
155+
}
156+
}
157+
158+
return response, errs
159+
}
160+
161+
func NewKidozBidder(endpoint string) *KidozAdapter {
162+
return &KidozAdapter{
163+
endpoint: endpoint,
164+
}
165+
}
166+
167+
const UndefinedMediaType = openrtb_ext.BidType("")
168+
169+
func GetMediaTypeForImp(impID string, imps []openrtb.Imp) openrtb_ext.BidType {
170+
var bidType openrtb_ext.BidType = UndefinedMediaType
171+
for _, impression := range imps {
172+
if impression.ID != impID {
173+
continue
174+
}
175+
switch {
176+
case impression.Banner != nil:
177+
bidType = openrtb_ext.BidTypeBanner
178+
case impression.Video != nil:
179+
bidType = openrtb_ext.BidTypeVideo
180+
case impression.Native != nil:
181+
bidType = openrtb_ext.BidTypeNative
182+
case impression.Audio != nil:
183+
bidType = openrtb_ext.BidTypeAudio
184+
}
185+
break
186+
}
187+
return bidType
188+
}

adapters/kidoz/kidoz_test.go

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package kidoz
2+
3+
import (
4+
"math"
5+
"net/http"
6+
"testing"
7+
8+
"github.com/mxmCherry/openrtb"
9+
"github.com/prebid/prebid-server/adapters"
10+
"github.com/prebid/prebid-server/adapters/adapterstest"
11+
"github.com/prebid/prebid-server/openrtb_ext"
12+
"github.com/stretchr/testify/assert"
13+
)
14+
15+
func TestJsonSamples(t *testing.T) {
16+
adapterstest.RunJSONBidderTest(t, "kidoztest", NewKidozBidder("http://example.com/prebid"))
17+
}
18+
19+
func makeBidRequest() *openrtb.BidRequest {
20+
request := &openrtb.BidRequest{
21+
ID: "test-req-id-0",
22+
Imp: []openrtb.Imp{
23+
{
24+
ID: "test-imp-id-0",
25+
Banner: &openrtb.Banner{
26+
Format: []openrtb.Format{
27+
{
28+
W: 320,
29+
H: 50,
30+
},
31+
},
32+
},
33+
Ext: []byte(`{"bidder":{"access_token":"token-0","publisher_id":"pub-0"}}`),
34+
},
35+
},
36+
}
37+
return request
38+
}
39+
40+
func TestMakeRequests(t *testing.T) {
41+
kidoz := NewKidozBidder("http://example.com/prebid")
42+
43+
t.Run("Handles Request marshal failure", func(t *testing.T) {
44+
request := makeBidRequest()
45+
request.Imp[0].BidFloor = math.Inf(1) // cant be marshalled
46+
extra := &adapters.ExtraRequestInfo{}
47+
reqs, errs := kidoz.MakeRequests(request, extra)
48+
// cant assert message its different on different versions of go
49+
assert.Equal(t, 1, len(errs))
50+
assert.Contains(t, errs[0].Error(), "json")
51+
assert.Equal(t, 0, len(reqs))
52+
})
53+
}
54+
55+
func TestMakeBids(t *testing.T) {
56+
kidoz := NewKidozBidder("http://example.com/prebid")
57+
58+
t.Run("Handles response marshal failure", func(t *testing.T) {
59+
request := makeBidRequest()
60+
requestData := &adapters.RequestData{}
61+
responseData := &adapters.ResponseData{
62+
StatusCode: http.StatusOK,
63+
}
64+
65+
resp, errs := kidoz.MakeBids(request, requestData, responseData)
66+
// cant assert message its different on different versions of go
67+
assert.Equal(t, 1, len(errs))
68+
assert.Contains(t, errs[0].Error(), "JSON")
69+
assert.Nil(t, resp)
70+
})
71+
}
72+
73+
func TestGetMediaTypeForImp(t *testing.T) {
74+
imps := []openrtb.Imp{
75+
{
76+
ID: "1",
77+
Banner: &openrtb.Banner{},
78+
},
79+
{
80+
ID: "2",
81+
Video: &openrtb.Video{},
82+
},
83+
{
84+
ID: "3",
85+
Native: &openrtb.Native{},
86+
},
87+
{
88+
ID: "4",
89+
Audio: &openrtb.Audio{},
90+
},
91+
}
92+
93+
t.Run("Bid not found is type empty string", func(t *testing.T) {
94+
actual := GetMediaTypeForImp("ARGLE_BARGLE", imps)
95+
assert.Equal(t, UndefinedMediaType, actual)
96+
})
97+
t.Run("Can find banner type", func(t *testing.T) {
98+
actual := GetMediaTypeForImp("1", imps)
99+
assert.Equal(t, openrtb_ext.BidTypeBanner, actual)
100+
})
101+
t.Run("Can find video type", func(t *testing.T) {
102+
actual := GetMediaTypeForImp("2", imps)
103+
assert.Equal(t, openrtb_ext.BidTypeVideo, actual)
104+
})
105+
t.Run("Can find native type", func(t *testing.T) {
106+
actual := GetMediaTypeForImp("3", imps)
107+
assert.Equal(t, openrtb_ext.BidTypeNative, actual)
108+
})
109+
t.Run("Can find audio type", func(t *testing.T) {
110+
actual := GetMediaTypeForImp("4", imps)
111+
assert.Equal(t, openrtb_ext.BidTypeAudio, actual)
112+
})
113+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
{
2+
"mockBidRequest": {
3+
"id": "test-request-id",
4+
"imp": [
5+
{
6+
"id": "test-impression-id-1",
7+
"banner": {
8+
"format": [
9+
{
10+
"w": 320,
11+
"h": 50
12+
}
13+
]
14+
},
15+
"ext": {
16+
"bidder": {
17+
"access_token": "test-token-1",
18+
"publisher_id": "test-publisher-1"
19+
}
20+
}
21+
}
22+
]
23+
},
24+
"httpCalls": [
25+
{
26+
"expectedRequest": {
27+
"uri": "http://example.com/prebid",
28+
"body": {
29+
"id": "test-request-id",
30+
"imp": [
31+
{
32+
"id": "test-impression-id-1",
33+
"banner": {
34+
"format": [
35+
{
36+
"w": 320,
37+
"h": 50
38+
}
39+
]
40+
},
41+
"ext": {
42+
"bidder": {
43+
"access_token": "test-token-1",
44+
"publisher_id": "test-publisher-1"
45+
}
46+
}
47+
}
48+
]
49+
}
50+
},
51+
"mockResponse": {
52+
"status": 200,
53+
"body": {
54+
"id": "test-response-id",
55+
"seatbid": [
56+
{
57+
"bid": [
58+
{
59+
"id": "test-bid-id-1",
60+
"impid": "test-impression-id-1",
61+
"price": 1
62+
}
63+
],
64+
"seat": "kidoz"
65+
}
66+
]
67+
}
68+
}
69+
}
70+
]
71+
}

0 commit comments

Comments
 (0)