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: MeloZen #3784

Merged
merged 8 commits into from
Aug 12, 2024
185 changes: 185 additions & 0 deletions adapters/melozen/melozen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package melozen

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

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

type adapter struct {
endpointTemplate *template.Template
}

// Builder builds a new instance of the MeloZen adapter for the given bidder with the given config.
func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
template, err := template.New("endpointTemplate").Parse(config.Endpoint)
if err != nil {
return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
}

bidder := &adapter{
endpointTemplate: template,
}

return bidder, nil
}

func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
var requests []*adapters.RequestData
var errors []error
headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")

requestCopy := *request
for _, imp := range request.Imp {
// Extract Melozen Params
var strImpExt adapters.ExtImpBidder
if err := json.Unmarshal(imp.Ext, &strImpExt); err != nil {
errors = append(errors, err)
continue
}
var strImpParams openrtb_ext.ImpExtMeloZen
if err := json.Unmarshal(strImpExt.Bidder, &strImpParams); err != nil {
errors = append(errors, err)
continue
}

url, err := macros.ResolveMacros(a.endpointTemplate, macros.EndpointTemplateParams{PublisherID: strImpParams.PubId})
if err != nil {
errors = append(errors, err)
continue
}
// Convert Floor into USD
if imp.BidFloor > 0 && imp.BidFloorCur != "" && !strings.EqualFold(imp.BidFloorCur, "USD") {
convertedValue, err := reqInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD")
if err != nil {
errors = append(errors, err)
continue
}
imp.BidFloorCur = "USD"
imp.BidFloor = convertedValue
}

impressionsByMediaType, err := splitImpressionsByMediaType(&imp)
if err != nil {
errors = append(errors, err)
continue
}

for _, impression := range impressionsByMediaType {
requestCopy.Imp = []openrtb2.Imp{impression}

requestJSON, err := json.Marshal(requestCopy)
if err != nil {
errors = append(errors, err)
continue
}

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

return requests, errors
}

func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
if adapters.IsResponseStatusCodeNoContent(response) {
return nil, nil
}

if err := adapters.CheckResponseStatusCodeForErrors(response); err != nil {
return nil, []error{err}
}

var bidReq openrtb2.BidRequest
if err := json.Unmarshal(requestData.Body, &bidReq); err != nil {
return nil, []error{err}
}

var bidResp openrtb2.BidResponse
if err := json.Unmarshal(response.Body, &bidResp); err != nil {
return nil, []error{err}
}

bidderResponse := adapters.NewBidderResponse()
var errors []error
for _, seatBid := range bidResp.SeatBid {
for i := range seatBid.Bid {
bid := &seatBid.Bid[i]
bidType, err := getMediaTypeForBid(*bid)
if err != nil {
errors = append(errors, err)
continue
}

bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
BidType: bidType,
Bid: bid,
})
}
}
return bidderResponse, errors
}

func splitImpressionsByMediaType(impression *openrtb2.Imp) ([]openrtb2.Imp, error) {
if impression.Banner == nil && impression.Native == nil && impression.Video == nil {
return nil, &errortypes.BadInput{Message: "Invalid MediaType. MeloZen only supports Banner, Video and Native."}
onkarvhanumante marked this conversation as resolved.
Show resolved Hide resolved
}
Comment on lines +142 to +144
Copy link
Contributor

Choose a reason for hiding this comment

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

as per static/bidder-info/melozen.yaml video media type is not supported for app.

should update bidder info yaml to support video media type or remove video check

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for your feedback. I have updated the bidder-info/melozen.yaml file to reflect the correct media type support as suggested. The changes ensure that the video media type is appropriately handled. The updated configuration has been pushed to the PR. Please review the changes at your convenience. Thank you for guiding the improvements.


impressions := make([]openrtb2.Imp, 0, 2)

if impression.Banner != nil {
impCopy := *impression
impCopy.Video = nil
impCopy.Native = nil
impressions = append(impressions, impCopy)
}

if impression.Video != nil {
impCopy := *impression
impCopy.Banner = nil
impCopy.Native = nil
impressions = append(impressions, impCopy)
}
Comment on lines +155 to +160
Copy link
Contributor

Choose a reason for hiding this comment

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

as per static/bidder-info/melozen.yaml video media type is not supported for app.

should update bidder info yaml to support video media type or remove video check

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for pointing out the need to align our code with the supported media types defined in bidder-info/melozen.yaml. I have updated the handling of media types within our adapter to ensure consistency with our declared capabilities. The changes have been made in the latest commit. Please review the updates and let me know if further adjustments are required.


if impression.Native != nil {
impCopy := *impression
impCopy.Banner = nil
impCopy.Video = nil
impressions = append(impressions, impCopy)
}

return impressions, nil
}

func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) {

if bid.Ext != nil {
var bidExt openrtb_ext.ExtBid
err := json.Unmarshal(bid.Ext, &bidExt)
if err == nil && bidExt.Prebid != nil {
return openrtb_ext.ParseBidType(string(bidExt.Prebid.Type))
onkarvhanumante marked this conversation as resolved.
Show resolved Hide resolved
onkarvhanumante marked this conversation as resolved.
Show resolved Hide resolved
}
}

return "", &errortypes.BadServerResponse{
Message: fmt.Sprintf("Failed to parse bid mediatype for impression \"%s\"", bid.ImpID),
}
onkarvhanumante marked this conversation as resolved.
Show resolved Hide resolved
}
30 changes: 30 additions & 0 deletions adapters/melozen/melozen_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package melozen

import (
"testing"

"github.com/prebid/prebid-server/v2/adapters/adapterstest"
"github.com/prebid/prebid-server/v2/config"
"github.com/prebid/prebid-server/v2/openrtb_ext"
"github.com/stretchr/testify/assert"
)

func TestJsonSamples(t *testing.T) {

bidder, buildErr := Builder(openrtb_ext.BidderMeloZen, config.Adapter{
Endpoint: "https://example.com/rtb/v2/bid?publisher_id={{.PublisherID}}",
}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})

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

adapterstest.RunJSONBidderTest(t, "melozentest", bidder)
}

func TestEndpointTemplateMalformed(t *testing.T) {
_, buildErr := Builder(openrtb_ext.BidderMeloZen, config.Adapter{
Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})

assert.Error(t, buildErr)
}
132 changes: 132 additions & 0 deletions adapters/melozen/melozentest/exemplary/app-banner.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
{
"mockBidRequest": {
"id": "web-banner",
"tmax": 3000,
"imp": [
{
"id": "banner-imp-id",
"ext": {
"bidder": {
"pubId": "386276e072"
}
},
"banner": {
"format": [
{
"w": 300,
"h": 250
}
]
}
}
],
"app": {
"bundle": "com.fake.app",
"publisher": {
"id": "42",
"name": "whatever.pub"
}
},
"device": {
"w": 1200,
"h": 900
}
},
"httpCalls": [
{
"expectedRequest": {
"uri": "https://example.com/rtb/v2/bid?publisher_id=386276e072",
"headers": {
"Content-Type": ["application/json;charset=utf-8"],
"Accept": ["application/json"]
},
"body": {
"id": "web-banner",
"tmax": 3000,
"imp": [
{
"id": "banner-imp-id",
"ext": {
"bidder": {
"pubId": "386276e072"
}
},
"banner": {
"format": [
{
"w": 300,
"h": 250
}
]
}
}
],
"device": {
"w": 1200,
"h": 900
},
"app": {
"bundle": "com.fake.app",
"publisher": {
"id": "42",
"name": "whatever.pub"
}
}
},
"impIDs":["banner-imp-id"]
},
"mockResponse": {
"status": 200,
"body": {
"id": "web-banner",
"cur": "USD",
"seatbid": [
{
"bid": [
{
"id": "web-banner",
"impid": "banner-imp-id",
"crid": "some-creative-id",
"adm": "<div>Ad</div>",
"price": 20,
"w": 300,
"h": 250,
"ext": {
"prebid": {
"type": "banner"
}
}
}
]
}
]
}
}
}
],
"expectedBidResponses": [
{
"currency": "USD",
"bids": [
{
"bid": {
"id": "web-banner",
"impid": "banner-imp-id",
"crid": "some-creative-id",
"adm": "<div>Ad</div>",
"price": 20,
"w": 300,
"h": 250,
"ext": {
"prebid": {
"type": "banner"
}
}
},
"type": "banner"
}
]
}
]
}

Loading
Loading