-
Notifications
You must be signed in to change notification settings - Fork 760
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: Kobler #3904
New Adapter: Kobler #3904
Changes from 12 commits
9d685e6
5baa4ce
c65ede1
c9a87a6
27d406a
a18ec12
23ebbb6
68a40db
ed3330e
a67d266
1b122fd
e6b4eeb
aba06b9
32483fe
c51b0e5
cf56a4d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
package kobler | ||
|
||
import ( | ||
"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" | ||
"github.com/prebid/prebid-server/v3/util/sliceutil" | ||
) | ||
|
||
type adapter struct { | ||
endpoint string | ||
devEndpoint string | ||
} | ||
|
||
const ( | ||
devBidderEndpoint = "https://bid-service.dev.essrtb.com/bid/prebid_server_rtb_call" | ||
supportedCurrency = "USD" | ||
) | ||
|
||
func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { | ||
bidder := &adapter{ | ||
endpoint: config.Endpoint, | ||
devEndpoint: devBidderEndpoint, | ||
} | ||
|
||
return bidder, nil | ||
} | ||
|
||
func (a adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { | ||
var requestData []*adapters.RequestData | ||
var errors []error | ||
|
||
if !sliceutil.Contains(request.Cur, supportedCurrency) { | ||
request.Cur = append(request.Cur, supportedCurrency) | ||
} | ||
|
||
for i := range request.Imp { | ||
if err := convertImpCurrency(&request.Imp[i], reqInfo); err != nil { | ||
errors = append(errors, err) | ||
return nil, errors | ||
} | ||
} | ||
|
||
requestJSON, err := jsonutil.Marshal(request) | ||
if err != nil { | ||
errors = append(errors, err) | ||
return nil, errors | ||
} | ||
|
||
headers := http.Header{} | ||
headers.Add("Content-Type", "application/json;charset=utf-8") | ||
|
||
requestData = append(requestData, &adapters.RequestData{ | ||
Method: "POST", | ||
Uri: a.getEndpoint(request), | ||
Body: requestJSON, | ||
ImpIDs: openrtb_ext.GetImpIDs(request.Imp), | ||
Headers: headers, | ||
}) | ||
|
||
return requestData, nil | ||
} | ||
|
||
func (a adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { | ||
if responseData.StatusCode == http.StatusNoContent || responseData.Body == nil { | ||
return nil, nil | ||
} | ||
|
||
if responseData.StatusCode != http.StatusOK { | ||
return nil, []error{&errortypes.BadServerResponse{ | ||
Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode), | ||
}} | ||
} | ||
|
||
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 | ||
|
||
for _, seatBid := range response.SeatBid { | ||
for i, bid := range seatBid.Bid { | ||
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ | ||
Bid: &seatBid.Bid[i], | ||
BidType: getMediaTypeForBid(bid), | ||
}) | ||
} | ||
} | ||
|
||
return bidResponse, nil | ||
} | ||
|
||
// Use a separate endpoint for testing purposes in the DEV environment. | ||
// Required due to Kobler's internal test campaign setup. | ||
func (a adapter) getEndpoint(request *openrtb2.BidRequest) string { | ||
if request.Test == 1 { | ||
return a.devEndpoint | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I haven't seen this behavior before. Why do you require a separate endpoint for test requests? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We have a test campaign in our DEV environment that's set to bid on a specific article. For this to work, we have to do some manual manipulation of the article in our system and this is easier to do in the DEV environment. This is the same as for our Prebid.js adapter. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. IMO it's good to add some comments: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @przemkaczmarek I have added this now, thank you! |
||
|
||
return a.endpoint | ||
} | ||
|
||
func getMediaTypeForBid(bid openrtb2.Bid) openrtb_ext.BidType { | ||
TommyHPettersen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if bid.Ext != nil { | ||
var bidExt openrtb_ext.ExtBid | ||
err := jsonutil.Unmarshal(bid.Ext, &bidExt) | ||
if err == nil && bidExt.Prebid != nil { | ||
mediaType, err := openrtb_ext.ParseBidType(string(bidExt.Prebid.Type)) | ||
TommyHPettersen marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if err == nil { | ||
return mediaType | ||
} | ||
} | ||
} | ||
|
||
return openrtb_ext.BidTypeBanner | ||
} | ||
|
||
func convertImpCurrency(imp *openrtb2.Imp, reqInfo *adapters.ExtraRequestInfo) error { | ||
if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != supportedCurrency { | ||
convertedValue, err := reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, supportedCurrency) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
imp.BidFloor = convertedValue | ||
imp.BidFloorCur = supportedCurrency | ||
} | ||
|
||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package kobler | ||
|
||
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.BidderKargo, config.Adapter{ | ||
Endpoint: "http://fake.endpoint"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) | ||
|
||
if buildErr != nil { | ||
t.Fatalf("Builder returned unexpected error %v", buildErr) | ||
} | ||
|
||
adapterstest.RunJSONBidderTest(t, "koblertest", bidder) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
{ | ||
"mockBidRequest": { | ||
"id": "test-request-id", | ||
"device": { | ||
"devicetype": 2 | ||
}, | ||
"site": { | ||
"page": "http://example.com" | ||
}, | ||
"imp": [ | ||
{ | ||
"id": "test-imp-id", | ||
"tagid": "test", | ||
"banner": { | ||
"format": [ | ||
{ | ||
"w": 300, | ||
"h": 250 | ||
}, | ||
{ | ||
"w": 320, | ||
"h": 100 | ||
} | ||
] | ||
} | ||
} | ||
] | ||
}, | ||
"httpCalls": [ | ||
{ | ||
"expectedRequest": { | ||
"uri": "http://fake.endpoint", | ||
"body": { | ||
"id": "test-request-id", | ||
"device": { | ||
"devicetype": 2 | ||
}, | ||
"site": { | ||
"page": "http://example.com" | ||
}, | ||
"imp": [ | ||
{ | ||
"id": "test-imp-id", | ||
"tagid": "test", | ||
"banner": { | ||
"format": [ | ||
{ | ||
"w": 300, | ||
"h": 250 | ||
}, | ||
{ | ||
"w": 320, | ||
"h": 100 | ||
} | ||
] | ||
} | ||
} | ||
], | ||
"cur": ["USD"] | ||
}, | ||
"impIDs": ["test-imp-id"] | ||
}, | ||
"mockResponse": { | ||
"status": 200, | ||
"body": { | ||
"id": "test-request-id", | ||
"seatbid": [ | ||
{ | ||
"bid": [ | ||
{ | ||
"id": "test_bid_id", | ||
"impid": "test-imp-id", | ||
"price": 0.27543, | ||
"adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://example.com\"></iframe>", | ||
"cid": "test_cid", | ||
"crid": "test_crid", | ||
"dealid": "test_dealid", | ||
"w": 300, | ||
"h": 250, | ||
"ext": { | ||
"prebid": { | ||
"type": "banner" | ||
} | ||
} | ||
} | ||
], | ||
"seat": "kobler" | ||
} | ||
], | ||
"cur": "USD" | ||
} | ||
} | ||
} | ||
], | ||
"expectedBidResponses": [ | ||
{ | ||
"bids": [ | ||
{ | ||
"bid": { | ||
"id": "test_bid_id", | ||
"impid": "test-imp-id", | ||
"price": 0.27543, | ||
"adm": "<iframe id=\"adm-banner-16\" width=\"300\" height=\"250\" frameborder=\"0\" marginheight=\"0\" marginwidth=\"0\" style=\"{overflow:hidden}\" src=\"http://example.com\"></iframe>", | ||
"cid": "test_cid", | ||
"crid": "test_crid", | ||
"dealid": "test_dealid", | ||
"w": 300, | ||
"h": 250, | ||
"ext": { | ||
"prebid": { | ||
"type": "banner" | ||
} | ||
} | ||
}, | ||
"type": "banner" | ||
} | ||
] | ||
} | ||
] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
{ | ||
"mockBidRequest": { | ||
"id": "test-request-id", | ||
"device": { | ||
"devicetype": 2 | ||
}, | ||
"site": { | ||
"page": "http://example.com" | ||
}, | ||
"imp": [ | ||
{ | ||
"id": "test-imp-id", | ||
"tagid": "test", | ||
"bidfloor": 1, | ||
"bidfloorcur": "GBP", | ||
"banner": { | ||
"format": [ | ||
{ | ||
"w": 300, | ||
"h": 250 | ||
}, | ||
{ | ||
"w": 320, | ||
"h": 100 | ||
} | ||
] | ||
} | ||
} | ||
], | ||
"ext": { | ||
"prebid": { | ||
"currency": { | ||
"rates": { | ||
"EUR": { | ||
"USD": 1.11 | ||
} | ||
}, | ||
"usepbsrates": false | ||
} | ||
} | ||
} | ||
}, | ||
"httpCalls": [], | ||
"expectedBidResponses": [], | ||
"expectedMakeRequestsErrors": [ | ||
{ | ||
"value": "Currency conversion rate not found: 'GBP' => 'USD'", | ||
"comparison": "literal" | ||
} | ||
] | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We discussed this with the team and decided
request.test
is not the best place to control the URL.Instead we propose to add a test flag to
imp[].ext.prebid.bidder.kobler
orrequest.imp[].ext.kobler
locations (they are equivalent).With this approach this flag will be encapsulated to Kobler adapter only.
To make it work you will need to add an optional property to kobler.json, define imp_kobler.go and handle it in adapter code accordingly.
For the reference, please see missena.json,
ExtImpMissena
and usages ofTestMode
flag.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay thank you, I have now added a similar solution to our server adapter here