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: Ogury #4082

Merged
merged 14 commits into from
Jan 24, 2025
178 changes: 178 additions & 0 deletions adapters/ogury/ogury.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package ogury

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

"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(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) {
return &adapter{endpoint: config.Endpoint}, nil
}

func (a adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
headers := buildHeaders(request)

var errors []error
var impsWithOguryParams []openrtb2.Imp
for i, imp := range request.Imp {
Copy link
Contributor

Choose a reason for hiding this comment

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

Please consolidate this for loop with the for loop starting at the line 72.

var impExt, impExtBidderHoist map[string]json.RawMessage
// extract ext
if err := jsonutil.Unmarshal(imp.Ext, &impExt); err != nil {
return nil, append(errors, &errortypes.BadInput{
Message: "Bidder extension not provided or can't be unmarshalled",
})
}
// find Ogury bidder params
if bidder, ok := impExt[openrtb_ext.PrebidExtBidderKey]; ok {
if err := jsonutil.Unmarshal(bidder, &impExtBidderHoist); err != nil {
return nil, append(errors, &errortypes.BadInput{
Message: "Ogury bidder extension not provided or can't be unmarshalled",
})
}
}

// extract every value from imp[].ext.bidder to imp[].ext
for key, value := range impExtBidderHoist {
impExt[key] = value
}
delete(impExt, openrtb_ext.PrebidExtBidderKey)

ext, err := jsonutil.Marshal(impExt)
if err != nil {
return nil, append(errors, &errortypes.BadInput{
Message: "Error while marshaling Imp.Ext bidder extension",
})
}
request.Imp[i].Ext = ext

// save adUnitCode
request.Imp[i].TagID = imp.ID

// currency conversion
// Check if imp comes with bid floor amount defined in a foreign currency
if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" {

// Convert to US dollars
convertedValue, err := requestInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD")
if err != nil {
return nil, []error{err}
}

// Update after conversion. All imp elements inside request.Imp are shallow copies
// therefore, their non-pointer values are not shared memory and are safe to modify.
request.Imp[i].BidFloorCur = "USD"
request.Imp[i].BidFloor = convertedValue
}

// check if imp has ogury params and filter it
_, hasAssetKey := impExtBidderHoist["assetKey"]
_, hasAdUnitId := impExtBidderHoist["adUnitId"]
if hasAssetKey && hasAdUnitId {
impsWithOguryParams = append(impsWithOguryParams, request.Imp[i])
}
}

if len(impsWithOguryParams) == 0 && (request.Site == nil || request.Site.Publisher.ID == "") {
return nil, []error{&errortypes.BadInput{
Message: "Invalid request. assetKey/adUnitId or request.site.publisher.id required",
}}
} else if len(impsWithOguryParams) > 0 {
request.Imp = impsWithOguryParams
}

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

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

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

}

func buildHeaders(request *openrtb2.BidRequest) http.Header {
headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
if request.Device != nil {
headers.Add("X-Forwarded-For", request.Device.IP)
krdzo marked this conversation as resolved.
Show resolved Hide resolved
headers.Add("X-Forwarded-For", request.Device.IPv6)
headers.Add("User-Agent", request.Device.UA)
headers.Add("Accept-Language", request.Device.Language)
}
return headers

}

func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {
switch bid.MType {
case openrtb2.MarkupBanner:
return openrtb_ext.BidTypeBanner, nil
case openrtb2.MarkupAudio:
return openrtb_ext.BidTypeAudio, nil
case openrtb2.MarkupNative:
return openrtb_ext.BidTypeNative, nil
case openrtb2.MarkupVideo:
return openrtb_ext.BidTypeVideo, nil
default:
return "", &errortypes.BadServerResponse{
Message: fmt.Sprintf("Unsupported MType \"%d\", for impression \"%s\"", bid.MType, bid.ImpID),
}
}
}
krdzo marked this conversation as resolved.
Show resolved Hide resolved

func (a adapter) MakeBids(request *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 response openrtb2.BidResponse
if err := jsonutil.Unmarshal(responseData.Body, &response); err != nil {
return nil, []error{err}
}

bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
bidResponse.Currency = response.Cur
var errors []error
for _, seatBid := range response.SeatBid {
for i, bid := range seatBid.Bid {
bidType, err := getMediaTypeForBid(bid)
if err != nil {
errors = append(errors, err)
continue
}
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
Bid: &seatBid.Bid[i],
BidType: bidType,
})
}
}
if errors != nil {
return nil, errors
}

return bidResponse, nil
}
21 changes: 21 additions & 0 deletions adapters/ogury/ogury_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ogury

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.BidderOgury, config.Adapter{
Endpoint: "http://ogury.example.com"},
config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})

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

adapterstest.RunJSONBidderTest(t, "ogurytest", bidder)
}
89 changes: 89 additions & 0 deletions adapters/ogury/ogurytest/exemplary/banner_asset_ad_unit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{
"mockBidRequest": {
"id": "test-request-id",
"imp": [
{
"id": "ad-unit-code-as-imp-id",
"banner": {
"format": [{"w": 128, "h": 100}]
},
"ext": {
"gpid": "global position id",
"bidder": {
"assetKey": "OGY",
"adUnitId": "123"
}
}
}
]
},

"httpCalls": [
{
"expectedRequest": {
"uri": "http://ogury.example.com",
"body": {
"id": "test-request-id",
"imp": [
{
"id":"ad-unit-code-as-imp-id",
"tagid": "ad-unit-code-as-imp-id",
"banner": {
"format": [{"w": 128, "h": 100}]
},
"ext": {
"gpid": "global position id",
"assetKey": "OGY",
"adUnitId": "123"
}
}
]
},
"impIDs":["ad-unit-code-as-imp-id"]
},
"mockResponse": {
"status": 200,
"body": {
"id": "test-request-id",
"cur": "USD",
"seatbid": [
{
"seat": "seat",
"bid": [{
"id": "some-UUID",
"impid": "ad-unit-code-as-imp-id",
"price": 0.500000,
"adm": "adm string",
"crid": "crid_10",
"h": 100,
"w": 128,
"mtype": 1
}]
}
]
}
}
}
],

"expectedBidResponses": [
{
"currency": "USD",
"bids": [
{
"bid": {
"id": "some-UUID",
"impid": "ad-unit-code-as-imp-id",
"price": 0.5,
"adm": "adm string",
"crid": "crid_10",
"h": 100,
"w": 128,
"mtype": 1
},
"type": "banner"
}
]
}
]
}
Loading
Loading