Skip to content
Open
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
1 change: 1 addition & 0 deletions .github/workflows/scripts/codepath-notification
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ adapters/ix|imp_ix|ix.json|ix.yaml: pdu-supply-prebid@indexexchange.com
medianet: prebid@media.net
gumgum: prebid@gumgum.com
kargo: kraken@kargo.com
proxistore: proxistore-technics@proxistore.com
51 changes: 51 additions & 0 deletions adapters/proxistore/params_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package proxistore

import (
"encoding/json"
"testing"

"github.com/prebid/prebid-server/v3/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 schema. %v", err)
}

for _, p := range validParams {
if err := validator.Validate(openrtb_ext.BidderProxistore, json.RawMessage(p)); err != nil {
t.Errorf("Schema rejected valid params: %s", p)
}
}
}

func TestInvalidParams(t *testing.T) {
validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
if err != nil {
t.Fatalf("Failed to fetch the json schema. %v", err)
}

for _, p := range invalidParams {
if err := validator.Validate(openrtb_ext.BidderProxistore, json.RawMessage(p)); err == nil {
t.Errorf("Schema allowed invalid params: %s", p)
}
}
}

var validParams = []string{
`{"website": "example.com", "language": "fr"}`,
`{"website": "test-site", "language": "en"}`,
`{"website": "publisher.be", "language": "nl"}`,
}

var invalidParams = []string{
`{}`,
`{"website": "example.com"}`,
`{"language": "fr"}`,
`{"website": "", "language": "fr"}`,
`{"website": "example.com", "language": ""}`,
`{"website": 123, "language": "fr"}`,
`{"website": "example.com", "language": 456}`,
`null`,
}
139 changes: 139 additions & 0 deletions adapters/proxistore/proxistore.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
package proxistore

import (
"fmt"
"net/http"

"github.com/prebid/openrtb/v20/openrtb2"
"github.com/prebid/prebid-server/v3/adapters"
"github.com/prebid/prebid-server/v3/config"
"github.com/prebid/prebid-server/v3/errortypes"
"github.com/prebid/prebid-server/v3/openrtb_ext"
"github.com/prebid/prebid-server/v3/util/jsonutil"
)

type adapter struct {
endpoint string
}

func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
bidder := &adapter{
endpoint: config.Endpoint,
}
return bidder, nil
}

func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
var errors []error

// Validate that all impressions have required bidder params
for _, imp := range request.Imp {
var extBidder adapters.ExtImpBidder
if err := jsonutil.Unmarshal(imp.Ext, &extBidder); err != nil {
errors = append(errors, &errortypes.BadInput{
Message: fmt.Sprintf("Error parsing imp[%s].ext: %s", imp.ID, err.Error()),
})
continue
}

var bidderExt openrtb_ext.ExtImpProxistore
if err := jsonutil.Unmarshal(extBidder.Bidder, &bidderExt); err != nil {
errors = append(errors, &errortypes.BadInput{
Message: fmt.Sprintf("Error parsing imp[%s].ext.bidder: %s", imp.ID, err.Error()),
})
continue
}

if bidderExt.Website == "" || bidderExt.Language == "" {
errors = append(errors, &errortypes.BadInput{
Message: fmt.Sprintf("imp[%s]: website and language are required bidder params", imp.ID),
})
}
}

if len(errors) > 0 {
return nil, errors
}

requestJSON, err := jsonutil.Marshal(request)
if err != nil {
return nil, []error{err}
}

headers := http.Header{}
headers.Add("Content-Type", "application/json")
headers.Add("Accept", "application/json")
headers.Add("X-Prebid-Source", "server")

requestData := &adapters.RequestData{
Method: "POST",
Uri: a.endpoint,
Body: requestJSON,
Headers: headers,
ImpIDs: openrtb_ext.GetImpIDs(request.Imp),
}

return []*adapters.RequestData{requestData}, nil
}

func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
if responseData.StatusCode == http.StatusNoContent {
return nil, nil
}

if responseData.StatusCode == http.StatusBadRequest {
err := &errortypes.BadInput{
Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
}
return nil, []error{err}
}

if responseData.StatusCode != http.StatusOK {
err := &errortypes.BadServerResponse{
Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode),
}
return nil, []error{err}
}

var response openrtb2.BidResponse
if err := jsonutil.Unmarshal(responseData.Body, &response); err != nil {
return nil, []error{err}
}

bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
bidResponse.Currency = response.Cur

var errors []error
for _, seatBid := range response.SeatBid {
for i, bid := range seatBid.Bid {
bidType, err := getMediaTypeForBid(bid)
if err != nil {
errors = append(errors, err)
continue
}
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
Bid: &seatBid.Bid[i],
BidType: bidType,
})
}
}

return bidResponse, errors
}

func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
switch bid.MType {
case openrtb2.MarkupBanner:
return openrtb_ext.BidTypeBanner, nil
case openrtb2.MarkupVideo:
return openrtb_ext.BidTypeVideo, nil
case openrtb2.MarkupAudio:
return openrtb_ext.BidTypeAudio, nil
case openrtb2.MarkupNative:
return openrtb_ext.BidTypeNative, nil
default:
return "", &errortypes.BadServerResponse{
Message: fmt.Sprintf("unsupported MType %d for bid %s", bid.MType, bid.ID),
}
}
}
27 changes: 27 additions & 0 deletions adapters/proxistore/proxistore_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package proxistore

import (
"testing"

"github.com/prebid/prebid-server/v3/adapters/adapterstest"
"github.com/prebid/prebid-server/v3/config"
"github.com/prebid/prebid-server/v3/openrtb_ext"
"github.com/stretchr/testify/assert"
)

func TestJsonSamples(t *testing.T) {
bidder, buildErr := Builder(
openrtb_ext.BidderProxistore,
config.Adapter{
Endpoint: "https://api.proxistore.com/v3/rtb/openrtb",
},
config.Server{
ExternalUrl: "http://hosturl.com",
GvlID: 1,
DataCenter: "2",
},
)

assert.NoError(t, buildErr)
adapterstest.RunJSONBidderTest(t, "proxistoretest", bidder)
}
151 changes: 151 additions & 0 deletions adapters/proxistore/proxistoretest/exemplary/multi-imp-banner.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
{
"mockBidRequest": {
"id": "test-request-id",
"device": {
"ip": "123.123.123.123",
"ua": "Mozilla/5.0"
},
"site": {
"domain": "example.com",
"page": "https://example.com/page"
},
"imp": [
{
"id": "imp-1",
"banner": {
"format": [{"w": 300, "h": 250}]
},
"ext": {
"bidder": {
"website": "example.com",
"language": "fr"
}
}
},
{
"id": "imp-2",
"banner": {
"format": [{"w": 728, "h": 90}]
},
"ext": {
"bidder": {
"website": "example.com",
"language": "fr"
}
}
}
]
},
"httpCalls": [
{
"expectedRequest": {
"uri": "https://api.proxistore.com/v3/rtb/openrtb",
"body": {
"id": "test-request-id",
"device": {
"ip": "123.123.123.123",
"ua": "Mozilla/5.0"
},
"site": {
"domain": "example.com",
"page": "https://example.com/page"
},
"imp": [
{
"id": "imp-1",
"banner": {
"format": [{"w": 300, "h": 250}]
},
"ext": {
"bidder": {
"website": "example.com",
"language": "fr"
}
}
},
{
"id": "imp-2",
"banner": {
"format": [{"w": 728, "h": 90}]
},
"ext": {
"bidder": {
"website": "example.com",
"language": "fr"
}
}
}
]
},
"impIDs": ["imp-1", "imp-2"]
},
"mockResponse": {
"status": 200,
"body": {
"id": "test-request-id",
"seatbid": [
{
"bid": [
{
"id": "bid-1",
"impid": "imp-1",
"price": 1.50,
"adm": "<div>Ad 1</div>",
"crid": "creative-1",
"w": 300,
"h": 250,
"mtype": 1
},
{
"id": "bid-2",
"impid": "imp-2",
"price": 0.80,
"adm": "<div>Ad 2</div>",
"crid": "creative-2",
"w": 728,
"h": 90,
"mtype": 1
}
],
"seat": "proxistore"
}
],
"cur": "EUR"
}
}
}
],
"expectedBidResponses": [
{
"currency": "EUR",
"bids": [
{
"bid": {
"id": "bid-1",
"impid": "imp-1",
"price": 1.50,
"adm": "<div>Ad 1</div>",
"crid": "creative-1",
"w": 300,
"h": 250,
"mtype": 1
},
"type": "banner"
},
{
"bid": {
"id": "bid-2",
"impid": "imp-2",
"price": 0.80,
"adm": "<div>Ad 2</div>",
"crid": "creative-2",
"w": 728,
"h": 90,
"mtype": 1
},
"type": "banner"
}
]
}
]
}
Loading
Loading