-
Notifications
You must be signed in to change notification settings - Fork 749
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
New Adapter: Pixfuture #4105
base: master
Are you sure you want to change the base?
New Adapter: Pixfuture #4105
Changes from all commits
93e6573
458fad1
d67a079
a9fe81b
606b185
061b818
d0bc2e1
eec17ff
cb8e3a7
bba6eed
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
mode: set | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:20.119,25.2 2 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:28.146,29.27 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:29.27,31.3 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:32.2,33.16 2 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:33.16,35.3 1 0 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:37.2,45.50 2 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:49.72,50.20 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:50.20,53.41 3 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:53.41,55.4 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:58.2,60.3 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:64.175,65.53 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:65.53,67.3 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:69.2,69.54 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:69.54,73.3 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:75.2,75.46 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:75.46,79.3 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:81.2,82.73 2 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:82.73,84.3 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:86.2,90.43 4 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:90.43,91.35 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:91.35,93.18 2 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:93.18,95.13 2 0 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:97.4,100.6 1 1 | ||
github.com/prebid/prebid-server/v3/adapters/pixfuture/pixfuture.go:104.2,104.28 1 1 |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package pixfuture | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/prebid/prebid-server/v3/openrtb_ext" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestPixfutureParams(t *testing.T) { | ||
testCases := []struct { | ||
name string | ||
params openrtb_ext.ImpExtPixfuture | ||
expectedError string | ||
}{ | ||
{ | ||
name: "Valid Params", | ||
params: openrtb_ext.ImpExtPixfuture{ | ||
PlacementID: "123", | ||
}, | ||
expectedError: "", | ||
}, | ||
{ | ||
name: "Missing PlacementID", | ||
params: openrtb_ext.ImpExtPixfuture{ | ||
PlacementID: "", | ||
}, | ||
expectedError: "PlacementID is required", | ||
}, | ||
} | ||
|
||
for _, test := range testCases { | ||
t.Run(test.name, func(t *testing.T) { | ||
err := validatePixfutureParams(test.params) | ||
if test.expectedError == "" { | ||
assert.NoError(t, err) | ||
} else { | ||
assert.EqualError(t, err, test.expectedError) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func validatePixfutureParams(params openrtb_ext.ImpExtPixfuture) error { | ||
if params.PlacementID == "" { | ||
return fmt.Errorf("PlacementID is required") | ||
} | ||
return nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
package pixfuture | ||
|
||
import ( | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/prebid/openrtb/v20/openrtb2" | ||
"github.com/prebid/prebid-server/v3/adapters" | ||
"github.com/prebid/prebid-server/v3/config" | ||
"github.com/prebid/prebid-server/v3/errortypes" | ||
"github.com/prebid/prebid-server/v3/openrtb_ext" | ||
"github.com/prebid/prebid-server/v3/util/jsonutil" | ||
) | ||
|
||
type PixfutureAdapter struct { | ||
endpoint string | ||
} | ||
|
||
// Builder builds a new instance of the Pixfuture adapter. | ||
func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { | ||
bidder := &PixfutureAdapter{ | ||
endpoint: config.Endpoint, | ||
} | ||
return bidder, nil | ||
} | ||
|
||
// MakeRequests prepares and serializes HTTP requests to be sent to the Pixfuture endpoint. | ||
func (a *PixfutureAdapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { | ||
if len(request.Imp) == 0 { | ||
return nil, []error{&errortypes.BadInput{Message: "No impressions in the bid request"}} | ||
} | ||
requestJSON, err := jsonutil.Marshal(request) | ||
if err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
requestData := &adapters.RequestData{ | ||
Method: "POST", | ||
Uri: a.endpoint, | ||
Body: requestJSON, | ||
Headers: http.Header{ | ||
"Content-Type": []string{"application/json"}, | ||
}, | ||
} | ||
return []*adapters.RequestData{requestData}, nil | ||
} | ||
|
||
// getMediaTypeForBid extracts the bid type based on the bid extension data. | ||
func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { | ||
if bid.Ext != nil { | ||
var bidExt openrtb_ext.ExtBid | ||
err := jsonutil.Unmarshal(bid.Ext, &bidExt) | ||
if err == nil && bidExt.Prebid != nil { | ||
return openrtb_ext.ParseBidType(string(bidExt.Prebid.Type)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider this as a suggestion. Prebid server expects the media type to be explicitly set in the adapter response. Therefore, recommends implementing a pattern where the adapter server sets the MType field in the response to accurately determine the media type for the impression. |
||
} | ||
} | ||
|
||
return "", &errortypes.BadServerResponse{ | ||
Message: fmt.Sprintf("Failed to parse impression \"%s\" mediatype", bid.ImpID), | ||
} | ||
} | ||
|
||
// MakeBids parses the HTTP response from the Pixfuture endpoint and generates a BidderResponse. | ||
func (a *PixfutureAdapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { | ||
if responseData.StatusCode == http.StatusNoContent { | ||
return nil, nil | ||
} | ||
|
||
if responseData.StatusCode == http.StatusBadRequest { | ||
return nil, []error{&errortypes.BadInput{ | ||
Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.", | ||
}} | ||
} | ||
|
||
if responseData.StatusCode != http.StatusOK { | ||
return nil, []error{&errortypes.BadServerResponse{ | ||
Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode), | ||
}} | ||
} | ||
|
||
var response openrtb2.BidResponse | ||
if err := jsonutil.Unmarshal(responseData.Body, &response); err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) | ||
bidResponse.Currency = response.Cur | ||
var errors []error | ||
|
||
for _, seatBid := range response.SeatBid { | ||
for i, bid := range seatBid.Bid { | ||
bidType, err := getMediaTypeForBid(bid) | ||
if err != nil { | ||
errors = append(errors, err) | ||
continue | ||
} | ||
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ | ||
Bid: &seatBid.Bid[i], | ||
BidType: bidType, | ||
}) | ||
} | ||
} | ||
|
||
return bidResponse, errors | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
package pixfuture | ||
|
||
import ( | ||
"encoding/json" | ||
"net/http" | ||
"testing" | ||
|
||
"github.com/prebid/openrtb/v20/openrtb2" | ||
"github.com/prebid/prebid-server/v3/adapters" | ||
"github.com/prebid/prebid-server/v3/config" | ||
"github.com/prebid/prebid-server/v3/openrtb_ext" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func TestBuilder(t *testing.T) { | ||
adapter, err := Builder("pixfuture", config.Adapter{Endpoint: "http://mock-endpoint.com"}, config.Server{}) | ||
assert.NoError(t, err, "unexpected error during Builder execution") | ||
assert.NotNil(t, adapter, "expected a non-nil adapter instance") | ||
} | ||
|
||
func TestPixfutureAdapter_MakeRequests(t *testing.T) { | ||
adapter := &PixfutureAdapter{endpoint: "http://mock-pixfuture-endpoint.com"} | ||
|
||
t.Run("Valid Request", func(t *testing.T) { | ||
bidRequest := &openrtb2.BidRequest{ | ||
ID: "test-request-id", | ||
Imp: []openrtb2.Imp{ | ||
{ | ||
ID: "test-imp-id", | ||
Banner: &openrtb2.Banner{W: int64Ptr(300), H: int64Ptr(250)}, | ||
Ext: jsonRawExt(`{"bidder":{"siteId":"123"}}`), | ||
}, | ||
}, | ||
} | ||
|
||
requests, errs := adapter.MakeRequests(bidRequest, nil) | ||
assert.Empty(t, errs, "unexpected errors in MakeRequests") | ||
assert.Equal(t, 1, len(requests), "expected exactly one request") | ||
|
||
request := requests[0] | ||
assert.Equal(t, "POST", request.Method, "unexpected HTTP method") | ||
assert.Equal(t, "http://mock-pixfuture-endpoint.com", request.Uri, "unexpected request URI") | ||
assert.Contains(t, string(request.Body), `"id":"test-request-id"`, "unexpected request body") | ||
assert.Equal(t, "application/json", request.Headers.Get("Content-Type"), "unexpected content-type") | ||
}) | ||
|
||
t.Run("No Impressions", func(t *testing.T) { | ||
bidRequest := &openrtb2.BidRequest{ID: "test-request-id"} | ||
|
||
requests, errs := adapter.MakeRequests(bidRequest, nil) | ||
assert.NotEmpty(t, errs, "expected error for request with no impressions") | ||
assert.Nil(t, requests, "expected no requests for request with no impressions") | ||
}) | ||
|
||
t.Run("Malformed BidRequest", func(t *testing.T) { | ||
bidRequest := &openrtb2.BidRequest{} | ||
|
||
requests, errs := adapter.MakeRequests(bidRequest, nil) | ||
assert.NotEmpty(t, errs, "expected error for malformed request") | ||
assert.Nil(t, requests, "expected no requests for malformed request") | ||
}) | ||
} | ||
|
||
func TestPixfutureAdapter_MakeBids(t *testing.T) { | ||
adapter := &PixfutureAdapter{} | ||
|
||
t.Run("Valid Response", func(t *testing.T) { | ||
responseData := &adapters.ResponseData{ | ||
StatusCode: http.StatusOK, | ||
Body: []byte(`{ | ||
"id": "test-response-id", | ||
"seatbid": [{ | ||
"bid": [{ | ||
"id": "test-bid-id", | ||
"impid": "test-imp-id", | ||
"price": 1.23, | ||
"adm": "<html>Ad Content</html>", | ||
"crid": "creative-123", | ||
"w": 300, | ||
"h": 250, | ||
"ext": {"prebid":{"type":"banner"}} | ||
}] | ||
}], | ||
"cur": "USD" | ||
}`), | ||
} | ||
|
||
bidRequest := &openrtb2.BidRequest{ID: "test-request-id", Imp: []openrtb2.Imp{{ID: "test-imp-id"}}} | ||
bidResponse, errs := adapter.MakeBids(bidRequest, nil, responseData) | ||
|
||
assert.Empty(t, errs, "unexpected errors in MakeBids") | ||
assert.NotNil(t, bidResponse, "expected bid response") | ||
assert.Equal(t, "USD", bidResponse.Currency, "unexpected currency") | ||
assert.Equal(t, 1, len(bidResponse.Bids), "expected one bid") | ||
|
||
bid := bidResponse.Bids[0] | ||
assert.Equal(t, "test-bid-id", bid.Bid.ID, "unexpected bid ID") | ||
assert.Equal(t, "test-imp-id", bid.Bid.ImpID, "unexpected impression ID") | ||
assert.Equal(t, 1.23, bid.Bid.Price, "unexpected bid price") | ||
assert.Equal(t, openrtb_ext.BidTypeBanner, bid.BidType, "unexpected bid type") | ||
}) | ||
|
||
t.Run("No Content Response", func(t *testing.T) { | ||
responseData := &adapters.ResponseData{StatusCode: http.StatusNoContent} | ||
bidRequest := &openrtb2.BidRequest{} | ||
bidResponse, errs := adapter.MakeBids(bidRequest, nil, responseData) | ||
assert.Nil(t, bidResponse, "expected no bid response") | ||
assert.Empty(t, errs, "unexpected errors for no content response") | ||
}) | ||
|
||
t.Run("Bad Request Response", func(t *testing.T) { | ||
responseData := &adapters.ResponseData{StatusCode: http.StatusBadRequest} | ||
bidRequest := &openrtb2.BidRequest{} | ||
bidResponse, errs := adapter.MakeBids(bidRequest, nil, responseData) | ||
assert.Nil(t, bidResponse, "expected no bid response") | ||
assert.NotEmpty(t, errs, "expected errors for bad request response") | ||
}) | ||
|
||
t.Run("Unexpected Status Code", func(t *testing.T) { | ||
responseData := &adapters.ResponseData{StatusCode: http.StatusInternalServerError} | ||
bidRequest := &openrtb2.BidRequest{} | ||
bidResponse, errs := adapter.MakeBids(bidRequest, nil, responseData) | ||
assert.Nil(t, bidResponse, "expected no bid response") | ||
assert.NotEmpty(t, errs, "expected errors for unexpected status code") | ||
}) | ||
|
||
t.Run("Malformed Response Body", func(t *testing.T) { | ||
responseData := &adapters.ResponseData{ | ||
StatusCode: http.StatusOK, | ||
Body: []byte(`malformed response`), | ||
} | ||
bidRequest := &openrtb2.BidRequest{} | ||
bidResponse, errs := adapter.MakeBids(bidRequest, nil, responseData) | ||
assert.Nil(t, bidResponse, "expected no bid response") | ||
assert.NotEmpty(t, errs, "expected errors for malformed response body") | ||
}) | ||
} | ||
|
||
func TestGetMediaTypeForBid(t *testing.T) { | ||
t.Run("Valid Bid Ext", func(t *testing.T) { | ||
bid := openrtb2.Bid{ | ||
ID: "test-bid", | ||
Ext: json.RawMessage(`{"prebid":{"type":"banner"}}`), | ||
} | ||
bidType, err := getMediaTypeForBid(bid) | ||
assert.NoError(t, err, "unexpected error in getMediaTypeForBid") | ||
assert.Equal(t, openrtb_ext.BidTypeBanner, bidType, "unexpected bid type") | ||
}) | ||
|
||
t.Run("Invalid Bid Ext", func(t *testing.T) { | ||
bid := openrtb2.Bid{ | ||
ID: "test-bid", | ||
Ext: json.RawMessage(`{"invalid":"data"}`), | ||
} | ||
bidType, err := getMediaTypeForBid(bid) | ||
assert.Error(t, err, "expected error for invalid bid ext") | ||
assert.Equal(t, openrtb_ext.BidType(""), bidType, "expected empty bid type for invalid bid ext") | ||
}) | ||
} | ||
|
||
func int64Ptr(i int64) *int64 { | ||
return &i | ||
} | ||
|
||
func jsonRawExt(jsonStr string) json.RawMessage { | ||
return json.RawMessage(jsonStr) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"id": "test-request-id", | ||
"imp": [ | ||
{ | ||
"id": "test-imp-id", | ||
"banner": { | ||
"w": 300, | ||
"h": 250 | ||
}, | ||
"ext": { | ||
"bidder": { | ||
"siteId": "123" | ||
} | ||
} | ||
} | ||
], | ||
"site": { | ||
"page": "http://example.com" | ||
}, | ||
"device": { | ||
"ua": "Mozilla/5.0", | ||
"ip": "192.168.0.1" | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can call this simply "adapter", the
PixfutureAdapter
identification is already supplied by the package name. As you have it, referencing your adapter from outside the package would bePixfutureAdapter.PixfutureAdapter
which looks a little redundant. See example below: