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

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

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) getRequestExtImpBeop(imp *openrtb2.Imp) (*openrtb_ext.ExtImpBeop, error) {
var bidderExt adapters.ExtImpBidder
if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil {
return nil, &errortypes.BadInput{
Message: "ext.bidder not provided",
}
}
var beopExt openrtb_ext.ExtImpBeop
if err := jsonutil.Unmarshal(bidderExt.Bidder, &beopExt); err != nil {
return nil, &errortypes.BadInput{
Message: "ext.bidder not provided",
}
}
if beopExt.BeopPublisherID == "" && beopExt.BeopNetworkID == "" {
return nil, &errortypes.BadInput{
Message: "Missing pid or nid parameters",
}
}
return &beopExt, nil
}

func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtImpBeop) (string, error) {
url, err := url.Parse(a.endpoint)
if err != nil {
return "", &errortypes.Warning{
Message: "Failed to parse endpoint",
}
}
query := url.Query()
if pid := params.BeopPublisherID; len(pid) != 0 {
query.Set("pid", pid)
}
if nid := params.BeopNetworkID; len(nid) != 0 {
query.Set("nid", nid)
}
if nptnid := params.BeopNetworkPartnerID; len(nptnid) != 0 {
query.Set("nptnid", nptnid)
}
url.RawQuery = query.Encode()
return url.String(), nil
}

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

beopExt, err = a.getRequestExtImpBeop(&request.Imp[0])
if err != nil {
return nil, []error{err}
}

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

headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")

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

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

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

func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
if bid.Ext != nil {
var bidExt openrtb_ext.ExtBid
err := jsonutil.Unmarshal(bid.Ext, &bidExt)
if err == nil && bidExt.Prebid != nil {
return openrtb_ext.ParseBidType(string(bidExt.Prebid.Type))

Choose a reason for hiding this comment

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

Consider this as a suggestion. Prebid server expects the media type to be explicitly set in the adapter response. Therefore, recommends implementing a pattern where the adapter server sets the MType field in the response to accurately determine the media type for the impression.

Choose a reason for hiding this comment

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

Consider this as a suggestion. Prebid server expects the media type to be explicitly set in the adapter response. Therefore, recommends implementing a pattern where the adapter server sets the MType field in the response to accurately determine the media type for the impression.

Copy link

Choose a reason for hiding this comment

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

Consider this as a suggestion. Prebid server expects the media type to be explicitly set in the adapter response. Therefore, recommends implementing a pattern where the adapter server sets the MType field in the response to accurately determine the media type for the impression.

Copy link

Choose a reason for hiding this comment

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

Consider this as a suggestion. Prebid server expects the media type to be explicitly set in the adapter response. Therefore, recommends implementing a pattern where the adapter server sets the MType field in the response to accurately determine the media type for the impression.

Copy link

Choose a reason for hiding this comment

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

Consider this as a suggestion. Prebid server expects the media type to be explicitly set in the adapter response. Therefore, recommends implementing a pattern where the adapter server sets the MType field in the response to accurately determine the media type for the impression.

Choose a reason for hiding this comment

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

Consider this as a suggestion. Prebid server expects the media type to be explicitly set in the adapter response. Therefore, recommends implementing a pattern where the adapter server sets the MType field in the response to accurately determine the media type for the impression.

Choose a reason for hiding this comment

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

Consider this as a suggestion. Prebid server expects the media type to be explicitly set in the adapter response. Therefore, recommends implementing a pattern where the adapter server sets the MType field in the response to accurately determine the media type for the impression.

}
}

return "", &errortypes.BadServerResponse{
Message: fmt.Sprintf("Failed to parse impression \"%s\" mediatype", bid.ImpID),
}
}

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.StatusServiceUnavailable {
return nil, []error{&errortypes.BadInput{
Message: fmt.Sprintf("Service Unavailable. Status Code: [ %d ] ", responseData.StatusCode),
}}
}

if responseData.StatusCode == http.StatusBadRequest {
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 responseBody openrtb2.BidResponse
if err := jsonutil.Unmarshal(responseData.Body, &responseBody); err != nil {
return nil, []error{&errortypes.BadServerResponse{
Message: "Bad Server Response",
}}
}

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

bidResponseFinal := adapters.NewBidderResponseWithBidsCapacity(len(responseBody.SeatBid[0].Bid))
seatBid := responseBody.SeatBid[0]
var errors []error
for _, bid := range seatBid.Bid {
bidType, err := getMediaTypeForBid(bid)
if err != nil {
errors = append(errors, err)
continue
}
bidResponseFinal.Bids = append(bidResponseFinal.Bids, &adapters.TypedBid{
Bid: &bid,

Choose a reason for hiding this comment

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

Found incorrect assignment made to Bid. bid variable receives a new value in each iteration of range loop. Assigning the address of bid (&bid) to Bid will result in a pointer that always points to the same memory address with the value of the last iteration. This can lead to unexpected behavior or incorrect results. Refer https://go.dev/play/p/9ZS1f-5h4qS
Consider using an index variable in the seatBids.Bid loop as shown below

  for _, seatBid := range response.SeatBid {
    for i := range seatBids.Bid {
      ...
      responseBid := &adapters.TypedBid{
        Bid: &seatBids.Bid[i],
        ...
      }
      ...
      ...
    }
  }

Choose a reason for hiding this comment

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

Found incorrect assignment made to Bid. bid variable receives a new value in each iteration of range loop. Assigning the address of bid (&bid) to Bid will result in a pointer that always points to the same memory address with the value of the last iteration. This can lead to unexpected behavior or incorrect results. Refer https://go.dev/play/p/9ZS1f-5h4qS
Consider using an index variable in the seatBids.Bid loop as shown below

  for _, seatBid := range response.SeatBid {
    for i := range seatBids.Bid {
      ...
      responseBid := &adapters.TypedBid{
        Bid: &seatBids.Bid[i],
        ...
      }
      ...
      ...
    }
  }

Copy link

Choose a reason for hiding this comment

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

Found incorrect assignment made to Bid. bid variable receives a new value in each iteration of range loop. Assigning the address of bid (&bid) to Bid will result in a pointer that always points to the same memory address with the value of the last iteration. This can lead to unexpected behavior or incorrect results. Refer https://go.dev/play/p/9ZS1f-5h4qS
Consider using an index variable in the seatBids.Bid loop as shown below

  for _, seatBid := range response.SeatBid {
    for i := range seatBids.Bid {
      ...
      responseBid := &adapters.TypedBid{
        Bid: &seatBids.Bid[i],
        ...
      }
      ...
      ...
    }
  }

Copy link

Choose a reason for hiding this comment

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

Found incorrect assignment made to Bid. bid variable receives a new value in each iteration of range loop. Assigning the address of bid (&bid) to Bid will result in a pointer that always points to the same memory address with the value of the last iteration. This can lead to unexpected behavior or incorrect results. Refer https://go.dev/play/p/9ZS1f-5h4qS
Consider using an index variable in the seatBids.Bid loop as shown below

  for _, seatBid := range response.SeatBid {
    for i := range seatBids.Bid {
      ...
      responseBid := &adapters.TypedBid{
        Bid: &seatBids.Bid[i],
        ...
      }
      ...
      ...
    }
  }

Copy link

Choose a reason for hiding this comment

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

Found incorrect assignment made to Bid. bid variable receives a new value in each iteration of range loop. Assigning the address of bid (&bid) to Bid will result in a pointer that always points to the same memory address with the value of the last iteration. This can lead to unexpected behavior or incorrect results. Refer https://go.dev/play/p/9ZS1f-5h4qS
Consider using an index variable in the seatBids.Bid loop as shown below

  for _, seatBid := range response.SeatBid {
    for i := range seatBids.Bid {
      ...
      responseBid := &adapters.TypedBid{
        Bid: &seatBids.Bid[i],
        ...
      }
      ...
      ...
    }
  }

Choose a reason for hiding this comment

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

Found incorrect assignment made to Bid. bid variable receives a new value in each iteration of range loop. Assigning the address of bid (&bid) to Bid will result in a pointer that always points to the same memory address with the value of the last iteration. This can lead to unexpected behavior or incorrect results. Refer https://go.dev/play/p/9ZS1f-5h4qS
Consider using an index variable in the seatBids.Bid loop as shown below

  for _, seatBid := range response.SeatBid {
    for i := range seatBids.Bid {
      ...
      responseBid := &adapters.TypedBid{
        Bid: &seatBids.Bid[i],
        ...
      }
      ...
      ...
    }
  }

Choose a reason for hiding this comment

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

Found incorrect assignment made to Bid. bid variable receives a new value in each iteration of range loop. Assigning the address of bid (&bid) to Bid will result in a pointer that always points to the same memory address with the value of the last iteration. This can lead to unexpected behavior or incorrect results. Refer https://go.dev/play/p/9ZS1f-5h4qS
Consider using an index variable in the seatBids.Bid loop as shown below

  for _, seatBid := range response.SeatBid {
    for i := range seatBids.Bid {
      ...
      responseBid := &adapters.TypedBid{
        Bid: &seatBids.Bid[i],
        ...
      }
      ...
      ...
    }
  }

BidType: bidType,
})
}
return bidResponseFinal, errors
}
21 changes: 21 additions & 0 deletions adapters/beop/beop_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package beop

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.BidderBeop, config.Adapter{
Endpoint: "http://hb-test.collectiveaudience.co/rtb/bid"},
config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})

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

adapterstest.RunJSONBidderTest(t, "beoptest", bidder)
}
153 changes: 153 additions & 0 deletions adapters/beop/beoptest/exemplary/banner-web-with-nid.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
{
"mockBidRequest": {
"id": "request-id",
"site": {
"page": "test.com/page",
"domain": "test.com",
"cat": [
"IAB9-1"
],
"publisher": {
"id": "123456789"
}
},
"device": {
"ua": "useragent",
"ip": "100.100.100.100",
"language": "en"
},
"tmax": 1000,
"user": {
"id": "some-user"
},
"imp": [
{
"id": "impression-id",
"tagid": "tid",
"banner": {
"w": 320,
"h": 50
},
"ext": {
"bidder": {
"nid": "aaaaaaaaaaaaaaaaaaaaaaaa",
"nptnid": "1234"
}
}
}
]
},
"httpCalls": [
{
"expectedRequest": {
"method": "POST",
"uri": "http://hb-test.collectiveaudience.co/rtb/bid?nid=aaaaaaaaaaaaaaaaaaaaaaaa&nptnid=1234",
"headers": {
"Content-Type": [
"application/json;charset=utf-8"
],
"Accept": [
"application/json"
]
},
"body": {
"id": "request-id",
"device": {
"ua": "useragent",
"ip": "100.100.100.100",
"language": "en"
},
"imp": [
{
"id": "impression-id",
"banner": {
"w": 320,
"h": 50
},
"tagid": "tid",
"ext": {
"bidder": {
"nid": "aaaaaaaaaaaaaaaaaaaaaaaa",
"nptnid": "1234"
}
}
}
],
"site": {
"page": "test.com/page",
"domain": "test.com",
"cat": [
"IAB9-1"
],
"publisher": {
"id": "123456789"
}
},
"user": {
"id": "some-user"
},
"tmax": 1000
},
"impIDs": [
"impression-id"
]
},
"mockResponse": {
"status": 200,
"body": {
"id": "resp-id",
"seatbid": [
{
"bid": [
{
"id": "123456789",
"impid": "impression-id",
"price": 2,
"adm": "adm code",
"adomain": [
"testdomain.com"
],
"crid": "100",
"w": 320,
"h": 50,
"ext": {
"prebid": {
"type": "banner"
}
}
}
]
}
],
"cur": "USD"
}
}
}
],
"expectedBidResponses": [
{
"bids": [
{
"bid": {
"id": "123456789",
"impid": "impression-id",
"price": 2,
"adm": "adm code",
"adomain": [
"testdomain.com"
],
"crid": "100",
"w": 320,
"h": 50,
"ext": {
"prebid": {
"type": "banner"
}
}
},
"type": "banner"
}
]
}
]
}
Loading
Loading