diff --git a/adapters/mabidder/mabidder.go b/adapters/mabidder/mabidder.go
new file mode 100644
index 00000000000..184f068b333
--- /dev/null
+++ b/adapters/mabidder/mabidder.go
@@ -0,0 +1,99 @@
+package mabidder
+
+import (
+ "encoding/json"
+
+ "github.com/prebid/openrtb/v19/openrtb2"
+ "github.com/prebid/prebid-server/adapters"
+ "github.com/prebid/prebid-server/config"
+ "github.com/prebid/prebid-server/openrtb_ext"
+)
+
+type serverResponse struct {
+ Responses []bidResponse
+ PrivateIdStatus string `json:"-"`
+}
+
+type bidResponse struct {
+ RequestID string `json:"requestId"`
+ Currency string `json:"currency"`
+ Width int32 `json:"width"`
+ Height int32 `json:"height"`
+ PlacementId string `json:"creativeId"`
+ Deal string `json:"dealId,omitempty"`
+ NetRevenue bool `json:"netRevenue"`
+ TimeToLiveSeconds int32 `json:"ttl"`
+ AdTag string `json:"ad"`
+ MediaType string `json:"mediaType"`
+ Meta meta `json:"meta"`
+ CPM float32 `json:"cpm"`
+}
+
+type meta struct {
+ AdDomain []string `json:"advertiserDomains"`
+}
+
+type adapter struct {
+ endpoint string
+}
+
+// Builder builds a new instance of the Mabidder 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) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+ requestJSON, err := json.Marshal(request)
+ if err != nil {
+ return nil, []error{err}
+ }
+
+ requestData := &adapters.RequestData{
+ Method: "POST",
+ Uri: a.endpoint,
+ Body: requestJSON,
+ }
+
+ return []*adapters.RequestData{requestData}, nil
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+ if adapters.IsResponseStatusCodeNoContent(responseData) {
+ return nil, nil
+ }
+
+ if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil {
+ return nil, []error{err}
+ }
+
+ var response serverResponse
+ if err := json.Unmarshal(responseData.Body, &response); err != nil {
+ return nil, []error{err}
+ }
+
+ bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
+ for _, maBidResp := range response.Responses {
+ b := &adapters.TypedBid{
+ Bid: &openrtb2.Bid{
+ ID: maBidResp.RequestID,
+ ImpID: maBidResp.RequestID,
+ Price: float64(maBidResp.CPM),
+ AdM: maBidResp.AdTag,
+ W: int64(maBidResp.Width),
+ H: int64(maBidResp.Height),
+ CrID: maBidResp.PlacementId,
+ DealID: maBidResp.Deal,
+ ADomain: maBidResp.Meta.AdDomain,
+ },
+ BidType: openrtb_ext.BidType(maBidResp.MediaType),
+ }
+ bidResponse.Bids = append(bidResponse.Bids, b)
+ if maBidResp.Currency != "" {
+ bidResponse.Currency = maBidResp.Currency
+ }
+ }
+ return bidResponse, nil
+}
diff --git a/adapters/mabidder/mabidder_test.go b/adapters/mabidder/mabidder_test.go
new file mode 100644
index 00000000000..89cfd31633e
--- /dev/null
+++ b/adapters/mabidder/mabidder_test.go
@@ -0,0 +1,21 @@
+package mabidder
+
+import (
+ "testing"
+
+ "github.com/prebid/prebid-server/adapters/adapterstest"
+ "github.com/prebid/prebid-server/config"
+ "github.com/prebid/prebid-server/openrtb_ext"
+)
+
+func TestJsonSamples(t *testing.T) {
+ bidder, buildErr := Builder(openrtb_ext.BidderMabidder, config.Adapter{
+ Endpoint: "https://prebid.ecdrsvc.com/pbs"},
+ config.Server{ExternalUrl: "https://prebid.ecdrsvc.com/pbs", GvlID: 1, DataCenter: "2"})
+
+ if buildErr != nil {
+ t.Fatalf("Builder returned unexpected error %v", buildErr)
+ }
+
+ adapterstest.RunJSONBidderTest(t, "mabiddertest", bidder)
+}
diff --git a/adapters/mabidder/mabiddertest/exemplary/simple-app-banner.json b/adapters/mabidder/mabiddertest/exemplary/simple-app-banner.json
new file mode 100644
index 00000000000..4d3cbdfd278
--- /dev/null
+++ b/adapters/mabidder/mabiddertest/exemplary/simple-app-banner.json
@@ -0,0 +1,110 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "ppid": "ppidtest"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://prebid.ecdrsvc.com/pbs",
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "ppid": "ppidtest"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "Responses": [
+ {
+ "requestId": "1",
+ "currency": "CAD",
+ "width": 300,
+ "height": 250,
+ "creativeId": "6002677",
+ "dealId": "testdeal",
+ "netRevenue": false,
+ "ttl": 5,
+ "ad": "",
+ "meta": {
+ "advertiserDomains": [
+ "https://www.loblaws.ca/"
+ ]
+ },
+ "cpm": 3.5764000415802
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "id": "test-request-id",
+ "bids": [
+ {
+ "bid": {
+ "id": "1",
+ "impid": "1",
+ "price":3.5764000415802,
+ "adm": "",
+ "adomain": [
+ "https://www.loblaws.ca/"
+ ],
+ "crid": "6002677",
+ "dealid": "testdeal",
+ "w": 300,
+ "h": 250
+ }
+ }
+ ],
+ "cur": "USD"
+ }
+ ]
+}
diff --git a/adapters/mabidder/mabiddertest/supplemental/bad-request-example.json b/adapters/mabidder/mabiddertest/supplemental/bad-request-example.json
new file mode 100644
index 00000000000..8ab37e13079
--- /dev/null
+++ b/adapters/mabidder/mabiddertest/supplemental/bad-request-example.json
@@ -0,0 +1,74 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "ppid": "mabidder-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://prebid.ecdrsvc.com/pbs",
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "ppid": "mabidder-test"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 400,
+ "body": {
+ }
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/mabidder/mabiddertest/supplemental/bad-response-malformed.json b/adapters/mabidder/mabiddertest/supplemental/bad-response-malformed.json
new file mode 100644
index 00000000000..f62ed2a7a61
--- /dev/null
+++ b/adapters/mabidder/mabiddertest/supplemental/bad-response-malformed.json
@@ -0,0 +1,72 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "ppid": "mabidder-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://prebid.ecdrsvc.com/pbs",
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "ppid": "mabidder-test"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 200
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "unexpected end of JSON input",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/mabidder/mabiddertest/supplemental/bad-response-status-500.json b/adapters/mabidder/mabiddertest/supplemental/bad-response-status-500.json
new file mode 100644
index 00000000000..8ee59384071
--- /dev/null
+++ b/adapters/mabidder/mabiddertest/supplemental/bad-response-status-500.json
@@ -0,0 +1,74 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "ppid": "mabidder-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://prebid.ecdrsvc.com/pbs",
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "ppid": "mabidder-test"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 500,
+ "body": {
+ }
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unexpected status code: 500. Run with request.debug = 1 for more info",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/mabidder/mabiddertest/supplemental/no-content-response.json b/adapters/mabidder/mabiddertest/supplemental/no-content-response.json
new file mode 100644
index 00000000000..640e78dcec8
--- /dev/null
+++ b/adapters/mabidder/mabiddertest/supplemental/no-content-response.json
@@ -0,0 +1,69 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "ppid": "mabidder-test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://prebid.ecdrsvc.com/pbs",
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa":"87857b31-8942-4646-ae80-ab9c95bf3fab"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "ppid": "mabidder-test"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 204,
+ "body": {
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": []
+}
diff --git a/adapters/mabidder/params_test.go b/adapters/mabidder/params_test.go
new file mode 100644
index 00000000000..b0f3762d843
--- /dev/null
+++ b/adapters/mabidder/params_test.go
@@ -0,0 +1,50 @@
+package mabidder
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/prebid/prebid-server/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.BidderMabidder, 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.BidderMabidder, json.RawMessage(p)); err == nil {
+ t.Errorf("Schema allowed invalid params: %s", p)
+ }
+ }
+}
+
+var validParams = []string{
+ `{"ppid": ""}`,
+ `{"ppid": "testppid"}`,
+}
+
+var invalidParams = []string{
+ ``,
+ `null`,
+ `true`,
+ `5`,
+ `4.2`,
+ `[]`,
+ `{}`,
+ `{"ppid": 42}`,
+}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index f64b366d717..395e4fbbc39 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -113,6 +113,7 @@ import (
"github.com/prebid/prebid-server/adapters/logan"
"github.com/prebid/prebid-server/adapters/logicad"
"github.com/prebid/prebid-server/adapters/lunamedia"
+ mabidder "github.com/prebid/prebid-server/adapters/mabidder"
"github.com/prebid/prebid-server/adapters/madvertise"
"github.com/prebid/prebid-server/adapters/marsmedia"
"github.com/prebid/prebid-server/adapters/medianet"
@@ -317,6 +318,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
openrtb_ext.BidderLogan: logan.Builder,
openrtb_ext.BidderLogicad: logicad.Builder,
openrtb_ext.BidderLunaMedia: lunamedia.Builder,
+ openrtb_ext.BidderMabidder: mabidder.Builder,
openrtb_ext.BidderMadvertise: madvertise.Builder,
openrtb_ext.BidderMarsmedia: marsmedia.Builder,
openrtb_ext.BidderMediafuse: appnexus.Builder,
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index 1fb90e974b0..e12316f7e72 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -140,6 +140,7 @@ var coreBidderNames []BidderName = []BidderName{
BidderLogan,
BidderLogicad,
BidderLunaMedia,
+ BidderMabidder,
BidderMadvertise,
BidderMarsmedia,
BidderMediafuse,
@@ -434,6 +435,7 @@ const (
BidderLogan BidderName = "logan"
BidderLogicad BidderName = "logicad"
BidderLunaMedia BidderName = "lunamedia"
+ BidderMabidder BidderName = "mabidder"
BidderMadvertise BidderName = "madvertise"
BidderMarsmedia BidderName = "marsmedia"
BidderMediafuse BidderName = "mediafuse"
diff --git a/openrtb_ext/imp_mabidder.go b/openrtb_ext/imp_mabidder.go
new file mode 100644
index 00000000000..796a4877215
--- /dev/null
+++ b/openrtb_ext/imp_mabidder.go
@@ -0,0 +1,5 @@
+package openrtb_ext
+
+type ImpExtMabidder struct {
+ Ppid string `json:"ppid"`
+}
diff --git a/static/bidder-info/mabidder.yaml b/static/bidder-info/mabidder.yaml
new file mode 100644
index 00000000000..3f03fd87854
--- /dev/null
+++ b/static/bidder-info/mabidder.yaml
@@ -0,0 +1,11 @@
+endpoint: "https://prebid.ecdrsvc.com/pbs"
+maintainer:
+ email: lmprebidadapter@loblaw.ca
+endpointCompression: gzip
+capabilities:
+ app:
+ mediaTypes:
+ - banner
+ site:
+ mediaTypes:
+ - banner
\ No newline at end of file
diff --git a/static/bidder-params/mabidder.json b/static/bidder-params/mabidder.json
new file mode 100644
index 00000000000..f005e88e33d
--- /dev/null
+++ b/static/bidder-params/mabidder.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "Mabidder Adapter Params",
+ "description": "A schema which validates params accepted by the Mabidder adapter",
+ "type": "object",
+
+ "properties": {
+ "ppid": {
+ "type": "string",
+ "description": "Publisher Placement ID"
+ }
+ },
+
+ "required": ["ppid"]
+}
\ No newline at end of file