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: Missena #3761

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
215 changes: 215 additions & 0 deletions adapters/missena/missena.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package missena

import (
"encoding/json"
"fmt"
"net/http"
"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/openrtb_ext"
)

type adapter struct {
endpoint string
}

type MissenaAdRequest struct {
RequestId string `json:"request_id"`
Timeout int `json:"timeout"`
Referer string `json:"referer"`
RefererCanonical string `json:"referer_canonical"`
GDPRConsent string `json:"consent_string"`
GDPR bool `json:"consent_required"`
Placement string `json:"placement"`
TestMode string `json:"test"`
}

type MissenaBidServerResponse struct {
Ad string `json:"ad"`
Cpm float64 `json:"cpm"`
Currency string `json:"currency"`
RequestId string `json:"requestId"`
}

type MissenaInternalParams struct {
ApiKey string
RequestId string
Timeout int
Referer string
RefererCanonical string
GDPRConsent string
GDPR bool
Placement string
TestMode string
}

type MissenaAdapter struct {
EndpointTemplate *template.Template
}

// Builder builds a new instance of the Foo adapter for the given bidder with the given config.
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) makeRequest(missenaParams MissenaInternalParams, reqInfo *adapters.ExtraRequestInfo, impID string, request *openrtb2.BidRequest) (*adapters.RequestData, error) {
url := a.endpoint + "?t=" + missenaParams.ApiKey

missenaRequest := MissenaAdRequest{
RequestId: request.ID,
Timeout: 2000,
Referer: request.Site.Page,
RefererCanonical: request.Site.Domain,
GDPRConsent: missenaParams.GDPRConsent,
GDPR: missenaParams.GDPR,
Placement: missenaParams.Placement,
TestMode: missenaParams.TestMode,
}

body, errm := json.Marshal(missenaRequest)
if errm != nil {
return nil, errm
}

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

if request.Device != nil {
headers.Add("User-Agent", request.Device.UA)
if request.Device.IP != "" {
headers.Add("X-Forwarded-For", request.Device.IP)
} else if request.Device.IPv6 != "" {
headers.Add("X-Forwarded-For", request.Device.IPv6)
Copy link
Collaborator

Choose a reason for hiding this comment

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

add test for it.
image

Copy link
Author

Choose a reason for hiding this comment

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

Done ✅

}
}
if request.Site != nil {
headers.Add("Referer", request.Site.Page)
}

return &adapters.RequestData{
Method: "POST",
Uri: url,
Headers: headers,
Body: body,
ImpIDs: []string{impID},
}, nil
}

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

var httpRequests []*adapters.RequestData
var errors []error
gdprApplies, consentString := readGDPR(request)

missenaInternalParams := MissenaInternalParams{
GDPR: gdprApplies,
GDPRConsent: consentString,
}

for _, imp := range request.Imp {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It appears you are prepared to handle multiple imps but really you only care that at least one of them is valid as you're just preparing a single request without specifying the impression ID, though you are grabbing the params from the last imp you process. Is this what you want or do you want to grab the info from the first imp?

I suggest adding a test where there are multiple imps with different params, specifically placement. I also think you should add a test where one imp results in an error and the other is well formed.

Copy link
Author

Choose a reason for hiding this comment

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

Done ✅

var bidderExt adapters.ExtImpBidder
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
errors = append(errors, &errortypes.BadInput{
Message: "Error parsing bidderExt object",
})
continue
}

var missenaExt openrtb_ext.ExtImpMissena
if err := json.Unmarshal(bidderExt.Bidder, &missenaExt); err != nil {
errors = append(errors, &errortypes.BadInput{
Message: "Error parsing missenaExt parameters",
})
continue
}

missenaInternalParams.ApiKey = missenaExt.ApiKey
missenaInternalParams.Placement = missenaExt.Placement
missenaInternalParams.TestMode = missenaExt.TestMode

newHttpRequest, err := a.makeRequest(missenaInternalParams, requestInfo, imp.ID, request)
if err != nil {
errors = append(errors, err)
continue
}

httpRequests = append(httpRequests, newHttpRequest)

break
}

return httpRequests, errors
}

func readGDPR(request *openrtb2.BidRequest) (bool, string) {
consentString := ""
if request.User != nil {
var extUser openrtb_ext.ExtUser
if err := json.Unmarshal(request.User.Ext, &extUser); err == nil {
consentString = extUser.Consent
}
}
gdprApplies := false
var extRegs openrtb_ext.ExtRegs
if request.Regs != nil {
if err := json.Unmarshal(request.Regs.Ext, &extRegs); err == nil {
if extRegs.GDPR != nil {
gdprApplies = (*extRegs.GDPR == 1)
}
}
}
return gdprApplies, consentString
}

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.StatusBadRequest {
err := &errortypes.BadInput{
Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
}
return nil, []error{err}
}

if responseData.StatusCode != http.StatusOK {
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 missenaResponse MissenaBidServerResponse
if err := json.Unmarshal(responseData.Body, &missenaResponse); err != nil {
return nil, []error{err}
}

bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
bidResponse.Currency = missenaResponse.Currency

responseBid := &openrtb2.Bid{
ID: request.ID,
Price: float64(missenaResponse.Cpm),
ImpID: request.Imp[0].ID,
AdM: missenaResponse.Ad,
CrID: missenaResponse.RequestId,
}

b := &adapters.TypedBid{
Bid: responseBid,
BidType: openrtb_ext.BidTypeBanner,
}

bidResponse.Bids = append(bidResponse.Bids, b)

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

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

func TestJsonSamples(t *testing.T) {
bidder, buildErr := Builder(openrtb_ext.BidderMissena, config.Adapter{
Endpoint: "http://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, "missenatest", bidder)
}
129 changes: 129 additions & 0 deletions adapters/missena/missenatest/exemplary/multiple-imps.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
{
"mockBidRequest": {
"id": "test-request-id",
"tmax": 500,
"at": 1,
"cur": [
"EUR"
],
"regs": {
"ext": {
"gdpr": 1
}
},
"user": {
"ext": {
"consent": "CO-X2XiO_eyUoAsAxBFRBECsA"
}
},
"device": {
"ip": "123.123.123.123",
"ua": "test-user-agent"
},
"site": {
"page": "https://example.com/page",
"domain": "example.com"
},
"imp": [
{
"id": "test-imp-id-1",
"banner": {
"h": 50,
"w": 320
},
"ext": {
"bidder": {
"apiKey": "test-api-key",
"placement": "test-placement-1",
"test": "1"
}
}
},
{
"id": "test-imp-id-2",
"banner": {
"h": 50,
"w": 320
},
"ext": {
"bidder": {
"apiKey": "test-api-key",
"placement": "test-placement-2",
"test": "1"
}
}
},
{
"id": "test-imp-id-3",
"banner": {
"h": 50,
"w": 320
},
"ext": {
"bidder": "abc"
}
}
]
},
"httpCalls": [
{
"expectedRequest": {
"uri": "http://example.com/?t=test-api-key",
"headers": {
"Content-Type": [
"application/json;charset=utf-8"
],
"Accept": [
"application/json"
],
"User-Agent": [
"test-user-agent"
],
"X-Forwarded-For": [
"123.123.123.123"
],
"Referer": [
"https://example.com/page"
]
},
"body": {
"request_id": "test-request-id",
"timeout": 2000,
"referer": "https://example.com/page",
"referer_canonical": "example.com",
"consent_string": "CO-X2XiO_eyUoAsAxBFRBECsA",
"consent_required": true,
"placement": "test-placement-1",
"test": "1"
},
"impIDs":["test-imp-id-1"]
},
"mockResponse": {
"status": 200,
"body": {
"ad": "<div>test ad</div>",
"cpm": 1.5,
"currency": "EUR",
"requestId": "test-request-id"
}
}
}
],
"expectedBidResponses": [
{
"currency": "EUR",
"bids": [
{
"bid": {
"id": "test-request-id",
"impid": "test-imp-id-1",
"price": 1.5,
"adm": "<div>test ad</div>",
"crid": "test-request-id"
},
"type": "banner"
}
]
}
]
}
Loading
Loading