This repository has been archived by the owner on Dec 22, 2022. It is now read-only.
forked from prebid/prebid-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: Ryan Haksi <ryan.haksi@freshgrade.com>
- Loading branch information
1 parent
1517202
commit a9a5d95
Showing
35 changed files
with
1,394 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
package kidoz | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"net/http" | ||
"strconv" | ||
|
||
"github.com/mxmCherry/openrtb" | ||
"github.com/prebid/prebid-server/adapters" | ||
"github.com/prebid/prebid-server/errortypes" | ||
"github.com/prebid/prebid-server/openrtb_ext" | ||
) | ||
|
||
type KidozAdapter struct { | ||
endpoint string | ||
} | ||
|
||
func (a *KidozAdapter) MakeRequests(request *openrtb.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { | ||
headers := http.Header{} | ||
headers.Add("Content-Type", "application/json;charset=utf-8") | ||
headers.Add("Accept", "application/json") | ||
headers.Add("x-openrtb-version", "2.5") | ||
|
||
impressions := request.Imp | ||
result := make([]*adapters.RequestData, 0, len(impressions)) | ||
errs := make([]error, 0, len(impressions)) | ||
|
||
for i, impression := range impressions { | ||
if impression.Banner == nil && impression.Video == nil { | ||
errs = append(errs, &errortypes.BadInput{ | ||
Message: "Kidoz only supports banner or video ads", | ||
}) | ||
continue | ||
} | ||
|
||
if impression.Banner != nil { | ||
banner := impression.Banner | ||
if banner.Format == nil { | ||
errs = append(errs, &errortypes.BadInput{ | ||
Message: "banner format required", | ||
}) | ||
continue | ||
} | ||
if len(banner.Format) == 0 { | ||
errs = append(errs, &errortypes.BadInput{ | ||
Message: "banner format array is empty", | ||
}) | ||
continue | ||
} | ||
} | ||
|
||
if len(impression.Ext) == 0 { | ||
errs = append(errs, errors.New("impression extensions required")) | ||
continue | ||
} | ||
var bidderExt adapters.ExtImpBidder | ||
err := json.Unmarshal(impression.Ext, &bidderExt) | ||
if err != nil { | ||
errs = append(errs, err) | ||
continue | ||
} | ||
if len(bidderExt.Bidder) == 0 { | ||
errs = append(errs, errors.New("bidder required")) | ||
continue | ||
} | ||
var impressionExt openrtb_ext.ExtImpKidoz | ||
err = json.Unmarshal(bidderExt.Bidder, &impressionExt) | ||
if err != nil { | ||
errs = append(errs, err) | ||
continue | ||
} | ||
if impressionExt.AccessToken == "" { | ||
errs = append(errs, errors.New("Kidoz access_token required")) | ||
continue | ||
} | ||
if impressionExt.PublisherID == "" { | ||
errs = append(errs, errors.New("Kidoz publisher_id required")) | ||
continue | ||
} | ||
|
||
request.Imp = impressions[i : i+1] | ||
body, err := json.Marshal(request) | ||
if err != nil { | ||
errs = append(errs, err) | ||
continue | ||
} | ||
result = append(result, &adapters.RequestData{ | ||
Method: "POST", | ||
Uri: a.endpoint, | ||
Body: body, | ||
Headers: headers, | ||
}) | ||
} | ||
|
||
request.Imp = impressions | ||
|
||
if len(result) == 0 { | ||
return nil, errs | ||
} | ||
return result, errs | ||
} | ||
|
||
func (a *KidozAdapter) MakeBids(request *openrtb.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { | ||
var errs []error | ||
|
||
switch responseData.StatusCode { | ||
case http.StatusNoContent: | ||
fallthrough | ||
case http.StatusServiceUnavailable: | ||
return nil, nil | ||
|
||
case http.StatusBadRequest: | ||
fallthrough | ||
case http.StatusUnauthorized: | ||
fallthrough | ||
case http.StatusForbidden: | ||
return nil, []error{&errortypes.BadInput{ | ||
Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode) + " " + string(responseData.Body), | ||
}} | ||
|
||
case http.StatusOK: | ||
break | ||
|
||
default: | ||
return nil, []error{&errortypes.BadServerResponse{ | ||
Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode) + " " + string(responseData.Body), | ||
}} | ||
} | ||
|
||
var bidResponse openrtb.BidResponse | ||
err := json.Unmarshal(responseData.Body, &bidResponse) | ||
if err != nil { | ||
return nil, []error{&errortypes.BadServerResponse{ | ||
Message: err.Error(), | ||
}} | ||
} | ||
|
||
response := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) | ||
|
||
for _, seatBid := range bidResponse.SeatBid { | ||
for _, bid := range seatBid.Bid { | ||
thisBid := bid | ||
bidType := GetMediaTypeForImp(bid.ImpID, request.Imp) | ||
if bidType == UndefinedMediaType { | ||
errs = append(errs, &errortypes.BadServerResponse{ | ||
Message: "ignoring bid id=" + bid.ID + ", request doesn't contain any valid impression with id=" + bid.ImpID, | ||
}) | ||
continue | ||
} | ||
response.Bids = append(response.Bids, &adapters.TypedBid{ | ||
Bid: &thisBid, | ||
BidType: bidType, | ||
}) | ||
} | ||
} | ||
|
||
return response, errs | ||
} | ||
|
||
func NewKidozBidder(endpoint string) *KidozAdapter { | ||
return &KidozAdapter{ | ||
endpoint: endpoint, | ||
} | ||
} | ||
|
||
const UndefinedMediaType = openrtb_ext.BidType("") | ||
|
||
func GetMediaTypeForImp(impID string, imps []openrtb.Imp) openrtb_ext.BidType { | ||
var bidType openrtb_ext.BidType = UndefinedMediaType | ||
for _, impression := range imps { | ||
if impression.ID != impID { | ||
continue | ||
} | ||
switch { | ||
case impression.Banner != nil: | ||
bidType = openrtb_ext.BidTypeBanner | ||
case impression.Video != nil: | ||
bidType = openrtb_ext.BidTypeVideo | ||
case impression.Native != nil: | ||
bidType = openrtb_ext.BidTypeNative | ||
case impression.Audio != nil: | ||
bidType = openrtb_ext.BidTypeAudio | ||
} | ||
break | ||
} | ||
return bidType | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
package kidoz | ||
|
||
import ( | ||
"math" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/mxmCherry/openrtb" | ||
"github.com/prebid/prebid-server/adapters" | ||
"github.com/prebid/prebid-server/adapters/adapterstest" | ||
"github.com/prebid/prebid-server/openrtb_ext" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestJsonSamples(t *testing.T) { | ||
adapterstest.RunJSONBidderTest(t, "kidoztest", NewKidozBidder("http://example.com/prebid")) | ||
} | ||
|
||
func makeBidRequest() *openrtb.BidRequest { | ||
request := &openrtb.BidRequest{ | ||
ID: "test-req-id-0", | ||
Imp: []openrtb.Imp{ | ||
{ | ||
ID: "test-imp-id-0", | ||
Banner: &openrtb.Banner{ | ||
Format: []openrtb.Format{ | ||
{ | ||
W: 320, | ||
H: 50, | ||
}, | ||
}, | ||
}, | ||
Ext: []byte(`{"bidder":{"access_token":"token-0","publisher_id":"pub-0"}}`), | ||
}, | ||
}, | ||
} | ||
return request | ||
} | ||
|
||
func TestMakeRequests(t *testing.T) { | ||
kidoz := NewKidozBidder("http://example.com/prebid") | ||
|
||
t.Run("Handles Request marshal failure", func(t *testing.T) { | ||
request := makeBidRequest() | ||
request.Imp[0].BidFloor = math.Inf(1) // cant be marshalled | ||
extra := &adapters.ExtraRequestInfo{} | ||
reqs, errs := kidoz.MakeRequests(request, extra) | ||
// cant assert message its different on different versions of go | ||
assert.Equal(t, 1, len(errs)) | ||
assert.Contains(t, errs[0].Error(), "json") | ||
assert.Equal(t, 0, len(reqs)) | ||
}) | ||
} | ||
|
||
func TestMakeBids(t *testing.T) { | ||
kidoz := NewKidozBidder("http://example.com/prebid") | ||
|
||
t.Run("Handles response marshal failure", func(t *testing.T) { | ||
request := makeBidRequest() | ||
requestData := &adapters.RequestData{} | ||
responseData := &adapters.ResponseData{ | ||
StatusCode: http.StatusOK, | ||
} | ||
|
||
resp, errs := kidoz.MakeBids(request, requestData, responseData) | ||
// cant assert message its different on different versions of go | ||
assert.Equal(t, 1, len(errs)) | ||
assert.Contains(t, errs[0].Error(), "JSON") | ||
assert.Nil(t, resp) | ||
}) | ||
} | ||
|
||
func TestGetMediaTypeForImp(t *testing.T) { | ||
imps := []openrtb.Imp{ | ||
{ | ||
ID: "1", | ||
Banner: &openrtb.Banner{}, | ||
}, | ||
{ | ||
ID: "2", | ||
Video: &openrtb.Video{}, | ||
}, | ||
{ | ||
ID: "3", | ||
Native: &openrtb.Native{}, | ||
}, | ||
{ | ||
ID: "4", | ||
Audio: &openrtb.Audio{}, | ||
}, | ||
} | ||
|
||
t.Run("Bid not found is type empty string", func(t *testing.T) { | ||
actual := GetMediaTypeForImp("ARGLE_BARGLE", imps) | ||
assert.Equal(t, UndefinedMediaType, actual) | ||
}) | ||
t.Run("Can find banner type", func(t *testing.T) { | ||
actual := GetMediaTypeForImp("1", imps) | ||
assert.Equal(t, openrtb_ext.BidTypeBanner, actual) | ||
}) | ||
t.Run("Can find video type", func(t *testing.T) { | ||
actual := GetMediaTypeForImp("2", imps) | ||
assert.Equal(t, openrtb_ext.BidTypeVideo, actual) | ||
}) | ||
t.Run("Can find native type", func(t *testing.T) { | ||
actual := GetMediaTypeForImp("3", imps) | ||
assert.Equal(t, openrtb_ext.BidTypeNative, actual) | ||
}) | ||
t.Run("Can find audio type", func(t *testing.T) { | ||
actual := GetMediaTypeForImp("4", imps) | ||
assert.Equal(t, openrtb_ext.BidTypeAudio, actual) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
{ | ||
"mockBidRequest": { | ||
"id": "test-request-id", | ||
"imp": [ | ||
{ | ||
"id": "test-impression-id-1", | ||
"banner": { | ||
"format": [ | ||
{ | ||
"w": 320, | ||
"h": 50 | ||
} | ||
] | ||
}, | ||
"ext": { | ||
"bidder": { | ||
"access_token": "test-token-1", | ||
"publisher_id": "test-publisher-1" | ||
} | ||
} | ||
} | ||
] | ||
}, | ||
"httpCalls": [ | ||
{ | ||
"expectedRequest": { | ||
"uri": "http://example.com/prebid", | ||
"body": { | ||
"id": "test-request-id", | ||
"imp": [ | ||
{ | ||
"id": "test-impression-id-1", | ||
"banner": { | ||
"format": [ | ||
{ | ||
"w": 320, | ||
"h": 50 | ||
} | ||
] | ||
}, | ||
"ext": { | ||
"bidder": { | ||
"access_token": "test-token-1", | ||
"publisher_id": "test-publisher-1" | ||
} | ||
} | ||
} | ||
] | ||
} | ||
}, | ||
"mockResponse": { | ||
"status": 200, | ||
"body": { | ||
"id": "test-response-id", | ||
"seatbid": [ | ||
{ | ||
"bid": [ | ||
{ | ||
"id": "test-bid-id-1", | ||
"impid": "test-impression-id-1", | ||
"price": 1 | ||
} | ||
], | ||
"seat": "kidoz" | ||
} | ||
] | ||
} | ||
} | ||
} | ||
] | ||
} |
Oops, something went wrong.