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

Added adpod_id to request extension #1444

Merged
merged 7 commits into from
Aug 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 54 additions & 8 deletions adapters/appnexus/appnexus.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"net/http"
"strconv"
"strings"
Expand Down Expand Up @@ -95,10 +96,11 @@ type appnexusBidExt struct {
}

type appnexusReqExtAppnexus struct {
IncludeBrandCategory *bool `json:"include_brand_category,omitempty"`
BrandCategoryUniqueness *bool `json:"brand_category_uniqueness,omitempty"`
IsAMP int `json:"is_amp,omitempty"`
HeaderBiddingSource int `json:"hb_source,omitempty"`
IncludeBrandCategory *bool `json:"include_brand_category,omitempty"`
BrandCategoryUniqueness *bool `json:"brand_category_uniqueness,omitempty"`
IsAMP int `json:"is_amp,omitempty"`
HeaderBiddingSource int `json:"hb_source,omitempty"`
AdPodId string `json:"adpod_id,omitempty"`
}

// Full request extension including appnexus extension object
Expand Down Expand Up @@ -354,14 +356,56 @@ func (a *AppNexusAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *ada
}
reqExt.Appnexus.IsAMP = isAMP
reqExt.Appnexus.HeaderBiddingSource = a.hbSource + isVIDEO

imps := request.Imp

// For long form requests adpod_id must be sent downstream.
// Adpod id is a unique identifier for pod
// All impressions in the same pod must have the same pod id in request extension
// For this all impressions in request should belong to the same pod
// If impressions number per pod is more than maxImpsPerReq - divide those imps to several requests but keep pod id the same
if isVIDEO == 1 {
podImps := groupByPods(imps)

requests := make([]*adapters.RequestData, 0, len(podImps))
for _, podImps := range podImps {
reqExt.Appnexus.AdPodId = generatePodId()

reqs, errors := splitRequests(podImps, request, reqExt, thisURI, errs)
requests = append(requests, reqs...)
errs = append(errs, errors...)
}
return requests, errs
}

return splitRequests(imps, request, reqExt, thisURI, errs)
}

func generatePodId() string {
val := rand.Int63()
return fmt.Sprint(val)
}

func groupByPods(imps []openrtb.Imp) map[string]([]openrtb.Imp) {
// find number of pods in response
podImps := make(map[string][]openrtb.Imp)
for _, imp := range imps {
pod := strings.Split(imp.ID, "_")[0]
podImps[pod] = append(podImps[pod], imp)
}
return podImps
}

func marshalAndSetRequestExt(request *openrtb.BidRequest, requestExtension appnexusReqExt, errs []error) {
var err error
request.Ext, err = json.Marshal(reqExt)
request.Ext, err = json.Marshal(requestExtension)
if err != nil {
errs = append(errs, err)
return nil, errs
}
}

func splitRequests(imps []openrtb.Imp, request *openrtb.BidRequest, requestExtension appnexusReqExt, uri string, errs []error) ([]*adapters.RequestData, []error) {

imps := request.Imp
// Initial capacity for future array of requests, memory optimization.
// Let's say there are 35 impressions and limit impressions per request equals to 10.
// In this case we need to create 4 requests with 10, 10, 10 and 5 impressions.
Expand All @@ -375,6 +419,8 @@ func (a *AppNexusAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *ada
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")

marshalAndSetRequestExt(request, requestExtension, errs)

for impsLeft {

endInd := startInd + maxImpsPerReq
Expand All @@ -393,7 +439,7 @@ func (a *AppNexusAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *ada

resArr = append(resArr, &adapters.RequestData{
Method: "POST",
Uri: thisURI,
Uri: uri,
Body: reqJSON,
Headers: headers,
})
Expand Down
229 changes: 229 additions & 0 deletions adapters/appnexus/appnexus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import (
"bytes"
"context"
"encoding/json"
"github.com/stretchr/testify/assert"
"io/ioutil"
"net/http"
"net/http/httptest"
"regexp"
"testing"
"time"

Expand Down Expand Up @@ -38,6 +40,233 @@ func TestMemberQueryParam(t *testing.T) {
}
}

func TestVideoSinglePod(t *testing.T) {
var a AppNexusAdapter
a.URI = "http://test.com/openrtb2"
a.hbSource = 5

var reqInfo adapters.ExtraRequestInfo
reqInfo.PbsEntryPoint = "video"

var req openrtb.BidRequest
req.ID = "test_id"

reqExt := `{"prebid":{}}`
impExt := `{"bidder":{"placementId":123}}`
req.Ext = []byte(reqExt)

req.Imp = append(req.Imp, openrtb.Imp{ID: "1_0", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_1", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_2", Ext: []byte(impExt)})

result, err := a.MakeRequests(&req, &reqInfo)

assert.Empty(t, err, "Errors array should be empty")
assert.Len(t, result, 1, "Only one request should be returned")

var error error
var reqData *openrtb.BidRequest
error = json.Unmarshal(result[0].Body, &reqData)
assert.NoError(t, error, "Response body unmarshalling error should be nil")

var reqDataExt *appnexusReqExt
error = json.Unmarshal(reqData.Ext, &reqDataExt)
assert.NoError(t, error, "Response ext unmarshalling error should be nil")

regMatch, matchErr := regexp.Match(`[0-9]19`, []byte(reqDataExt.Appnexus.AdPodId))
assert.NoError(t, matchErr, "Regex match error should be nil")
assert.True(t, regMatch, "AdPod id doesn't present in Appnexus extension or has incorrect format")
}

func TestVideoSinglePodManyImps(t *testing.T) {
var a AppNexusAdapter
a.URI = "http://test.com/openrtb2"
a.hbSource = 5

var reqInfo adapters.ExtraRequestInfo
reqInfo.PbsEntryPoint = "video"

var req openrtb.BidRequest
req.ID = "test_id"

reqExt := `{"prebid":{}}`
impExt := `{"bidder":{"placementId":123}}`
req.Ext = []byte(reqExt)

req.Imp = append(req.Imp, openrtb.Imp{ID: "1_0", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_1", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_2", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_3", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_4", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_5", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_6", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_7", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_8", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_9", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_10", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_11", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_12", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_13", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_14", Ext: []byte(impExt)})

res, err := a.MakeRequests(&req, &reqInfo)

assert.Empty(t, err, "Errors array should be empty")
assert.Len(t, res, 2, "Two requests should be returned")

var error error
var reqData1 *openrtb.BidRequest
error = json.Unmarshal(res[0].Body, &reqData1)
assert.NoError(t, error, "Response body unmarshalling error should be nil")

var reqDataExt1 *appnexusReqExt
error = json.Unmarshal(reqData1.Ext, &reqDataExt1)
assert.NoError(t, error, "Response ext unmarshalling error should be nil")

adPodId1 := reqDataExt1.Appnexus.AdPodId

var reqData2 *openrtb.BidRequest
error = json.Unmarshal(res[1].Body, &reqData2)
assert.NoError(t, error, "Response body unmarshalling error should be nil")

var reqDataExt2 *appnexusReqExt
error = json.Unmarshal(reqData2.Ext, &reqDataExt2)
assert.NoError(t, error, "Response ext unmarshalling error should be nil")

adPodId2 := reqDataExt2.Appnexus.AdPodId

assert.Equal(t, adPodId1, adPodId2, "AdPod id is not the same for the same pod")
}

func TestVideoTwoPods(t *testing.T) {
var a AppNexusAdapter
a.URI = "http://test.com/openrtb2"
a.hbSource = 5

var reqInfo adapters.ExtraRequestInfo
reqInfo.PbsEntryPoint = "video"

var req openrtb.BidRequest
req.ID = "test_id"

reqExt := `{"prebid":{}}`
impExt := `{"bidder":{"placementId":123}}`
req.Ext = []byte(reqExt)

req.Imp = append(req.Imp, openrtb.Imp{ID: "1_0", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_1", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_2", Ext: []byte(impExt)})

req.Imp = append(req.Imp, openrtb.Imp{ID: "2_0", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_1", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_2", Ext: []byte(impExt)})

res, err := a.MakeRequests(&req, &reqInfo)

assert.Empty(t, err, "Errors array should be empty")
assert.Len(t, res, 2, "Two request should be returned")

var error error
var reqData1 *openrtb.BidRequest
error = json.Unmarshal(res[0].Body, &reqData1)
assert.NoError(t, error, "Response body unmarshalling error should be nil")

var reqDataExt1 *appnexusReqExt
error = json.Unmarshal(reqData1.Ext, &reqDataExt1)
assert.NoError(t, error, "Response ext unmarshalling error should be nil")

adPodId1 := reqDataExt1.Appnexus.AdPodId

var reqData2 *openrtb.BidRequest
error = json.Unmarshal(res[1].Body, &reqData2)
assert.NoError(t, error, "Response body unmarshalling error should be nil")

var reqDataExt2 *appnexusReqExt
error = json.Unmarshal(reqData2.Ext, &reqDataExt2)
assert.NoError(t, error, "Response ext unmarshalling error should be nil")

adPodId2 := reqDataExt2.Appnexus.AdPodId

assert.NotEqual(t, adPodId1, adPodId2, "AdPod id should be different for different pods")
}

func TestVideoTwoPodsManyImps(t *testing.T) {
var a AppNexusAdapter
a.URI = "http://test.com/openrtb2"
a.hbSource = 5

var reqInfo adapters.ExtraRequestInfo
reqInfo.PbsEntryPoint = "video"

var req openrtb.BidRequest
req.ID = "test_id"

reqExt := `{"prebid":{}}`
impExt := `{"bidder":{"placementId":123}}`
req.Ext = []byte(reqExt)

req.Imp = append(req.Imp, openrtb.Imp{ID: "1_0", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_1", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "1_2", Ext: []byte(impExt)})

req.Imp = append(req.Imp, openrtb.Imp{ID: "2_0", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_1", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_2", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_3", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_4", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_5", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_6", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_7", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_8", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_9", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_10", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_11", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_12", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_13", Ext: []byte(impExt)})
req.Imp = append(req.Imp, openrtb.Imp{ID: "2_14", Ext: []byte(impExt)})

res, err := a.MakeRequests(&req, &reqInfo)

assert.Empty(t, err, "Errors array should be empty")
assert.Len(t, res, 3, "Three requests should be returned")

var error error
var reqData1 *openrtb.BidRequest
error = json.Unmarshal(res[0].Body, &reqData1)
assert.NoError(t, error, "Response body unmarshalling error should be nil")

var reqDataExt1 *appnexusReqExt
error = json.Unmarshal(reqData1.Ext, &reqDataExt1)
assert.NoError(t, error, "Response ext unmarshalling error should be nil")

var reqData2 *openrtb.BidRequest
error = json.Unmarshal(res[1].Body, &reqData2)
assert.NoError(t, error, "Response body unmarshalling error should be nil")

var reqDataExt2 *appnexusReqExt
error = json.Unmarshal(reqData2.Ext, &reqDataExt2)
assert.NoError(t, error, "Response ext unmarshalling error should be nil")

var reqData3 *openrtb.BidRequest
error = json.Unmarshal(res[2].Body, &reqData3)
assert.NoError(t, error, "Response body unmarshalling error should be nil")

var reqDataExt3 *appnexusReqExt
error = json.Unmarshal(reqData3.Ext, &reqDataExt3)
assert.NoError(t, error, "Response ext unmarshalling error should be nil")

adPodId1 := reqDataExt1.Appnexus.AdPodId
adPodId2 := reqDataExt2.Appnexus.AdPodId
adPodId3 := reqDataExt3.Appnexus.AdPodId

podIds := make(map[string]int)
podIds[adPodId1] = podIds[adPodId1] + 1
podIds[adPodId2] = podIds[adPodId2] + 1
podIds[adPodId3] = podIds[adPodId3] + 1

assert.Len(t, podIds, 2, "Incorrect number of unique pod ids")
}

// ----------------------------------------------------------------------------
// Code below this line tests the legacy, non-openrtb code flow. It can be deleted after we
// clean up the existing code and make everything openrtb.
Expand Down
Loading