-
Notifications
You must be signed in to change notification settings - Fork 735
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
Smarty ads adapter #1500
Smarty ads adapter #1500
Changes from 3 commits
f77a03e
54c92b4
fd2a54c
eec96b5
ee3bcda
8f507d5
8ee4ac3
f575e6b
5ca2242
b46c9f6
a490ed3
f343bb6
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,51 @@ | ||
package smartyads | ||
|
||
import ( | ||
"encoding/json" | ||
"testing" | ||
|
||
"github.com/prebid/prebid-server/openrtb_ext" | ||
) | ||
|
||
var validParams = []string{ | ||
`{ "host": "smartyadstest.com", "sourceid": "partner", "accountid": "hash" }`, | ||
} | ||
|
||
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.BidderSmartyAds, json.RawMessage(validParam)); err != nil { | ||
t.Errorf("Schema rejected SmartyAds params: %s", validParam) | ||
} | ||
} | ||
} | ||
|
||
var invalidParams = []string{ | ||
``, | ||
`null`, | ||
`true`, | ||
`5`, | ||
`4.2`, | ||
`[]`, | ||
`{}`, | ||
`{"adCode": "string", "seatCode": 5, "originalPublisherid": "string"}`, | ||
`{ "host": "smartyadstest.com", "sourceid": "partner" }`, | ||
`{ "host": "smartyadstest.com", "accountid": "hash" }`, | ||
} | ||
|
||
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.BidderSmartyAds, json.RawMessage(invalidParam)); err == nil { | ||
t.Errorf("Schema allowed unexpected params: %s", invalidParam) | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,209 @@ | ||
package smartyads | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"strconv" | ||
"text/template" | ||
|
||
"github.com/mxmCherry/openrtb" | ||
"github.com/prebid/prebid-server/adapters" | ||
"github.com/prebid/prebid-server/errortypes" | ||
"github.com/prebid/prebid-server/macros" | ||
"github.com/prebid/prebid-server/openrtb_ext" | ||
) | ||
|
||
// Implements Bidder interface. | ||
type SmartyAdsAdapter struct { | ||
endpoint template.Template | ||
} | ||
|
||
func NewSmartyAdsBidder(endpointTemplate string) *SmartyAdsAdapter { | ||
template, err := template.New("endpointTemplate").Parse(endpointTemplate) | ||
if err != nil { | ||
return nil | ||
} | ||
return &SmartyAdsAdapter{endpoint: *template} | ||
} | ||
|
||
func (a *SmartyAdsAdapter) CheckHasImps(request *openrtb.BidRequest) error { | ||
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. This check isn't necessary. The "core" code will not call an adapter if there are no imps. |
||
if len(request.Imp) == 0 { | ||
err := &errortypes.BadInput{ | ||
Message: "Missing Imp Object", | ||
} | ||
return err | ||
} | ||
return 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.IP) > 0 { | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
headers.Add("X-Forwarded-For", request.Device.IP) | ||
} | ||
|
||
if len(request.Device.Language) > 0 { | ||
headers.Add("Accept-Language", request.Device.Language) | ||
} | ||
|
||
if request.Device.DNT != nil { | ||
headers.Add("Dnt", strconv.Itoa(int(*request.Device.DNT))) | ||
} | ||
} | ||
|
||
return &headers | ||
} | ||
|
||
func (a *SmartyAdsAdapter) MakeRequests( | ||
openRTBRequest *openrtb.BidRequest, | ||
reqInfo *adapters.ExtraRequestInfo, | ||
) ( | ||
requestsToBidder []*adapters.RequestData, | ||
errs []error, | ||
) { | ||
|
||
request := *openRTBRequest | ||
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 request that is passed to |
||
|
||
if noImps := a.CheckHasImps(&request); noImps != nil { | ||
return nil, []error{noImps} | ||
} | ||
|
||
var errors []error | ||
var smartyadsExt *openrtb_ext.ExtSmartyAds | ||
var err error | ||
|
||
for i, imp := range request.Imp { | ||
smartyadsExt, err = a.getImpressionExt(&imp) | ||
if err != nil { | ||
errors = append(errors, err) | ||
break | ||
} | ||
request.Imp[i].Ext = nil | ||
} | ||
|
||
if len(errors) > 0 { | ||
return nil, errors | ||
} | ||
|
||
url, err := a.buildEndpointURL(smartyadsExt) | ||
if err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
reqJSON, err := json.Marshal(request) | ||
if err != nil { | ||
return nil, []error{err} | ||
} | ||
|
||
return []*adapters.RequestData{{ | ||
Method: http.MethodPost, | ||
Body: reqJSON, | ||
Uri: url, | ||
Headers: *GetHeaders(&request), | ||
}}, nil | ||
} | ||
|
||
func (a *SmartyAdsAdapter) getImpressionExt(imp *openrtb.Imp) (*openrtb_ext.ExtSmartyAds, error) { | ||
var bidderExt adapters.ExtImpBidder | ||
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { | ||
return nil, &errortypes.BadInput{ | ||
Message: "ext.bidder not provided", | ||
} | ||
} | ||
var smartyadsExt openrtb_ext.ExtSmartyAds | ||
if err := json.Unmarshal(bidderExt.Bidder, &smartyadsExt); err != nil { | ||
return nil, &errortypes.BadInput{ | ||
Message: "ext.bidder not provided", | ||
} | ||
} | ||
return &smartyadsExt, nil | ||
} | ||
|
||
func (a *SmartyAdsAdapter) buildEndpointURL(params *openrtb_ext.ExtSmartyAds) (string, error) { | ||
endpointParams := macros.EndpointTemplateParams{Host: params.Host, SourceId: params.SourceId, AccountID: params.AccountID} | ||
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. Adapters may not allow the publisher to specify the entire endpoint domain. I can offer more specific suggestions if you could please give us more context on why a publisher would specify the host. 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. Hello! @SyntaxNode , thanks for you review! Our company has different ns for different regions such as (US_EAST, EU, SGP) . We need to use for example endpoints ns1.example.com for EU and ns2.example.com for SGP. That is main reason when I use host variable. 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. Should the region not be controlled by the PBS host (as in adapter config) rather than publisher, as the adapter host would control which endpoint is "closest"? Or if the region specific requirement is driven by which advertisers are active on an endpoint (east coat advertisers just want to bid on requests coming to the US_EAST node), then from your example have host = "ns1" and hardcode ".example.com" into your adapter's URL. The reasoning behind this is we don't want PBS to be used as an open relay. For example, with an openended host setting, someone could have "host": "www.disney.com" and then flood PBS installs with requests which would then be forwarded to www.disney.com to try to overload their website. Given that Disney will see all the requests coming from PBS hosts rather than the original attacker, it will be most difficult or impossible to find the one responsible. 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. @hhhjort , thanks for your answer ! I will change host to hardcode ".example.com" domain with ns1 as parameter! Thank you for clarification! |
||
return macros.ResolveMacros(a.endpoint, endpointParams) | ||
} | ||
|
||
func (a *SmartyAdsAdapter) CheckResponseStatusCodes(response *adapters.ResponseData) error { | ||
if response.StatusCode == http.StatusNoContent { | ||
return &errortypes.BadInput{Message: "No bid response"} | ||
} | ||
|
||
if response.StatusCode == http.StatusBadRequest { | ||
return &errortypes.BadInput{ | ||
Message: fmt.Sprintf("Unexpected status code: [ %d ]", response.StatusCode), | ||
} | ||
} | ||
|
||
if response.StatusCode == http.StatusServiceUnavailable { | ||
return &errortypes.BadInput{ | ||
Message: fmt.Sprintf("Something went wrong, please contact your Account Manager. Status Code: [ %d ] ", response.StatusCode), | ||
} | ||
} | ||
|
||
if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices { | ||
return &errortypes.BadInput{ | ||
Message: fmt.Sprintf("Something went wrong, please contact your Account Manager. Status Code: [ %d ] ", response.StatusCode), | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (a *SmartyAdsAdapter) MakeBids( | ||
openRTBRequest *openrtb.BidRequest, | ||
requestToBidder *adapters.RequestData, | ||
bidderRawResponse *adapters.ResponseData, | ||
) ( | ||
bidderResponse *adapters.BidderResponse, | ||
errs []error, | ||
) { | ||
httpStatusError := a.CheckResponseStatusCodes(bidderRawResponse) | ||
if httpStatusError != nil { | ||
return nil, []error{httpStatusError} | ||
} | ||
|
||
responseBody := bidderRawResponse.Body | ||
var bidResp openrtb.BidResponse | ||
if err := json.Unmarshal(responseBody, &bidResp); err != nil { | ||
return nil, []error{&errortypes.BadServerResponse{ | ||
Message: "Bad Server Response", | ||
}} | ||
} | ||
|
||
bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) | ||
SyntaxNode marked this conversation as resolved.
Show resolved
Hide resolved
|
||
sb := bidResp.SeatBid[0] | ||
|
||
for _, bid := range sb.Bid { | ||
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ | ||
Bid: &bid, | ||
BidType: getMediaTypeForImp(bid.ImpID, openRTBRequest.Imp), | ||
}) | ||
} | ||
return bidResponse, nil | ||
} | ||
|
||
func getMediaTypeForImp(impId string, imps []openrtb.Imp) openrtb_ext.BidType { | ||
mediaType := openrtb_ext.BidTypeBanner | ||
for _, imp := range imps { | ||
if imp.ID == impId { | ||
if imp.Video != nil { | ||
mediaType = openrtb_ext.BidTypeVideo | ||
} else if imp.Native != nil { | ||
mediaType = openrtb_ext.BidTypeNative | ||
} | ||
return mediaType | ||
} | ||
} | ||
return mediaType | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package smartyads | ||
|
||
import ( | ||
"testing" | ||
|
||
"github.com/prebid/prebid-server/adapters/adapterstest" | ||
) | ||
|
||
func TestJsonSamples(t *testing.T) { | ||
adapterstest.RunJSONBidderTest(t, "smartyadstest", NewSmartyAdsBidder("http://{{.Host}}/bid?rtb_seat_id={{.SourceId}}&secret_key={{.AccountID}}")) | ||
} |
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.
Please follow the Go standard for comments, such as
SmartyAdsAdapter ...
or remove the comment entirely.