Skip to content

Commit

Permalink
New Adapter: Escalax (prebid#3798)
Browse files Browse the repository at this point in the history
  • Loading branch information
escalax authored Aug 30, 2024
1 parent 4ea0e33 commit 2e2b49f
Show file tree
Hide file tree
Showing 21 changed files with 1,896 additions and 0 deletions.
162 changes: 162 additions & 0 deletions adapters/escalax/escalax.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
package escalax

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

"github.com/prebid/openrtb/v20/openrtb2"
"github.com/prebid/prebid-server/v2/adapters"
"github.com/prebid/prebid-server/v2/config"
"github.com/prebid/prebid-server/v2/errortypes"
"github.com/prebid/prebid-server/v2/macros"
"github.com/prebid/prebid-server/v2/openrtb_ext"
)

type adapter struct {
endpoint *template.Template
}

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

bidder := &adapter{
endpoint: template,
}
return bidder, nil
}

func getHeaders(request *openrtb2.BidRequest) http.Header {
headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")
headers.Add("X-Openrtb-Version", "2.5")

if request.Device != nil {
if len(request.Device.UA) > 0 {
headers.Add("User-Agent", request.Device.UA)
}

if len(request.Device.IPv6) > 0 {
headers.Add("X-Forwarded-For", request.Device.IPv6)
}

if len(request.Device.IP) > 0 {
headers.Add("X-Forwarded-For", request.Device.IP)
}
}

return headers
}

func (a *adapter) MakeRequests(openRTBRequest *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) (requestsToBidder []*adapters.RequestData, errs []error) {
escalaxExt, err := getImpressionExt(&openRTBRequest.Imp[0])
if err != nil {
return nil, []error{err}
}

openRTBRequest.Imp[0].Ext = nil

url, err := a.buildEndpointURL(escalaxExt)
if err != nil {
return nil, []error{err}
}

reqJSON, err := json.Marshal(openRTBRequest)
if err != nil {
return nil, []error{err}
}

return []*adapters.RequestData{{
Method: http.MethodPost,
Body: reqJSON,
Uri: url,
Headers: getHeaders(openRTBRequest),
ImpIDs: openrtb_ext.GetImpIDs(openRTBRequest.Imp),
}}, nil
}

func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtEscalax, error) {
var bidderExt adapters.ExtImpBidder
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
return nil, &errortypes.BadInput{
Message: "Error parsing escalaxExt - " + err.Error(),
}
}
var escalaxExt openrtb_ext.ExtEscalax
if err := json.Unmarshal(bidderExt.Bidder, &escalaxExt); err != nil {
return nil, &errortypes.BadInput{
Message: "Error parsing bidderExt - " + err.Error(),
}
}

return &escalaxExt, nil
}

func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtEscalax) (string, error) {
endpointParams := macros.EndpointTemplateParams{AccountID: params.AccountID, SourceId: params.SourceID}
return macros.ResolveMacros(a.endpoint, endpointParams)
}

func (a *adapter) MakeBids(openRTBRequest *openrtb2.BidRequest, requestToBidder *adapters.RequestData, bidderRawResponse *adapters.ResponseData) (bidderResponse *adapters.BidderResponse, errs []error) {
if adapters.IsResponseStatusCodeNoContent(bidderRawResponse) {
return nil, nil
}

if err := adapters.CheckResponseStatusCodeForErrors(bidderRawResponse); err != nil {
return nil, []error{err}
}

responseBody := bidderRawResponse.Body
var bidResp openrtb2.BidResponse
if err := json.Unmarshal(responseBody, &bidResp); err != nil {
return nil, []error{&errortypes.BadServerResponse{
Message: "Bad Server Response",
}}
}

if len(bidResp.SeatBid) == 0 {
return nil, []error{&errortypes.BadServerResponse{
Message: "Empty SeatBid array",
}}
}

bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)
var bidsArray []*adapters.TypedBid

for _, sb := range bidResp.SeatBid {
for idx, bid := range sb.Bid {
bidType, err := determineImpressionMediaType(bid)
if err != nil {
return nil, []error{err}
}

bidsArray = append(bidsArray, &adapters.TypedBid{
Bid: &sb.Bid[idx],
BidType: bidType,
})
}
}

bidResponse.Bids = bidsArray
return bidResponse, nil
}

func determineImpressionMediaType(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.BadInput{
Message: fmt.Sprintf("unsupported MType %d", bid.MType),
}
}
}
28 changes: 28 additions & 0 deletions adapters/escalax/escalax_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package escalax

import (
"testing"

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

func TestJsonSamples(t *testing.T) {
bidder, buildErr := Builder(openrtb_ext.BidderEscalax, config.Adapter{
Endpoint: "http://bidder_us.escalax.io/?partner={{.SourceId}}&token={{.AccountID}}&type=pbs"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})

if buildErr != nil {
t.Fatalf("Builder returned unexpected error %v", buildErr)
}

adapterstest.RunJSONBidderTest(t, "escalaxtest", bidder)
}

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

assert.Error(t, buildErr)
}
155 changes: 155 additions & 0 deletions adapters/escalax/escalaxtest/exemplary/banner-app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
{
"mockBidRequest": {
"id": "some-request-id",
"device": {
"ua": "test-user-agent",
"ip": "123.123.123.123",
"language": "en",
"dnt": 0
},
"tmax": 1000,
"user": {
"buyeruid": "awesome-user"
},
"app": {
"publisher": {
"id": "123456789"
},
"cat": [
"IAB22-1"
],
"bundle": "com.app.awesome",
"name": "Awesome App",
"domain": "awesomeapp.com",
"id": "123456789"
},
"imp": [
{
"id": "1",
"tagid": "ogTAGID",
"banner": {
"w": 320,
"h": 50
},
"ext": {
"bidder": {
"accountId": "accountId",
"sourceId": "sourceId"
}
}
}
]
},
"httpCalls": [
{
"expectedRequest": {
"headers": {
"Content-Type": [
"application/json;charset=utf-8"
],
"Accept": [
"application/json"
],
"X-Openrtb-Version": [
"2.5"
],
"User-Agent": [
"test-user-agent"
],
"X-Forwarded-For": [
"123.123.123.123"
]
},
"uri": "http://bidder_us.escalax.io/?partner=sourceId&token=accountId&type=pbs",
"body": {
"id": "some-request-id",
"device": {
"ua": "test-user-agent",
"ip": "123.123.123.123",
"language": "en",
"dnt": 0
},
"imp": [
{
"id": "1",
"banner": {
"w": 320,
"h": 50
},
"tagid": "ogTAGID"
}
],
"app": {
"id": "123456789",
"name": "Awesome App",
"bundle": "com.app.awesome",
"domain": "awesomeapp.com",
"cat": [
"IAB22-1"
],
"publisher": {
"id": "123456789"
}
},
"user": {
"buyeruid": "awesome-user"
},
"tmax": 1000
},
"impIDs": [
"1"
]
},
"mockResponse": {
"status": 200,
"body": {
"id": "awesome-resp-id",
"seatbid": [
{
"bid": [
{
"id": "a3ae1b4e2fc24a4fb45540082e98e161",
"impid": "1",
"price": 3.5,
"adm": "awesome-markup",
"adomain": [
"awesome.com"
],
"crid": "20",
"w": 320,
"h": 50,
"mtype": 1
}
],
"type": "banner",
"seat": "escalax"
}
],
"cur": "USD"
}
}
}
],
"expectedBidResponses": [
{
"bids": [
{
"bid": {
"id": "a3ae1b4e2fc24a4fb45540082e98e161",
"impid": "1",
"price": 3.5,
"adm": "awesome-markup",
"adomain": [
"awesome.com"
],
"crid": "20",
"w": 320,
"h": 50,
"mtype": 1
},
"type": "banner"
}
]
}
]
}
Loading

0 comments on commit 2e2b49f

Please sign in to comment.