forked from prebid/prebid-server
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
New adapter: MinuteMedia (prebid#3399)
- Loading branch information
1 parent
18aa538
commit daebdcc
Showing
17 changed files
with
1,684 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
package minutemedia | ||
|
||
import ( | ||
"encoding/json" | ||
"errors" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
"strings" | ||
|
||
"github.com/prebid/openrtb/v19/openrtb2" | ||
|
||
"github.com/prebid/prebid-server/v2/adapters" | ||
"github.com/prebid/prebid-server/v2/config" | ||
"github.com/prebid/prebid-server/v2/errortypes" | ||
"github.com/prebid/prebid-server/v2/openrtb_ext" | ||
) | ||
|
||
// adapter is a MinuteMedia implementation of the adapters.Bidder interface. | ||
type adapter struct { | ||
endpointURL string | ||
} | ||
|
||
func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) { | ||
return &adapter{ | ||
endpointURL: config.Endpoint, | ||
}, nil | ||
} | ||
|
||
// MakeRequests prepares the HTTP requests which should be made to fetch bids. | ||
func (a *adapter) MakeRequests(openRTBRequest *openrtb2.BidRequest, _ *adapters.ExtraRequestInfo) (requestsToBidder []*adapters.RequestData, errs []error) { | ||
org, err := extractOrg(openRTBRequest) | ||
if err != nil { | ||
return nil, append(errs, fmt.Errorf("failed to extract org: %w", err)) | ||
} | ||
|
||
openRTBRequestJSON, err := json.Marshal(openRTBRequest) | ||
if err != nil { | ||
return nil, append(errs, err) | ||
} | ||
|
||
headers := http.Header{} | ||
headers.Add("Content-Type", "application/json;charset=utf-8") | ||
|
||
return append(requestsToBidder, &adapters.RequestData{ | ||
Method: http.MethodPost, | ||
Uri: a.endpointURL + "?publisher_id=" + url.QueryEscape(org), | ||
Body: openRTBRequestJSON, | ||
Headers: headers, | ||
}), nil | ||
} | ||
|
||
// MakeBids unpacks the server's response into Bids. | ||
func (a *adapter) MakeBids(request *openrtb2.BidRequest, _ *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 response openrtb2.BidResponse | ||
if err := json.Unmarshal(responseData.Body, &response); err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) | ||
bidResponse.Currency = response.Cur | ||
|
||
var errs []error | ||
|
||
for _, seatBid := range response.SeatBid { | ||
for i, bid := range seatBid.Bid { | ||
bidType, err := getMediaTypeForBid(bid) | ||
if err != nil { | ||
errs = append(errs, err) | ||
continue | ||
} | ||
|
||
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ | ||
Bid: &seatBid.Bid[i], | ||
BidType: bidType, | ||
}) | ||
} | ||
} | ||
|
||
return bidResponse, errs | ||
} | ||
|
||
func extractOrg(openRTBRequest *openrtb2.BidRequest) (string, error) { | ||
var err error | ||
for _, imp := range openRTBRequest.Imp { | ||
var bidderExt adapters.ExtImpBidder | ||
if err = json.Unmarshal(imp.Ext, &bidderExt); err != nil { | ||
return "", fmt.Errorf("failed to unmarshal bidderExt: %w", err) | ||
} | ||
|
||
var impExt openrtb_ext.ImpExtMinuteMedia | ||
if err = json.Unmarshal(bidderExt.Bidder, &impExt); err != nil { | ||
return "", fmt.Errorf("failed to unmarshal ImpExtMinuteMedia: %w", err) | ||
} | ||
|
||
return strings.TrimSpace(impExt.Org), nil | ||
} | ||
|
||
return "", errors.New("no imps in bid request") | ||
} | ||
|
||
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 | ||
default: | ||
return "", &errortypes.BadServerResponse{ | ||
Message: fmt.Sprintf("unsupported MType %d", bid.MType), | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package minutemedia | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/prebid/prebid-server/v2/adapters/adapterstest" | ||
"github.com/prebid/prebid-server/v2/config" | ||
"github.com/prebid/prebid-server/v2/openrtb_ext" | ||
) | ||
|
||
const testsDir = "minutemediatest" | ||
const testsBidderEndpoint = "http://localhost/prebid_server" | ||
|
||
func TestJsonSamples(t *testing.T) { | ||
bidder, buildErr := Builder( | ||
openrtb_ext.BidderMinuteMedia, | ||
config.Adapter{Endpoint: testsBidderEndpoint}, | ||
config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) | ||
if buildErr != nil { | ||
t.Fatalf("Builder returned unexpected error %v", buildErr) | ||
} | ||
|
||
adapterstest.RunJSONBidderTest(t, testsDir, bidder) | ||
} |
251 changes: 251 additions & 0 deletions
251
adapters/minutemedia/minutemediatest/exemplary/banner-and-video-app.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
{ | ||
"mockBidRequest": { | ||
"id": "test-request-id", | ||
"imp": [ | ||
{ | ||
"id": "test-imp-id", | ||
"banner": { | ||
"format": [ | ||
{ | ||
"w": 300, | ||
"h": 250 | ||
}, | ||
{ | ||
"w": 300, | ||
"h": 300 | ||
} | ||
] | ||
}, | ||
"ext": { | ||
"bidder": { | ||
"org": "72721", | ||
"path": "mvo", | ||
"zone": "1r" | ||
} | ||
} | ||
}, | ||
{ | ||
"id": "test-imp-video-id", | ||
"video": { | ||
"mimes": [ | ||
"video/mp4" | ||
], | ||
"minduration": 1, | ||
"maxduration": 2, | ||
"protocols": [ | ||
1, | ||
2, | ||
5 | ||
], | ||
"w": 1020, | ||
"h": 780, | ||
"startdelay": 1, | ||
"placement": 1, | ||
"playbackmethod": [ | ||
2 | ||
], | ||
"delivery": [ | ||
1 | ||
], | ||
"api": [ | ||
1, | ||
2, | ||
3, | ||
4 | ||
] | ||
}, | ||
"ext": { | ||
"bidder": { | ||
"org": "72721", | ||
"path": "mvo", | ||
"zone": "1r" | ||
} | ||
} | ||
} | ||
], | ||
"app": { | ||
"id": "fake-app-id", | ||
"name": "MinuteMedia Fake Test App", | ||
"bundle": "12345", | ||
"storeurl": "https://example.com/fake-store-url", | ||
"cat": [ | ||
"IAB15", | ||
"IAB15-10" | ||
], | ||
"ver": "1.0.2", | ||
"publisher": { | ||
"id": "1" | ||
} | ||
}, | ||
"device": { | ||
"dnt": 0, | ||
"ua": "Fake Browser", | ||
"ip": "123.145.167.189", | ||
"carrier": "FakeTelco", | ||
"language": "en", | ||
"make": "Apple", | ||
"model": "iPhone", | ||
"os": "iOS", | ||
"osv": "6.1", | ||
"js": 1, | ||
"ifa": "1234567890ABCDEF", | ||
"connectiontype": 3, | ||
"devicetype": 1 | ||
} | ||
}, | ||
"httpCalls": [ | ||
{ | ||
"expectedRequest": { | ||
"uri": "http://localhost/prebid_server?publisher_id=72721", | ||
"body": { | ||
"id": "test-request-id", | ||
"imp": [ | ||
{ | ||
"id": "test-imp-id", | ||
"banner": { | ||
"format": [ | ||
{ | ||
"w": 300, | ||
"h": 250 | ||
}, | ||
{ | ||
"w": 300, | ||
"h": 300 | ||
} | ||
] | ||
}, | ||
"ext": { | ||
"bidder": { | ||
"org": "72721", | ||
"zone": "1r", | ||
"path": "mvo" | ||
} | ||
} | ||
}, | ||
{ | ||
"id": "test-imp-video-id", | ||
"video": { | ||
"mimes": [ | ||
"video/mp4" | ||
], | ||
"minduration": 1, | ||
"maxduration": 2, | ||
"protocols": [ | ||
1, | ||
2, | ||
5 | ||
], | ||
"w": 1020, | ||
"h": 780, | ||
"startdelay": 1, | ||
"placement": 1, | ||
"playbackmethod": [ | ||
2 | ||
], | ||
"delivery": [ | ||
1 | ||
], | ||
"api": [ | ||
1, | ||
2, | ||
3, | ||
4 | ||
] | ||
}, | ||
"ext": { | ||
"bidder": { | ||
"org": "72721", | ||
"zone": "1r", | ||
"path": "mvo" | ||
} | ||
} | ||
} | ||
], | ||
"app": { | ||
"id": "fake-app-id", | ||
"name": "MinuteMedia Fake Test App", | ||
"bundle": "12345", | ||
"storeurl": "https://example.com/fake-store-url", | ||
"cat": [ | ||
"IAB15", | ||
"IAB15-10" | ||
], | ||
"ver": "1.0.2", | ||
"publisher": { | ||
"id": "1" | ||
} | ||
}, | ||
"device": { | ||
"dnt": 0, | ||
"ua": "Fake Browser", | ||
"ip": "123.145.167.189", | ||
"carrier": "FakeTelco", | ||
"language": "en", | ||
"make": "Apple", | ||
"model": "iPhone", | ||
"os": "iOS", | ||
"osv": "6.1", | ||
"js": 1, | ||
"ifa": "1234567890ABCDEF", | ||
"connectiontype": 3, | ||
"devicetype": 1 | ||
} | ||
} | ||
}, | ||
"mockResponse": { | ||
"status": 200, | ||
"body": { | ||
"id": "test-request-id", | ||
"seatbid": [ | ||
{ | ||
"seat": "958", | ||
"bid": [ | ||
{ | ||
"id": "7706636740145184841", | ||
"impid": "test-imp-video-id", | ||
"price": 0.5, | ||
"adid": "29681110", | ||
"adm": "some-test-ad", | ||
"adomain": [ | ||
"yahoo.com" | ||
], | ||
"cid": "958", | ||
"crid": "29681110", | ||
"h": 576, | ||
"w": 1024, | ||
"mtype": 2 | ||
} | ||
] | ||
} | ||
], | ||
"bidid": "5778926625248726496", | ||
"cur": "USD" | ||
} | ||
} | ||
} | ||
], | ||
"expectedBidResponses": [ | ||
{ | ||
"bids": [ | ||
{ | ||
"bid": { | ||
"id": "7706636740145184841", | ||
"impid": "test-imp-video-id", | ||
"price": 0.5, | ||
"adm": "some-test-ad", | ||
"adid": "29681110", | ||
"adomain": [ | ||
"yahoo.com" | ||
], | ||
"cid": "958", | ||
"crid": "29681110", | ||
"w": 1024, | ||
"h": 576, | ||
"mtype": 2 | ||
}, | ||
"type": "video" | ||
} | ||
] | ||
} | ||
] | ||
} |
Oops, something went wrong.