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

Commit

Permalink
add zeroclickfraud adapter (prebid#1207)
Browse files Browse the repository at this point in the history
* add zeroclickfraud adapter

* fixes for PR

* fix casing of Zeroclickfraud
  • Loading branch information
htang555 authored Mar 12, 2020
1 parent a7aa7ee commit 5c15d43
Show file tree
Hide file tree
Showing 26 changed files with 1,241 additions and 0 deletions.
12 changes: 12 additions & 0 deletions adapters/zeroclickfraud/usersync.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package zeroclickfraud

import (
"text/template"

"github.com/prebid/prebid-server/adapters"
"github.com/prebid/prebid-server/usersync"
)

func NewZeroClickFraudSyncer(temp *template.Template) usersync.Usersyncer {
return adapters.NewSyncer("zeroclickfraud", 0, temp, adapters.SyncTypeIframe)
}
34 changes: 34 additions & 0 deletions adapters/zeroclickfraud/usersync_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package zeroclickfraud

import (
"testing"
"text/template"

"github.com/prebid/prebid-server/privacy"
"github.com/prebid/prebid-server/privacy/ccpa"
"github.com/prebid/prebid-server/privacy/gdpr"
"github.com/stretchr/testify/assert"
)

func TestZeroClickFraudSyncer(t *testing.T) {
syncURL := "https://s.0cf.io/sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r=https%3A%2F%2Flocalhost%3A8888%2Fsetuid%3Fbidder%3Dzeroclickfraud%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7Buid%7D"
syncURLTemplate := template.Must(
template.New("sync-template").Parse(syncURL),
)

syncer := NewZeroClickFraudSyncer(syncURLTemplate)
syncInfo, err := syncer.GetUsersyncInfo(privacy.Policies{
GDPR: gdpr.Policy{
Signal: "1",
Consent: "BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw",
},
CCPA: ccpa.Policy{
Value: "1NYN",
},
})

assert.NoError(t, err)
assert.Equal(t, "https://s.0cf.io/sync?gdpr=1&gdpr_consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw&us_privacy=1NYN&r=https%3A%2F%2Flocalhost%3A8888%2Fsetuid%3Fbidder%3Dzeroclickfraud%26gdpr%3D1%26gdpr_consent%3DBONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw%26uid%3D%24%7Buid%7D", syncInfo.URL)
assert.Equal(t, "iframe", syncInfo.Type)
assert.Equal(t, false, syncInfo.SupportCORS)
}
187 changes: 187 additions & 0 deletions adapters/zeroclickfraud/zeroclickfraud.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
package zeroclickfraud

import (
"encoding/json"
"fmt"
"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/macros"
"github.com/prebid/prebid-server/openrtb_ext"
"net/http"
"strconv"
"text/template"
)

type ZeroClickFraudAdapter struct {
EndpointTemplate template.Template
}

func (a *ZeroClickFraudAdapter) MakeRequests(request *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {

errs := make([]error, 0, len(request.Imp))
headers := http.Header{
"Content-Type": {"application/json"},
"Accept": {"application/json"},
}

// Pull the host and source ID info from the bidder params.
reqImps, err := splitImpressions(request.Imp)

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

requests := []*adapters.RequestData{}

for reqExt, reqImp := range reqImps {
request.Imp = reqImp
reqJson, err := json.Marshal(request)

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

urlParams := macros.EndpointTemplateParams{Host: reqExt.Host, SourceId: strconv.Itoa(reqExt.SourceId)}
url, err := macros.ResolveMacros(a.EndpointTemplate, urlParams)

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

request := adapters.RequestData{
Method: "POST",
Uri: url,
Body: reqJson,
Headers: headers}

requests = append(requests, &request)
}

return requests, errs
}

/*
internal original request in OpenRTB, external = result of us having converted it (what comes out of MakeRequests)
*/
func (a *ZeroClickFraudAdapter) MakeBids(
internalRequest *openrtb.BidRequest,
externalRequest *adapters.RequestData,
response *adapters.ResponseData,
) (*adapters.BidderResponse, []error) {

if response.StatusCode == http.StatusNoContent {
return nil, nil
}

if response.StatusCode == http.StatusBadRequest {
return nil, []error{&errortypes.BadInput{
Message: fmt.Sprintf("ERR, bad input %d", response.StatusCode),
}}
} else if response.StatusCode != http.StatusOK {
return nil, []error{&errortypes.BadServerResponse{
Message: fmt.Sprintf("ERR, response with status %d", response.StatusCode),
}}
}

var bidResp openrtb.BidResponse

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

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

for _, seatBid := range bidResp.SeatBid {
for i := 0; i < len(seatBid.Bid); i++ {
bid := seatBid.Bid[i]
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
Bid: &bid,
BidType: getMediaType(bid.ImpID, internalRequest.Imp),
})
}
}

return bidResponse, nil
}

func splitImpressions(imps []openrtb.Imp) (map[openrtb_ext.ExtImpZeroClickFraud][]openrtb.Imp, error) {

var m = make(map[openrtb_ext.ExtImpZeroClickFraud][]openrtb.Imp)

for _, imp := range imps {
bidderParams, err := getBidderParams(&imp)
if err != nil {
return nil, err
}

m[*bidderParams] = append(m[*bidderParams], imp)
}

return m, nil
}

func getBidderParams(imp *openrtb.Imp) (*openrtb_ext.ExtImpZeroClickFraud, error) {
var bidderExt adapters.ExtImpBidder
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
return nil, &errortypes.BadInput{
Message: fmt.Sprintf("Missing bidder ext: %s", err.Error()),
}
}
var zeroclickfraudExt openrtb_ext.ExtImpZeroClickFraud
if err := json.Unmarshal(bidderExt.Bidder, &zeroclickfraudExt); err != nil {
return nil, &errortypes.BadInput{
Message: fmt.Sprintf("Cannot Resolve host or sourceId: %s", err.Error()),
}
}

if zeroclickfraudExt.SourceId < 1 {
return nil, &errortypes.BadInput{
Message: "Invalid/Missing SourceId",
}
}

if len(zeroclickfraudExt.Host) < 1 {
return nil, &errortypes.BadInput{
Message: "Invalid/Missing Host",
}
}

return &zeroclickfraudExt, nil
}

func getMediaType(impID string, imps []openrtb.Imp) openrtb_ext.BidType {

bidType := openrtb_ext.BidTypeBanner

for _, imp := range imps {
if imp.ID == impID {
if imp.Video != nil {
bidType = openrtb_ext.BidTypeVideo
break
} else if imp.Native != nil {
bidType = openrtb_ext.BidTypeNative
break
} else {
bidType = openrtb_ext.BidTypeBanner
break
}
}
}

return bidType
}

func NewZeroClickFraudBidder(endpoint string) *ZeroClickFraudAdapter {
template, err := template.New("endpointTemplate").Parse(endpoint)
if err != nil {
glog.Fatal("Unable to parse endpoint url template")
return nil
}

return &ZeroClickFraudAdapter{EndpointTemplate: *template}
}
11 changes: 11 additions & 0 deletions adapters/zeroclickfraud/zeroclickfraud_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package zeroclickfraud

import (
"testing"

"github.com/prebid/prebid-server/adapters/adapterstest"
)

func TestJsonSamples(t *testing.T) {
adapterstest.RunJSONBidderTest(t, "zeroclickfraudtest", NewZeroClickFraudBidder("http://{{.Host}}/openrtb2?sid={{.SourceId}}"))
}
Loading

0 comments on commit 5c15d43

Please sign in to comment.