diff --git a/adapters/ucfunnel/params_test.go b/adapters/ucfunnel/params_test.go new file mode 100644 index 00000000000..4faec8739da --- /dev/null +++ b/adapters/ucfunnel/params_test.go @@ -0,0 +1,47 @@ +package ucfunnel + +import ( + "encoding/json" + "github.com/prebid/prebid-server/openrtb_ext" + "testing" +) + +// This file actually intends to test static/bidder-params/ucfunnel.json +// +// These also validate the format of the external API: request.imp[i].ext.ucfunnel + +// TestValidParams makes sure that the ucfunnel 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.BidderUcfunnel, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected ucfunnel params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the ucfunnel 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.BidderUcfunnel, json.RawMessage(invalidParam)); err != nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"adunitid": "ad-83444226E44368D1E32E49EEBE6D29","partnerid": "par-2EDDB423AA24474188B843EE4842932"}`, +} + +var invalidParams = []string{ + `{"adunitid": "","partnerid": ""}`, +} diff --git a/adapters/ucfunnel/ucfunnel.go b/adapters/ucfunnel/ucfunnel.go new file mode 100644 index 00000000000..2c64e81205b --- /dev/null +++ b/adapters/ucfunnel/ucfunnel.go @@ -0,0 +1,150 @@ +package ucfunnel + +import ( + "encoding/json" + "fmt" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" + "net/http" + "net/url" +) + +type UcfunnelAdapter struct { + URI string +} + +func NewUcfunnelBidder(endpoint string) *UcfunnelAdapter { + return &UcfunnelAdapter{ + URI: endpoint} +} + +func (a *UcfunnelAdapter) Name() string { + return "ucfunnel" +} + +func (a *UcfunnelAdapter) SkipNoCookies() bool { + return false +} + +func (a *UcfunnelAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil + } + + if response.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + if response.StatusCode != http.StatusOK { + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} + } + + var errs []error + var bidResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + var bidReq openrtb.BidRequest + if err := json.Unmarshal(externalRequest.Body, &bidReq); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + bidType := getBidType(bidReq, sb.Bid[i].ImpID) + if (bidType == openrtb_ext.BidTypeBanner) || (bidType == openrtb_ext.BidTypeVideo) { + b := &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: bidType, + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + } + return bidResponse, errs +} + +func (a *UcfunnelAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + errs := make([]error, 0, len(request.Imp)) + + // If all the requests were malformed, don't bother making a server call with no impressions. + if len(request.Imp) == 0 { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("No impression in the bid request\n"), + }} + } + + partnerId, partnerErr := getPartnerId(request) + if partnerErr != nil { + return nil, partnerErr + } + + reqJSON, err := json.Marshal(request) + if err != nil { + return nil, []error{err} + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json") + + uri := a.URI + "/" + url.PathEscape(partnerId) + "/request" + return []*adapters.RequestData{{ + Method: "POST", + Uri: uri, + Body: reqJSON, + Headers: headers, + }}, errs +} + +func getPartnerId(request *openrtb.BidRequest) (string, []error) { + var ext ExtBidderUcfunnel + var errs = []error{} + err := json.Unmarshal(request.Imp[0].Ext, &ext) + if err != nil { + errs = append(errs, err) + return "", errs + } + errs = checkBidderParameter(ext) + if errs != nil { + return "", errs + } + return ext.Bidder.PartnerId, nil +} + +func checkBidderParameter(ext ExtBidderUcfunnel) []error { + var errs = []error{} + if len(ext.Bidder.PartnerId) == 0 || len(ext.Bidder.AdUnitId) == 0 { + errs = append(errs, fmt.Errorf("No PartnerId or AdUnitId in the bid request\n")) + return errs + } + return nil +} + +func getBidType(bidReq openrtb.BidRequest, impid string) openrtb_ext.BidType { + for i := range bidReq.Imp { + if bidReq.Imp[i].ID == impid { + if bidReq.Imp[i].Banner != nil { + return openrtb_ext.BidTypeBanner + } else if bidReq.Imp[i].Video != nil { + return openrtb_ext.BidTypeVideo + } else if bidReq.Imp[i].Audio != nil { + return openrtb_ext.BidTypeAudio + } else if bidReq.Imp[i].Native != nil { + return openrtb_ext.BidTypeNative + } + } + } + return openrtb_ext.BidTypeNative +} + +type ExtBidderUcfunnel struct { + Bidder openrtb_ext.ExtImpUcfunnel `json:"bidder"` +} diff --git a/adapters/ucfunnel/ucfunnel_test.go b/adapters/ucfunnel/ucfunnel_test.go new file mode 100644 index 00000000000..60d796fdf99 --- /dev/null +++ b/adapters/ucfunnel/ucfunnel_test.go @@ -0,0 +1,163 @@ +package ucfunnel + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/adapters/adapterstest" +) + +func TestUcfunnelAdapterNames(t *testing.T) { + adapter := NewUcfunnelBidder("http://localhost/bid") + adapterstest.VerifyStringValue(adapter.Name(), "ucfunnel", t) +} + +func TestSkipNoCookies(t *testing.T) { + adapter := NewUcfunnelBidder("http://localhost/bid") + status := adapter.SkipNoCookies() + if status != false { + t.Errorf("actual = %t expected != %t", status, false) + } +} + +func TestMakeRequests(t *testing.T) { + + imp := openrtb.Imp{ + ID: "1234", + Banner: &openrtb.Banner{}, + } + imp2 := openrtb.Imp{ + ID: "1235", + Video: &openrtb.Video{}, + } + + imp3 := openrtb.Imp{ + ID: "1236", + Audio: &openrtb.Audio{}, + } + + imp4 := openrtb.Imp{ + ID: "1237", + Native: &openrtb.Native{}, + } + imp5 := openrtb.Imp{ + ID: "1237", + Native: &openrtb.Native{}, + } + + internalRequest01 := openrtb.BidRequest{Imp: []openrtb.Imp{}} + internalRequest02 := openrtb.BidRequest{Imp: []openrtb.Imp{imp, imp2, imp3, imp4, imp5}} + internalRequest03 := openrtb.BidRequest{Imp: []openrtb.Imp{imp, imp2, imp3, imp4, imp5}} + + internalRequest03.Imp[0].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[1].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[2].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[3].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[4].Ext = []byte(`{"bidder": {"adunitid": "aa","partnerid": ""}}`) + + adapter := NewUcfunnelBidder("http://localhost/bid") + + var testCases = []struct { + in []openrtb.BidRequest + out1 [](int) + out2 [](bool) + }{ + { + in: []openrtb.BidRequest{internalRequest01, internalRequest02, internalRequest03}, + out1: [](int){1, 1, 0}, + out2: [](bool){false, false, true}, + }, + } + + for idx := range testCases { + for i := range testCases[idx].in { + RequestData, err := adapter.MakeRequests(&testCases[idx].in[i], nil) + if ((RequestData == nil) == testCases[idx].out2[i]) && (len(err) == testCases[idx].out1[i]) { + t.Errorf("actual = %v expected = %v", len(err), testCases[idx].out1[i]) + } + } + } +} + +func TestMakeBids(t *testing.T) { + imp := openrtb.Imp{ + ID: "1234", + Banner: &openrtb.Banner{}, + } + imp2 := openrtb.Imp{ + ID: "1235", + Video: &openrtb.Video{}, + } + + imp3 := openrtb.Imp{ + ID: "1236", + Audio: &openrtb.Audio{}, + } + + imp4 := openrtb.Imp{ + ID: "1237", + Native: &openrtb.Native{}, + } + imp5 := openrtb.Imp{ + ID: "1237", + Native: &openrtb.Native{}, + } + + internalRequest03 := openrtb.BidRequest{Imp: []openrtb.Imp{imp, imp2, imp3, imp4, imp5}} + internalRequest04 := openrtb.BidRequest{Imp: []openrtb.Imp{imp}} + + internalRequest03.Imp[0].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[1].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[2].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[3].Ext = []byte(`{"bidder": {"adunitid": "ad-488663D474E44841E8A293379892348","partnerid": "par-7E6D2DB9A8922AB07B44A444D2BA67"}}`) + internalRequest03.Imp[4].Ext = []byte(`{"bidder": {"adunitid": "aa","partnerid": ""}}`) + internalRequest04.Imp[0].Ext = []byte(`{"bidder": {"adunitid": "0"}}`) + + mockResponse200 := adapters.ResponseData{StatusCode: 200, Body: json.RawMessage(`{"seatbid": [{"bid": [{"impid": "1234"}]},{"bid": [{"impid": "1235"}]},{"bid": [{"impid": "1236"}]},{"bid": [{"impid": "1237"}]}]}`)} + mockResponse203 := adapters.ResponseData{StatusCode: 203, Body: json.RawMessage(`{"seatbid":[{"bid":[{"impid":"1234"}]},{"bid":[{"impid":"1235"}]}]}`)} + mockResponse204 := adapters.ResponseData{StatusCode: 204, Body: json.RawMessage(`{"seatbid":[{"bid":[{"impid":"1234"}]},{"bid":[{"impid":"1235"}]}]}`)} + mockResponse400 := adapters.ResponseData{StatusCode: 400, Body: json.RawMessage(`{"seatbid":[{"bid":[{"impid":"1234"}]},{"bid":[{"impid":"1235"}]}]}`)} + mockResponseError := adapters.ResponseData{StatusCode: 200, Body: json.RawMessage(`{"seatbid":[{"bid":[{"im236"}],{"bid":[{"impid":"1237}]}`)} + + RequestData01 := adapters.RequestData{Method: "POST", Body: []byte(`{"imp":[{"id":"1234","banner":{}},{"id":"1235","video":{}},{"id":"1236","audio":{}},{"id":"1237","native":{}}]}`)} + RequestData02 := adapters.RequestData{Method: "POST", Body: []byte(`{"imp":[{"id":"1234","banne"1235","video":{}},{"id":"1236","audio":{}},{"id":"1237","native":{}}]}`)} + + adapter := NewUcfunnelBidder("http://localhost/bid") + + var testCases = []struct { + in1 []openrtb.BidRequest + in2 []adapters.RequestData + in3 []adapters.ResponseData + out1 [](bool) + out2 [](bool) + }{ + { + in1: []openrtb.BidRequest{internalRequest03, internalRequest03, internalRequest03, internalRequest03, internalRequest03, internalRequest04}, + in2: []adapters.RequestData{RequestData01, RequestData01, RequestData01, RequestData01, RequestData01, RequestData02}, + in3: []adapters.ResponseData{mockResponse200, mockResponse203, mockResponse204, mockResponse400, mockResponseError, mockResponse200}, + out1: [](bool){true, false, false, false, false, false}, + out2: [](bool){false, true, false, true, true, true}, + }, + } + + for idx := range testCases { + for i := range testCases[idx].in1 { + BidderResponse, err := adapter.MakeBids(&testCases[idx].in1[i], &testCases[idx].in2[i], &testCases[idx].in3[i]) + + if (BidderResponse == nil) == testCases[idx].out1[i] { + fmt.Println(i) + fmt.Println("BidderResponse") + t.Errorf("actual = %t expected == %v", (BidderResponse == nil), testCases[idx].out1[i]) + } + + if (err == nil) == testCases[idx].out2[i] { + fmt.Println(i) + fmt.Println("error") + t.Errorf("actual = %t expected == %v", err, testCases[idx].out2[i]) + } + } + } +} diff --git a/adapters/ucfunnel/ucfunneltest/exemplary/ucfunnel.json b/adapters/ucfunnel/ucfunneltest/exemplary/ucfunnel.json new file mode 100644 index 00000000000..2a7e4b2b861 --- /dev/null +++ b/adapters/ucfunnel/ucfunneltest/exemplary/ucfunnel.json @@ -0,0 +1,103 @@ +{ + "imp": [ + { + "id": "1", + "banner": { + "w": 970, + "h": 250, + "mimes": [ + "image/gif", + "image/jpeg", + "image/png", + "text/html", + "text/javascript", + "application/javascript" + ], + "pos": 1, + "battr": [ + 6, + 7 + ], + "topframe": 1 + }, + "instl": 0, + "displaymanager": "aralego.com", + "displaymanagerver": "v1.0.0", + "secure": 0, + "bidfloor": 0.01, + "bidfloorcur": "USD", + "exp": 3600, + "ext":{ + "ucfunnel":{ + "adunitid":"ad-BE7E9EB323E9996218A733887B6E924" + } + } + } + ], + "id": "c901f218-4ca6-480b-97dc-c6fd50e24544", + "at": 2, + "tmax": 300, + "bcat": [ + "IAB11-1" + ], + "badv": [ + "abc.com", + "cbn.com", + "xyz.com" + ], + "regs": { + "coppa": 0, + "ext": { + "gdpr": 0 + }, + "us_privacy": "1--" + }, + "site": { + "id": "5b88fd05beffd764bb0f7a3a", + "name": "test", + "page": "http://127.0.0.1:8000/tmp66.html", + "domain": "127.0.0.1", + "cat": [ + "IAB1" + ], + "publisher": { + "id": "par-7E6D2DB9A8922AB07B44A444D2BA67" + } + }, + "device": { + "w": 1440, + "h": 900, + "dnt": 1, + "ip": "127.0.0.1", + "js": 1, + "os": "MacOS", + "osv": "10.15.1", + "ua": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.97 Safari/537.36", + "language": "zh-TW", + "devicetype": 2, + "geo": { + "country": "unknown", + "type": 2 + } + }, + "user": { + "id": "e7bf9b85-9554-441c-964e-c8112d35d17b" + }, + "source": { + "fd": 1, + "ext": { + "schain": { + "complete": 1, + "ver": "1.0", + "nodes": [ + { + "asi": "aralego.com", + "sid": "par-7E6D2DB9A8922AB07B44A444D2BA67", + "rid": "c8f800c2-d285-4cb9-8fc9-f95df52f6e0c", + "hp": 1 + } + ] + } + } + } +} \ No newline at end of file diff --git a/adapters/ucfunnel/ucfunneltest/params/race/banner.json b/adapters/ucfunnel/ucfunneltest/params/race/banner.json new file mode 100644 index 00000000000..2c8c2e1e198 --- /dev/null +++ b/adapters/ucfunnel/ucfunneltest/params/race/banner.json @@ -0,0 +1,5 @@ +{ + "adunitid": "ad-83444226E44368D1E32E49EEBE6D29", + "partnerid": "par-2EDDB423AA24474188B843EE4842932" +} + \ No newline at end of file diff --git a/adapters/ucfunnel/ucfunneltest/params/race/video.json b/adapters/ucfunnel/ucfunneltest/params/race/video.json new file mode 100644 index 00000000000..0a562b34aa1 --- /dev/null +++ b/adapters/ucfunnel/ucfunneltest/params/race/video.json @@ -0,0 +1,5 @@ +{ + "adunitid": "ad-E2B22B678D6A664E092824848D26BB2", + "partnerid": "par-2EDDB423AA24474188B843EE4842932" +} + \ No newline at end of file diff --git a/adapters/ucfunnel/usersync.go b/adapters/ucfunnel/usersync.go new file mode 100644 index 00000000000..92eba0d73e0 --- /dev/null +++ b/adapters/ucfunnel/usersync.go @@ -0,0 +1,12 @@ +package ucfunnel + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewUcfunnelSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("ucfunnel", 607, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/ucfunnel/usersync_test.go b/adapters/ucfunnel/usersync_test.go new file mode 100644 index 00000000000..45320b8cac1 --- /dev/null +++ b/adapters/ucfunnel/usersync_test.go @@ -0,0 +1,30 @@ +package ucfunnel + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestUcfunnelSyncer(t *testing.T) { + syncURL := "//sync.aralego.com/idsync?gdpr={{.GDPR}}&redirect=externalURL.com%2Fsetuid%3Fbidder%3Ducfunnel%26uid%3DSspCookieUserId" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewUcfunnelSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "0", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "//sync.aralego.com/idsync?gdpr=0&redirect=externalURL.com%2Fsetuid%3Fbidder%3Ducfunnel%26uid%3DSspCookieUserId", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 607, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 943d18a95de..a8056a1651b 100644 --- a/config/config.go +++ b/config/config.go @@ -528,6 +528,7 @@ func (cfg *Configuration) setDerivedDefaults() { // openrtb_ext.BidderTappx doesn't have a good default. setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTriplelift, "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderTripleliftNative, "https://eb2.3lift.com/getuid?gdpr={{.GDPR}}&cmp_cs={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dtriplelift_native%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderUcfunnel, "https://sync.aralego.com/idsync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&usprivacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Ducfunnel%26uid%3DSspCookieUserId") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderUnruly, "https://usermatch.targeting.unrulymedia.com/pbsync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dunruly%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderVisx, "https://t.visx.net/s2s_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dvisx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D") // openrtb_ext.BidderVrtcal doesn't have a good default. @@ -716,6 +717,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.triplelift_native.disabled", true) v.SetDefault("adapters.triplelift_native.extra_info", "{\"publisher_whitelist\":[]}") v.SetDefault("adapters.triplelift.endpoint", "https://tlx.3lift.com/s2s/auction?supplier_id=20") + v.SetDefault("adapters.ucfunnel.endpoint", "http://apac-hk-adx.aralego.com/prebid") v.SetDefault("adapters.unruly.endpoint", "http://targeting.unrulymedia.com/openrtb/2.2") v.SetDefault("adapters.verizonmedia.disabled", true) v.SetDefault("adapters.visx.endpoint", "https://t.visx.net/s2s_bid?wrapperType=s2s_prebid_standard") diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 95f5b7f5882..c000f895e00 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -53,6 +53,7 @@ import ( "github.com/prebid/prebid-server/adapters/tappx" "github.com/prebid/prebid-server/adapters/triplelift" "github.com/prebid/prebid-server/adapters/triplelift_native" + "github.com/prebid/prebid-server/adapters/ucfunnel" "github.com/prebid/prebid-server/adapters/unruly" "github.com/prebid/prebid-server/adapters/verizonmedia" "github.com/prebid/prebid-server/adapters/visx" @@ -119,6 +120,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderTappx: tappx.NewTappxBidder(client, cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderTappx))].Endpoint), openrtb_ext.BidderTriplelift: triplelift.NewTripleliftBidder(client, cfg.Adapters[string(openrtb_ext.BidderTriplelift)].Endpoint), openrtb_ext.BidderTripleliftNative: triplelift_native.NewTripleliftNativeBidder(client, cfg.Adapters[string(openrtb_ext.BidderTripleliftNative)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderTripleliftNative)].ExtraAdapterInfo), + openrtb_ext.BidderUcfunnel: ucfunnel.NewUcfunnelBidder(cfg.Adapters[string(openrtb_ext.BidderUcfunnel)].Endpoint), openrtb_ext.BidderUnruly: unruly.NewUnrulyBidder(client, cfg.Adapters[string(openrtb_ext.BidderUnruly)].Endpoint), openrtb_ext.BidderVerizonMedia: verizonmedia.NewVerizonMediaBidder(client, cfg.Adapters[string(openrtb_ext.BidderVerizonMedia)].Endpoint), openrtb_ext.BidderVisx: visx.NewVisxBidder(cfg.Adapters[string(openrtb_ext.BidderVisx)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 7a3f24eb07f..197260d511e 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -66,6 +66,7 @@ const ( BidderTappx BidderName = "tappx" BidderTriplelift BidderName = "triplelift" BidderTripleliftNative BidderName = "triplelift_native" + BidderUcfunnel BidderName = "ucfunnel" BidderUnruly BidderName = "unruly" BidderVerizonMedia BidderName = "verizonmedia" BidderVisx BidderName = "visx" @@ -121,6 +122,7 @@ var BidderMap = map[string]BidderName{ "tappx": BidderTappx, "triplelift": BidderTriplelift, "triplelift_native": BidderTripleliftNative, + "ucfunnel": BidderUcfunnel, "unruly": BidderUnruly, "verizonmedia": BidderVerizonMedia, "visx": BidderVisx, diff --git a/openrtb_ext/imp_ucfunnel.go b/openrtb_ext/imp_ucfunnel.go new file mode 100644 index 00000000000..408c1e0a35e --- /dev/null +++ b/openrtb_ext/imp_ucfunnel.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtImpUcfunnel defines the contract for bidrequest.imp[i].ext.ucfunnel +type ExtImpUcfunnel struct { + AdUnitId string `json:"adunitid"` + PartnerId string `json:"partnerid"` +} diff --git a/static/bidder-info/ucfunnel.yaml b/static/bidder-info/ucfunnel.yaml new file mode 100644 index 00000000000..288b0b3f1b8 --- /dev/null +++ b/static/bidder-info/ucfunnel.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "support@ucfunnel.com" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/ucfunnel.json b/static/bidder-params/ucfunnel.json new file mode 100644 index 00000000000..d39d006cf1f --- /dev/null +++ b/static/bidder-params/ucfunnel.json @@ -0,0 +1,17 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Ucfunnel Adapter Params", + "description": "A schema which validates params accepted by the Ucfunnel adapter", + "type": "object", + "properties": { + "adunitid": { + "type": "string", + "description": "ID for ad unit" + }, + "partnerid": { + "type": "string", + "description": "ID for partner" + } + }, + "required": ["partnerid"] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 5447cd28800..347857e2024 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -48,6 +48,7 @@ import ( "github.com/prebid/prebid-server/adapters/synacormedia" "github.com/prebid/prebid-server/adapters/triplelift" "github.com/prebid/prebid-server/adapters/triplelift_native" + "github.com/prebid/prebid-server/adapters/ucfunnel" "github.com/prebid/prebid-server/adapters/unruly" "github.com/prebid/prebid-server/adapters/verizonmedia" "github.com/prebid/prebid-server/adapters/visx" @@ -106,6 +107,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderSynacormedia, synacormedia.NewSynacorMediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderTriplelift, triplelift.NewTripleliftSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderTripleliftNative, triplelift_native.NewTripleliftSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderUcfunnel, ucfunnel.NewUcfunnelSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderUnruly, unruly.NewUnrulySyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderVerizonMedia, verizonmedia.NewVerizonMediaSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderVisx, visx.NewVisxSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index ded8fd2bd78..f00d4c9c7b5 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -56,6 +56,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderSynacormedia): syncConfig, string(openrtb_ext.BidderTriplelift): syncConfig, string(openrtb_ext.BidderTripleliftNative): syncConfig, + string(openrtb_ext.BidderUcfunnel): syncConfig, string(openrtb_ext.BidderUnruly): syncConfig, string(openrtb_ext.BidderVerizonMedia): syncConfig, string(openrtb_ext.BidderVisx): syncConfig,