Skip to content

Commit

Permalink
New Adapter: operaads (prebid#1916)
Browse files Browse the repository at this point in the history
  • Loading branch information
jizeyopera authored and shunj-nb committed Nov 8, 2022
1 parent b94185e commit 291902a
Show file tree
Hide file tree
Showing 22 changed files with 1,327 additions and 0 deletions.
215 changes: 215 additions & 0 deletions adapters/operaads/operaads.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
package operaads

import (
"encoding/json"
"fmt"
"github.com/prebid/prebid-server/macros"
"net/http"
"text/template"

"github.com/mxmCherry/openrtb/v15/openrtb2"
"github.com/prebid/prebid-server/adapters"
"github.com/prebid/prebid-server/config"
"github.com/prebid/prebid-server/errortypes"
"github.com/prebid/prebid-server/openrtb_ext"
)

type adapter struct {
epTemplate *template.Template
}

// Builder builds a new instance of the operaads adapter for the given bidder with the given config.
func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
epTemplate, err := template.New("endpoint").Parse(config.Endpoint)
if err != nil {
return nil, err
}
bidder := &adapter{
epTemplate: epTemplate,
}
return bidder, nil
}

func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
impCount := len(request.Imp)
requestData := make([]*adapters.RequestData, 0, impCount)
errs := []error{}
headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")

err := checkRequest(request)
if err != nil {
errs = append(errs, err)
return nil, errs
}

for _, imp := range request.Imp {
requestCopy := *request
var bidderExt adapters.ExtImpBidder
if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
errs = append(errs, &errortypes.BadInput{
Message: err.Error(),
})
continue
}

var operaadsExt openrtb_ext.ImpExtOperaads
if err := json.Unmarshal(bidderExt.Bidder, &operaadsExt); err != nil {
errs = append(errs, &errortypes.BadInput{
Message: err.Error(),
})
continue
}

err := convertImpression(&imp)
if err != nil {
errs = append(errs, &errortypes.BadInput{
Message: err.Error(),
})
continue
}

imp.TagID = operaadsExt.PlacementID

requestCopy.Imp = []openrtb2.Imp{imp}
reqJSON, err := json.Marshal(&requestCopy)
if err != nil {
errs = append(errs, err)
return nil, errs
}

macro := macros.EndpointTemplateParams{PublisherID: operaadsExt.PublisherID, AccountID: operaadsExt.EndpointID}
endpoint, err := macros.ResolveMacros(*a.epTemplate, &macro)
if err != nil {
errs = append(errs, err)
continue
}
reqData := &adapters.RequestData{
Method: http.MethodPost,
Uri: endpoint,
Body: reqJSON,
Headers: headers,
}
requestData = append(requestData, reqData)
}
return requestData, errs
}

func checkRequest(request *openrtb2.BidRequest) error {
if request.Device == nil || len(request.Device.OS) == 0 {
return &errortypes.BadInput{
Message: "Impression is missing device OS information",
}
}

return nil
}

func convertImpression(imp *openrtb2.Imp) error {
if imp.Banner != nil {
bannerCopy, err := convertBanner(imp.Banner)
if err != nil {
return err
}
imp.Banner = bannerCopy
}
if imp.Native != nil && imp.Native.Request != "" {
v := make(map[string]interface{})
err := json.Unmarshal([]byte(imp.Native.Request), &v)
if err != nil {
return err
}
_, ok := v["native"]
if !ok {
body, err := json.Marshal(struct {
Native interface{} `json:"native"`
}{
Native: v,
})
if err != nil {
return err
}
native := *imp.Native
native.Request = string(body)
imp.Native = &native
}
}
return nil
}

// make sure that banner has openrtb 2.3-compatible size information
func convertBanner(banner *openrtb2.Banner) (*openrtb2.Banner, error) {
if banner.W == nil || banner.H == nil || *banner.W == 0 || *banner.H == 0 {
if len(banner.Format) > 0 {
f := banner.Format[0]

bannerCopy := *banner

bannerCopy.W = openrtb2.Int64Ptr(f.W)
bannerCopy.H = openrtb2.Int64Ptr(f.H)

return &bannerCopy, nil
} else {
return nil, &errortypes.BadInput{
Message: "Size information missing for banner",
}
}
}
return banner, nil
}

func (a *adapter) MakeBids(internalRequest *openrtb2.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("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
}}
}

if response.StatusCode != http.StatusOK {
return nil, []error{&errortypes.BadServerResponse{
Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
}}
}

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

bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)

for _, sb := range parsedResponse.SeatBid {
for i := 0; i < len(sb.Bid); i++ {
bid := sb.Bid[i]
if bid.Price != 0 {
bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
Bid: &bid,
BidType: getMediaTypeForImp(bid.ImpID, internalRequest.Imp),
})
}
}
}
return bidResponse, nil
}

func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType {
mediaType := openrtb_ext.BidTypeBanner
for _, imp := range imps {
if imp.ID == impId {
if imp.Video != nil {
mediaType = openrtb_ext.BidTypeVideo
} else if imp.Native != nil {
mediaType = openrtb_ext.BidTypeNative
}
return mediaType
}
}
return mediaType
}
20 changes: 20 additions & 0 deletions adapters/operaads/operaads_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package operaads

import (
"testing"

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

func TestJsonSamples(t *testing.T) {
bidder, buildErr := Builder(openrtb_ext.BidderOperaads, config.Adapter{
Endpoint: "http://example.com/operaads/ortb/v2/{{.PublisherID}}?ep={{.AccountID}}"})

if buildErr != nil {
t.Fatalf("Builder returned unexpected error %v", buildErr)
}

adapterstest.RunJSONBidderTest(t, "operaadstest", bidder)
}
137 changes: 137 additions & 0 deletions adapters/operaads/operaadstest/exemplary/native.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
{
"mockBidRequest":{
"id":"some-req-id",
"imp":[
{
"id":"some-imp-id",
"native":{
"request":"{\"native\":{\"ver\":\"1.1\",\"layout\":3,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":90}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":344,\"hmin\":194}},{\"id\":3,\"required\":1,\"img\":{\"type\":1,\"w\":128,\"wmin\":80,\"h\":128,\"hmin\":80}},{\"id\":4,\"required\":1,\"data\":{\"type\":2,\"len\":90}},{\"id\":6,\"data\":{\"type\":12,\"len\":15}}]}}",
"ver":"1.1"
},
"ext":{
"bidder":{
"placementId":"s123456",
"endpointId":"ep19978",
"publisherId":"pub123"
}
}
}
],
"site":{
"domain":"example.com",
"page":"example.com"
},
"device":{
"ip":"152.193.6.74",
"os":"android"
},
"user":{
"id":"db089de9-a62e-4861-a881-0ff15e052516",
"buyeruid":"8299345306627569435"
},
"tmax":500
},
"httpcalls":[
{
"expectedRequest":{
"uri":"http://example.com/operaads/ortb/v2/pub123?ep=ep19978",
"body":{
"id":"some-req-id",
"imp":[
{
"id":"some-imp-id",
"tagid":"s123456",
"ext":{
"bidder":{
"placementId":"s123456",
"endpointId":"ep19978",
"publisherId":"pub123"
}
},
"native":{
"request":"{\"native\":{\"ver\":\"1.1\",\"layout\":3,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":90}},{\"id\":2,\"required\":1,\"img\":{\"type\":3,\"wmin\":344,\"hmin\":194}},{\"id\":3,\"required\":1,\"img\":{\"type\":1,\"w\":128,\"wmin\":80,\"h\":128,\"hmin\":80}},{\"id\":4,\"required\":1,\"data\":{\"type\":2,\"len\":90}},{\"id\":6,\"data\":{\"type\":12,\"len\":15}}]}}",
"ver":"1.1"
}
}
],
"site":{
"domain":"example.com",
"page":"example.com"
},
"device":{
"ip":"152.193.6.74",
"os":"android"
},
"user":{
"id":"db089de9-a62e-4861-a881-0ff15e052516",
"buyeruid":"8299345306627569435"
},
"tmax":500
}
},
"mockResponse":{
"status":200,
"body":{
"id":"some-req-id",
"seatbid":[
{
"bid":[
{
"id":"928185755156387460",
"impid":"some-imp-id",
"price":1,
"adid":"69595837",
"adm":"{\"assets\":[{\"id\": 2,\"img\":{\"url\":\"http://example.com/p/creative-image/5e/b6/de/c3/5eb6dec3-4854-4dcd-980a-347f36ab502e.jpg\",\"w\": 344,\"h\": 194}},{\"id\": 3,\"img\":{\"url\":\"http://example.com/p/creative-image/5e/b6/de/c3/5eb6dec3-4854-4dcd-980a-347f36ab502e.jpg\",\"w\": 100,\"h\": 100}} ,{\"id\": 1,\"title\":{\"text\":\"This is an example Prebid Native creative\"}},{\"id\": 4,\"data\":{\"value\":\"This is a Prebid Native Creative. There are many like it, but this one is mine.\"}},{\"id\": 6,\"data\":{\"value\":\"Please.\"}} ],\"link\":{\"url\":\"http://example.com/click?AAAAAAAA8D8AAAAAAADwPwAAAAAAAAAAAAAAAAAA8D8AAAAAAADwPwhdYz3ZyNFNG3fXpZUyLXNZ0o5aAAAAACrElgC-AwAAvgMAAAIAAAC98iUEeP4QAAAAAABVU0QAVVNEAAEAAQARIAAAAAABAgQCAAAAAAEAhBaSXgAAAAA./pp=${AUCTION_PRICE}/cnd=%21OwwGAQiGmooHEL3llyEY-PxDIAQoADoRZGVmYXVsdCNOWU0yOjQwMjM./bn=75922/test=1/referrer=prebid.org/clickenc=http%3A%2F%2Fprebid.org%2Fdev-docs%2Fshow-native-ads.html\"},\"imptrackers\":[\"http://example.com/openrtb_win?e=wqT_3QLFBqBFAwAAAwDWAAUBCNmku9QFEIi6jeuTm_LoTRib7t2u2tLMlnMqNgkAAAECCPA_EQEHEAAA8D8ZCQkIAAAhCQkI8D8pEQkAMQkJqAAAMKqI2wQ4vgdAvgdIAlC95ZchWPj8Q2AAaJFAeJLRBIABAYoBA1VTRJIFBvBQmAEBoAEBqAEBsAEAuAECwAEEyAEC0AEJ2AEA4AEB8AEAigI7dWYoJ2EnLCAxMzc2ODYwLCAxNTE5MzA5NDAxKTt1ZigncicsIDY5NTk1ODM3Nh4A8IqSAvUBIXRETkdfUWlHbW9vSEVMM2xseUVZQUNENF9FTXdBRGdBUUFSSXZnZFFxb2piQkZnQVlMTURhQUJ3QUhnQWdBRUFpQUVBa0FFQm1BRUJvQUVCcUFFRHNBRUF1UUVwaTRpREFBRHdQOEVCS1l1SWd3QUE4RF9KQVhfelYzek1zXzBfMlFFQUFBAQMkRHdQLUFCQVBVQgEOLEFKZ0NBS0FDQUxVQwUQBEwwCQjwTE1BQ0FNZ0NBT0FDQU9nQ0FQZ0NBSUFEQVpBREFKZ0RBYWdEaHBxS0I3b0RFV1JsWm1GMWJIUWpUbGxOTWpvME1ESXqaAjkhT3d3R0FRNvgA8E4tUHhESUFRb0FEb1JaR1ZtWVhWc2RDTk9XVTB5T2pRd01qTS7YAugH4ALH0wHqAgpwcmViaWQub3Jn8gIRCgZBRFZfSUQSBzEzNzY4NjDyARQMQ1BHXwEUNDM1MDMwOTjyAhEKBUNQARPwmQgxNDg0NzIzOIADAYgDAZADAJgDFKADAaoDAMADkBzIAwDYAwDgAwDoAwD4AwOABACSBAkvb3BlbnJ0YjKYBACiBAwxNTIuMTkzLjYuNzSoBJrMI7IEDAgAEAAYACAAMAA4ALgEAMAEAMgEANIEEWRlZmF1bHQjTllNMjo0MDIz2gQCCADgBADwBL3llyGIBQGYBQCgBf____8FA1ABqgULc29tZS1yZXEtaWTABQDJBQAFARTwP9IFCQkFC2QAAADYBQHgBQHwBd4C-gUECAAQAJAGAZgGAA..&s=08b1535744639c904684afe46e3c6c0e4786089f&test=1&referrer=prebid.org&pp=${AUCTION_PRICE}\"]}",
"adomain":[
"example.com"
],
"iurl":"http://example.com/cr?id=69595837",
"cid":"958",
"crid":"69595837",
"cat":[
"IAB3-1"
],
"ext":{

}
}
],
"seat":"958"
}
],
"bidid":"8141327771600527856",
"cur":"USD"
}
}
}
],
"expectedBidResponses":[
{
"currency":"USD",
"bids":[
{
"bid":{
"id":"928185755156387460",
"impid":"some-imp-id",
"price":1,
"adm":"{\"assets\":[{\"id\": 2,\"img\":{\"url\":\"http://example.com/p/creative-image/5e/b6/de/c3/5eb6dec3-4854-4dcd-980a-347f36ab502e.jpg\",\"w\": 344,\"h\": 194}},{\"id\": 3,\"img\":{\"url\":\"http://example.com/p/creative-image/5e/b6/de/c3/5eb6dec3-4854-4dcd-980a-347f36ab502e.jpg\",\"w\": 100,\"h\": 100}} ,{\"id\": 1,\"title\":{\"text\":\"This is an example Prebid Native creative\"}},{\"id\": 4,\"data\":{\"value\":\"This is a Prebid Native Creative. There are many like it, but this one is mine.\"}},{\"id\": 6,\"data\":{\"value\":\"Please.\"}} ],\"link\":{\"url\":\"http://example.com/click?AAAAAAAA8D8AAAAAAADwPwAAAAAAAAAAAAAAAAAA8D8AAAAAAADwPwhdYz3ZyNFNG3fXpZUyLXNZ0o5aAAAAACrElgC-AwAAvgMAAAIAAAC98iUEeP4QAAAAAABVU0QAVVNEAAEAAQARIAAAAAABAgQCAAAAAAEAhBaSXgAAAAA./pp=${AUCTION_PRICE}/cnd=%21OwwGAQiGmooHEL3llyEY-PxDIAQoADoRZGVmYXVsdCNOWU0yOjQwMjM./bn=75922/test=1/referrer=prebid.org/clickenc=http%3A%2F%2Fprebid.org%2Fdev-docs%2Fshow-native-ads.html\"},\"imptrackers\":[\"http://example.com/openrtb_win?e=wqT_3QLFBqBFAwAAAwDWAAUBCNmku9QFEIi6jeuTm_LoTRib7t2u2tLMlnMqNgkAAAECCPA_EQEHEAAA8D8ZCQkIAAAhCQkI8D8pEQkAMQkJqAAAMKqI2wQ4vgdAvgdIAlC95ZchWPj8Q2AAaJFAeJLRBIABAYoBA1VTRJIFBvBQmAEBoAEBqAEBsAEAuAECwAEEyAEC0AEJ2AEA4AEB8AEAigI7dWYoJ2EnLCAxMzc2ODYwLCAxNTE5MzA5NDAxKTt1ZigncicsIDY5NTk1ODM3Nh4A8IqSAvUBIXRETkdfUWlHbW9vSEVMM2xseUVZQUNENF9FTXdBRGdBUUFSSXZnZFFxb2piQkZnQVlMTURhQUJ3QUhnQWdBRUFpQUVBa0FFQm1BRUJvQUVCcUFFRHNBRUF1UUVwaTRpREFBRHdQOEVCS1l1SWd3QUE4RF9KQVhfelYzek1zXzBfMlFFQUFBAQMkRHdQLUFCQVBVQgEOLEFKZ0NBS0FDQUxVQwUQBEwwCQjwTE1BQ0FNZ0NBT0FDQU9nQ0FQZ0NBSUFEQVpBREFKZ0RBYWdEaHBxS0I3b0RFV1JsWm1GMWJIUWpUbGxOTWpvME1ESXqaAjkhT3d3R0FRNvgA8E4tUHhESUFRb0FEb1JaR1ZtWVhWc2RDTk9XVTB5T2pRd01qTS7YAugH4ALH0wHqAgpwcmViaWQub3Jn8gIRCgZBRFZfSUQSBzEzNzY4NjDyARQMQ1BHXwEUNDM1MDMwOTjyAhEKBUNQARPwmQgxNDg0NzIzOIADAYgDAZADAJgDFKADAaoDAMADkBzIAwDYAwDgAwDoAwD4AwOABACSBAkvb3BlbnJ0YjKYBACiBAwxNTIuMTkzLjYuNzSoBJrMI7IEDAgAEAAYACAAMAA4ALgEAMAEAMgEANIEEWRlZmF1bHQjTllNMjo0MDIz2gQCCADgBADwBL3llyGIBQGYBQCgBf____8FA1ABqgULc29tZS1yZXEtaWTABQDJBQAFARTwP9IFCQkFC2QAAADYBQHgBQHwBd4C-gUECAAQAJAGAZgGAA..&s=08b1535744639c904684afe46e3c6c0e4786089f&test=1&referrer=prebid.org&pp=${AUCTION_PRICE}\"]}",
"adid":"69595837",
"adomain":[
"example.com"
],
"iurl":"http://example.com/cr?id=69595837",
"cid":"958",
"crid":"69595837",
"cat":[
"IAB3-1"
],
"ext":{

}
},
"type":"native"
}
]
}
]
}
Loading

0 comments on commit 291902a

Please sign in to comment.