From d48e042966ab9489d5d14a5887e4cd46adf8eeb4 Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Fri, 31 Oct 2025 09:27:10 +0100 Subject: [PATCH 01/15] New Adapter: targetVideo --- adapters/targetVideo/params_test.go | 48 +++++ adapters/targetVideo/targetvideo.go | 194 ++++++++++++++++++ adapters/targetVideo/targetvideo_test.go | 20 ++ .../targetvideotest/exemplary/app-video.json | 130 ++++++++++++ .../targetvideotest/exemplary/video.json | 122 +++++++++++ .../targetvideotest/supplemental/204.json | 85 ++++++++ .../targetvideotest/supplemental/400.json | 90 ++++++++ .../supplemental/no_seatbid.json | 89 ++++++++ exchange/adapter_builders.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_targetvideo.go | 6 + static/bidder-info/targetVideo.yaml | 18 ++ static/bidder-params/targetVideo.json | 14 ++ 13 files changed, 820 insertions(+) create mode 100644 adapters/targetVideo/params_test.go create mode 100644 adapters/targetVideo/targetvideo.go create mode 100644 adapters/targetVideo/targetvideo_test.go create mode 100644 adapters/targetVideo/targetvideotest/exemplary/app-video.json create mode 100644 adapters/targetVideo/targetvideotest/exemplary/video.json create mode 100644 adapters/targetVideo/targetvideotest/supplemental/204.json create mode 100644 adapters/targetVideo/targetvideotest/supplemental/400.json create mode 100644 adapters/targetVideo/targetvideotest/supplemental/no_seatbid.json create mode 100644 openrtb_ext/imp_targetvideo.go create mode 100644 static/bidder-info/targetVideo.yaml create mode 100644 static/bidder-params/targetVideo.json diff --git a/adapters/targetVideo/params_test.go b/adapters/targetVideo/params_test.go new file mode 100644 index 00000000000..7c1291f4d2a --- /dev/null +++ b/adapters/targetVideo/params_test.go @@ -0,0 +1,48 @@ +package targetVideo + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v3/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.BidderTargetVideo, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected targetVideo 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.BidderTargetVideo, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"placementId":846}`, + `{"placementId":"846"}`, +} + +var invalidParams = []string{ + `null`, + `nil`, + `undefined`, + `{"placementId": "%9"}`, + `{"publisherId": "as9""}`, + `{"placementId": true}`, +} diff --git a/adapters/targetVideo/targetvideo.go b/adapters/targetVideo/targetvideo.go new file mode 100644 index 00000000000..5e3a13c9eea --- /dev/null +++ b/adapters/targetVideo/targetvideo.go @@ -0,0 +1,194 @@ +package targetVideo + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/prebid/openrtb/v20/openrtb2" + "github.com/prebid/prebid-server/v3/adapters" + "github.com/prebid/prebid-server/v3/config" + "github.com/prebid/prebid-server/v3/errortypes" + "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/prebid/prebid-server/v3/util/jsonutil" +) + +type TargetVideoAdapter struct { + endpoint string +} + +type impExt struct { + TargetVideo openrtb_ext.ExtImpTargetVideo `json:"targetVideo"` +} + +func (a *TargetVideoAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + totalImps := len(request.Imp) + errors := make([]error, 0) + adapterRequests := make([]*adapters.RequestData, 0, totalImps) + + // Split multi-imp request into multiple ad server requests. SRA is currently not recommended. + for i := 0; i < totalImps; i++ { + if adapterReq, err := a.makeRequest(*request, request.Imp[i]); err == nil { + adapterRequests = append(adapterRequests, adapterReq) + } else { + errors = append(errors, err) + } + } + + return adapterRequests, errors +} + +func (a *TargetVideoAdapter) makeRequest(request openrtb2.BidRequest, imp openrtb2.Imp) (*adapters.RequestData, error) { + + // For now, this adapter sends one imp per request, but we still + // iterate over all imps in the request to perform the required + // imp.ext transformation. + request.Imp = []openrtb2.Imp{imp} + + _, errImp := validateImpAndSetExt(&imp) + if errImp != nil { + return nil, errImp + } + + for i := range request.Imp { + if len(request.Imp[i].Ext) == 0 { + continue + } + var root map[string]json.RawMessage + if err := json.Unmarshal(request.Imp[i].Ext, &root); err != nil { + // If ext cannot be parsed, skip transformation for this imp + continue + } + + // Try to extract placementId from ext.bidder.targetVideo (or targetvideo) + placementId := "" + if bRaw, ok := root["bidder"]; ok && len(bRaw) > 0 { + var bidder map[string]json.RawMessage + if err := json.Unmarshal(bRaw, &bidder); err == nil { + if placementIdRaw, ok := bidder["placementId"]; ok && len(placementIdRaw) > 0 { + + var asStr string + var asInt int64 + if err := json.Unmarshal(placementIdRaw, &asStr); err == nil && asStr != "" { + placementId = asStr + } else if err := json.Unmarshal(placementIdRaw, &asInt); err == nil { + placementId = fmt.Sprintf("%d", asInt) + } + + } + } + // Remove bidder node as required + delete(root, "bidder") + } + + // If we obtained a placementId, set ext.prebid.storedrequest.id = placementId + if placementId != "" { + // Build prebid.storedrequest structure, preserving existing prebid if any + var prebid map[string]json.RawMessage + if pr, ok := root["prebid"]; ok && len(pr) > 0 { + _ = json.Unmarshal(pr, &prebid) + } + if prebid == nil { + prebid = make(map[string]json.RawMessage) + } + stored := map[string]string{"id": placementId} + storedRaw, _ := json.Marshal(stored) + prebid["storedrequest"] = storedRaw + prebidRaw, _ := json.Marshal(prebid) + root["prebid"] = prebidRaw + } + + // Marshal back the transformed ext + if newExt, err := json.Marshal(root); err == nil { + request.Imp[i].Ext = newExt + } + } + + reqJSON, err := json.Marshal(request) + if err != nil { + return nil, err + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + + //fmt.Println("TARGET VIDEO reqJson: ", string(reqJSON)) + + return &adapters.RequestData{ + Method: "POST", + Uri: a.endpoint, + Body: reqJSON, + Headers: headers, + ImpIDs: openrtb_ext.GetImpIDs(request.Imp), + }, nil +} + +func (a *TargetVideoAdapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.RequestData, httpRes *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if httpRes.StatusCode == http.StatusNoContent { + return nil, nil + } + if httpRes.StatusCode == http.StatusBadRequest { + return nil, []error{&errortypes.BadInput{Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", httpRes.StatusCode)}} + } + + var bidResp openrtb2.BidResponse + if err := jsonutil.Unmarshal(httpRes.Body, &bidResp); err != nil { + return nil, []error{&errortypes.BadServerResponse{Message: fmt.Sprintf("error while decoding response, err: %s", err)}} + } + + if len(bidResp.SeatBid) == 0 { + return nil, nil + } + + if len(bidResp.SeatBid[0].Bid) == 0 { + return nil, nil + } + + br := adapters.NewBidderResponse() + errs := []error{} + + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + bid := sb.Bid[i] + // Ensure imp exists and is video + mediaType := openrtb_ext.BidTypeVideo + for _, imp := range bidReq.Imp { + if imp.ID == bid.ImpID { + if imp.Video == nil { + // Not a video impression; skip + errs = append(errs, &errortypes.BadServerResponse{Message: fmt.Sprintf("ignoring bid id=%s for non-video imp id=%s", bid.ID, bid.ImpID)}) + mediaType = "" + } + break + } + } + + br.Bids = append(br.Bids, &adapters.TypedBid{Bid: &bid, BidType: mediaType}) + } + } + return br, errs +} + +func validateImpAndSetExt(imp *openrtb2.Imp) (int, error) { + if imp.Video == nil { + return 0, &errortypes.BadInput{Message: fmt.Sprintf("Only video impressions are supported by targetvideo. ImpID=%s", imp.ID)} + } + if len(imp.Ext) == 0 { + return 0, &errortypes.BadInput{Message: fmt.Sprintf("imp.ext is required and must contain bidder params for targetvideo. ImpID=%s", imp.ID)} + } + var ext impExt + if err := jsonutil.Unmarshal(imp.Ext, &ext); err != nil { + return 0, &errortypes.BadInput{Message: fmt.Sprintf("error parsing imp.ext for targetvideo, err: %s", err)} + } + + return 0, nil +} + +func Builder(bidderName openrtb_ext.BidderName, cfg config.Adapter, server config.Server) (adapters.Bidder, error) { + + bidder := &TargetVideoAdapter{ + endpoint: cfg.Endpoint, + } + return bidder, nil +} diff --git a/adapters/targetVideo/targetvideo_test.go b/adapters/targetVideo/targetvideo_test.go new file mode 100644 index 00000000000..c25a50014be --- /dev/null +++ b/adapters/targetVideo/targetvideo_test.go @@ -0,0 +1,20 @@ +package targetVideo + +import ( + "testing" + + "github.com/prebid/prebid-server/v3/adapters/adapterstest" + "github.com/prebid/prebid-server/v3/config" + "github.com/prebid/prebid-server/v3/openrtb_ext" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderTargetVideo, config.Adapter{ + Endpoint: "http://localhost/pbs"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "targetvideotest", bidder) +} diff --git a/adapters/targetVideo/targetvideotest/exemplary/app-video.json b/adapters/targetVideo/targetvideotest/exemplary/app-video.json new file mode 100644 index 00000000000..956da513e58 --- /dev/null +++ b/adapters/targetVideo/targetvideotest/exemplary/app-video.json @@ -0,0 +1,130 @@ +{ + "mockBidRequest": { + "id": "test-request-id-video", + "app": { + "id": "appID", + "publisher": { + "id": "uniq_pub_id" + } + }, + "cur": ["EUR"], + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "bidder": { + "placementId": "77777" + } + } + } + ], + "device": { + "ua": "test-user-agent" + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": ["application/json"], + "Content-Type": ["application/json;charset=utf-8"] + }, + "uri": "http://localhost/pbs", + "body": { + "id": "test-request-id-video", + "app": { + "id": "appID", + "publisher": { + "id": "uniq_pub_id" + } + }, + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "77777" + } + } + } + } + ], + "device": { + "ua": "test-user-agent" + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + }, + "cur": ["EUR"] + }, + "impIDs":["test-imp-id-video"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id-video", + "seatbid": [ + { + "seat": "targetVideo", + "bid": [ + { + "id": "randomID", + "impid": "test-imp-id-video", + "price": 5.5, + "adid": "12345678", + "adm": "test-imp-id-video", + "cid": "789", + "crid": "12345678", + "h": 360, + "w": 640 + } + ] + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids" : [{ + "bid": { + "id": "randomID", + "adid": "12345678", + "impid": "test-imp-id-video", + "price": 5.5, + "adm": "test-imp-id-video", + "crid": "12345678", + "cid": "789", + "h": 360, + "w": 640 + }, + "type": "video" + }] + } + ] +} diff --git a/adapters/targetVideo/targetvideotest/exemplary/video.json b/adapters/targetVideo/targetvideotest/exemplary/video.json new file mode 100644 index 00000000000..fcb6e34ed3d --- /dev/null +++ b/adapters/targetVideo/targetvideotest/exemplary/video.json @@ -0,0 +1,122 @@ +{ + "mockBidRequest": { + "id": "test-request-id-video", + "cur": ["EUR"], + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "bidder": { + "placementId": "77777" + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": ["application/json"], + "Content-Type": ["application/json;charset=utf-8"] + }, + "uri": "http://localhost/pbs", + "body": { + "id": "test-request-id-video", + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "77777" + } + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + }, + "cur": ["EUR"] + }, + "impIDs":["test-imp-id-video"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id-video", + "seatbid": [ + { + "seat": "targetVideo", + "bid": [ + { + "id": "randomID", + "impid": "test-imp-id-video", + "price": 5.5, + "adm": "test-imp-id-video", + "cid": "789", + "crid": "12345678", + "h": 360, + "w": 640 + } + ] + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids" : [{ + "bid": { + "id": "randomID", + "impid": "test-imp-id-video", + "price": 5.5, + "adm": "test-imp-id-video", + "crid": "12345678", + "cid": "789", + "h": 360, + "w": 640 + }, + "type": "video" + }] + } + ] +} diff --git a/adapters/targetVideo/targetvideotest/supplemental/204.json b/adapters/targetVideo/targetvideotest/supplemental/204.json new file mode 100644 index 00000000000..c64d797b32c --- /dev/null +++ b/adapters/targetVideo/targetvideotest/supplemental/204.json @@ -0,0 +1,85 @@ +{ + "mockBidRequest": { + "id": "test-request-id-video", + "cur": ["EUR"], + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "bidder": { + "placementId": "77777" + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": ["application/json"], + "Content-Type": ["application/json;charset=utf-8"] + }, + "uri": "http://localhost/pbs", + "body": { + "id": "test-request-id-video", + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "77777" + } + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + }, + "cur": ["EUR"] + }, + "impIDs":["test-imp-id-video"] + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/targetVideo/targetvideotest/supplemental/400.json b/adapters/targetVideo/targetvideotest/supplemental/400.json new file mode 100644 index 00000000000..e69838a3c1d --- /dev/null +++ b/adapters/targetVideo/targetvideotest/supplemental/400.json @@ -0,0 +1,90 @@ +{ + "mockBidRequest": { + "id": "test-request-id-video", + "cur": ["EUR"], + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "bidder": { + "placementId": "77777" + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": ["application/json"], + "Content-Type": ["application/json;charset=utf-8"] + }, + "uri": "http://localhost/pbs", + "body": { + "id": "test-request-id-video", + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "77777" + } + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + }, + "cur": ["EUR"] + }, + "impIDs":["test-imp-id-video"] + }, + "mockResponse": { + "status": 400, + "body": {} + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/targetVideo/targetvideotest/supplemental/no_seatbid.json b/adapters/targetVideo/targetvideotest/supplemental/no_seatbid.json new file mode 100644 index 00000000000..329c2ae49ae --- /dev/null +++ b/adapters/targetVideo/targetvideotest/supplemental/no_seatbid.json @@ -0,0 +1,89 @@ +{ + "mockBidRequest": { + "id": "test-request-id-video", + "cur": ["EUR"], + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "bidder": { + "placementId": "77777" + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": ["application/json"], + "Content-Type": ["application/json;charset=utf-8"] + }, + "uri": "http://localhost/pbs", + "body": { + "id": "test-request-id-video", + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "77777" + } + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + }, + "cur": ["EUR"] + }, + "impIDs":["test-imp-id-video"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [] +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index aa578b089ff..802d91aba39 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -226,6 +226,7 @@ import ( "github.com/prebid/prebid-server/v3/adapters/stroeerCore" "github.com/prebid/prebid-server/v3/adapters/taboola" "github.com/prebid/prebid-server/v3/adapters/tappx" + "github.com/prebid/prebid-server/v3/adapters/targetVideo" "github.com/prebid/prebid-server/v3/adapters/teads" "github.com/prebid/prebid-server/v3/adapters/telaria" "github.com/prebid/prebid-server/v3/adapters/teqblaze" @@ -496,6 +497,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderStroeerCore: stroeerCore.Builder, openrtb_ext.BidderTaboola: taboola.Builder, openrtb_ext.BidderTappx: tappx.Builder, + openrtb_ext.BidderTargetVideo: targetVideo.Builder, openrtb_ext.BidderTeads: teads.Builder, openrtb_ext.BidderTelaria: telaria.Builder, openrtb_ext.BidderTeqBlaze: teqblaze.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 4469ed3f2fd..4312afcdcb8 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -244,6 +244,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderStroeerCore, BidderTaboola, BidderTappx, + BidderTargetVideo, BidderTeads, BidderTelaria, BidderTeqBlaze, @@ -618,6 +619,7 @@ const ( BidderStroeerCore BidderName = "stroeerCore" BidderTaboola BidderName = "taboola" BidderTappx BidderName = "tappx" + BidderTargetVideo BidderName = "targetVideo" BidderTeads BidderName = "teads" BidderTelaria BidderName = "telaria" BidderTeqBlaze BidderName = "teqblaze" diff --git a/openrtb_ext/imp_targetvideo.go b/openrtb_ext/imp_targetvideo.go new file mode 100644 index 00000000000..3fbef3c1e14 --- /dev/null +++ b/openrtb_ext/imp_targetvideo.go @@ -0,0 +1,6 @@ +package openrtb_ext + +// ExtImpTargetVideo defines the contract for bidrequest.imp[i].ext.prebid.bidder.targetVideo +type ExtImpTargetVideo struct { + PlacementId int `json:"placementId,omitempty"` +} diff --git a/static/bidder-info/targetVideo.yaml b/static/bidder-info/targetVideo.yaml new file mode 100644 index 00000000000..bc54a568bec --- /dev/null +++ b/static/bidder-info/targetVideo.yaml @@ -0,0 +1,18 @@ +#endpoint: "https://pbs.prebrid.tv/openrtb2/auction" +endpoint: "http://localhost:8000/openrtb2/auction" +geoscope: + - global +maintainer: + email: "contact@target-video.com" +gvlVendorID: 786 +capabilities: + app: + mediaTypes: + - video + site: + mediaTypes: + - video +userSync: + redirect: + url: "https://pbs.prebrid.tv/user_sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redirect={{.RedirectURL}}" + userMacro: "[TVUID]" diff --git a/static/bidder-params/targetVideo.json b/static/bidder-params/targetVideo.json new file mode 100644 index 00000000000..86c73b4bb7b --- /dev/null +++ b/static/bidder-params/targetVideo.json @@ -0,0 +1,14 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "TargetVideo Adapter Params", + "description": "A schema which validates params accepted by the TargetVideo adapter", + "type": "object", + "properties": { + "placementId": { + "type": ["integer", "string"], + "pattern": "^\\d+$", + "description": "An ID which identifies this placement of the impression" + } + }, + "required": ["placementId"] +} From 197d70415d4f8a09c6b8999782e2fc9f447b8b25 Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Fri, 31 Oct 2025 09:41:35 +0100 Subject: [PATCH 02/15] endpoint fix --- static/bidder-info/targetVideo.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/static/bidder-info/targetVideo.yaml b/static/bidder-info/targetVideo.yaml index bc54a568bec..decc110db3e 100644 --- a/static/bidder-info/targetVideo.yaml +++ b/static/bidder-info/targetVideo.yaml @@ -1,5 +1,4 @@ -#endpoint: "https://pbs.prebrid.tv/openrtb2/auction" -endpoint: "http://localhost:8000/openrtb2/auction" +endpoint: "https://pbs.prebrid.tv/openrtb2/auction" geoscope: - global maintainer: From 85c95b5c83eee98ec64c8662830b20edea178efe Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Fri, 31 Oct 2025 09:54:44 +0100 Subject: [PATCH 03/15] adapter redundancy fix --- adapters/targetVideo/targetvideo.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/adapters/targetVideo/targetvideo.go b/adapters/targetVideo/targetvideo.go index 5e3a13c9eea..3a1aa5a84b5 100644 --- a/adapters/targetVideo/targetvideo.go +++ b/adapters/targetVideo/targetvideo.go @@ -13,7 +13,7 @@ import ( "github.com/prebid/prebid-server/v3/util/jsonutil" ) -type TargetVideoAdapter struct { +type adapter struct { endpoint string } @@ -21,7 +21,7 @@ type impExt struct { TargetVideo openrtb_ext.ExtImpTargetVideo `json:"targetVideo"` } -func (a *TargetVideoAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { totalImps := len(request.Imp) errors := make([]error, 0) adapterRequests := make([]*adapters.RequestData, 0, totalImps) @@ -38,7 +38,7 @@ func (a *TargetVideoAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo return adapterRequests, errors } -func (a *TargetVideoAdapter) makeRequest(request openrtb2.BidRequest, imp openrtb2.Imp) (*adapters.RequestData, error) { +func (a *adapter) makeRequest(request openrtb2.BidRequest, imp openrtb2.Imp) (*adapters.RequestData, error) { // For now, this adapter sends one imp per request, but we still // iterate over all imps in the request to perform the required @@ -124,7 +124,7 @@ func (a *TargetVideoAdapter) makeRequest(request openrtb2.BidRequest, imp openrt }, nil } -func (a *TargetVideoAdapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.RequestData, httpRes *adapters.ResponseData) (*adapters.BidderResponse, []error) { +func (a *adapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.RequestData, httpRes *adapters.ResponseData) (*adapters.BidderResponse, []error) { if httpRes.StatusCode == http.StatusNoContent { return nil, nil } @@ -187,7 +187,7 @@ func validateImpAndSetExt(imp *openrtb2.Imp) (int, error) { func Builder(bidderName openrtb_ext.BidderName, cfg config.Adapter, server config.Server) (adapters.Bidder, error) { - bidder := &TargetVideoAdapter{ + bidder := &adapter{ endpoint: cfg.Endpoint, } return bidder, nil From b5eebc5eb6c7321ac8234da3d2760b2195173c26 Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Fri, 21 Nov 2025 13:42:37 +0100 Subject: [PATCH 04/15] code simplification, unnecessary code removed, tests fixed --- adapters/targetVideo/params_test.go | 1 + adapters/targetVideo/targetvideo.go | 127 ++++++------------ .../targetvideotest/exemplary/app-video.json | 14 -- .../supplemental/ext-bidder-error.json | 41 ++++++ .../supplemental/no_seatbid.json | 4 +- openrtb_ext/imp_targetvideo.go | 4 +- static/bidder-info/targetVideo.yaml | 2 +- 7 files changed, 90 insertions(+), 103 deletions(-) create mode 100644 adapters/targetVideo/targetvideotest/supplemental/ext-bidder-error.json diff --git a/adapters/targetVideo/params_test.go b/adapters/targetVideo/params_test.go index 7c1291f4d2a..185b1bb75bb 100644 --- a/adapters/targetVideo/params_test.go +++ b/adapters/targetVideo/params_test.go @@ -45,4 +45,5 @@ var invalidParams = []string{ `{"placementId": "%9"}`, `{"publisherId": "as9""}`, `{"placementId": true}`, + `{"placementId": ""}`, } diff --git a/adapters/targetVideo/targetvideo.go b/adapters/targetVideo/targetvideo.go index 3a1aa5a84b5..43cf30c2960 100644 --- a/adapters/targetVideo/targetvideo.go +++ b/adapters/targetVideo/targetvideo.go @@ -21,6 +21,13 @@ type impExt struct { TargetVideo openrtb_ext.ExtImpTargetVideo `json:"targetVideo"` } +type impExtBidder struct { + TargetVideo openrtb_ext.ExtImpTargetVideo `json:"targetVideo"` +} +type impExtPrebid struct { + Prebid *openrtb_ext.ExtImpPrebid `json:"prebid,omitempty"` +} + func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { totalImps := len(request.Imp) errors := make([]error, 0) @@ -45,63 +52,36 @@ func (a *adapter) makeRequest(request openrtb2.BidRequest, imp openrtb2.Imp) (*a // imp.ext transformation. request.Imp = []openrtb2.Imp{imp} - _, errImp := validateImpAndSetExt(&imp) - if errImp != nil { - return nil, errImp - } - for i := range request.Imp { - if len(request.Imp[i].Ext) == 0 { - continue + + var extBidder adapters.ExtImpBidder + if err := jsonutil.Unmarshal(imp.Ext, &extBidder); err != nil { + return nil, &errortypes.BadInput{Message: fmt.Sprintf("error parsing imp.ext, err: %s", err)} } - var root map[string]json.RawMessage - if err := json.Unmarshal(request.Imp[i].Ext, &root); err != nil { - // If ext cannot be parsed, skip transformation for this imp - continue + var extImpTargetVideo openrtb_ext.ExtImpTargetVideo + if err := jsonutil.Unmarshal(extBidder.Bidder, &extImpTargetVideo); err != nil { + return nil, &errortypes.BadInput{Message: fmt.Sprintf("error parsing imp.ext.bidder, err: %s", err)} } - - // Try to extract placementId from ext.bidder.targetVideo (or targetvideo) - placementId := "" - if bRaw, ok := root["bidder"]; ok && len(bRaw) > 0 { - var bidder map[string]json.RawMessage - if err := json.Unmarshal(bRaw, &bidder); err == nil { - if placementIdRaw, ok := bidder["placementId"]; ok && len(placementIdRaw) > 0 { - - var asStr string - var asInt int64 - if err := json.Unmarshal(placementIdRaw, &asStr); err == nil && asStr != "" { - placementId = asStr - } else if err := json.Unmarshal(placementIdRaw, &asInt); err == nil { - placementId = fmt.Sprintf("%d", asInt) - } - - } - } - // Remove bidder node as required - delete(root, "bidder") + var prebid *openrtb_ext.ExtImpPrebid + if extBidder.Prebid == nil { + prebid = &openrtb_ext.ExtImpPrebid{} + } + if prebid.StoredRequest == nil { + prebid.StoredRequest = &openrtb_ext.ExtStoredRequest{} } + prebid.StoredRequest.ID = fmt.Sprintf("%d", extImpTargetVideo.PlacementId) - // If we obtained a placementId, set ext.prebid.storedrequest.id = placementId - if placementId != "" { - // Build prebid.storedrequest structure, preserving existing prebid if any - var prebid map[string]json.RawMessage - if pr, ok := root["prebid"]; ok && len(pr) > 0 { - _ = json.Unmarshal(pr, &prebid) - } - if prebid == nil { - prebid = make(map[string]json.RawMessage) - } - stored := map[string]string{"id": placementId} - storedRaw, _ := json.Marshal(stored) - prebid["storedrequest"] = storedRaw - prebidRaw, _ := json.Marshal(prebid) - root["prebid"] = prebidRaw + ext := impExtPrebid{ + Prebid: prebid, } - // Marshal back the transformed ext - if newExt, err := json.Marshal(root); err == nil { - request.Imp[i].Ext = newExt + extRaw, err := jsonutil.Marshal(ext) + if err != nil { + return nil, &errortypes.BadInput{Message: fmt.Sprintf("error building imp.ext, err: %s", err)} } + + request.Imp[i].Ext = extRaw + } reqJSON, err := json.Marshal(request) @@ -125,24 +105,16 @@ func (a *adapter) makeRequest(request openrtb2.BidRequest, imp openrtb2.Imp) (*a } func (a *adapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.RequestData, httpRes *adapters.ResponseData) (*adapters.BidderResponse, []error) { - if httpRes.StatusCode == http.StatusNoContent { + if adapters.IsResponseStatusCodeNoContent(httpRes) { return nil, nil } - if httpRes.StatusCode == http.StatusBadRequest { - return nil, []error{&errortypes.BadInput{Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", httpRes.StatusCode)}} + if statusError := adapters.CheckResponseStatusCodeForErrors(httpRes); statusError != nil { + return nil, []error{statusError} } - var bidResp openrtb2.BidResponse - if err := jsonutil.Unmarshal(httpRes.Body, &bidResp); err != nil { - return nil, []error{&errortypes.BadServerResponse{Message: fmt.Sprintf("error while decoding response, err: %s", err)}} - } - - if len(bidResp.SeatBid) == 0 { - return nil, nil - } - - if len(bidResp.SeatBid[0].Bid) == 0 { - return nil, nil + bidResp, errResp := prepareBidResponse(httpRes.Body) + if errResp != nil { + return nil, []error{errResp} } br := adapters.NewBidderResponse() @@ -151,18 +123,8 @@ func (a *adapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.Request for _, sb := range bidResp.SeatBid { for i := range sb.Bid { bid := sb.Bid[i] - // Ensure imp exists and is video + mediaType := openrtb_ext.BidTypeVideo - for _, imp := range bidReq.Imp { - if imp.ID == bid.ImpID { - if imp.Video == nil { - // Not a video impression; skip - errs = append(errs, &errortypes.BadServerResponse{Message: fmt.Sprintf("ignoring bid id=%s for non-video imp id=%s", bid.ID, bid.ImpID)}) - mediaType = "" - } - break - } - } br.Bids = append(br.Bids, &adapters.TypedBid{Bid: &bid, BidType: mediaType}) } @@ -170,19 +132,12 @@ func (a *adapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.Request return br, errs } -func validateImpAndSetExt(imp *openrtb2.Imp) (int, error) { - if imp.Video == nil { - return 0, &errortypes.BadInput{Message: fmt.Sprintf("Only video impressions are supported by targetvideo. ImpID=%s", imp.ID)} - } - if len(imp.Ext) == 0 { - return 0, &errortypes.BadInput{Message: fmt.Sprintf("imp.ext is required and must contain bidder params for targetvideo. ImpID=%s", imp.ID)} +func prepareBidResponse(body []byte) (openrtb2.BidResponse, error) { + var response openrtb2.BidResponse + if err := jsonutil.Unmarshal(body, &response); err != nil { + return response, err } - var ext impExt - if err := jsonutil.Unmarshal(imp.Ext, &ext); err != nil { - return 0, &errortypes.BadInput{Message: fmt.Sprintf("error parsing imp.ext for targetvideo, err: %s", err)} - } - - return 0, nil + return response, nil } func Builder(bidderName openrtb_ext.BidderName, cfg config.Adapter, server config.Server) (adapters.Bidder, error) { diff --git a/adapters/targetVideo/targetvideotest/exemplary/app-video.json b/adapters/targetVideo/targetvideotest/exemplary/app-video.json index 956da513e58..88557a1e46b 100644 --- a/adapters/targetVideo/targetvideotest/exemplary/app-video.json +++ b/adapters/targetVideo/targetvideotest/exemplary/app-video.json @@ -25,13 +25,6 @@ ], "device": { "ua": "test-user-agent" - }, - "site": { - "domain": "www.publisher.com", - "page": "http://www.publisher.com/some/path", - "ext": { - "amp": 0 - } } }, "httpCalls": [ @@ -70,13 +63,6 @@ "device": { "ua": "test-user-agent" }, - "site": { - "domain": "www.publisher.com", - "page": "http://www.publisher.com/some/path", - "ext": { - "amp": 0 - } - }, "cur": ["EUR"] }, "impIDs":["test-imp-id-video"] diff --git a/adapters/targetVideo/targetvideotest/supplemental/ext-bidder-error.json b/adapters/targetVideo/targetvideotest/supplemental/ext-bidder-error.json new file mode 100644 index 00000000000..8c9d1960441 --- /dev/null +++ b/adapters/targetVideo/targetvideotest/supplemental/ext-bidder-error.json @@ -0,0 +1,41 @@ +{ + "expectedMakeRequestsErrors": [ + { + "value": "error parsing imp.ext.bidder, err: cannot unmarshal openrtb_ext.ExtImpTargetVideo.PlacementId: Value looks like Number/Boolean/None, but can't find its end: ',' or '}' symbol", + "comparison": "literal" + } + ], + "mockBidRequest": { + "id": "test-request-id-video", + "cur": ["EUR"], + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "bidder": { + "placementId": "test" + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + } + }, + "httpCalls": [] +} \ No newline at end of file diff --git a/adapters/targetVideo/targetvideotest/supplemental/no_seatbid.json b/adapters/targetVideo/targetvideotest/supplemental/no_seatbid.json index 329c2ae49ae..73321a58380 100644 --- a/adapters/targetVideo/targetvideotest/supplemental/no_seatbid.json +++ b/adapters/targetVideo/targetvideotest/supplemental/no_seatbid.json @@ -85,5 +85,7 @@ } } ], - "expectedBidResponses": [] + "expectedBidResponses": [{ + "currency": "USD" + }] } diff --git a/openrtb_ext/imp_targetvideo.go b/openrtb_ext/imp_targetvideo.go index 3fbef3c1e14..c09296168ce 100644 --- a/openrtb_ext/imp_targetvideo.go +++ b/openrtb_ext/imp_targetvideo.go @@ -1,6 +1,8 @@ package openrtb_ext +import "github.com/prebid/prebid-server/v3/util/jsonutil" + // ExtImpTargetVideo defines the contract for bidrequest.imp[i].ext.prebid.bidder.targetVideo type ExtImpTargetVideo struct { - PlacementId int `json:"placementId,omitempty"` + PlacementId jsonutil.StringInt `json:"placementId,omitempty"` } diff --git a/static/bidder-info/targetVideo.yaml b/static/bidder-info/targetVideo.yaml index decc110db3e..aa935f472a7 100644 --- a/static/bidder-info/targetVideo.yaml +++ b/static/bidder-info/targetVideo.yaml @@ -2,7 +2,7 @@ endpoint: "https://pbs.prebrid.tv/openrtb2/auction" geoscope: - global maintainer: - email: "contact@target-video.com" + email: "predrag.milosevic@target-video.com" gvlVendorID: 786 capabilities: app: From e0eac3d3bcebee4c1c040766b650832c5d4cd200 Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Mon, 24 Nov 2025 09:19:40 +0100 Subject: [PATCH 05/15] error handling fix --- adapters/targetVideo/targetvideo.go | 11 +---- .../supplemental/ext-bidder-error.json | 8 ++-- .../supplemental/invalid-placement-id.json | 41 +++++++++++++++++++ 3 files changed, 46 insertions(+), 14 deletions(-) create mode 100644 adapters/targetVideo/targetvideotest/supplemental/invalid-placement-id.json diff --git a/adapters/targetVideo/targetvideo.go b/adapters/targetVideo/targetvideo.go index 43cf30c2960..db2e14b8af2 100644 --- a/adapters/targetVideo/targetvideo.go +++ b/adapters/targetVideo/targetvideo.go @@ -17,13 +17,6 @@ type adapter struct { endpoint string } -type impExt struct { - TargetVideo openrtb_ext.ExtImpTargetVideo `json:"targetVideo"` -} - -type impExtBidder struct { - TargetVideo openrtb_ext.ExtImpTargetVideo `json:"targetVideo"` -} type impExtPrebid struct { Prebid *openrtb_ext.ExtImpPrebid `json:"prebid,omitempty"` } @@ -56,11 +49,11 @@ func (a *adapter) makeRequest(request openrtb2.BidRequest, imp openrtb2.Imp) (*a var extBidder adapters.ExtImpBidder if err := jsonutil.Unmarshal(imp.Ext, &extBidder); err != nil { - return nil, &errortypes.BadInput{Message: fmt.Sprintf("error parsing imp.ext, err: %s", err)} + return nil, &errortypes.BadInput{Message: fmt.Sprintf("Invalid ext.bidder")} } var extImpTargetVideo openrtb_ext.ExtImpTargetVideo if err := jsonutil.Unmarshal(extBidder.Bidder, &extImpTargetVideo); err != nil { - return nil, &errortypes.BadInput{Message: fmt.Sprintf("error parsing imp.ext.bidder, err: %s", err)} + return nil, &errortypes.BadInput{Message: fmt.Sprintf("Placement ID missing")} } var prebid *openrtb_ext.ExtImpPrebid if extBidder.Prebid == nil { diff --git a/adapters/targetVideo/targetvideotest/supplemental/ext-bidder-error.json b/adapters/targetVideo/targetvideotest/supplemental/ext-bidder-error.json index 8c9d1960441..81e6a5742ad 100644 --- a/adapters/targetVideo/targetvideotest/supplemental/ext-bidder-error.json +++ b/adapters/targetVideo/targetvideotest/supplemental/ext-bidder-error.json @@ -1,7 +1,7 @@ { "expectedMakeRequestsErrors": [ { - "value": "error parsing imp.ext.bidder, err: cannot unmarshal openrtb_ext.ExtImpTargetVideo.PlacementId: Value looks like Number/Boolean/None, but can't find its end: ',' or '}' symbol", + "value": "Invalid ext.bidder", "comparison": "literal" } ], @@ -16,10 +16,8 @@ "w": 640, "h": 360 }, - "ext": { - "bidder": { - "placementId": "test" - } + "prebid": { + "placementId": "test" } } ], diff --git a/adapters/targetVideo/targetvideotest/supplemental/invalid-placement-id.json b/adapters/targetVideo/targetvideotest/supplemental/invalid-placement-id.json new file mode 100644 index 00000000000..5e02af68455 --- /dev/null +++ b/adapters/targetVideo/targetvideotest/supplemental/invalid-placement-id.json @@ -0,0 +1,41 @@ +{ + "expectedMakeRequestsErrors": [ + { + "value": "Placement ID missing", + "comparison": "literal" + } + ], + "mockBidRequest": { + "id": "test-request-id-video", + "cur": ["EUR"], + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "bidder": { + "placementId": "test" + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + } + }, + "httpCalls": [] +} \ No newline at end of file From 8331306dc9f969540b7d027f56324afe0c582c06 Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Tue, 25 Nov 2025 10:02:41 +0100 Subject: [PATCH 06/15] code rewrite --- adapters/targetVideo/targetvideo.go | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/adapters/targetVideo/targetvideo.go b/adapters/targetVideo/targetvideo.go index db2e14b8af2..6ddb53cd5c7 100644 --- a/adapters/targetVideo/targetvideo.go +++ b/adapters/targetVideo/targetvideo.go @@ -28,11 +28,12 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.E // Split multi-imp request into multiple ad server requests. SRA is currently not recommended. for i := 0; i < totalImps; i++ { - if adapterReq, err := a.makeRequest(*request, request.Imp[i]); err == nil { - adapterRequests = append(adapterRequests, adapterReq) - } else { + adapterReq, err := a.makeRequest(*request, request.Imp[i]) + if err != nil { errors = append(errors, err) + continue } + adapterRequests = append(adapterRequests, adapterReq) } return adapterRequests, errors @@ -116,10 +117,7 @@ func (a *adapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.Request for _, sb := range bidResp.SeatBid { for i := range sb.Bid { bid := sb.Bid[i] - - mediaType := openrtb_ext.BidTypeVideo - - br.Bids = append(br.Bids, &adapters.TypedBid{Bid: &bid, BidType: mediaType}) + br.Bids = append(br.Bids, &adapters.TypedBid{Bid: &bid, BidType: openrtb_ext.BidTypeVideo}) } } return br, errs @@ -134,7 +132,6 @@ func prepareBidResponse(body []byte) (openrtb2.BidResponse, error) { } func Builder(bidderName openrtb_ext.BidderName, cfg config.Adapter, server config.Server) (adapters.Bidder, error) { - bidder := &adapter{ endpoint: cfg.Endpoint, } From 4fe67ea756827ff652329c19dfcbe54f75f929e4 Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Thu, 4 Dec 2025 10:43:34 +0100 Subject: [PATCH 07/15] bidderResponse currency fix --- adapters/targetVideo/targetvideo.go | 1 + 1 file changed, 1 insertion(+) diff --git a/adapters/targetVideo/targetvideo.go b/adapters/targetVideo/targetvideo.go index 6ddb53cd5c7..5101672277a 100644 --- a/adapters/targetVideo/targetvideo.go +++ b/adapters/targetVideo/targetvideo.go @@ -118,6 +118,7 @@ func (a *adapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.Request for i := range sb.Bid { bid := sb.Bid[i] br.Bids = append(br.Bids, &adapters.TypedBid{Bid: &bid, BidType: openrtb_ext.BidTypeVideo}) + br.Currency = bidResp.Cur } } return br, errs From 95040363d2dc1db8bedeff86dd0662c155c6d0e9 Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Fri, 30 Jan 2026 14:41:35 +0100 Subject: [PATCH 08/15] code fix --- adapters/targetVideo/targetvideo.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/adapters/targetVideo/targetvideo.go b/adapters/targetVideo/targetvideo.go index 5101672277a..47de2da261f 100644 --- a/adapters/targetVideo/targetvideo.go +++ b/adapters/targetVideo/targetvideo.go @@ -1,7 +1,6 @@ package targetVideo import ( - "encoding/json" "fmt" "net/http" @@ -22,8 +21,8 @@ type impExtPrebid struct { } func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errors []error totalImps := len(request.Imp) - errors := make([]error, 0) adapterRequests := make([]*adapters.RequestData, 0, totalImps) // Split multi-imp request into multiple ad server requests. SRA is currently not recommended. @@ -50,11 +49,11 @@ func (a *adapter) makeRequest(request openrtb2.BidRequest, imp openrtb2.Imp) (*a var extBidder adapters.ExtImpBidder if err := jsonutil.Unmarshal(imp.Ext, &extBidder); err != nil { - return nil, &errortypes.BadInput{Message: fmt.Sprintf("Invalid ext.bidder")} + return nil, &errortypes.BadInput{Message: "Invalid ext.bidder"} } var extImpTargetVideo openrtb_ext.ExtImpTargetVideo if err := jsonutil.Unmarshal(extBidder.Bidder, &extImpTargetVideo); err != nil { - return nil, &errortypes.BadInput{Message: fmt.Sprintf("Placement ID missing")} + return nil, &errortypes.BadInput{Message: "Placement ID missing"} } var prebid *openrtb_ext.ExtImpPrebid if extBidder.Prebid == nil { @@ -78,7 +77,7 @@ func (a *adapter) makeRequest(request openrtb2.BidRequest, imp openrtb2.Imp) (*a } - reqJSON, err := json.Marshal(request) + reqJSON, err := jsonutil.Marshal(request) if err != nil { return nil, err } @@ -112,7 +111,7 @@ func (a *adapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.Request } br := adapters.NewBidderResponse() - errs := []error{} + var errs []error for _, sb := range bidResp.SeatBid { for i := range sb.Bid { From 66c82f6ca1d555b1b398c01bf025b84bc1333a73 Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Tue, 3 Feb 2026 09:17:49 +0100 Subject: [PATCH 09/15] fast test fail --- adapters/targetVideo/params_test.go | 9 +++------ adapters/targetVideo/targetvideo.go | 2 -- adapters/targetVideo/targetvideo_test.go | 7 +++---- 3 files changed, 6 insertions(+), 12 deletions(-) diff --git a/adapters/targetVideo/params_test.go b/adapters/targetVideo/params_test.go index 185b1bb75bb..367d0e431bd 100644 --- a/adapters/targetVideo/params_test.go +++ b/adapters/targetVideo/params_test.go @@ -5,13 +5,12 @@ import ( "testing" "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/stretchr/testify/require" ) 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) - } + require.NoError(t, err, "Failed to fetch the json-schemas") for _, validParam := range validParams { if err := validator.Validate(openrtb_ext.BidderTargetVideo, json.RawMessage(validParam)); err != nil { @@ -22,9 +21,7 @@ func TestValidParams(t *testing.T) { 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) - } + require.NoError(t, err, "Failed to fetch the json-schemas") for _, invalidParam := range invalidParams { if err := validator.Validate(openrtb_ext.BidderTargetVideo, json.RawMessage(invalidParam)); err == nil { diff --git a/adapters/targetVideo/targetvideo.go b/adapters/targetVideo/targetvideo.go index 47de2da261f..2b88c5b583a 100644 --- a/adapters/targetVideo/targetvideo.go +++ b/adapters/targetVideo/targetvideo.go @@ -86,8 +86,6 @@ func (a *adapter) makeRequest(request openrtb2.BidRequest, imp openrtb2.Imp) (*a headers.Add("Content-Type", "application/json;charset=utf-8") headers.Add("Accept", "application/json") - //fmt.Println("TARGET VIDEO reqJson: ", string(reqJSON)) - return &adapters.RequestData{ Method: "POST", Uri: a.endpoint, diff --git a/adapters/targetVideo/targetvideo_test.go b/adapters/targetVideo/targetvideo_test.go index c25a50014be..693b8c26c7c 100644 --- a/adapters/targetVideo/targetvideo_test.go +++ b/adapters/targetVideo/targetvideo_test.go @@ -6,15 +6,14 @@ import ( "github.com/prebid/prebid-server/v3/adapters/adapterstest" "github.com/prebid/prebid-server/v3/config" "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/stretchr/testify/require" ) func TestJsonSamples(t *testing.T) { bidder, buildErr := Builder(openrtb_ext.BidderTargetVideo, config.Adapter{ Endpoint: "http://localhost/pbs"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) - - if buildErr != nil { - t.Fatalf("Builder returned unexpected error %v", buildErr) - } + + require.NoError(t, buildErr, "Builder returned unexpected error") adapterstest.RunJSONBidderTest(t, "targetvideotest", bidder) } From 92a1b1e0b66e8c025f36bf43f79af5167f5e1332 Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Wed, 4 Feb 2026 09:00:16 +0100 Subject: [PATCH 10/15] format issue fix --- adapters/targetVideo/targetvideo_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/targetVideo/targetvideo_test.go b/adapters/targetVideo/targetvideo_test.go index 693b8c26c7c..b4574db254d 100644 --- a/adapters/targetVideo/targetvideo_test.go +++ b/adapters/targetVideo/targetvideo_test.go @@ -12,7 +12,7 @@ import ( func TestJsonSamples(t *testing.T) { bidder, buildErr := Builder(openrtb_ext.BidderTargetVideo, config.Adapter{ Endpoint: "http://localhost/pbs"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) - + require.NoError(t, buildErr, "Builder returned unexpected error") adapterstest.RunJSONBidderTest(t, "targetvideotest", bidder) From 1a57b789d5727b6ecae07c8338a047a88978ab61 Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Mon, 9 Feb 2026 14:54:05 +0100 Subject: [PATCH 11/15] tests fix --- adapters/targetVideo/params_test.go | 11 +++++------ .../exemplary/{video.json => site-video.json} | 0 2 files changed, 5 insertions(+), 6 deletions(-) rename adapters/targetVideo/targetvideotest/exemplary/{video.json => site-video.json} (100%) diff --git a/adapters/targetVideo/params_test.go b/adapters/targetVideo/params_test.go index 367d0e431bd..a6d648b5a8b 100644 --- a/adapters/targetVideo/params_test.go +++ b/adapters/targetVideo/params_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -13,9 +14,8 @@ func TestValidParams(t *testing.T) { require.NoError(t, err, "Failed to fetch the json-schemas") for _, validParam := range validParams { - if err := validator.Validate(openrtb_ext.BidderTargetVideo, json.RawMessage(validParam)); err != nil { - t.Errorf("Schema rejected targetVideo params: %s", validParam) - } + assert.NoError(t, validator.Validate(openrtb_ext.BidderTargetVideo, json.RawMessage(validParam)), + "Schema rejected targetVideo params: %s", validParam) } } @@ -24,9 +24,8 @@ func TestInvalidParams(t *testing.T) { require.NoError(t, err, "Failed to fetch the json-schemas") for _, invalidParam := range invalidParams { - if err := validator.Validate(openrtb_ext.BidderTargetVideo, json.RawMessage(invalidParam)); err == nil { - t.Errorf("Schema allowed unexpected params: %s", invalidParam) - } + assert.Error(t, validator.Validate(openrtb_ext.BidderTargetVideo, json.RawMessage(invalidParam)), + "Schema should have rejected unexpected params: %s", invalidParam) } } diff --git a/adapters/targetVideo/targetvideotest/exemplary/video.json b/adapters/targetVideo/targetvideotest/exemplary/site-video.json similarity index 100% rename from adapters/targetVideo/targetvideotest/exemplary/video.json rename to adapters/targetVideo/targetvideotest/exemplary/site-video.json From 324f6dc600eebd65a9f4e9aa52c28274ee7c63c5 Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Tue, 17 Mar 2026 21:53:47 +0100 Subject: [PATCH 12/15] switch to v4 --- adapters/targetVideo/params_test.go | 2 +- adapters/targetVideo/targetvideo.go | 10 +++++----- adapters/targetVideo/targetvideo_test.go | 6 +++--- openrtb_ext/imp_targetvideo.go | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/adapters/targetVideo/params_test.go b/adapters/targetVideo/params_test.go index a6d648b5a8b..1839f0bbe6b 100644 --- a/adapters/targetVideo/params_test.go +++ b/adapters/targetVideo/params_test.go @@ -4,7 +4,7 @@ import ( "encoding/json" "testing" - "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/prebid/prebid-server/v4/openrtb_ext" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/adapters/targetVideo/targetvideo.go b/adapters/targetVideo/targetvideo.go index 2b88c5b583a..bc37a854b4a 100644 --- a/adapters/targetVideo/targetvideo.go +++ b/adapters/targetVideo/targetvideo.go @@ -5,11 +5,11 @@ import ( "net/http" "github.com/prebid/openrtb/v20/openrtb2" - "github.com/prebid/prebid-server/v3/adapters" - "github.com/prebid/prebid-server/v3/config" - "github.com/prebid/prebid-server/v3/errortypes" - "github.com/prebid/prebid-server/v3/openrtb_ext" - "github.com/prebid/prebid-server/v3/util/jsonutil" + "github.com/prebid/prebid-server/v4/adapters" + "github.com/prebid/prebid-server/v4/config" + "github.com/prebid/prebid-server/v4/errortypes" + "github.com/prebid/prebid-server/v4/openrtb_ext" + "github.com/prebid/prebid-server/v4/util/jsonutil" ) type adapter struct { diff --git a/adapters/targetVideo/targetvideo_test.go b/adapters/targetVideo/targetvideo_test.go index b4574db254d..39ddd9d82b5 100644 --- a/adapters/targetVideo/targetvideo_test.go +++ b/adapters/targetVideo/targetvideo_test.go @@ -3,9 +3,9 @@ package targetVideo import ( "testing" - "github.com/prebid/prebid-server/v3/adapters/adapterstest" - "github.com/prebid/prebid-server/v3/config" - "github.com/prebid/prebid-server/v3/openrtb_ext" + "github.com/prebid/prebid-server/v4/adapters/adapterstest" + "github.com/prebid/prebid-server/v4/config" + "github.com/prebid/prebid-server/v4/openrtb_ext" "github.com/stretchr/testify/require" ) diff --git a/openrtb_ext/imp_targetvideo.go b/openrtb_ext/imp_targetvideo.go index c09296168ce..c13b8a42aa4 100644 --- a/openrtb_ext/imp_targetvideo.go +++ b/openrtb_ext/imp_targetvideo.go @@ -1,6 +1,6 @@ package openrtb_ext -import "github.com/prebid/prebid-server/v3/util/jsonutil" +import "github.com/prebid/prebid-server/v4/util/jsonutil" // ExtImpTargetVideo defines the contract for bidrequest.imp[i].ext.prebid.bidder.targetVideo type ExtImpTargetVideo struct { From 3f08288975df81b9c64472c5ba28a0e5957d304a Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Wed, 8 Apr 2026 08:33:14 +0200 Subject: [PATCH 13/15] email changed to contact@target-video.com --- static/bidder-info/targetVideo.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/bidder-info/targetVideo.yaml b/static/bidder-info/targetVideo.yaml index aa935f472a7..decc110db3e 100644 --- a/static/bidder-info/targetVideo.yaml +++ b/static/bidder-info/targetVideo.yaml @@ -2,7 +2,7 @@ endpoint: "https://pbs.prebrid.tv/openrtb2/auction" geoscope: - global maintainer: - email: "predrag.milosevic@target-video.com" + email: "contact@target-video.com" gvlVendorID: 786 capabilities: app: From 082d2dba1067ace42df0bfefea0758231bf82ae8 Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Thu, 16 Apr 2026 10:56:41 +0200 Subject: [PATCH 14/15] tests fixed, request fields fix --- adapters/targetVideo/targetvideo.go | 30 +++++++------- .../supplemental/ext-bidder-error.json | 4 +- .../supplemental/invalid-placement-id.json | 41 ------------------- 3 files changed, 17 insertions(+), 58 deletions(-) delete mode 100644 adapters/targetVideo/targetvideotest/supplemental/invalid-placement-id.json diff --git a/adapters/targetVideo/targetvideo.go b/adapters/targetVideo/targetvideo.go index bc37a854b4a..97d1bfd832a 100644 --- a/adapters/targetVideo/targetvideo.go +++ b/adapters/targetVideo/targetvideo.go @@ -1,6 +1,7 @@ package targetVideo import ( + "encoding/json" "fmt" "net/http" @@ -16,10 +17,6 @@ type adapter struct { endpoint string } -type impExtPrebid struct { - Prebid *openrtb_ext.ExtImpPrebid `json:"prebid,omitempty"` -} - func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { var errors []error totalImps := len(request.Imp) @@ -52,11 +49,9 @@ func (a *adapter) makeRequest(request openrtb2.BidRequest, imp openrtb2.Imp) (*a return nil, &errortypes.BadInput{Message: "Invalid ext.bidder"} } var extImpTargetVideo openrtb_ext.ExtImpTargetVideo - if err := jsonutil.Unmarshal(extBidder.Bidder, &extImpTargetVideo); err != nil { - return nil, &errortypes.BadInput{Message: "Placement ID missing"} - } - var prebid *openrtb_ext.ExtImpPrebid - if extBidder.Prebid == nil { + jsonutil.Unmarshal(extBidder.Bidder, &extImpTargetVideo) + prebid := extBidder.Prebid + if prebid == nil { prebid = &openrtb_ext.ExtImpPrebid{} } if prebid.StoredRequest == nil { @@ -64,11 +59,19 @@ func (a *adapter) makeRequest(request openrtb2.BidRequest, imp openrtb2.Imp) (*a } prebid.StoredRequest.ID = fmt.Sprintf("%d", extImpTargetVideo.PlacementId) - ext := impExtPrebid{ - Prebid: prebid, + var extMap map[string]json.RawMessage + if err := jsonutil.Unmarshal(imp.Ext, &extMap); err != nil { + return nil, &errortypes.BadInput{Message: fmt.Sprintf("error parsing imp.ext, err: %s", err)} + } + delete(extMap, "bidder") + + prebidRaw, err := jsonutil.Marshal(prebid) + if err != nil { + return nil, &errortypes.BadInput{Message: fmt.Sprintf("error building imp.ext.prebid, err: %s", err)} } + extMap["prebid"] = prebidRaw - extRaw, err := jsonutil.Marshal(ext) + extRaw, err := jsonutil.Marshal(extMap) if err != nil { return nil, &errortypes.BadInput{Message: fmt.Sprintf("error building imp.ext, err: %s", err)} } @@ -109,7 +112,6 @@ func (a *adapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.Request } br := adapters.NewBidderResponse() - var errs []error for _, sb := range bidResp.SeatBid { for i := range sb.Bid { @@ -118,7 +120,7 @@ func (a *adapter) MakeBids(bidReq *openrtb2.BidRequest, unused *adapters.Request br.Currency = bidResp.Cur } } - return br, errs + return br, nil } func prepareBidResponse(body []byte) (openrtb2.BidResponse, error) { diff --git a/adapters/targetVideo/targetvideotest/supplemental/ext-bidder-error.json b/adapters/targetVideo/targetvideotest/supplemental/ext-bidder-error.json index 81e6a5742ad..009d397e3f9 100644 --- a/adapters/targetVideo/targetvideotest/supplemental/ext-bidder-error.json +++ b/adapters/targetVideo/targetvideotest/supplemental/ext-bidder-error.json @@ -16,9 +16,7 @@ "w": 640, "h": 360 }, - "prebid": { - "placementId": "test" - } + "ext": "invalid-ext" } ], "device": { diff --git a/adapters/targetVideo/targetvideotest/supplemental/invalid-placement-id.json b/adapters/targetVideo/targetvideotest/supplemental/invalid-placement-id.json deleted file mode 100644 index 5e02af68455..00000000000 --- a/adapters/targetVideo/targetvideotest/supplemental/invalid-placement-id.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "expectedMakeRequestsErrors": [ - { - "value": "Placement ID missing", - "comparison": "literal" - } - ], - "mockBidRequest": { - "id": "test-request-id-video", - "cur": ["EUR"], - "imp": [ - { - "id": "test-imp-id-video", - "video": { - "mimes": ["video/mp4"], - "w": 640, - "h": 360 - }, - "ext": { - "bidder": { - "placementId": "test" - } - } - } - ], - "device": { - "ua": "test-user-agent", - "ip": "123.123.123.123", - "language": "en", - "dnt": 0 - }, - "site": { - "domain": "www.publisher.com", - "page": "http://www.publisher.com/some/path", - "ext": { - "amp": 0 - } - } - }, - "httpCalls": [] -} \ No newline at end of file From 8e9e7b43e932b3e0e3ea635c1259e987c96e72ae Mon Sep 17 00:00:00 2001 From: "Predrag.Milosevic" Date: Thu, 16 Apr 2026 14:59:02 +0200 Subject: [PATCH 15/15] supplemental tests --- .../overwrite-stored-request.json | 127 +++++++++++++++++ .../supplemental/preserve-imp-ext-fields.json | 130 ++++++++++++++++++ 2 files changed, 257 insertions(+) create mode 100644 adapters/targetVideo/targetvideotest/supplemental/overwrite-stored-request.json create mode 100644 adapters/targetVideo/targetvideotest/supplemental/preserve-imp-ext-fields.json diff --git a/adapters/targetVideo/targetvideotest/supplemental/overwrite-stored-request.json b/adapters/targetVideo/targetvideotest/supplemental/overwrite-stored-request.json new file mode 100644 index 00000000000..1d611a78108 --- /dev/null +++ b/adapters/targetVideo/targetvideotest/supplemental/overwrite-stored-request.json @@ -0,0 +1,127 @@ +{ + "mockBidRequest": { + "id": "test-request-id-video", + "cur": ["EUR"], + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "bidder": { + "placementId": "77777" + }, + "prebid": { + "storedrequest": { + "id": "existing-stored-req" + } + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": ["application/json"], + "Content-Type": ["application/json;charset=utf-8"] + }, + "uri": "http://localhost/pbs", + "body": { + "id": "test-request-id-video", + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "77777" + } + } + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + }, + "cur": ["EUR"] + }, + "impIDs": ["test-imp-id-video"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id-video", + "seatbid": [ + { + "seat": "targetVideo", + "bid": [ + { + "id": "randomID", + "impid": "test-imp-id-video", + "price": 5.5, + "adm": "test-imp-id-video", + "cid": "789", + "crid": "12345678", + "h": 360, + "w": 640 + } + ] + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [{ + "bid": { + "id": "randomID", + "impid": "test-imp-id-video", + "price": 5.5, + "adm": "test-imp-id-video", + "crid": "12345678", + "cid": "789", + "h": 360, + "w": 640 + }, + "type": "video" + }] + } + ] +} diff --git a/adapters/targetVideo/targetvideotest/supplemental/preserve-imp-ext-fields.json b/adapters/targetVideo/targetvideotest/supplemental/preserve-imp-ext-fields.json new file mode 100644 index 00000000000..4db92850830 --- /dev/null +++ b/adapters/targetVideo/targetvideotest/supplemental/preserve-imp-ext-fields.json @@ -0,0 +1,130 @@ +{ + "mockBidRequest": { + "id": "test-request-id-video", + "cur": ["EUR"], + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "bidder": { + "placementId": "77777" + }, + "prebid": { + "is_rewarded_inventory": 1 + }, + "tid": "some-transaction-id", + "gpid": "/1234/main-player" + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Accept": ["application/json"], + "Content-Type": ["application/json;charset=utf-8"] + }, + "uri": "http://localhost/pbs", + "body": { + "id": "test-request-id-video", + "imp": [ + { + "id": "test-imp-id-video", + "video": { + "mimes": ["video/mp4"], + "w": 640, + "h": 360 + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "77777" + }, + "is_rewarded_inventory": 1 + }, + "tid": "some-transaction-id", + "gpid": "/1234/main-player" + } + } + ], + "device": { + "ua": "test-user-agent", + "ip": "123.123.123.123", + "language": "en", + "dnt": 0 + }, + "site": { + "domain": "www.publisher.com", + "page": "http://www.publisher.com/some/path", + "ext": { + "amp": 0 + } + }, + "cur": ["EUR"] + }, + "impIDs": ["test-imp-id-video"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id-video", + "seatbid": [ + { + "seat": "targetVideo", + "bid": [ + { + "id": "randomID", + "impid": "test-imp-id-video", + "price": 5.5, + "adm": "test-imp-id-video", + "cid": "789", + "crid": "12345678", + "h": 360, + "w": 640 + } + ] + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [{ + "bid": { + "id": "randomID", + "impid": "test-imp-id-video", + "price": 5.5, + "adm": "test-imp-id-video", + "crid": "12345678", + "cid": "789", + "h": 360, + "w": 640 + }, + "type": "video" + }] + } + ] +}