Skip to content

Commit

Permalink
AMX Adapter: support native format (prebid#2220)
Browse files Browse the repository at this point in the history
  • Loading branch information
nickjacob authored and shunj-nb committed Nov 8, 2022
1 parent 4ecd5d9 commit 179ec63
Show file tree
Hide file tree
Showing 11 changed files with 400 additions and 300 deletions.
36 changes: 6 additions & 30 deletions adapters/amx/amx.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"net/http"
"net/url"
"strings"

"github.com/mxmCherry/openrtb/v15/openrtb2"
"github.com/prebid/prebid-server/adapters"
Expand All @@ -14,10 +13,8 @@ import (
"github.com/prebid/prebid-server/openrtb_ext"
)

const vastImpressionFormat = "<Impression><![CDATA[%s]]></Impression>"
const vastSearchPoint = "</Impression>"
const nbrHeaderName = "x-nbr"
const adapterVersion = "pbs1.1"
const adapterVersion = "pbs1.2"

// AMXAdapter is the AMX bid adapter
type AMXAdapter struct {
Expand Down Expand Up @@ -114,8 +111,8 @@ func (adapter *AMXAdapter) MakeRequests(request *openrtb2.BidRequest, req *adapt
}

type amxBidExt struct {
Himp []string `json:"himp,omitempty"`
StartDelay *int `json:"startdelay,omitempty"`
StartDelay *int `json:"startdelay,omitempty"`
CreativeType *int `json:"ct,omitempty"`
}

// MakeBids will parse the bids from the AMX server
Expand Down Expand Up @@ -161,11 +158,6 @@ func (adapter *AMXAdapter) MakeBids(request *openrtb2.BidRequest, externalReques
Bid: &bid,
BidType: bidType,
}
if b.BidType == openrtb_ext.BidTypeVideo {
b.Bid.AdM = interpolateImpressions(bid, bidExt)
// remove the NURL so a client/player doesn't fire it twice
b.Bid.NURL = ""
}

bidResponse.Bids = append(bidResponse.Bids, b)
}
Expand All @@ -189,25 +181,9 @@ func getMediaTypeForBid(bidExt amxBidExt) openrtb_ext.BidType {
return openrtb_ext.BidTypeVideo
}

return openrtb_ext.BidTypeBanner
}

func pixelToImpression(pixel string) string {
return fmt.Sprintf(vastImpressionFormat, pixel)
}

func interpolateImpressions(bid openrtb2.Bid, ext amxBidExt) string {
var buffer strings.Builder
if bid.NURL != "" {
buffer.WriteString(pixelToImpression(bid.NURL))
}

for _, impPixel := range ext.Himp {
if impPixel != "" {
buffer.WriteString(pixelToImpression(impPixel))
}
if bidExt.CreativeType != nil && *bidExt.CreativeType == 10 {
return openrtb_ext.BidTypeNative
}

results := strings.Replace(bid.AdM, vastSearchPoint, vastSearchPoint+buffer.String(), 1)
return results
return openrtb_ext.BidTypeBanner
}
95 changes: 59 additions & 36 deletions adapters/amx/amx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package amx
import (
"encoding/json"
"fmt"
"regexp"
"testing"

"github.com/mxmCherry/openrtb/v15/openrtb2"
Expand Down Expand Up @@ -174,42 +173,66 @@ func TestMakeRequestsPublisherId(t *testing.T) {
}
}

var vastImpressionRXP = regexp.MustCompile(`<Impression><!\[CDATA\[[^\]]*\]\]></Impression>`)
func TestMakeBids(t *testing.T) {
bidder, buildErr := Builder(openrtb_ext.BidderAMX, config.Adapter{
Endpoint: amxTestEndpoint})

func countImpressionPixels(vast string) int {
matches := vastImpressionRXP.FindAllIndex([]byte(vast), -1)
return len(matches)
}
if buildErr != nil {
t.Fatalf("Failed to build bidder: %v", buildErr)
}

func TestVideoImpInsertion(t *testing.T) {
markup := interpolateImpressions(openrtb2.Bid{
AdM: sampleVastADM,
NURL: "https://example2.com/nurl",
}, amxBidExt{Himp: []string{"https://example.com/pixel.png"}})
assert.Contains(t, markup, "example2.com/nurl")
assert.Contains(t, markup, "example.com/pixel.png")
assert.Equal(t, 3, countImpressionPixels(markup), "should have 3 Impression pixels")

// make sure that a blank NURL won't result in a blank impression tag
markup = interpolateImpressions(openrtb2.Bid{
AdM: sampleVastADM,
NURL: "",
}, amxBidExt{})
assert.Equal(t, 1, countImpressionPixels(markup), "should have 1 impression pixels")

// we should also ignore blank ext.Himp pixels
markup = interpolateImpressions(openrtb2.Bid{
AdM: sampleVastADM,
NURL: "https://example-nurl.com/nurl",
}, amxBidExt{Himp: []string{"", "", ""}})
assert.Equal(t, 2, countImpressionPixels(markup), "should have 2 impression pixels")
}
type testCase struct {
bidType openrtb_ext.BidType
adm string
extRaw string
valid bool
}

tests := []testCase{
{openrtb_ext.BidTypeNative, `{"assets":[]}`, `{"ct":10}`, true},
{openrtb_ext.BidTypeBanner, sampleDisplayADM, `{"ct": 1}`, true},
{openrtb_ext.BidTypeBanner, sampleDisplayADM, `{"ct": "invalid"}`, false},
{openrtb_ext.BidTypeBanner, sampleDisplayADM, `{}`, true},
{openrtb_ext.BidTypeVideo, sampleVastADM, `{"startdelay": 1}`, true},
{openrtb_ext.BidTypeBanner, sampleVastADM, `{"ct": 1}`, true}, // the server shouldn't do this
}

for _, test := range tests {
bid := openrtb2.Bid{
AdM: test.adm,
Price: 1,
Ext: json.RawMessage(test.extRaw),
}

sb := openrtb2.SeatBid{
Bid: []openrtb2.Bid{bid},
}

resp := openrtb2.BidResponse{
SeatBid: []openrtb2.SeatBid{sb},
}

respJson, jsonErr := json.Marshal(resp)
if jsonErr != nil {
t.Fatalf("Failed to serialize test bid %v: %v", test, jsonErr)
}

bids, errs := bidder.MakeBids(nil, nil, &adapters.ResponseData{
StatusCode: 200,
Body: respJson,
})

if !test.valid {
assert.Len(t, errs, 1)
continue
}

if len(errs) > 0 {
t.Fatalf("Failed to make bids: %v", errs)
}

assert.Len(t, bids.Bids, 1)
assert.Equal(t, test.bidType, bids.Bids[0].BidType)
}

func TestNoDisplayImpInsertion(t *testing.T) {
data := interpolateImpressions(openrtb2.Bid{
AdM: sampleDisplayADM,
NURL: "https://example2.com/nurl",
}, amxBidExt{Himp: []string{"https://example.com/pixel.png"}})
assert.NotContains(t, data, "example2.com/nurl")
assert.NotContains(t, data, "example.com/pixel.png")
}
2 changes: 1 addition & 1 deletion adapters/amx/amxtest/exemplary/app-simple.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
"httpCalls": [
{
"expectedRequest": {
"uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1",
"uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.2",
"body": {
"app": {
"bundle": "639881495",
Expand Down
2 changes: 1 addition & 1 deletion adapters/amx/amxtest/exemplary/display-multiple.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@
},
"httpCalls": [{
"expectedRequest": {
"uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.1",
"uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.2",
"body": {
"device": {
"dnt": 0,
Expand Down
96 changes: 96 additions & 0 deletions adapters/amx/amxtest/exemplary/simple-native.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{
"mockBidRequest": {
"id": "req_id",
"imp": [
{
"id": "imp_id",
"native": {
"request": "{\"native\":{\"ver\":1.1,\"assets\":[]}}",
"ver": "1.1"
},
"ext": {
"bidder": {
"siteId": "1234"
}
}
}
]
},
"httpcalls": [
{
"expectedRequest": {
"uri": "http://pbs-dev.amxrtb.com/auction/openrtb?v=pbs1.2",
"body": {
"id": "req_id",
"imp": [
{
"id": "imp_id",
"native": {
"request": "{\"native\":{\"ver\":1.1,\"assets\":[]}}",
"ver": "1.1"
},
"ext": {
"bidder": {
"siteId": "1234"
}
}
}
]
}
},
"mockResponse": {
"status": 200,
"body": {
"id": "req_id",
"seatbid": [
{
"seat": "123",
"bid": [
{
"id": "example_bid_id",
"impid": "example_imp_id",
"price": 1,
"adid": "12345678",
"adm": "{\"native\":{\"ver\":1.1,\"assets\":[]}}",
"adomain": ["advertiser.com"],
"cid": "1",
"crid": "12345",
"cat": ["IAB1-1"],
"ext": {
"ct": 10
}
}
]
}
],
"bidid": "123456",
"cur": "USD"
}
}
}
],
"expectedBidResponses": [
{
"currency": "USD",
"bids": [
{
"bid": {
"id": "example_bid_id",
"impid": "example_imp_id",
"price": 1,
"adid": "12345678",
"adm": "{\"native\":{\"ver\":1.1,\"assets\":[]}}",
"adomain": ["advertiser.com"],
"cid": "1",
"crid": "12345",
"cat": ["IAB1-1"],
"ext": {
"ct": 10
}
},
"type": "native"
}
]
}
]
}
Loading

0 comments on commit 179ec63

Please sign in to comment.