-
Notifications
You must be signed in to change notification settings - Fork 720
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
base: master
Are you sure you want to change the base?
New Adapter: Missena #3761
Changes from all commits
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,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) | ||
} | ||
} | ||
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 { | ||
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. 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 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. 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 | ||
} |
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) | ||
} |
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" | ||
} | ||
] | ||
} | ||
] | ||
} |
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.
add test for it.
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.
Done ✅