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