Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/security.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
jobs:
build:
name: Trivy
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
Expand All @@ -31,4 +31,4 @@ jobs:
- name: Upload Results To GitHub Security Tab
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
sarif_file: 'trivy-results.sarif'
2 changes: 1 addition & 1 deletion .github/workflows/validate-merge.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:

jobs:
validate-merge:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest

steps:
- name: Install Go
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
strategy:
matrix:
go-version: [1.18.x, 1.19.x]
os: [ubuntu-20.04]
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}

steps:
Expand Down
1 change: 1 addition & 0 deletions adapters/bidder.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ type RequestData struct {
Uri string
Body []byte
Headers http.Header
ImpIDs []string
}

// ExtImpBidder can be used by Bidders to unmarshal any request.imp[i].ext.
Expand Down
233 changes: 233 additions & 0 deletions adapters/thetradedesk/thetradedesk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
package thetradedesk

import (
"encoding/json"
"errors"
"fmt"
"net/http"
"regexp"
"text/template"

"github.com/prebid/prebid-server/adapters"
"github.com/prebid/prebid-server/config"
"github.com/prebid/prebid-server/macros"
"github.com/prebid/prebid-server/openrtb_ext"

"github.com/prebid/openrtb/v19/openrtb2"
)

//const PREBID_INTEGRATION_TYPE = "1"

type adapter struct {
bidderEndpointTemplate string
defaultEndpoint string
templateEndpoint *template.Template
}

func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
pubID, supplySourceId, err := getExtensionInfo(request.Imp)

if err != nil {
return nil, []error{err}
}

modifiedImps := make([]openrtb2.Imp, 0, len(request.Imp))

for _, imp := range request.Imp {

if imp.Banner != nil {
if len(imp.Banner.Format) > 0 {
firstFormat := imp.Banner.Format[0]
bannerCopy := *imp.Banner
bannerCopy.H = &firstFormat.H
bannerCopy.W = &firstFormat.W
imp.Banner = &bannerCopy

}
}

modifiedImps = append(modifiedImps, imp)
}

request.Imp = modifiedImps

if request.Site != nil {
siteCopy := *request.Site
if siteCopy.Publisher != nil {
publisherCopy := *siteCopy.Publisher
if pubID != "" {
publisherCopy.ID = pubID
}
siteCopy.Publisher = &publisherCopy
} else {
siteCopy.Publisher = &openrtb2.Publisher{ID: pubID}
}
request.Site = &siteCopy
} else if request.App != nil {
appCopy := *request.App
if appCopy.Publisher != nil {
publisherCopy := *appCopy.Publisher
if pubID != "" {
publisherCopy.ID = pubID
}
appCopy.Publisher = &publisherCopy
} else {
appCopy.Publisher = &openrtb2.Publisher{ID: pubID}
}
request.App = &appCopy
}

errs := make([]error, 0, len(request.Imp))
reqJSON, err := json.Marshal(request)
if err != nil {
errs = append(errs, err)
return nil, errs
}

bidderEndpoint, err := a.buildEndpointURL(supplySourceId)
if err != nil {
return nil, []error{errors.New("Failed to build endpoint URL")}
}

headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")
//headers.Add("x-integration-type", PREBID_INTEGRATION_TYPE) this will be parsed and added conditionally later
return []*adapters.RequestData{{
Method: "POST",
Uri: bidderEndpoint,
Body: reqJSON,
Headers: headers,
ImpIDs: openrtb_ext.GetImpIDs(request.Imp),
}}, errs
}

func (a *adapter) buildEndpointURL(supplySourceId string) (string, error) {
if supplySourceId == "" {
return a.defaultEndpoint, nil
}

urlParams := macros.EndpointTemplateParams{SupplyId: supplySourceId}
bidderEndpoint, err := macros.ResolveMacros(a.templateEndpoint, urlParams)

if err != nil {
return "", fmt.Errorf("unable to resolve endpoint macros: %v", err)
}

return bidderEndpoint, nil
}

func getExtensionInfo(impressions []openrtb2.Imp) (string, string, error) {
publisherId := ""
supplySourceId := ""
for _, imp := range impressions {
var ttdExt, err = getImpressionExt(&imp)
if err != nil {
return "", "", err
}

if ttdExt.PublisherId != "" && publisherId == "" {
publisherId = ttdExt.PublisherId
}

if ttdExt.SupplySourceId != "" && supplySourceId == "" {
supplySourceId = ttdExt.SupplySourceId
}

if publisherId != "" && supplySourceId != "" {
break
}
}
return publisherId, supplySourceId, nil
}

func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpTheTradeDesk, error) {
var bidderExt adapters.ExtImpBidder
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
return nil, err
}

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

func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
if adapters.IsResponseStatusCodeNoContent(response) {
return adapters.NewBidderResponse(), nil
}

if err := adapters.CheckResponseStatusCodeForErrors(response); err != nil {
return nil, []error{err}
}

var bidResponse openrtb2.BidResponse
if err := json.Unmarshal(response.Body, &bidResponse); err != nil {
return nil, []error{err}
}

bidderResponse := adapters.NewBidderResponse()
bidderResponse.Currency = bidResponse.Cur

for _, seatBid := range bidResponse.SeatBid {
for _, bid := range seatBid.Bid {
bid := bid

bidType, err := getBidType(bid.MType)

if err != nil {
return nil, []error{err}
}

b := &adapters.TypedBid{
Bid: &bid,
BidType: bidType,
}
bidderResponse.Bids = append(bidderResponse.Bids, b)
}
}

return bidderResponse, nil
}

func getBidType(markupType openrtb2.MarkupType) (openrtb_ext.BidType, error) {
switch markupType {
case openrtb2.MarkupBanner:
return openrtb_ext.BidTypeBanner, nil
case openrtb2.MarkupVideo:
return openrtb_ext.BidTypeVideo, nil
case openrtb2.MarkupNative:
return openrtb_ext.BidTypeNative, nil
default:
return "", fmt.Errorf("unsupported mtype: %d", markupType)
}
}

func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
if len(config.ExtraAdapterInfo) > 0 {
isValidEndpoint, err := regexp.Match("([a-z]+)$", []byte(config.ExtraAdapterInfo))
if !isValidEndpoint || err != nil {
return nil, errors.New("ExtraAdapterInfo must be a simple string provided by TheTradeDesk")
}
}

template, err := template.New("endpointTemplate").Parse(config.Endpoint)
if err != nil {
return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
}

urlParams := macros.EndpointTemplateParams{SupplyId: config.ExtraAdapterInfo}
defaultEndpoint, err := macros.ResolveMacros(template, urlParams)

if err != nil {
return nil, fmt.Errorf("unable to resolve endpoint macros: %v", err)
}

return &adapter{
bidderEndpointTemplate: config.Endpoint,
defaultEndpoint: defaultEndpoint,
templateEndpoint: template,
}, nil
}
2 changes: 2 additions & 0 deletions exchange/adapter_builders.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ import (
"github.com/prebid/prebid-server/adapters/taboola"
"github.com/prebid/prebid-server/adapters/tappx"
"github.com/prebid/prebid-server/adapters/telaria"
"github.com/prebid/prebid-server/adapters/thetradedesk"
"github.com/prebid/prebid-server/adapters/trafficgate"
"github.com/prebid/prebid-server/adapters/triplelift"
"github.com/prebid/prebid-server/adapters/triplelift_native"
Expand Down Expand Up @@ -311,6 +312,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
openrtb_ext.BidderMobileFuse: mobilefuse.Builder,
openrtb_ext.BidderMotorik: motorik.Builder,
openrtb_ext.BidderNativo: nativo.Builder,
openrtb_ext.BidderTheTradeDesk: thetradedesk.Builder,
openrtb_ext.BidderNanoInteractive: nanointeractive.Builder,
openrtb_ext.BidderNextMillennium: nextmillennium.Builder,
openrtb_ext.BidderNinthDecimal: ninthdecimal.Builder,
Expand Down
17 changes: 12 additions & 5 deletions macros/macros.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"text/template"
)

// EndpointTemplateParams specifies params for an endpoint template
// EndpointTemplateParams specifies macros for bidder endpoints.
type EndpointTemplateParams struct {
Host string
PublisherID string
Expand All @@ -15,10 +15,17 @@ type EndpointTemplateParams struct {
AdUnit string
MediaType string
GvlID string
PageID string
SupplyId string
ImpID string
SspId string
SspID string
SeatID string
TokenID string
}

// UserSyncTemplateParams specifies params for an user sync URL template
type UserSyncTemplateParams struct {
// UserSyncPrivacy specifies privacy policy macros, represented as strings, for user sync urls.
type UserSyncPrivacy struct {
GDPR string
GDPRConsent string
USPrivacy string
Expand All @@ -30,10 +37,10 @@ type UserSyncTemplateParams struct {
func ResolveMacros(aTemplate *template.Template, params interface{}) (string, error) {
strBuf := bytes.Buffer{}

err := aTemplate.Execute(&strBuf, params)
if err != nil {
if err := aTemplate.Execute(&strBuf, params); err != nil {
return "", err
}

res := strBuf.String()
return res, nil
}
2 changes: 1 addition & 1 deletion macros/macros_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func TestResolveMacros(t *testing.T) {
},
{
givenTemplate: endpointTemplate,
givenParams: UserSyncTemplateParams{GDPR: "SomeGDPR", GDPRConsent: "SomeGDPRConsent"},
givenParams: UserSyncPrivacy{GDPR: "SomeGDPR", GDPRConsent: "SomeGDPRConsent"},
expectedResult: "",
expectedError: true,
},
Expand Down
2 changes: 2 additions & 0 deletions openrtb_ext/bidders.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,7 @@ const (
BidderMobileFuse BidderName = "mobilefuse"
BidderMotorik BidderName = "motorik"
BidderNativo BidderName = "nativo"
BidderTheTradeDesk BidderName = "thetradedesk"
BidderNanoInteractive BidderName = "nanointeractive"
BidderNextMillennium BidderName = "nextmillennium"
BidderNinthDecimal BidderName = "ninthdecimal"
Expand Down Expand Up @@ -420,6 +421,7 @@ func CoreBidderNames() []BidderName {
BidderMobileFuse,
BidderMotorik,
BidderNativo,
BidderTheTradeDesk,
BidderNanoInteractive,
BidderNextMillennium,
BidderNinthDecimal,
Expand Down
7 changes: 7 additions & 0 deletions openrtb_ext/imp_thetradedesk.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package openrtb_ext

// ExtImpTheTradeDesk defines the contract for bidrequest.imp[i].ext.prebid.bidder.thetradedesk
type ExtImpTheTradeDesk struct {
PublisherId string `json:"publisherId"`
SupplySourceId string `json:"supplySourceId"`
}
23 changes: 23 additions & 0 deletions static/bidder-info/thetradedesk.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
endpoint: "https://direct.adsrvr.org/bid/bidder/{{.SupplyId}}"
maintainer:
email: "[email protected]"
gvlVendorID: 21
capabilities:
app:
mediaTypes:
- banner
- video
- native
site:
mediaTypes:
- banner
- video
- native
userSync:
# TheTradeDesk supports user syncing, but requires configuration by the host. Contact a technical account manager
# or this bidder directly at the email address in this file to ask about enabling user sync.
supports:
- redirect
- iframe
openrtb:
gpp-supported: true
Loading
Loading