diff --git a/adapters/missena/missena.go b/adapters/missena/missena.go
new file mode 100644
index 00000000000..93d4c2ba1cb
--- /dev/null
+++ b/adapters/missena/missena.go
@@ -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 {
+ 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
+}
diff --git a/adapters/missena/missena_test.go b/adapters/missena/missena_test.go
new file mode 100644
index 00000000000..2b13bf085db
--- /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: "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)
+}
diff --git a/adapters/missena/missenatest/exemplary/multiple-imps.json b/adapters/missena/missenatest/exemplary/multiple-imps.json
new file mode 100644
index 00000000000..5b83f19ccd0
--- /dev/null
+++ b/adapters/missena/missenatest/exemplary/multiple-imps.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
diff --git a/adapters/missena/missenatest/exemplary/simple-banner-ipv6.json b/adapters/missena/missenatest/exemplary/simple-banner-ipv6.json
new file mode 100644
index 00000000000..ea240f82e09
--- /dev/null
+++ b/adapters/missena/missenatest/exemplary/simple-banner-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/simple-banner.json b/adapters/missena/missenatest/exemplary/simple-banner.json
new file mode 100644
index 00000000000..74ff3abfd57
--- /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": "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",
+ "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/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
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..59070ab4ecb
--- /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": "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",
+ "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..23a153208e3
--- /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": "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",
+ "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..8c913791fc3
--- /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": "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",
+ "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..e76b80b694f
--- /dev/null
+++ b/adapters/missena/params_test.go
@@ -0,0 +1,50 @@
+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": "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/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..415548a31fb
--- /dev/null
+++ b/static/bidder-info/missena.yaml
@@ -0,0 +1,16 @@
+endpoint: https://bid.missena.io/
+maintainer:
+ email: prebid@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..c9e20e5a828
--- /dev/null
+++ b/static/bidder-params/missena.json
@@ -0,0 +1,24 @@
+{
+ "$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",
+ "minLength": 1
+ },
+ "placement": {
+ "type": "string",
+ "description": "Placement Type (Sticky, Header, ...)"
+ },
+ "test": {
+ "type": "string",
+ "description": "Test Mode"
+ }
+ },
+ "required": [
+ "apiKey"
+ ]
+}
\ No newline at end of file