-
Notifications
You must be signed in to change notification settings - Fork 807
New Adapter: UNICORN #1719
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: UNICORN #1719
Changes from 19 commits
b699f42
956aa91
9bdb44b
0515c28
36ae31f
ec49639
725321d
003e27d
95844a4
ca3026a
285c5d7
f1c2427
42ae15a
019800d
7be6aa9
8b9dcd1
f067fac
8cb4906
74b363a
3b1f698
1080b56
435bf6e
c27da8b
fb58a7e
deaf6c0
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,61 @@ | ||
package unicorn | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/prebid/prebid-server/openrtb_ext" | ||
) | ||
|
||
func TestValidParams(t *testing.T) { | ||
validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") | ||
if err != nil { | ||
t.Fatalf("Failed to fetch the json schema. %v", err) | ||
} | ||
|
||
for _, p := range validParams { | ||
if err := validator.Validate(openrtb_ext.BidderUnicorn, json.RawMessage(p)); err != nil { | ||
t.Errorf("Schema rejected valid params: %s", p) | ||
} | ||
} | ||
} | ||
|
||
func TestInvalidParams(t *testing.T) { | ||
validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") | ||
if err != nil { | ||
t.Fatalf("Failed to fetch the json schema. %v", err) | ||
} | ||
|
||
for _, p := range invalidParams { | ||
if err := validator.Validate(openrtb_ext.BidderUnicorn, json.RawMessage(p)); err == nil { | ||
t.Errorf("Schema allowed invalid params: %s", p) | ||
} | ||
} | ||
} | ||
|
||
var validParams = []string{ | ||
`{ | ||
"accountId": 199578, | ||
"publisherId": 123456, | ||
"mediaId": "test_media", | ||
"placementId": "test_placement" | ||
}`, | ||
`{ | ||
"accountId": 199578, | ||
"mediaId": "test_media" | ||
}`, | ||
} | ||
|
||
var invalidParams = []string{ | ||
`{}`, | ||
`{ | ||
"accountId": "199578", | ||
"publisherId": "123456", | ||
"mediaId": 12345, | ||
"placementId": 12345 | ||
}`, | ||
`{ | ||
"publisherId": 123456, | ||
"placementId": "test_placement" | ||
}`, | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,285 @@ | ||
package unicorn | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/buger/jsonparser" | ||
"github.com/mxmCherry/openrtb" | ||
"github.com/prebid/prebid-server/adapters" | ||
"github.com/prebid/prebid-server/config" | ||
"github.com/prebid/prebid-server/errortypes" | ||
"github.com/prebid/prebid-server/openrtb_ext" | ||
) | ||
|
||
// UnicornAdapter describes a UNICORN prebid server adapter. | ||
type UnicornAdapter struct { | ||
endpoint string | ||
} | ||
|
||
// unicornImpExt is imp ext for UNICORN | ||
type unicornImpExt struct { | ||
Context *unicornImpExtContext `json:"context,omitempty"` | ||
Bidder openrtb_ext.ExtImpUnicorn `json:"bidder"` | ||
} | ||
|
||
type unicornImpExtContext struct { | ||
Data interface{} `json:"data,omitempty"` | ||
} | ||
|
||
// unicornSourceExt is source ext for UNICORN | ||
type unicornSourceExt struct { | ||
Stype string `json:"stype"` | ||
Bidder string `json"bidder"` | ||
} | ||
|
||
// unicornExt is ext for UNICORN | ||
type unicornExt struct { | ||
Prebid *openrtb_ext.ExtImpPrebid `json:"prebid,omitempty"` | ||
AccountID int64 `json:"accountId,omitempty"` | ||
} | ||
|
||
// Builder builds a new instance of the Foo adapter for the given bidder with the given config. | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) { | ||
bidder := &UnicornAdapter{ | ||
endpoint: config.Endpoint, | ||
} | ||
return bidder, nil | ||
} | ||
|
||
// MakeRequests makes the HTTP requests which should be made to fetch bids. | ||
func (a *UnicornAdapter) MakeRequests(request *openrtb.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { | ||
var extRegs openrtb_ext.ExtRegs | ||
if request.Regs != nil { | ||
if request.Regs.COPPA == 1 { | ||
return nil, []error{&errortypes.BadInput{ | ||
Message: "COPPA is not supported", | ||
}} | ||
} | ||
if err := json.Unmarshal(request.Regs.Ext, &extRegs); err == nil { | ||
if extRegs.GDPR != nil && (*extRegs.GDPR == 1) { | ||
return nil, []error{&errortypes.BadInput{ | ||
Message: "GDPR is not supported", | ||
}} | ||
} | ||
if extRegs.USPrivacy != "" { | ||
return nil, []error{&errortypes.BadInput{ | ||
Message: "CCPA is not supported", | ||
}} | ||
} | ||
} | ||
} | ||
|
||
imp, err := setImps(request, requestInfo) | ||
if err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
request.Imp = imp | ||
|
||
request.Source.Ext, err = setSourceExt() | ||
if err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
request.Ext, err = setExt(request) | ||
if err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
requestJSON, err := json.Marshal(request) | ||
if err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
requestData := &adapters.RequestData{ | ||
Method: "POST", | ||
Uri: a.endpoint, | ||
Body: requestJSON, | ||
Headers: getHeaders(request), | ||
} | ||
|
||
return []*adapters.RequestData{requestData}, nil | ||
} | ||
|
||
func getHeaders(request *openrtb.BidRequest) http.Header { | ||
headers := http.Header{} | ||
headers.Add("Content-Type", "application/json;charset=utf-8") | ||
headers.Add("Accept", "application/json") | ||
headers.Add("X-Openrtb-Version", "2.5") | ||
|
||
if request.Device != nil { | ||
if len(request.Device.UA) > 0 { | ||
headers.Add("User-Agent", request.Device.UA) | ||
} | ||
|
||
if len(request.Device.IPv6) > 0 { | ||
headers.Add("X-Forwarded-For", request.Device.IPv6) | ||
} | ||
|
||
if len(request.Device.IP) > 0 { | ||
headers.Add("X-Forwarded-For", request.Device.IP) | ||
} | ||
} | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
return headers | ||
} | ||
|
||
func setImps(request *openrtb.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]openrtb.Imp, error) { | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for i := 0; i < len(request.Imp); i++ { | ||
imp := &request.Imp[i] | ||
|
||
var ext unicornImpExt | ||
err := json.Unmarshal(imp.Ext, &ext) | ||
|
||
if err != nil { | ||
return nil, &errortypes.BadInput{ | ||
Message: fmt.Sprintf("Error while decoding imp[%d].ext: %s", i, err), | ||
} | ||
} | ||
|
||
var placementID string | ||
if ext.Bidder.PlacementID != "" { | ||
placementID = ext.Bidder.PlacementID | ||
} else { | ||
placementID, err = getStoredRequestImpID(imp) | ||
if err != nil { | ||
return nil, &errortypes.BadInput{ | ||
Message: fmt.Sprintf("Error get StoredRequestImpID from imp[%d]: %s", i, err), | ||
} | ||
} | ||
} | ||
|
||
ext.Bidder.PlacementID = placementID | ||
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. The fact that there's a scenario where
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. Certainly, fixed it. Thanks you. |
||
|
||
imp.Ext, err = json.Marshal(ext) | ||
if err != nil { | ||
return nil, &errortypes.BadInput{ | ||
Message: fmt.Sprintf("Error while encoding imp[%d].ext: %s", i, err), | ||
} | ||
} | ||
|
||
secure := int8(1) | ||
imp.Secure = &secure | ||
imp.TagID = placementID | ||
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. We seem to not be doing anything with
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. No, 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. Ohh you are right. Misread line 126 as a local copy of the imp ( |
||
} | ||
return request.Imp, nil | ||
} | ||
|
||
func getStoredRequestImpID(imp *openrtb.Imp) (string, error) { | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
var impExt map[string]json.RawMessage | ||
|
||
err := json.Unmarshal(imp.Ext, &impExt) | ||
|
||
if err != nil { | ||
return "", fmt.Errorf("Error while decoding ext because: %s", err) | ||
} | ||
|
||
rawPrebidExt, ok := impExt[openrtb_ext.PrebidExtKey] | ||
|
||
if !ok { | ||
return "", fmt.Errorf("ext.prebid is null") | ||
} | ||
|
||
var prebidExt openrtb_ext.ExtImpPrebid | ||
|
||
err = json.Unmarshal(rawPrebidExt, &prebidExt) | ||
|
||
if err != nil { | ||
return "", fmt.Errorf("cannot decoding ext.prebid because: %s", err) | ||
} | ||
|
||
if prebidExt.StoredRequest == nil { | ||
return "", fmt.Errorf("ext.prebid.storedrequest is null") | ||
} | ||
|
||
return prebidExt.StoredRequest.ID, nil | ||
} | ||
|
||
func setSourceExt() (json.RawMessage, error) { | ||
siteExt, err := json.Marshal(unicornSourceExt{ | ||
Stype: "prebid_server_uncn", | ||
Bidder: "unicorn", | ||
}) | ||
if err != nil { | ||
return nil, &errortypes.BadInput{ | ||
Message: fmt.Sprintf("Error while encoding source.ext, err: %s", err), | ||
} | ||
} | ||
return siteExt, nil | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
func setExt(request *openrtb.BidRequest) (json.RawMessage, error) { | ||
accountID, err := jsonparser.GetInt(request.Imp[0].Ext, "bidder", "accountId") | ||
if err != nil { | ||
accountID = 0 | ||
} | ||
var decodedExt *unicornExt | ||
err = json.Unmarshal(request.Ext, &decodedExt) | ||
if err != nil { | ||
decodedExt = &unicornExt{ | ||
Prebid: nil, | ||
} | ||
} | ||
decodedExt.AccountID = accountID | ||
|
||
ext, err := json.Marshal(decodedExt) | ||
if err != nil { | ||
return nil, &errortypes.BadInput{ | ||
Message: fmt.Sprintf("Error while encoding ext, err: %s", err), | ||
} | ||
} | ||
return ext, nil | ||
} | ||
|
||
// MakeBids unpacks the server's response into Bids. | ||
func (a *UnicornAdapter) MakeBids(request *openrtb.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { | ||
|
||
if responseData.StatusCode == http.StatusNoContent { | ||
return nil, nil | ||
} | ||
|
||
if responseData.StatusCode == http.StatusBadRequest { | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
err := &errortypes.BadInput{ | ||
Message: "Unexpected http status code: 400", | ||
} | ||
return nil, []error{err} | ||
} | ||
|
||
if responseData.StatusCode != http.StatusOK { | ||
err := &errortypes.BadServerResponse{ | ||
Message: fmt.Sprintf("Unexpected http status code: %d", responseData.StatusCode), | ||
} | ||
return nil, []error{err} | ||
} | ||
|
||
var response openrtb.BidResponse | ||
if err := json.Unmarshal(responseData.Body, &response); err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) | ||
bidResponse.Currency = response.Cur | ||
for _, seatBid := range response.SeatBid { | ||
for _, bid := range seatBid.Bid { | ||
var bidType openrtb_ext.BidType | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
for _, imp := range request.Imp { | ||
if imp.ID == bid.ImpID { | ||
if imp.Banner != nil { | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
bidType = openrtb_ext.BidTypeBanner | ||
} | ||
if imp.Native != nil { | ||
bidType = openrtb_ext.BidTypeNative | ||
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. The 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. Native will support it in the future, so I left it, but since it is not currently supported, I will remove this branch. 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. Fixed it |
||
} | ||
} | ||
} | ||
b := &adapters.TypedBid{ | ||
Bid: &bid, | ||
BidType: bidType, | ||
} | ||
bidResponse.Bids = append(bidResponse.Bids, b) | ||
} | ||
} | ||
return bidResponse, nil | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package unicorn | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/prebid/prebid-server/adapters/adapterstest" | ||
"github.com/prebid/prebid-server/config" | ||
"github.com/prebid/prebid-server/openrtb_ext" | ||
) | ||
|
||
func TestJsonSamples(t *testing.T) { | ||
bidder, buildErr := Builder(openrtb_ext.BidderUnicorn, config.Adapter{ | ||
Endpoint: "http://localhost:4000"}) | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if buildErr != nil { | ||
t.Fatalf("Builder returned unexpected error %v", buildErr) | ||
} | ||
|
||
adapterstest.RunJSONBidderTest(t, "unicorntest", bidder) | ||
} |
Uh oh!
There was an error while loading. Please reload this page.