Skip to content
Merged
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
135 changes: 135 additions & 0 deletions adapters/blis/blis.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package blis

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

"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/macros"
"github.com/prebid/prebid-server/v3/openrtb_ext"
"github.com/prebid/prebid-server/v3/util/iterutil"
"github.com/prebid/prebid-server/v3/util/jsonutil"
)

type adapter struct {
endpointTemplate *template.Template
}

func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
endpointTemplate, err := template.New("endpointTemplate").Parse(config.Endpoint)
if err != nil {
return nil, fmt.Errorf("unable to parse endpoint url template: %w", err)
}

bidder := &adapter{
endpointTemplate: endpointTemplate,
}
return bidder, nil
}

func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
var impExt adapters.ExtImpBidder
if err := jsonutil.Unmarshal(request.Imp[0].Ext, &impExt); err != nil {
Comment thread
tomaskoutny-blis marked this conversation as resolved.
return nil, []error{&errortypes.BadInput{
Message: fmt.Sprintf("Invalid imp.ext for impression index %d. Error Infomation: %v", 0, err),
}}
}
var impExtBidder openrtb_ext.ImpExtBlis
if err := jsonutil.Unmarshal(impExt.Bidder, &impExtBidder); err != nil {
return nil, []error{&errortypes.BadInput{
Message: fmt.Sprintf("Invalid imp.ext.bidder for impression index %d. Error Infomation: %v", 0, err),
Comment thread
tomaskoutny-blis marked this conversation as resolved.
}}
}

endpoint, err := a.buildEndpointURL(&impExtBidder)
if err != nil {
return nil, []error{err}
}

headers := http.Header{}
headers.Add("X-Supply-Partner-Id", impExtBidder.SupplyPartnerID)

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

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

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

func (a *adapter) buildEndpointURL(impExtBidder *openrtb_ext.ImpExtBlis) (string, error) {
endpointParams := macros.EndpointTemplateParams{SupplyId: impExtBidder.SupplyPartnerID}
return macros.ResolveMacros(a.endpointTemplate, endpointParams)
}

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

if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil {
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 iterutil.SlicePointerValues(response.SeatBid) {
for bid := range iterutil.SlicePointerValues(seatBid.Bid) {
Comment thread
tomaskoutny-blis marked this conversation as resolved.
resolveMacros(bid)
bidType, err := getMediaTypeForBid(bid)
if err != nil {
errors = append(errors, err)
continue
}
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
Bid: bid,
BidType: bidType,
})
}
}
return bidResponse, nil
}

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.MarkupNative:
return openrtb_ext.BidTypeNative, nil
default:
return "", &errortypes.BadServerResponse{
Message: fmt.Sprintf("Failed to parse media type of impression ID \"%s\"", bid.ImpID),
}
}
}

func resolveMacros(bid *openrtb2.Bid) {
if bid != nil {
price := strconv.FormatFloat(bid.Price, 'f', -1, 64)
bid.NURL = strings.ReplaceAll(bid.NURL, "${AUCTION_PRICE}", price)
bid.AdM = strings.ReplaceAll(bid.AdM, "${AUCTION_PRICE}", price)
bid.BURL = strings.ReplaceAll(bid.BURL, "${AUCTION_PRICE}", price)
}
}
29 changes: 29 additions & 0 deletions adapters/blis/blis_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package blis

import (
"github.com/stretchr/testify/assert"
"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/require"
)

func TestEndpointTemplateMalformed(t *testing.T) {
_, buildErr := Builder(openrtb_ext.BidderBlis, config.Adapter{
Endpoint: "{{Malformed}}"},
config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})

assert.Error(t, buildErr)
}

func TestJsonSamples(t *testing.T) {
bidder, buildErr := Builder(openrtb_ext.BidderBlis, config.Adapter{
Endpoint: "https://example.endpoint/{{.SupplyId}}"},
config.Server{ExternalUrl: "http://example.server", GvlID: 1, DataCenter: "2"})

require.NoError(t, buildErr)

adapterstest.RunJSONBidderTest(t, "blistest", bidder)
}
171 changes: 171 additions & 0 deletions adapters/blis/blistest/exemplary/banner.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
{
"mockBidRequest": {
"id": "123456789",
"imp": [
{
"ext": {
"bidder": {
"spid": "999"
}
},
"id": "testimp",
"banner": {
"format": [
{
"w": 320,
"h": 50
},
{
"w": 728,
"h": 90
}
]
},
"bidfloor": 0.98,
"bidfloorcur": "USD"
}
],
"cur": [
"USD"
],
"site": {
"publisher": {
"domain": "example.com"
},
"page": "http://lh.example.com/prebid_server_kitchen_sink.html?pbjs_debug=true",
"cat": [
"site-cat"
]
},
"device": {
"w": 1434,
"h": 686,
"geo": {
"country": "USA"
},
"ip": "8.8.8.8"
}
},
"httpCalls": [
{
"expectedRequest": {
"uri": "https://example.endpoint/999",
"headers": {
"X-Supply-Partner-Id": [
"999"
]
},
"impIDs": [
"testimp"
],
"body": {
"id": "123456789",
"imp": [
{
"id": "testimp",
"banner": {
"format": [
{
"w": 320,
"h": 50
},
{
"w": 728,
"h": 90
}
]
},
"bidfloor": 0.98,
"bidfloorcur": "USD",
"ext": {
"bidder": {
"spid": "999"
}
}
}
],
"site": {
"cat": [
"site-cat"
],
"page": "http://lh.example.com/prebid_server_kitchen_sink.html?pbjs_debug=true",
"publisher": {
"domain": "example.com"
}
},
"device": {
"geo": {
"country": "USA"
},
"ip": "8.8.8.8",
"h": 686,
"w": 1434
},
"cur": [
"USD"
]
}
},
"mockResponse": {
"status": 200,
"body": {
"id": "123456789",
"seatbid": [
{
"seat": "None",
"bid": [
{
"id": "1",
"impid": "testimp",
"price": 1.2,
"crid": "9999999",
"w": 320,
"h": 50,
"adomain": [
"example.com"
],
"cid": "999999",
"iurl": "https://adserverexample.com/example.gif",
"adm": "<a href=\"https://clickexample.com/\" target=\"_blank\"><img src=\"https://adserverexample.com/example.gif\" width=\"320\" height=\"50\" alt=\"\" /></a>",
"cat": [
"IAB3-1"
],
"burl": "https://trackingexample.com/pixel?wp=${AUCTION_PRICE}",
"mtype": 1
}
]
}
]
}
}
}
],
"expectedBidResponses": [
{
"bids": [
{
"bid": {
"id": "1",
"impid": "testimp",
"price": 1.2,
"crid": "9999999",
"w": 320,
"h": 50,
"adomain": [
"example.com"
],
"cid": "999999",
"iurl": "https://adserverexample.com/example.gif",
"adm": "<a href=\"https://clickexample.com/\" target=\"_blank\"><img src=\"https://adserverexample.com/example.gif\" width=\"320\" height=\"50\" alt=\"\" /></a>",
"cat": [
"IAB3-1"
],
"burl": "https://trackingexample.com/pixel?wp=1.2",
"mtype": 1
},
"type": "banner"
}
]
}
]
}
Loading
Loading