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

IX: MultiImp Implementation #2779

Merged
merged 7 commits into from
Jul 5, 2023
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
199 changes: 122 additions & 77 deletions adapters/ix/ix.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"sort"
"strings"

"github.com/golang/glog"
"github.com/prebid/prebid-server/adapters"
"github.com/prebid/prebid-server/config"
"github.com/prebid/prebid-server/errortypes"
Expand All @@ -19,8 +20,7 @@ import (
)

type IxAdapter struct {
URI string
maxRequests int
URI string
}

type ExtRequest struct {
Expand All @@ -30,108 +30,128 @@ type ExtRequest struct {
}

type IxDiag struct {
PbsV string `json:"pbsv,omitempty"`
PbjsV string `json:"pbjsv,omitempty"`
PbsV string `json:"pbsv,omitempty"`
PbjsV string `json:"pbjsv,omitempty"`
MultipleSiteIds string `json:"multipleSiteIds,omitempty"`
}

func (a *IxAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
nImp := len(request.Imp)
if nImp > a.maxRequests {
request.Imp = request.Imp[:a.maxRequests]
nImp = a.maxRequests
}

requests := make([]*adapters.RequestData, 0, len(request.Imp))
errs := make([]error, 0)

if err := BuildIxDiag(request); err != nil {
errs = append(errs, err)
}

// Multi-size banner imps are split into single-size requests.
// The first size imp requests are added to the first slice.
// Additional size requests are added to the second slice and are merged with the first at the end.
// Preallocate the max possible size to avoid reallocating arrays.
requests := make([]*adapters.RequestData, 0, a.maxRequests)
multiSizeRequests := make([]*adapters.RequestData, 0, a.maxRequests-nImp)

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

imps := request.Imp
for iImp := range imps {
request.Imp = imps[iImp : iImp+1]
if request.Site != nil {
if err := setSitePublisherId(request, iImp); err != nil {
errs = append(errs, err)
continue
}
uniqueSiteIDs := make(map[string]struct{})
filteredImps := make([]openrtb2.Imp, 0, len(request.Imp))
requestCopy := *request

ixDiag := &IxDiag{}

for _, imp := range requestCopy.Imp {
var err error
ixExt, err := unmarshalToIxExt(&imp)

if err != nil {
errs = append(errs, err)
continue
}

if request.Imp[0].Banner != nil {
banner := *request.Imp[0].Banner
request.Imp[0].Banner = &banner
formats := getBannerFormats(&banner)
for iFmt := range formats {
banner.Format = formats[iFmt : iFmt+1]
banner.W = openrtb2.Int64Ptr(banner.Format[0].W)
banner.H = openrtb2.Int64Ptr(banner.Format[0].H)
if requestData, err := createRequestData(a, request, &headers); err == nil {
if iFmt == 0 {
requests = append(requests, requestData)
} else {
multiSizeRequests = append(multiSizeRequests, requestData)
}
} else {
errs = append(errs, err)
}
if len(multiSizeRequests) == cap(multiSizeRequests) {
break
}
if err = parseSiteId(ixExt, uniqueSiteIDs); err != nil {
errs = append(errs, err)
continue
}

if err := moveSid(&imp, ixExt); err != nil {
errs = append(errs, err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To Confirm: It is your intent to continue with the auction if moveSid fails? Not sure if omitting the continue statement here is intentional or an oversight.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirming it is intentional to still make the bidrequest if that function errors out

}

if imp.Banner != nil {
bannerCopy := *imp.Banner

if len(bannerCopy.Format) == 0 && bannerCopy.W != nil && bannerCopy.H != nil {
bannerCopy.Format = []openrtb2.Format{{W: *bannerCopy.W, H: *bannerCopy.H}}
}
} else if requestData, err := createRequestData(a, request, &headers); err == nil {

if len(bannerCopy.Format) == 1 {
bannerCopy.W = openrtb2.Int64Ptr(bannerCopy.Format[0].W)
bannerCopy.H = openrtb2.Int64Ptr(bannerCopy.Format[0].H)
}
imp.Banner = &bannerCopy
}
filteredImps = append(filteredImps, imp)
}
requestCopy.Imp = filteredImps

setSitePublisherId(&requestCopy, uniqueSiteIDs, ixDiag)

err := setIxDiagIntoExtRequest(&requestCopy, ixDiag)
if err != nil {
errs = append(errs, err)
}

if len(requestCopy.Imp) != 0 {
if requestData, err := createRequestData(a, &requestCopy, &headers); err == nil {
requests = append(requests, requestData)
} else {
errs = append(errs, err)
}
}
request.Imp = imps

return append(requests, multiSizeRequests...), errs
return requests, errs
}

func setSitePublisherId(request *openrtb2.BidRequest, iImp int) error {
if iImp == 0 {
// first impression - create a site and pub copy
site := *request.Site
func setSitePublisherId(requestCopy *openrtb2.BidRequest, uniqueSiteIDs map[string]struct{}, ixDiag *IxDiag) {
if requestCopy.Site != nil {
site := *requestCopy.Site
if site.Publisher == nil {
site.Publisher = &openrtb2.Publisher{}
} else {
publisher := *site.Publisher
site.Publisher = &publisher
}
request.Site = &site

siteIDs := make([]string, 0, len(uniqueSiteIDs))
for key := range uniqueSiteIDs {
siteIDs = append(siteIDs, key)
}
if len(siteIDs) == 1 {
site.Publisher.ID = siteIDs[0]
}
if len(siteIDs) > 1 {
// Sorting siteIDs for predictable output as Go maps don't guarantee order
sort.Strings(siteIDs)
multipleSiteIDs := strings.Join(siteIDs, ", ")
glog.Warningf("Multiple SiteIDs found. %s", multipleSiteIDs)
ixDiag.MultipleSiteIds = multipleSiteIDs
}
requestCopy.Site = &site
}
}

func unmarshalToIxExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpIx, error) {
var bidderExt adapters.ExtImpBidder
if err := json.Unmarshal(request.Imp[0].Ext, &bidderExt); err != nil {
return err
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
return nil, err
}

var ixExt openrtb_ext.ExtImpIx
if err := json.Unmarshal(bidderExt.Bidder, &ixExt); err != nil {
return err
return nil, err
}

request.Site.Publisher.ID = ixExt.SiteId
return nil
return &ixExt, nil
}

func getBannerFormats(banner *openrtb2.Banner) []openrtb2.Format {
if len(banner.Format) == 0 && banner.W != nil && banner.H != nil {
banner.Format = []openrtb2.Format{{W: *banner.W, H: *banner.H}}
func parseSiteId(ixExt *openrtb_ext.ExtImpIx, uniqueSiteIDs map[string]struct{}) error {
if ixExt == nil {
return fmt.Errorf("Nil Ix Ext")
}
if ixExt.SiteId != "" {
uniqueSiteIDs[ixExt.SiteId] = struct{}{}
}
return banner.Format
return nil
}

func createRequestData(a *IxAdapter, request *openrtb2.BidRequest, headers *http.Header) (*adapters.RequestData, error) {
Expand Down Expand Up @@ -180,7 +200,8 @@ func (a *IxAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalReque
}
}

bidderResponse := adapters.NewBidderResponseWithBidsCapacity(5)
// capacity 0 will make channel unbuffered
bidderResponse := adapters.NewBidderResponseWithBidsCapacity(0)
bidderResponse.Currency = bidResponse.Cur

var errs []error
Expand Down Expand Up @@ -275,8 +296,7 @@ func getMediaTypeForBid(bid openrtb2.Bid, impMediaTypeReq map[string]openrtb_ext
// Builder builds a new instance of the Ix adapter for the given bidder with the given config.
func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
bidder := &IxAdapter{
URI: config.Endpoint,
maxRequests: 20,
URI: config.Endpoint,
}
return bidder, nil
}
Expand Down Expand Up @@ -328,29 +348,33 @@ func marshalJsonWithoutUnicode(v interface{}) (string, error) {
return strings.TrimSuffix(sb.String(), "\n"), nil
}

func BuildIxDiag(request *openrtb2.BidRequest) error {
func setIxDiagIntoExtRequest(request *openrtb2.BidRequest, ixDiag *IxDiag) error {
extRequest := &ExtRequest{}
if request.Ext != nil {
if err := json.Unmarshal(request.Ext, &extRequest); err != nil {
return err
}
}
ixdiag := &IxDiag{}

if extRequest.Prebid != nil && extRequest.Prebid.Channel != nil {
ixdiag.PbjsV = extRequest.Prebid.Channel.Version
ixDiag.PbjsV = extRequest.Prebid.Channel.Version
}

// Slice commit hash out of version
if strings.Contains(version.Ver, "-") {
ixdiag.PbsV = version.Ver[:strings.Index(version.Ver, "-")]
ixDiag.PbsV = version.Ver[:strings.Index(version.Ver, "-")]
} else if version.Ver != "" {
ixdiag.PbsV = version.Ver
ixDiag.PbsV = version.Ver
}

// Only set request.ext if ixDiag is not empty
if *ixdiag != (IxDiag{}) {
extRequest.IxDiag = ixdiag
if *ixDiag != (IxDiag{}) {
extRequest := &ExtRequest{}
if request.Ext != nil {
if err := json.Unmarshal(request.Ext, &extRequest); err != nil {
return err
}
}
extRequest.IxDiag = ixDiag
extRequestJson, err := json.Marshal(extRequest)
if err != nil {
return err
Expand All @@ -359,3 +383,24 @@ func BuildIxDiag(request *openrtb2.BidRequest) error {
}
return nil
}

// moves sid from imp[].ext.bidder.sid to imp[].ext.sid
func moveSid(imp *openrtb2.Imp, ixExt *openrtb_ext.ExtImpIx) error {
if ixExt == nil {
return fmt.Errorf("Nil Ix Ext")
}

if ixExt.Sid != "" {
var m map[string]interface{}
if err := json.Unmarshal(imp.Ext, &m); err != nil {
return err
}
m["sid"] = ixExt.Sid
ext, err := json.Marshal(m)
if err != nil {
return err
}
imp.Ext = ext
}
return nil
}
10 changes: 4 additions & 6 deletions adapters/ix/ix_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ const endpoint string = "http://host/endpoint"

func TestJsonSamples(t *testing.T) {
if bidder, err := Builder(openrtb_ext.BidderIx, config.Adapter{Endpoint: endpoint}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}); err == nil {
ixBidder := bidder.(*IxAdapter)
ixBidder.maxRequests = 2
adapterstest.RunJSONBidderTest(t, "ixtest", bidder)
} else {
t.Fatalf("Builder returned unexpected error %v", err)
Expand All @@ -44,7 +42,7 @@ func TestIxMakeBidsWithCategoryDuration(t *testing.T) {
`{
"prebid": {},
"bidder": {
"siteID": 123456
"siteID": "123456"
}
}`,
)},
Expand Down Expand Up @@ -106,7 +104,6 @@ func TestIxMakeBidsWithCategoryDuration(t *testing.T) {

func TestIxMakeRequestWithGppString(t *testing.T) {
bidder := &IxAdapter{}
bidder.maxRequests = 2

testGppString := "DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN"

Expand All @@ -124,7 +121,7 @@ func TestIxMakeRequestWithGppString(t *testing.T) {
`{
"prebid": {},
"bidder": {
"siteID": 123456
"siteId": "123456"
}
}`,
)},
Expand Down Expand Up @@ -256,7 +253,8 @@ func TestBuildIxDiag(t *testing.T) {
for _, test := range testCases {
t.Run(test.description, func(t *testing.T) {
version.Ver = test.pbsVersion
err := BuildIxDiag(test.request)
ixDiag := &IxDiag{}
err := setIxDiagIntoExtRequest(test.request, ixDiag)
if test.expectError {
assert.NotNil(t, err)
} else {
Expand Down
Loading