Skip to content
This repository has been archived by the owner on Dec 22, 2022. It is now read-only.

Commit

Permalink
Adding GumGum server adapter (prebid#748)
Browse files Browse the repository at this point in the history
* gumgum adapter implementation

* removing device info params since newest prebid client started sending over device info

* secure endpoints for gumgum

* removing app from gumgum bidder info
  • Loading branch information
cchang320 authored and dbemiller committed Dec 6, 2018
1 parent 268985c commit 6e7e5d4
Show file tree
Hide file tree
Showing 15 changed files with 373 additions and 0 deletions.
135 changes: 135 additions & 0 deletions adapters/gumgum/gumgum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package gumgum

import (
"encoding/json"
"fmt"
"github.com/mxmCherry/openrtb"
"github.com/prebid/prebid-server/adapters"
"github.com/prebid/prebid-server/errortypes"
"github.com/prebid/prebid-server/openrtb_ext"
"net/http"
)

type GumGumAdapter struct {
URI string
}

func (g *GumGumAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapters.RequestData, []error) {
var validImps []openrtb.Imp
var trackingId string

numRequests := len(request.Imp)
errs := make([]error, 0, numRequests)

for i := 0; i < numRequests; i++ {
imp := request.Imp[i]
zone, err := preprocess(&imp)
if err != nil {
errs = append(errs, err)
} else {
if request.Imp[i].Banner != nil {
bannerCopy := *request.Imp[i].Banner
if bannerCopy.W == nil && bannerCopy.H == nil && len(bannerCopy.Format) > 0 {
format := bannerCopy.Format[0]
bannerCopy.W = &(format.W)
bannerCopy.H = &(format.H)
}
request.Imp[i].Banner = &bannerCopy
validImps = append(validImps, request.Imp[i])
trackingId = zone
}
}
}

if len(validImps) == 0 {
return nil, errs
}

request.Imp = validImps

if request.Site != nil {
siteCopy := *request.Site
siteCopy.ID = trackingId
request.Site = &siteCopy
}

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

headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")
return []*adapters.RequestData{{
Method: "POST",
Uri: g.URI,
Body: reqJSON,
Headers: headers,
}}, errs
}

func (g *GumGumAdapter) 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("Bad user input: HTTP status %d", response.StatusCode),
}}
}

if response.StatusCode != http.StatusOK {
return nil, []error{&errortypes.BadServerResponse{
Message: fmt.Sprintf("Bad server response: HTTP status %d", response.StatusCode),
}}
}
var bidResp openrtb.BidResponse
if err := json.Unmarshal(response.Body, &bidResp); err != nil {
return nil, []error{&errortypes.BadServerResponse{
Message: fmt.Sprintf("Bad server response: %d. ", err),
}}
}

var errs []error
bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)

for _, sb := range bidResp.SeatBid {
for i := range sb.Bid {
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
Bid: &sb.Bid[i],
BidType: openrtb_ext.BidTypeBanner,
})
}
}
return bidResponse, errs
}

func preprocess(imp *openrtb.Imp) (string, error) {
var bidderExt adapters.ExtImpBidder
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
err = &errortypes.BadInput{
Message: err.Error(),
}
return "", err
}

var gumgumExt openrtb_ext.ExtImpGumGum
if err := json.Unmarshal(bidderExt.Bidder, &gumgumExt); err != nil {
err = &errortypes.BadInput{
Message: err.Error(),
}
return "", err
}

zone := gumgumExt.Zone
return zone, nil
}

func NewGumGumBidder(endpoint string) *GumGumAdapter {
return &GumGumAdapter{
URI: endpoint,
}
}
10 changes: 10 additions & 0 deletions adapters/gumgum/gumgum_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package gumgum

import (
"github.com/prebid/prebid-server/adapters/adapterstest"
"testing"
)

func TestJsonSamples(t *testing.T) {
adapterstest.RunJSONBidderTest(t, "gumgumtest", NewGumGumBidder("https://g2.gumgum.com/providers/prbds2s/bid"))
}
99 changes: 99 additions & 0 deletions adapters/gumgum/gumgumtest/exemplary/banner.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
{
"mockBidRequest": {
"id": "test-request-id",
"imp": [
{
"id": "test-imp-id",
"banner": {
"format": [
{
"w": 300,
"h": 250
},
{
"w": 300,
"h": 300
}
],
"w": 300,
"h": 250
},
"ext": {
"bidder": {
"zone": "dc9d6be1"
}
}
}
]
},

"httpCalls": [
{
"expectedRequest": {
"uri": "https://g2.gumgum.com/providers/prbds2s/bid",
"body":{
"id": "test-request-id",
"imp": [{
"id": "test-imp-id",
"banner": {
"format": [{
"w": 300,
"h": 250
}, {
"w": 300,
"h": 300
}],
"w": 300,
"h": 250
},
"ext": {
"bidder": {
"zone": "dc9d6be1"
}
}
}]
}
},
"mockResponse": {
"status": 200,
"body": {
"seatbid": [
{
"bid": [
{
"crid": "2068416",
"adm": "some-test-ad",
"adid": "2068416",
"price": 5,
"id": "5736a50b-6b05-42a8-aa6d-b0a4649dcd05",
"impid": "test-imp-id",
"cid": "4747"
}
]
}
]
}
}
}
],

"expectedBidResponses": [
{
"currency": "USD",
"bids": [
{
"bid": {
"crid": "2068416",
"adm": "some-test-ad",
"adid": "2068416",
"price": 5,
"id": "5736a50b-6b05-42a8-aa6d-b0a4649dcd05",
"impid": "test-imp-id",
"cid": "4747"
},
"type": "banner"
}
]
}
]
}
3 changes: 3 additions & 0 deletions adapters/gumgum/gumgumtest/params/race/banner.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"zone": "dc9d6be1"
}
52 changes: 52 additions & 0 deletions adapters/gumgum/params_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package gumgum

import (
"encoding/json"
"github.com/prebid/prebid-server/openrtb_ext"
"testing"
)

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.BidderGumGum, json.RawMessage(validParam)); err != nil {
t.Errorf("Schema rejected gumgum params: %s", validParam)
}
}
}

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.BidderGumGum, json.RawMessage(invalidParam)); err == nil {
t.Errorf("Schema allowed unexpected params: %s", invalidParam)
}
}
}

var validParams = []string{
`{"zone":"dc9d6be1"}`,
}

var invalidParams = []string{
`null`,
`nil`,
``,
`{}`,
`[]`,
`true`,
`2`,
`{"zone":12345678}`,
`{"zone":""}`,
`{"placementId": 1}`,
`{"zone": true}`,
`{"placementId": 1, "zone":"1234567"}`,
}
18 changes: 18 additions & 0 deletions adapters/gumgum/usersync.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package gumgum

import (
"github.com/prebid/prebid-server/adapters"
"github.com/prebid/prebid-server/config"
"github.com/prebid/prebid-server/openrtb_ext"
"github.com/prebid/prebid-server/usersync"

"net/url"
"strings"
)

func NewGumGumSyncer(cfg *config.Configuration) usersync.Usersyncer {
externalURL := strings.TrimRight(cfg.ExternalURL, "/")
redirectURI := url.QueryEscape(externalURL) + "%2Fsetuid%3Fbidder%3Dgumgum%26gdpr%3D{{gdpr}}%26gdpr_consent%3D{{gdpr_consent}}%26uid%3D"
usersyncURL := cfg.Adapters[string(openrtb_ext.BidderGumGum)].UserSyncURL
return adapters.NewSyncer("gumgum", 61, adapters.ResolveMacros(usersyncURL+redirectURI), adapters.SyncTypeIframe)
}
22 changes: 22 additions & 0 deletions adapters/gumgum/usersync_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package gumgum

import (
"github.com/prebid/prebid-server/config"
"github.com/prebid/prebid-server/openrtb_ext"
"github.com/stretchr/testify/assert"

"testing"
)

func TestGumGumSyncer(t *testing.T) {
syncer := NewGumGumSyncer(&config.Configuration{ExternalURL: "localhost", Adapters: map[string]config.Adapter{
string(openrtb_ext.BidderGumGum): {
UserSyncURL: "https://rtb.gumgum.com/usync/prbds2s?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&r=",
},
}})
u := syncer.GetUsersyncInfo("1", "BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA")
assert.Equal(t, "https://rtb.gumgum.com/usync/prbds2s?gdpr=1&gdpr_consent=BOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA&r=localhost%2Fsetuid%3Fbidder%3Dgumgum%26gdpr%3D1%26gdpr_consent%3DBOPVK28OVJoTBABABAENBs-AAAAhuAKAANAAoACwAGgAPAAxAB0AHgAQAAiABOADkA%26uid%3D", u.URL)
assert.Equal(t, "iframe", u.Type)
assert.Equal(t, uint16(61), syncer.GDPRVendorID())
assert.Equal(t, false, u.SupportCORS)
}
2 changes: 2 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,8 @@ func SetupViper(v *viper.Viper, filename string) {
v.SetDefault("adapters.33across.endpoint", "http://ssc.33across.com/api/v1/hb")
v.SetDefault("adapters.rhythmone.endpoint", "http://tag.1rx.io/rmp")
v.SetDefault("adapters.rhythmone.usersync_url", "//sync.1rx.io/usersync2/rmphb?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&redir=")
v.SetDefault("adapters.gumgum.endpoint", "https://g2.gumgum.com/providers/prbds2s/bid")
v.SetDefault("adapters.gumgum.usersync_url", "https://rtb.gumgum.com/usync/prbds2s?gdpr={{gdpr}}&gdpr_consent={{gdpr_consent}}&r=")

v.SetDefault("max_request_size", 1024*256)
v.SetDefault("analytics.file.filename", "")
Expand Down
2 changes: 2 additions & 0 deletions exchange/adapter_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/prebid/prebid-server/adapters/brightroll"
"github.com/prebid/prebid-server/adapters/conversant"
"github.com/prebid/prebid-server/adapters/eplanning"
"github.com/prebid/prebid-server/adapters/gumgum"
"github.com/prebid/prebid-server/adapters/ix"
"github.com/prebid/prebid-server/adapters/lifestreet"
"github.com/prebid/prebid-server/adapters/openx"
Expand All @@ -41,6 +42,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter
openrtb_ext.BidderBeachfront: beachfront.NewBeachfrontBidder(),
openrtb_ext.BidderBrightroll: brightroll.NewBrightrollBidder(cfg.Adapters[string(openrtb_ext.BidderBrightroll)].Endpoint),
openrtb_ext.BidderEPlanning: eplanning.NewEPlanningBidder(client, cfg.Adapters[string(openrtb_ext.BidderEPlanning)].Endpoint),
openrtb_ext.BidderGumGum: gumgum.NewGumGumBidder(cfg.Adapters[string(openrtb_ext.BidderGumGum)].Endpoint),
openrtb_ext.BidderOpenx: openx.NewOpenxBidder(cfg.Adapters[string(openrtb_ext.BidderOpenx)].Endpoint),
openrtb_ext.BidderPubmatic: pubmatic.NewPubmaticBidder(client, cfg.Adapters[string(openrtb_ext.BidderPubmatic)].Endpoint),
openrtb_ext.BidderRhythmone: rhythmone.NewRhythmoneBidder(cfg.Adapters[string(openrtb_ext.BidderRhythmone)].Endpoint),
Expand Down
2 changes: 2 additions & 0 deletions openrtb_ext/bidders.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const (
BidderConversant BidderName = "conversant"
BidderEPlanning BidderName = "eplanning"
BidderFacebook BidderName = "audienceNetwork"
BidderGumGum BidderName = "gumgum"
BidderIx BidderName = "ix"
BidderLifestreet BidderName = "lifestreet"
BidderOpenx BidderName = "openx"
Expand All @@ -52,6 +53,7 @@ var BidderMap = map[string]BidderName{
"brightroll": BidderBrightroll,
"conversant": BidderConversant,
"eplanning": BidderEPlanning,
"gumgum": BidderGumGum,
"ix": BidderIx,
"lifestreet": BidderLifestreet,
"openx": BidderOpenx,
Expand Down
5 changes: 5 additions & 0 deletions openrtb_ext/imp_gumgum.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package openrtb_ext

type ExtImpGumGum struct {
Zone string `json:"zone"`
}
6 changes: 6 additions & 0 deletions static/bidder-info/gumgum.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
maintainer:
email: "pubtech@gumgum.com"
capabilities:
site:
mediaTypes:
- banner
Loading

0 comments on commit 6e7e5d4

Please sign in to comment.