Skip to content
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

Merged
merged 12 commits into from
Oct 1, 2020
52 changes: 52 additions & 0 deletions adapters/smartyads/params_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package smartyads

import (
"encoding/json"
"testing"

"github.com/prebid/prebid-server/openrtb_ext"
)

var validParams = []string{
`{ "host": "ep1", "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": "ep1", "sourceid": "partner" }`,
`{ "host": "ep1, "accountid": "hash" }`,
`{ "sourceid": "partner", "sourceid": "partner" }`,
}

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)
}
}
}
202 changes: 202 additions & 0 deletions adapters/smartyads/smartyads.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
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"
)

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 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 {
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,
) {

var errors []error
var smartyadsExt *openrtb_ext.ExtSmartyAds
var err error

for i, imp := range openRTBRequest.Imp {
smartyadsExt, err = a.getImpressionExt(&imp)
if err != nil {
errors = append(errors, err)
break
}
openRTBRequest.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(openRTBRequest)
if err != nil {
return nil, []error{err}
}

return []*adapters.RequestData{{
Method: http.MethodPost,
Body: reqJSON,
Uri: url,
Headers: *GetHeaders(openRTBRequest),
}}, 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}
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",
}}
}

if len(bidResp.SeatBid) == 0 {
return nil, []error{&errortypes.BadServerResponse{
Message: "Empty SeatBid array",
}}
}

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
}
11 changes: 11 additions & 0 deletions adapters/smartyads/smartyads_test.go
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}}.example.com/bid?rtb_seat_id={{.SourceId}}&secret_key={{.AccountID}}"))
}
Loading