Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions adapters/ezoic/ezoic.go
Original file line number Diff line number Diff line change
@@ -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),
}
}
}
57 changes: 57 additions & 0 deletions adapters/ezoic/ezoic_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
124 changes: 124 additions & 0 deletions adapters/ezoic/ezoictest/exemplary/simple-banner.json
Original file line number Diff line number Diff line change
@@ -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": "<div>ezoic-self-tracking-creative</div>",
"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": "<div>ezoic-self-tracking-creative</div>",
"crid": "ezoic-creative-1",
"w": 300,
"h": 250,
"mtype": 1,
"exp": 300
},
"type": "banner"
}
]
}
]
}
112 changes: 112 additions & 0 deletions adapters/ezoic/ezoictest/exemplary/simple-native.json
Original file line number Diff line number Diff line change
@@ -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"
}
]
}
]
}
Loading
Loading