Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Adapter: Displayio #3691

Merged
merged 10 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
207 changes: 207 additions & 0 deletions adapters/displayio/displayio.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package displayio

import (
"encoding/json"
"errors"
"fmt"
"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"
"net/http"
"text/template"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nitpick: I suggest organizing your imports so all standard packages are listed first, in which case you would move these after "fmt" and before all of the github imports.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Imports reorganized

)

type DisplayioAdapter struct {
endpoint *template.Template
}

type reqDioExt struct {
UserSession string `json:"userSession,omitempty"`
PlacementId string `json:"placementId"`
InventoryId string `json:"inventoryId"`
}

func (adapter *DisplayioAdapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")
headers.Add("x-openrtb-version", "2.5")

var requestExt map[string]interface{}
var dioExt reqDioExt

if len(request.Imp) == 0 {
return nil, []error{&errortypes.BadInput{Message: "No impression in the bid request"}}
}
onkarvhanumante marked this conversation as resolved.
Show resolved Hide resolved

impressions := request.Imp
result := make([]*adapters.RequestData, 0, len(impressions))
errs := make([]error, 0, len(impressions))

for i, impression := range impressions {
if impression.BidFloor == 0 {
errs = append(errs, &errortypes.BadInput{
Message: "BidFloor should be defined",
})
continue
}

if impression.BidFloorCur == "" {
impression.BidFloorCur = "USD"
}

if impression.BidFloorCur != "USD" {
convertedValue, err := requestInfo.ConvertCurrency(impression.BidFloor, impression.BidFloorCur, "USD")

if err != nil {
errs = append(errs, err)
continue
}

impression.BidFloorCur = "USD"
impression.BidFloor = convertedValue
}

if len(impression.Ext) == 0 {
errs = append(errs, errors.New("impression extensions required"))
continue
}

var bidderExt adapters.ExtImpBidder
err := json.Unmarshal(impression.Ext, &bidderExt)

if err != nil {
errs = append(errs, err)
continue
}
if len(bidderExt.Bidder) == 0 {
errs = append(errs, errors.New("bidder required"))
continue
}
Copy link
Contributor

Choose a reason for hiding this comment

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

no need to add this check. Prebid core code will make sure Bidder value is present

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok, removed

var impressionExt openrtb_ext.ExtImpDisplayio
err = json.Unmarshal(bidderExt.Bidder, &impressionExt)
if err != nil {
errs = append(errs, err)
continue
}

if i == 0 {
dioExt = reqDioExt{PlacementId: impressionExt.PlacementId, InventoryId: impressionExt.InventoryId}

err = json.Unmarshal(request.Ext, &requestExt)
if err != nil {
requestExt = make(map[string]interface{})
}

requestExt["displayio"] = dioExt

request.Ext, err = json.Marshal(requestExt)
if err != nil {
errs = append(errs, err)
continue
}
}
onkarvhanumante marked this conversation as resolved.
Show resolved Hide resolved

request.Imp = []openrtb2.Imp{impression}
body, err := json.Marshal(request)
if err != nil {
errs = append(errs, err)
continue
}

url, err := adapter.buildEndpointURL(&impressionExt)
if err != nil {
return nil, []error{err}
}

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

request.Imp = impressions

if len(result) == 0 {
return nil, errs
}
return result, errs
}

// MakeBids translates Displayio bid response to prebid-server specific format
func (adapter *DisplayioAdapter) MakeBids(internalRequest *openrtb2.BidRequest, _ *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 bidResp openrtb2.BidResponse

if err := json.Unmarshal(responseData.Body, &bidResp); err != nil {
msg := fmt.Sprintf("Bad server response: %d", err)
return nil, []error{&errortypes.BadServerResponse{Message: msg}}
}

if len(bidResp.SeatBid) != 1 {
msg := fmt.Sprintf("Invalid SeatBids count: %d", len(bidResp.SeatBid))
return nil, []error{&errortypes.BadServerResponse{Message: msg}}
}

var errs []error
bidResponse := adapters.NewBidderResponse()

for _, sb := range bidResp.SeatBid {
for i := range sb.Bid {
bidType, err := getBidMediaTypeFromMtype(&sb.Bid[i])
if err != nil {
errs = append(errs, err)
} else {
b := &adapters.TypedBid{
Bid: &sb.Bid[i],
BidType: bidType,
}
bidResponse.Bids = append(bidResponse.Bids, b)
}
}
}

return bidResponse, errs
}

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

bidder := &DisplayioAdapter{
onkarvhanumante marked this conversation as resolved.
Show resolved Hide resolved
endpoint: endpoint,
}
return bidder, nil
}

func getBidMediaTypeFromMtype(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
default:
return "", fmt.Errorf("unexpected media type for bid: %s", bid.ImpID)
}
}

func (adapter *DisplayioAdapter) buildEndpointURL(params *openrtb_ext.ExtImpDisplayio) (string, error) {
endpointParams := macros.EndpointTemplateParams{PublisherID: params.PublisherId}
return macros.ResolveMacros(adapter.endpoint, endpointParams)
}
23 changes: 23 additions & 0 deletions adapters/displayio/displayio_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package displayio

import (
"testing"

"github.com/prebid/prebid-server/v2/config"
"github.com/prebid/prebid-server/v2/openrtb_ext"

"github.com/prebid/prebid-server/v2/adapters/adapterstest"
)

func TestJsonSamples(t *testing.T) {
bidder, buildErr := Builder(openrtb_ext.BidderDisplayio,
config.Adapter{Endpoint: "https://101.prebid.display.io"},
config.Server{ExternalUrl: "https://101.prebid.display.io"},
)

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

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