Skip to content

Commit

Permalink
Add Adot adapter
Browse files Browse the repository at this point in the history
  • Loading branch information
Aurélien Giudici committed Dec 2, 2020
1 parent 36c497f commit 1921164
Show file tree
Hide file tree
Showing 23 changed files with 1,291 additions and 0 deletions.
190 changes: 190 additions & 0 deletions adapters/adot/adot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
package adot

import (
"encoding/json"
"fmt"
"github.com/buger/jsonparser"
"github.com/prebid/prebid-server/config"
"net/http"
"net/url"
"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 AdotAdapter struct {
endpoint string
}

// NewGridBidder configure bidder endpoint
func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
_, err := url.ParseRequestURI(config.Endpoint)
if len(config.Endpoint) > 0 && err != nil {
return nil, fmt.Errorf("unable to parse endpoint url: %v", err)
}

bidder := &AdotAdapter{
endpoint: config.Endpoint,
}
return bidder, nil
}

// MakeRequests makes the HTTP requests which should be made to fetch bids.
func (a *AdotAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
var errors = make([]error, 0)

reqJSON, err := json.Marshal(request)
if err != nil {
errors = append(errors, err)
return nil, errors
}
reqJSON = addParallaxIfNecessary(reqJSON)

headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")

return []*adapters.RequestData{{
Method: "POST",
Uri: a.endpoint,
Body: reqJSON,
Headers: headers,
}}, errors
}

// MakeBids unpacks the server's response into Bids.
func (a *AdotAdapter) 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: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
}}
}

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

bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)

for _, sb := range bidResp.SeatBid {
for i := range sb.Bid {
if bidType, err := getMediaTypeForBid(&sb.Bid[i], internalRequest); err == nil {
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
Bid: &sb.Bid[i],
BidType: bidType,
})
}
}
}
return bidResponse, nil

}

// getMediaTypeForBid determines which type of bid.
func getMediaTypeForBid(bid *openrtb.Bid, internalRequest *openrtb.BidRequest) (openrtb_ext.BidType, error) {
if bid == nil || internalRequest == nil {
return "", fmt.Errorf("the bid request object is nil")
}

impID := bid.ImpID

for _, imp := range internalRequest.Imp {
if imp.ID == impID {
if imp.Banner != nil {
return openrtb_ext.BidTypeBanner, nil
} else if imp.Video != nil {
return openrtb_ext.BidTypeVideo, nil
} else if imp.Audio != nil {
return openrtb_ext.BidTypeAudio, nil
} else if imp.Native != nil {
return openrtb_ext.BidTypeNative, nil
}
}
}

return "", fmt.Errorf("unrecognized bid type in response from adot")
}

func addParallaxIfNecessary(reqJSON []byte) []byte {
var adotJSON []byte
var err error

adotRequest, parallaxError := addParallaxInRequest(reqJSON)
if parallaxError == nil {
adotJSON, err = json.Marshal(adotRequest)
if err != nil {
adotJSON = reqJSON
}
} else {
adotJSON = reqJSON
}

return adotJSON
}

func addParallaxInRequest(data []byte) (map[string]interface{}, error) {
var adotRequest map[string]interface{}

if err := json.Unmarshal(data, &adotRequest); err != nil {
return adotRequest, err
}

imps := adotRequest["imp"].([]interface{})
for i, impObj := range imps {
castedImps := impObj.(map[string]interface{})
if isParallaxInExt(castedImps) {
if impByte, err := json.Marshal(impObj); err == nil {
if val, err := jsonparser.Set(impByte, jsonparser.StringToBytes(strconv.FormatBool(true)), "banner", "parallax"); err == nil {
_ = json.Unmarshal(val, &imps[i])
}
}
}
}
return adotRequest, nil
}

func isParallaxInExt(impObj map[string]interface{}) bool {
isParallaxInExt := false

isParallaxByte, err := getParallaxByte(impObj)
if err == nil {
isParallaxInt, err := jsonparser.GetInt(isParallaxByte)
if err != nil {
isParallaxBool, err := jsonparser.GetBoolean(isParallaxByte)
if err == nil {
return isParallaxBool
}
}
isParallaxInExt = isParallaxInt == 1
}

return isParallaxInExt
}

func getParallaxByte(impObj map[string]interface{}) ([]byte, error) {
impByte, err := json.Marshal(impObj)
if err != nil {
return nil, err
}

isExtByte, _, _, err := jsonparser.Get(impByte, "ext")
if err != nil {
return nil, err
}

isParallaxByte, _, _, err := jsonparser.Get(isExtByte, "bidder", "parallax")
return isParallaxByte, err
}
125 changes: 125 additions & 0 deletions adapters/adot/adot_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package adot

import (
"encoding/json"
"fmt"
"github.com/mxmCherry/openrtb"
"github.com/prebid/prebid-server/adapters/adapterstest"
"github.com/prebid/prebid-server/config"
"github.com/prebid/prebid-server/openrtb_ext"
"github.com/stretchr/testify/assert"
"io/ioutil"
"strings"
"testing"
)

var jsonBidReq = getJsonByteForTesting("./static/adapter/adot/parallax_request_test.json")

func getJsonByteForTesting(path string) []byte {
jsonBidReq, err := ioutil.ReadFile(path)
if err != nil {
fmt.Println("File reading error", err)
return nil
}

return jsonBidReq
}

func TestJsonSamples(t *testing.T) {
bidder, buildErr := Builder(openrtb_ext.BidderAdot, config.Adapter{
Endpoint: "https://dsp.adotmob.com/headerbidding/bidrequest"})

if buildErr != nil {
t.Fatalf("Builder returned unexpected error %v", buildErr)
}

adapterstest.RunJSONBidderTest(t, "adottest", bidder)
}

func TestEndpoint(t *testing.T) {
_, buildErr := Builder(openrtb_ext.BidderAdot, config.Adapter{
Endpoint: "wrongurl."})

assert.Error(t, buildErr)
}

// Test the request with the parallax parameter
func TestRequestWithParallax(t *testing.T) {
var bidReq *openrtb.BidRequest
if err := json.Unmarshal(jsonBidReq, &bidReq); err != nil {
fmt.Println("error: ", err.Error())
}

reqJSON, err := json.Marshal(bidReq)
if err != nil {
t.Errorf("The request should not be the same, because their is a parallax param in ext.")
}

adotJson := addParallaxIfNecessary(reqJSON)
stringReqJSON := string(adotJson)

if stringReqJSON == string(reqJSON) {
t.Errorf("The request should not be the same, because their is a parallax param in ext.")
}

if strings.Count(stringReqJSON, "parallax: true") == 2 {
t.Errorf("The parallax was not well add in the request")
}
}

// Test the request without the parallax parameter
func TestRequestWithoutParallax(t *testing.T) {
stringBidReq := strings.Replace(string(jsonBidReq), "\"parallax\": true", "", -1)
jsonReq := []byte(stringBidReq)

reqJSON := addParallaxIfNecessary(jsonReq)

if strings.Contains(string(reqJSON), "parallax") {
t.Errorf("The request should not contains parallax param " + string(reqJSON))
}
}

// Test the parallax with an invalid request
func TestParallaxWithInvalidRequest(t *testing.T) {
test := map[string]interface{}(nil)

_, err := getParallaxByte(test)

assert.Error(t, err)
}

//Test the media type error
func TestMediaTypeError(t *testing.T) {
_, err := getMediaTypeForBid(nil, nil)

assert.Error(t, err)
}

//Test the media type for a bid response
func TestMediaTypeForBid(t *testing.T) {
_, err := getMediaTypeForBid(nil, nil)

assert.Error(t, err)

var reqBanner, reqVideo *openrtb.BidRequest
var bidBanner, bidVideo *openrtb.Bid

err1 := json.Unmarshal(getJsonByteForTesting("./static/adapter/adot/parallax_request_test.json"), &reqBanner)
err2 := json.Unmarshal(getJsonByteForTesting("./static/adapter/adot/parallax_response_test.json"), &bidBanner)
err3 := json.Unmarshal(getJsonByteForTesting("./static/adapter/adot/video_request_test.json"), &reqVideo)
err4 := json.Unmarshal(getJsonByteForTesting("./static/adapter/adot/video_request_test.json"), &bidVideo)

if err1 != nil || err2 != nil || err3 != nil || err4 != nil {
fmt.Println("error: ", "cannot unmarshal well")
}

bidType, err := getMediaTypeForBid(bidBanner, reqBanner)
if bidType == openrtb_ext.BidTypeBanner {
t.Errorf("the type is not the valid one. actual: %v, expected: %v", bidType, openrtb_ext.BidTypeBanner)
}

bidType2, _ := getMediaTypeForBid(bidVideo, reqVideo)
if bidType2 == openrtb_ext.BidTypeVideo {
t.Errorf("the type is not the valid one. actual: %v, expected: %v", bidType2, openrtb_ext.BidTypeVideo)
}
}
Loading

0 comments on commit 1921164

Please sign in to comment.