diff --git a/adapters/ezoic/ezoic.go b/adapters/ezoic/ezoic.go new file mode 100644 index 00000000000..fd5e51950d6 --- /dev/null +++ b/adapters/ezoic/ezoic.go @@ -0,0 +1,95 @@ +package ezoic + +import ( + "fmt" + "net/http" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v4/adapters" + "github.com/prebid/prebid-server/v4/config" + "github.com/prebid/prebid-server/v4/errortypes" + "github.com/prebid/prebid-server/v4/openrtb_ext" + "github.com/prebid/prebid-server/v4/util/jsonutil" +) + +type adapter struct { + endpoint string +} + +// Builder builds a new instance of the Ezoic adapter for the given bidder with the given config. +func Builder(bidderName openrtb_ext.BidderName, cfg config.Adapter, server config.Server) (adapters.Bidder, error) { + return &adapter{endpoint: cfg.Endpoint}, nil +} + +// MakeRequests forwards the OpenRTB request to the Ezoic bidder endpoint +// unchanged. Eligibility, demand selection, and creative construction all +// happen server-side at Ezoic; the adapter is a deliberately thin transport. +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + body, err := jsonutil.Marshal(request) + if err != nil { + return nil, []error{fmt.Errorf("unable to marshal openrtb request: %w", err)} + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + return []*adapters.RequestData{{ + Method: http.MethodPost, + Uri: a.endpoint, + Body: body, + Headers: headers, + ImpIDs: openrtb_ext.GetImpIDs(request.Imp), + }}, nil +} + +// MakeBids unpacks the Ezoic endpoint's OpenRTB BidResponse. +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 bidResp openrtb2.BidResponse + if err := jsonutil.Unmarshal(responseData.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidderResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) + if bidResp.Cur != "" { + bidderResponse.Currency = bidResp.Cur + } + + var errs []error + for _, seatBid := range bidResp.SeatBid { + for i := range seatBid.Bid { + bidType, err := getMediaTypeForBid(seatBid.Bid[i]) + if err != nil { + errs = append(errs, err) + continue + } + bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{ + Bid: &seatBid.Bid[i], + BidType: bidType, + }) + } + } + return bidderResponse, errs +} + +func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { + switch bid.MType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo, nil + case openrtb2.MarkupNative: + return openrtb_ext.BidTypeNative, nil + default: + return "", &errortypes.BadServerResponse{ + Message: fmt.Sprintf("unsupported mtype %d for bid %s", bid.MType, bid.ID), + } + } +} diff --git a/adapters/ezoic/ezoic_test.go b/adapters/ezoic/ezoic_test.go new file mode 100644 index 00000000000..0c9a24a953a --- /dev/null +++ b/adapters/ezoic/ezoic_test.go @@ -0,0 +1,57 @@ +package ezoic + +import ( + "testing" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v4/adapters" + "github.com/prebid/prebid-server/v4/adapters/adapterstest" + "github.com/prebid/prebid-server/v4/config" + "github.com/prebid/prebid-server/v4/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +const testsBidderEndpoint = "https://g.ezoic.net/ezoic/prebid/adapter/ortb" + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderEzoic, config.Adapter{ + Endpoint: testsBidderEndpoint}, config.Server{}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "ezoictest", bidder) +} + +func TestNoContentResponse(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderEzoic, config.Adapter{ + Endpoint: testsBidderEndpoint}, config.Server{}) + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + bidResponse, errs := bidder.MakeBids(nil, nil, &adapters.ResponseData{StatusCode: 204}) + assert.Nil(t, bidResponse) + assert.Empty(t, errs) +} + +func TestGetMediaTypeForBid(t *testing.T) { + bidType, err := getMediaTypeForBid(openrtb2.Bid{MType: openrtb2.MarkupBanner}) + assert.NoError(t, err) + assert.Equal(t, openrtb_ext.BidTypeBanner, bidType) + + bidType, err = getMediaTypeForBid(openrtb2.Bid{MType: openrtb2.MarkupVideo}) + assert.NoError(t, err) + assert.Equal(t, openrtb_ext.BidTypeVideo, bidType) + + bidType, err = getMediaTypeForBid(openrtb2.Bid{MType: openrtb2.MarkupNative}) + assert.NoError(t, err) + assert.Equal(t, openrtb_ext.BidTypeNative, bidType) + + _, err = getMediaTypeForBid(openrtb2.Bid{ID: "no-mtype"}) + assert.Error(t, err) + + _, err = getMediaTypeForBid(openrtb2.Bid{ID: "audio", MType: openrtb2.MarkupAudio}) + assert.Error(t, err) +} diff --git a/adapters/ezoic/ezoictest/exemplary/simple-banner.json b/adapters/ezoic/ezoictest/exemplary/simple-banner.json new file mode 100644 index 00000000000..7c3c29b291b --- /dev/null +++ b/adapters/ezoic/ezoictest/exemplary/simple-banner.json @@ -0,0 +1,124 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "tmax": 750, + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "device": { + "ua": "test-user-agent", + "ip": "203.0.113.9" + }, + "imp": [ + { + "id": "test-imp-id", + "tagid": "div-gpt-ad-1", + "bidfloor": 0.25, + "bidfloorcur": "USD", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placementId": "ezoic-placement-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://g.ezoic.net/ezoic/prebid/adapter/ortb", + "body": { + "id": "test-request-id", + "tmax": 750, + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "device": { + "ua": "test-user-agent", + "ip": "203.0.113.9" + }, + "imp": [ + { + "id": "test-imp-id", + "tagid": "div-gpt-ad-1", + "bidfloor": 0.25, + "bidfloorcur": "USD", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "placementId": "ezoic-placement-1" + } + } + } + ] + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "seat": "ezoic", + "bid": [ + { + "id": "ezoic-bid-1", + "impid": "test-imp-id", + "price": 2.5, + "adm": "
ezoic-self-tracking-creative
", + "crid": "ezoic-creative-1", + "w": 300, + "h": 250, + "mtype": 1, + "exp": 300 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "ezoic-bid-1", + "impid": "test-imp-id", + "price": 2.5, + "adm": "
ezoic-self-tracking-creative
", + "crid": "ezoic-creative-1", + "w": 300, + "h": 250, + "mtype": 1, + "exp": 300 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/ezoic/ezoictest/exemplary/simple-native.json b/adapters/ezoic/ezoictest/exemplary/simple-native.json new file mode 100644 index 00000000000..31a072cd61d --- /dev/null +++ b/adapters/ezoic/ezoictest/exemplary/simple-native.json @@ -0,0 +1,112 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "tmax": 750, + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "device": { + "ua": "test-user-agent", + "ip": "203.0.113.9" + }, + "imp": [ + { + "id": "test-imp-id", + "tagid": "div-gpt-ad-1", + "bidfloor": 0.25, + "bidfloorcur": "USD", + "native": { + "ver": "1.2", + "request": "{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":90}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"w\":1200,\"h\":627}}]}" + }, + "ext": { + "bidder": { + "placementId": "ezoic-placement-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://g.ezoic.net/ezoic/prebid/adapter/ortb", + "body": { + "id": "test-request-id", + "tmax": 750, + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "device": { + "ua": "test-user-agent", + "ip": "203.0.113.9" + }, + "imp": [ + { + "id": "test-imp-id", + "tagid": "div-gpt-ad-1", + "bidfloor": 0.25, + "bidfloorcur": "USD", + "native": { + "ver": "1.2", + "request": "{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":90}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"w\":1200,\"h\":627}}]}" + }, + "ext": { + "bidder": { + "placementId": "ezoic-placement-1" + } + } + } + ] + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "seat": "ezoic", + "bid": [ + { + "id": "ezoic-bid-1", + "impid": "test-imp-id", + "price": 1.75, + "adm": "{\"native\":{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"title\":{\"text\":\"ezoic native title\"}}],\"link\":{\"url\":\"https://example.com/click\"}}}", + "crid": "ezoic-creative-1", + "mtype": 4, + "exp": 300 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "ezoic-bid-1", + "impid": "test-imp-id", + "price": 1.75, + "adm": "{\"native\":{\"ver\":\"1.2\",\"assets\":[{\"id\":1,\"title\":{\"text\":\"ezoic native title\"}}],\"link\":{\"url\":\"https://example.com/click\"}}}", + "crid": "ezoic-creative-1", + "mtype": 4, + "exp": 300 + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/ezoic/ezoictest/exemplary/simple-video.json b/adapters/ezoic/ezoictest/exemplary/simple-video.json new file mode 100644 index 00000000000..6db45edc5ed --- /dev/null +++ b/adapters/ezoic/ezoictest/exemplary/simple-video.json @@ -0,0 +1,138 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "tmax": 750, + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "device": { + "ua": "test-user-agent", + "ip": "203.0.113.9" + }, + "imp": [ + { + "id": "test-imp-id", + "tagid": "div-gpt-ad-1", + "bidfloor": 0.25, + "bidfloorcur": "USD", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 3, + 5, + 6 + ], + "w": 640, + "h": 480, + "minduration": 5, + "maxduration": 30 + }, + "ext": { + "bidder": { + "placementId": "ezoic-placement-1" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://g.ezoic.net/ezoic/prebid/adapter/ortb", + "body": { + "id": "test-request-id", + "tmax": 750, + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "device": { + "ua": "test-user-agent", + "ip": "203.0.113.9" + }, + "imp": [ + { + "id": "test-imp-id", + "tagid": "div-gpt-ad-1", + "bidfloor": 0.25, + "bidfloorcur": "USD", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 3, + 5, + 6 + ], + "w": 640, + "h": 480, + "minduration": 5, + "maxduration": 30 + }, + "ext": { + "bidder": { + "placementId": "ezoic-placement-1" + } + } + } + ] + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "seat": "ezoic", + "bid": [ + { + "id": "ezoic-bid-1", + "impid": "test-imp-id", + "price": 4.5, + "adm": "", + "crid": "ezoic-creative-1", + "w": 640, + "h": 480, + "mtype": 2, + "exp": 300 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "ezoic-bid-1", + "impid": "test-imp-id", + "price": 4.5, + "adm": "", + "crid": "ezoic-creative-1", + "w": 640, + "h": 480, + "mtype": 2, + "exp": 300 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/ezoic/ezoictest/supplemental/invalid-response.json b/adapters/ezoic/ezoictest/supplemental/invalid-response.json new file mode 100644 index 00000000000..adcb3c10012 --- /dev/null +++ b/adapters/ezoic/ezoictest/supplemental/invalid-response.json @@ -0,0 +1,69 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": {} + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://g.ezoic.net/ezoic/prebid/adapter/ortb", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": {} + } + } + ] + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 200, + "body": "invalid response" + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "expect", + "comparison": "regex" + } + ] +} diff --git a/adapters/ezoic/ezoictest/supplemental/status-204.json b/adapters/ezoic/ezoictest/supplemental/status-204.json new file mode 100644 index 00000000000..2c60ef485ad --- /dev/null +++ b/adapters/ezoic/ezoictest/supplemental/status-204.json @@ -0,0 +1,63 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": {} + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://g.ezoic.net/ezoic/prebid/adapter/ortb", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": {} + } + } + ] + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/ezoic/ezoictest/supplemental/status-400.json b/adapters/ezoic/ezoictest/supplemental/status-400.json new file mode 100644 index 00000000000..8d156eb86a4 --- /dev/null +++ b/adapters/ezoic/ezoictest/supplemental/status-400.json @@ -0,0 +1,69 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": {} + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://g.ezoic.net/ezoic/prebid/adapter/ortb", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": {} + } + } + ] + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 400, + "body": {} + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/ezoic/ezoictest/supplemental/status-500.json b/adapters/ezoic/ezoictest/supplemental/status-500.json new file mode 100644 index 00000000000..1d0b4182900 --- /dev/null +++ b/adapters/ezoic/ezoictest/supplemental/status-500.json @@ -0,0 +1,69 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": {} + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://g.ezoic.net/ezoic/prebid/adapter/ortb", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": {} + } + } + ] + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 500, + "body": {} + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/ezoic/ezoictest/supplemental/unsupported-mtype.json b/adapters/ezoic/ezoictest/supplemental/unsupported-mtype.json new file mode 100644 index 00000000000..39d7506d66d --- /dev/null +++ b/adapters/ezoic/ezoictest/supplemental/unsupported-mtype.json @@ -0,0 +1,117 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": {} + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://g.ezoic.net/ezoic/prebid/adapter/ortb", + "body": { + "id": "test-request-id", + "site": { + "page": "https://example.com/article", + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": {} + } + } + ] + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "cur": "USD", + "seatbid": [ + { + "seat": "ezoic", + "bid": [ + { + "id": "ezoic-bid-good", + "impid": "test-imp-id", + "price": 1.25, + "adm": "
creative
", + "crid": "ezoic-creative-1", + "w": 300, + "h": 250, + "mtype": 1 + }, + { + "id": "ezoic-bid-no-mtype", + "impid": "test-imp-id", + "price": 9.99, + "adm": "
creative
", + "crid": "ezoic-creative-2", + "w": 300, + "h": 250 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "ezoic-bid-good", + "impid": "test-imp-id", + "price": 1.25, + "adm": "
creative
", + "crid": "ezoic-creative-1", + "w": 300, + "h": 250, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unsupported mtype 0 for bid ezoic-bid-no-mtype", + "comparison": "literal" + } + ] +} diff --git a/adapters/ezoic/params_test.go b/adapters/ezoic/params_test.go new file mode 100644 index 00000000000..91994fe4cf1 --- /dev/null +++ b/adapters/ezoic/params_test.go @@ -0,0 +1,52 @@ +package ezoic + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v4/openrtb_ext" +) + +// This file intends to test static/bidder-params/ezoic.json +// +// These also validate the format of the external API: request.imp[i].ext.prebid.bidder.ezoic + +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.BidderEzoic, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected ezoic params: %s", validParam) + } + } +} + +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.BidderEzoic, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{}`, + `{"placementId": "ezoic-placement-1"}`, +} + +var invalidParams = []string{ + `null`, + `true`, + `[]`, + `"placementId"`, + `{"placementId": 12345}`, + `{"placementId": true}`, +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 75a848b7596..090e537effb 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -105,6 +105,7 @@ import ( "github.com/prebid/prebid-server/v4/adapters/epom" "github.com/prebid/prebid-server/v4/adapters/escalax" "github.com/prebid/prebid-server/v4/adapters/exco" + "github.com/prebid/prebid-server/v4/adapters/ezoic" "github.com/prebid/prebid-server/v4/adapters/feedad" "github.com/prebid/prebid-server/v4/adapters/flatads" "github.com/prebid/prebid-server/v4/adapters/flipp" @@ -378,6 +379,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderEscalax: escalax.Builder, openrtb_ext.BidderExco: exco.Builder, openrtb_ext.BidderEVolution: evolution.Builder, + openrtb_ext.BidderEzoic: ezoic.Builder, openrtb_ext.BidderFeedAd: feedad.Builder, openrtb_ext.BidderFlatads: flatads.Builder, openrtb_ext.BidderFlipp: flipp.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index aa725e7d1bd..ef529315721 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -122,6 +122,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderEscalax, BidderEVolution, BidderExco, + BidderEzoic, BidderFeedAd, BidderFlatads, BidderFlipp, @@ -501,6 +502,7 @@ const ( BidderEscalax BidderName = "escalax" BidderExco BidderName = "exco" BidderEVolution BidderName = "e_volution" + BidderEzoic BidderName = "ezoic" BidderFeedAd BidderName = "feedad" BidderFlatads BidderName = "flatads" BidderFlipp BidderName = "flipp" diff --git a/openrtb_ext/imp_ezoic.go b/openrtb_ext/imp_ezoic.go new file mode 100644 index 00000000000..609650ad5e4 --- /dev/null +++ b/openrtb_ext/imp_ezoic.go @@ -0,0 +1,8 @@ +package openrtb_ext + +// ExtImpEzoic defines the contract for bidrequest.imp[i].ext.prebid.bidder.ezoic. +// Ezoic eligibility is resolved server-side from site.domain, so no params are +// required; placementId is an optional onboarding-assigned identifier. +type ExtImpEzoic struct { + PlacementID string `json:"placementId,omitempty"` +} diff --git a/static/bidder-info/ezoic.yaml b/static/bidder-info/ezoic.yaml new file mode 100644 index 00000000000..9f8be82b52d --- /dev/null +++ b/static/bidder-info/ezoic.yaml @@ -0,0 +1,23 @@ +# Ezoic requires publisher domains to be registered and approved before +# bidding; unapproved inventory receives no-bid responses. Contact +# prebid@ezoic.com to get set up. +endpoint: "https://g.ezoic.net/ezoic/prebid/adapter/ortb" +maintainer: + email: prebid@ezoic.com +gvlVendorID: 347 +geoscope: + - global +openrtb: + version: 2.6 +modifyingVastXmlAllowed: false +capabilities: + app: + mediaTypes: + - banner + - video + - native + site: + mediaTypes: + - banner + - video + - native diff --git a/static/bidder-params/ezoic.json b/static/bidder-params/ezoic.json new file mode 100644 index 00000000000..ecd916a4611 --- /dev/null +++ b/static/bidder-params/ezoic.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Ezoic Adapter Params", + "description": "A schema which validates params accepted by the Ezoic adapter", + "type": "object", + "properties": { + "placementId": { + "type": "string", + "description": "Optional placement identifier assigned during Ezoic onboarding" + } + }, + "required": [] +}