Skip to content

Commit

Permalink
Add RoundhouseAds adapter with v3 compatibility and required test files
Browse files Browse the repository at this point in the history
  • Loading branch information
Julian Salinas committed Nov 5, 2024
1 parent 106f6e4 commit c3c68cc
Show file tree
Hide file tree
Showing 24 changed files with 2,674 additions and 4 deletions.
53 changes: 53 additions & 0 deletions adapters/roundhouseads/params_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package roundhouseads

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-schemas. %v", err)
}

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

// TestInvalidParams makes sure that the Roundhouseads schema rejects all the imp.ext fields we don't support.
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.BidderRoundhouseads, json.RawMessage(invalidParam)); err == nil {
t.Errorf("Schema allowed unexpected params: %s", invalidParam)
}
}
}

var validParams = []string{
`{"publisherId": "123456"}`,
`{"publisherId": "pub-123456"}`,
`{"publisherId": "publisherIDAllString"}`,
}

var invalidParams = []string{
``,
`null`,
`true`,
`5`,
`4.2`,
`[]`,
`{}`,
`{"publisherId": 123456}`,
`{"publisherId": 0}`,
}
196 changes: 196 additions & 0 deletions adapters/roundhouseads/roundhouseads.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
package roundhouseads

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

"github.com/prebid/prebid-server/v3/adapters"
"github.com/prebid/prebid-server/v3/config"
"github.com/prebid/prebid-server/v3/macros"
"github.com/prebid/prebid-server/v3/openrtb_ext"

"github.com/prebid/openrtb/v20/openrtb2"
)

const PREBID_INTEGRATION_TYPE = "1"

type adapter struct {
bidderEndpoint string
}

type ExtImpBidderRoundhouseads struct {
adapters.ExtImpBidder
}

func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
pubID, err := getPublisherId(request.Imp)

if err != nil {
return nil, []error{err}
}

modifiedImps := make([]openrtb2.Imp, 0, len(request.Imp))

for _, imp := range request.Imp {

if imp.Banner != nil {
if len(imp.Banner.Format) > 0 {
firstFormat := imp.Banner.Format[0]
bannerCopy := *imp.Banner
bannerCopy.H = &firstFormat.H
bannerCopy.W = &firstFormat.W
imp.Banner = &bannerCopy

}
}

modifiedImps = append(modifiedImps, imp)
}

request.Imp = modifiedImps

if request.Site != nil {
siteCopy := *request.Site
if siteCopy.Publisher != nil {
publisherCopy := *siteCopy.Publisher
if pubID != "" {
publisherCopy.ID = pubID
}
siteCopy.Publisher = &publisherCopy
} else {
siteCopy.Publisher = &openrtb2.Publisher{ID: pubID}
}
request.Site = &siteCopy
} else if request.App != nil {
appCopy := *request.App
if appCopy.Publisher != nil {
publisherCopy := *appCopy.Publisher
if pubID != "" {
publisherCopy.ID = pubID
}
appCopy.Publisher = &publisherCopy
} else {
appCopy.Publisher = &openrtb2.Publisher{ID: pubID}
}
request.App = &appCopy
}

errs := make([]error, 0, len(request.Imp))
reqJSON, err := json.Marshal(request)
if err != nil {
errs = append(errs, err)
return nil, errs
}

headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")
headers.Add("x-integration-type", PREBID_INTEGRATION_TYPE)
return []*adapters.RequestData{{
Method: "POST",
Uri: a.bidderEndpoint,
Body: reqJSON,
Headers: headers,
ImpIDs: openrtb_ext.GetImpIDs(request.Imp),
}}, errs
}

func getPublisherId(impressions []openrtb2.Imp) (string, error) {
for _, imp := range impressions {

var bidderExt ExtImpBidderRoundhouseads
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
return "", err
}

var roundhouseadsExt openrtb_ext.ExtImpRoundhouseads
if err := json.Unmarshal(bidderExt.Bidder, &roundhouseadsExt); err != nil {
return "", err
}

if roundhouseadsExt.PublisherId != "" {
return roundhouseadsExt.PublisherId, nil
}
}
return "", nil
}

func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
if adapters.IsResponseStatusCodeNoContent(response) {
return adapters.NewBidderResponse(), nil
}

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

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

bidderResponse := adapters.NewBidderResponse()
bidderResponse.Currency = bidResponse.Cur

for _, seatBid := range bidResponse.SeatBid {
for _, bid := range seatBid.Bid {
bid := bid

bidType, err := getBidType(bid.MType)

if err != nil {
return nil, []error{err}
}

b := &adapters.TypedBid{
Bid: &bid,
BidType: bidType,
}
bidderResponse.Bids = append(bidderResponse.Bids, b)
}
}

return bidderResponse, nil
}

func getBidType(markupType openrtb2.MarkupType) (openrtb_ext.BidType, error) {
switch markupType {
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 "", fmt.Errorf("unsupported mtype: %d", markupType)
}
}

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)
}

if len(config.ExtraAdapterInfo) > 0 {
isValidEndpoint, err := regexp.Match("([a-z]+)$", []byte(config.ExtraAdapterInfo))
if !isValidEndpoint || err != nil {
return nil, errors.New("ExtraAdapterInfo must be a simple string provided by Roundhouseads")
}
}

urlParams := macros.EndpointTemplateParams{SupplyId: config.ExtraAdapterInfo}
bidderEndpoint, err := macros.ResolveMacros(template, urlParams)

if err != nil {
return nil, fmt.Errorf("unable to resolve endpoint macros: %v", err)
}

return &adapter{
bidderEndpoint: bidderEndpoint,
}, nil
}
Loading

0 comments on commit c3c68cc

Please sign in to comment.