Skip to content

Commit

Permalink
Adding Smartadserver adapter (#1346)
Browse files Browse the repository at this point in the history
Co-authored-by: tadam <tadam@smartadserver.com>
  • Loading branch information
tadam75 and tadam authored Jun 20, 2020
1 parent 9c79ee4 commit 5f39344
Show file tree
Hide file tree
Showing 27 changed files with 1,198 additions and 0 deletions.
61 changes: 61 additions & 0 deletions adapters/smartadserver/params_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package smartadserver

import (
"encoding/json"
"testing"

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

// This file actually intends to test static/bidder-params/smartadserver.json
//
// These also validate the format of the external API: request.imp[i].ext.smartadserver

// TestValidParams makes sure that the smartadserver schema accepts all imp.ext fields which we intend to support.
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.BidderSmartadserver, json.RawMessage(validParam)); err != nil {
t.Errorf("Schema rejected smartadserver params: %s \n Error: %s", validParam, err)
}
}
}

// TestInvalidParams makes sure that the smartadserver schema rejects all the imp.ext fields we don't support.
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.BidderSmartadserver, json.RawMessage(invalidParam)); err == nil {
t.Errorf("Schema allowed unexpected params: %s", invalidParam)
}
}
}

var validParams = []string{
`{"networkId":73}`,
`{"networkId":73,"siteId":1,"pageId":2,"formatId":3}`,
}

var invalidParams = []string{
``,
`null`,
`true`,
`5`,
`4.2`,
`[]`,
`{}`,
`{"networkId":"73"}`,
`{"networkId":"73","siteId":"1","pageId":"2","formatId":"3"}`,
`{"siteId":1,"pageId":2,"formatId":3}`,
`{"networkId":73,"pageId":2,"formatId":3}`,
`{"networkId":73,"siteId":1,"formatId":3}`,
`{"networkId":73,"siteId":1,"pageId":2}`,
}
179 changes: 179 additions & 0 deletions adapters/smartadserver/smartadserver.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
package smartadserver

import (
"encoding/json"
"fmt"
"net/http"
"net/url"
"path"
"strconv"

"github.com/mxmCherry/openrtb"
"github.com/prebid/prebid-server/adapters"
"github.com/prebid/prebid-server/errortypes"
"github.com/prebid/prebid-server/openrtb_ext"
)

type SmartAdserverAdapter struct {
host string
}

func NewSmartadserverBidder(host string) *SmartAdserverAdapter {
return &SmartAdserverAdapter{
host: host,
}
}

// MakeRequests makes the HTTP requests which should be made to fetch bids.
func (a *SmartAdserverAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
if len(request.Imp) == 0 {
return nil, []error{&errortypes.BadInput{
Message: "No impression in the bid request",
}}
}

var adapterRequests []*adapters.RequestData
var errs []error

// We copy the original request.
smartRequest := *request

// We create or copy the Site object.
if smartRequest.Site == nil {
smartRequest.Site = &openrtb.Site{}
} else {
site := *smartRequest.Site
smartRequest.Site = &site
}

// We create or copy the Publisher object.
if smartRequest.Site.Publisher == nil {
smartRequest.Site.Publisher = &openrtb.Publisher{}
} else {
publisher := *smartRequest.Site.Publisher
smartRequest.Site.Publisher = &publisher
}

// We send one serialized "smartRequest" per impression of the original request.
for _, imp := range request.Imp {
var bidderExt adapters.ExtImpBidder
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
errs = append(errs, &errortypes.BadInput{
Message: "Error parsing bidderExt object",
})
continue
}

var smartadserverExt openrtb_ext.ExtImpSmartadserver
if err := json.Unmarshal(bidderExt.Bidder, &smartadserverExt); err != nil {
errs = append(errs, &errortypes.BadInput{
Message: "Error parsing smartadserverExt parameters",
})
continue
}

// Adding publisher id.
smartRequest.Site.Publisher.ID = strconv.Itoa(smartadserverExt.NetworkID)

// We send one request for each impression.
smartRequest.Imp = []openrtb.Imp{imp}

var errMarshal error
if imp.Ext, errMarshal = json.Marshal(smartadserverExt); errMarshal != nil {
errs = append(errs, &errortypes.BadInput{
Message: errMarshal.Error(),
})
continue
}

reqJSON, err := json.Marshal(smartRequest)
if err != nil {
errs = append(errs, &errortypes.BadInput{
Message: "Error parsing reqJSON object",
})
continue
}

url, err := a.BuildEndpointURL(&smartadserverExt)
if url == "" {
errs = append(errs, err)
continue
}

headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")
adapterRequests = append(adapterRequests, &adapters.RequestData{
Method: "POST",
Uri: url,
Body: reqJSON,
Headers: headers,
})
}
return adapterRequests, errs
}

// MakeBids unpacks the server's response into Bids.
func (a *SmartAdserverAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
if response.StatusCode == http.StatusNoContent {
return nil, nil
}

if response.StatusCode == http.StatusBadRequest {
return nil, []error{&errortypes.BadInput{
Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
}}
}

if response.StatusCode != http.StatusOK {
return nil, []error{&errortypes.BadServerResponse{
Message: "Unexpected status code: " + strconv.Itoa(response.StatusCode) + ". Run with request.debug = 1 for more info",
}}
}

var bidResp openrtb.BidResponse
if err := json.Unmarshal(response.Body, &bidResp); err != nil {
return nil, []error{err}
}

bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)

for _, sb := range bidResp.SeatBid {
for i := 0; i < len(sb.Bid); i++ {
bid := sb.Bid[i]
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
Bid: &bid,
BidType: getMediaTypeForImp(bid.ImpID, internalRequest.Imp),
})

}
}
return bidResponse, []error{}
}

// BuildEndpointURL : Builds endpoint url
func (a *SmartAdserverAdapter) BuildEndpointURL(params *openrtb_ext.ExtImpSmartadserver) (string, error) {
uri, err := url.Parse(a.host)
if err != nil || uri.Scheme == "" || uri.Host == "" {
return "", &errortypes.BadInput{
Message: "Malformed URL: " + a.host + ".",
}
}

uri.Path = path.Join(uri.Path, "api/bid")
uri.RawQuery = "callerId=5"

return uri.String(), nil
}

func getMediaTypeForImp(impID string, imps []openrtb.Imp) openrtb_ext.BidType {
for _, imp := range imps {
if imp.ID == impID {
if imp.Video != nil {
return openrtb_ext.BidTypeVideo
}
return openrtb_ext.BidTypeBanner
}
}
return openrtb_ext.BidTypeBanner
}
11 changes: 11 additions & 0 deletions adapters/smartadserver/smartadserver_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package smartadserver

import (
"testing"

"github.com/prebid/prebid-server/adapters/adapterstest"
)

func TestJsonSamples(t *testing.T) {
adapterstest.RunJSONBidderTest(t, "smartadservertest", NewSmartadserverBidder("https://ssb.smartadserver.com"))
}
Loading

0 comments on commit 5f39344

Please sign in to comment.