Skip to content

Commit

Permalink
New Adapter: VideoByte (prebid#2058)
Browse files Browse the repository at this point in the history
  • Loading branch information
videobyte20 authored and shunj-nb committed Nov 8, 2022
1 parent 13d9c6b commit 230ffda
Show file tree
Hide file tree
Showing 21 changed files with 1,854 additions and 0 deletions.
55 changes: 55 additions & 0 deletions adapters/videobyte/params_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package videobyte

import (
"encoding/json"
"testing"

"github.com/prebid/prebid-server/openrtb_ext"
)

// This file actually intends to test static/bidder-params/videobyte.json
//
// These also validate the format of the external API: request.imp[i].ext.videobyte

// TestValidParams makes sure that the videobyte schema accepts all imp.ext fields which we intend to support.
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.BidderVideoByte, json.RawMessage(validParam)); err != nil {
t.Errorf("Schema rejected videobyte params: %s", validParam)
}
}
}

// TestInvalidParams makes sure that the videobyte schema rejects all the imp.ext fields we don't support.
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.BidderVideoByte, json.RawMessage(invalidParam)); err == nil {
t.Errorf("Schema allowed unexpected params: %s", invalidParam)
}
}
}

var validParams = []string{
`{"pubId": "123"}`,
`{"pubId": "123", "placementId": "4"}`,
`{"pubId": "123", "nid": "5"}`,
`{"pubId": "123", "placementId": "4", "nid": "5"}`,
}

var invalidParams = []string{
`{"invalidParam" : "a"}`,
`{}`,
`{"placementId": "4"}`,
`{"nid": "5"}`,
`{"placementId": "4", "nid": "5"}`,
}
158 changes: 158 additions & 0 deletions adapters/videobyte/videobyte.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package videobyte

import (
"encoding/json"
"fmt"
"net/http"
"net/url"

"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"

"github.com/mxmCherry/openrtb/v15/openrtb2"
)

type adapter struct {
endpoint string
}

func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) {
bidder := &adapter{
endpoint: config.Endpoint,
}
return bidder, nil
}

func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
impressions := request.Imp
adapterRequests := make([]*adapters.RequestData, 0, len(impressions))
var errs []error

for _, impression := range impressions {
impExt, err := parseExt(&impression)
if err != nil {
errs = append(errs, err)
continue
}

request.Imp = []openrtb2.Imp{impression}
body, err := json.Marshal(request)
if err != nil {
errs = append(errs, err)
continue
}

adapterRequests = append(adapterRequests, &adapters.RequestData{
Method: http.MethodPost,
Uri: a.endpoint + "?" + getParams(impExt).Encode(),
Body: body,
Headers: getHeaders(request),
})
}

request.Imp = impressions
return adapterRequests, errs
}

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("Bad user input: HTTP status %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 ortbResponse openrtb2.BidResponse
err := json.Unmarshal(response.Body, &ortbResponse)
if err != nil {
return nil, []error{&errortypes.BadServerResponse{
Message: "Bad Server Response",
}}
}

impIdToImp := make(map[string]*openrtb2.Imp)
for i := range internalRequest.Imp {
imp := internalRequest.Imp[i]
impIdToImp[imp.ID] = &imp
}

bidderResponse := adapters.NewBidderResponseWithBidsCapacity(1)
for _, seatBid := range ortbResponse.SeatBid {
for i := range seatBid.Bid {
bid := seatBid.Bid[i]
bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
Bid: &bid,
BidType: getMediaTypeForImp(impIdToImp[bid.ImpID]),
})
}
}

return bidderResponse, nil
}

func getMediaTypeForImp(imp *openrtb2.Imp) openrtb_ext.BidType {
if imp != nil && imp.Banner != nil {
return openrtb_ext.BidTypeBanner
}
return openrtb_ext.BidTypeVideo
}

func getParams(impExt *openrtb_ext.ExtImpVideoByte) url.Values {
params := url.Values{}
params.Add("source", "pbs")
params.Add("pid", impExt.PublisherId)
if impExt.PlacementId != "" {
params.Add("placementId", impExt.PlacementId)
}
if impExt.NetworkId != "" {
params.Add("nid", impExt.NetworkId)
}
return params
}

func getHeaders(request *openrtb2.BidRequest) http.Header {
headers := http.Header{}
headers.Add("Content-Type", "application/json;charset=utf-8")
headers.Add("Accept", "application/json")

if request.Site != nil {
if request.Site.Domain != "" {
headers.Add("Origin", request.Site.Domain)
}
if request.Site.Ref != "" {
headers.Set("Referer", request.Site.Ref)
}
}
return headers
}

func parseExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpVideoByte, error) {
var bidderExt adapters.ExtImpBidder

if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
return nil, &errortypes.BadInput{
Message: fmt.Sprintf("Ignoring imp id=%s, error while decoding extImpBidder, err: %s", imp.ID, err),
}
}

impExt := openrtb_ext.ExtImpVideoByte{}
err := json.Unmarshal(bidderExt.Bidder, &impExt)
if err != nil {
return nil, &errortypes.BadInput{
Message: fmt.Sprintf("Ignoring imp id=%s, error while decoding impExt, err: %s", imp.ID, err),
}
}

return &impExt, nil
}
17 changes: 17 additions & 0 deletions adapters/videobyte/videobyte_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package videobyte

import (
"testing"

"github.com/prebid/prebid-server/config"

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

func TestJsonSamples(t *testing.T) {
bidder, buildErr := Builder("videobyte", config.Adapter{Endpoint: "https://mock.videobyte.com"})
if buildErr != nil {
t.Fatalf("Builder returned unexpected error %v", buildErr)
}
adapterstest.RunJSONBidderTest(t, "videobytetest", bidder)
}
145 changes: 145 additions & 0 deletions adapters/videobyte/videobytetest/exemplary/banner.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
{
"mockBidRequest": {
"id": "test-request-id",
"bcat": [
"IAB25",
"IAB7-39",
"IAB8-18"
],
"user": {
"buyeruid": "user-101",
"yob": 1973
},
"device": {
"ua": "my-user-agent",
"ip": "1.2.3.4"
},
"imp": [
{
"id": "test-imp-id",
"banner": {
"w": 300,
"h": 250
},
"ext": {
"bidder": {
"pubId": "examplePublisherId",
"placementId": "examplePlacementId",
"nid": "exampleNetworkId"
}
}
}
],
"site": {
"domain": "example.com",
"page": "http://example.com/page-1",
"ref": "http://referer.com/page-2"
}
},
"httpCalls": [
{
"expectedRequest": {
"method": "GET",
"headers": {
"Referer": [
"http://referer.com/page-2"
],
"Origin": [
"example.com"
],
"Accept": [
"application/json"
],
"Content-Type": [
"application/json;charset=utf-8"
]
},
"uri": "https://mock.videobyte.com?nid=exampleNetworkId&pid=examplePublisherId&placementId=examplePlacementId&source=pbs",
"body": {
"id": "test-request-id",
"bcat": [
"IAB25",
"IAB7-39",
"IAB8-18"
],
"user": {
"buyeruid": "user-101",
"yob": 1973
},
"device": {
"ua": "my-user-agent",
"ip": "1.2.3.4"
},
"imp": [
{
"id": "test-imp-id",
"banner": {
"w": 300,
"h": 250
},
"ext": {
"bidder": {
"pubId": "examplePublisherId",
"placementId": "examplePlacementId",
"nid": "exampleNetworkId"
}
}
}
],
"site": {
"domain": "example.com",
"page": "http://example.com/page-1",
"ref": "http://referer.com/page-2"
}
}
},
"mockResponse": {
"status": 200,
"body": {
"cur": "USD",
"seatbid": [
{
"bid": [
{
"id": "fe69dd6d-e85c-4186-80a7-605b530bc23b",
"crid": "72745",
"adomain": [
"ad-domain.com"
],
"price": 3,
"impid": "test-imp-id",
"adid": "564",
"adm": "<iframe bordercolor=\"#000000\" frameborder=\"0\" height=\"250\" hspace=\"0\" marginheight=\"0\" marginwidth=\"0\" scrolling=\"no\" srcdoc=\"contents\" vspace=\"0\" width=\"300\"></iframe>"
}
],
"seat": "videobyte"
}
],
"bidid": "test-request-id",
"id": "test-request-id"
}
}
}
],
"expectedBidResponses": [
{
"currency": "USD",
"bids": [
{
"bid": {
"id": "fe69dd6d-e85c-4186-80a7-605b530bc23b",
"crid": "72745",
"adomain": [
"ad-domain.com"
],
"price": 3,
"impid": "test-imp-id",
"adid": "564",
"adm": "<iframe bordercolor=\"#000000\" frameborder=\"0\" height=\"250\" hspace=\"0\" marginheight=\"0\" marginwidth=\"0\" scrolling=\"no\" srcdoc=\"contents\" vspace=\"0\" width=\"300\"></iframe>"
},
"type": "banner"
}
]
}
]
}

0 comments on commit 230ffda

Please sign in to comment.