Skip to content

Commit

Permalink
Merge pull request #1 in RAD/app_prebid-server-spotx from add_spotx_a…
Browse files Browse the repository at this point in the history
…dapter to master

Squashed commit of the following:

commit 4a0d4c64c36903fc56f029f5b646cdace895139e
Author: Cameron Rice <cameron.l.rice@gmail.com>
Date:   Wed Jul 22 11:10:00 2020 -0400

    Cleaning up 204 case and updating bidder-info for media type

commit eb5a31065ee342e08670f207a726f54baf382cb2
Author: Cameron Rice <cameron.l.rice@gmail.com>
Date:   Mon Jul 20 16:49:09 2020 -0400

    Re-adding unmarshal error check

commit 184602b99076a5efd71ad77f4ab22522ad379296
Author: Cameron Rice <cameron.l.rice@gmail.com>
Date:   Mon Jul 20 13:13:08 2020 -0400

    Adding unit tests to increase coverage

commit 424c36850ebcfe0e72e240c8d060c715eb0fcd44
Author: Cameron Rice <cameron.l.rice@gmail.com>
Date:   Fri Jul 17 15:23:24 2020 -0400

    Further updates from code review comments

    * Code clean up
    * Added macro to endpoint URL
    * Renamed test file for better description

commit 0a44211706a15b2df3b4c13887b43ef3b5be2b6e
Author: Cameron Rice <cameron.l.rice@gmail.com>
Date:   Thu Jul 16 17:46:11 2020 -0400

    Updates from code review comments

    * Removed license file
    * Cleaned up adapter code
    * Updated maintainer

commit 4e84c38a9207ae64989e135e0f95800d069fd703
Author: Cameron Rice <cameron.l.rice@gmail.com>
Date:   Wed Jul 15 18:24:40 2020 -0400

    Initial commit for adapter
  • Loading branch information
Hans Hjort committed Jul 22, 2020
1 parent 034928e commit 31b3857
Show file tree
Hide file tree
Showing 20 changed files with 1,033 additions and 0 deletions.
52 changes: 52 additions & 0 deletions adapters/spotx/params_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package spotx

import (
"encoding/json"
"testing"

"github.com/prebid/prebid-server/openrtb_ext"
)

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.BidderSpotX, json.RawMessage(validParam)); err != nil {
t.Errorf("Schema rejected spotx params: %s", validParam)
}
}
}

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.BidderSpotX, json.RawMessage(invalidParam)); err == nil {
t.Errorf("Schema allowed unexpected params: %s", invalidParam)
}
}
}

var validParams = []string{
`{"channel_id":"123", "publisher_id":"456"}`,
}

var invalidParams = []string{
``,
`null`,
`true`,
`5`,
`4.2`,
`[]`,
`{}`,
`{"channel_id":"123"}`,
`{"publisher_id":"123"}`,
`{"channel_id":"123", "publisher_id":456}`,
`{"channel_id":123, "publisher_id":"456"}`,
}
192 changes: 192 additions & 0 deletions adapters/spotx/spotx.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
package spotx

import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"
"text/template"

"github.com/golang/glog"
"github.com/mxmCherry/openrtb"
"github.com/prebid/prebid-server/adapters"
"github.com/prebid/prebid-server/errortypes"
"github.com/prebid/prebid-server/macros"
"github.com/prebid/prebid-server/openrtb_ext"
)

type SpotXAdapter struct {
EndpointTemplate template.Template
}

type SpotXReqExt struct {
NumberOfAds int `json:"number_of_ads"`
ChannelID string `json:"channel_id"`
}

type SpotXBidExt struct {
Duration int `json:"duration"`
Tier string `json:"tier"`
}

func (a *SpotXAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
if len(request.Imp) == 0 {
return nil, []error{&errortypes.BadInput{
Message: "Request must have at least one Imp",
}}
}

var bidderExt adapters.ExtImpBidder
if err := json.Unmarshal(request.Imp[0].Ext, &bidderExt); err != nil {
return nil, []error{&errortypes.BadInput{
Message: err.Error(),
}}
}

var spotXExt openrtb_ext.ExtImpSpotX
if err := json.Unmarshal(bidderExt.Bidder, &spotXExt); err != nil {
return nil, []error{&errortypes.BadInput{
Message: err.Error(),
}}
}

if len(spotXExt.ChannelID) == 0 {
return nil, []error{&errortypes.BadInput{
Message: "Channel ID must be provided in bidder configuration",
}}
}

if len(spotXExt.PublisherID) == 0 {
return nil, []error{&errortypes.BadInput{
Message: "Publisher ID must be provided in bidder configuration",
}}
}

headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")

uri, err := a.buildEndpointURI(&spotXExt)
if err != nil {
return nil, []error{&errortypes.BadInput{
Message: err.Error(),
}}
}

spotXReqExt := SpotXReqExt{
NumberOfAds: len(request.Imp),
ChannelID: spotXExt.ChannelID,
}

spotXReqExtJSON, err := json.Marshal(spotXReqExt)
if err != nil {
return nil, []error{&errortypes.BadInput{
Message: err.Error(),
}}
}
request.Ext = spotXReqExtJSON

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

requests := []*adapters.RequestData{
{
Method: "POST",
Uri: uri,
Body: reqJSON,
Headers: headers,
},
}

return requests, nil
}

// Builds endpoint url based on adapter-specific pub settings from imp.ext
func (a *SpotXAdapter) buildEndpointURI(params *openrtb_ext.ExtImpSpotX) (string, error) {
endpointParams := macros.EndpointTemplateParams{PublisherID: params.PublisherID}
return macros.ResolveMacros(a.EndpointTemplate, endpointParams)
}

func (a *SpotXAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
if response.StatusCode == http.StatusNoContent {
errMsg := "204 status code received: Empty response"
// check if the empty response was due to an exception
if _, ok := response.Headers["X-spotx-Exception-RESULT"]; ok {
exceptionID := strings.Join(response.Headers["X-spotx-Exception-0-ID"], ", ")
exceptionMsg := strings.Join(response.Headers["X-spotx-Exception-0-Message"], ", ")

errMsg = fmt.Sprintf("204 status code received due to failure. Exception ID: %s. Exception Message: %s", exceptionID, exceptionMsg)
}
return nil, []error{&errortypes.BadServerResponse{
Message: errMsg,
}}
}

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{&errortypes.BadServerResponse{
Message: err.Error(),
}}
}

bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)

for _, sb := range bidResp.SeatBid {
for i := 0; i < len(sb.Bid); i++ {
bid := sb.Bid[i]
var bidExt SpotXBidExt
if err := json.Unmarshal(bid.Ext, &bidExt); err != nil {
return nil, []error{&errortypes.BadServerResponse{
Message: "Unable to unmarshal bid ext",
}}
}

if len(bid.Cat) > 0 {
bid.Cat = bid.Cat[0:1]
} else {
//create empty categories array to force bid to be rejected
bid.Cat = []string{}
}

impVideo := &openrtb_ext.ExtBidPrebidVideo{
Duration: bidExt.Duration,
}

bidTier := 0
if respTier, err := strconv.Atoi(bidExt.Tier); err == nil {
bidTier = respTier
}

bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
Bid: &bid,
BidVideo: impVideo,
BidType: openrtb_ext.BidTypeVideo,
DealPriority: bidTier,
})
}
}

return bidResponse, nil
}

func NewSpotXAdapter(endpointTemplate string) *SpotXAdapter {
template, err := template.New("endpointTemplate").Parse(endpointTemplate)
if err != nil {
glog.Fatal("Unable to parse endpoint url template")
return nil
}
return &SpotXAdapter{
EndpointTemplate: *template,
}
}
145 changes: 145 additions & 0 deletions adapters/spotx/spotx/exemplary/simple-video.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
{
"mockBidRequest": {
"id": "test-request-id",
"imp": [
{
"id": "test-imp-id",
"video": {
"mimes": [
"video/mp4"
],
"minduration": 15,
"maxduration": 30,
"protocols": [
2,
3,
5,
6,
7,
8
],
"w": 940,
"h": 560
},
"ext": {
"bidder": {
"channel_id": "123",
"publisher_id": "456"
}
}
}
]
},
"httpCalls": [
{
"expectedRequest": {
"uri": "http://search.spotxchange.com/openrtb/2.5/456",
"body": {
"id": "test-request-id",
"imp": [
{
"id": "test-imp-id",
"video": {
"mimes": [
"video/mp4"
],
"minduration": 15,
"maxduration": 30,
"protocols": [
2,
3,
5,
6,
7,
8
],
"w": 940,
"h": 560
},
"ext": {
"bidder": {
"channel_id": "123",
"publisher_id": "456"
}
}
}

],
"ext": {
"number_of_ads": 1,
"channel_id": "123"
}
}
},
"mockResponse": {
"status": 200,
"body": {
"id": "test-request-id",
"seatbid": [
{
"seat": "958",
"bid": [
{
"id": "7706636740145184841",
"impid": "test-imp-id",
"price": 0.500000,
"adid": "29681110",
"adm": "some-test-ad",
"adomain": [
"appnexus.com"
],
"iurl": "http://nym1-ib.adnxs.com/cr?id=29681110",
"cid": "958",
"crid": "29681110",
"h": 250,
"w": 300,
"cat": [
"IAB9-1"
],
"ext": {
"duration": 15,
"tier": "5"
}
}
]
}
],
"bidid": "5778926625248726496",
"cur": "USD"
}
}
}
],
"expectedBidResponses": [
{
"currency": "USD",
"bids": [
{
"bid": {
"id": "7706636740145184841",
"impid": "test-imp-id",
"price": 0.5,
"adm": "some-test-ad",
"adid": "29681110",
"adomain": [
"appnexus.com"
],
"iurl": "http://nym1-ib.adnxs.com/cr?id=29681110",
"cid": "958",
"crid": "29681110",
"w": 300,
"h": 250,
"cat": [
"IAB9-1"
],
"ext": {
"duration": 15,
"tier": "5"
}
},
"type": "video"
}
]
}
]
}
4 changes: 4 additions & 0 deletions adapters/spotx/spotx/params/race/video.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"channel_id": "123",
"publisher_id": "456"
}
Loading

0 comments on commit 31b3857

Please sign in to comment.