diff --git a/adapters/avocet/avocet.go b/adapters/avocet/avocet.go new file mode 100644 index 00000000000..918fc23e894 --- /dev/null +++ b/adapters/avocet/avocet.go @@ -0,0 +1,124 @@ +package avocet + +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" +) + +// AvocetAdapter implements a adapters.Bidder compatible with the Avocet advertising platform. +type AvocetAdapter struct { + // Endpoint is a http endpoint to use when making requests to the Avocet advertising platform. + Endpoint string +} + +func (a *AvocetAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + if len(request.Imp) == 0 { + return nil, nil + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + body, err := json.Marshal(request) + if err != nil { + return nil, []error{&errortypes.FailedToRequestBids{ + Message: err.Error(), + }} + } + reqData := &adapters.RequestData{ + Method: http.MethodPost, + Uri: a.Endpoint, + Body: body, + Headers: headers, + } + return []*adapters.RequestData{reqData}, nil +} + +type avocetBidExt struct { + Avocet avocetBidExtension `json:"avocet"` +} + +type avocetBidExtension struct { + Duration int `json:"duration"` + DealPriority int `json:"deal_priority"` +} + +func (a *AvocetAdapter) 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.StatusOK { + var errStr string + if len(response.Body) > 0 { + errStr = string(response.Body) + } else { + errStr = "no response body" + } + return nil, []error{&errortypes.BadServerResponse{ + Message: fmt.Sprintf("received status code: %v error: %s", response.StatusCode, errStr), + }} + } + + var br openrtb.BidResponse + err := json.Unmarshal(response.Body, &br) + if err != nil { + return nil, []error{&errortypes.BadServerResponse{ + Message: err.Error(), + }} + } + var errs []error + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) + for i := range br.SeatBid { + for j := range br.SeatBid[i].Bid { + var ext avocetBidExt + if len(br.SeatBid[i].Bid[j].Ext) > 0 { + err := json.Unmarshal(br.SeatBid[i].Bid[j].Ext, &ext) + if err != nil { + errs = append(errs, err) + continue + } + } + tbid := &adapters.TypedBid{ + Bid: &br.SeatBid[i].Bid[j], + DealPriority: ext.Avocet.DealPriority, + } + tbid.BidType = getBidType(br.SeatBid[i].Bid[j], ext) + if tbid.BidType == openrtb_ext.BidTypeVideo { + tbid.BidVideo = &openrtb_ext.ExtBidPrebidVideo{ + Duration: ext.Avocet.Duration, + } + } + bidResponse.Bids = append(bidResponse.Bids, tbid) + } + } + return bidResponse, nil +} + +// getBidType returns the openrtb_ext.BidType for the provided bid. +func getBidType(bid openrtb.Bid, ext avocetBidExt) openrtb_ext.BidType { + if ext.Avocet.Duration != 0 { + return openrtb_ext.BidTypeVideo + } + switch bid.API { + case openrtb.APIFrameworkVPAID10, openrtb.APIFrameworkVPAID20: + return openrtb_ext.BidTypeVideo + default: + return openrtb_ext.BidTypeBanner + } +} + +// NewAvocetAdapter returns a new AvocetAdapter using the provided endpoint. +func NewAvocetAdapter(endpoint string) *AvocetAdapter { + return &AvocetAdapter{ + Endpoint: endpoint, + } +} diff --git a/adapters/avocet/avocet/exemplary/banner.json b/adapters/avocet/avocet/exemplary/banner.json new file mode 100644 index 00000000000..b5e308ea725 --- /dev/null +++ b/adapters/avocet/avocet/exemplary/banner.json @@ -0,0 +1,106 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "placement": "5ea9601ac865f911007f1b6a" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bid.staging.avct.cloud/ortb/bid/5e722ee9bd6df11d063a8013", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + } + ] + }, + "ext": { + "bidder": { + "placement": "5ea9601ac865f911007f1b6a" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "bidid": "dd87f80c-16a0-43c8-a673-b94b3ea4d417", + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "adm": "", + "adomain": ["avocet.io"], + "cid": "5b51e2d689654741306813a4", + "crid": "5b51e49634f2021f127ff7c9", + "h": 250, + "id": "bc708396-9202-437b-b726-08b9864cb8b8", + "impid": "test-imp-id", + "iurl": "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5b51e49634f2021f127ff7c9.jpeg", + "language": "en", + "price": 15.64434783, + "w": 300 + } + ], + "seat": "TEST_SEAT_ID" + } + ] + } + } + } + ], + + "expectedBids": [ + { + "bid": { + "adm": "", + "adomain": ["avocet.io"], + "cid": "5b51e2d689654741306813a4", + "crid": "5b51e49634f2021f127ff7c9", + "h": 250, + "id": "bc708396-9202-437b-b726-08b9864cb8b8", + "impid": "test-imp-id", + "iurl": "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5b51e49634f2021f127ff7c9.jpeg", + "language": "en", + "price": 15.64434783, + "w": 300 + }, + "type": "banner" + } + ] +} diff --git a/adapters/avocet/avocet/exemplary/video.json b/adapters/avocet/avocet/exemplary/video.json new file mode 100644 index 00000000000..2398256b0dd --- /dev/null +++ b/adapters/avocet/avocet/exemplary/video.json @@ -0,0 +1,104 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "placement": "5ea9601ac865f911007f1b6a" + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://bid.staging.avct.cloud/ortb/bid/5e722ee9bd6df11d063a8013", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [2, 5], + "w": 1920, + "h": 1080 + }, + "ext": { + "bidder": { + "placement": "5ea9601ac865f911007f1b6a" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "bidid": "a0eec3aa-f9f6-42fb-9aa4-f1b5656d4f42", + "id": "749d36d7-c993-455f-aefd-ffd8a7e3ccf", + "seatbid": [ + { + "bid": [ + { + "adm": "Avocet", + "adomain": ["avocet.io"], + "cid": "5b51e2d689654741306813a4", + "crid": "5ec530e32d57fe1100f17d87", + "h": 396, + "id": "3d4c2d45-5a8c-43b8-9e15-4f48ac45204f", + "impid": "dfp-ad--top-above-nav", + "iurl": "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5ec530e32d57fe1100f17d87.jpeg", + "language": "en", + "price": 15.64434783, + "w": 600, + "ext": { + "avocet": { + "duration": 30 + } + } + } + ], + "seat": "TEST_SEAT_ID" + } + ] + } + } + } + ], + + "expectedBids": [ + { + "bid": { + "adm": "Avocet", + "adomain": ["avocet.io"], + "cid": "5b51e2d689654741306813a4", + "crid": "5ec530e32d57fe1100f17d87", + "h": 396, + "id": "3d4c2d45-5a8c-43b8-9e15-4f48ac45204f", + "impid": "dfp-ad--top-above-nav", + "iurl": "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5ec530e32d57fe1100f17d87.jpeg", + "language": "en", + "price": 15.64434783, + "w": 600, + "ext": { + "avocet": { + "duration": 30 + } + } + }, + "type": "video" + } + ] +} diff --git a/adapters/avocet/avocet_test.go b/adapters/avocet/avocet_test.go new file mode 100644 index 00000000000..ff2159bf406 --- /dev/null +++ b/adapters/avocet/avocet_test.go @@ -0,0 +1,301 @@ +package avocet + +import ( + "encoding/json" + "net/http" + "reflect" + "testing" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +func TestJsonSamples(t *testing.T) { + adapterstest.RunJSONBidderTest(t, "avocet", NewAvocetAdapter("https://bid.staging.avct.cloud/ortb/bid/5e722ee9bd6df11d063a8013")) +} + +func TestAvocetAdapter_MakeRequests(t *testing.T) { + type fields struct { + Endpoint string + } + type args struct { + request *openrtb.BidRequest + reqInfo *adapters.ExtraRequestInfo + } + type reqData []*adapters.RequestData + tests := []struct { + name string + fields fields + args args + want []*adapters.RequestData + wantErrs []error + }{ + { + name: "return nil if zero imps", + fields: fields{Endpoint: "https://bid.avct.cloud"}, + args: args{ + &openrtb.BidRequest{}, + nil, + }, + want: nil, + wantErrs: nil, + }, + { + name: "makes POST request with JSON content", + fields: fields{Endpoint: "https://bid.avct.cloud"}, + args: args{ + &openrtb.BidRequest{Imp: []openrtb.Imp{{}}}, + nil, + }, + want: reqData{ + &adapters.RequestData{ + Method: http.MethodPost, + Uri: "https://bid.avct.cloud", + Body: []byte(`{"id":"","imp":[{"id":""}]}`), + Headers: map[string][]string{ + "Accept": {"application/json"}, + "Content-Type": {"application/json;charset=utf-8"}, + }, + }, + }, + wantErrs: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &AvocetAdapter{ + Endpoint: tt.fields.Endpoint, + } + got, got1 := a.MakeRequests(tt.args.request, tt.args.reqInfo) + if len(got) != len(tt.want) { + t.Errorf("AvocetAdapter.MakeRequests() got %v requests, wanted %v requests", len(got), len(tt.want)) + } + if len(got) == len(tt.want) { + for i := range tt.want { + if !reflect.DeepEqual(got[i], tt.want[i]) { + t.Errorf("AvocetAdapter.MakeRequests() got = %v, want %v", got[i], tt.want[i]) + } + } + } + if !reflect.DeepEqual(got1, tt.wantErrs) { + t.Errorf("AvocetAdapter.MakeRequests() got1 = %v, want %v", got1, tt.wantErrs) + } + }) + } +} + +func TestAvocetAdapter_MakeBids(t *testing.T) { + type fields struct { + Endpoint string + } + type args struct { + internalRequest *openrtb.BidRequest + externalRequest *adapters.RequestData + response *adapters.ResponseData + } + tests := []struct { + name string + fields fields + args args + want *adapters.BidderResponse + errs []error + }{ + { + name: "204 No Content indicates no bids", + fields: fields{Endpoint: "https://bid.avct.cloud"}, + args: args{ + nil, + nil, + &adapters.ResponseData{StatusCode: http.StatusNoContent}, + }, + want: nil, + errs: nil, + }, + { + name: "Non-200 return error", + fields: fields{Endpoint: "https://bid.avct.cloud"}, + args: args{ + nil, + nil, + &adapters.ResponseData{StatusCode: http.StatusBadRequest, Body: []byte("message")}, + }, + want: nil, + errs: []error{&errortypes.BadServerResponse{Message: "received status code: 400 error: message"}}, + }, + { + name: "200 response containing banner bids", + fields: fields{Endpoint: "https://bid.avct.cloud"}, + args: args{ + nil, + nil, + &adapters.ResponseData{StatusCode: http.StatusOK, Body: validBannerBidResponseBody}, + }, + want: &adapters.BidderResponse{ + Currency: "USD", + Bids: []*adapters.TypedBid{ + { + Bid: &validBannerBid, + BidType: openrtb_ext.BidTypeBanner, + }, + }, + }, + errs: nil, + }, + { + name: "200 response containing video bids", + fields: fields{Endpoint: "https://bid.avct.cloud"}, + args: args{ + nil, + nil, + &adapters.ResponseData{StatusCode: http.StatusOK, Body: validVideoBidResponseBody}, + }, + want: &adapters.BidderResponse{ + Currency: "USD", + Bids: []*adapters.TypedBid{ + { + Bid: &validVideoBid, + BidType: openrtb_ext.BidTypeVideo, + BidVideo: &openrtb_ext.ExtBidPrebidVideo{ + Duration: 30, + }, + }, + }, + }, + errs: nil, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + a := &AvocetAdapter{ + Endpoint: tt.fields.Endpoint, + } + got, got1 := a.MakeBids(tt.args.internalRequest, tt.args.externalRequest, tt.args.response) + if !reflect.DeepEqual(got, tt.want) { + gotb, _ := json.Marshal(got) + wantb, _ := json.Marshal(tt.want) + t.Errorf("AvocetAdapter.MakeBids() got = %s, want %s", string(gotb), string(wantb)) + } + if !reflect.DeepEqual(got1, tt.errs) { + t.Errorf("AvocetAdapter.MakeBids() got1 = %v, want %v", got1, tt.errs) + } + }) + } +} + +func Test_getBidType(t *testing.T) { + type args struct { + bid openrtb.Bid + ext avocetBidExt + } + tests := []struct { + name string + args args + want openrtb_ext.BidType + }{ + { + name: "VPAID 1.0", + args: args{openrtb.Bid{API: openrtb.APIFrameworkVPAID10}, avocetBidExt{}}, + want: openrtb_ext.BidTypeVideo, + }, + { + name: "VPAID 2.0", + args: args{openrtb.Bid{API: openrtb.APIFrameworkVPAID20}, avocetBidExt{}}, + want: openrtb_ext.BidTypeVideo, + }, + { + name: "other", + args: args{openrtb.Bid{}, avocetBidExt{}}, + want: openrtb_ext.BidTypeBanner, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := getBidType(tt.args.bid, tt.args.ext); !reflect.DeepEqual(got, tt.want) { + t.Errorf("getBidType() = %v, want %v", got, tt.want) + } + }) + } +} + +var validBannerBid = openrtb.Bid{ + AdM: "", + ADomain: []string{"avocet.io"}, + CID: "5b51e2d689654741306813a4", + CrID: "5b51e49634f2021f127ff7c9", + H: 250, + ID: "bc708396-9202-437b-b726-08b9864cb8b8", + ImpID: "test-imp-id", + IURL: "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5b51e49634f2021f127ff7c9.jpeg", + Language: "en", + Price: 15.64434783, + W: 300, +} + +var validBannerBidResponseBody = []byte(`{ + "bidid": "dd87f80c-16a0-43c8-a673-b94b3ea4d417", + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "adm": "", + "adomain": ["avocet.io"], + "cid": "5b51e2d689654741306813a4", + "crid": "5b51e49634f2021f127ff7c9", + "h": 250, + "id": "bc708396-9202-437b-b726-08b9864cb8b8", + "impid": "test-imp-id", + "iurl": "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5b51e49634f2021f127ff7c9.jpeg", + "language": "en", + "price": 15.64434783, + "w": 300 + } + ], + "seat": "TEST_SEAT_ID" + } + ] +}`) + +var validVideoBid = openrtb.Bid{ + AdM: "Avocet", + ADomain: []string{"avocet.io"}, + CID: "5b51e2d689654741306813a4", + CrID: "5ec530e32d57fe1100f17d87", + H: 396, + ID: "3d4c2d45-5a8c-43b8-9e15-4f48ac45204f", + ImpID: "dfp-ad--top-above-nav", + IURL: "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5ec530e32d57fe1100f17d87.jpeg", + Language: "en", + Price: 15.64434783, + W: 600, + Ext: []byte(`{"avocet":{"duration":30}}`), +} + +var validVideoBidResponseBody = []byte(`{ + "bidid": "dd87f80c-16a0-43c8-a673-b94b3ea4d417", + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "adm": "Avocet", + "adomain": ["avocet.io"], + "cid": "5b51e2d689654741306813a4", + "crid": "5ec530e32d57fe1100f17d87", + "h": 396, + "id": "3d4c2d45-5a8c-43b8-9e15-4f48ac45204f", + "impid": "dfp-ad--top-above-nav", + "iurl": "https://cdn.staging.avocet.io/snapshots/5b51dd1634f2021f127ff7c0/5ec530e32d57fe1100f17d87.jpeg", + "language": "en", + "price": 15.64434783, + "w": 600, + "ext": {"avocet":{"duration":30}} + } + ], + "seat": "TEST_SEAT_ID" + } + ] +}`) diff --git a/adapters/avocet/usersync.go b/adapters/avocet/usersync.go new file mode 100644 index 00000000000..f1075ab3c52 --- /dev/null +++ b/adapters/avocet/usersync.go @@ -0,0 +1,12 @@ +package avocet + +import ( + "text/template" + + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" +) + +func NewAvocetSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("avocet", 63, temp, adapters.SyncTypeRedirect) +} diff --git a/adapters/avocet/usersync_test.go b/adapters/avocet/usersync_test.go new file mode 100644 index 00000000000..8fba403f1b1 --- /dev/null +++ b/adapters/avocet/usersync_test.go @@ -0,0 +1,35 @@ +package avocet + +import ( + "testing" + "text/template" + + "github.com/prebid/prebid-server/privacy" + "github.com/prebid/prebid-server/privacy/ccpa" + "github.com/prebid/prebid-server/privacy/gdpr" + "github.com/stretchr/testify/assert" +) + +func TestAvocetSyncer(t *testing.T) { + syncURL := "https://ads.avct.cloud/getuid?&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url=%2Fsetuid%3Fbidder%3Davocet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7BUUID%7D%7D" + syncURLTemplate := template.Must( + template.New("sync-template").Parse(syncURL), + ) + + syncer := NewAvocetSyncer(syncURLTemplate) + syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{ + GDPR: gdpr.Policy{ + Signal: "1", + Consent: "ConsentString", + }, + CCPA: ccpa.Policy{ + Value: "PrivacyString", + }, + }) + + assert.NoError(t, err) + assert.Equal(t, "https://ads.avct.cloud/getuid?&gdpr=1&gdpr_consent=ConsentString&us_privacy=PrivacyString&url=%2Fsetuid%3Fbidder%3Davocet%26gdpr%3D1%26gdpr_consent%3DConsentString%26uid%3D%7B%7BUUID%7D%7D", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 63, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) +} diff --git a/config/config.go b/config/config.go index 0f470c6a611..01de9b1ab2e 100755 --- a/config/config.go +++ b/config/config.go @@ -567,6 +567,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAdvangelists, "https://nep.advangelists.com/xp/user-sync?acctid={aid}&&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadvangelists%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAJA, "https://ad.as.amanad.adtdp.com/v1/sync/ssp?ssp=4&gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Daja%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25s") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAppnexus, "https://ib.adnxs.com/getuid?"+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dadnxs%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderAvocet, "https://ads.avct.cloud/getuid?&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Davocet%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7BUUID%7D%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeachfront, "https://sync.bfmio.com/sync_s2s?gdpr={{.GDPR}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeachfront%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5Bio_cid%5D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBeintoo, "https://ib.beintoo.com/um?ssp=pbs&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbeintoo%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderBrightroll, "https://pr-bh.ybp.yahoo.com/sync/appnexusprebidserver/?gdpr={{.GDPR}}&euconsent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&url="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dbrightroll%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") @@ -772,6 +773,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.applogy.endpoint", "http://rtb.applogy.com/v1/prebid") v.SetDefault("adapters.appnexus.endpoint", "http://ib.adnxs.com/openrtb2") // Docs: https://wiki.appnexus.com/display/supply/Incoming+Bid+Request+from+SSPs v.SetDefault("adapters.appnexus.platform_id", "5") + v.SetDefault("adapters.avocet.disabled", true) v.SetDefault("adapters.beachfront.endpoint", "https://display.bfmio.com/prebid_display") v.SetDefault("adapters.beachfront.extra_info", "{\"video_endpoint\":\"https://reachms.bfmio.com/bid.json?exchange_id\"}") v.SetDefault("adapters.beintoo.endpoint", "https://ib.beintoo.com/um") diff --git a/docs/bidders/avocet.md b/docs/bidders/avocet.md new file mode 100644 index 00000000000..6aa67391af4 --- /dev/null +++ b/docs/bidders/avocet.md @@ -0,0 +1,5 @@ +# Avocet Bidder + +Please contact Avocet at info@avocet.io if you would like to get started selling inventory via the Avocet platform. + +**Note:** Avocet is disabled by default. Please enable it in the app config if you wish to use it. This can be done by setting `adapters.avocet.disabled` to `false` and by setting `adapters.avocet.endpoint` to a valid Avocet endpoint url. \ No newline at end of file diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index 2ea8f7fb648..6e771236fb7 100755 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -25,6 +25,7 @@ import ( "github.com/prebid/prebid-server/adapters/applogy" "github.com/prebid/prebid-server/adapters/appnexus" "github.com/prebid/prebid-server/adapters/audienceNetwork" + "github.com/prebid/prebid-server/adapters/avocet" "github.com/prebid/prebid-server/adapters/beachfront" "github.com/prebid/prebid-server/adapters/beintoo" "github.com/prebid/prebid-server/adapters/brightroll" @@ -106,6 +107,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter openrtb_ext.BidderAJA: aja.NewAJABidder(cfg.Adapters[string(openrtb_ext.BidderAJA)].Endpoint), openrtb_ext.BidderApplogy: applogy.NewApplogyBidder(cfg.Adapters[string(openrtb_ext.BidderApplogy)].Endpoint), openrtb_ext.BidderAppnexus: appnexus.NewAppNexusBidder(client, cfg.Adapters[string(openrtb_ext.BidderAppnexus)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderAppnexus)].PlatformID), + openrtb_ext.BidderAvocet: avocet.NewAvocetAdapter(cfg.Adapters[string(openrtb_ext.BidderAvocet)].Endpoint), openrtb_ext.BidderBeachfront: beachfront.NewBeachfrontBidder(cfg.Adapters[string(openrtb_ext.BidderBeachfront)].Endpoint, cfg.Adapters[string(openrtb_ext.BidderBeachfront)].ExtraAdapterInfo), openrtb_ext.BidderBeintoo: beintoo.NewBeintooBidder(cfg.Adapters[string(openrtb_ext.BidderBeintoo)].Endpoint), openrtb_ext.BidderBrightroll: brightroll.NewBrightrollBidder(cfg.Adapters[string(openrtb_ext.BidderBrightroll)].Endpoint), diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 659c6616fea..416f36d135f 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -40,6 +40,7 @@ const ( BidderApplogy BidderName = "applogy" BidderAppnexus BidderName = "appnexus" BidderAdoppler BidderName = "adoppler" + BidderAvocet BidderName = "avocet" BidderBeachfront BidderName = "beachfront" BidderBeintoo BidderName = "beintoo" BidderBrightroll BidderName = "brightroll" @@ -118,6 +119,7 @@ var BidderMap = map[string]BidderName{ "applogy": BidderApplogy, "appnexus": BidderAppnexus, "adoppler": BidderAdoppler, + "avocet": BidderAvocet, "beachfront": BidderBeachfront, "beintoo": BidderBeintoo, "brightroll": BidderBrightroll, diff --git a/openrtb_ext/imp_avocet.go b/openrtb_ext/imp_avocet.go new file mode 100644 index 00000000000..7c9ca8c6eed --- /dev/null +++ b/openrtb_ext/imp_avocet.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtImpAvocet defines the contract for bidrequest.imp[i].ext.avocet +type ExtImpAvocet struct { + Placement string `json:"placement,omitempty"` + PlacementCode string `json:"placement_code,omitempty"` +} diff --git a/static/bidder-info/avocet.yaml b/static/bidder-info/avocet.yaml new file mode 100644 index 00000000000..ea98982d69c --- /dev/null +++ b/static/bidder-info/avocet.yaml @@ -0,0 +1,11 @@ +maintainer: + email: "developers@avocet.io" +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video diff --git a/static/bidder-params/avocet.json b/static/bidder-params/avocet.json new file mode 100644 index 00000000000..f27e5950f7c --- /dev/null +++ b/static/bidder-params/avocet.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Avocet Adapter Params", + "description": "A schema which validates params accepted by the Avocet adapter", + "type": "object", + "properties": { + "placement": { + "type": "string", + "description": "An Avocet placement ID" + }, + "placement_code": { + "type": "string", + "description": "An Avocet placement external code" + } + }, + "oneOf": [ + { + "required": ["placement"] + }, + { + "required": ["placement_code"] + } + ] +} diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 751d2aabfbe..1beb9d586df 100755 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -18,6 +18,7 @@ import ( "github.com/prebid/prebid-server/adapters/aja" "github.com/prebid/prebid-server/adapters/appnexus" "github.com/prebid/prebid-server/adapters/audienceNetwork" + "github.com/prebid/prebid-server/adapters/avocet" "github.com/prebid/prebid-server/adapters/beachfront" "github.com/prebid/prebid-server/adapters/beintoo" "github.com/prebid/prebid-server/adapters/brightroll" @@ -90,6 +91,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderAdvangelists, advangelists.NewAdvangelistsSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAJA, aja.NewAJASyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderAppnexus, appnexus.NewAppnexusSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderAvocet, avocet.NewAvocetSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderBeachfront, beachfront.NewBeachfrontSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderBeintoo, beintoo.NewBeintooSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderBrightroll, brightroll.NewBrightrollSyncer) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index c9ef382fc92..69751dd55f4 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -26,6 +26,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderAdvangelists): syncConfig, string(openrtb_ext.BidderAJA): syncConfig, string(openrtb_ext.BidderAppnexus): syncConfig, + string(openrtb_ext.BidderAvocet): syncConfig, string(openrtb_ext.BidderBeachfront): syncConfig, string(openrtb_ext.BidderBeintoo): syncConfig, string(openrtb_ext.BidderBrightroll): syncConfig,