From 4d210b973d2104870dc673c8bca4d1516057be26 Mon Sep 17 00:00:00 2001 From: youssef Date: Tue, 18 Jun 2024 01:48:38 +0100 Subject: [PATCH 1/3] New Adapter: Missena --- adapters/missena/missena.go | 226 ++++++++++++++++++ adapters/missena/missena_test.go | 21 ++ .../missenatest/exemplary/simple-banner.json | 105 ++++++++ .../supplemental/error-ext-bidder.json | 25 ++ .../supplemental/error-imp-ext.json | 23 ++ .../missenatest/supplemental/status-204.json | 83 +++++++ .../missenatest/supplemental/status-400.json | 89 +++++++ .../supplemental/status-not-200.json | 89 +++++++ adapters/missena/params_test.go | 45 ++++ exchange/adapter_builders.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_missena.go | 7 + static/bidder-info/missena.yaml | 16 ++ static/bidder-params/missena.json | 23 ++ 14 files changed, 756 insertions(+) create mode 100644 adapters/missena/missena.go create mode 100644 adapters/missena/missena_test.go create mode 100644 adapters/missena/missenatest/exemplary/simple-banner.json create mode 100644 adapters/missena/missenatest/supplemental/error-ext-bidder.json create mode 100644 adapters/missena/missenatest/supplemental/error-imp-ext.json create mode 100644 adapters/missena/missenatest/supplemental/status-204.json create mode 100644 adapters/missena/missenatest/supplemental/status-400.json create mode 100644 adapters/missena/missenatest/supplemental/status-not-200.json create mode 100644 adapters/missena/params_test.go create mode 100644 openrtb_ext/imp_missena.go create mode 100644 static/bidder-info/missena.yaml create mode 100644 static/bidder-params/missena.json diff --git a/adapters/missena/missena.go b/adapters/missena/missena.go new file mode 100644 index 00000000000..73b2be27b9c --- /dev/null +++ b/adapters/missena/missena.go @@ -0,0 +1,226 @@ +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) makeParameter(missenaParams MissenaInternalParams, request *openrtb2.BidRequest) *MissenaAdRequest { + 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, + } + return &missenaRequest +} + +func (a *adapter) makeRequest(missenaParams MissenaInternalParams, reqInfo *adapters.ExtraRequestInfo, existingRequests []*adapters.RequestData, request *openrtb2.BidRequest) (*adapters.RequestData, error) { + + url := a.endpoint + "?t=" + missenaParams.ApiKey + parameter := a.makeParameter(missenaParams, request) + body, errm := json.Marshal(parameter) + 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 != nil { + 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: openrtb_ext.GetImpIDs(request.Imp), + }, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + + var httpRequests []*adapters.RequestData + var tempErrors []error + gdprApplies, consentString := readGDPR(request) + + var missenaInternalParams MissenaInternalParams + + for _, imp := range request.Imp { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + tempErrors = append(tempErrors, &errortypes.BadInput{ + Message: "Error parsing bidderExt object", + }) + continue + } + + var missenaExt openrtb_ext.ExtImpMissena + if err := json.Unmarshal(bidderExt.Bidder, &missenaExt); err != nil { + tempErrors = append(tempErrors, &errortypes.BadInput{ + Message: "Error parsing missenaExt parameters", + }) + continue + } + + missenaInternalParams.ApiKey = missenaExt.ApiKey + missenaInternalParams.Placement = missenaExt.Placement + missenaInternalParams.TestMode = missenaExt.TestMode + } + + var finalErrors []error + missenaInternalParams.GDPR = gdprApplies + missenaInternalParams.GDPRConsent = consentString + + newHttpRequest, err := a.makeRequest(missenaInternalParams, requestInfo, httpRequests, request) + + if len(tempErrors) > 0 { + return nil, tempErrors + } + + if err != nil { + finalErrors = append(finalErrors, err) + } else if newHttpRequest != nil { + httpRequests = append(httpRequests, newHttpRequest) + } + + return httpRequests, finalErrors +} + +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 := true + 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) + + var errors []error + return bidResponse, errors +} diff --git a/adapters/missena/missena_test.go b/adapters/missena/missena_test.go new file mode 100644 index 00000000000..25169f9f282 --- /dev/null +++ b/adapters/missena/missena_test.go @@ -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: "https://bid.missena.io"}, + 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) +} diff --git a/adapters/missena/missenatest/exemplary/simple-banner.json b/adapters/missena/missenatest/exemplary/simple-banner.json new file mode 100644 index 00000000000..98fc041e04b --- /dev/null +++ b/adapters/missena/missenatest/exemplary/simple-banner.json @@ -0,0 +1,105 @@ +{ + "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", + "banner": { + "h": 50, + "w": 320 + }, + "ext": { + "bidder": { + "apiKey": "test-api-key", + "placement": "test-placement", + "test": "1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bid.missena.io?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", + "test": "1" + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "ad": "
test ad
", + "cpm": 1.5, + "currency": "EUR", + "requestId": "test-request-id" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "test-request-id", + "impid": "test-imp-id", + "price": 1.5, + "adm": "
test ad
", + "crid": "test-request-id" + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/missena/missenatest/supplemental/error-ext-bidder.json b/adapters/missena/missenatest/supplemental/error-ext-bidder.json new file mode 100644 index 00000000000..fdc08f4704b --- /dev/null +++ b/adapters/missena/missenatest/supplemental/error-ext-bidder.json @@ -0,0 +1,25 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.com/url" + }, + "user": { + "buyeruid": "1" + }, + "imp": [ + { + "id": "test-imp-id", + "ext": { + "bidder": "abc" + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "Error parsing missenaExt parameters", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/missena/missenatest/supplemental/error-imp-ext.json b/adapters/missena/missenatest/supplemental/error-imp-ext.json new file mode 100644 index 00000000000..3905efa6bab --- /dev/null +++ b/adapters/missena/missenatest/supplemental/error-imp-ext.json @@ -0,0 +1,23 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://publisher.com/url" + }, + "user": { + "buyeruid": "1" + }, + "imp": [ + { + "id": "test-imp-id", + "ext": "error" + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "Error parsing bidderExt object", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/missena/missenatest/supplemental/status-204.json b/adapters/missena/missenatest/supplemental/status-204.json new file mode 100644 index 00000000000..eb266d97889 --- /dev/null +++ b/adapters/missena/missenatest/supplemental/status-204.json @@ -0,0 +1,83 @@ +{ + "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", + "banner": { + "h": 50, + "w": 320 + }, + "ext": { + "bidder": { + "apiKey": "test-api-key", + "placement": "test-placement", + "test": "1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bid.missena.io?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", + "test": "1" + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 204 + } + } + ], + "expectedBidResponses": [] +} \ No newline at end of file diff --git a/adapters/missena/missenatest/supplemental/status-400.json b/adapters/missena/missenatest/supplemental/status-400.json new file mode 100644 index 00000000000..81f22bca933 --- /dev/null +++ b/adapters/missena/missenatest/supplemental/status-400.json @@ -0,0 +1,89 @@ +{ + "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", + "banner": { + "h": 50, + "w": 320 + }, + "ext": { + "bidder": { + "apiKey": "test-api-key", + "placement": "test-placement", + "test": "1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bid.missena.io?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", + "test": "1" + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 400, + "body": "Bad request from publisher." + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.", + "comparison": "literal" + } + ] + } \ No newline at end of file diff --git a/adapters/missena/missenatest/supplemental/status-not-200.json b/adapters/missena/missenatest/supplemental/status-not-200.json new file mode 100644 index 00000000000..6a90a824076 --- /dev/null +++ b/adapters/missena/missenatest/supplemental/status-not-200.json @@ -0,0 +1,89 @@ +{ + "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", + "banner": { + "h": 50, + "w": 320 + }, + "ext": { + "bidder": { + "apiKey": "test-api-key", + "placement": "test-placement", + "test": "1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bid.missena.io?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", + "test": "1" + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 404, + "body": {} + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 404. Run with request.debug = 1 for more info.", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/missena/params_test.go b/adapters/missena/params_test.go new file mode 100644 index 00000000000..8b27e7a0076 --- /dev/null +++ b/adapters/missena/params_test.go @@ -0,0 +1,45 @@ +package missena + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json schema. %v", err) + } + + for _, p := range validParams { + if err := validator.Validate(openrtb_ext.BidderMissena, json.RawMessage(p)); err != nil { + t.Errorf("Schema rejected valid params: %s", p) + } + } +} + +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json schema. %v", err) + } + + for _, p := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderMissena, json.RawMessage(p)); err == nil { + t.Errorf("Schema allowed invalid params: %s", p) + } + } +} + +var validParams = []string{ + `{"apiKey": ""}`, + `{"apiKey": "PA-123456"}`, + `{"apiKey": "PA-123456", "placement": "sticky"}`, +} + +var invalidParams = []string{ + `{"apiKey": 42}`, + `{"placement": 111}`, +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index f4a2e8ec7fa..b5d81794f41 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -127,6 +127,7 @@ import ( "github.com/prebid/prebid-server/v2/adapters/mgid" "github.com/prebid/prebid-server/v2/adapters/mgidX" "github.com/prebid/prebid-server/v2/adapters/minutemedia" + "github.com/prebid/prebid-server/v2/adapters/missena" "github.com/prebid/prebid-server/v2/adapters/mobfoxpb" "github.com/prebid/prebid-server/v2/adapters/mobilefuse" "github.com/prebid/prebid-server/v2/adapters/motorik" @@ -342,6 +343,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderMgid: mgid.Builder, openrtb_ext.BidderMgidX: mgidX.Builder, openrtb_ext.BidderMinuteMedia: minutemedia.Builder, + openrtb_ext.BidderMissena: missena.Builder, openrtb_ext.BidderMobfoxpb: mobfoxpb.Builder, openrtb_ext.BidderMobileFuse: mobilefuse.Builder, openrtb_ext.BidderMotorik: motorik.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 49f5d317ff8..0cb6966032c 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -145,6 +145,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderMgid, BidderMgidX, BidderMinuteMedia, + BidderMissena, BidderMobfoxpb, BidderMobileFuse, BidderMotorik, @@ -457,6 +458,7 @@ const ( BidderMgid BidderName = "mgid" BidderMgidX BidderName = "mgidX" BidderMinuteMedia BidderName = "minutemedia" + BidderMissena BidderName = "missena" BidderMobfoxpb BidderName = "mobfoxpb" BidderMobileFuse BidderName = "mobilefuse" BidderMotorik BidderName = "motorik" diff --git a/openrtb_ext/imp_missena.go b/openrtb_ext/imp_missena.go new file mode 100644 index 00000000000..3e341957123 --- /dev/null +++ b/openrtb_ext/imp_missena.go @@ -0,0 +1,7 @@ +package openrtb_ext + +type ExtImpMissena struct { + ApiKey string `json:"apiKey"` + Placement string `json:"placement"` + TestMode string `json:"test"` +} diff --git a/static/bidder-info/missena.yaml b/static/bidder-info/missena.yaml new file mode 100644 index 00000000000..ad67d4f291f --- /dev/null +++ b/static/bidder-info/missena.yaml @@ -0,0 +1,16 @@ +endpoint: https://bid.missena.io/ +maintainer: + email: yboussafa@missena.com +gvlVendorID: 687 +modifyingVastXmlAllowed: true +capabilities: + app: + mediaTypes: + - banner + site: + mediaTypes: + - banner +userSync: + redirect: + url: https://sync.missena.io/iframe?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}} + userMacro: $UID \ No newline at end of file diff --git a/static/bidder-params/missena.json b/static/bidder-params/missena.json new file mode 100644 index 00000000000..e3f3b0823b9 --- /dev/null +++ b/static/bidder-params/missena.json @@ -0,0 +1,23 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Missena Adapter Params", + "description": "A schema which validates params accepted by the Missena adapter", + "type": "object", + "properties": { + "apiKey": { + "type": "string", + "description": "API Key" + }, + "placement": { + "type": "string", + "description": "Placement Type (Sticky, Header, ...)" + }, + "test": { + "type": "string", + "description": "Test Mode" + } + }, + "required": [ + "apiKey" + ] +} \ No newline at end of file From 8c79b48721778469c15fe1e3c8bbc0bf2dc2f88e Mon Sep 17 00:00:00 2001 From: youssef Date: Sun, 15 Sep 2024 15:09:59 +0100 Subject: [PATCH 2/3] New Adapter: Missena - Updated --- adapters/missena/missena.go | 43 ++++--- adapters/missena/missena_test.go | 2 +- .../missena/missenatest/exemplary/ipv6.json | 105 ++++++++++++++++ .../missenatest/exemplary/multiple-imps.json | 115 ++++++++++++++++++ .../missenatest/exemplary/simple-banner.json | 2 +- .../missenatest/supplemental/status-204.json | 2 +- .../missenatest/supplemental/status-400.json | 2 +- .../supplemental/status-not-200.json | 2 +- adapters/missena/params_test.go | 7 +- static/bidder-info/missena.yaml | 2 +- static/bidder-params/missena.json | 3 +- 11 files changed, 257 insertions(+), 28 deletions(-) create mode 100644 adapters/missena/missenatest/exemplary/ipv6.json create mode 100644 adapters/missena/missenatest/exemplary/multiple-imps.json diff --git a/adapters/missena/missena.go b/adapters/missena/missena.go index 73b2be27b9c..cc68f7ec68d 100644 --- a/adapters/missena/missena.go +++ b/adapters/missena/missena.go @@ -73,7 +73,7 @@ func (a *adapter) makeParameter(missenaParams MissenaInternalParams, request *op return &missenaRequest } -func (a *adapter) makeRequest(missenaParams MissenaInternalParams, reqInfo *adapters.ExtraRequestInfo, existingRequests []*adapters.RequestData, request *openrtb2.BidRequest) (*adapters.RequestData, error) { +func (a *adapter) makeRequest(missenaParams MissenaInternalParams, reqInfo *adapters.ExtraRequestInfo, imp *openrtb2.Imp, request *openrtb2.BidRequest) (*adapters.RequestData, error) { url := a.endpoint + "?t=" + missenaParams.ApiKey parameter := a.makeParameter(missenaParams, request) @@ -106,22 +106,26 @@ func (a *adapter) makeRequest(missenaParams MissenaInternalParams, reqInfo *adap Uri: url, Headers: headers, Body: body, - ImpIDs: openrtb_ext.GetImpIDs(request.Imp), + ImpIDs: []string{imp.ID}, }, nil } func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + // print the request var httpRequests []*adapters.RequestData - var tempErrors []error + var errors []error gdprApplies, consentString := readGDPR(request) - var missenaInternalParams MissenaInternalParams + missenaInternalParams := MissenaInternalParams{ + GDPR: gdprApplies, + GDPRConsent: consentString, + } for _, imp := range request.Imp { var bidderExt adapters.ExtImpBidder if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { - tempErrors = append(tempErrors, &errortypes.BadInput{ + errors = append(errors, &errortypes.BadInput{ Message: "Error parsing bidderExt object", }) continue @@ -129,7 +133,7 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapte var missenaExt openrtb_ext.ExtImpMissena if err := json.Unmarshal(bidderExt.Bidder, &missenaExt); err != nil { - tempErrors = append(tempErrors, &errortypes.BadInput{ + errors = append(errors, &errortypes.BadInput{ Message: "Error parsing missenaExt parameters", }) continue @@ -138,25 +142,25 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapte missenaInternalParams.ApiKey = missenaExt.ApiKey missenaInternalParams.Placement = missenaExt.Placement missenaInternalParams.TestMode = missenaExt.TestMode - } - var finalErrors []error - missenaInternalParams.GDPR = gdprApplies - missenaInternalParams.GDPRConsent = consentString + newHttpRequest, err := a.makeRequest(missenaInternalParams, requestInfo, &imp, request) + if err != nil { + errors = append(errors, err) + continue + } - newHttpRequest, err := a.makeRequest(missenaInternalParams, requestInfo, httpRequests, request) + httpRequests = append(httpRequests, newHttpRequest) - if len(tempErrors) > 0 { - return nil, tempErrors + break } - if err != nil { - finalErrors = append(finalErrors, err) - } else if newHttpRequest != nil { - httpRequests = append(httpRequests, newHttpRequest) + if len(httpRequests) == 0 && len(errors) == 0 { + errors = append(errors, &errortypes.BadInput{ + Message: "No valid impressions found", + }) } - return httpRequests, finalErrors + return httpRequests, errors } func readGDPR(request *openrtb2.BidRequest) (bool, string) { @@ -221,6 +225,5 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.R bidResponse.Bids = append(bidResponse.Bids, b) - var errors []error - return bidResponse, errors + return bidResponse, nil } diff --git a/adapters/missena/missena_test.go b/adapters/missena/missena_test.go index 25169f9f282..2b13bf085db 100644 --- a/adapters/missena/missena_test.go +++ b/adapters/missena/missena_test.go @@ -10,7 +10,7 @@ import ( func TestJsonSamples(t *testing.T) { bidder, buildErr := Builder(openrtb_ext.BidderMissena, config.Adapter{ - Endpoint: "https://bid.missena.io"}, + Endpoint: "http://example.com/"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) if buildErr != nil { diff --git a/adapters/missena/missenatest/exemplary/ipv6.json b/adapters/missena/missenatest/exemplary/ipv6.json new file mode 100644 index 00000000000..ea240f82e09 --- /dev/null +++ b/adapters/missena/missenatest/exemplary/ipv6.json @@ -0,0 +1,105 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "tmax": 500, + "at": 1, + "cur": [ + "EUR" + ], + "regs": { + "ext": { + "gdpr": 1 + } + }, + "user": { + "ext": { + "consent": "CO-X2XiO_eyUoAsAxBFRBECsA" + } + }, + "device": { + "ipv6": "2001:0000:130F:0000:0000:09C0:876A:130B", + "ua": "test-user-agent" + }, + "site": { + "page": "https://example.com/page", + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "h": 50, + "w": 320 + }, + "ext": { + "bidder": { + "apiKey": "test-api-key", + "placement": "test-placement", + "test": "1" + } + } + } + ] + }, + "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": [ + "2001:0000:130F:0000:0000:09C0:876A:130B" + ], + "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", + "test": "1" + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "ad": "
test ad
", + "cpm": 1.5, + "currency": "EUR", + "requestId": "test-request-id" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "test-request-id", + "impid": "test-imp-id", + "price": 1.5, + "adm": "
test ad
", + "crid": "test-request-id" + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/missena/missenatest/exemplary/multiple-imps.json b/adapters/missena/missenatest/exemplary/multiple-imps.json new file mode 100644 index 00000000000..6b1db679542 --- /dev/null +++ b/adapters/missena/missenatest/exemplary/multiple-imps.json @@ -0,0 +1,115 @@ +{ + "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": "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": "
test ad
", + "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": "
test ad
", + "crid": "test-request-id" + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/missena/missenatest/exemplary/simple-banner.json b/adapters/missena/missenatest/exemplary/simple-banner.json index 98fc041e04b..74ff3abfd57 100644 --- a/adapters/missena/missenatest/exemplary/simple-banner.json +++ b/adapters/missena/missenatest/exemplary/simple-banner.json @@ -44,7 +44,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bid.missena.io?t=test-api-key", + "uri": "http://example.com/?t=test-api-key", "headers": { "Content-Type": [ "application/json;charset=utf-8" diff --git a/adapters/missena/missenatest/supplemental/status-204.json b/adapters/missena/missenatest/supplemental/status-204.json index eb266d97889..59070ab4ecb 100644 --- a/adapters/missena/missenatest/supplemental/status-204.json +++ b/adapters/missena/missenatest/supplemental/status-204.json @@ -44,7 +44,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bid.missena.io?t=test-api-key", + "uri": "http://example.com/?t=test-api-key", "headers": { "Content-Type": [ "application/json;charset=utf-8" diff --git a/adapters/missena/missenatest/supplemental/status-400.json b/adapters/missena/missenatest/supplemental/status-400.json index 81f22bca933..23a153208e3 100644 --- a/adapters/missena/missenatest/supplemental/status-400.json +++ b/adapters/missena/missenatest/supplemental/status-400.json @@ -44,7 +44,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bid.missena.io?t=test-api-key", + "uri": "http://example.com/?t=test-api-key", "headers": { "Content-Type": [ "application/json;charset=utf-8" diff --git a/adapters/missena/missenatest/supplemental/status-not-200.json b/adapters/missena/missenatest/supplemental/status-not-200.json index 6a90a824076..8c913791fc3 100644 --- a/adapters/missena/missenatest/supplemental/status-not-200.json +++ b/adapters/missena/missenatest/supplemental/status-not-200.json @@ -44,7 +44,7 @@ "httpCalls": [ { "expectedRequest": { - "uri": "https://bid.missena.io?t=test-api-key", + "uri": "http://example.com/?t=test-api-key", "headers": { "Content-Type": [ "application/json;charset=utf-8" diff --git a/adapters/missena/params_test.go b/adapters/missena/params_test.go index 8b27e7a0076..e76b80b694f 100644 --- a/adapters/missena/params_test.go +++ b/adapters/missena/params_test.go @@ -34,12 +34,17 @@ func TestInvalidParams(t *testing.T) { } var validParams = []string{ - `{"apiKey": ""}`, `{"apiKey": "PA-123456"}`, `{"apiKey": "PA-123456", "placement": "sticky"}`, + `{"apiKey": "PA-123456", "test": "native"}`, } var invalidParams = []string{ + `{"apiKey": ""}`, `{"apiKey": 42}`, `{"placement": 111}`, + `{"placement": "sticky"}`, + `{"apiKey": "PA-123456", "placement": 111}`, + `{"test": "native"}`, + `{"apiKey": "PA-123456", "test": 111}`, } diff --git a/static/bidder-info/missena.yaml b/static/bidder-info/missena.yaml index ad67d4f291f..415548a31fb 100644 --- a/static/bidder-info/missena.yaml +++ b/static/bidder-info/missena.yaml @@ -1,6 +1,6 @@ endpoint: https://bid.missena.io/ maintainer: - email: yboussafa@missena.com + email: prebid@missena.com gvlVendorID: 687 modifyingVastXmlAllowed: true capabilities: diff --git a/static/bidder-params/missena.json b/static/bidder-params/missena.json index e3f3b0823b9..c9e20e5a828 100644 --- a/static/bidder-params/missena.json +++ b/static/bidder-params/missena.json @@ -6,7 +6,8 @@ "properties": { "apiKey": { "type": "string", - "description": "API Key" + "description": "API Key", + "minLength": 1 }, "placement": { "type": "string", From 00993a5a0d889bbcb914b0d007bcb8d61a0dab39 Mon Sep 17 00:00:00 2001 From: youssef Date: Wed, 18 Sep 2024 13:01:04 +0100 Subject: [PATCH 3/3] Code cleaning --- adapters/missena/missena.go | 28 +--- .../missenatest/exemplary/multiple-imps.json | 14 ++ .../{ipv6.json => simple-banner-ipv6.json} | 0 .../exemplary/valid-imp-error-imp.json | 129 ++++++++++++++++++ 4 files changed, 150 insertions(+), 21 deletions(-) rename adapters/missena/missenatest/exemplary/{ipv6.json => simple-banner-ipv6.json} (100%) create mode 100644 adapters/missena/missenatest/exemplary/valid-imp-error-imp.json diff --git a/adapters/missena/missena.go b/adapters/missena/missena.go index cc68f7ec68d..93d4c2ba1cb 100644 --- a/adapters/missena/missena.go +++ b/adapters/missena/missena.go @@ -59,7 +59,9 @@ func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server co return bidder, nil } -func (a *adapter) makeParameter(missenaParams MissenaInternalParams, request *openrtb2.BidRequest) *MissenaAdRequest { +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, @@ -70,14 +72,8 @@ func (a *adapter) makeParameter(missenaParams MissenaInternalParams, request *op Placement: missenaParams.Placement, TestMode: missenaParams.TestMode, } - return &missenaRequest -} -func (a *adapter) makeRequest(missenaParams MissenaInternalParams, reqInfo *adapters.ExtraRequestInfo, imp *openrtb2.Imp, request *openrtb2.BidRequest) (*adapters.RequestData, error) { - - url := a.endpoint + "?t=" + missenaParams.ApiKey - parameter := a.makeParameter(missenaParams, request) - body, errm := json.Marshal(parameter) + body, errm := json.Marshal(missenaRequest) if errm != nil { return nil, errm } @@ -88,9 +84,6 @@ func (a *adapter) makeRequest(missenaParams MissenaInternalParams, reqInfo *adap if request.Device != nil { headers.Add("User-Agent", request.Device.UA) - } - - if request.Device != nil { if request.Device.IP != "" { headers.Add("X-Forwarded-For", request.Device.IP) } else if request.Device.IPv6 != "" { @@ -106,12 +99,11 @@ func (a *adapter) makeRequest(missenaParams MissenaInternalParams, reqInfo *adap Uri: url, Headers: headers, Body: body, - ImpIDs: []string{imp.ID}, + ImpIDs: []string{impID}, }, nil } func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { - // print the request var httpRequests []*adapters.RequestData var errors []error @@ -143,7 +135,7 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapte missenaInternalParams.Placement = missenaExt.Placement missenaInternalParams.TestMode = missenaExt.TestMode - newHttpRequest, err := a.makeRequest(missenaInternalParams, requestInfo, &imp, request) + newHttpRequest, err := a.makeRequest(missenaInternalParams, requestInfo, imp.ID, request) if err != nil { errors = append(errors, err) continue @@ -154,12 +146,6 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapte break } - if len(httpRequests) == 0 && len(errors) == 0 { - errors = append(errors, &errortypes.BadInput{ - Message: "No valid impressions found", - }) - } - return httpRequests, errors } @@ -171,7 +157,7 @@ func readGDPR(request *openrtb2.BidRequest) (bool, string) { consentString = extUser.Consent } } - gdprApplies := true + gdprApplies := false var extRegs openrtb_ext.ExtRegs if request.Regs != nil { if err := json.Unmarshal(request.Regs.Ext, &extRegs); err == nil { diff --git a/adapters/missena/missenatest/exemplary/multiple-imps.json b/adapters/missena/missenatest/exemplary/multiple-imps.json index 6b1db679542..5b83f19ccd0 100644 --- a/adapters/missena/missenatest/exemplary/multiple-imps.json +++ b/adapters/missena/missenatest/exemplary/multiple-imps.json @@ -45,6 +45,20 @@ "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" } diff --git a/adapters/missena/missenatest/exemplary/ipv6.json b/adapters/missena/missenatest/exemplary/simple-banner-ipv6.json similarity index 100% rename from adapters/missena/missenatest/exemplary/ipv6.json rename to adapters/missena/missenatest/exemplary/simple-banner-ipv6.json diff --git a/adapters/missena/missenatest/exemplary/valid-imp-error-imp.json b/adapters/missena/missenatest/exemplary/valid-imp-error-imp.json new file mode 100644 index 00000000000..61be3f78c4c --- /dev/null +++ b/adapters/missena/missenatest/exemplary/valid-imp-error-imp.json @@ -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": "
test ad
", + "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": "
test ad
", + "crid": "test-request-id" + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file