diff --git a/adapters/lockerdome/lockerdome.go b/adapters/lockerdome/lockerdome.go new file mode 100644 index 00000000000..cc3545fa364 --- /dev/null +++ b/adapters/lockerdome/lockerdome.go @@ -0,0 +1,153 @@ +package lockerdome + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +const unexpectedStatusCodeMessage = "Unexpected status code: %d. Run with request.debug = 1 for more info" + +// Implements Bidder interface. +type LockerDomeAdapter struct { + endpoint string +} + +// MakeRequests makes the HTTP requests which should be made to fetch bids [from the bidder, in this case, LockerDome] +func (adapter *LockerDomeAdapter) MakeRequests(openRTBRequest *openrtb.BidRequest, extraReqInfo *adapters.ExtraRequestInfo) (requestsToBidder []*adapters.RequestData, errs []error) { + + numberOfImps := len(openRTBRequest.Imp) + + if openRTBRequest.Imp == nil || numberOfImps == 0 { // lockerdometest/supplemental/empty_imps.json + err := &errortypes.BadInput{ + Message: "No valid impressions in the bid request.", + } + errs = append(errs, err) + return nil, errs + } + + var indexesOfValidImps []int + for i := 0; i < numberOfImps; i++ { + // LockerDome currently only supports banner impressions, and requires data in the ext field. + if openRTBRequest.Imp[i].Banner == nil { // lockerdometest/supplemental/unsupported_imp_type.json + err := &errortypes.BadInput{ + Message: "LockerDome does not currently support non-banner types.", + } + errs = append(errs, err) + continue + } + var bidderExt adapters.ExtImpBidder + err := json.Unmarshal(openRTBRequest.Imp[i].Ext, &bidderExt) + if err != nil { // lockerdometest/supplemental/no_ext.json + err = &errortypes.BadInput{ + Message: "ext was not provided.", + } + errs = append(errs, err) + continue + } + var lockerdomeExt openrtb_ext.ExtImpLockerDome + err = json.Unmarshal(bidderExt.Bidder, &lockerdomeExt) + if err != nil { // lockerdometest/supplemental/no_adUnitId_param.json + err = &errortypes.BadInput{ + Message: "ext.bidder.adUnitId was not provided.", + } + errs = append(errs, err) + continue + } + if lockerdomeExt.AdUnitId == "" { // lockerdometest/supplemental/empty_adUnitId_param.json + err := &errortypes.BadInput{ + Message: "ext.bidder.adUnitId is empty.", + } + errs = append(errs, err) + continue + } + indexesOfValidImps = append(indexesOfValidImps, i) + } + if numberOfImps > len(indexesOfValidImps) { + var validImps []openrtb.Imp + for j := 0; j < len(indexesOfValidImps); j++ { + validImps = append(validImps, openRTBRequest.Imp[j]) + } + if len(validImps) == 0 { + err := &errortypes.BadInput{ + Message: "No valid or supported impressions in the bid request.", + } + errs = append(errs, err) + return nil, errs + } else { + openRTBRequest.Imp = validImps + } + } + openRTBRequestJSON, err := json.Marshal(openRTBRequest) + if err != nil { + errs = append(errs, err) + return nil, errs + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + headers.Add("x-openrtb-version", "2.5") + + requestToBidder := &adapters.RequestData{ + Method: "POST", + Uri: adapter.endpoint, + Body: openRTBRequestJSON, + Headers: headers, + } + + requestsToBidder = append(requestsToBidder, requestToBidder) + + return requestsToBidder, nil + +} + +// MakeBids unpacks the server's response into Bids. +func (adapter *LockerDomeAdapter) MakeBids(openRTBRequest *openrtb.BidRequest, requestToBidder *adapters.RequestData, bidderRawResponse *adapters.ResponseData) (bidderResponse *adapters.BidderResponse, errs []error) { + + if bidderRawResponse.StatusCode == http.StatusNoContent { + return nil, nil + } + + if bidderRawResponse.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf(unexpectedStatusCodeMessage, bidderRawResponse.StatusCode), + }} + } + + if bidderRawResponse.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf(unexpectedStatusCodeMessage, bidderRawResponse.StatusCode), + }} + } + + var openRTBBidderResponse openrtb.BidResponse + if err := json.Unmarshal(bidderRawResponse.Body, &openRTBBidderResponse); err != nil { + return nil, []error{ + fmt.Errorf("Error unmarshaling LockerDome bid response - %s", err.Error()), + } + } + + if len(openRTBBidderResponse.SeatBid) == 0 { + return nil, nil + } + + bidderResponse = adapters.NewBidderResponseWithBidsCapacity(len(openRTBBidderResponse.SeatBid[0].Bid)) + + for _, seatBid := range openRTBBidderResponse.SeatBid { + for i := range seatBid.Bid { + typedBid := adapters.TypedBid{Bid: &seatBid.Bid[i], BidType: openrtb_ext.BidTypeBanner} + bidderResponse.Bids = append(bidderResponse.Bids, &typedBid) + } + } + return bidderResponse, nil +} + +func NewLockerDomeBidder(endpoint string) *LockerDomeAdapter { + return &LockerDomeAdapter{endpoint: endpoint} +} diff --git a/adapters/lockerdome/lockerdome_test.go b/adapters/lockerdome/lockerdome_test.go new file mode 100644 index 00000000000..5f8c19f0652 --- /dev/null +++ b/adapters/lockerdome/lockerdome_test.go @@ -0,0 +1,10 @@ +package lockerdome + +import ( + "github.com/prebid/prebid-server/adapters/adapterstest" + "testing" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "lockerdometest", NewLockerDomeBidder("https://lockerdome.com/ladbid/prebidserver/openrtb2")) +} diff --git a/adapters/lockerdome/lockerdometest/exemplary/simple-banner-multiple.json b/adapters/lockerdome/lockerdometest/exemplary/simple-banner-multiple.json new file mode 100644 index 00000000000..e58369d1695 --- /dev/null +++ b/adapters/lockerdome/lockerdometest/exemplary/simple-banner-multiple.json @@ -0,0 +1,146 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + }, + { + "id": "test-imp-id-2", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9360401292656742" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://lockerdome.com/ladbid/prebidserver/openrtb2", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + }, + { + "id": "test-imp-id-2", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9360401292656742" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "lockerdome", + "bid": [ + { + "id": "729c0d99-3b45-4225-9ad5-64aef2b46df3", + "impid": "test-imp-id", + "price": 0.23, + "adm": "test ad markup #1", + "crid": "LD12134555163033624", + "h": 250, + "w": 300 + }, + { + "id": "9euij6w8-sj89-6397-6er4-5we9t1a67wkj", + "impid": "test-imp-id-2", + "price": 0.5, + "adm": "test ad markup #2", + "crid": "LD12258417590730764", + "h": 250, + "w": 300 + } + ] + } + ] + } + } + } + ], + + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "729c0d99-3b45-4225-9ad5-64aef2b46df3", + "impid": "test-imp-id", + "price": 0.23, + "adm": "test ad markup #1", + "crid": "LD12134555163033624", + "w": 300, + "h": 250 + }, + "type": "banner" + }, + { + "bid": { + "id": "9euij6w8-sj89-6397-6er4-5we9t1a67wkj", + "impid": "test-imp-id-2", + "price": 0.5, + "adm": "test ad markup #2", + "crid": "LD12258417590730764", + "w": 300, + "h": 250 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/lockerdome/lockerdometest/exemplary/simple-banner.json b/adapters/lockerdome/lockerdometest/exemplary/simple-banner.json new file mode 100644 index 00000000000..19256ef9a78 --- /dev/null +++ b/adapters/lockerdome/lockerdometest/exemplary/simple-banner.json @@ -0,0 +1,93 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://lockerdome.com/ladbid/prebidserver/openrtb2", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "lockerdome", + "bid": [ + { + "id": "729c0d99-3b45-4225-9ad5-64aef2b46df3", + "impid": "test-imp-id", + "price": 0.60, + "adm": "test ad markup", + "crid": "LD12134555163033624", + "h": 250, + "w": 300 + } + ] + } + ] + } + } + } + ], + + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "729c0d99-3b45-4225-9ad5-64aef2b46df3", + "impid": "test-imp-id", + "price": 0.60, + "adm": "test ad markup", + "crid": "LD12134555163033624", + "w": 300, + "h": 250 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/lockerdome/lockerdometest/params/race/banner.json b/adapters/lockerdome/lockerdometest/params/race/banner.json new file mode 100644 index 00000000000..acd8a7de87d --- /dev/null +++ b/adapters/lockerdome/lockerdometest/params/race/banner.json @@ -0,0 +1,3 @@ +{ + "adUnitId": "LD9434769725128805" +} diff --git a/adapters/lockerdome/lockerdometest/supplemental/bad_response.json b/adapters/lockerdome/lockerdometest/supplemental/bad_response.json new file mode 100644 index 00000000000..037a1c3ae2a --- /dev/null +++ b/adapters/lockerdome/lockerdometest/supplemental/bad_response.json @@ -0,0 +1,60 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://lockerdome.com/ladbid/prebidserver/openrtb2", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": "{\"id\"data.lost" + } + } + ], + + "expectedMakeBidsErrors": [ + "Error unmarshaling LockerDome bid response - json: cannot unmarshal string into Go value of type openrtb.BidResponse" + ] +} diff --git a/adapters/lockerdome/lockerdometest/supplemental/empty_adUnitId_param.json b/adapters/lockerdome/lockerdometest/supplemental/empty_adUnitId_param.json new file mode 100644 index 00000000000..c1c7966284b --- /dev/null +++ b/adapters/lockerdome/lockerdometest/supplemental/empty_adUnitId_param.json @@ -0,0 +1,28 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "" + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + "ext.bidder.adUnitId is empty.", + "No valid or supported impressions in the bid request." + ] +} diff --git a/adapters/lockerdome/lockerdometest/supplemental/empty_imps.json b/adapters/lockerdome/lockerdometest/supplemental/empty_imps.json new file mode 100644 index 00000000000..258679b43e7 --- /dev/null +++ b/adapters/lockerdome/lockerdometest/supplemental/empty_imps.json @@ -0,0 +1,10 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [] + }, + + "expectedMakeRequestsErrors": [ + "No valid impressions in the bid request." + ] +} diff --git a/adapters/lockerdome/lockerdometest/supplemental/empty_seatbid.json b/adapters/lockerdome/lockerdometest/supplemental/empty_seatbid.json new file mode 100644 index 00000000000..36f9c1143c3 --- /dev/null +++ b/adapters/lockerdome/lockerdometest/supplemental/empty_seatbid.json @@ -0,0 +1,61 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://lockerdome.com/ladbid/prebidserver/openrtb2", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [] + } + } + } + ], + + "expectedBidResponses": [] +} diff --git a/adapters/lockerdome/lockerdometest/supplemental/no_adUnitId_param.json b/adapters/lockerdome/lockerdometest/supplemental/no_adUnitId_param.json new file mode 100644 index 00000000000..8ef4d116bca --- /dev/null +++ b/adapters/lockerdome/lockerdometest/supplemental/no_adUnitId_param.json @@ -0,0 +1,24 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": {} + } + ] + }, + + "expectedMakeRequestsErrors": [ + "ext.bidder.adUnitId was not provided.", + "No valid or supported impressions in the bid request." + ] +} diff --git a/adapters/lockerdome/lockerdometest/supplemental/no_ext.json b/adapters/lockerdome/lockerdometest/supplemental/no_ext.json new file mode 100644 index 00000000000..5d3bc1a9772 --- /dev/null +++ b/adapters/lockerdome/lockerdometest/supplemental/no_ext.json @@ -0,0 +1,23 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + "ext was not provided.", + "No valid or supported impressions in the bid request." + ] +} diff --git a/adapters/lockerdome/lockerdometest/supplemental/status_204.json b/adapters/lockerdome/lockerdometest/supplemental/status_204.json new file mode 100644 index 00000000000..de37c0da04a --- /dev/null +++ b/adapters/lockerdome/lockerdometest/supplemental/status_204.json @@ -0,0 +1,58 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://lockerdome.com/ladbid/prebidserver/openrtb2", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + } + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + + "expectedBidResponses": [] +} diff --git a/adapters/lockerdome/lockerdometest/supplemental/status_400.json b/adapters/lockerdome/lockerdometest/supplemental/status_400.json new file mode 100644 index 00000000000..d19c38c2f09 --- /dev/null +++ b/adapters/lockerdome/lockerdometest/supplemental/status_400.json @@ -0,0 +1,60 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://lockerdome.com/ladbid/prebidserver/openrtb2", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + } + }, + "mockResponse": { + "status": 400, + "body": {} + } + } + ], + + "expectedMakeBidsErrors": [ + "Unexpected status code: 400. Run with request.debug = 1 for more info" + ] +} diff --git a/adapters/lockerdome/lockerdometest/supplemental/status_418.json b/adapters/lockerdome/lockerdometest/supplemental/status_418.json new file mode 100644 index 00000000000..cfa5b48a261 --- /dev/null +++ b/adapters/lockerdome/lockerdometest/supplemental/status_418.json @@ -0,0 +1,60 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://lockerdome.com/ladbid/prebidserver/openrtb2", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + } + }, + "mockResponse": { + "status": 418, + "body": {} + } + } + ], + + "expectedMakeBidsErrors": [ + "Unexpected status code: 418. Run with request.debug = 1 for more info" + ] +} diff --git a/adapters/lockerdome/lockerdometest/supplemental/unsupported_imp_type.json b/adapters/lockerdome/lockerdometest/supplemental/unsupported_imp_type.json new file mode 100644 index 00000000000..6007cb3c01b --- /dev/null +++ b/adapters/lockerdome/lockerdometest/supplemental/unsupported_imp_type.json @@ -0,0 +1,28 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "LD9434769725128806" + } + } + } + ] + }, + + "expectedMakeRequestsErrors": [ + "LockerDome does not currently support non-banner types.", + "No valid or supported impressions in the bid request." + ] +} diff --git a/adapters/lockerdome/params_test.go b/adapters/lockerdome/params_test.go new file mode 100644 index 00000000000..815246571e3 --- /dev/null +++ b/adapters/lockerdome/params_test.go @@ -0,0 +1,56 @@ +package lockerdome + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +// This file tests static/bidder-params/lockerdome.json +// and validates the format of the external API: request.imp[i].ext.lockerdome + +// TestValidParams makes sure that the LockerDome schema accepts all imp.ext fields which we intend to support. +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderLockerDome, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected LockerDome params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the LockerDome schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderLockerDome, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"adUnitId": "LD1234567890"}`, // adUnitId can start with "LD" +} + +var invalidParams = []string{ + `null`, + `nil`, + ``, + `true`, + `1`, + `1.5`, + `[]`, + `{}`, + `{"adUnitId": true}`, + `{"adUnitId": 123456789}`, // adUnitId can't be a number +} diff --git a/adapters/lockerdome/usersync.go b/adapters/lockerdome/usersync.go new file mode 100644 index 00000000000..fb01f8e2d99 --- /dev/null +++ b/adapters/lockerdome/usersync.go @@ -0,0 +1,12 @@ +package lockerdome + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewLockerDomeSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("lockerdome", 0, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/lockerdome/usersync_test.go b/adapters/lockerdome/usersync_test.go new file mode 100644 index 00000000000..b7466ef2836 --- /dev/null +++ b/adapters/lockerdome/usersync_test.go @@ -0,0 +1,19 @@ +package lockerdome + +import ( + "testing" + "text/template" + + "github.com/stretchr/testify/assert" +) + +func TestLockerDomeSyncer(t *testing.T) { + temp := template.Must(template.New("sync-template").Parse("https://lockerdome.com/usync/prebidserver?pid=&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect=https%3A%2F%2Flocalhost%2Fsetuid%3Fbidder%3Dlockerdome%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7Buid%7D%7D")) + syncer := NewLockerDomeSyncer(temp) + syncInfo, err := syncer.GetUsersyncInfo("", "") + assert.NoError(t, err) + assert.Equal(t, "https://lockerdome.com/usync/prebidserver?pid=&gdpr=&gdpr_consent=&redirect=https%3A%2F%2Flocalhost%2Fsetuid%3Fbidder%3Dlockerdome%26gdpr%3D%26gdpr_consent%3D%26uid%3D%7B%7Buid%7D%7D", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 0, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 113fc38e8d8..a565d7cc2ae 100644 --- a/config/config.go +++ b/config/config.go @@ -473,6 +473,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderImprovedigital, "https://ad.360yield.com/server_match?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dimprovedigital%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7BPUB_USER_ID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderIx, "https://ssum.casalemedia.com/usermatchredir?s=184932&cb="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dix%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLifestreet, "https://ads.lfstmedia.com/idsync/137062?synced=1&ttl=1s&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlifestreet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%24visitor_cookie%24%24") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLockerDome, "https://lockerdome.com/usync/prebidserver?pid="+cfg.Adapters["lockerdome"].PlatformID+"&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlockerdome%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7Buid%7D%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderOpenx, "https://rtb.openx.net/sync/prebid?r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dopenx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderPubmatic, "https://ads.pubmatic.com/AdServer/js/user_sync.html?predirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderPulsepoint, "https://bh.contextweb.com/rtset?pid=561205&ev=1&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dpulsepoint%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25%25VGUID%25%25") @@ -639,6 +640,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.improvedigital.endpoint", "http://ad.360yield.com/pbs") v.SetDefault("adapters.ix.endpoint", "http://appnexus-us-east.lb.indexww.com/transbidder?p=184932") v.SetDefault("adapters.lifestreet.endpoint", "https://prebid.s2s.lfstmedia.com/adrequest") + v.SetDefault("adapters.lockerdome.endpoint", "https://lockerdome.com/ladbid/prebidserver/openrtb2") v.SetDefault("adapters.openx.endpoint", "http://rtb.openx.net/prebid") v.SetDefault("adapters.pubmatic.endpoint", "http://hbopenbid.pubmatic.com/translator?source=prebid-server") v.SetDefault("adapters.pulsepoint.endpoint", "http://bid.contextweb.com/header/s/ortb/prebid-s2s") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index a4207044a85..490c9545fb9 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -29,6 +29,7 @@ import ( "github.com/prebid/prebid-server/adapters/improvedigital" "github.com/prebid/prebid-server/adapters/ix" "github.com/prebid/prebid-server/adapters/lifestreet" + "github.com/prebid/prebid-server/adapters/lockerdome" "github.com/prebid/prebid-server/adapters/mgid" "github.com/prebid/prebid-server/adapters/openx" "github.com/prebid/prebid-server/adapters/pubmatic" @@ -71,6 +72,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderEmxDigital: emx_digital.NewEmxDigitalBidder(cfg.Adapters[string(openrtb_ext.BidderEmxDigital)].Endpoint), openrtb_ext.BidderEPlanning: eplanning.NewEPlanningBidder(client, cfg.Adapters[string(openrtb_ext.BidderEPlanning)].Endpoint), openrtb_ext.BidderGumGum: gumgum.NewGumGumBidder(cfg.Adapters[string(openrtb_ext.BidderGumGum)].Endpoint), + openrtb_ext.BidderLockerDome: lockerdome.NewLockerDomeBidder(cfg.Adapters[string(openrtb_ext.BidderLockerDome)].Endpoint), openrtb_ext.BidderOpenx: openx.NewOpenxBidder(cfg.Adapters[string(openrtb_ext.BidderOpenx)].Endpoint), openrtb_ext.BidderPubmatic: pubmatic.NewPubmaticBidder(client, cfg.Adapters[string(openrtb_ext.BidderPubmatic)].Endpoint), openrtb_ext.BidderRhythmone: rhythmone.NewRhythmoneBidder(cfg.Adapters[string(openrtb_ext.BidderRhythmone)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index de5d23904b6..98c9c073977 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -42,6 +42,7 @@ const ( BidderImprovedigital BidderName = "improvedigital" BidderIx BidderName = "ix" BidderLifestreet BidderName = "lifestreet" + BidderLockerDome BidderName = "lockerdome" BidderMgid BidderName = "mgid" BidderOpenx BidderName = "openx" BidderPubmatic BidderName = "pubmatic" @@ -87,6 +88,7 @@ var BidderMap = map[string]BidderName{ "improvedigital": BidderImprovedigital, "ix": BidderIx, "lifestreet": BidderLifestreet, + "lockerdome": BidderLockerDome, "openx": BidderOpenx, "mgid": BidderMgid, "pubmatic": BidderPubmatic, diff --git a/openrtb_ext/imp_lockerdome.go b/openrtb_ext/imp_lockerdome.go new file mode 100644 index 00000000000..8f228f12458 --- /dev/null +++ b/openrtb_ext/imp_lockerdome.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtImpLockerDome defines the contract for bidrequest.imp[i].ext.lockerdome +type ExtImpLockerDome struct { + // LockerDome params + AdUnitId string `json:"adUnitId"` +} diff --git a/static/bidder-info/lockerdome.yaml b/static/bidder-info/lockerdome.yaml new file mode 100644 index 00000000000..b005e7c1f85 --- /dev/null +++ b/static/bidder-info/lockerdome.yaml @@ -0,0 +1,9 @@ +maintainer: + email: "bidding@lockerdome.com" +capabilities: + app: + mediaTypes: + - banner + site: + mediaTypes: + - banner diff --git a/static/bidder-params/lockerdome.json b/static/bidder-params/lockerdome.json new file mode 100644 index 00000000000..0ab9df3c635 --- /dev/null +++ b/static/bidder-params/lockerdome.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "LockerDome Adapter Params", + "description": "A schema which validates params accepted by the LockerDome adapter", + "type": "object", + "properties": { + "adUnitId": { + "type": "string", + "description": "A tag which identifies the LockerDome ad unit by adUnitId" + } + }, + "required": ["adUnitId"] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 03229489f67..2ef4b76ad67 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -28,6 +28,7 @@ import ( "github.com/prebid/prebid-server/adapters/improvedigital" "github.com/prebid/prebid-server/adapters/ix" "github.com/prebid/prebid-server/adapters/lifestreet" + "github.com/prebid/prebid-server/adapters/lockerdome" "github.com/prebid/prebid-server/adapters/mgid" "github.com/prebid/prebid-server/adapters/openx" "github.com/prebid/prebid-server/adapters/pubmatic" @@ -78,6 +79,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderImprovedigital, improvedigital.NewImprovedigitalSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderIx, ix.NewIxSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderLifestreet, lifestreet.NewLifestreetSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderLockerDome, lockerdome.NewLockerDomeSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderOpenx, openx.NewOpenxSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderPubmatic, pubmatic.NewPubmaticSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderPulsepoint, pulsepoint.NewPulsepointSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 4546e3664bf..1207f5c49f0 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -33,6 +33,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderImprovedigital): syncConfig, string(openrtb_ext.BidderIx): syncConfig, string(openrtb_ext.BidderLifestreet): syncConfig, + string(openrtb_ext.BidderLockerDome): syncConfig, string(openrtb_ext.BidderMgid): syncConfig, string(openrtb_ext.BidderOpenx): syncConfig, string(openrtb_ext.BidderPubmatic): syncConfig,