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
226 changes: 226 additions & 0 deletions adapters/nexx360/nexx360.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package nexx360

import (
"encoding/json"
"fmt"
"net/http"
"net/url"

"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"
"github.com/prebid/prebid-server/v3/version"
)

type adapter struct {
endpoint string
}

type Ext struct {
Nexx360 json.RawMessage `json:"nexx360"`
}

type Nexx360Caller struct {
Name string `json:"name,omitempty"`
Version string `json:"version,omitempty"`
}

type ReqExt struct {
Nexx360 *ReqNexx360Ext `json:"nexx360,omitempty"`
}

type ReqNexx360Ext struct {
Caller []Nexx360Caller `json:"caller,omitempty"`
}

type Nexx360ResBidExt struct {
BidType string `json:"bidType,omitempty"`
}

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

// CALLER Info used to track Prebid Server
// as one of the hops in the request to exchange

func getVersion() string {
if version.Ver != "" {
return version.Ver
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please update one of your JSON tests to include a non-empty string version.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get how to do this

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops I thought the version was on the incoming request but this is the PBS version. You don't need to add any additional test coverage.

}
return "n/a"
}

var CALLER = Nexx360Caller{"Prebid-Server", getVersion()}

func processImps(impList []openrtb2.Imp) (imp []openrtb2.Imp, tagId string, placement string, error error) {
var imps []openrtb2.Imp
for idx, imp := range impList {
var bidderExt adapters.ExtImpBidder
if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil {
return nil, "", "", &errortypes.BadInput{
Message: err.Error(),
}
}

impExt := Ext{
Nexx360: bidderExt.Bidder,
}

impExtJSON, err := json.Marshal(impExt)
if err != nil {
return nil, "", "", &errortypes.BadInput{
Message: err.Error(),
}
}

impCopy := imp
impCopy.Ext = impExtJSON
imps = append(imps, impCopy)
if idx == 0 {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sorry I was gone for so long. One last ask:

Can we please move the unmarshal of nexx360Ext into the idx == 0 condition? Sorry for the confusion before, I meant the unmarshal to only happen for the first element of the Imp array as those nexx360Ext and nexx360Ext.Placement are only passed to tagId and placement when idx == 0 saving us a bunch of unmarshals when idx equals 1, 2, 3, and so on.

59   func processImps(impList []openrtb2.Imp) (imp []openrtb2.Imp, tagId string, placement string, error error) {
60       var imps []openrtb2.Imp
61       for idx, imp := range impList {
62           var bidderExt adapters.ExtImpBidder
63           if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil {
64               return nil, "", "", &errortypes.BadInput{
65                   Message: err.Error(),
66               }
67           }
68  
69 -         var nexx360Ext openrtb_ext.ExtImpNexx360
70 -         if err := jsonutil.Unmarshal(bidderExt.Bidder, &nexx360Ext); err != nil {
71 -             return nil, "", "", &errortypes.BadInput{
72 -                 Message: err.Error(),
73 -             }
74 -         }
75  
76           impExt := Ext{
77               Nexx360: bidderExt.Bidder,
78           }
79  
80           impExtJSON, err := json.Marshal(impExt)
81           if err != nil {
82               return nil, "", "", &errortypes.BadInput{
83                   Message: err.Error(),
84               }
85           }
86  
87           impCopy := imp
88           impCopy.Ext = impExtJSON
89           imps = append(imps, impCopy)
90           if idx == 0 {
   +             var nexx360Ext openrtb_ext.ExtImpNexx360
   +             if err := jsonutil.Unmarshal(bidderExt.Bidder, &nexx360Ext); err != nil {
   +                 return nil, "", "", &errortypes.BadInput{
   +                     Message: err.Error(),
   +                 }
   +             }
91               tagId = nexx360Ext.TagId
92               placement = nexx360Ext.Placement
93           }
94       }
95  
96       return imps, tagId, placement, nil
97   }
adapters/nexx360/nexx360.go

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@guscarreon done :)
@guscarreon @bsardo , can you please review? Thanks!

var nexx360Ext openrtb_ext.ExtImpNexx360
if err := jsonutil.Unmarshal(bidderExt.Bidder, &nexx360Ext); err != nil {
return nil, "", "", &errortypes.BadInput{
Message: err.Error(),
}
}
Comment on lines +84 to +89
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think these lines can be deleted. You're already unmarshaling bidderExt.Bidder into an instance of openrtb_ext.ExtImpNexx360 called nexx360Ext above on line 69 and you will only make it this far if that unmarshal operation succeeded.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

tagId = nexx360Ext.TagId
placement = nexx360Ext.Placement
}
}

return imps, tagId, placement, nil
}

func makeReqExt() ([]byte, error) {
reqExt := ReqExt{
Nexx360: &ReqNexx360Ext{
Caller: []Nexx360Caller{CALLER},
},
}

return json.Marshal(reqExt)
}

func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
var imp, tagId, placement, err = processImps(request.Imp)
if err != nil {
return nil, []error{err}
}

request.Imp = imp

urlBuilder, err := url.Parse(a.endpoint)
if err != nil {
return nil, []error{err}
}

query := url.Values{}

if placement != "" {
query.Add("placement", placement)
}

if tagId != "" {
query.Add("tag_id", tagId)
}
urlBuilder.RawQuery = query.Encode()

uri := urlBuilder.String()

reqExt, err := makeReqExt()
if err != nil {
return nil, []error{err}
}
request.Ext = reqExt

// Last Step
reqJSON, err := json.Marshal(request)
if err != nil {
return nil, []error{err}
}

headers := http.Header{}
headers.Add("Content-Type", "application/json")

adapter := &adapters.RequestData{
Method: http.MethodPost,
Uri: uri,
Body: reqJSON,
Headers: headers,
ImpIDs: openrtb_ext.GetImpIDs(request.Imp),
}

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

// MakeBids make the bids for the bid response.
func (a *adapter) MakeBids(request *openrtb2.BidRequest, externalRequest *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{&errortypes.BadInput{
Message: fmt.Sprintf("Unexpected http status code: %d", responseData.StatusCode),
}}
}

var response openrtb2.BidResponse

if err := jsonutil.Unmarshal(responseData.Body, &response); err != nil {
return nil, []error{err}
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a supplemental/invalid-bid-response.json test to cover this error block.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}

var Bids []*adapters.TypedBid
var errors []error
for _, seatBid := range response.SeatBid {
for i, bid := range seatBid.Bid {

bidType, err := getBidType(bid)
if err != nil {
errors = append(errors, err)
continue
}
Bids = append(Bids, &adapters.TypedBid{
Bid: &seatBid.Bid[i],
BidType: bidType,
})
}
}
if len(Bids) == 0 {
return nil, nil
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason you are entirely discarding the response from adapter and returning nil, instead of returning response itselt?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kept as it causes issues with tests.

}
bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(Bids))
bidResponse.Bids = Bids
if response.Cur != "" {
bidResponse.Currency = response.Cur
}

return bidResponse, errors
}

func getBidType(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
var bidExt Nexx360ResBidExt
err := jsonutil.Unmarshal(bid.Ext, &bidExt)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why bid. MType is not being used?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those are our internal processes based on Open RTB 2.5, might be further updated

if err == nil {
if bidExt.BidType == "video" {
return openrtb_ext.BidTypeVideo, nil
}
if bidExt.BidType == "audio" {
return openrtb_ext.BidTypeAudio, nil
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add an exemplary/simple-audio.json test case as you've declared support for audio in your yaml file.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}
if bidExt.BidType == "native" {
return openrtb_ext.BidTypeNative, nil
}
if bidExt.BidType == "banner" {
return openrtb_ext.BidTypeBanner, nil
}
}
return "", &errortypes.BadServerResponse{
Message: fmt.Sprintf("unable to fetch mediaType in multi-format: %s", bid.ImpID),
}
}
20 changes: 20 additions & 0 deletions adapters/nexx360/nexx360_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package nexx360

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

func TestJsonSamples(t *testing.T) {
bidder, buildErr := Builder(openrtb_ext.BidderNexx360, config.Adapter{
Endpoint: "http://fast.nexx360.io/prebid-server"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})

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

adapterstest.RunJSONBidderTest(t, "nexx360test", bidder)
}
Loading
Loading