From 81781cf0813195cc8162e5918655db34c92cd602 Mon Sep 17 00:00:00 2001 From: Christopher Nguyen and Johnny Chau Date: Mon, 14 May 2018 17:32:58 -0700 Subject: [PATCH 01/26] wip --- adapters/sharethrough/sharethrough.go | 117 ++++++++++++++++++++++++++ openrtb_ext/bidders.go | 2 + openrtb_ext/imp_sharethrough.go | 90 ++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 adapters/sharethrough/sharethrough.go create mode 100644 openrtb_ext/imp_sharethrough.go diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go new file mode 100644 index 00000000000..121fe2e728c --- /dev/null +++ b/adapters/sharethrough/sharethrough.go @@ -0,0 +1,117 @@ +package sharethrough + +import ( + "encoding/json" + "net/http" + "net/url" + "strings" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/openrtb_ext" +) + +const hbEndpoint = "https://dumb-waiter.sharethrough.com/header-bid/v1" + +// SharethroughAdapter converts the Sharethrough Adserver response into a +// prebid server compatible format +type SharethroughAdapter struct { + http *adapters.HTTPAdapter + URI string +} + +// Name returns the adapter name as a string +func (s SharethroughAdapter) Name() string { + return "sharethrough" +} + +type sharethroughParams struct { + BidID string `json:"bidId"` + PlacementKey string `json:"placement_key"` + HBVersion string `json:"hbVersion"` + StrVersion string `json:"strVersion"` + HBSource string `json:"hbSource"` +} + +func (s *SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapters.RequestData, []error) { + pKeys := make([]string, 0, len(request.Imp)) + potentialRequests := make([]*adapters.RequestData) + errs := make([]error, 0, len(request.Imp)) + + for i := 0; i < len(request.Imp); i++ { + pKey, err := preprocess(&request.Imp[i]) + if pKey != "" { + pKeys = append(pKeys, pkey) + } + + // If the preprocessing failed, the server won't be able to bid on this Imp. Delete it, and note the error. + if err != nil { + errs = append(errs, err) + request.Imp = append(request.Imp[:i], request.Imp[i+1:]...) + i-- + } + } + + hbURI := generateHBUri(pKey, "testBidID") + + // If all the requests were malformed, don't bother making a server call with no impressions. + if len(request.Imp) == 0 { + return nil, errs + } + + 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: thisURI, + Body: reqJSON, + Headers: headers, + }}, errs +} + +func preprocess(imp *openrtb.Imp) (pKey, error) { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return "", err + } + + var sharethroughExt openrtb_ext.ExtImpSharethrough + if err := json.Unmarshal(bidderExt, &sharethroughExt); err != nil { + return "", err + } + + return sharethroughExt.PlacementKey, nil +} + +func appendPkey(uri string, pKey string) string { + if strings.Contains(uri, "?") { + return uri + "&placement_key=" + pKey + } + + return uri + "?placement_key=" + pKey +} + +func keys(m map[string]bool) []string { + keys := make([]string, 0, len(m)) + for key, _ := range m { + keys = append(keys, key) + } + return keys +} + +func generateHBUri(pKey string, bidID string) string { + v := url.Values{} + v.Set("placement_key", pKey) + v.Set("bidId", bidID) + v.Set("hbVersion", "test-version") + v.Set("hbSource", "prebid-server") + + return hbEndpoint + "?" + v.Encode() +} diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 7c527be9352..8bcd4ace1e2 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -41,6 +41,7 @@ const ( BidderPulsepoint BidderName = "pulsepoint" BidderRhythmone BidderName = "rhythmone" BidderRubicon BidderName = "rubicon" + BidderSharethrough BidderName = "sharethrough" BidderSomoaudience BidderName = "somoaudience" BidderSovrn BidderName = "sovrn" BidderSonobi BidderName = "sonobi" @@ -70,6 +71,7 @@ var BidderMap = map[string]BidderName{ "pulsepoint": BidderPulsepoint, "rhythmone": BidderRhythmone, "rubicon": BidderRubicon, + "sharethrough": BidderSharethrough, "somoaudience": BidderSomoaudience, "sovrn": BidderSovrn, "sonobi": BidderSonobi, diff --git a/openrtb_ext/imp_sharethrough.go b/openrtb_ext/imp_sharethrough.go new file mode 100644 index 00000000000..0b4f81b54d0 --- /dev/null +++ b/openrtb_ext/imp_sharethrough.go @@ -0,0 +1,90 @@ +package openrtb_ext + +import "encoding/json" + +type ExtImpSharethrough struct { + PlacementKey string `json:"placement_key"` +} + +// ExtImpSharethrough defines the contract for bidrequest.imp[i].ext.sharethrough +type ExtImpSharethroughResponse struct { + AdServerRequestID string `json:"adserverRequestId"` + BidID string `json:"bidId"` + CookieSyncUrls []string `json:"cookieSyncUrls"` + Creatives []ExtImpSharethroughCreative `json:"creatives"` + Placement ExtImpSharethroughPlacement `json:"placement"` + StxUserID string `json:"stxUserId"` +} +type ExtImpSharethroughCreative struct { + AuctionWinID string `json:"auctionWinId"` + CPM float64 `json:"cpm"` + Creative ExtImpSharethroughCreativeMetadata `json:"creative"` + Version int `json:"version"` +} + +type ExtImpSharethroughCreativeMetadata struct { + Action string `json:"clickout"` + Advertiser string `json:"advertiser"` + AdvertiserKey string `json:"advertiser_key"` + Beacons ExtImpSharethroughCreativeBeacons `json:"beacons"` + BrandLogoURL string `json:"brand_logo_url"` + CampaignKey string `json:"campaign_key"` + CreativeKey string `json:"creative_key"` + CustomEngagementAction string `json:"custom_engagement_action"` + CustomEngagementLabel string `json:"custom_engagement_label"` + CustomEngagementURL string `json:"custom_engagement_url"` + DealID string `json:"deal_id"` + Description string `json:"description"` + ForceClickToPlay bool `json:"force_click_to_play"` + IconURL string `json:"icon_url"` + ImpressionHTML string `json:"impression_html"` + InstantPlayMobileCount int `json:"instant_play_mobile_count"` + InstantPlayMobileURL string `json:"instant_play_mobile_url"` + MediaURL string `json:"media_url"` + ShareURL string `json:"share_url"` + SourceID string `json:"source_id"` + ThumbnailURL string `json:"thumbnail_url"` + Title string `json:"title"` + VariantKey string `json:"variant_key"` +} + +type ExtImpSharethroughCreativeBeacons struct { + Click []string `json:"click"` + Impression []string `json:"impression"` + Play []string `json:"play"` + Visible []string `json:"visible"` + WinNotification []string `json:"win-notification"` +} + +type ExtImpSharethroughPlacement struct { + AllowInstantPlay bool `json:"allow_instant_play"` + ArticlesBeforeFirstAd int `json:"articles_before_first_ad"` + ArticlesBetweenAds int `json:"articles_between_ads` + Layout string `json:"layout"` + Metadata json.RawMessage `json:"metadata"` + PlacementAttributes ExtImpSharethroughPlacementAttributes `json:"placement_attributes"` + Status string `json:"status"` +} + +type ExtImpSharethroughPlacementAttributes struct { + AdServerKey string `json:"ad_server_key"` + AdServerPath string `json:"ad_server_path"` + AllowDynamicCropping bool `json:"allow_dynamic_cropping"` + AppThirdPartyPartners []string `json:"app_third_party_partners"` + CustomCardCSS string `json:"custom_card_css"` + DFPPath string `json:"dfp_path"` + DirectSellPromotedByText string `json:"direct_sell_promoted_by_text"` + Domain string `json:"domain"` + EnableLinkRedirection bool `json:"enable_link_redirection"` + FeaturedContent json.RawMessage `json:"featured_content"` + MaxHeadlineLength int `json:"max_headline_length"` + MultiAdPlacement bool `json:"multi_ad_placement"` + PromotedByText string `json:"promoted_by_text"` + PublisherKey string `json:"publisher_key"` + RenderingPixelOffset int `json:"rendering_pixel_offset"` + SafeFrameSize []int `json:"safe_frame_size"` + SiteKey string `json:"site_key"` + StrOptOutURL string `json:"str_opt_out_url"` + Template string `json:"template"` + ThirdPartyPartners []string `json:"third_party_partners"` +} From 9e5f5cc4290c053e47aad3186cd1e5d7dbaa4db1 Mon Sep 17 00:00:00 2001 From: Christopher Nguyen and Johnny Chau Date: Tue, 15 May 2018 17:25:27 -0700 Subject: [PATCH 02/26] wip --- adapters/sharethrough/sharethrough.go | 105 +++++++++++++++---------- exchange/adapter_map.go | 2 + static/bidder-info/sharethrough.yaml | 12 +++ static/bidder-params/sharethrough.json | 13 +++ 4 files changed, 91 insertions(+), 41 deletions(-) create mode 100644 static/bidder-info/sharethrough.yaml create mode 100644 static/bidder-params/sharethrough.json diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index 121fe2e728c..ae2d5c195d9 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -2,9 +2,9 @@ package sharethrough import ( "encoding/json" + "fmt" "net/http" "net/url" - "strings" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" @@ -13,6 +13,15 @@ import ( const hbEndpoint = "https://dumb-waiter.sharethrough.com/header-bid/v1" +func NewSharethroughBidder(client *http.Client, endpoint string) *SharethroughAdapter { + adapter := &adapters.HTTPAdapter{Client: client} + + return &SharethroughAdapter{ + http: adapter, + URI: endpoint, + } +} + // SharethroughAdapter converts the Sharethrough Adserver response into a // prebid server compatible format type SharethroughAdapter struct { @@ -25,7 +34,7 @@ func (s SharethroughAdapter) Name() string { return "sharethrough" } -type sharethroughParams struct { +type params struct { BidID string `json:"bidId"` PlacementKey string `json:"placement_key"` HBVersion string `json:"hbVersion"` @@ -33,15 +42,19 @@ type sharethroughParams struct { HBSource string `json:"hbSource"` } -func (s *SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapters.RequestData, []error) { +func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapters.RequestData, []error) { pKeys := make([]string, 0, len(request.Imp)) - potentialRequests := make([]*adapters.RequestData) errs := make([]error, 0, len(request.Imp)) + headers := http.Header{} + var potentialRequests []*adapters.RequestData + + headers.Add("Content-Type", "text/plain;charset=utf-8") + headers.Add("Accept", "application/json") for i := 0; i < len(request.Imp); i++ { pKey, err := preprocess(&request.Imp[i]) if pKey != "" { - pKeys = append(pKeys, pkey) + pKeys = append(pKeys, pKey) } // If the preprocessing failed, the server won't be able to bid on this Imp. Delete it, and note the error. @@ -49,63 +62,73 @@ func (s *SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adap errs = append(errs, err) request.Imp = append(request.Imp[:i], request.Imp[i+1:]...) i-- + continue } + + hbURI := generateHBUri(pKey, "testBidID-"+string(i)) + potentialRequests = append(potentialRequests, &adapters.RequestData{ + Method: "GET", + Uri: hbURI, + Body: nil, + Headers: headers, + }) } - hbURI := generateHBUri(pKey, "testBidID") + return potentialRequests, errs +} - // If all the requests were malformed, don't bother making a server call with no impressions. - if len(request.Imp) == 0 { - return nil, errs +func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if response.StatusCode == http.StatusNoContent { + return nil, nil } - reqJSON, err := json.Marshal(request) - if err != nil { - errs = append(errs, err) - return nil, errs + if response.StatusCode == http.StatusBadRequest { + return nil, []error{&adapters.BadInputError{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), + }} } - headers := http.Header{} - headers.Add("Content-Type", "application/json;charset=utf-8") - headers.Add("Accept", "application/json") - return []*adapters.RequestData{{ - Method: "POST", - Uri: thisURI, - Body: reqJSON, - Headers: headers, - }}, errs -} + if response.StatusCode != http.StatusOK { + return nil, []error{fmt.Errorf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode)} + } -func preprocess(imp *openrtb.Imp) (pKey, error) { + var bidResp openrtb.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponse() + + var errs []error + for _, sb := range bidResp.SeatBid { + for i := 0; i < len(sb.Bid); i++ { + bid := sb.Bid[i] + if bidType, err := getMediaTypeForBid(&bid); err == nil { + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: bidType, + }) + } else { + errs = append(errs, err) + } + } + } + return bidResponse, errs +} +func preprocess(imp *openrtb.Imp) (pKey string, err error) { var bidderExt adapters.ExtImpBidder if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { return "", err } var sharethroughExt openrtb_ext.ExtImpSharethrough - if err := json.Unmarshal(bidderExt, &sharethroughExt); err != nil { + if err := json.Unmarshal(bidderExt.Bidder, &sharethroughExt); err != nil { return "", err } return sharethroughExt.PlacementKey, nil } -func appendPkey(uri string, pKey string) string { - if strings.Contains(uri, "?") { - return uri + "&placement_key=" + pKey - } - - return uri + "?placement_key=" + pKey -} - -func keys(m map[string]bool) []string { - keys := make([]string, 0, len(m)) - for key, _ := range m { - keys = append(keys, key) - } - return keys -} - func generateHBUri(pKey string, bidID string) string { v := url.Values{} v.Set("placement_key", pKey) diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go index d31a3db1220..1b0577533e2 100644 --- a/exchange/adapter_map.go +++ b/exchange/adapter_map.go @@ -27,6 +27,7 @@ import ( "github.com/prebid/prebid-server/adapters/pulsepoint" "github.com/prebid/prebid-server/adapters/rhythmone" "github.com/prebid/prebid-server/adapters/rubicon" + "github.com/prebid/prebid-server/adapters/sharethrough" "github.com/prebid/prebid-server/adapters/somoaudience" "github.com/prebid/prebid-server/adapters/sonobi" "github.com/prebid/prebid-server/adapters/sovrn" @@ -59,6 +60,7 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Username, cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Password, cfg.Adapters[string(openrtb_ext.BidderRubicon)].XAPI.Tracker), + openrtb_ext.BidderSharethrough: sharethrough.NewSharethroughBidder(cfg.Adapters[string(openrtb_ext.BidderSharethrough)].Endpoint), openrtb_ext.BidderSomoaudience: somoaudience.NewSomoaudienceBidder(cfg.Adapters[string(openrtb_ext.BidderSomoaudience)].Endpoint), openrtb_ext.BidderSovrn: sovrn.NewSovrnBidder(client, cfg.Adapters[string(openrtb_ext.BidderSovrn)].Endpoint), openrtb_ext.Bidder33Across: ttx.New33AcrossBidder(cfg.Adapters[string(openrtb_ext.Bidder33Across)].Endpoint), diff --git a/static/bidder-info/sharethrough.yaml b/static/bidder-info/sharethrough.yaml new file mode 100644 index 00000000000..bbb6dc5c96d --- /dev/null +++ b/static/bidder-info/sharethrough.yaml @@ -0,0 +1,12 @@ +maintainer: + email: "cnguyen@sharethrough.com" +capabilities: + app: + mediaTypes: + - banner + - native + site: + mediaTypes: + - banner + - video + - native diff --git a/static/bidder-params/sharethrough.json b/static/bidder-params/sharethrough.json new file mode 100644 index 00000000000..8b78b930ec4 --- /dev/null +++ b/static/bidder-params/sharethrough.json @@ -0,0 +1,13 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "EPlanning Adapter Params", + "description": "A schema which validates params accepted by the Sharethrough adapter", + "type": "object", + "properties": { + "placement_key": { + "type": ["string"], + "description": "placement_key to use." + } + }, + "required": ["placement_key"] +} From 90879cc33b6ca2fc91d94aeda6709b75b0a91912 Mon Sep 17 00:00:00 2001 From: Christopher Nguyen and Johnny Chau Date: Thu, 17 May 2018 19:36:37 -0700 Subject: [PATCH 03/26] wip --- adapters/sharethrough/sharethrough.go | 61 +++++++++++++++++++------- openrtb_ext/imp_sharethrough.go | 4 +- static/bidder-info/sharethrough.yaml | 1 + static/bidder-params/sharethrough.json | 6 +-- usersync/sharethrough.go | 20 +++++++++ 5 files changed, 70 insertions(+), 22 deletions(-) create mode 100644 usersync/sharethrough.go diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index ae2d5c195d9..de69c9985e9 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -5,13 +5,15 @@ import ( "fmt" "net/http" "net/url" + "strconv" + "github.com/golang/glog" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/openrtb_ext" ) -const hbEndpoint = "https://dumb-waiter.sharethrough.com/header-bid/v1" +const hbEndpoint = "http://dumb-waiter.sharethrough.com/header-bid/v1" func NewSharethroughBidder(client *http.Client, endpoint string) *SharethroughAdapter { adapter := &adapters.HTTPAdapter{Client: client} @@ -65,7 +67,7 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt continue } - hbURI := generateHBUri(pKey, "testBidID-"+string(i)) + hbURI := generateHBUri(pKey, "testBidID-"+strconv.Itoa(i)) potentialRequests = append(potentialRequests, &adapters.RequestData{ Method: "GET", Uri: hbURI, @@ -78,6 +80,7 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt } func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + glog.Infof("response code: %d\n", response.StatusCode) if response.StatusCode == http.StatusNoContent { return nil, nil } @@ -92,29 +95,53 @@ func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, exter return nil, []error{fmt.Errorf("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 { + // var bidResp openrtb.BidResponse + var strBidResp openrtb_ext.ExtImpSharethroughResponse + if err := json.Unmarshal(response.Body, &strBidResp); err != nil { return nil, []error{err} } + glog.Infof("body: %+v\n", strBidResp) bidResponse := adapters.NewBidderResponse() var errs []error - for _, sb := range bidResp.SeatBid { - for i := 0; i < len(sb.Bid); i++ { - bid := sb.Bid[i] - if bidType, err := getMediaTypeForBid(&bid); err == nil { - bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ - Bid: &bid, - BidType: bidType, - }) - } else { - errs = append(errs, err) - } - } - } + // for _, sb := range bidResp.SeatBid { + // for i := 0; i < len(sb.Bid); i++ { + // bid := sb.Bid[i] + // if bidType, err := getMediaTypeForBid(&bid); err == nil { + // bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + // Bid: &bid, + // BidType: bidType, + // }) + // } else { + // errs = append(errs, err) + // } + // } + // } return bidResponse, errs } + +func getMediaTypeForBid(bid *openrtb.Bid) (openrtb_ext.BidType, error) { + var impExt struct { + Sharethrough struct { + BidType int `json:"bid_type"` + } `json:"sharethrough"` + } + if err := json.Unmarshal(bid.Ext, &impExt); err != nil { + return "", err + } + switch impExt.Sharethrough.BidType { + case 0: + return openrtb_ext.BidTypeBanner, nil + case 1: + return openrtb_ext.BidTypeVideo, nil + case 2: + return openrtb_ext.BidTypeNative, nil + default: + return "", fmt.Errorf("Unrecognized bid_ad_type in response from appnexus: %d", impExt.Sharethrough.BidType) + } +} + func preprocess(imp *openrtb.Imp) (pKey string, err error) { var bidderExt adapters.ExtImpBidder if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { diff --git a/openrtb_ext/imp_sharethrough.go b/openrtb_ext/imp_sharethrough.go index 0b4f81b54d0..45859276488 100644 --- a/openrtb_ext/imp_sharethrough.go +++ b/openrtb_ext/imp_sharethrough.go @@ -3,7 +3,7 @@ package openrtb_ext import "encoding/json" type ExtImpSharethrough struct { - PlacementKey string `json:"placement_key"` + PlacementKey string `json:"pkey"` } // ExtImpSharethrough defines the contract for bidrequest.imp[i].ext.sharethrough @@ -59,7 +59,7 @@ type ExtImpSharethroughCreativeBeacons struct { type ExtImpSharethroughPlacement struct { AllowInstantPlay bool `json:"allow_instant_play"` ArticlesBeforeFirstAd int `json:"articles_before_first_ad"` - ArticlesBetweenAds int `json:"articles_between_ads` + ArticlesBetweenAds int `json:"articles_between_ads"` Layout string `json:"layout"` Metadata json.RawMessage `json:"metadata"` PlacementAttributes ExtImpSharethroughPlacementAttributes `json:"placement_attributes"` diff --git a/static/bidder-info/sharethrough.yaml b/static/bidder-info/sharethrough.yaml index bbb6dc5c96d..1662857aeb1 100644 --- a/static/bidder-info/sharethrough.yaml +++ b/static/bidder-info/sharethrough.yaml @@ -5,6 +5,7 @@ capabilities: mediaTypes: - banner - native + - video site: mediaTypes: - banner diff --git a/static/bidder-params/sharethrough.json b/static/bidder-params/sharethrough.json index 8b78b930ec4..9e66ad1d1f9 100644 --- a/static/bidder-params/sharethrough.json +++ b/static/bidder-params/sharethrough.json @@ -4,10 +4,10 @@ "description": "A schema which validates params accepted by the Sharethrough adapter", "type": "object", "properties": { - "placement_key": { + "pkey": { "type": ["string"], - "description": "placement_key to use." + "description": "placement key to use." } }, - "required": ["placement_key"] + "required": ["pkey"] } diff --git a/usersync/sharethrough.go b/usersync/sharethrough.go new file mode 100644 index 00000000000..57fda251310 --- /dev/null +++ b/usersync/sharethrough.go @@ -0,0 +1,20 @@ +package usersync + +import ( + "fmt" + "net/url" +) + +func NewSharethroughSyncer(externalURL string) Usersyncer { + redirect_uri := fmt.Sprintf("%s/setuid?bidder=sharethrough&uid=$UID", externalURL) + usersyncURL := "//sharethrough.adnxs.com/getuid?" + return &syncer{ + familyName: "adnxs", + gdprVendorID: 80, + syncInfo: &UsersyncInfo{ + URL: fmt.Sprintf("%s%s", usersyncURL, url.QueryEscape(redirect_uri)), + Type: "redirect", + SupportCORS: false, + }, + } +} From 174976192c7868009841352e7bba0653fa4b27fb Mon Sep 17 00:00:00 2001 From: Christopher Nguyen Date: Thu, 25 Oct 2018 11:00:10 -0700 Subject: [PATCH 04/26] exploration for sharethrough prebid-server --- adapters/sharethrough/sharethrough.go | 113 ++++++++++++++++---------- endpoints/openrtb2/auction.go | 1 + 2 files changed, 70 insertions(+), 44 deletions(-) diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index de69c9985e9..a7093212304 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -4,7 +4,6 @@ import ( "encoding/json" "fmt" "net/http" - "net/url" "strconv" "github.com/golang/glog" @@ -69,7 +68,7 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt hbURI := generateHBUri(pKey, "testBidID-"+strconv.Itoa(i)) potentialRequests = append(potentialRequests, &adapters.RequestData{ - Method: "GET", + Method: "POST", Uri: hbURI, Body: nil, Headers: headers, @@ -95,51 +94,73 @@ func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, exter return nil, []error{fmt.Errorf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode)} } - // var bidResp openrtb.BidResponse - var strBidResp openrtb_ext.ExtImpSharethroughResponse - if err := json.Unmarshal(response.Body, &strBidResp); err != nil { + var bidResp openrtb.BidResponse + // var strBidResp openrtb_ext.ExtImpSharethroughResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { return nil, []error{err} } - glog.Infof("body: %+v\n", strBidResp) + // bidResp.ID = strBidResp.AdServerRequestID + // bidResp.BidID = strBidResp.BidID + // bidResp.Cur = "USD" + // if len(strBidResp.Creatives) > 0 { + // var bid openrtb.Bid + // bid.ImpID = strBidResp.Creatives[0].AuctionWinID + // bid.Price = strBidResp.Creatives[0].CPM + // if _, ok := strBidResp.Creatives[0].beacons["win-notification"]; ok { + // bid.NURL = strBidResp.Creatives[0].beacons["win-notification"][0] + // } + // bidResp.SeatBid.Bid = append(bidResp.SeatBid.Bid, bid) + // } + + glog.Infof("body: %+v\n", bidResp) bidResponse := adapters.NewBidderResponse() var errs []error - // for _, sb := range bidResp.SeatBid { - // for i := 0; i < len(sb.Bid); i++ { - // bid := sb.Bid[i] - // if bidType, err := getMediaTypeForBid(&bid); err == nil { - // bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ - // Bid: &bid, - // BidType: bidType, - // }) - // } else { - // errs = append(errs, err) - // } - // } - // } + for _, sb := range bidResp.SeatBid { + for i := 0; i < len(sb.Bid); i++ { + bid := sb.Bid[i] + if bidType, err := getMediaTypeForBid(&bid); err == nil { + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &bid, + BidType: bidType, + }) + } else { + errs = append(errs, err) + } + } + } + for _, bid := range bidResponse.Bids { + glog.Infof("bidResponse.Bids: %+v\n", bid) + } + if len(errs) > 0 { + for _, err := range errs { + glog.Infof("error: %s\n", err) + } + } return bidResponse, errs } func getMediaTypeForBid(bid *openrtb.Bid) (openrtb_ext.BidType, error) { - var impExt struct { - Sharethrough struct { - BidType int `json:"bid_type"` - } `json:"sharethrough"` - } - if err := json.Unmarshal(bid.Ext, &impExt); err != nil { - return "", err - } - switch impExt.Sharethrough.BidType { - case 0: - return openrtb_ext.BidTypeBanner, nil - case 1: - return openrtb_ext.BidTypeVideo, nil - case 2: - return openrtb_ext.BidTypeNative, nil - default: - return "", fmt.Errorf("Unrecognized bid_ad_type in response from appnexus: %d", impExt.Sharethrough.BidType) - } + return openrtb_ext.BidTypeNative, nil + // var impExt struct { + // Sharethrough struct { + // BidType int `json:"bid_type"` + // } `json:"sharethrough"` + // } + // if err := json.Unmarshal(bid.Ext, &impExt); err != nil { + // return "", err + // } + // switch impExt.Sharethrough.BidType { + // case 0: + // return openrtb_ext.BidTypeBanner, nil + // case 1: + // return openrtb_ext.BidTypeVideo, nil + // case 2: + // return openrtb_ext.BidTypeNative, nil + // default: + // return "", fmt.Errorf("Unrecognized bid_ad_type in response from sharethrough: %d", impExt.Sharethrough.BidType) + // } } func preprocess(imp *openrtb.Imp) (pKey string, err error) { @@ -157,11 +178,15 @@ func preprocess(imp *openrtb.Imp) (pKey string, err error) { } func generateHBUri(pKey string, bidID string) string { - v := url.Values{} - v.Set("placement_key", pKey) - v.Set("bidId", bidID) - v.Set("hbVersion", "test-version") - v.Set("hbSource", "prebid-server") - - return hbEndpoint + "?" + v.Encode() + return "http://localhost:8000/bid" } + +// func generateHBUri(pKey string, bidID string) string { +// v := url.Values{} +// v.Set("placement_key", pKey) +// v.Set("bidId", bidID) +// v.Set("hbVersion", "test-version") +// v.Set("hbSource", "prebid-server") + +// return hbEndpoint + "?" + v.Encode() +// } diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 62f02d5d0f4..49bbb8fba0a 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -131,6 +131,7 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http } numImps = len(req.Imp) + req.Test = 1 response, err := deps.ex.HoldAuction(ctx, req, usersyncs, labels, &deps.categories) ao.Request = req ao.Response = response From 67436b0c4122e7075d8ecc398529ff904aa3351c Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Fri, 29 Mar 2019 15:37:45 -0700 Subject: [PATCH 05/26] WIP: updating sharethrough adapter to latest prebid version Co-authored-by: Chris Nguyen --- adapters/sharethrough/sharethrough.go | 127 ++++++++++++++++++------- adapters/sharethrough/usersync.go | 11 +++ config/config.go | 2 + openrtb_ext/imp_sharethrough.go | 2 +- static/bidder-params/sharethrough.json | 2 +- usersync/sharethrough.go | 20 ---- 6 files changed, 107 insertions(+), 57 deletions(-) create mode 100644 adapters/sharethrough/usersync.go delete mode 100644 usersync/sharethrough.go diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index a7093212304..b267f6b62be 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -1,33 +1,28 @@ package sharethrough import ( + "encoding/base64" "encoding/json" "fmt" - "net/http" - "strconv" - - "github.com/golang/glog" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" + "html/template" + "net/http" + "net/url" ) const hbEndpoint = "http://dumb-waiter.sharethrough.com/header-bid/v1" -func NewSharethroughBidder(client *http.Client, endpoint string) *SharethroughAdapter { - adapter := &adapters.HTTPAdapter{Client: client} - - return &SharethroughAdapter{ - http: adapter, - URI: endpoint, - } +func NewSharethroughBidder(endpoint string) *SharethroughAdapter { + return &SharethroughAdapter{URI: endpoint} } // SharethroughAdapter converts the Sharethrough Adserver response into a // prebid server compatible format type SharethroughAdapter struct { - http *adapters.HTTPAdapter - URI string + URI string } // Name returns the adapter name as a string @@ -44,6 +39,7 @@ type params struct { } func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapters.RequestData, []error) { + fmt.Println("in sharethrough adapter") pKeys := make([]string, 0, len(request.Imp)) errs := make([]error, 0, len(request.Imp)) headers := http.Header{} @@ -66,10 +62,10 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt continue } - hbURI := generateHBUri(pKey, "testBidID-"+strconv.Itoa(i)) + //hbURI := generateHBUri(pKey, "testBidID-"+strconv.Itoa(i)) potentialRequests = append(potentialRequests, &adapters.RequestData{ Method: "POST", - Uri: hbURI, + Uri: s.URI + "?pkey=" + pKey, Body: nil, Headers: headers, }) @@ -79,13 +75,15 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt } func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { - glog.Infof("response code: %d\n", response.StatusCode) + fmt.Printf("internal request: %v\n", internalRequest) + fmt.Printf("external request: %v\n", externalRequest) + if response.StatusCode == http.StatusNoContent { return nil, nil } if response.StatusCode == http.StatusBadRequest { - return nil, []error{&adapters.BadInputError{ + return nil, []error{&errortypes.BadInput{ Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), }} } @@ -95,27 +93,15 @@ func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, exter } var bidResp openrtb.BidResponse - // var strBidResp openrtb_ext.ExtImpSharethroughResponse - if err := json.Unmarshal(response.Body, &bidResp); err != nil { + var strBidResp openrtb_ext.ExtImpSharethroughResponse + if err := json.Unmarshal(response.Body, &strBidResp); err != nil { return nil, []error{err} } - // bidResp.ID = strBidResp.AdServerRequestID - // bidResp.BidID = strBidResp.BidID - // bidResp.Cur = "USD" - // if len(strBidResp.Creatives) > 0 { - // var bid openrtb.Bid - // bid.ImpID = strBidResp.Creatives[0].AuctionWinID - // bid.Price = strBidResp.Creatives[0].CPM - // if _, ok := strBidResp.Creatives[0].beacons["win-notification"]; ok { - // bid.NURL = strBidResp.Creatives[0].beacons["win-notification"][0] - // } - // bidResp.SeatBid.Bid = append(bidResp.SeatBid.Bid, bid) - // } - - glog.Infof("body: %+v\n", bidResp) bidResponse := adapters.NewBidderResponse() + br, _ := butlerToOpenRTBResponse(externalRequest, strBidResp) + fmt.Printf("br code: %v\n", br) var errs []error for _, sb := range bidResp.SeatBid { for i := 0; i < len(sb.Bid); i++ { @@ -131,16 +117,87 @@ func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, exter } } for _, bid := range bidResponse.Bids { - glog.Infof("bidResponse.Bids: %+v\n", bid) + fmt.Printf("bidResponse.Bids: %+v\n", bid) } if len(errs) > 0 { for _, err := range errs { - glog.Infof("error: %s\n", err) + fmt.Printf("error: %s\n", err) } } return bidResponse, errs } +func butlerToOpenRTBResponse(btlrReq *adapters.RequestData, strResp openrtb_ext.ExtImpSharethroughResponse) (*adapters.BidderResponse, []error) { + var errs []error + bidResponse := adapters.NewBidderResponse() + + bidResponse.Currency = "USD" + typedBid := &adapters.TypedBid{BidType: openrtb_ext.BidTypeNative} + creative := strResp.Creatives[0] + + btlrUrl, err := url.Parse(btlrReq.Uri) + if err != nil { + errs = append(errs, err) + return nil, errs + } + pkey := btlrUrl.Query().Get("pkey") + + bid := &openrtb.Bid{ + ID: strResp.BidID, + ImpID: strResp.AdServerRequestID, // MAYBE? + Price: creative.CPM, + // NURL: creative.Beacons.WinNotification[0] // what do we do with other notification URLs ??? + CID: creative.Metadata.CampaignKey, + CrID: creative.Metadata.CreativeKey, + DealID: creative.Metadata.DealID, + AdM: getAdMarkup(strResp, pkey), + } + + typedBid.Bid = bid + bidResponse.Bids = append(bidResponse.Bids, typedBid) + + return bidResponse, errs +} + +func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, pkey string) string { + strRespId := fmt.Sprintf("str_response_%s", strResp.BidID) + //b64EncodedJson := base64.NewEncoding(json.Mar) + //tmpl := ` + //
+ // + //` + // + //let adMarkup = ` + //
+ //
+ // + //` + // + //if (req.strData.stayInIframe) { + // // Don't break out of iframe + // adMarkup = adMarkup + `` + //} else { + // // Break out of iframe + // adMarkup = adMarkup + ` + // + // ` + //} +} + func getMediaTypeForBid(bid *openrtb.Bid) (openrtb_ext.BidType, error) { return openrtb_ext.BidTypeNative, nil // var impExt struct { diff --git a/adapters/sharethrough/usersync.go b/adapters/sharethrough/usersync.go new file mode 100644 index 00000000000..a951fcd6a0a --- /dev/null +++ b/adapters/sharethrough/usersync.go @@ -0,0 +1,11 @@ +package sharethrough + +import ( + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/usersync" + "text/template" +) + +func NewSharethroughSyncer(temp *template.Template) usersync.Usersyncer { + return adapters.NewSyncer("sharethrough", 80, temp, adapters.SyncTypeRedirect) +} diff --git a/config/config.go b/config/config.go index 7eacf5e9229..47a1d40ed01 100644 --- a/config/config.go +++ b/config/config.go @@ -425,6 +425,7 @@ func (cfg *Configuration) setDerivedDefaults() { setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderPulsepoint, "https://bh.contextweb.com/rtset?pid=561205&ev=1&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dpulsepoint%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25%25VGUID%25%25") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderRhythmone, "https://sync.1rx.io/usersync2/rmphb?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Drhythmone%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%5BRX_UUID%5D") // openrtb_ext.BidderRubicon doesn't have a good default. + setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSharethrough, "https://sharethrough.adnxs.com/getuid?"+url.QueryEscape(externalURL)+"/setuid?bidder=sharethrough&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&uid=$UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSomoaudience, "https://publisher-east.mobileadtrading.com/usersync?ru="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsomoaudience%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUID%7D") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSovrn, "https://ap.lijit.com/pixel?redir="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsovrn%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID") setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderSonobi, "https://sync.go.sonobi.com/us.gif?loc="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dsonobi%26consent_string%3D{{.GDPR}}%26gdpr%3D{{.GDPRConsent}}%26uid%3D%24UID") @@ -556,6 +557,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.rhythmone.endpoint", "http://tag.1rx.io/rmp") v.SetDefault("adapters.gumgum.endpoint", "https://g2.gumgum.com/providers/prbds2s/bid") v.SetDefault("adapters.grid.endpoint", "http://grid.bidswitch.net/sp_bid?sp=prebid") + v.SetDefault("adapters.sharethrough.endpoint", "http://localhost:8001/butler") v.SetDefault("adapters.sonobi.endpoint", "https://apex.go.sonobi.com/prebid?partnerid=71d9d3d8af") v.SetDefault("adapters.yieldmo.endpoint", "http://ads.yieldmo.com/exchange/prebid-server") v.SetDefault("adapters.gamoshi.endpoint", "https://rtb.gamoshi.io") diff --git a/openrtb_ext/imp_sharethrough.go b/openrtb_ext/imp_sharethrough.go index 45859276488..13ed898b9d6 100644 --- a/openrtb_ext/imp_sharethrough.go +++ b/openrtb_ext/imp_sharethrough.go @@ -18,7 +18,7 @@ type ExtImpSharethroughResponse struct { type ExtImpSharethroughCreative struct { AuctionWinID string `json:"auctionWinId"` CPM float64 `json:"cpm"` - Creative ExtImpSharethroughCreativeMetadata `json:"creative"` + Metadata ExtImpSharethroughCreativeMetadata `json:"creative"` Version int `json:"version"` } diff --git a/static/bidder-params/sharethrough.json b/static/bidder-params/sharethrough.json index 9e66ad1d1f9..7b53e8f6125 100644 --- a/static/bidder-params/sharethrough.json +++ b/static/bidder-params/sharethrough.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-04/schema#", - "title": "EPlanning Adapter Params", + "title": "Sharethrough Adapter Params", "description": "A schema which validates params accepted by the Sharethrough adapter", "type": "object", "properties": { diff --git a/usersync/sharethrough.go b/usersync/sharethrough.go deleted file mode 100644 index 57fda251310..00000000000 --- a/usersync/sharethrough.go +++ /dev/null @@ -1,20 +0,0 @@ -package usersync - -import ( - "fmt" - "net/url" -) - -func NewSharethroughSyncer(externalURL string) Usersyncer { - redirect_uri := fmt.Sprintf("%s/setuid?bidder=sharethrough&uid=$UID", externalURL) - usersyncURL := "//sharethrough.adnxs.com/getuid?" - return &syncer{ - familyName: "adnxs", - gdprVendorID: 80, - syncInfo: &UsersyncInfo{ - URL: fmt.Sprintf("%s%s", usersyncURL, url.QueryEscape(redirect_uri)), - Type: "redirect", - SupportCORS: false, - }, - } -} From 3b15444beb9a4b2ed962bdcdb931df419d1bdd17 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Thu, 4 Apr 2019 17:21:34 -0700 Subject: [PATCH 06/26] WIP: adding butler params to the request #164291358 Co-authored-by: Josh Becker --- adapters/sharethrough/sharethrough.go | 271 +++++++++++++++---------- openrtb_ext/imp_sharethrough.go | 13 +- static/bidder-params/sharethrough.json | 17 +- 3 files changed, 187 insertions(+), 114 deletions(-) diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index b267f6b62be..9efe3d1a8b0 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -1,6 +1,7 @@ package sharethrough import ( + "bytes" "encoding/base64" "encoding/json" "fmt" @@ -11,9 +12,11 @@ import ( "html/template" "net/http" "net/url" + "strconv" ) -const hbEndpoint = "http://dumb-waiter.sharethrough.com/header-bid/v1" +const hbSource = "prebid-server" +const strVersion = "1.0.0" func NewSharethroughBidder(endpoint string) *SharethroughAdapter { return &SharethroughAdapter{URI: endpoint} @@ -30,17 +33,8 @@ func (s SharethroughAdapter) Name() string { return "sharethrough" } -type params struct { - BidID string `json:"bidId"` - PlacementKey string `json:"placement_key"` - HBVersion string `json:"hbVersion"` - StrVersion string `json:"strVersion"` - HBSource string `json:"hbSource"` -} - func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapters.RequestData, []error) { - fmt.Println("in sharethrough adapter") - pKeys := make([]string, 0, len(request.Imp)) + //fmt.Printf("in sharethrough adapter\nrequest: %+v\n", request) errs := make([]error, 0, len(request.Imp)) headers := http.Header{} var potentialRequests []*adapters.RequestData @@ -49,23 +43,42 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt headers.Add("Accept", "application/json") for i := 0; i < len(request.Imp); i++ { - pKey, err := preprocess(&request.Imp[i]) - if pKey != "" { - pKeys = append(pKeys, pKey) + imp := request.Imp[i] + + fmt.Printf("processing imp") + + var extBtlrParams openrtb_ext.ExtImpSharethroughExt + if err := json.Unmarshal(imp.Ext, &extBtlrParams); err != nil { + return nil, []error{err} } - // If the preprocessing failed, the server won't be able to bid on this Imp. Delete it, and note the error. - if err != nil { - errs = append(errs, err) - request.Imp = append(request.Imp[:i], request.Imp[i+1:]...) - i-- - continue + var extUser struct { + Consent string `json:"consent"` + } + if err := json.Unmarshal(request.User.Ext, &extUser); err != nil { + extUser.Consent = "" } + fmt.Println(extUser) + + // todo: get gdpr from Regs + //var extRegs struct{ Gdpr int } + //if err := json.Unmarshal(request.Regs.Ext, &extRegs); err != nil { + // extRegs.Gdpr = 0 + //} + + pKey := extBtlrParams.Bidder.Pkey - //hbURI := generateHBUri(pKey, "testBidID-"+strconv.Itoa(i)) potentialRequests = append(potentialRequests, &adapters.RequestData{ - Method: "POST", - Uri: s.URI + "?pkey=" + pKey, + Method: "POST", + Uri: generateHBUri(s.URI, hbUriParams{ + Pkey: pKey, + BidID: imp.ID, + //ConsentRequired: !(extRegs.Gdpr == 0), + ConsentString: extUser.Consent, + Iframe: extBtlrParams.Bidder.Iframe, + IframeWidth: extBtlrParams.Bidder.IframeSize[0], + IframeHeight: extBtlrParams.Bidder.IframeSize[1], + }), Body: nil, Headers: headers, }) @@ -75,9 +88,6 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt } func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { - fmt.Printf("internal request: %v\n", internalRequest) - fmt.Printf("external request: %v\n", externalRequest) - if response.StatusCode == http.StatusNoContent { return nil, nil } @@ -89,42 +99,17 @@ func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, exter } if response.StatusCode != http.StatusOK { - return nil, []error{fmt.Errorf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode)} + return nil, []error{fmt.Errorf("unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode)} } - var bidResp openrtb.BidResponse var strBidResp openrtb_ext.ExtImpSharethroughResponse if err := json.Unmarshal(response.Body, &strBidResp); err != nil { return nil, []error{err} } - bidResponse := adapters.NewBidderResponse() + br, bidderResponseErr := butlerToOpenRTBResponse(externalRequest, strBidResp) - br, _ := butlerToOpenRTBResponse(externalRequest, strBidResp) - fmt.Printf("br code: %v\n", br) - var errs []error - for _, sb := range bidResp.SeatBid { - for i := 0; i < len(sb.Bid); i++ { - bid := sb.Bid[i] - if bidType, err := getMediaTypeForBid(&bid); err == nil { - bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ - Bid: &bid, - BidType: bidType, - }) - } else { - errs = append(errs, err) - } - } - } - for _, bid := range bidResponse.Bids { - fmt.Printf("bidResponse.Bids: %+v\n", bid) - } - if len(errs) > 0 { - for _, err := range errs { - fmt.Printf("error: %s\n", err) - } - } - return bidResponse, errs + return br, bidderResponseErr } func butlerToOpenRTBResponse(btlrReq *adapters.RequestData, strResp openrtb_ext.ExtImpSharethroughResponse) (*adapters.BidderResponse, []error) { @@ -135,22 +120,18 @@ func butlerToOpenRTBResponse(btlrReq *adapters.RequestData, strResp openrtb_ext. typedBid := &adapters.TypedBid{BidType: openrtb_ext.BidTypeNative} creative := strResp.Creatives[0] - btlrUrl, err := url.Parse(btlrReq.Uri) - if err != nil { - errs = append(errs, err) - return nil, errs - } - pkey := btlrUrl.Query().Get("pkey") + btlrParams, _ := parseHBUri(btlrReq.Uri) bid := &openrtb.Bid{ - ID: strResp.BidID, - ImpID: strResp.AdServerRequestID, // MAYBE? - Price: creative.CPM, - // NURL: creative.Beacons.WinNotification[0] // what do we do with other notification URLs ??? + AdID: strResp.AdServerRequestID, + ID: strResp.BidID, + ImpID: btlrParams.BidID, + Price: creative.CPM, CID: creative.Metadata.CampaignKey, CrID: creative.Metadata.CreativeKey, DealID: creative.Metadata.DealID, - AdM: getAdMarkup(strResp, pkey), + AdM: getAdMarkup(strResp, btlrParams), + // NURL: creative.Beacons.WinNotification[0] // what do we do with other notification URLs ??? } typedBid.Bid = bid @@ -159,43 +140,73 @@ func butlerToOpenRTBResponse(btlrReq *adapters.RequestData, strResp openrtb_ext. return bidResponse, errs } -func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, pkey string) string { +func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriParams) string { strRespId := fmt.Sprintf("str_response_%s", strResp.BidID) - //b64EncodedJson := base64.NewEncoding(json.Mar) - //tmpl := ` - //
- // - //` - // - //let adMarkup = ` - //
- //
- // - //` - // - //if (req.strData.stayInIframe) { - // // Don't break out of iframe - // adMarkup = adMarkup + `` - //} else { - // // Break out of iframe - // adMarkup = adMarkup + ` - // - // ` - //} + jsonPayload, err := json.Marshal(strResp) + + if err != nil { + //handle error + fmt.Printf("ERROR: %s\n", err) + } + + tmplBody := ` +
+ + ` + + if params.Iframe { + tmplBody = tmplBody + ` + + ` + } else { + tmplBody = tmplBody + ` + + + ` + + } + + tmpl, err := template.New("sfpjs").Parse(tmplBody) + if err != nil { + // handle error + fmt.Printf("ERROR TEMPLATE: %s\n", err) + } + + var buf []byte + templatedBuf := bytes.NewBuffer(buf) + + b64EncodedJson := base64.StdEncoding.EncodeToString(jsonPayload) + err = tmpl.Execute(templatedBuf, struct { + Pkey string + StrRespId template.JS + B64EncodedJson string + }{ + params.Pkey, + template.JS(strRespId), + b64EncodedJson, + }) + + if err != nil { + // handle error + fmt.Printf("ERROR TEMPLATE Execute: %s\n", err) + + } + + return templatedBuf.String() } func getMediaTypeForBid(bid *openrtb.Bid) (openrtb_ext.BidType, error) { @@ -234,16 +245,54 @@ func preprocess(imp *openrtb.Imp) (pKey string, err error) { return sharethroughExt.PlacementKey, nil } -func generateHBUri(pKey string, bidID string) string { - return "http://localhost:8000/bid" +type hbUriParams struct { + Pkey string + BidID string + ConsentRequired bool + ConsentString string + InstantPlayCapable bool + Iframe bool + IframeHeight int + IframeWidth int } -// func generateHBUri(pKey string, bidID string) string { -// v := url.Values{} -// v.Set("placement_key", pKey) -// v.Set("bidId", bidID) -// v.Set("hbVersion", "test-version") -// v.Set("hbSource", "prebid-server") +func generateHBUri(baseUrl string, params hbUriParams) string { + v := url.Values{} + v.Set("placement_key", params.Pkey) + v.Set("bidId", params.BidID) + v.Set("consent_required", fmt.Sprintf("%t", params.ConsentRequired)) + + if params.ConsentRequired { + v.Set("consent_string", params.ConsentString) + } + + v.Set("instant_play_capable", fmt.Sprintf("%t", params.InstantPlayCapable)) + v.Set("stayInIframe", fmt.Sprintf("%t", params.Iframe)) + v.Set("iframeHeight", string(params.IframeHeight)) + v.Set("iframeWidth", string(params.IframeWidth)) + + v.Set("hbVersion", "test-version") // todo: figure out the version dynamically + v.Set("hbSource", hbSource) + v.Set("strVersion", strVersion) -// return hbEndpoint + "?" + v.Encode() -// } + return baseUrl + "?" + v.Encode() +} + +func parseHBUri(uri string) (*hbUriParams, error) { + btlrUrl, err := url.Parse(uri) + if err != nil { + return nil, err + } + + params := btlrUrl.Query() + iframeHeight, _ := strconv.ParseInt(params.Get("iframeHeight"), 10, 64) + iframeWidth, _ := strconv.ParseInt(params.Get("iframeWidth"), 10, 64) + + return &hbUriParams{ + Pkey: params.Get("placement_key"), + BidID: params.Get("bidId"), + Iframe: params.Get("stayInIframe") == "true", + IframeHeight: int(iframeHeight), + IframeWidth: int(iframeWidth), + }, nil +} diff --git a/openrtb_ext/imp_sharethrough.go b/openrtb_ext/imp_sharethrough.go index 13ed898b9d6..6d30bfb7b78 100644 --- a/openrtb_ext/imp_sharethrough.go +++ b/openrtb_ext/imp_sharethrough.go @@ -4,6 +4,7 @@ import "encoding/json" type ExtImpSharethrough struct { PlacementKey string `json:"pkey"` + Iframe bool `json:"iframe"` } // ExtImpSharethrough defines the contract for bidrequest.imp[i].ext.sharethrough @@ -23,7 +24,7 @@ type ExtImpSharethroughCreative struct { } type ExtImpSharethroughCreativeMetadata struct { - Action string `json:"clickout"` + Action string `json:"action"` Advertiser string `json:"advertiser"` AdvertiserKey string `json:"advertiser_key"` Beacons ExtImpSharethroughCreativeBeacons `json:"beacons"` @@ -62,7 +63,7 @@ type ExtImpSharethroughPlacement struct { ArticlesBetweenAds int `json:"articles_between_ads"` Layout string `json:"layout"` Metadata json.RawMessage `json:"metadata"` - PlacementAttributes ExtImpSharethroughPlacementAttributes `json:"placement_attributes"` + PlacementAttributes ExtImpSharethroughPlacementAttributes `json:"placementAttributes"` Status string `json:"status"` } @@ -88,3 +89,11 @@ type ExtImpSharethroughPlacementAttributes struct { Template string `json:"template"` ThirdPartyPartners []string `json:"third_party_partners"` } + +type ExtImpSharethroughExt struct { + Bidder struct { + Pkey string `json:"pkey"` + Iframe bool `json:"iframe"` + IframeSize []int `json:"iframeSize"` + } `json:"bidder"` +} diff --git a/static/bidder-params/sharethrough.json b/static/bidder-params/sharethrough.json index 7b53e8f6125..03f4ec293ec 100644 --- a/static/bidder-params/sharethrough.json +++ b/static/bidder-params/sharethrough.json @@ -5,8 +5,23 @@ "type": "object", "properties": { "pkey": { - "type": ["string"], + "type": "string", "description": "placement key to use." + }, + "iframe": { + "type": "boolean", + "description": "whether or not to stay in iframe", + "default": false + }, + "iframeSize": { + "type": "array", + "minItems": 2, + "maxItems": 2, + "items": { + "type": "integer" + }, + "description": "iframe dimensions", + "default": [0, 0] } }, "required": ["pkey"] From 49002fbf9d875f052ac862681dc6383db565d774 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Thu, 11 Apr 2019 15:45:56 -0700 Subject: [PATCH 07/26] Manage bid sizes [#164291358] Co-authored-by: Josh Becker --- adapters/sharethrough/sharethrough.go | 56 ++++++++++++++++++++------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index 9efe3d1a8b0..3d4615ada93 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -68,6 +68,13 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt pKey := extBtlrParams.Bidder.Pkey + var height, width uint64 + if len(extBtlrParams.Bidder.IframeSize) >= 2 { + height, width = uint64(extBtlrParams.Bidder.IframeSize[0]), uint64(extBtlrParams.Bidder.IframeSize[1]) + } else { + height, width = getPlacementSize(imp.Banner.Format) + } + potentialRequests = append(potentialRequests, &adapters.RequestData{ Method: "POST", Uri: generateHBUri(s.URI, hbUriParams{ @@ -76,8 +83,8 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt //ConsentRequired: !(extRegs.Gdpr == 0), ConsentString: extUser.Consent, Iframe: extBtlrParams.Bidder.Iframe, - IframeWidth: extBtlrParams.Bidder.IframeSize[0], - IframeHeight: extBtlrParams.Bidder.IframeSize[1], + Height: height, + Width: width, }), Body: nil, Headers: headers, @@ -131,7 +138,8 @@ func butlerToOpenRTBResponse(btlrReq *adapters.RequestData, strResp openrtb_ext. CrID: creative.Metadata.CreativeKey, DealID: creative.Metadata.DealID, AdM: getAdMarkup(strResp, btlrParams), - // NURL: creative.Beacons.WinNotification[0] // what do we do with other notification URLs ??? + H: btlrParams.Height, + W: btlrParams.Width, } typedBid.Bid = bid @@ -252,8 +260,8 @@ type hbUriParams struct { ConsentString string InstantPlayCapable bool Iframe bool - IframeHeight int - IframeWidth int + Height uint64 + Width uint64 } func generateHBUri(baseUrl string, params hbUriParams) string { @@ -268,8 +276,8 @@ func generateHBUri(baseUrl string, params hbUriParams) string { v.Set("instant_play_capable", fmt.Sprintf("%t", params.InstantPlayCapable)) v.Set("stayInIframe", fmt.Sprintf("%t", params.Iframe)) - v.Set("iframeHeight", string(params.IframeHeight)) - v.Set("iframeWidth", string(params.IframeWidth)) + v.Set("height", strconv.FormatUint(params.Height, 10)) + v.Set("width", strconv.FormatUint(params.Width, 10)) v.Set("hbVersion", "test-version") // todo: figure out the version dynamically v.Set("hbSource", hbSource) @@ -285,14 +293,34 @@ func parseHBUri(uri string) (*hbUriParams, error) { } params := btlrUrl.Query() - iframeHeight, _ := strconv.ParseInt(params.Get("iframeHeight"), 10, 64) - iframeWidth, _ := strconv.ParseInt(params.Get("iframeWidth"), 10, 64) + height, _ := strconv.ParseUint(params.Get("height"), 10, 64) + width, _ := strconv.ParseUint(params.Get("width"), 10, 64) return &hbUriParams{ - Pkey: params.Get("placement_key"), - BidID: params.Get("bidId"), - Iframe: params.Get("stayInIframe") == "true", - IframeHeight: int(iframeHeight), - IframeWidth: int(iframeWidth), + Pkey: params.Get("placement_key"), + BidID: params.Get("bidId"), + Iframe: params.Get("stayInIframe") == "true", + Height: height, + Width: width, }, nil } + +func getPlacementSize(formats []openrtb.Format) (height uint64, width uint64) { + biggest := struct { + Height uint64 + Width uint64 + }{ + Height: 1, + Width: 1, + } + + for i := 0; i < len(formats); i++ { + format := formats[i] + if (format.H * format.W) > (biggest.Height * biggest.Width) { + biggest.Height = format.H + biggest.Width = format.W + } + } + + return biggest.Height, biggest.Width +} From 8d4fff0fdedc52fbc1e467e0536024c790e15af9 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Thu, 11 Apr 2019 17:11:52 -0700 Subject: [PATCH 08/26] Manage GDPR [#164291358] Co-authored-by: Josh Becker --- adapters/sharethrough/sharethrough.go | 47 ++++++++++++++------------- 1 file changed, 25 insertions(+), 22 deletions(-) diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index 3d4615ada93..c6ea37adb9c 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -58,13 +58,17 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt if err := json.Unmarshal(request.User.Ext, &extUser); err != nil { extUser.Consent = "" } - fmt.Println(extUser) - // todo: get gdpr from Regs - //var extRegs struct{ Gdpr int } - //if err := json.Unmarshal(request.Regs.Ext, &extRegs); err != nil { - // extRegs.Gdpr = 0 - //} + var extRegs struct { + Gdpr int `json:"gdpr"` + } + if request.Regs != nil && request.Regs.Ext != nil { + if err := json.Unmarshal(request.Regs.Ext, &extRegs); err != nil { + extRegs.Gdpr = 0 + } + } else { + extRegs.Gdpr = 0 + } pKey := extBtlrParams.Bidder.Pkey @@ -78,13 +82,13 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt potentialRequests = append(potentialRequests, &adapters.RequestData{ Method: "POST", Uri: generateHBUri(s.URI, hbUriParams{ - Pkey: pKey, - BidID: imp.ID, - //ConsentRequired: !(extRegs.Gdpr == 0), - ConsentString: extUser.Consent, - Iframe: extBtlrParams.Bidder.Iframe, - Height: height, - Width: width, + Pkey: pKey, + BidID: imp.ID, + ConsentRequired: !(extRegs.Gdpr == 0), + ConsentString: extUser.Consent, + Iframe: extBtlrParams.Bidder.Iframe, + Height: height, + Width: width, }), Body: nil, Headers: headers, @@ -269,10 +273,7 @@ func generateHBUri(baseUrl string, params hbUriParams) string { v.Set("placement_key", params.Pkey) v.Set("bidId", params.BidID) v.Set("consent_required", fmt.Sprintf("%t", params.ConsentRequired)) - - if params.ConsentRequired { - v.Set("consent_string", params.ConsentString) - } + v.Set("consent_string", params.ConsentString) v.Set("instant_play_capable", fmt.Sprintf("%t", params.InstantPlayCapable)) v.Set("stayInIframe", fmt.Sprintf("%t", params.Iframe)) @@ -297,11 +298,13 @@ func parseHBUri(uri string) (*hbUriParams, error) { width, _ := strconv.ParseUint(params.Get("width"), 10, 64) return &hbUriParams{ - Pkey: params.Get("placement_key"), - BidID: params.Get("bidId"), - Iframe: params.Get("stayInIframe") == "true", - Height: height, - Width: width, + Pkey: params.Get("placement_key"), + BidID: params.Get("bidId"), + Iframe: params.Get("stayInIframe") == "true", + Height: height, + Width: width, + ConsentRequired: params.Get("consent_required") == "true", + ConsentString: params.Get("consent_string"), }, nil } From e492203e6edea770a917a918b205a01b354c3293 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Thu, 11 Apr 2019 17:28:31 -0700 Subject: [PATCH 09/26] Populate prebid-server version if provided [#164291358] Co-authored-by: Josh Becker --- adapters/sharethrough/sharethrough.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index c6ea37adb9c..71d4a571b9e 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "github.com/buger/jsonparser" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/errortypes" @@ -89,7 +90,7 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt Iframe: extBtlrParams.Bidder.Iframe, Height: height, Width: width, - }), + }, request.App), Body: nil, Headers: headers, }) @@ -268,7 +269,7 @@ type hbUriParams struct { Width uint64 } -func generateHBUri(baseUrl string, params hbUriParams) string { +func generateHBUri(baseUrl string, params hbUriParams, app *openrtb.App) string { v := url.Values{} v.Set("placement_key", params.Pkey) v.Set("bidId", params.BidID) @@ -280,7 +281,17 @@ func generateHBUri(baseUrl string, params hbUriParams) string { v.Set("height", strconv.FormatUint(params.Height, 10)) v.Set("width", strconv.FormatUint(params.Width, 10)) - v.Set("hbVersion", "test-version") // todo: figure out the version dynamically + var version string + if app != nil { + var err error + version, err = jsonparser.GetString(app.Ext, "prebid", "version") + if err == nil { + // todo: handle error + fmt.Printf("Error extracting version: %+v", err) + } + } + + v.Set("hbVersion", version) v.Set("hbSource", hbSource) v.Set("strVersion", strVersion) From edc8d76397b16144ea858177fb3d2ea15074cf24 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Fri, 12 Apr 2019 14:29:28 -0700 Subject: [PATCH 10/26] Refactor gdpr data extraction from request [#164291358] Co-authored-by: Eddy Pechuzal --- adapters/sharethrough/sharethrough.go | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index 71d4a571b9e..8f491de36fb 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -53,22 +53,18 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt return nil, []error{err} } - var extUser struct { - Consent string `json:"consent"` - } - if err := json.Unmarshal(request.User.Ext, &extUser); err != nil { - extUser.Consent = "" + var gdprApplies int64 = 0 + if request.Regs != nil { + if jsonExtRegs, err := request.Regs.Ext.MarshalJSON(); err == nil { + gdprApplies, _ = jsonparser.GetInt(jsonExtRegs, "gdpr") + } } - var extRegs struct { - Gdpr int `json:"gdpr"` - } - if request.Regs != nil && request.Regs.Ext != nil { - if err := json.Unmarshal(request.Regs.Ext, &extRegs); err != nil { - extRegs.Gdpr = 0 + consentString := "" + if request.User != nil { + if jsonExtUser, err := request.User.Ext.MarshalJSON(); err == nil { + consentString, _ = jsonparser.GetString(jsonExtUser, "consent") } - } else { - extRegs.Gdpr = 0 } pKey := extBtlrParams.Bidder.Pkey @@ -85,8 +81,8 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt Uri: generateHBUri(s.URI, hbUriParams{ Pkey: pKey, BidID: imp.ID, - ConsentRequired: !(extRegs.Gdpr == 0), - ConsentString: extUser.Consent, + ConsentRequired: !(gdprApplies == 0), + ConsentString: consentString, Iframe: extBtlrParams.Bidder.Iframe, Height: height, Width: width, From c9301b4791230a92c55790a78adce6f0ded7d39e Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Fri, 12 Apr 2019 15:24:36 -0700 Subject: [PATCH 11/26] Add instant play capability [#164291358] Co-authored-by: Eddy Pechuzal --- adapters/sharethrough/sharethrough.go | 82 +++++++++++++-------------- 1 file changed, 39 insertions(+), 43 deletions(-) diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index 8f491de36fb..cb29c583ec5 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -13,6 +13,7 @@ import ( "html/template" "net/http" "net/url" + "regexp" "strconv" ) @@ -79,13 +80,14 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt potentialRequests = append(potentialRequests, &adapters.RequestData{ Method: "POST", Uri: generateHBUri(s.URI, hbUriParams{ - Pkey: pKey, - BidID: imp.ID, - ConsentRequired: !(gdprApplies == 0), - ConsentString: consentString, - Iframe: extBtlrParams.Bidder.Iframe, - Height: height, - Width: width, + Pkey: pKey, + BidID: imp.ID, + ConsentRequired: !(gdprApplies == 0), + ConsentString: consentString, + Iframe: extBtlrParams.Bidder.Iframe, + Height: height, + Width: width, + InstantPlayCapable: canAutoPlayVideo(request.Device.UA), }, request.App), Body: nil, Headers: headers, @@ -218,42 +220,6 @@ func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriPa return templatedBuf.String() } -func getMediaTypeForBid(bid *openrtb.Bid) (openrtb_ext.BidType, error) { - return openrtb_ext.BidTypeNative, nil - // var impExt struct { - // Sharethrough struct { - // BidType int `json:"bid_type"` - // } `json:"sharethrough"` - // } - // if err := json.Unmarshal(bid.Ext, &impExt); err != nil { - // return "", err - // } - // switch impExt.Sharethrough.BidType { - // case 0: - // return openrtb_ext.BidTypeBanner, nil - // case 1: - // return openrtb_ext.BidTypeVideo, nil - // case 2: - // return openrtb_ext.BidTypeNative, nil - // default: - // return "", fmt.Errorf("Unrecognized bid_ad_type in response from sharethrough: %d", impExt.Sharethrough.BidType) - // } -} - -func preprocess(imp *openrtb.Imp) (pKey string, err error) { - var bidderExt adapters.ExtImpBidder - if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { - return "", err - } - - var sharethroughExt openrtb_ext.ExtImpSharethrough - if err := json.Unmarshal(bidderExt.Bidder, &sharethroughExt); err != nil { - return "", err - } - - return sharethroughExt.PlacementKey, nil -} - type hbUriParams struct { Pkey string BidID string @@ -334,3 +300,33 @@ func getPlacementSize(formats []openrtb.Format) (height uint64, width uint64) { return biggest.Height, biggest.Width } + +func canAutoPlayVideo(userAgent string) bool { + const minChromeVersion = 53 + const minSafariVersion = 10 + + isAndroid, _ := regexp.MatchString("(?i)Android", userAgent) + isiOS, _ := regexp.MatchString("(?i)iPhone|iPad|iPod", userAgent) + + var chromeVersion, chromeiOSVersion, safariVersion int64 + + chromeVersionRegex := regexp.MustCompile(`Chrome\/(?P\d+)`) + chromeVersionMatch := chromeVersionRegex.FindStringSubmatch(userAgent) + if len(chromeVersionMatch) > 1 { + chromeVersion, _ = strconv.ParseInt(chromeVersionMatch[1], 10, 64) + } + + chromeiOSVersionRegex := regexp.MustCompile(`CriOS\/(?P\d+)`) + chromeiOSVersionMatch := chromeiOSVersionRegex.FindStringSubmatch(userAgent) + if len(chromeiOSVersionMatch) > 1 { + chromeiOSVersion, _ = strconv.ParseInt(chromeiOSVersionMatch[1], 10, 64) + } + + safariVersionRegex := regexp.MustCompile(`Version\/(?P\d+)`) + safariVersionMatch := safariVersionRegex.FindStringSubmatch(userAgent) + if len(safariVersionMatch) > 1 { + safariVersion, _ = strconv.ParseInt(safariVersionMatch[1], 10, 64) + } + + return (isAndroid && chromeVersion >= minChromeVersion) || (isiOS && (safariVersion >= minSafariVersion || chromeiOSVersion >= minChromeVersion)) || !(isAndroid || isiOS) +} From 1b2044209c7c3214addef7e605136e83c07bbc48 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Fri, 12 Apr 2019 15:43:53 -0700 Subject: [PATCH 12/26] Split in multiple files [#164291358] Co-authored-by: Eddy Pechuzal --- adapters/sharethrough/butler.go | 101 ++++++++++++ adapters/sharethrough/sharethrough.go | 217 -------------------------- adapters/sharethrough/utils.go | 132 ++++++++++++++++ 3 files changed, 233 insertions(+), 217 deletions(-) create mode 100644 adapters/sharethrough/butler.go create mode 100644 adapters/sharethrough/utils.go diff --git a/adapters/sharethrough/butler.go b/adapters/sharethrough/butler.go new file mode 100644 index 00000000000..e9208d902ae --- /dev/null +++ b/adapters/sharethrough/butler.go @@ -0,0 +1,101 @@ +package sharethrough + +import ( + "fmt" + "github.com/buger/jsonparser" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/openrtb_ext" + "net/url" + "strconv" +) + +type hbUriParams struct { + Pkey string + BidID string + ConsentRequired bool + ConsentString string + InstantPlayCapable bool + Iframe bool + Height uint64 + Width uint64 +} + +func butlerToOpenRTBResponse(btlrReq *adapters.RequestData, strResp openrtb_ext.ExtImpSharethroughResponse) (*adapters.BidderResponse, []error) { + var errs []error + bidResponse := adapters.NewBidderResponse() + + bidResponse.Currency = "USD" + typedBid := &adapters.TypedBid{BidType: openrtb_ext.BidTypeNative} + creative := strResp.Creatives[0] + + btlrParams, _ := parseHBUri(btlrReq.Uri) + + bid := &openrtb.Bid{ + AdID: strResp.AdServerRequestID, + ID: strResp.BidID, + ImpID: btlrParams.BidID, + Price: creative.CPM, + CID: creative.Metadata.CampaignKey, + CrID: creative.Metadata.CreativeKey, + DealID: creative.Metadata.DealID, + AdM: getAdMarkup(strResp, btlrParams), + H: btlrParams.Height, + W: btlrParams.Width, + } + + typedBid.Bid = bid + bidResponse.Bids = append(bidResponse.Bids, typedBid) + + return bidResponse, errs +} + +func generateHBUri(baseUrl string, params hbUriParams, app *openrtb.App) string { + v := url.Values{} + v.Set("placement_key", params.Pkey) + v.Set("bidId", params.BidID) + v.Set("consent_required", fmt.Sprintf("%t", params.ConsentRequired)) + v.Set("consent_string", params.ConsentString) + + v.Set("instant_play_capable", fmt.Sprintf("%t", params.InstantPlayCapable)) + v.Set("stayInIframe", fmt.Sprintf("%t", params.Iframe)) + v.Set("height", strconv.FormatUint(params.Height, 10)) + v.Set("width", strconv.FormatUint(params.Width, 10)) + + var version string + if app != nil { + var err error + version, err = jsonparser.GetString(app.Ext, "prebid", "version") + if err == nil { + // todo: handle error + fmt.Printf("Error extracting version: %+v", err) + } + } + + v.Set("hbVersion", version) + v.Set("hbSource", hbSource) + v.Set("strVersion", strVersion) + + return baseUrl + "?" + v.Encode() +} + +func parseHBUri(uri string) (*hbUriParams, error) { + btlrUrl, err := url.Parse(uri) + if err != nil { + return nil, err + } + + params := btlrUrl.Query() + height, _ := strconv.ParseUint(params.Get("height"), 10, 64) + width, _ := strconv.ParseUint(params.Get("width"), 10, 64) + + return &hbUriParams{ + Pkey: params.Get("placement_key"), + BidID: params.Get("bidId"), + Iframe: params.Get("stayInIframe") == "true", + Height: height, + Width: width, + ConsentRequired: params.Get("consent_required") == "true", + ConsentString: params.Get("consent_string"), + }, nil +} diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index cb29c583ec5..6496ea80c92 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -1,8 +1,6 @@ package sharethrough import ( - "bytes" - "encoding/base64" "encoding/json" "fmt" "github.com/buger/jsonparser" @@ -10,11 +8,7 @@ import ( "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" - "html/template" "net/http" - "net/url" - "regexp" - "strconv" ) const hbSource = "prebid-server" @@ -24,8 +18,6 @@ func NewSharethroughBidder(endpoint string) *SharethroughAdapter { return &SharethroughAdapter{URI: endpoint} } -// SharethroughAdapter converts the Sharethrough Adserver response into a -// prebid server compatible format type SharethroughAdapter struct { URI string } @@ -121,212 +113,3 @@ func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, exter return br, bidderResponseErr } - -func butlerToOpenRTBResponse(btlrReq *adapters.RequestData, strResp openrtb_ext.ExtImpSharethroughResponse) (*adapters.BidderResponse, []error) { - var errs []error - bidResponse := adapters.NewBidderResponse() - - bidResponse.Currency = "USD" - typedBid := &adapters.TypedBid{BidType: openrtb_ext.BidTypeNative} - creative := strResp.Creatives[0] - - btlrParams, _ := parseHBUri(btlrReq.Uri) - - bid := &openrtb.Bid{ - AdID: strResp.AdServerRequestID, - ID: strResp.BidID, - ImpID: btlrParams.BidID, - Price: creative.CPM, - CID: creative.Metadata.CampaignKey, - CrID: creative.Metadata.CreativeKey, - DealID: creative.Metadata.DealID, - AdM: getAdMarkup(strResp, btlrParams), - H: btlrParams.Height, - W: btlrParams.Width, - } - - typedBid.Bid = bid - bidResponse.Bids = append(bidResponse.Bids, typedBid) - - return bidResponse, errs -} - -func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriParams) string { - strRespId := fmt.Sprintf("str_response_%s", strResp.BidID) - jsonPayload, err := json.Marshal(strResp) - - if err != nil { - //handle error - fmt.Printf("ERROR: %s\n", err) - } - - tmplBody := ` -
- - ` - - if params.Iframe { - tmplBody = tmplBody + ` - - ` - } else { - tmplBody = tmplBody + ` - - - ` - - } - - tmpl, err := template.New("sfpjs").Parse(tmplBody) - if err != nil { - // handle error - fmt.Printf("ERROR TEMPLATE: %s\n", err) - } - - var buf []byte - templatedBuf := bytes.NewBuffer(buf) - - b64EncodedJson := base64.StdEncoding.EncodeToString(jsonPayload) - err = tmpl.Execute(templatedBuf, struct { - Pkey string - StrRespId template.JS - B64EncodedJson string - }{ - params.Pkey, - template.JS(strRespId), - b64EncodedJson, - }) - - if err != nil { - // handle error - fmt.Printf("ERROR TEMPLATE Execute: %s\n", err) - - } - - return templatedBuf.String() -} - -type hbUriParams struct { - Pkey string - BidID string - ConsentRequired bool - ConsentString string - InstantPlayCapable bool - Iframe bool - Height uint64 - Width uint64 -} - -func generateHBUri(baseUrl string, params hbUriParams, app *openrtb.App) string { - v := url.Values{} - v.Set("placement_key", params.Pkey) - v.Set("bidId", params.BidID) - v.Set("consent_required", fmt.Sprintf("%t", params.ConsentRequired)) - v.Set("consent_string", params.ConsentString) - - v.Set("instant_play_capable", fmt.Sprintf("%t", params.InstantPlayCapable)) - v.Set("stayInIframe", fmt.Sprintf("%t", params.Iframe)) - v.Set("height", strconv.FormatUint(params.Height, 10)) - v.Set("width", strconv.FormatUint(params.Width, 10)) - - var version string - if app != nil { - var err error - version, err = jsonparser.GetString(app.Ext, "prebid", "version") - if err == nil { - // todo: handle error - fmt.Printf("Error extracting version: %+v", err) - } - } - - v.Set("hbVersion", version) - v.Set("hbSource", hbSource) - v.Set("strVersion", strVersion) - - return baseUrl + "?" + v.Encode() -} - -func parseHBUri(uri string) (*hbUriParams, error) { - btlrUrl, err := url.Parse(uri) - if err != nil { - return nil, err - } - - params := btlrUrl.Query() - height, _ := strconv.ParseUint(params.Get("height"), 10, 64) - width, _ := strconv.ParseUint(params.Get("width"), 10, 64) - - return &hbUriParams{ - Pkey: params.Get("placement_key"), - BidID: params.Get("bidId"), - Iframe: params.Get("stayInIframe") == "true", - Height: height, - Width: width, - ConsentRequired: params.Get("consent_required") == "true", - ConsentString: params.Get("consent_string"), - }, nil -} - -func getPlacementSize(formats []openrtb.Format) (height uint64, width uint64) { - biggest := struct { - Height uint64 - Width uint64 - }{ - Height: 1, - Width: 1, - } - - for i := 0; i < len(formats); i++ { - format := formats[i] - if (format.H * format.W) > (biggest.Height * biggest.Width) { - biggest.Height = format.H - biggest.Width = format.W - } - } - - return biggest.Height, biggest.Width -} - -func canAutoPlayVideo(userAgent string) bool { - const minChromeVersion = 53 - const minSafariVersion = 10 - - isAndroid, _ := regexp.MatchString("(?i)Android", userAgent) - isiOS, _ := regexp.MatchString("(?i)iPhone|iPad|iPod", userAgent) - - var chromeVersion, chromeiOSVersion, safariVersion int64 - - chromeVersionRegex := regexp.MustCompile(`Chrome\/(?P\d+)`) - chromeVersionMatch := chromeVersionRegex.FindStringSubmatch(userAgent) - if len(chromeVersionMatch) > 1 { - chromeVersion, _ = strconv.ParseInt(chromeVersionMatch[1], 10, 64) - } - - chromeiOSVersionRegex := regexp.MustCompile(`CriOS\/(?P\d+)`) - chromeiOSVersionMatch := chromeiOSVersionRegex.FindStringSubmatch(userAgent) - if len(chromeiOSVersionMatch) > 1 { - chromeiOSVersion, _ = strconv.ParseInt(chromeiOSVersionMatch[1], 10, 64) - } - - safariVersionRegex := regexp.MustCompile(`Version\/(?P\d+)`) - safariVersionMatch := safariVersionRegex.FindStringSubmatch(userAgent) - if len(safariVersionMatch) > 1 { - safariVersion, _ = strconv.ParseInt(safariVersionMatch[1], 10, 64) - } - - return (isAndroid && chromeVersion >= minChromeVersion) || (isiOS && (safariVersion >= minSafariVersion || chromeiOSVersion >= minChromeVersion)) || !(isAndroid || isiOS) -} diff --git a/adapters/sharethrough/utils.go b/adapters/sharethrough/utils.go new file mode 100644 index 00000000000..84f4b0d87bd --- /dev/null +++ b/adapters/sharethrough/utils.go @@ -0,0 +1,132 @@ +package sharethrough + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/openrtb_ext" + "html/template" + "regexp" + "strconv" +) + +func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriParams) string { + strRespId := fmt.Sprintf("str_response_%s", strResp.BidID) + jsonPayload, err := json.Marshal(strResp) + + if err != nil { + //handle error + fmt.Printf("ERROR: %s\n", err) + } + + tmplBody := ` +
+ + ` + + if params.Iframe { + tmplBody = tmplBody + ` + + ` + } else { + tmplBody = tmplBody + ` + + + ` + + } + + tmpl, err := template.New("sfpjs").Parse(tmplBody) + if err != nil { + // handle error + fmt.Printf("ERROR TEMPLATE: %s\n", err) + } + + var buf []byte + templatedBuf := bytes.NewBuffer(buf) + + b64EncodedJson := base64.StdEncoding.EncodeToString(jsonPayload) + err = tmpl.Execute(templatedBuf, struct { + Pkey string + StrRespId template.JS + B64EncodedJson string + }{ + params.Pkey, + template.JS(strRespId), + b64EncodedJson, + }) + + if err != nil { + // handle error + fmt.Printf("ERROR TEMPLATE Execute: %s\n", err) + + } + + return templatedBuf.String() +} + +func getPlacementSize(formats []openrtb.Format) (height uint64, width uint64) { + biggest := struct { + Height uint64 + Width uint64 + }{ + Height: 1, + Width: 1, + } + + for i := 0; i < len(formats); i++ { + format := formats[i] + if (format.H * format.W) > (biggest.Height * biggest.Width) { + biggest.Height = format.H + biggest.Width = format.W + } + } + + return biggest.Height, biggest.Width +} + +func canAutoPlayVideo(userAgent string) bool { + const minChromeVersion = 53 + const minSafariVersion = 10 + + isAndroid, _ := regexp.MatchString("(?i)Android", userAgent) + isiOS, _ := regexp.MatchString("(?i)iPhone|iPad|iPod", userAgent) + + var chromeVersion, chromeiOSVersion, safariVersion int64 + + chromeVersionRegex := regexp.MustCompile(`Chrome\/(?P\d+)`) + chromeVersionMatch := chromeVersionRegex.FindStringSubmatch(userAgent) + if len(chromeVersionMatch) > 1 { + chromeVersion, _ = strconv.ParseInt(chromeVersionMatch[1], 10, 64) + } + + chromeiOSVersionRegex := regexp.MustCompile(`CriOS\/(?P\d+)`) + chromeiOSVersionMatch := chromeiOSVersionRegex.FindStringSubmatch(userAgent) + if len(chromeiOSVersionMatch) > 1 { + chromeiOSVersion, _ = strconv.ParseInt(chromeiOSVersionMatch[1], 10, 64) + } + + safariVersionRegex := regexp.MustCompile(`Version\/(?P\d+)`) + safariVersionMatch := safariVersionRegex.FindStringSubmatch(userAgent) + if len(safariVersionMatch) > 1 { + safariVersion, _ = strconv.ParseInt(safariVersionMatch[1], 10, 64) + } + + return (isAndroid && chromeVersion >= minChromeVersion) || (isiOS && (safariVersion >= minSafariVersion || chromeiOSVersion >= minChromeVersion)) || !(isAndroid || isiOS) +} From 665ae99ce04766a3cca035ab7d32bf44a324f2a0 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Fri, 19 Apr 2019 15:13:15 -0700 Subject: [PATCH 13/26] Add s2s-win beacon todo? replace server name by server id? [#164291358] Co-authored-by: Eddy Pechuzal --- adapters/sharethrough/utils.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/adapters/sharethrough/utils.go b/adapters/sharethrough/utils.go index 84f4b0d87bd..74853a432d4 100644 --- a/adapters/sharethrough/utils.go +++ b/adapters/sharethrough/utils.go @@ -22,6 +22,8 @@ func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriPa } tmplBody := ` + +
` @@ -63,10 +65,12 @@ func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriPa b64EncodedJson := base64.StdEncoding.EncodeToString(jsonPayload) err = tmpl.Execute(templatedBuf, struct { + Arid template.JS Pkey string StrRespId template.JS B64EncodedJson string }{ + template.JS(strResp.AdServerRequestID), params.Pkey, template.JS(strRespId), b64EncodedJson, From c6cefa5e930e36d30111655b97ac2fd24ec873c5 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Tue, 23 Apr 2019 09:15:49 -0500 Subject: [PATCH 14/26] Removing `server` param in s2s-win beacon (will be added in imp req) [#164291358] --- adapters/sharethrough/utils.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/sharethrough/utils.go b/adapters/sharethrough/utils.go index 74853a432d4..0a5e34f7f92 100644 --- a/adapters/sharethrough/utils.go +++ b/adapters/sharethrough/utils.go @@ -22,7 +22,7 @@ func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriPa } tmplBody := ` - +
From 6800634a48a56899ef2f047eaf2a7aa38d78d131 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Fri, 3 May 2019 13:18:40 -0700 Subject: [PATCH 15/26] Clean up code + enable syncer (#4) [#165257793] --- adapters/sharethrough/butler.go | 14 ++++++-------- adapters/sharethrough/sharethrough.go | 8 ++------ adapters/sharethrough/utils.go | 17 +++++------------ usersync/usersyncers/syncer.go | 2 ++ 4 files changed, 15 insertions(+), 26 deletions(-) diff --git a/adapters/sharethrough/butler.go b/adapters/sharethrough/butler.go index e9208d902ae..a985be280c4 100644 --- a/adapters/sharethrough/butler.go +++ b/adapters/sharethrough/butler.go @@ -29,7 +29,10 @@ func butlerToOpenRTBResponse(btlrReq *adapters.RequestData, strResp openrtb_ext. typedBid := &adapters.TypedBid{BidType: openrtb_ext.BidTypeNative} creative := strResp.Creatives[0] - btlrParams, _ := parseHBUri(btlrReq.Uri) + btlrParams, err := parseHBUri(btlrReq.Uri) + if err != nil { + errs = append(errs, err) + } bid := &openrtb.Bid{ AdID: strResp.AdServerRequestID, @@ -62,14 +65,9 @@ func generateHBUri(baseUrl string, params hbUriParams, app *openrtb.App) string v.Set("height", strconv.FormatUint(params.Height, 10)) v.Set("width", strconv.FormatUint(params.Width, 10)) - var version string + version := "unknown" if app != nil { - var err error - version, err = jsonparser.GetString(app.Ext, "prebid", "version") - if err == nil { - // todo: handle error - fmt.Printf("Error extracting version: %+v", err) - } + version, _ = jsonparser.GetString(app.Ext, "prebid", "version") } v.Set("hbVersion", version) diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index 6496ea80c92..79d2d7a21c1 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -22,13 +22,11 @@ type SharethroughAdapter struct { URI string } -// Name returns the adapter name as a string func (s SharethroughAdapter) Name() string { return "sharethrough" } func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapters.RequestData, []error) { - //fmt.Printf("in sharethrough adapter\nrequest: %+v\n", request) errs := make([]error, 0, len(request.Imp)) headers := http.Header{} var potentialRequests []*adapters.RequestData @@ -39,8 +37,6 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt for i := 0; i < len(request.Imp); i++ { imp := request.Imp[i] - fmt.Printf("processing imp") - var extBtlrParams openrtb_ext.ExtImpSharethroughExt if err := json.Unmarshal(imp.Ext, &extBtlrParams); err != nil { return nil, []error{err} @@ -109,7 +105,7 @@ func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, exter return nil, []error{err} } - br, bidderResponseErr := butlerToOpenRTBResponse(externalRequest, strBidResp) + br, errs := butlerToOpenRTBResponse(externalRequest, strBidResp) - return br, bidderResponseErr + return br, errs } diff --git a/adapters/sharethrough/utils.go b/adapters/sharethrough/utils.go index 0a5e34f7f92..a5131ee4685 100644 --- a/adapters/sharethrough/utils.go +++ b/adapters/sharethrough/utils.go @@ -13,12 +13,12 @@ import ( ) func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriParams) string { + var errs []error + strRespId := fmt.Sprintf("str_response_%s", strResp.BidID) jsonPayload, err := json.Marshal(strResp) - if err != nil { - //handle error - fmt.Printf("ERROR: %s\n", err) + return "" } tmplBody := ` @@ -55,10 +55,7 @@ func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriPa } tmpl, err := template.New("sfpjs").Parse(tmplBody) - if err != nil { - // handle error - fmt.Printf("ERROR TEMPLATE: %s\n", err) - } + errs = append(errs, err) var buf []byte templatedBuf := bytes.NewBuffer(buf) @@ -76,11 +73,7 @@ func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriPa b64EncodedJson, }) - if err != nil { - // handle error - fmt.Printf("ERROR TEMPLATE Execute: %s\n", err) - - } + errs = append(errs, err) return templatedBuf.String() } diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go index 51544b63bf1..a6a4f0feb45 100644 --- a/usersync/usersyncers/syncer.go +++ b/usersync/usersyncers/syncer.go @@ -2,6 +2,7 @@ package usersyncers import ( "github.com/prebid/prebid-server/adapters/gamoshi" + "github.com/prebid/prebid-server/adapters/sharethrough" "strings" "text/template" @@ -61,6 +62,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync insertIntoMap(cfg, syncers, openrtb_ext.BidderPulsepoint, pulsepoint.NewPulsepointSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderRhythmone, rhythmone.NewRhythmoneSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderRubicon, rubicon.NewRubiconSyncer) + insertIntoMap(cfg, syncers, openrtb_ext.BidderSharethrough, sharethrough.NewSharethroughSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderSomoaudience, somoaudience.NewSomoaudienceSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderSovrn, sovrn.NewSovrnSyncer) insertIntoMap(cfg, syncers, openrtb_ext.BidderSonobi, sonobi.NewSonobiSyncer) From 93756eff1812d566673178e588f6ddbd7b0f40ac Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Fri, 10 May 2019 15:31:33 -0700 Subject: [PATCH 16/26] Proper error handling (#6) * Proper error handling [#165745574] * Address review (baby clean up) + catch error that was missed [#165745574] --- adapters/sharethrough/butler.go | 36 +++++++-- adapters/sharethrough/sharethrough.go | 25 ++---- adapters/sharethrough/utils.go | 107 +++++++++++++++++++++----- 3 files changed, 122 insertions(+), 46 deletions(-) diff --git a/adapters/sharethrough/butler.go b/adapters/sharethrough/butler.go index a985be280c4..5c6f0e9ad10 100644 --- a/adapters/sharethrough/butler.go +++ b/adapters/sharethrough/butler.go @@ -5,6 +5,7 @@ import ( "github.com/buger/jsonparser" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" "net/url" "strconv" @@ -29,9 +30,15 @@ func butlerToOpenRTBResponse(btlrReq *adapters.RequestData, strResp openrtb_ext. typedBid := &adapters.TypedBid{BidType: openrtb_ext.BidTypeNative} creative := strResp.Creatives[0] - btlrParams, err := parseHBUri(btlrReq.Uri) - if err != nil { - errs = append(errs, err) + btlrParams, parseHBUriErr := parseHBUri(btlrReq.Uri) + if parseHBUriErr != nil { + errs = append(errs, &errortypes.BadInput{Message: parseHBUriErr.Error()}) + return nil, errs + } + + adm, admErr := getAdMarkup(strResp, btlrParams) + if admErr != nil { + errs = append(errs, &errortypes.BadServerResponse{Message: admErr.Error()}) } bid := &openrtb.Bid{ @@ -42,7 +49,7 @@ func butlerToOpenRTBResponse(btlrReq *adapters.RequestData, strResp openrtb_ext. CID: creative.Metadata.CampaignKey, CrID: creative.Metadata.CreativeKey, DealID: creative.Metadata.DealID, - AdM: getAdMarkup(strResp, btlrParams), + AdM: adm, H: btlrParams.Height, W: btlrParams.Width, } @@ -65,13 +72,19 @@ func generateHBUri(baseUrl string, params hbUriParams, app *openrtb.App) string v.Set("height", strconv.FormatUint(params.Height, 10)) v.Set("width", strconv.FormatUint(params.Width, 10)) - version := "unknown" + var version string + if app != nil { + // Skipping error handling here because it should fall through to unknown in the flow version, _ = jsonparser.GetString(app.Ext, "prebid", "version") } + if len(version) == 0 { + version = "unknown" + } + v.Set("hbVersion", version) - v.Set("hbSource", hbSource) + v.Set("supplyId", supplyId) v.Set("strVersion", strVersion) return baseUrl + "?" + v.Encode() @@ -84,8 +97,15 @@ func parseHBUri(uri string) (*hbUriParams, error) { } params := btlrUrl.Query() - height, _ := strconv.ParseUint(params.Get("height"), 10, 64) - width, _ := strconv.ParseUint(params.Get("width"), 10, 64) + height, err := strconv.ParseUint(params.Get("height"), 10, 64) + if err != nil { + return nil, err + } + + width, err := strconv.ParseUint(params.Get("width"), 10, 64) + if err != nil { + return nil, err + } return &hbUriParams{ Pkey: params.Get("placement_key"), diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index 79d2d7a21c1..58384711120 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -3,7 +3,6 @@ package sharethrough import ( "encoding/json" "fmt" - "github.com/buger/jsonparser" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/errortypes" @@ -11,7 +10,7 @@ import ( "net/http" ) -const hbSource = "prebid-server" +const supplyId = "FGMrCMMc" const strVersion = "1.0.0" func NewSharethroughBidder(endpoint string) *SharethroughAdapter { @@ -27,7 +26,6 @@ func (s SharethroughAdapter) Name() string { } func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapters.RequestData, []error) { - errs := make([]error, 0, len(request.Imp)) headers := http.Header{} var potentialRequests []*adapters.RequestData @@ -42,20 +40,6 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt return nil, []error{err} } - var gdprApplies int64 = 0 - if request.Regs != nil { - if jsonExtRegs, err := request.Regs.Ext.MarshalJSON(); err == nil { - gdprApplies, _ = jsonparser.GetInt(jsonExtRegs, "gdpr") - } - } - - consentString := "" - if request.User != nil { - if jsonExtUser, err := request.User.Ext.MarshalJSON(); err == nil { - consentString, _ = jsonparser.GetString(jsonExtUser, "consent") - } - } - pKey := extBtlrParams.Bidder.Pkey var height, width uint64 @@ -70,8 +54,8 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt Uri: generateHBUri(s.URI, hbUriParams{ Pkey: pKey, BidID: imp.ID, - ConsentRequired: !(gdprApplies == 0), - ConsentString: consentString, + ConsentRequired: gdprApplies(request), + ConsentString: gdprConsentString(request), Iframe: extBtlrParams.Bidder.Iframe, Height: height, Width: width, @@ -82,7 +66,8 @@ func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapt }) } - return potentialRequests, errs + // We never add to the errs slice (early return), so we just create an empty one to return + return potentialRequests, []error{} } func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { diff --git a/adapters/sharethrough/utils.go b/adapters/sharethrough/utils.go index a5131ee4685..825d35b6aa8 100644 --- a/adapters/sharethrough/utils.go +++ b/adapters/sharethrough/utils.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "github.com/buger/jsonparser" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/openrtb_ext" "html/template" @@ -12,13 +13,14 @@ import ( "strconv" ) -func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriParams) string { - var errs []error +const minChromeVersion = 53 +const minSafariVersion = 10 +func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriParams) (string, error) { strRespId := fmt.Sprintf("str_response_%s", strResp.BidID) jsonPayload, err := json.Marshal(strResp) if err != nil { - return "" + return "", err } tmplBody := ` @@ -50,12 +52,13 @@ func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriPa } })() - ` - + ` } tmpl, err := template.New("sfpjs").Parse(tmplBody) - errs = append(errs, err) + if err != nil { + return "", err + } var buf []byte templatedBuf := bytes.NewBuffer(buf) @@ -72,10 +75,11 @@ func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriPa template.JS(strRespId), b64EncodedJson, }) + if err != nil { + return "", err + } - errs = append(errs, err) - - return templatedBuf.String() + return templatedBuf.String(), nil } func getPlacementSize(formats []openrtb.Format) (height uint64, width uint64) { @@ -99,31 +103,98 @@ func getPlacementSize(formats []openrtb.Format) (height uint64, width uint64) { } func canAutoPlayVideo(userAgent string) bool { - const minChromeVersion = 53 - const minSafariVersion = 10 + return (isAndroid(userAgent) && isAtMinChromeVersion(userAgent)) || + (isiOS(userAgent) && + (isAtMinSafariVersion(userAgent) || isAtMinChromeIosVersion(userAgent))) || + !(isAndroid(userAgent) || isiOS(userAgent)) +} + +func isAndroid(userAgent string) bool { + isAndroid, err := regexp.MatchString("(?i)Android", userAgent) + if err != nil { + return false + } + return isAndroid +} - isAndroid, _ := regexp.MatchString("(?i)Android", userAgent) - isiOS, _ := regexp.MatchString("(?i)iPhone|iPad|iPod", userAgent) +func isiOS(userAgent string) bool { + isiOS, err := regexp.MatchString("(?i)iPhone|iPad|iPod", userAgent) + if err != nil { + return false + } + return isiOS +} - var chromeVersion, chromeiOSVersion, safariVersion int64 +func isAtMinChromeVersion(userAgent string) bool { + var chromeVersion int64 + var err error chromeVersionRegex := regexp.MustCompile(`Chrome\/(?P\d+)`) chromeVersionMatch := chromeVersionRegex.FindStringSubmatch(userAgent) if len(chromeVersionMatch) > 1 { - chromeVersion, _ = strconv.ParseInt(chromeVersionMatch[1], 10, 64) + chromeVersion, err = strconv.ParseInt(chromeVersionMatch[1], 10, 64) + } + if err != nil { + return false } + return chromeVersion >= minChromeVersion +} + +func isAtMinChromeIosVersion(userAgent string) bool { + var chromeiOSVersion int64 + var err error + chromeiOSVersionRegex := regexp.MustCompile(`CriOS\/(?P\d+)`) chromeiOSVersionMatch := chromeiOSVersionRegex.FindStringSubmatch(userAgent) if len(chromeiOSVersionMatch) > 1 { - chromeiOSVersion, _ = strconv.ParseInt(chromeiOSVersionMatch[1], 10, 64) + chromeiOSVersion, err = strconv.ParseInt(chromeiOSVersionMatch[1], 10, 64) + } + if err != nil { + return false } + return chromeiOSVersion >= minChromeVersion +} + +func isAtMinSafariVersion(userAgent string) bool { + var safariVersion int64 + var err error + safariVersionRegex := regexp.MustCompile(`Version\/(?P\d+)`) safariVersionMatch := safariVersionRegex.FindStringSubmatch(userAgent) if len(safariVersionMatch) > 1 { - safariVersion, _ = strconv.ParseInt(safariVersionMatch[1], 10, 64) + safariVersion, err = strconv.ParseInt(safariVersionMatch[1], 10, 64) + } + if err != nil { + return false + } + + return safariVersion >= minSafariVersion +} + +func gdprApplies(request *openrtb.BidRequest) bool { + var gdprApplies int64 + + if request.Regs != nil { + if jsonExtRegs, err := request.Regs.Ext.MarshalJSON(); err == nil { + // 0 is the return value if error, so no need to handle + gdprApplies, _ = jsonparser.GetInt(jsonExtRegs, "gdpr") + } + } + + return gdprApplies != 0 +} + +func gdprConsentString(request *openrtb.BidRequest) string { + var consentString string + + if request.User != nil { + if jsonExtUser, err := request.User.Ext.MarshalJSON(); err == nil { + // empty string is the return value if error, so no need to handle + consentString, _ = jsonparser.GetString(jsonExtUser, "consent") + } } - return (isAndroid && chromeVersion >= minChromeVersion) || (isiOS && (safariVersion >= minSafariVersion || chromeiOSVersion >= minChromeVersion)) || !(isAndroid || isiOS) + return consentString } From 33e75e4deca33130d020150d09e0dd7207cb81c0 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Tue, 14 May 2019 16:27:44 -0700 Subject: [PATCH 17/26] Implement Unit Tests (#7) * Proper error handling [#165745574] * Address review (baby clean up) + catch error that was missed [#165745574] * Implement unit tests for utils [#165891351] * Add UT for utils + butler [#165891351] Co-authored-by: Michael Duran * Attempt for testing Bidder interface function implementations [#165891351] Co-authored-by: Michael Duran * Finalizing Unit tests [#165891351] Co-authored-by: Chris Nguyen Co-authored-by: Josh Becker * Fixing sharethrough.yaml capabilities [#165891351] Co-authored-by: Josh Becker --- adapters/sharethrough/butler.go | 78 +++- adapters/sharethrough/butler_test.go | 403 +++++++++++++++++++++ adapters/sharethrough/sharethrough.go | 56 +-- adapters/sharethrough/sharethrough_test.go | 245 +++++++++++++ adapters/sharethrough/usersync_test.go | 19 + adapters/sharethrough/utils.go | 45 ++- adapters/sharethrough/utils_test.go | 372 +++++++++++++++++++ static/bidder-info/sharethrough.yaml | 6 +- 8 files changed, 1157 insertions(+), 67 deletions(-) create mode 100644 adapters/sharethrough/butler_test.go create mode 100644 adapters/sharethrough/sharethrough_test.go create mode 100644 adapters/sharethrough/usersync_test.go create mode 100644 adapters/sharethrough/utils_test.go diff --git a/adapters/sharethrough/butler.go b/adapters/sharethrough/butler.go index 5c6f0e9ad10..8e4294854c6 100644 --- a/adapters/sharethrough/butler.go +++ b/adapters/sharethrough/butler.go @@ -1,17 +1,19 @@ package sharethrough import ( + "encoding/json" "fmt" "github.com/buger/jsonparser" "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" "net/url" "strconv" ) -type hbUriParams struct { +type StrAdSeverParams struct { Pkey string BidID string ConsentRequired bool @@ -22,21 +24,81 @@ type hbUriParams struct { Width uint64 } -func butlerToOpenRTBResponse(btlrReq *adapters.RequestData, strResp openrtb_ext.ExtImpSharethroughResponse) (*adapters.BidderResponse, []error) { +type StrOpenRTBInterface interface { + requestFromOpenRTB(openrtb.Imp, *openrtb.BidRequest) (*adapters.RequestData, error) + responseToOpenRTB(openrtb_ext.ExtImpSharethroughResponse, *adapters.RequestData) (*adapters.BidderResponse, []error) +} + +type StrAdServerUriInterface interface { + buildUri(StrAdSeverParams, *openrtb.App) string + parseUri(string) (*StrAdSeverParams, error) +} + +type StrUriHelper struct { + BaseURI string +} + +type StrOpenRTBTranslator struct { + UriHelper StrAdServerUriInterface + Util UtilityInterface +} + +func (s StrOpenRTBTranslator) requestFromOpenRTB(imp openrtb.Imp, request *openrtb.BidRequest) (*adapters.RequestData, error) { + headers := http.Header{} + headers.Add("Content-Type", "text/plain;charset=utf-8") + headers.Add("Accept", "application/json") + + var extBtlrParams openrtb_ext.ExtImpSharethroughExt + if err := json.Unmarshal(imp.Ext, &extBtlrParams); err != nil { + return nil, err + } + + pKey := extBtlrParams.Bidder.Pkey + + var height, width uint64 + if len(extBtlrParams.Bidder.IframeSize) >= 2 { + height, width = uint64(extBtlrParams.Bidder.IframeSize[0]), uint64(extBtlrParams.Bidder.IframeSize[1]) + } else { + height, width = s.Util.getPlacementSize(imp.Banner.Format) + } + + return &adapters.RequestData{ + Method: "POST", + Uri: s.UriHelper.buildUri(StrAdSeverParams{ + Pkey: pKey, + BidID: imp.ID, + ConsentRequired: s.Util.gdprApplies(request), + ConsentString: s.Util.gdprConsentString(request), + Iframe: extBtlrParams.Bidder.Iframe, + Height: height, + Width: width, + InstantPlayCapable: s.Util.canAutoPlayVideo(request.Device.UA), + }, request.App), + Body: nil, + Headers: headers, + }, nil +} + +func (s StrOpenRTBTranslator) responseToOpenRTB(strResp openrtb_ext.ExtImpSharethroughResponse, btlrReq *adapters.RequestData) (*adapters.BidderResponse, []error) { var errs []error bidResponse := adapters.NewBidderResponse() bidResponse.Currency = "USD" typedBid := &adapters.TypedBid{BidType: openrtb_ext.BidTypeNative} + + if len(strResp.Creatives) == 0 { + errs = append(errs, &errortypes.BadInput{Message: "No creative provided"}) + return nil, errs + } creative := strResp.Creatives[0] - btlrParams, parseHBUriErr := parseHBUri(btlrReq.Uri) + btlrParams, parseHBUriErr := s.UriHelper.parseUri(btlrReq.Uri) if parseHBUriErr != nil { errs = append(errs, &errortypes.BadInput{Message: parseHBUriErr.Error()}) return nil, errs } - adm, admErr := getAdMarkup(strResp, btlrParams) + adm, admErr := s.Util.getAdMarkup(strResp, btlrParams) if admErr != nil { errs = append(errs, &errortypes.BadServerResponse{Message: admErr.Error()}) } @@ -60,7 +122,7 @@ func butlerToOpenRTBResponse(btlrReq *adapters.RequestData, strResp openrtb_ext. return bidResponse, errs } -func generateHBUri(baseUrl string, params hbUriParams, app *openrtb.App) string { +func (h StrUriHelper) buildUri(params StrAdSeverParams, app *openrtb.App) string { v := url.Values{} v.Set("placement_key", params.Pkey) v.Set("bidId", params.BidID) @@ -87,10 +149,10 @@ func generateHBUri(baseUrl string, params hbUriParams, app *openrtb.App) string v.Set("supplyId", supplyId) v.Set("strVersion", strVersion) - return baseUrl + "?" + v.Encode() + return h.BaseURI + "?" + v.Encode() } -func parseHBUri(uri string) (*hbUriParams, error) { +func (h StrUriHelper) parseUri(uri string) (*StrAdSeverParams, error) { btlrUrl, err := url.Parse(uri) if err != nil { return nil, err @@ -107,7 +169,7 @@ func parseHBUri(uri string) (*hbUriParams, error) { return nil, err } - return &hbUriParams{ + return &StrAdSeverParams{ Pkey: params.Get("placement_key"), BidID: params.Get("bidId"), Iframe: params.Get("stayInIframe") == "true", diff --git a/adapters/sharethrough/butler_test.go b/adapters/sharethrough/butler_test.go new file mode 100644 index 00000000000..8d218efcb6f --- /dev/null +++ b/adapters/sharethrough/butler_test.go @@ -0,0 +1,403 @@ +package sharethrough + +import ( + "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" + "strings" + "testing" +) + +type MockUtil struct { + mockCanAutoPlayVideo func() bool + mockGdprApplies func() bool + mockGdprConsentString func() string + mockGenerateHBUri func() string + mockGetPlacementSize func() (uint64, uint64) + UtilityInterface +} + +func (m MockUtil) canAutoPlayVideo(userAgent string) bool { + return m.mockCanAutoPlayVideo() +} + +func (m MockUtil) gdprApplies(request *openrtb.BidRequest) bool { + return m.mockGdprApplies() +} + +func (m MockUtil) gdprConsentString(bidRequest *openrtb.BidRequest) string { + return m.mockGdprConsentString() +} + +func (m MockUtil) generateHBUri(baseUrl string, params StrAdSeverParams, app *openrtb.App) string { + return m.mockGenerateHBUri() +} + +func (m MockUtil) getPlacementSize(formats []openrtb.Format) (height uint64, width uint64) { + return m.mockGetPlacementSize() +} + +func assertRequestDataEquals(t *testing.T, testName string, expected *adapters.RequestData, actual *adapters.RequestData) { + t.Logf("Test case: %s\n", testName) + if expected.Method != actual.Method { + t.Errorf("Method mismatch: expected %s got %s\n", expected.Method, actual.Method) + } + if expected.Uri != actual.Uri { + t.Errorf("Uri mismatch: expected %s got %s\n", expected.Uri, actual.Uri) + } + if len(expected.Body) != len(actual.Body) { + t.Errorf("Body mismatch: expected %s got %s\n", expected.Body, actual.Body) + } + for headerIndex, expectedHeader := range expected.Headers { + if expectedHeader[0] != actual.Headers[headerIndex][0] { + t.Errorf("Header %s mismatch: expected %s got %s\n", headerIndex, expectedHeader[0], actual.Headers[headerIndex][0]) + } + } +} + +func TestSuccessRequestFromOpenRTB(t *testing.T) { + tests := map[string]struct { + inputImp openrtb.Imp + inputReq *openrtb.BidRequest + expected *adapters.RequestData + }{ + "Generates the correct AdServer request from Imp": { + inputImp: openrtb.Imp{ + ID: "abc", + Ext: []byte(`{"pkey": "pkey", "iframe": true, "iframeSize": [10, 20]}`), + Banner: &openrtb.Banner{ + Format: []openrtb.Format{{H: 30, W: 40}}, + }, + }, + inputReq: &openrtb.BidRequest{ + App: &openrtb.App{Ext: []byte(`{}`)}, + Device: &openrtb.Device{ + UA: "Android Chome/60", + }, + }, + expected: &adapters.RequestData{ + Method: "POST", + Uri: "http://abc.com", + Body: nil, + Headers: http.Header{ + "Content-Type": []string{"text/plain;charset=utf-8"}, + "Accept": []string{"application/json"}, + }, + }, + }, + } + + mockUriHelper := MockStrUriHelper{ + mockBuildUri: func() string { + return "http://abc.com" + }, + } + + adServer := StrOpenRTBTranslator{UriHelper: mockUriHelper, Util: Util{}} + for testName, test := range tests { + outputSuccess, outputError := adServer.requestFromOpenRTB(test.inputImp, test.inputReq) + assertRequestDataEquals(t, testName, test.expected, outputSuccess) + if outputError != nil { + t.Errorf("Expected no errors, got %s\n", outputError) + } + } +} + +func assertBidderResponseEquals(t *testing.T, testName string, expected adapters.BidderResponse, actual adapters.BidderResponse) { + t.Logf("Test case: %s\n", testName) + if len(expected.Bids) != len(actual.Bids) { + t.Errorf("Expected %d bids in BidResponse, got %d\n", len(expected.Bids), len(actual.Bids)) + return + } + for index, expectedTypedBid := range expected.Bids { + if expectedTypedBid.BidType != actual.Bids[index].BidType { + t.Errorf("Bid[%d]: Type mismatch, expected %s got %s\n", index, expectedTypedBid.BidType, actual.Bids[index].BidType) + } + if expectedTypedBid.Bid.AdID != actual.Bids[index].Bid.AdID { + t.Errorf("Bid[%d]: AdID mismatch, expected %s got %s\n", index, expectedTypedBid.Bid.AdID, actual.Bids[index].Bid.AdID) + } + if expectedTypedBid.Bid.ID != actual.Bids[index].Bid.ID { + t.Errorf("Bid[%d]: ID mismatch, expected %s got %s\n", index, expectedTypedBid.Bid.ID, actual.Bids[index].Bid.ID) + } + if expectedTypedBid.Bid.ImpID != actual.Bids[index].Bid.ImpID { + t.Errorf("Bid[%d]: ImpID mismatch, expected %s got %s\n", index, expectedTypedBid.Bid.ImpID, actual.Bids[index].Bid.ImpID) + } + if expectedTypedBid.Bid.Price != actual.Bids[index].Bid.Price { + t.Errorf("Bid[%d]: Price mismatch, expected %f got %f\n", index, expectedTypedBid.Bid.Price, actual.Bids[index].Bid.Price) + } + if expectedTypedBid.Bid.CID != actual.Bids[index].Bid.CID { + t.Errorf("Bid[%d]: CID mismatch, expected %s got %s\n", index, expectedTypedBid.Bid.CID, actual.Bids[index].Bid.CID) + } + if expectedTypedBid.Bid.CrID != actual.Bids[index].Bid.CrID { + t.Errorf("Bid[%d]: CrID mismatch, expected %s got %s\n", index, expectedTypedBid.Bid.CrID, actual.Bids[index].Bid.CrID) + } + if expectedTypedBid.Bid.DealID != actual.Bids[index].Bid.DealID { + t.Errorf("Bid[%d]: DealID mismatch, expected %s got %s\n", index, expectedTypedBid.Bid.DealID, actual.Bids[index].Bid.DealID) + } + if expectedTypedBid.Bid.H != actual.Bids[index].Bid.H { + t.Errorf("Bid[%d]: H mismatch, expected %d got %d\n", index, expectedTypedBid.Bid.H, actual.Bids[index].Bid.H) + } + if expectedTypedBid.Bid.W != actual.Bids[index].Bid.W { + t.Errorf("Bid[%d]: W mismatch, expected %d got %d\n", index, expectedTypedBid.Bid.W, actual.Bids[index].Bid.W) + } + } +} + +func TestSuccessResponseToOpenRTB(t *testing.T) { + tests := map[string]struct { + inputButlerReq *adapters.RequestData + inputStrResp openrtb_ext.ExtImpSharethroughResponse + expectedSuccess *adapters.BidderResponse + expectedErrors []error + }{ + "Generates expected openRTB bid response": { + inputButlerReq: &adapters.RequestData{ + Uri: "http://uri.com?placement_key=pkey&bidId=bidid&height=20&width=30", + }, + inputStrResp: openrtb_ext.ExtImpSharethroughResponse{ + AdServerRequestID: "arid", + BidID: "bid", + Creatives: []openrtb_ext.ExtImpSharethroughCreative{{ + CPM: 10, + Metadata: openrtb_ext.ExtImpSharethroughCreativeMetadata{ + CampaignKey: "cmpKey", + CreativeKey: "creaKey", + DealID: "dealId", + }, + }}, + }, + expectedSuccess: &adapters.BidderResponse{ + Bids: []*adapters.TypedBid{{ + BidType: openrtb_ext.BidTypeNative, + Bid: &openrtb.Bid{ + AdID: "arid", + ID: "bid", + ImpID: "bidid", + Price: 10, + CID: "cmpKey", + CrID: "creaKey", + DealID: "dealId", + H: 20, + W: 30, + }, + }}, + }, + expectedErrors: []error{}, + }, + } + + adServer := StrOpenRTBTranslator{Util: Util{}, UriHelper: StrUriHelper{}} + for testName, test := range tests { + outputSuccess, outputErrors := adServer.responseToOpenRTB(test.inputStrResp, test.inputButlerReq) + assertBidderResponseEquals(t, testName, *test.expectedSuccess, *outputSuccess) + if len(outputErrors) != len(test.expectedErrors) { + t.Errorf("Expected %d errors, got %d\n", len(test.expectedErrors), len(outputErrors)) + } + } +} + +func TestFailResponseToOpenRTB(t *testing.T) { + tests := map[string]struct { + inputButlerReq *adapters.RequestData + inputStrResp openrtb_ext.ExtImpSharethroughResponse + expectedSuccess *adapters.BidderResponse + expectedErrors []error + }{ + "Returns nil if no creatives provided": { + inputButlerReq: &adapters.RequestData{}, + inputStrResp: openrtb_ext.ExtImpSharethroughResponse{ + Creatives: []openrtb_ext.ExtImpSharethroughCreative{}, + }, + expectedSuccess: nil, + expectedErrors: []error{ + &errortypes.BadInput{Message: "No creative provided"}, + }, + }, + "Returns nil if failed to parse Uri": { + inputButlerReq: &adapters.RequestData{ + Uri: "wrong format url", + }, + inputStrResp: openrtb_ext.ExtImpSharethroughResponse{ + Creatives: []openrtb_ext.ExtImpSharethroughCreative{{}}, + }, + expectedSuccess: nil, + expectedErrors: []error{ + &errortypes.BadInput{Message: `strconv.ParseUint: parsing "": invalid syntax`}, + }, + }, + } + + adServer := StrOpenRTBTranslator{UriHelper: StrUriHelper{}} + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + outputSuccess, outputErrors := adServer.responseToOpenRTB(test.inputStrResp, test.inputButlerReq) + + if test.expectedSuccess != outputSuccess { + t.Errorf("Expected result %+v, got %+v\n", test.expectedSuccess, outputSuccess) + } + + if len(outputErrors) != len(test.expectedErrors) { + t.Errorf("Expected %d errors, got %d\n", len(test.expectedErrors), len(outputErrors)) + } + + for index, expectedError := range test.expectedErrors { + if fmt.Sprintf("%T", expectedError) != fmt.Sprintf("%T", outputErrors[index]) { + t.Errorf("Error type mismatch, expected %T, got %T\n", expectedError, outputErrors[index]) + } + if expectedError.Error() != outputErrors[index].Error() { + t.Errorf("Expected error %s, got %s\n", expectedError.Error(), outputErrors[index].Error()) + } + } + } +} + +func TestBuildUri(t *testing.T) { + tests := map[string]struct { + inputParams StrAdSeverParams + inputApp *openrtb.App + expected []string + }{ + "Generates expected URL, appending all params": { + inputParams: StrAdSeverParams{ + Pkey: "pkey", + BidID: "bid", + ConsentRequired: true, + ConsentString: "consent", + InstantPlayCapable: true, + Iframe: false, + Height: 20, + Width: 30, + }, + inputApp: &openrtb.App{Ext: []byte(`{"prebid": {"version": "1"}}`)}, + expected: []string{ + "http://abc.com?", + "placement_key=pkey", + "bidId=bid", + "consent_required=true", + "consent_string=consent", + "instant_play_capable=true", + "stayInIframe=false", + "height=20", + "width=30", + "hbVersion=1", + "supplyId=FGMrCMMc", + "strVersion=1.0.0", + }, + }, + "Sets version to unknown if version not found": { + inputParams: StrAdSeverParams{}, + inputApp: &openrtb.App{Ext: []byte(`{}`)}, + expected: []string{ + "hbVersion=unknown", + }, + }, + } + + uriHelper := StrUriHelper{BaseURI: "http://abc.com"} + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + output := uriHelper.buildUri(test.inputParams, test.inputApp) + + for _, uriParam := range test.expected { + if !strings.Contains(output, uriParam) { + t.Errorf("Expected %s to be found in URL, got %s\n", uriParam, output) + } + } + } +} + +func assertStrAdServerParamsEquals(t *testing.T, testName string, expected *StrAdSeverParams, actual *StrAdSeverParams) { + t.Logf("Test case: %s\n", testName) + if expected.Pkey != actual.Pkey { + t.Errorf("Expected Pkey to be %s, got %s\n", expected.Pkey, actual.Pkey) + } + if expected.BidID != actual.BidID { + t.Errorf("Expected BidID to be %s, got %s\n", expected.BidID, actual.BidID) + } + if expected.Iframe != actual.Iframe { + t.Errorf("Expected Iframe to be %t, got %t\n", expected.Iframe, actual.Iframe) + } + if expected.Height != actual.Height { + t.Errorf("Expected Height to be %d, got %d\n", expected.Height, actual.Height) + } + if expected.Width != actual.Width { + t.Errorf("Expected Width to be %d, got %d\n", expected.Width, actual.Width) + } + if expected.ConsentRequired != actual.ConsentRequired { + t.Errorf("Expected ConsentRequired to be %t, got %t\n", expected.ConsentRequired, actual.ConsentRequired) + } + if expected.ConsentString != actual.ConsentString { + t.Errorf("Expected ConsentString to be %s, got %s\n", expected.ConsentString, actual.ConsentString) + } +} + +func TestSuccessParseUri(t *testing.T) { + tests := map[string]struct { + input string + expectedSuccess *StrAdSeverParams + }{ + "Decodes URI successfully": { + input: "http://abc.com?placement_key=pkey&bidId=bid&consent_required=true&consent_string=consent&instant_play_capable=true&stayInIframe=false&height=20&width=30&hbVersion=1&supplyId=FGMrCMMc&strVersion=1.0.0", + expectedSuccess: &StrAdSeverParams{ + Pkey: "pkey", + BidID: "bid", + Iframe: false, + Height: 20, + Width: 30, + ConsentRequired: true, + ConsentString: "consent", + }, + }, + } + + uriHelper := StrUriHelper{} + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + output, actualError := uriHelper.parseUri(test.input) + + assertStrAdServerParamsEquals(t, testName, test.expectedSuccess, output) + if actualError != nil { + t.Errorf("Expected no errors, got %s\n", actualError) + } + } +} + +func TestFailParseUri(t *testing.T) { + tests := map[string]struct { + input string + expectedError string + }{ + "Fails decoding if unable to parse URI": { + input: "wrong URI", + expectedError: `strconv.ParseUint: parsing "": invalid syntax`, + }, + "Fails decoding if height not provided": { + input: "http://abc.com?width=10", + expectedError: `strconv.ParseUint: parsing "": invalid syntax`, + }, + "Fails decoding if width not provided": { + input: "http://abc.com?height=10", + expectedError: `strconv.ParseUint: parsing "": invalid syntax`, + }, + } + + uriHelper := StrUriHelper{} + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + output, actualError := uriHelper.parseUri(test.input) + + if output != nil { + t.Errorf("Expected return value nil, got %+v\n", output) + } + if actualError == nil { + t.Errorf("Expected error not to be nil\n") + break + } + if actualError.Error() != test.expectedError { + t.Errorf("Expected error '%s', got '%s'\n", test.expectedError, actualError.Error()) + } + } +} diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index 58384711120..b30700ccf37 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -14,63 +14,39 @@ const supplyId = "FGMrCMMc" const strVersion = "1.0.0" func NewSharethroughBidder(endpoint string) *SharethroughAdapter { - return &SharethroughAdapter{URI: endpoint} + return &SharethroughAdapter{ + AdServer: StrOpenRTBTranslator{ + UriHelper: StrUriHelper{BaseURI: endpoint}, + Util: Util{}, + }, + } } type SharethroughAdapter struct { - URI string + AdServer StrOpenRTBInterface } -func (s SharethroughAdapter) Name() string { +func (a SharethroughAdapter) Name() string { return "sharethrough" } -func (s SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapters.RequestData, []error) { - headers := http.Header{} - var potentialRequests []*adapters.RequestData - - headers.Add("Content-Type", "text/plain;charset=utf-8") - headers.Add("Accept", "application/json") +func (a SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapters.RequestData, []error) { + var reqs []*adapters.RequestData for i := 0; i < len(request.Imp); i++ { - imp := request.Imp[i] + req, err := a.AdServer.requestFromOpenRTB(request.Imp[i], request) - var extBtlrParams openrtb_ext.ExtImpSharethroughExt - if err := json.Unmarshal(imp.Ext, &extBtlrParams); err != nil { + if err != nil { return nil, []error{err} } - - pKey := extBtlrParams.Bidder.Pkey - - var height, width uint64 - if len(extBtlrParams.Bidder.IframeSize) >= 2 { - height, width = uint64(extBtlrParams.Bidder.IframeSize[0]), uint64(extBtlrParams.Bidder.IframeSize[1]) - } else { - height, width = getPlacementSize(imp.Banner.Format) - } - - potentialRequests = append(potentialRequests, &adapters.RequestData{ - Method: "POST", - Uri: generateHBUri(s.URI, hbUriParams{ - Pkey: pKey, - BidID: imp.ID, - ConsentRequired: gdprApplies(request), - ConsentString: gdprConsentString(request), - Iframe: extBtlrParams.Bidder.Iframe, - Height: height, - Width: width, - InstantPlayCapable: canAutoPlayVideo(request.Device.UA), - }, request.App), - Body: nil, - Headers: headers, - }) + reqs = append(reqs, req) } // We never add to the errs slice (early return), so we just create an empty one to return - return potentialRequests, []error{} + return reqs, []error{} } -func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { +func (a SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { if response.StatusCode == http.StatusNoContent { return nil, nil } @@ -90,7 +66,7 @@ func (s SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, exter return nil, []error{err} } - br, errs := butlerToOpenRTBResponse(externalRequest, strBidResp) + br, errs := a.AdServer.responseToOpenRTB(strBidResp, externalRequest) return br, errs } diff --git a/adapters/sharethrough/sharethrough_test.go b/adapters/sharethrough/sharethrough_test.go new file mode 100644 index 00000000000..f1697279c68 --- /dev/null +++ b/adapters/sharethrough/sharethrough_test.go @@ -0,0 +1,245 @@ +package sharethrough + +import ( + "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" + "testing" +) + +type MockStrAdServer struct { + mockRequestFromOpenRTB func() (*adapters.RequestData, error) + mockResponseToOpenRTB func() (*adapters.BidderResponse, []error) + + StrOpenRTBInterface +} + +func (m MockStrAdServer) requestFromOpenRTB(imp openrtb.Imp, request *openrtb.BidRequest) (*adapters.RequestData, error) { + return m.mockRequestFromOpenRTB() +} + +func (m MockStrAdServer) responseToOpenRTB(strResp openrtb_ext.ExtImpSharethroughResponse, btlrReq *adapters.RequestData) (*adapters.BidderResponse, []error) { + return m.mockResponseToOpenRTB() +} + +type MockStrUriHelper struct { + mockBuildUri func() string + mockParseUri func() (*StrAdSeverParams, error) + + StrAdServerUriInterface +} + +func (m MockStrUriHelper) buildUri(params StrAdSeverParams, app *openrtb.App) string { + return m.mockBuildUri() +} + +func (m MockStrUriHelper) parseUri(uri string) (*StrAdSeverParams, error) { + return m.mockParseUri() +} + +func TestSuccessMakeRequests(t *testing.T) { + stubReq := &adapters.RequestData{ + Method: "POST", + Uri: "http://test.com", + Body: nil, + Headers: http.Header{ + "Content-Type": []string{"text/plain;charset=utf-8"}, + "Accept": []string{"application/json"}, + }, + } + + tests := map[string]struct { + input *openrtb.BidRequest + expected []*adapters.RequestData + }{ + "Generates expected Request": { + input: &openrtb.BidRequest{ + App: &openrtb.App{Ext: []byte(`{}`)}, + Device: &openrtb.Device{ + UA: "Android Chome/60", + }, + Imp: []openrtb.Imp{{ + ID: "abc", + Ext: []byte(`{"pkey": "pkey", "iframe": true, "iframeSize": [10, 20]}`), + Banner: &openrtb.Banner{ + Format: []openrtb.Format{{H: 30, W: 40}}, + }, + }}, + }, + expected: []*adapters.RequestData{stubReq}, + }, + } + + mockAdServer := MockStrAdServer{ + mockRequestFromOpenRTB: func() (*adapters.RequestData, error) { + return stubReq, nil + }, + } + + adapter := SharethroughAdapter{AdServer: mockAdServer} + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + output, actualErrors := adapter.MakeRequests(test.input) + + if len(output) != 1 { + t.Errorf("Expected one request in result, got %d\n", len(output)) + return + } + + assertRequestDataEquals(t, testName, test.expected[0], output[0]) + if len(actualErrors) != 0 { + t.Errorf("Expected no errors, got %d\n", len(actualErrors)) + } + } +} + +func TestFailureMakeRequests(t *testing.T) { + tests := map[string]struct { + input *openrtb.BidRequest + expected string + }{ + "Returns nil if failed to generate request": { + input: &openrtb.BidRequest{ + App: &openrtb.App{Ext: []byte(`{}`)}, + Device: &openrtb.Device{ + UA: "Android Chome/60", + }, + Imp: []openrtb.Imp{{ + ID: "abc", + Ext: []byte(`{"pkey": "pkey", "iframe": true, "iframeSize": [10, 20]}`), + Banner: &openrtb.Banner{ + Format: []openrtb.Format{{H: 30, W: 40}}, + }, + }}, + }, + expected: "error generating request", + }, + } + + mockAdServer := MockStrAdServer{ + mockRequestFromOpenRTB: func() (*adapters.RequestData, error) { + return nil, fmt.Errorf("error generating request") + }, + } + + adapter := SharethroughAdapter{AdServer: mockAdServer} + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + output, actualErrors := adapter.MakeRequests(test.input) + + if output != nil { + t.Errorf("Expected result to be nil, got %d elements\n", len(output)) + } + if len(actualErrors) != 1 { + t.Errorf("Expected one error, got %d\n", len(actualErrors)) + } + if actualErrors[0].Error() != test.expected { + t.Errorf("Error mismatch: expected '%s' got '%s'\n", test.expected, actualErrors[0].Error()) + } + } +} + +func TestSuccessMakeBids(t *testing.T) { + stubBidderResponse := adapters.BidderResponse{} + + tests := map[string]struct { + inputResponse *adapters.ResponseData + expected *adapters.BidderResponse + }{ + "Returns nil,nil if ad server responded with no content": { + inputResponse: &adapters.ResponseData{ + StatusCode: http.StatusNoContent, + }, + expected: nil, + }, + "Generates response if ad server responded with 200": { + inputResponse: &adapters.ResponseData{ + StatusCode: http.StatusOK, + Body: []byte(`{}`), + }, + expected: &stubBidderResponse, + }, + } + + mockAdServer := MockStrAdServer{ + mockResponseToOpenRTB: func() (*adapters.BidderResponse, []error) { + return &stubBidderResponse, []error{} + }, + } + + adapter := SharethroughAdapter{AdServer: mockAdServer} + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + response, errors := adapter.MakeBids(&openrtb.BidRequest{}, &adapters.RequestData{}, test.inputResponse) + if len(errors) > 0 { + t.Errorf("Expected no errors, got %d\n", len(errors)) + } + if response != test.expected { + t.Errorf("Response mismatch: expected '%+v' got '%+v'\n", test.expected, response) + } + } +} + +func TestFailureMakeBids(t *testing.T) { + tests := map[string]struct { + inputResponse *adapters.ResponseData + expected []error + }{ + "Returns BadInput error if ad server responds with BadRequest": { + inputResponse: &adapters.ResponseData{ + StatusCode: http.StatusBadRequest, + }, + expected: []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", http.StatusBadRequest), + }}, + }, + "Returns default error if ad server does not respond with Status OK": { + inputResponse: &adapters.ResponseData{ + StatusCode: http.StatusInternalServerError, + }, + expected: []error{fmt.Errorf("unexpected status code: %d. Run with request.debug = 1 for more info", http.StatusInternalServerError)}, + }, + "Returns error if failed parsing body": { + inputResponse: &adapters.ResponseData{ + StatusCode: http.StatusOK, + Body: []byte(`{ wrong json`), + }, + expected: []error{fmt.Errorf("invalid character 'w' looking for beginning of object key string")}, + }, + "Passes by errors from responseToOpenRTB": { + inputResponse: &adapters.ResponseData{ + StatusCode: http.StatusOK, + Body: []byte(`{}`), + }, + expected: []error{fmt.Errorf("failed in responseToOpenRTB")}, + }, + } + + mockAdServer := MockStrAdServer{ + mockResponseToOpenRTB: func() (*adapters.BidderResponse, []error) { + return nil, []error{fmt.Errorf("failed in responseToOpenRTB")} + }, + } + + adapter := SharethroughAdapter{AdServer: mockAdServer} + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + response, errors := adapter.MakeBids(&openrtb.BidRequest{}, &adapters.RequestData{}, test.inputResponse) + if response != nil { + t.Errorf("Expected response to be nil, got %+v\n", response) + } + if len(errors) != 1 { + t.Errorf("Expected no errors, got %d\n", len(errors)) + } + if errors[0].Error() != test.expected[0].Error() { + t.Errorf("Error mismatch: expected '%s' got '%s'\n", test.expected[0].Error(), errors[0].Error()) + } + } +} diff --git a/adapters/sharethrough/usersync_test.go b/adapters/sharethrough/usersync_test.go new file mode 100644 index 00000000000..0cfc177f254 --- /dev/null +++ b/adapters/sharethrough/usersync_test.go @@ -0,0 +1,19 @@ +package sharethrough + +import ( + "github.com/stretchr/testify/assert" + "testing" + "text/template" +) + +func TestSharethroughSyncer(t *testing.T) { + temp := template.Must(template.New("sync-template").Parse("https://match.sharethrough.com?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}")) + syncer := NewSharethroughSyncer(temp) + syncInfo, err := syncer.GetUsersyncInfo("0", "") + assert.NoError(t, err) + assert.Equal(t, "https://match.sharethrough.com?gdpr=0&gdpr_consent=", syncInfo.URL) + assert.Equal(t, "redirect", syncInfo.Type) + assert.EqualValues(t, 80, syncer.GDPRVendorID()) + assert.Equal(t, false, syncInfo.SupportCORS) + assert.Equal(t, "sharethrough", syncer.FamilyName()) +} diff --git a/adapters/sharethrough/utils.go b/adapters/sharethrough/utils.go index 825d35b6aa8..ed22e992b67 100644 --- a/adapters/sharethrough/utils.go +++ b/adapters/sharethrough/utils.go @@ -16,7 +16,24 @@ import ( const minChromeVersion = 53 const minSafariVersion = 10 -func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriParams) (string, error) { +type UtilityInterface interface { + gdprApplies(*openrtb.BidRequest) bool + gdprConsentString(*openrtb.BidRequest) string + + getAdMarkup(openrtb_ext.ExtImpSharethroughResponse, *StrAdSeverParams) (string, error) + getPlacementSize([]openrtb.Format) (uint64, uint64) + + canAutoPlayVideo(string) bool + isAndroid(string) bool + isiOS(string) bool + isAtMinChromeIosVersion(string) bool + isAtMinChromeVersion(string) bool + isAtMinSafariVersion(string) bool +} + +type Util struct{} + +func (u Util) getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *StrAdSeverParams) (string, error) { strRespId := fmt.Sprintf("str_response_%s", strResp.BidID) jsonPayload, err := json.Marshal(strResp) if err != nil { @@ -82,7 +99,7 @@ func getAdMarkup(strResp openrtb_ext.ExtImpSharethroughResponse, params *hbUriPa return templatedBuf.String(), nil } -func getPlacementSize(formats []openrtb.Format) (height uint64, width uint64) { +func (u Util) getPlacementSize(formats []openrtb.Format) (height uint64, width uint64) { biggest := struct { Height uint64 Width uint64 @@ -102,14 +119,14 @@ func getPlacementSize(formats []openrtb.Format) (height uint64, width uint64) { return biggest.Height, biggest.Width } -func canAutoPlayVideo(userAgent string) bool { - return (isAndroid(userAgent) && isAtMinChromeVersion(userAgent)) || - (isiOS(userAgent) && - (isAtMinSafariVersion(userAgent) || isAtMinChromeIosVersion(userAgent))) || - !(isAndroid(userAgent) || isiOS(userAgent)) +func (u Util) canAutoPlayVideo(userAgent string) bool { + return (u.isAndroid(userAgent) && u.isAtMinChromeVersion(userAgent)) || + (u.isiOS(userAgent) && + (u.isAtMinSafariVersion(userAgent) || u.isAtMinChromeIosVersion(userAgent))) || + !(u.isAndroid(userAgent) || u.isiOS(userAgent)) } -func isAndroid(userAgent string) bool { +func (u Util) isAndroid(userAgent string) bool { isAndroid, err := regexp.MatchString("(?i)Android", userAgent) if err != nil { return false @@ -117,7 +134,7 @@ func isAndroid(userAgent string) bool { return isAndroid } -func isiOS(userAgent string) bool { +func (u Util) isiOS(userAgent string) bool { isiOS, err := regexp.MatchString("(?i)iPhone|iPad|iPod", userAgent) if err != nil { return false @@ -125,7 +142,7 @@ func isiOS(userAgent string) bool { return isiOS } -func isAtMinChromeVersion(userAgent string) bool { +func (u Util) isAtMinChromeVersion(userAgent string) bool { var chromeVersion int64 var err error @@ -141,7 +158,7 @@ func isAtMinChromeVersion(userAgent string) bool { return chromeVersion >= minChromeVersion } -func isAtMinChromeIosVersion(userAgent string) bool { +func (u Util) isAtMinChromeIosVersion(userAgent string) bool { var chromeiOSVersion int64 var err error @@ -157,7 +174,7 @@ func isAtMinChromeIosVersion(userAgent string) bool { return chromeiOSVersion >= minChromeVersion } -func isAtMinSafariVersion(userAgent string) bool { +func (u Util) isAtMinSafariVersion(userAgent string) bool { var safariVersion int64 var err error @@ -173,7 +190,7 @@ func isAtMinSafariVersion(userAgent string) bool { return safariVersion >= minSafariVersion } -func gdprApplies(request *openrtb.BidRequest) bool { +func (u Util) gdprApplies(request *openrtb.BidRequest) bool { var gdprApplies int64 if request.Regs != nil { @@ -186,7 +203,7 @@ func gdprApplies(request *openrtb.BidRequest) bool { return gdprApplies != 0 } -func gdprConsentString(request *openrtb.BidRequest) string { +func (u Util) gdprConsentString(request *openrtb.BidRequest) string { var consentString string if request.User != nil { diff --git a/adapters/sharethrough/utils_test.go b/adapters/sharethrough/utils_test.go new file mode 100644 index 00000000000..0b3bfd71cd7 --- /dev/null +++ b/adapters/sharethrough/utils_test.go @@ -0,0 +1,372 @@ +package sharethrough + +import ( + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/openrtb_ext" + "strings" + "testing" +) + +func TestGetAdMarkup(t *testing.T) { + tests := map[string]struct { + inputResponse openrtb_ext.ExtImpSharethroughResponse + inputParams *StrAdSeverParams + expectedSuccess []string + expectedError error + }{ + "Sets template variables": { + inputResponse: openrtb_ext.ExtImpSharethroughResponse{BidID: "bid", AdServerRequestID: "arid"}, + inputParams: &StrAdSeverParams{Pkey: "pkey"}, + expectedSuccess: []string{ + ``, + `
`, + ``, + }, + expectedError: nil, + }, + "Includes sfp.js without iFrame busting logic if iFrame param is true": { + inputResponse: openrtb_ext.ExtImpSharethroughResponse{BidID: "bid", AdServerRequestID: "arid"}, + inputParams: &StrAdSeverParams{Pkey: "pkey", Iframe: true}, + expectedSuccess: []string{ + ``, + }, + expectedError: nil, + }, + "Includes sfp.js with iFrame busting logic if iFrame param is false": { + inputResponse: openrtb_ext.ExtImpSharethroughResponse{BidID: "bid", AdServerRequestID: "arid"}, + inputParams: &StrAdSeverParams{Pkey: "pkey", Iframe: false}, + expectedSuccess: []string{ + ``, + }, + expectedError: nil, + }, + "Includes sfp.js with iFrame busting logic if iFrame param is not provided": { + inputResponse: openrtb_ext.ExtImpSharethroughResponse{BidID: "bid", AdServerRequestID: "arid"}, + inputParams: &StrAdSeverParams{Pkey: "pkey"}, + expectedSuccess: []string{ + ``, + }, + expectedError: nil, + }, + } + + util := Util{} + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + outputSuccess, outputError := util.getAdMarkup(test.inputResponse, test.inputParams) + for _, markup := range test.expectedSuccess { + if !strings.Contains(outputSuccess, markup) { + t.Errorf("Expected Ad Markup to contain: %s, got %s\n", markup, outputSuccess) + } + } + if outputError != test.expectedError { + t.Errorf("Expected Error to be: %s, got %s\n", test.expectedError, outputError) + } + } +} + +func TestGetPlacementSize(t *testing.T) { + tests := map[string]struct { + input []openrtb.Format + expectedHeight uint64 + expectedWidth uint64 + }{ + "Returns default size if empty input": { + input: []openrtb.Format{}, + expectedHeight: 1, + expectedWidth: 1, + }, + "Returns size if only one is passed": { + input: []openrtb.Format{{H: 100, W: 100}}, + expectedHeight: 100, + expectedWidth: 100, + }, + "Returns biggest size if multiple are passed": { + input: []openrtb.Format{{H: 100, W: 100}, {H: 200, W: 200}, {H: 50, W: 50}}, + expectedHeight: 200, + expectedWidth: 200, + }, + } + + util := Util{} + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + outputHeight, outputWidth := util.getPlacementSize(test.input) + if outputHeight != test.expectedHeight { + t.Errorf("Expected Height: %d, got %d\n", test.expectedHeight, outputHeight) + } + if outputWidth != test.expectedWidth { + t.Errorf("Expected Width: %d, got %d\n", test.expectedWidth, outputWidth) + } + } +} + +type userAgentTest struct { + input string + expected bool +} + +func runUserAgentTests(tests map[string]userAgentTest, fn func(string) bool, t *testing.T) { + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + output := fn(test.input) + if output != test.expected { + t.Errorf("Expected: %t, got %t\n", test.expected, output) + } + } +} + +func TestCanAutoPlayVideo(t *testing.T) { + ableAgents := map[string]string{ + "Android at min Chrome version": "Android Chrome/60.0", + "iOS at min Chrome version": "iPhone CriOS/60.0", + "iOS at min Safari version": "iPad Version/14.0", + "Neither Android or iOS": "Some User Agent", + } + unableAgents := map[string]string{ + "Android not at min Chrome version": "Android Chrome/12", + "iOS not at min Chrome version": "iPod Chrome/12", + "iOS not at min Safari version": "iPod Version/8", + } + + tests := map[string]userAgentTest{} + for testName, agent := range ableAgents { + tests[testName] = userAgentTest{ + input: agent, + expected: true, + } + } + for testName, agent := range unableAgents { + tests[testName] = userAgentTest{ + input: agent, + expected: false, + } + } + + runUserAgentTests(tests, Util{}.canAutoPlayVideo, t) +} + +func TestIsAndroid(t *testing.T) { + goodUserAgent := "Mozilla/5.0 (Linux; Android 6.0.1; Nexus 6P Build/MMB29P)" + badUserAgent := "fake user agent" + + // This is an alternate way to do testing if you have many test cases that only change the input and output + tests := map[string]userAgentTest{ + "Match the Android user agent": { + input: goodUserAgent, + expected: true, + }, + "Does not match Android user agent": { + input: badUserAgent, + expected: false, + }, + } + + runUserAgentTests(tests, Util{}.isAndroid, t) +} + +func TestIsiOS(t *testing.T) { + iPhoneUserAgent := "Some string containing iPhone" + iPadUserAgent := "Some string containing iPad" + iPodUserAgent := "Some string containing iPOD" + badUserAgent := "Fake User Agent" + + tests := map[string]userAgentTest{ + "Match the iPhone user agent": { + input: iPhoneUserAgent, + expected: true, + }, + "Match the iPad user agent": { + input: iPadUserAgent, + expected: true, + }, + "Match the iPod user agent": { + input: iPodUserAgent, + expected: true, + }, + "Does not match Android user agent": { + input: badUserAgent, + expected: false, + }, + } + + runUserAgentTests(tests, Util{}.isiOS, t) +} + +func TestIsAtMinChromeVersion(t *testing.T) { + v60ChromeUA := "Mozilla/5.0 Chrome/60.0.3112.113" + v12ChromeUA := "Mozilla/5.0 Chrome/12.0.3112.113" + badUA := "Fake User Agent" + + tests := map[string]userAgentTest{ + "Return true if greater than min (53)": { + input: v60ChromeUA, + expected: true, + }, + "Return false if lower than min (53)": { + input: v12ChromeUA, + expected: false, + }, + "Return false if no version found": { + input: badUA, + expected: false, + }, + } + + runUserAgentTests(tests, Util{}.isAtMinChromeVersion, t) +} + +func TestIsAtMinChromeIosVersion(t *testing.T) { + v60ChrIosUA := "Mozilla/5.0 CriOS/60.0.3112.113" + v12ChrIosUA := "Mozilla/5.0 CriOS/12.0.3112.113" + badUA := "Fake User Agent" + + tests := map[string]userAgentTest{ + "Return true if greater than min (53)": { + input: v60ChrIosUA, + expected: true, + }, + "Return false if lower than min (53)": { + input: v12ChrIosUA, + expected: false, + }, + "Return false if no version found": { + input: badUA, + expected: false, + }, + } + + runUserAgentTests(tests, Util{}.isAtMinChromeIosVersion, t) +} + +func TestIsAtMinSafariVersion(t *testing.T) { + v12SafariUA := "Mozilla/5.0 Version/12.0.3112.113" + v07SafariUA := "Mozilla/5.0 Version/07.0.3112.113" + badUA := "Fake User Agent" + + tests := map[string]userAgentTest{ + "Return true if greater than min (10)": { + input: v12SafariUA, + expected: true, + }, + "Return false if lower than min (10)": { + input: v07SafariUA, + expected: false, + }, + "Return false if no version found": { + input: badUA, + expected: false, + }, + } + + runUserAgentTests(tests, Util{}.isAtMinSafariVersion, t) +} + +func TestGdprApplies(t *testing.T) { + bidRequestGdpr := openrtb.BidRequest{ + Regs: &openrtb.Regs{ + Ext: []byte(`{"gdpr": 1}`), + }, + } + bidRequestNonGdpr := openrtb.BidRequest{ + Regs: &openrtb.Regs{ + Ext: []byte(`{"gdpr": 0}`), + }, + } + bidRequestEmptyGdpr := openrtb.BidRequest{ + Regs: &openrtb.Regs{ + Ext: []byte(``), + }, + } + bidRequestEmptyRegs := openrtb.BidRequest{ + Regs: &openrtb.Regs{}, + } + + tests := map[string]struct { + input *openrtb.BidRequest + expected bool + }{ + "Return true if gdpr set to 1": { + input: &bidRequestGdpr, + expected: true, + }, + "Return false if gdpr set to 0": { + input: &bidRequestNonGdpr, + expected: false, + }, + "Return false if no gdpr set": { + input: &bidRequestEmptyGdpr, + expected: false, + }, + "Return false if no Regs set": { + input: &bidRequestEmptyRegs, + expected: false, + }, + } + + util := Util{} + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + output := util.gdprApplies(test.input) + if output != test.expected { + t.Errorf("Expected: %t, got %t\n", test.expected, output) + } + } +} + +func TestGdprConsentString(t *testing.T) { + bidRequestWithConsent := openrtb.BidRequest{ + User: &openrtb.User{ + Ext: []byte(`{"consent": "abc"}`), + }, + } + bidRequestWithEmptyConsent := openrtb.BidRequest{ + User: &openrtb.User{ + Ext: []byte(`{"consent": ""}`), + }, + } + bidRequestWithoutConsent := openrtb.BidRequest{ + User: &openrtb.User{ + Ext: []byte(`{"other": "abc"}`), + }, + } + bidRequestWithUserExt := openrtb.BidRequest{ + User: &openrtb.User{}, + } + + tests := map[string]struct { + input *openrtb.BidRequest + expected string + }{ + "Return consent string if provided": { + input: &bidRequestWithConsent, + expected: "abc", + }, + "Return empty string if consent string empty": { + input: &bidRequestWithEmptyConsent, + expected: "", + }, + "Return empty string if no consent string provided": { + input: &bidRequestWithoutConsent, + expected: "", + }, + "Return empty string if User set": { + input: &bidRequestWithUserExt, + expected: "", + }, + } + + util := Util{} + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + output := util.gdprConsentString(test.input) + if output != test.expected { + t.Errorf("Expected: %s, got %s\n", test.expected, output) + } + } +} diff --git a/static/bidder-info/sharethrough.yaml b/static/bidder-info/sharethrough.yaml index 1662857aeb1..6d829252171 100644 --- a/static/bidder-info/sharethrough.yaml +++ b/static/bidder-info/sharethrough.yaml @@ -1,13 +1,9 @@ maintainer: - email: "cnguyen@sharethrough.com" + email: "pubgrowth.engineering@sharethrough.com" capabilities: app: mediaTypes: - - banner - native - - video site: mediaTypes: - - banner - - video - native From 44455a20639c0bae8bc576388735c56c51310f19 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Wed, 15 May 2019 11:05:14 -0700 Subject: [PATCH 18/26] Send supplyId to imp req instead of hbSource (#5) [#165477915] From a17beca06f284ce10e27e6f77f9c53766ca83b9e Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Wed, 15 May 2019 11:05:50 -0700 Subject: [PATCH 19/26] Finalize PR (#8) [#164911891] Co-authored-by: Josh Becker --- adapters/sharethrough/params_test.go | 58 +++++++++++++++++++ .../sharethroughtest/params/race/banner.json | 5 ++ .../sharethroughtest/params/race/native.json | 5 ++ config/config.go | 2 +- static/bidder-info/sharethrough.yaml | 2 + 5 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 adapters/sharethrough/params_test.go create mode 100644 adapters/sharethrough/sharethroughtest/params/race/banner.json create mode 100644 adapters/sharethrough/sharethroughtest/params/race/native.json diff --git a/adapters/sharethrough/params_test.go b/adapters/sharethrough/params_test.go new file mode 100644 index 00000000000..416f459341d --- /dev/null +++ b/adapters/sharethrough/params_test.go @@ -0,0 +1,58 @@ +package sharethrough + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +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.BidderSharethrough, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected Sharethrough 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.BidderSharethrough, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"pkey": "123"}`, + `{"pkey": "123", "iframe": true}`, + `{"pkey": "abc", "iframe": false}`, + `{"pkey": "abc123", "iframe": true, "iframeSize": [20, 20]}`, +} + +var invalidParams = []string{ + ``, + `null`, + `true`, + `5`, + `4.2`, + `[]`, + `{}`, + `{"pkey": 123}`, + `{"iframe": 123}`, + `{"iframeSize": [20, 20]}`, + `{"pkey": 123, "iframe": 123}`, + `{"pkey": 123, "iframe": true, "iframeSize": [20]}`, + `{"pkey": 123, "iframe": true, "iframeSize": []}`, + `{"pkey": 123, "iframe": true, "iframeSize": 123}`, +} diff --git a/adapters/sharethrough/sharethroughtest/params/race/banner.json b/adapters/sharethrough/sharethroughtest/params/race/banner.json new file mode 100644 index 00000000000..6702f4c2965 --- /dev/null +++ b/adapters/sharethrough/sharethroughtest/params/race/banner.json @@ -0,0 +1,5 @@ +{ + "pkey": "abc123", + "iframe": true, + "iframeSize": [50, 50] +} \ No newline at end of file diff --git a/adapters/sharethrough/sharethroughtest/params/race/native.json b/adapters/sharethrough/sharethroughtest/params/race/native.json new file mode 100644 index 00000000000..6702f4c2965 --- /dev/null +++ b/adapters/sharethrough/sharethroughtest/params/race/native.json @@ -0,0 +1,5 @@ +{ + "pkey": "abc123", + "iframe": true, + "iframeSize": [50, 50] +} \ No newline at end of file diff --git a/config/config.go b/config/config.go index 47a1d40ed01..1374e41e241 100644 --- a/config/config.go +++ b/config/config.go @@ -557,7 +557,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.rhythmone.endpoint", "http://tag.1rx.io/rmp") v.SetDefault("adapters.gumgum.endpoint", "https://g2.gumgum.com/providers/prbds2s/bid") v.SetDefault("adapters.grid.endpoint", "http://grid.bidswitch.net/sp_bid?sp=prebid") - v.SetDefault("adapters.sharethrough.endpoint", "http://localhost:8001/butler") + v.SetDefault("adapters.sharethrough.endpoint", "http://btlr.sharethrough.com/FGMrCMMc/v1") v.SetDefault("adapters.sonobi.endpoint", "https://apex.go.sonobi.com/prebid?partnerid=71d9d3d8af") v.SetDefault("adapters.yieldmo.endpoint", "http://ads.yieldmo.com/exchange/prebid-server") v.SetDefault("adapters.gamoshi.endpoint", "https://rtb.gamoshi.io") diff --git a/static/bidder-info/sharethrough.yaml b/static/bidder-info/sharethrough.yaml index 6d829252171..09530be508c 100644 --- a/static/bidder-info/sharethrough.yaml +++ b/static/bidder-info/sharethrough.yaml @@ -4,6 +4,8 @@ capabilities: app: mediaTypes: - native + - banner site: mediaTypes: - native + - banner From 0d58bb08e294e5ca10288b95e6369bd1547bd038 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Wed, 15 May 2019 11:45:47 -0700 Subject: [PATCH 20/26] Remove test setting --- endpoints/openrtb2/auction.go | 1 - 1 file changed, 1 deletion(-) diff --git a/endpoints/openrtb2/auction.go b/endpoints/openrtb2/auction.go index 2f09a64a1de..772acbeba1f 100644 --- a/endpoints/openrtb2/auction.go +++ b/endpoints/openrtb2/auction.go @@ -145,7 +145,6 @@ func (deps *endpointDeps) Auction(w http.ResponseWriter, r *http.Request, _ http } numImps = len(req.Imp) - req.Test = 1 response, err := deps.ex.HoldAuction(ctx, req, usersyncs, labels, &deps.categories) ao.Request = req ao.Response = response From 60ecbdef2ac7c8a03c7b755ea2d3f84e0c3a3642 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Wed, 15 May 2019 12:07:08 -0700 Subject: [PATCH 21/26] Add Sharethrough in syncer_test --- usersync/usersyncers/syncer_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index bb42b8622ac..7e44ed34f33 100644 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -34,6 +34,7 @@ func TestNewSyncerMap(t *testing.T) { string(openrtb_ext.BidderPulsepoint): syncConfig, string(openrtb_ext.BidderRhythmone): syncConfig, string(openrtb_ext.BidderRubicon): syncConfig, + string(openrtb_ext.BidderSharethrough): syncConfig, string(openrtb_ext.BidderSomoaudience): syncConfig, string(openrtb_ext.BidderSovrn): syncConfig, string(openrtb_ext.Bidder33Across): syncConfig, From 23686e3d5372ce07f0098e4d578434402b32c593 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Fri, 17 May 2019 16:18:13 -0700 Subject: [PATCH 22/26] Update deserializing of third party partners --- openrtb_ext/imp_sharethrough.go | 45 ++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 20 deletions(-) diff --git a/openrtb_ext/imp_sharethrough.go b/openrtb_ext/imp_sharethrough.go index 6d30bfb7b78..03f1bbaf1b5 100644 --- a/openrtb_ext/imp_sharethrough.go +++ b/openrtb_ext/imp_sharethrough.go @@ -67,27 +67,32 @@ type ExtImpSharethroughPlacement struct { Status string `json:"status"` } +type ExtImpSharethroughPlacementThirdPartyPartner struct { + Key string `json:"key"` + Tag string `json:"tag"` +} + type ExtImpSharethroughPlacementAttributes struct { - AdServerKey string `json:"ad_server_key"` - AdServerPath string `json:"ad_server_path"` - AllowDynamicCropping bool `json:"allow_dynamic_cropping"` - AppThirdPartyPartners []string `json:"app_third_party_partners"` - CustomCardCSS string `json:"custom_card_css"` - DFPPath string `json:"dfp_path"` - DirectSellPromotedByText string `json:"direct_sell_promoted_by_text"` - Domain string `json:"domain"` - EnableLinkRedirection bool `json:"enable_link_redirection"` - FeaturedContent json.RawMessage `json:"featured_content"` - MaxHeadlineLength int `json:"max_headline_length"` - MultiAdPlacement bool `json:"multi_ad_placement"` - PromotedByText string `json:"promoted_by_text"` - PublisherKey string `json:"publisher_key"` - RenderingPixelOffset int `json:"rendering_pixel_offset"` - SafeFrameSize []int `json:"safe_frame_size"` - SiteKey string `json:"site_key"` - StrOptOutURL string `json:"str_opt_out_url"` - Template string `json:"template"` - ThirdPartyPartners []string `json:"third_party_partners"` + AdServerKey string `json:"ad_server_key"` + AdServerPath string `json:"ad_server_path"` + AllowDynamicCropping bool `json:"allow_dynamic_cropping"` + AppThirdPartyPartners []string `json:"app_third_party_partners"` + CustomCardCSS string `json:"custom_card_css"` + DFPPath string `json:"dfp_path"` + DirectSellPromotedByText string `json:"direct_sell_promoted_by_text"` + Domain string `json:"domain"` + EnableLinkRedirection bool `json:"enable_link_redirection"` + FeaturedContent json.RawMessage `json:"featured_content"` + MaxHeadlineLength int `json:"max_headline_length"` + MultiAdPlacement bool `json:"multi_ad_placement"` + PromotedByText string `json:"promoted_by_text"` + PublisherKey string `json:"publisher_key"` + RenderingPixelOffset int `json:"rendering_pixel_offset"` + SafeFrameSize []int `json:"safe_frame_size"` + SiteKey string `json:"site_key"` + StrOptOutURL string `json:"str_opt_out_url"` + Template string `json:"template"` + ThirdPartyPartners []ExtImpSharethroughPlacementThirdPartyPartner `json:"third_party_partners"` } type ExtImpSharethroughExt struct { From 6bbeade391438d6e9d22893ab571eb564951e193 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Fri, 31 May 2019 11:21:50 -0700 Subject: [PATCH 23/26] Refactor/optimize UserAgent parsing (#9) following josephveach's review in prebid/prebid-server#903 --- adapters/sharethrough/butler.go | 14 ++++-- adapters/sharethrough/butler_test.go | 7 ++- adapters/sharethrough/sharethrough.go | 6 +++ adapters/sharethrough/utils.go | 71 +++++++++++---------------- adapters/sharethrough/utils_test.go | 46 +++++++++++++++-- 5 files changed, 93 insertions(+), 51 deletions(-) diff --git a/adapters/sharethrough/butler.go b/adapters/sharethrough/butler.go index 8e4294854c6..aacb0c28773 100644 --- a/adapters/sharethrough/butler.go +++ b/adapters/sharethrough/butler.go @@ -10,6 +10,7 @@ import ( "github.com/prebid/prebid-server/openrtb_ext" "net/http" "net/url" + "regexp" "strconv" ) @@ -34,13 +35,20 @@ type StrAdServerUriInterface interface { parseUri(string) (*StrAdSeverParams, error) } +type UserAgentParsers struct { + ChromeVersion *regexp.Regexp + ChromeiOSVersion *regexp.Regexp + SafariVersion *regexp.Regexp +} + type StrUriHelper struct { BaseURI string } type StrOpenRTBTranslator struct { - UriHelper StrAdServerUriInterface - Util UtilityInterface + UriHelper StrAdServerUriInterface + Util UtilityInterface + UserAgentParsers UserAgentParsers } func (s StrOpenRTBTranslator) requestFromOpenRTB(imp openrtb.Imp, request *openrtb.BidRequest) (*adapters.RequestData, error) { @@ -72,7 +80,7 @@ func (s StrOpenRTBTranslator) requestFromOpenRTB(imp openrtb.Imp, request *openr Iframe: extBtlrParams.Bidder.Iframe, Height: height, Width: width, - InstantPlayCapable: s.Util.canAutoPlayVideo(request.Device.UA), + InstantPlayCapable: s.Util.canAutoPlayVideo(request.Device.UA, s.UserAgentParsers), }, request.App), Body: nil, Headers: headers, diff --git a/adapters/sharethrough/butler_test.go b/adapters/sharethrough/butler_test.go index 8d218efcb6f..be9a4050f1f 100644 --- a/adapters/sharethrough/butler_test.go +++ b/adapters/sharethrough/butler_test.go @@ -7,6 +7,7 @@ import ( "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" "net/http" + "regexp" "strings" "testing" ) @@ -96,7 +97,11 @@ func TestSuccessRequestFromOpenRTB(t *testing.T) { }, } - adServer := StrOpenRTBTranslator{UriHelper: mockUriHelper, Util: Util{}} + adServer := StrOpenRTBTranslator{UriHelper: mockUriHelper, Util: Util{}, UserAgentParsers: UserAgentParsers{ + ChromeVersion: regexp.MustCompile(`Chrome\/(?P\d+)`), + ChromeiOSVersion: regexp.MustCompile(`CriOS\/(?P\d+)`), + SafariVersion: regexp.MustCompile(`Version\/(?P\d+)`), + }} for testName, test := range tests { outputSuccess, outputError := adServer.requestFromOpenRTB(test.inputImp, test.inputReq) assertRequestDataEquals(t, testName, test.expected, outputSuccess) diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index b30700ccf37..df14b85fe43 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -8,6 +8,7 @@ import ( "github.com/prebid/prebid-server/errortypes" "github.com/prebid/prebid-server/openrtb_ext" "net/http" + "regexp" ) const supplyId = "FGMrCMMc" @@ -18,6 +19,11 @@ func NewSharethroughBidder(endpoint string) *SharethroughAdapter { AdServer: StrOpenRTBTranslator{ UriHelper: StrUriHelper{BaseURI: endpoint}, Util: Util{}, + UserAgentParsers: UserAgentParsers{ + ChromeVersion: regexp.MustCompile(`Chrome\/(?P\d+)`), + ChromeiOSVersion: regexp.MustCompile(`CriOS\/(?P\d+)`), + SafariVersion: regexp.MustCompile(`Version\/(?P\d+)`), + }, }, } } diff --git a/adapters/sharethrough/utils.go b/adapters/sharethrough/utils.go index ed22e992b67..b5e1bf9614d 100644 --- a/adapters/sharethrough/utils.go +++ b/adapters/sharethrough/utils.go @@ -23,12 +23,11 @@ type UtilityInterface interface { getAdMarkup(openrtb_ext.ExtImpSharethroughResponse, *StrAdSeverParams) (string, error) getPlacementSize([]openrtb.Format) (uint64, uint64) - canAutoPlayVideo(string) bool + canAutoPlayVideo(string, UserAgentParsers) bool isAndroid(string) bool isiOS(string) bool - isAtMinChromeIosVersion(string) bool - isAtMinChromeVersion(string) bool - isAtMinSafariVersion(string) bool + isAtMinChromeVersion(string, *regexp.Regexp) bool + isAtMinSafariVersion(string, *regexp.Regexp) bool } type Util struct{} @@ -119,11 +118,22 @@ func (u Util) getPlacementSize(formats []openrtb.Format) (height uint64, width u return biggest.Height, biggest.Width } -func (u Util) canAutoPlayVideo(userAgent string) bool { - return (u.isAndroid(userAgent) && u.isAtMinChromeVersion(userAgent)) || - (u.isiOS(userAgent) && - (u.isAtMinSafariVersion(userAgent) || u.isAtMinChromeIosVersion(userAgent))) || - !(u.isAndroid(userAgent) || u.isiOS(userAgent)) +func (u Util) canAutoPlayVideo(userAgent string, parsers UserAgentParsers) bool { + if u.isAndroid(userAgent) { + if u.isAtMinChromeVersion(userAgent, parsers.ChromeVersion) { + return true + } else { + return false + } + } else if u.isiOS(userAgent) { + if u.isAtMinSafariVersion(userAgent, parsers.SafariVersion) || u.isAtMinChromeVersion(userAgent, parsers.ChromeiOSVersion) { + return true + } else { + return false + } + } else { + return true + } } func (u Util) isAndroid(userAgent string) bool { @@ -142,52 +152,27 @@ func (u Util) isiOS(userAgent string) bool { return isiOS } -func (u Util) isAtMinChromeVersion(userAgent string) bool { - var chromeVersion int64 +func (u Util) isAtMinVersion(userAgent string, parser *regexp.Regexp, minVersion int64) bool { + var version int64 var err error - chromeVersionRegex := regexp.MustCompile(`Chrome\/(?P\d+)`) - chromeVersionMatch := chromeVersionRegex.FindStringSubmatch(userAgent) + chromeVersionMatch := parser.FindStringSubmatch(userAgent) if len(chromeVersionMatch) > 1 { - chromeVersion, err = strconv.ParseInt(chromeVersionMatch[1], 10, 64) + version, err = strconv.ParseInt(chromeVersionMatch[1], 10, 64) } if err != nil { return false } - return chromeVersion >= minChromeVersion + return version >= minVersion } -func (u Util) isAtMinChromeIosVersion(userAgent string) bool { - var chromeiOSVersion int64 - var err error - - chromeiOSVersionRegex := regexp.MustCompile(`CriOS\/(?P\d+)`) - chromeiOSVersionMatch := chromeiOSVersionRegex.FindStringSubmatch(userAgent) - if len(chromeiOSVersionMatch) > 1 { - chromeiOSVersion, err = strconv.ParseInt(chromeiOSVersionMatch[1], 10, 64) - } - if err != nil { - return false - } - - return chromeiOSVersion >= minChromeVersion +func (u Util) isAtMinChromeVersion(userAgent string, parser *regexp.Regexp) bool { + return u.isAtMinVersion(userAgent, parser, minChromeVersion) } -func (u Util) isAtMinSafariVersion(userAgent string) bool { - var safariVersion int64 - var err error - - safariVersionRegex := regexp.MustCompile(`Version\/(?P\d+)`) - safariVersionMatch := safariVersionRegex.FindStringSubmatch(userAgent) - if len(safariVersionMatch) > 1 { - safariVersion, err = strconv.ParseInt(safariVersionMatch[1], 10, 64) - } - if err != nil { - return false - } - - return safariVersion >= minSafariVersion +func (u Util) isAtMinSafariVersion(userAgent string, parser *regexp.Regexp) bool { + return u.isAtMinVersion(userAgent, parser, minSafariVersion) } func (u Util) gdprApplies(request *openrtb.BidRequest) bool { diff --git a/adapters/sharethrough/utils_test.go b/adapters/sharethrough/utils_test.go index 0b3bfd71cd7..a5ee882707e 100644 --- a/adapters/sharethrough/utils_test.go +++ b/adapters/sharethrough/utils_test.go @@ -3,6 +3,7 @@ package sharethrough import ( "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/openrtb_ext" + "regexp" "strings" "testing" ) @@ -120,6 +121,12 @@ func runUserAgentTests(tests map[string]userAgentTest, fn func(string) bool, t * } func TestCanAutoPlayVideo(t *testing.T) { + uaParsers := UserAgentParsers{ + ChromeVersion: regexp.MustCompile(`Chrome\/(?P\d+)`), + ChromeiOSVersion: regexp.MustCompile(`CriOS\/(?P\d+)`), + SafariVersion: regexp.MustCompile(`Version\/(?P\d+)`), + } + ableAgents := map[string]string{ "Android at min Chrome version": "Android Chrome/60.0", "iOS at min Chrome version": "iPhone CriOS/60.0", @@ -146,7 +153,14 @@ func TestCanAutoPlayVideo(t *testing.T) { } } - runUserAgentTests(tests, Util{}.canAutoPlayVideo, t) + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + output := Util{}.canAutoPlayVideo(test.input, uaParsers) + if output != test.expected { + t.Errorf("Expected: %t, got %t\n", test.expected, output) + } + } } func TestIsAndroid(t *testing.T) { @@ -197,6 +211,7 @@ func TestIsiOS(t *testing.T) { } func TestIsAtMinChromeVersion(t *testing.T) { + regex := regexp.MustCompile(`Chrome\/(?P\d+)`) v60ChromeUA := "Mozilla/5.0 Chrome/60.0.3112.113" v12ChromeUA := "Mozilla/5.0 Chrome/12.0.3112.113" badUA := "Fake User Agent" @@ -216,10 +231,18 @@ func TestIsAtMinChromeVersion(t *testing.T) { }, } - runUserAgentTests(tests, Util{}.isAtMinChromeVersion, t) + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + output := Util{}.isAtMinChromeVersion(test.input, regex) + if output != test.expected { + t.Errorf("Expected: %t, got %t\n", test.expected, output) + } + } } func TestIsAtMinChromeIosVersion(t *testing.T) { + regex := regexp.MustCompile(`CriOS\/(?P\d+)`) v60ChrIosUA := "Mozilla/5.0 CriOS/60.0.3112.113" v12ChrIosUA := "Mozilla/5.0 CriOS/12.0.3112.113" badUA := "Fake User Agent" @@ -239,10 +262,18 @@ func TestIsAtMinChromeIosVersion(t *testing.T) { }, } - runUserAgentTests(tests, Util{}.isAtMinChromeIosVersion, t) + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + output := Util{}.isAtMinChromeVersion(test.input, regex) + if output != test.expected { + t.Errorf("Expected: %t, got %t\n", test.expected, output) + } + } } func TestIsAtMinSafariVersion(t *testing.T) { + regex := regexp.MustCompile(`Version\/(?P\d+)`) v12SafariUA := "Mozilla/5.0 Version/12.0.3112.113" v07SafariUA := "Mozilla/5.0 Version/07.0.3112.113" badUA := "Fake User Agent" @@ -262,7 +293,14 @@ func TestIsAtMinSafariVersion(t *testing.T) { }, } - runUserAgentTests(tests, Util{}.isAtMinSafariVersion, t) + for testName, test := range tests { + t.Logf("Test case: %s\n", testName) + + output := Util{}.isAtMinSafariVersion(test.input, regex) + if output != test.expected { + t.Errorf("Expected: %t, got %t\n", test.expected, output) + } + } } func TestGdprApplies(t *testing.T) { From 5aac9ec2a697c131b90597301c34d9fc2b7e5467 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Mon, 3 Jun 2019 15:29:25 -0700 Subject: [PATCH 24/26] Addressing June 3rd review from prebid/prebid-server#903 Optimizations, clean up suggested by @mansinahar --- adapters/sharethrough/butler.go | 33 ++++++++++++++++++++------- adapters/sharethrough/butler_test.go | 2 +- adapters/sharethrough/sharethrough.go | 8 +------ adapters/sharethrough/utils.go | 8 +++---- openrtb_ext/imp_sharethrough.go | 8 +++---- 5 files changed, 34 insertions(+), 25 deletions(-) diff --git a/adapters/sharethrough/butler.go b/adapters/sharethrough/butler.go index aacb0c28773..4b4fc6918bd 100644 --- a/adapters/sharethrough/butler.go +++ b/adapters/sharethrough/butler.go @@ -56,16 +56,20 @@ func (s StrOpenRTBTranslator) requestFromOpenRTB(imp openrtb.Imp, request *openr headers.Add("Content-Type", "text/plain;charset=utf-8") headers.Add("Accept", "application/json") - var extBtlrParams openrtb_ext.ExtImpSharethroughExt - if err := json.Unmarshal(imp.Ext, &extBtlrParams); err != nil { + var strImpExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &strImpExt); err != nil { + return nil, err + } + var strImpParams openrtb_ext.ExtImpSharethroughExt + if err := json.Unmarshal(strImpExt.Bidder, &strImpParams); err != nil { return nil, err } - pKey := extBtlrParams.Bidder.Pkey + pKey := strImpParams.Pkey var height, width uint64 - if len(extBtlrParams.Bidder.IframeSize) >= 2 { - height, width = uint64(extBtlrParams.Bidder.IframeSize[0]), uint64(extBtlrParams.Bidder.IframeSize[1]) + if len(strImpParams.IframeSize) >= 2 { + height, width = uint64(strImpParams.IframeSize[0]), uint64(strImpParams.IframeSize[1]) } else { height, width = s.Util.getPlacementSize(imp.Banner.Format) } @@ -77,7 +81,7 @@ func (s StrOpenRTBTranslator) requestFromOpenRTB(imp openrtb.Imp, request *openr BidID: imp.ID, ConsentRequired: s.Util.gdprApplies(request), ConsentString: s.Util.gdprConsentString(request), - Iframe: extBtlrParams.Bidder.Iframe, + Iframe: strImpParams.Iframe, Height: height, Width: width, InstantPlayCapable: s.Util.canAutoPlayVideo(request.Device.UA, s.UserAgentParsers), @@ -110,6 +114,9 @@ func (s StrOpenRTBTranslator) responseToOpenRTB(strResp openrtb_ext.ExtImpSharet if admErr != nil { errs = append(errs, &errortypes.BadServerResponse{Message: admErr.Error()}) } + if adm == "" { + return nil, errs + } bid := &openrtb.Bid{ AdID: strResp.AdServerRequestID, @@ -177,13 +184,23 @@ func (h StrUriHelper) parseUri(uri string) (*StrAdSeverParams, error) { return nil, err } + stayInIframe, err := strconv.ParseBool(params.Get("stayInIframe")) + if err != nil { + stayInIframe = false + } + + consentRequired, err := strconv.ParseBool(params.Get("consent_required")) + if err != nil { + consentRequired = false + } + return &StrAdSeverParams{ Pkey: params.Get("placement_key"), BidID: params.Get("bidId"), - Iframe: params.Get("stayInIframe") == "true", + Iframe: stayInIframe, Height: height, Width: width, - ConsentRequired: params.Get("consent_required") == "true", + ConsentRequired: consentRequired, ConsentString: params.Get("consent_string"), }, nil } diff --git a/adapters/sharethrough/butler_test.go b/adapters/sharethrough/butler_test.go index be9a4050f1f..635e5d6a004 100644 --- a/adapters/sharethrough/butler_test.go +++ b/adapters/sharethrough/butler_test.go @@ -68,7 +68,7 @@ func TestSuccessRequestFromOpenRTB(t *testing.T) { "Generates the correct AdServer request from Imp": { inputImp: openrtb.Imp{ ID: "abc", - Ext: []byte(`{"pkey": "pkey", "iframe": true, "iframeSize": [10, 20]}`), + Ext: []byte(`{ "bidder": {"pkey": "pkey", "iframe": true, "iframeSize": [10, 20]} }`), Banner: &openrtb.Banner{ Format: []openrtb.Format{{H: 30, W: 40}}, }, diff --git a/adapters/sharethrough/sharethrough.go b/adapters/sharethrough/sharethrough.go index df14b85fe43..ea6a35cf619 100644 --- a/adapters/sharethrough/sharethrough.go +++ b/adapters/sharethrough/sharethrough.go @@ -32,10 +32,6 @@ type SharethroughAdapter struct { AdServer StrOpenRTBInterface } -func (a SharethroughAdapter) Name() string { - return "sharethrough" -} - func (a SharethroughAdapter) MakeRequests(request *openrtb.BidRequest) ([]*adapters.RequestData, []error) { var reqs []*adapters.RequestData @@ -72,7 +68,5 @@ func (a SharethroughAdapter) MakeBids(internalRequest *openrtb.BidRequest, exter return nil, []error{err} } - br, errs := a.AdServer.responseToOpenRTB(strBidResp, externalRequest) - - return br, errs + return a.AdServer.responseToOpenRTB(strBidResp, externalRequest) } diff --git a/adapters/sharethrough/utils.go b/adapters/sharethrough/utils.go index b5e1bf9614d..18c481eaa37 100644 --- a/adapters/sharethrough/utils.go +++ b/adapters/sharethrough/utils.go @@ -152,13 +152,13 @@ func (u Util) isiOS(userAgent string) bool { return isiOS } -func (u Util) isAtMinVersion(userAgent string, parser *regexp.Regexp, minVersion int64) bool { +func (u Util) isAtMinVersion(userAgent string, versionParser *regexp.Regexp, minVersion int64) bool { var version int64 var err error - chromeVersionMatch := parser.FindStringSubmatch(userAgent) - if len(chromeVersionMatch) > 1 { - version, err = strconv.ParseInt(chromeVersionMatch[1], 10, 64) + versionMatch := versionParser.FindStringSubmatch(userAgent) + if len(versionMatch) > 1 { + version, err = strconv.ParseInt(versionMatch[1], 10, 64) } if err != nil { return false diff --git a/openrtb_ext/imp_sharethrough.go b/openrtb_ext/imp_sharethrough.go index 03f1bbaf1b5..21c6b4fd140 100644 --- a/openrtb_ext/imp_sharethrough.go +++ b/openrtb_ext/imp_sharethrough.go @@ -96,9 +96,7 @@ type ExtImpSharethroughPlacementAttributes struct { } type ExtImpSharethroughExt struct { - Bidder struct { - Pkey string `json:"pkey"` - Iframe bool `json:"iframe"` - IframeSize []int `json:"iframeSize"` - } `json:"bidder"` + Pkey string `json:"pkey"` + Iframe bool `json:"iframe"` + IframeSize []int `json:"iframeSize"` } From 3caa28e3554310727e7786fd4f9facf6c8fdedf8 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Tue, 4 Jun 2019 10:48:21 -0700 Subject: [PATCH 25/26] Addressing June 4th review from prebid/prebid-server#903 (#10) * Addressing June 4th review from prebid/prebid-server#903 Clean up canAutoPlayVideo + hardcode bhVersion to unknown for now... * Removing hbVersion butler param since it's not accessible --- adapters/sharethrough/butler.go | 19 +++---------------- adapters/sharethrough/butler_test.go | 11 +---------- adapters/sharethrough/sharethrough_test.go | 2 +- adapters/sharethrough/utils.go | 15 +++------------ 4 files changed, 8 insertions(+), 39 deletions(-) diff --git a/adapters/sharethrough/butler.go b/adapters/sharethrough/butler.go index 4b4fc6918bd..984aebac4c1 100644 --- a/adapters/sharethrough/butler.go +++ b/adapters/sharethrough/butler.go @@ -3,7 +3,6 @@ package sharethrough import ( "encoding/json" "fmt" - "github.com/buger/jsonparser" "github.com/mxmCherry/openrtb" "github.com/prebid/prebid-server/adapters" "github.com/prebid/prebid-server/errortypes" @@ -31,7 +30,7 @@ type StrOpenRTBInterface interface { } type StrAdServerUriInterface interface { - buildUri(StrAdSeverParams, *openrtb.App) string + buildUri(StrAdSeverParams) string parseUri(string) (*StrAdSeverParams, error) } @@ -85,7 +84,7 @@ func (s StrOpenRTBTranslator) requestFromOpenRTB(imp openrtb.Imp, request *openr Height: height, Width: width, InstantPlayCapable: s.Util.canAutoPlayVideo(request.Device.UA, s.UserAgentParsers), - }, request.App), + }), Body: nil, Headers: headers, }, nil @@ -137,7 +136,7 @@ func (s StrOpenRTBTranslator) responseToOpenRTB(strResp openrtb_ext.ExtImpSharet return bidResponse, errs } -func (h StrUriHelper) buildUri(params StrAdSeverParams, app *openrtb.App) string { +func (h StrUriHelper) buildUri(params StrAdSeverParams) string { v := url.Values{} v.Set("placement_key", params.Pkey) v.Set("bidId", params.BidID) @@ -149,18 +148,6 @@ func (h StrUriHelper) buildUri(params StrAdSeverParams, app *openrtb.App) string v.Set("height", strconv.FormatUint(params.Height, 10)) v.Set("width", strconv.FormatUint(params.Width, 10)) - var version string - - if app != nil { - // Skipping error handling here because it should fall through to unknown in the flow - version, _ = jsonparser.GetString(app.Ext, "prebid", "version") - } - - if len(version) == 0 { - version = "unknown" - } - - v.Set("hbVersion", version) v.Set("supplyId", supplyId) v.Set("strVersion", strVersion) diff --git a/adapters/sharethrough/butler_test.go b/adapters/sharethrough/butler_test.go index 635e5d6a004..2e258ecf775 100644 --- a/adapters/sharethrough/butler_test.go +++ b/adapters/sharethrough/butler_test.go @@ -276,7 +276,6 @@ func TestBuildUri(t *testing.T) { Height: 20, Width: 30, }, - inputApp: &openrtb.App{Ext: []byte(`{"prebid": {"version": "1"}}`)}, expected: []string{ "http://abc.com?", "placement_key=pkey", @@ -287,24 +286,16 @@ func TestBuildUri(t *testing.T) { "stayInIframe=false", "height=20", "width=30", - "hbVersion=1", "supplyId=FGMrCMMc", "strVersion=1.0.0", }, }, - "Sets version to unknown if version not found": { - inputParams: StrAdSeverParams{}, - inputApp: &openrtb.App{Ext: []byte(`{}`)}, - expected: []string{ - "hbVersion=unknown", - }, - }, } uriHelper := StrUriHelper{BaseURI: "http://abc.com"} for testName, test := range tests { t.Logf("Test case: %s\n", testName) - output := uriHelper.buildUri(test.inputParams, test.inputApp) + output := uriHelper.buildUri(test.inputParams) for _, uriParam := range test.expected { if !strings.Contains(output, uriParam) { diff --git a/adapters/sharethrough/sharethrough_test.go b/adapters/sharethrough/sharethrough_test.go index f1697279c68..b1d252d36d5 100644 --- a/adapters/sharethrough/sharethrough_test.go +++ b/adapters/sharethrough/sharethrough_test.go @@ -32,7 +32,7 @@ type MockStrUriHelper struct { StrAdServerUriInterface } -func (m MockStrUriHelper) buildUri(params StrAdSeverParams, app *openrtb.App) string { +func (m MockStrUriHelper) buildUri(params StrAdSeverParams) string { return m.mockBuildUri() } diff --git a/adapters/sharethrough/utils.go b/adapters/sharethrough/utils.go index 18c481eaa37..08f0ae3ae39 100644 --- a/adapters/sharethrough/utils.go +++ b/adapters/sharethrough/utils.go @@ -120,20 +120,11 @@ func (u Util) getPlacementSize(formats []openrtb.Format) (height uint64, width u func (u Util) canAutoPlayVideo(userAgent string, parsers UserAgentParsers) bool { if u.isAndroid(userAgent) { - if u.isAtMinChromeVersion(userAgent, parsers.ChromeVersion) { - return true - } else { - return false - } + return u.isAtMinChromeVersion(userAgent, parsers.ChromeVersion) } else if u.isiOS(userAgent) { - if u.isAtMinSafariVersion(userAgent, parsers.SafariVersion) || u.isAtMinChromeVersion(userAgent, parsers.ChromeiOSVersion) { - return true - } else { - return false - } - } else { - return true + return u.isAtMinSafariVersion(userAgent, parsers.SafariVersion) || u.isAtMinChromeVersion(userAgent, parsers.ChromeiOSVersion) } + return true } func (u Util) isAndroid(userAgent string) bool { From 55f03f0bade33c77f1dbf9fda1b68ba886c91502 Mon Sep 17 00:00:00 2001 From: Mathieu Pheulpin Date: Tue, 4 Jun 2019 12:12:45 -0700 Subject: [PATCH 26/26] Fix adMarkup error handling --- adapters/sharethrough/butler.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/adapters/sharethrough/butler.go b/adapters/sharethrough/butler.go index 984aebac4c1..f16be7a9f17 100644 --- a/adapters/sharethrough/butler.go +++ b/adapters/sharethrough/butler.go @@ -112,8 +112,6 @@ func (s StrOpenRTBTranslator) responseToOpenRTB(strResp openrtb_ext.ExtImpSharet adm, admErr := s.Util.getAdMarkup(strResp, btlrParams) if admErr != nil { errs = append(errs, &errortypes.BadServerResponse{Message: admErr.Error()}) - } - if adm == "" { return nil, errs }