From d33777669e6b058944bc3c716e6a902558ad5e6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Fri, 13 Oct 2023 14:33:59 +0200 Subject: [PATCH 1/9] relevantBidAdapter: add new bid adapter --- adapters/relevantdigital/params_test.go | 42 +++ adapters/relevantdigital/relevantdigital.go | 278 ++++++++++++++++++ .../relevantdigital/relevantdigital_test.go | 28 ++ .../exemplary/simple-audio.json | 116 ++++++++ .../exemplary/simple-banner.json | 206 +++++++++++++ .../exemplary/simple-native.json | 118 ++++++++ .../exemplary/simple-video.json | 185 ++++++++++++ .../supplemental/invalidBidMType.json | 99 +++++++ .../supplemental/invalidBidType.json | 102 +++++++ .../supplemental/invalidParam.json | 42 +++ .../supplemental/invalidRequestCount.json | 39 +++ exchange/adapter_builders.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_relevantdigital.go | 8 + static/bidder-info/relevantdigital.yaml | 15 + static/bidder-params/relevantdigital.json | 29 ++ 16 files changed, 1311 insertions(+) create mode 100644 adapters/relevantdigital/params_test.go create mode 100644 adapters/relevantdigital/relevantdigital.go create mode 100644 adapters/relevantdigital/relevantdigital_test.go create mode 100644 adapters/relevantdigital/relevantdigitaltest/exemplary/simple-audio.json create mode 100644 adapters/relevantdigital/relevantdigitaltest/exemplary/simple-banner.json create mode 100644 adapters/relevantdigital/relevantdigitaltest/exemplary/simple-native.json create mode 100644 adapters/relevantdigital/relevantdigitaltest/exemplary/simple-video.json create mode 100644 adapters/relevantdigital/relevantdigitaltest/supplemental/invalidBidMType.json create mode 100644 adapters/relevantdigital/relevantdigitaltest/supplemental/invalidBidType.json create mode 100644 adapters/relevantdigital/relevantdigitaltest/supplemental/invalidParam.json create mode 100644 adapters/relevantdigital/relevantdigitaltest/supplemental/invalidRequestCount.json create mode 100644 openrtb_ext/imp_relevantdigital.go create mode 100644 static/bidder-info/relevantdigital.yaml create mode 100644 static/bidder-params/relevantdigital.json diff --git a/adapters/relevantdigital/params_test.go b/adapters/relevantdigital/params_test.go new file mode 100644 index 00000000000..cd889a5eaa8 --- /dev/null +++ b/adapters/relevantdigital/params_test.go @@ -0,0 +1,42 @@ +package relevantdigital + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v2/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 schema. %v", err) + } + + for _, p := range validParams { + if err := validator.Validate(openrtb_ext.BidderRelevantDigital, json.RawMessage(p)); err != nil { + t.Errorf("Schema rejected valid params: %s", p) + } + } +} + +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json schema. %v", err) + } + + for _, p := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderRelevantDigital, json.RawMessage(p)); err == nil { + t.Errorf("Schema allowed invalid params: %s", p) + } + } +} + +var validParams = []string{ + `{"accountId": "5fcf49f83a64ba6602b5be7e", "placementId" : "63b68275b4f35962c8eec9b1_5fcf49f83a64ba6602b5be9a", "pbsHost" : "some-host" }`, +} + +var invalidParams = []string{ + `{"accountId": 123, "placementId" : 123, "pbsHost" : ""}`, +} diff --git a/adapters/relevantdigital/relevantdigital.go b/adapters/relevantdigital/relevantdigital.go new file mode 100644 index 00000000000..a623c0f5fbc --- /dev/null +++ b/adapters/relevantdigital/relevantdigital.go @@ -0,0 +1,278 @@ +package relevantdigital + +import ( + "encoding/json" + "fmt" + "math" + "net/http" + "strings" + "text/template" + + "github.com/buger/jsonparser" + "github.com/prebid/openrtb/v19/openrtb2" + "github.com/prebid/prebid-server/v2/adapters" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/errortypes" + "github.com/prebid/prebid-server/v2/macros" + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +type adapter struct { + endpoint *template.Template + name string +} + +const RELEVANT_DOMAIN = ".relevant-digital.com" +const DEFAULT_TIMEOUT = 1000 +const DEFAULT_BUFFER_MS = 250 +const STORED_REQUEST_EXT = "{\"prebid\":{\"debug\":%t,\"storedrequest\":{\"id\":\"%s\"}},\"relevant\":{\"count\":%d,\"adapterType\":\"server\"}}" +const STORED_IMP_EXT = "{\"prebid\":{\"storedrequest\":{\"id\":\"%s\"}}}" + +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + template, err := template.New("endpointTemplate").Parse(config.Endpoint) + if err != nil { + return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) + } + return &adapter{ + endpoint: template, + name: bidderName.String(), + }, nil +} + +func patchBidRequestExt(prebidBidRequest *openrtb2.BidRequest, id string) error { + count, cerr := jsonparser.GetInt(prebidBidRequest.Ext, "relevant", "count") + if cerr != nil { + count = 0 + } + + if count >= 5 { + return &errortypes.FailedToRequestBids{ + Message: "too many requests", + } + } else { + count = count + 1 + } + + debug, derr := jsonparser.GetBoolean(prebidBidRequest.Ext, "prebid", "debug") + if derr != nil { + debug = false + } + + prebidBidRequest.Ext = []byte(fmt.Sprintf(STORED_REQUEST_EXT, debug, id, count)) + return nil +} + +func patchBidImpExt(imp *openrtb2.Imp, id string) { + imp.Ext = []byte(fmt.Sprintf(STORED_IMP_EXT, id)) + if imp.Banner != nil { + imp.Banner.Ext = nil + } + if imp.Video != nil { + imp.Video.Ext = nil + } + if imp.Native != nil { + imp.Native.Ext = nil + } + if imp.Audio != nil { + imp.Audio.Ext = nil + } +} + +func setTMax(prebidBidRequest *openrtb2.BidRequest, pbsBufferMs int) { + timeout := float64(prebidBidRequest.TMax) + if timeout <= 0 { + timeout = DEFAULT_TIMEOUT + } + buffer := float64(pbsBufferMs) + prebidBidRequest.TMax = int64(math.Min(math.Max(timeout-buffer, buffer), timeout)) +} + +func cloneBidRequest(prebidBidRequest *openrtb2.BidRequest) (*openrtb2.BidRequest, error) { + jsonRes, err := json.Marshal(prebidBidRequest) + if err != nil { + return nil, err + } + var copy openrtb2.BidRequest + err = json.Unmarshal(jsonRes, ©) + return ©, err +} + +func createBidRequest(prebidBidRequest *openrtb2.BidRequest, params []*openrtb_ext.ExtRelevantDigital) (*openrtb2.BidRequest, error) { + bidRequestCopy, err := cloneBidRequest(prebidBidRequest) + if err != nil { + return nil, err + } + + err = patchBidRequestExt(bidRequestCopy, params[0].AccountId) + if err != nil { + return nil, &errortypes.BadInput{ + Message: fmt.Sprintf("failed to create bidRequest, error: %s", err), + } + } + + setTMax(bidRequestCopy, params[0].PbsBufferMs) + + for idx := range bidRequestCopy.Imp { + patchBidImpExt(&bidRequestCopy.Imp[idx], params[idx].PlacementId) + } + return bidRequestCopy, err +} + +func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtRelevantDigital, error) { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, &errortypes.BadInput{ + Message: "imp.ext not provided", + } + } + relevantExt := openrtb_ext.ExtRelevantDigital{PbsBufferMs: DEFAULT_BUFFER_MS} + if err := json.Unmarshal(bidderExt.Bidder, &relevantExt); err != nil { + return nil, &errortypes.BadInput{ + Message: "ext.bidder not provided", + } + } + return &relevantExt, nil +} + +func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtRelevantDigital) (string, error) { + params.Host = strings.ReplaceAll(params.Host, "http://", "") + params.Host = strings.ReplaceAll(params.Host, "https://", "") + params.Host = strings.ReplaceAll(params.Host, RELEVANT_DOMAIN, "") + + endpointParams := macros.EndpointTemplateParams{Host: params.Host} + return macros.ResolveMacros(a.endpoint, endpointParams) +} + +func (a *adapter) buildAdapterRequest(prebidBidRequest *openrtb2.BidRequest, params []*openrtb_ext.ExtRelevantDigital) (*adapters.RequestData, error) { + newBidRequest, err := createBidRequest(prebidBidRequest, params) + + if err != nil { + return nil, err + } + + reqJSON, err := json.Marshal(newBidRequest) + if err != nil { + return nil, err + } + + url, err := a.buildEndpointURL(params[0]) + if err != nil { + return nil, err + } + + return &adapters.RequestData{ + Method: "POST", + Uri: url, + Body: reqJSON, + Headers: getHeaders(prebidBidRequest), + }, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + impParams, errs := getImpressionsInfo(request.Imp) + if len(errs) > 0 { + return nil, errs + } + + bidRequest, err := a.buildAdapterRequest(request, impParams) + if err != nil { + errs = []error{err} + } + + if bidRequest != nil { + return []*adapters.RequestData{bidRequest}, errs + } + return nil, errs +} + +func getImpressionsInfo(imps []openrtb2.Imp) (resImps []*openrtb_ext.ExtRelevantDigital, errors []error) { + for _, imp := range imps { + impExt, err := getImpressionExt(&imp) + if err != nil { + errors = append(errors, err) + continue + } + resImps = append(resImps, impExt) + } + return +} + +func getHeaders(request *openrtb2.BidRequest) http.Header { + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + headers.Add("Accept", "application/json") + headers.Add("X-Openrtb-Version", "2.5") + + if request.Device != nil { + if len(request.Device.UA) > 0 { + headers.Add("User-Agent", request.Device.UA) + } + if len(request.Device.IPv6) > 0 { + headers.Add("X-Forwarded-For", request.Device.IPv6) + } + if len(request.Device.IP) > 0 { + headers.Add("X-Forwarded-For", request.Device.IP) + } + } + return headers +} + +func getMediaTypeForBidFromExt(bid openrtb2.Bid) (openrtb_ext.BidType, error) { + if bid.Ext != nil { + var bidExt openrtb_ext.ExtBid + err := json.Unmarshal(bid.Ext, &bidExt) + if err == nil && bidExt.Prebid != nil { + return openrtb_ext.ParseBidType(string(bidExt.Prebid.Type)) + } + } + return "", fmt.Errorf("failed to parse bid type, missing ext: %s", bid.ImpID) +} + +func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { + switch bid.MType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo, nil + case openrtb2.MarkupAudio: + return openrtb_ext.BidTypeAudio, nil + case openrtb2.MarkupNative: + return openrtb_ext.BidTypeNative, nil + default: + return getMediaTypeForBidFromExt(bid) + } +} + +func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if adapters.IsResponseStatusCodeNoContent(responseData) { + return nil, nil + } + + if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil { + return nil, []error{err} + } + + var response openrtb2.BidResponse + if err := json.Unmarshal(responseData.Body, &response); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(response.SeatBid)) + bidResponse.Currency = response.Cur + var errs []error + for _, seatBid := range response.SeatBid { + for i, bid := range seatBid.Bid { + bidType, err := getMediaTypeForBid(bid) + if err != nil { + errs = append(errs, err) + } else { + b := &adapters.TypedBid{ + Bid: &seatBid.Bid[i], + BidType: bidType, + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + } + return bidResponse, errs +} diff --git a/adapters/relevantdigital/relevantdigital_test.go b/adapters/relevantdigital/relevantdigital_test.go new file mode 100644 index 00000000000..5c627ca7c65 --- /dev/null +++ b/adapters/relevantdigital/relevantdigital_test.go @@ -0,0 +1,28 @@ +package relevantdigital + +import ( + "testing" + + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderRelevantDigital, config.Adapter{ + Endpoint: "https://{{.Host}}.relevant-digital.com/openrtb2/auction"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "relevantdigitaltest", bidder) +} + +func TestEndpointTemplateMalformed(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderAceex, config.Adapter{ + Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + assert.Error(t, buildErr) +} diff --git a/adapters/relevantdigital/relevantdigitaltest/exemplary/simple-audio.json b/adapters/relevantdigital/relevantdigitaltest/exemplary/simple-audio.json new file mode 100644 index 00000000000..2a7e6189154 --- /dev/null +++ b/adapters/relevantdigital/relevantdigitaltest/exemplary/simple-audio.json @@ -0,0 +1,116 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-audio-id", + "audio": { + "mimes": [ + "audio/mp4" + ] + }, + "ext": { + "bidder": { + "accountId": "620523ae7f4bbe1691bbb815", + "pbsHost": "fakeHost", + "placementId": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakeHost.relevant-digital.com/openrtb2/auction", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-audio-id", + "audio": { + "mimes": [ + "audio/mp4" + ] + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + } + ], + "ext": { + "prebid": { + "debug": false, + "storedrequest": { + "id": "620523ae7f4bbe1691bbb815" + } + }, + "relevant": { + "adapterType": "server", + "count": 1 + } + }, + "tmax": 750 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "test-seat", + "bid": [ + { + "id": "9244e776-e0c7-4d76-8c20-88971909114b", + "impid": "test-audio-id", + "price": 2.50, + "adm": "a creative", + "adomain": [ + "advertiser.com" + ], + "crid": "9999", + "mtype": 3, + "ext": { + "prebid": { + "type": "audio" + } + } + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "9244e776-e0c7-4d76-8c20-88971909114b", + "impid": "test-audio-id", + "price": 2.50, + "adm": "a creative", + "adomain": [ + "advertiser.com" + ], + "crid": "9999", + "mtype": 3, + "ext": { + "prebid": { + "type": "audio" + } + } + }, + "type": "audio" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/relevantdigital/relevantdigitaltest/exemplary/simple-banner.json b/adapters/relevantdigital/relevantdigitaltest/exemplary/simple-banner.json new file mode 100644 index 00000000000..ea30dfe3177 --- /dev/null +++ b/adapters/relevantdigital/relevantdigitaltest/exemplary/simple-banner.json @@ -0,0 +1,206 @@ +{ + "mockBidRequest": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "imp": [ + { + "id": "div-1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + }, + { + "w": 320, + "h": 320 + } + ] + }, + "ext": { + "bidder": { + "accountId": "620523ae7f4bbe1691bbb815", + "pbsHost": "fakeHost", + "placementId": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + ], + "site": { + "domain": "somedomain.com", + "page": "https://somedomain.com", + "publisher": { + "id": "1001", + "domain": "somepub.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "userAgent", + "ip": "193.168.244.1" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakeHost.relevant-digital.com/openrtb2/auction", + "body": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "imp": [ + { + "id": "div-1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + }, + { + "w": 300, + "h": 600 + }, + { + "w": 320, + "h": 320 + } + ] + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + } + ], + "site": { + "domain": "somedomain.com", + "page": "https://somedomain.com", + "publisher": { + "id": "1001", + "domain": "somepub.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "userAgent", + "ip": "193.168.244.1" + }, + "ext": { + "prebid": { + "debug" : false, + "storedrequest": { + "id": "620523ae7f4bbe1691bbb815" + } + }, + "relevant": { + "adapterType": "server", + "count": 1 + } + }, + "tmax": 750 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "seatbid": [ + { + "seat": "relevantdigital", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "div-1", + "price": 0.500000, + "adm": "", + "crid": "crid_10", + "w": 728, + "h": 90, + "mtype": 1, + "ext": { + "bidtype": 0, + "dspid": 6, + "origbidcpm": 11.13998874085194, + "origbidcur": "USD", + "prebid": { + "bidid": "09abd496-5706-4311-a356-d559629a1d17", + "events": { + "win": "https://fakeHost.relevant-digital.com/event?t=win\u0026b=09abd496-5706-4311-a356-d559629a1d17\u0026a=1001\u0026bidder=providerA\u0026ts=1694939785078", + "imp": "https://fakeHost.relevant-digital.com/event?t=imp\u0026b=09abd496-5706-4311-a356-d559629a1d17\u0026a=1001\u0026bidder=providerA\u0026ts=1694939785078" + }, + "meta": { + "adaptercode": "relevantdigital" + }, + "targeting": { + "hb_bidder": "relevantdigital", + "hb_cache_host": "somedomain.com", + "hb_cache_path": "/analytics_cache/read", + "hb_pb": "11.10", + "hb_size": "300x250" + }, + "type": "banner" + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "div-1", + "price": 0.5, + "adm": "", + "crid": "crid_10", + "w": 728, + "h": 90, + "mtype": 1, + "ext": { + "bidtype": 0, + "dspid": 6, + "origbidcpm": 11.13998874085194, + "origbidcur": "USD", + "prebid": { + "bidid": "09abd496-5706-4311-a356-d559629a1d17", + "events": { + "win": "https://fakeHost.relevant-digital.com/event?t=win\u0026b=09abd496-5706-4311-a356-d559629a1d17\u0026a=1001\u0026bidder=providerA\u0026ts=1694939785078", + "imp": "https://fakeHost.relevant-digital.com/event?t=imp\u0026b=09abd496-5706-4311-a356-d559629a1d17\u0026a=1001\u0026bidder=providerA\u0026ts=1694939785078" + }, + "meta": { + "adaptercode": "relevantdigital" + }, + "targeting": { + "hb_bidder": "relevantdigital", + "hb_cache_host": "somedomain.com", + "hb_cache_path": "/analytics_cache/read", + "hb_pb": "11.10", + "hb_size": "300x250" + }, + "type": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/relevantdigital/relevantdigitaltest/exemplary/simple-native.json b/adapters/relevantdigital/relevantdigitaltest/exemplary/simple-native.json new file mode 100644 index 00000000000..a42a8f3b1b4 --- /dev/null +++ b/adapters/relevantdigital/relevantdigitaltest/exemplary/simple-native.json @@ -0,0 +1,118 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-native-id", + "native": { + "request": "test-native-request" + }, + "ext": { + "bidder": { + "accountId": "620523ae7f4bbe1691bbb815", + "pbsHost": "fakeHost", + "placementId": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakeHost.relevant-digital.com/openrtb2/auction", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-native-id", + "native": { + "request": "test-native-request" + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + } + ], + "ext": { + "prebid": { + "debug": false, + "storedrequest": { + "id": "620523ae7f4bbe1691bbb815" + } + }, + "relevant": { + "adapterType": "server", + "count": 1 + } + }, + "tmax": 750 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "test-seat", + "bid": [ + { + "id": "9244e776-e0c7-4d76-8c20-88971909114b", + "impid": "test-native-id", + "price": 2.50, + "adm": "a creative", + "adid": "9999", + "adomain": [ + "advertiser.com" + ], + "crid": "9999", + "w": 300, + "h": 600, + "mtype": 4, + "ext": { + "prebid": { + "type": "native" + } + } + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "9244e776-e0c7-4d76-8c20-88971909114b", + "impid": "test-native-id", + "price": 2.50, + "adm": "a creative", + "adid": "9999", + "adomain": [ + "advertiser.com" + ], + "crid": "9999", + "w": 300, + "h": 600, + "mtype": 4, + "ext": { + "prebid": { + "type": "native" + } + } + }, + "type": "native" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/relevantdigital/relevantdigitaltest/exemplary/simple-video.json b/adapters/relevantdigital/relevantdigitaltest/exemplary/simple-video.json new file mode 100644 index 00000000000..6cc8d25468a --- /dev/null +++ b/adapters/relevantdigital/relevantdigitaltest/exemplary/simple-video.json @@ -0,0 +1,185 @@ +{ + "mockBidRequest": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "imp": [ + { + "id": "div-1", + "video": { + "mimes": [ + "video/mp4", + "video/webm", + "video/ogg" + ], + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "accountId": "620523ae7f4bbe1691bbb815", + "pbsHost": "fakeHost", + "placementId": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + ], + "site": { + "domain": "somedomain.com", + "page": "https://somedomain.com", + "publisher": { + "id": "1001", + "domain": "somepub.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "userAgent", + "ipv6": "2607:fb90:f27:4512:d800:cb23:a603:e245" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakeHost.relevant-digital.com/openrtb2/auction", + "body": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "imp": [ + { + "id": "div-1", + "video": { + "mimes": [ + "video/mp4", + "video/webm", + "video/ogg" + ], + "w": 640, + "h": 480 + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + } + ], + "site": { + "domain": "somedomain.com", + "page": "https://somedomain.com", + "publisher": { + "id": "1001", + "domain": "somepub.com" + }, + "ext": { + "amp": 0 + } + }, + "device": { + "ua": "userAgent", + "ipv6": "2607:fb90:f27:4512:d800:cb23:a603:e245" + }, + "ext": { + "prebid": { + "debug": false, + "storedrequest": { + "id": "620523ae7f4bbe1691bbb815" + } + }, + "relevant": { + "adapterType": "server", + "count": 1 + } + }, + "tmax": 750 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "seatbid": [ + { + "seat": "relevantdigital", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "div-1", + "price": 0.500000, + "adm": " ", + "crid": "crid_10", + "mtype": 2, + "ext": { + "dspid": 6, + "origbidcpm": 11.13998874085194, + "origbidcur": "USD", + "prebid": { + "bidid": "09abd496-5706-4311-a356-d559629a1d17", + "events": { + "win": "https://fakeHost.relevant-digital.com/event?t=win\u0026b=09abd496-5706-4311-a356-d559629a1d17\u0026a=1001\u0026bidder=providerA\u0026ts=1694939785078", + "imp": "https://fakeHost.relevant-digital.com/event?t=imp\u0026b=09abd496-5706-4311-a356-d559629a1d17\u0026a=1001\u0026bidder=providerA\u0026ts=1694939785078" + }, + "meta": { + "adaptercode": "relevantdigital" + }, + "targeting": { + "hb_bidder": "relevantdigital", + "hb_cache_host": "somedomain.com", + "hb_cache_path": "/analytics_cache/read", + "hb_pb": "11.10" + }, + "type": "video" + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "div-1", + "price": 0.5, + "adm": " ", + "crid": "crid_10", + "mtype": 2, + "ext": { + "dspid": 6, + "origbidcpm": 11.13998874085194, + "origbidcur": "USD", + "prebid": { + "bidid": "09abd496-5706-4311-a356-d559629a1d17", + "events": { + "win": "https://fakeHost.relevant-digital.com/event?t=win\u0026b=09abd496-5706-4311-a356-d559629a1d17\u0026a=1001\u0026bidder=providerA\u0026ts=1694939785078", + "imp": "https://fakeHost.relevant-digital.com/event?t=imp\u0026b=09abd496-5706-4311-a356-d559629a1d17\u0026a=1001\u0026bidder=providerA\u0026ts=1694939785078" + }, + "meta": { + "adaptercode": "relevantdigital" + }, + "targeting": { + "hb_bidder": "relevantdigital", + "hb_cache_host": "somedomain.com", + "hb_cache_path": "/analytics_cache/read", + "hb_pb": "11.10" + }, + "type": "video" + } + } + }, + "type": "video" + } + ], + "cur": "USD" + } + ] +} \ No newline at end of file diff --git a/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidBidMType.json b/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidBidMType.json new file mode 100644 index 00000000000..17473625e89 --- /dev/null +++ b/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidBidMType.json @@ -0,0 +1,99 @@ +{ + "mockBidRequest": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "imp": [ + { + "id": "div-1", + "banner": { + "format": [ + { + "w": 320, + "h": 320 + } + ] + }, + "ext": { + "bidder": { + "accountId": "620523ae7f4bbe1691bbb815", + "pbsHost": "fakeHost", + "placementId": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakeHost.relevant-digital.com/openrtb2/auction", + "body": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "imp": [ + { + "id": "div-1", + "banner": { + "format": [ + { + "w": 320, + "h": 320 + } + ] + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + } + ], + "ext": { + "prebid": { + "debug": false, + "storedrequest": { + "id": "620523ae7f4bbe1691bbb815" + } + }, + "relevant": { + "adapterType": "server", + "count": 1 + } + }, + "tmax": 750 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "seatbid": [ + { + "seat": "relevantdigital", + "bid": [ + { + "impid": "div-1", + "mtype": 10 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "failed to parse bid type, missing ext: div-1", + "comparison": "literal" + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + ] + } + ] +} diff --git a/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidBidType.json b/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidBidType.json new file mode 100644 index 00000000000..86c405c87c6 --- /dev/null +++ b/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidBidType.json @@ -0,0 +1,102 @@ +{ + "mockBidRequest": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "imp": [ + { + "id": "div-1", + "banner": { + "format": [ + { + "w": 320, + "h": 320 + } + ] + }, + "ext": { + "bidder": { + "accountId": "620523ae7f4bbe1691bbb815", + "pbsHost": "fakeHost", + "placementId": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakeHost.relevant-digital.com/openrtb2/auction", + "body": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "imp": [ + { + "id": "div-1", + "banner": { + "format": [ + { + "w": 320, + "h": 320 + } + ] + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + } + ], + "ext": { + "prebid": { + "debug": false, + "storedrequest": { + "id": "620523ae7f4bbe1691bbb815" + } + }, + "relevant": { + "adapterType": "server", + "count": 1 + } + }, + "tmax": 750 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "seatbid": [ + { + "seat": "relevantdigital", + "bid": [ + { + "impid": "div-1", + "ext": { + "prebid" : { + "type" : "invalid" + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "invalid BidType: invalid", + "comparison": "literal" + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [] + } + ] +} diff --git a/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidParam.json b/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidParam.json new file mode 100644 index 00000000000..011e332548c --- /dev/null +++ b/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidParam.json @@ -0,0 +1,42 @@ +{ + "mockBidRequest": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "imp": [ + { + "id": "div-1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + } + }, + { + "id": "div-1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "imp.ext not provided", + "comparison": "literal" + }, + { + "value": "ext.bidder not provided", + "comparison": "literal" + } + ], + "expectedBidResponses": [] +} \ No newline at end of file diff --git a/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidRequestCount.json b/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidRequestCount.json new file mode 100644 index 00000000000..37c6748d311 --- /dev/null +++ b/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidRequestCount.json @@ -0,0 +1,39 @@ +{ + "mockBidRequest": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "imp": [ + { + "id": "div-1", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "accountId": "620523ae7f4bbe1691bbb815", + "pbsHost": "fakeHost", + "placementId": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + ], + "ext": { + "relevant": { + "adapterType": "server", + "count": 10 + } + }, + "tmax": 750 + }, + "expectedMakeRequestsErrors": [ + { + "value": "failed to create bidRequest, error: too many requests", + "comparison": "literal" + } + ], + "expectedBidResponses": [] +} \ No newline at end of file diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 825dd411a96..d8fd57c3187 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -140,6 +140,7 @@ import ( "github.com/prebid/prebid-server/v2/adapters/pubnative" "github.com/prebid/prebid-server/v2/adapters/pulsepoint" "github.com/prebid/prebid-server/v2/adapters/pwbid" + "github.com/prebid/prebid-server/v2/adapters/relevantdigital" "github.com/prebid/prebid-server/v2/adapters/revcontent" "github.com/prebid/prebid-server/v2/adapters/richaudience" "github.com/prebid/prebid-server/v2/adapters/rise" @@ -338,6 +339,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderPubnative: pubnative.Builder, openrtb_ext.BidderPulsepoint: pulsepoint.Builder, openrtb_ext.BidderPWBid: pwbid.Builder, + openrtb_ext.BidderRelevantDigital: relevantdigital.Builder, openrtb_ext.BidderRevcontent: revcontent.Builder, openrtb_ext.BidderRichaudience: richaudience.Builder, openrtb_ext.BidderRise: rise.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 1f6eebbf6d8..9c2afdae13e 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -158,6 +158,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderPubnative, BidderPulsepoint, BidderPWBid, + BidderRelevantDigital, BidderRevcontent, BidderRichaudience, BidderRise, @@ -436,6 +437,7 @@ const ( BidderPubnative BidderName = "pubnative" BidderPulsepoint BidderName = "pulsepoint" BidderPWBid BidderName = "pwbid" + BidderRelevantDigital BidderName = "relevantdigital" BidderRevcontent BidderName = "revcontent" BidderRichaudience BidderName = "richaudience" BidderRise BidderName = "rise" diff --git a/openrtb_ext/imp_relevantdigital.go b/openrtb_ext/imp_relevantdigital.go new file mode 100644 index 00000000000..ec250557c2b --- /dev/null +++ b/openrtb_ext/imp_relevantdigital.go @@ -0,0 +1,8 @@ +package openrtb_ext + +type ExtRelevantDigital struct { + AccountId string `json:"accountId"` + PlacementId string `json:"placementId"` + Host string `json:"pbsHost"` + PbsBufferMs int `json:"pbsBufferMs"` +} diff --git a/static/bidder-info/relevantdigital.yaml b/static/bidder-info/relevantdigital.yaml new file mode 100644 index 00000000000..34d90e25e93 --- /dev/null +++ b/static/bidder-info/relevantdigital.yaml @@ -0,0 +1,15 @@ +endpoint: "https://{{.Host}}.relevant-digital.com/openrtb2/auction" +maintainer: + email: "support@relevant-digital.com" +gvlVendorID: 1100 +capabilities: + app: + mediaTypes: + - banner + - video + - native + site: + mediaTypes: + - banner + - video + - native diff --git a/static/bidder-params/relevantdigital.json b/static/bidder-params/relevantdigital.json new file mode 100644 index 00000000000..5f88d85fe58 --- /dev/null +++ b/static/bidder-params/relevantdigital.json @@ -0,0 +1,29 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Relevant Digital Adapter Params", + "description": "A schema which validates params accepted by the Relevant Digital adapter", + "type": "object", + "properties": { + "accountId": { + "type": "string", + "description": "An ID which identifies the Relevant Digital account ID" + }, + "placementId": { + "type": "string", + "description": "An ID which identifies the Relevant Digital placement ID" + }, + "pbsHost": { + "type": "string", + "description": "Prebid Server Host supplied by Relevant Digital" + }, + "pbsBufferMs": { + "type": "number", + "description": "TMax buffer, default is 250" + } + }, + "required": [ + "accountId", + "placementId", + "pbsHost" + ] +} From a198b68539030786e7d1b9ebbf4c0153c59a0bae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Fri, 24 Nov 2023 11:05:56 +0100 Subject: [PATCH 2/9] relevant: test mtype invalid --- .../invalidBidMTypeParsesExt.json | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 adapters/relevantdigital/relevantdigitaltest/supplemental/invalidBidMTypeParsesExt.json diff --git a/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidBidMTypeParsesExt.json b/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidBidMTypeParsesExt.json new file mode 100644 index 00000000000..e7492a4dd3c --- /dev/null +++ b/adapters/relevantdigital/relevantdigitaltest/supplemental/invalidBidMTypeParsesExt.json @@ -0,0 +1,157 @@ +{ + "mockBidRequest": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "imp": [ + { + "id": "div-1", + "banner": { + "format": [ + { + "w": 320, + "h": 320 + } + ] + }, + "ext": { + "bidder": { + "accountId": "620523ae7f4bbe1691bbb815", + "pbsHost": "fakeHost", + "placementId": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + }, + { + "id": "div-2", + "video": { + "mimes": null + }, + "ext": { + "bidder": { + "accountId": "620523ae7f4bbe1691bbb815", + "pbsHost": "fakeHost", + "placementId": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb818" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://fakeHost.relevant-digital.com/openrtb2/auction", + "body": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "imp": [ + { + "id": "div-1", + "banner": { + "format": [ + { + "w": 320, + "h": 320 + } + ] + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb817" + } + } + } + }, + { + "id": "div-2", + "video": { + "mimes": null + }, + "ext": { + "prebid": { + "storedrequest": { + "id": "620525862d7518bfd4bbb81e_620523b5d1dbed6b0fbbb818" + } + } + } + } + ], + "ext": { + "prebid": { + "debug": false, + "storedrequest": { + "id": "620523ae7f4bbe1691bbb815" + } + }, + "relevant": { + "adapterType": "server", + "count": 1 + } + }, + "tmax": 750 + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "3621f78b-abdf-4562-8eca-1c5e893387d0", + "seatbid": [ + { + "seat": "relevantdigital", + "bid": [ + { + "impid": "div-1", + "mtype": 0, + "ext" : { + "prebid" : { + "type": "banner" + } + } + }, + { + "impid": "div-2", + "mtype": 0, + "ext": { + "prebid": { + "type": "video" + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [{ + "currency": "USD", + "bids": [ + { + "bid": { + "id": "", + "impid": "div-1", + "price": 0, + "ext": { + "prebid": { + "type": "banner" + } + } + }, + "type": "banner" + }, + { + "bid": { + "id": "", + "impid": "div-2", + "price": 0, + "ext": { + "prebid": { + "type": "video" + } + } + }, + "type": "video" + } + ] + }] +} From 1e2c113879eaaef8d8c6ecf1e15f4aa40deb2d7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Fri, 24 Nov 2023 11:09:41 +0100 Subject: [PATCH 3/9] relevant: use private constants --- adapters/relevantdigital/relevantdigital.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/adapters/relevantdigital/relevantdigital.go b/adapters/relevantdigital/relevantdigital.go index a623c0f5fbc..68cf14f250c 100644 --- a/adapters/relevantdigital/relevantdigital.go +++ b/adapters/relevantdigital/relevantdigital.go @@ -22,11 +22,11 @@ type adapter struct { name string } -const RELEVANT_DOMAIN = ".relevant-digital.com" -const DEFAULT_TIMEOUT = 1000 -const DEFAULT_BUFFER_MS = 250 -const STORED_REQUEST_EXT = "{\"prebid\":{\"debug\":%t,\"storedrequest\":{\"id\":\"%s\"}},\"relevant\":{\"count\":%d,\"adapterType\":\"server\"}}" -const STORED_IMP_EXT = "{\"prebid\":{\"storedrequest\":{\"id\":\"%s\"}}}" +const relevant_domain = ".relevant-digital.com" +const default_timeout = 1000 +const default_bufffer_ms = 250 +const stored_request_ext = "{\"prebid\":{\"debug\":%t,\"storedrequest\":{\"id\":\"%s\"}},\"relevant\":{\"count\":%d,\"adapterType\":\"server\"}}" +const stored_imp_ext = "{\"prebid\":{\"storedrequest\":{\"id\":\"%s\"}}}" func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { template, err := template.New("endpointTemplate").Parse(config.Endpoint) @@ -58,12 +58,12 @@ func patchBidRequestExt(prebidBidRequest *openrtb2.BidRequest, id string) error debug = false } - prebidBidRequest.Ext = []byte(fmt.Sprintf(STORED_REQUEST_EXT, debug, id, count)) + prebidBidRequest.Ext = []byte(fmt.Sprintf(stored_request_ext, debug, id, count)) return nil } func patchBidImpExt(imp *openrtb2.Imp, id string) { - imp.Ext = []byte(fmt.Sprintf(STORED_IMP_EXT, id)) + imp.Ext = []byte(fmt.Sprintf(stored_imp_ext, id)) if imp.Banner != nil { imp.Banner.Ext = nil } @@ -81,7 +81,7 @@ func patchBidImpExt(imp *openrtb2.Imp, id string) { func setTMax(prebidBidRequest *openrtb2.BidRequest, pbsBufferMs int) { timeout := float64(prebidBidRequest.TMax) if timeout <= 0 { - timeout = DEFAULT_TIMEOUT + timeout = default_timeout } buffer := float64(pbsBufferMs) prebidBidRequest.TMax = int64(math.Min(math.Max(timeout-buffer, buffer), timeout)) @@ -125,7 +125,7 @@ func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtRelevantDigital, error Message: "imp.ext not provided", } } - relevantExt := openrtb_ext.ExtRelevantDigital{PbsBufferMs: DEFAULT_BUFFER_MS} + relevantExt := openrtb_ext.ExtRelevantDigital{PbsBufferMs: default_bufffer_ms} if err := json.Unmarshal(bidderExt.Bidder, &relevantExt); err != nil { return nil, &errortypes.BadInput{ Message: "ext.bidder not provided", @@ -137,7 +137,7 @@ func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtRelevantDigital, error func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtRelevantDigital) (string, error) { params.Host = strings.ReplaceAll(params.Host, "http://", "") params.Host = strings.ReplaceAll(params.Host, "https://", "") - params.Host = strings.ReplaceAll(params.Host, RELEVANT_DOMAIN, "") + params.Host = strings.ReplaceAll(params.Host, relevant_domain, "") endpointParams := macros.EndpointTemplateParams{Host: params.Host} return macros.ResolveMacros(a.endpoint, endpointParams) From 67d3c2c3fe6c98dd70ed9f69e36532f1ec1b6a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Fri, 24 Nov 2023 13:36:30 +0100 Subject: [PATCH 4/9] relevant: create json request instead of deep copy --- adapters/relevantdigital/relevantdigital.go | 51 +++++++++------------ 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/adapters/relevantdigital/relevantdigital.go b/adapters/relevantdigital/relevantdigital.go index 68cf14f250c..3463f2b79e8 100644 --- a/adapters/relevantdigital/relevantdigital.go +++ b/adapters/relevantdigital/relevantdigital.go @@ -64,18 +64,6 @@ func patchBidRequestExt(prebidBidRequest *openrtb2.BidRequest, id string) error func patchBidImpExt(imp *openrtb2.Imp, id string) { imp.Ext = []byte(fmt.Sprintf(stored_imp_ext, id)) - if imp.Banner != nil { - imp.Banner.Ext = nil - } - if imp.Video != nil { - imp.Video.Ext = nil - } - if imp.Native != nil { - imp.Native.Ext = nil - } - if imp.Audio != nil { - imp.Audio.Ext = nil - } } func setTMax(prebidBidRequest *openrtb2.BidRequest, pbsBufferMs int) { @@ -87,35 +75,37 @@ func setTMax(prebidBidRequest *openrtb2.BidRequest, pbsBufferMs int) { prebidBidRequest.TMax = int64(math.Min(math.Max(timeout-buffer, buffer), timeout)) } -func cloneBidRequest(prebidBidRequest *openrtb2.BidRequest) (*openrtb2.BidRequest, error) { - jsonRes, err := json.Marshal(prebidBidRequest) - if err != nil { - return nil, err - } - var copy openrtb2.BidRequest - err = json.Unmarshal(jsonRes, ©) - return ©, err -} - func createBidRequest(prebidBidRequest *openrtb2.BidRequest, params []*openrtb_ext.ExtRelevantDigital) (*openrtb2.BidRequest, error) { - bidRequestCopy, err := cloneBidRequest(prebidBidRequest) - if err != nil { - return nil, err - } + bidRequestCopy := *prebidBidRequest; - err = patchBidRequestExt(bidRequestCopy, params[0].AccountId) + err := patchBidRequestExt(&bidRequestCopy, params[0].AccountId) if err != nil { return nil, &errortypes.BadInput{ Message: fmt.Sprintf("failed to create bidRequest, error: %s", err), } } - setTMax(bidRequestCopy, params[0].PbsBufferMs) + setTMax(&bidRequestCopy, params[0].PbsBufferMs) for idx := range bidRequestCopy.Imp { patchBidImpExt(&bidRequestCopy.Imp[idx], params[idx].PlacementId) } - return bidRequestCopy, err + return &bidRequestCopy, err +} + +func createJSONRequest(bidRequest *openrtb2.BidRequest) ([]byte, error){ + reqJSON, err := json.Marshal(bidRequest) + if err != nil { + return nil, err + } + + types := []string{"banner", "video", "native", "audio"} + for idx := range bidRequest.Imp { + for _, key := range types { + reqJSON = jsonparser.Delete(reqJSON, "imp", fmt.Sprintf("[%d]", idx), key, "ext") + } + } + return reqJSON, nil } func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtRelevantDigital, error) { @@ -150,7 +140,8 @@ func (a *adapter) buildAdapterRequest(prebidBidRequest *openrtb2.BidRequest, par return nil, err } - reqJSON, err := json.Marshal(newBidRequest) + reqJSON, err := createJSONRequest(newBidRequest); + if err != nil { return nil, err } From 176e9020f6c4681b95073b564d1ba87e74194637 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Fri, 24 Nov 2023 14:31:24 +0100 Subject: [PATCH 5/9] relevant: use unmarshal/marshal instead of jsonparser --- adapters/relevantdigital/relevantdigital.go | 68 ++++++++++++++------- 1 file changed, 45 insertions(+), 23 deletions(-) diff --git a/adapters/relevantdigital/relevantdigital.go b/adapters/relevantdigital/relevantdigital.go index 3463f2b79e8..910ee482bf9 100644 --- a/adapters/relevantdigital/relevantdigital.go +++ b/adapters/relevantdigital/relevantdigital.go @@ -25,8 +25,21 @@ type adapter struct { const relevant_domain = ".relevant-digital.com" const default_timeout = 1000 const default_bufffer_ms = 250 -const stored_request_ext = "{\"prebid\":{\"debug\":%t,\"storedrequest\":{\"id\":\"%s\"}},\"relevant\":{\"count\":%d,\"adapterType\":\"server\"}}" -const stored_imp_ext = "{\"prebid\":{\"storedrequest\":{\"id\":\"%s\"}}}" + +type PrebidExt struct { + StoredRequest struct { + Id string `json:"id"` + } `json:"storedrequest"` + Debug bool `json:"debug"` +} + +type RelevantExt struct { + Relevant struct { + Count int `json:"count"` + AdapterType string `json:"adapterType"` + } `json:"relevant"` + Prebid PrebidExt `json:"prebid"` +} func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { template, err := template.New("endpointTemplate").Parse(config.Endpoint) @@ -40,12 +53,17 @@ func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server co } func patchBidRequestExt(prebidBidRequest *openrtb2.BidRequest, id string) error { - count, cerr := jsonparser.GetInt(prebidBidRequest.Ext, "relevant", "count") - if cerr != nil { - count = 0 + var bidRequestExt RelevantExt + if len(prebidBidRequest.Ext) != 0 { + if err := json.Unmarshal(prebidBidRequest.Ext, &bidRequestExt); err != nil { + return &errortypes.FailedToRequestBids{ + Message: fmt.Sprintf("failed to unmarshal ext, %s", prebidBidRequest.Ext), + } + } } - if count >= 5 { + count := bidRequestExt.Relevant.Count + if bidRequestExt.Relevant.Count >= 5 { return &errortypes.FailedToRequestBids{ Message: "too many requests", } @@ -53,17 +71,22 @@ func patchBidRequestExt(prebidBidRequest *openrtb2.BidRequest, id string) error count = count + 1 } - debug, derr := jsonparser.GetBoolean(prebidBidRequest.Ext, "prebid", "debug") - if derr != nil { - debug = false - } + bidRequestExt.Relevant.Count = count + bidRequestExt.Relevant.AdapterType = "server" + bidRequestExt.Prebid.StoredRequest.Id = id - prebidBidRequest.Ext = []byte(fmt.Sprintf(stored_request_ext, debug, id, count)) + ext, err := json.Marshal(bidRequestExt) + if err != nil { + return &errortypes.FailedToRequestBids{ + Message: "failed to marshal", + } + } + prebidBidRequest.Ext = ext return nil } func patchBidImpExt(imp *openrtb2.Imp, id string) { - imp.Ext = []byte(fmt.Sprintf(stored_imp_ext, id)) + imp.Ext = []byte(fmt.Sprintf("{\"prebid\":{\"storedrequest\":{\"id\":\"%s\"}}}", id)) } func setTMax(prebidBidRequest *openrtb2.BidRequest, pbsBufferMs int) { @@ -75,8 +98,8 @@ func setTMax(prebidBidRequest *openrtb2.BidRequest, pbsBufferMs int) { prebidBidRequest.TMax = int64(math.Min(math.Max(timeout-buffer, buffer), timeout)) } -func createBidRequest(prebidBidRequest *openrtb2.BidRequest, params []*openrtb_ext.ExtRelevantDigital) (*openrtb2.BidRequest, error) { - bidRequestCopy := *prebidBidRequest; +func createBidRequest(prebidBidRequest *openrtb2.BidRequest, params []*openrtb_ext.ExtRelevantDigital) ([]byte, error) { + bidRequestCopy := *prebidBidRequest err := patchBidRequestExt(&bidRequestCopy, params[0].AccountId) if err != nil { @@ -90,10 +113,15 @@ func createBidRequest(prebidBidRequest *openrtb2.BidRequest, params []*openrtb_e for idx := range bidRequestCopy.Imp { patchBidImpExt(&bidRequestCopy.Imp[idx], params[idx].PlacementId) } - return &bidRequestCopy, err + + if err != nil { + return nil, err + } + + return createJSONRequest(&bidRequestCopy) } -func createJSONRequest(bidRequest *openrtb2.BidRequest) ([]byte, error){ +func createJSONRequest(bidRequest *openrtb2.BidRequest) ([]byte, error) { reqJSON, err := json.Marshal(bidRequest) if err != nil { return nil, err @@ -134,13 +162,7 @@ func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtRelevantDigital) (stri } func (a *adapter) buildAdapterRequest(prebidBidRequest *openrtb2.BidRequest, params []*openrtb_ext.ExtRelevantDigital) (*adapters.RequestData, error) { - newBidRequest, err := createBidRequest(prebidBidRequest, params) - - if err != nil { - return nil, err - } - - reqJSON, err := createJSONRequest(newBidRequest); + reqJSON, err := createBidRequest(prebidBidRequest, params) if err != nil { return nil, err From 31c259fd8cedbfa0dc49cf22988adb9fbcd43f06 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Fri, 24 Nov 2023 15:18:48 +0100 Subject: [PATCH 6/9] relevant: check for supported media types --- adapters/relevantdigital/relevantdigital.go | 19 +++++++++++++++++++ static/bidder-info/relevantdigital.yaml | 2 ++ 2 files changed, 21 insertions(+) diff --git a/adapters/relevantdigital/relevantdigital.go b/adapters/relevantdigital/relevantdigital.go index 910ee482bf9..6cd8d9a2acd 100644 --- a/adapters/relevantdigital/relevantdigital.go +++ b/adapters/relevantdigital/relevantdigital.go @@ -256,6 +256,20 @@ func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { } } +func isSupportedMediaType(bidType openrtb_ext.BidType) error { + switch bidType { + case openrtb_ext.BidTypeBanner: + fallthrough + case openrtb_ext.BidTypeVideo: + fallthrough + case openrtb_ext.BidTypeAudio: + fallthrough + case openrtb_ext.BidTypeNative: + return nil + } + return fmt.Errorf("bid type not supported %s", bidType) +} + func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { if adapters.IsResponseStatusCodeNoContent(responseData) { return nil, nil @@ -276,8 +290,13 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.R for _, seatBid := range response.SeatBid { for i, bid := range seatBid.Bid { bidType, err := getMediaTypeForBid(bid) + if err != nil { errs = append(errs, err) + continue + } + if err := isSupportedMediaType(bidType); err != nil { + errs = append(errs, err) } else { b := &adapters.TypedBid{ Bid: &seatBid.Bid[i], diff --git a/static/bidder-info/relevantdigital.yaml b/static/bidder-info/relevantdigital.yaml index 34d90e25e93..cc873f2804a 100644 --- a/static/bidder-info/relevantdigital.yaml +++ b/static/bidder-info/relevantdigital.yaml @@ -8,8 +8,10 @@ capabilities: - banner - video - native + - audio site: mediaTypes: - banner - video - native + - audio From b64f23b45743c6e93c931922119fa3ba6052131a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Sun, 26 Nov 2023 16:49:17 +0100 Subject: [PATCH 7/9] relevant: only overwrite/remove necessary ext data --- adapters/relevantdigital/relevantdigital.go | 32 ++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/adapters/relevantdigital/relevantdigital.go b/adapters/relevantdigital/relevantdigital.go index 6cd8d9a2acd..8cf10a26ad1 100644 --- a/adapters/relevantdigital/relevantdigital.go +++ b/adapters/relevantdigital/relevantdigital.go @@ -15,6 +15,7 @@ import ( "github.com/prebid/prebid-server/v2/errortypes" "github.com/prebid/prebid-server/v2/macros" "github.com/prebid/prebid-server/v2/openrtb_ext" + jsonpatch "gopkg.in/evanphx/json-patch.v4" ) type adapter struct { @@ -81,7 +82,19 @@ func patchBidRequestExt(prebidBidRequest *openrtb2.BidRequest, id string) error Message: "failed to marshal", } } - prebidBidRequest.Ext = ext + + if len(prebidBidRequest.Ext) == 0 { + prebidBidRequest.Ext = ext + return nil + } + + patchedExt, err := jsonpatch.MergePatch(prebidBidRequest.Ext, ext) + if err != nil { + return &errortypes.FailedToRequestBids{ + Message: fmt.Sprintf("failed patch ext, %s", err), + } + } + prebidBidRequest.Ext = patchedExt return nil } @@ -127,11 +140,22 @@ func createJSONRequest(bidRequest *openrtb2.BidRequest) ([]byte, error) { return nil, err } - types := []string{"banner", "video", "native", "audio"} + // Scrub previous ext data from relevant, if any + // imp[].ext.context.relevant + // imp[].[banner/native/video/audio].ext.relevant + impKeyTypes := []string{"banner", "video", "native", "audio"} for idx := range bidRequest.Imp { - for _, key := range types { - reqJSON = jsonparser.Delete(reqJSON, "imp", fmt.Sprintf("[%d]", idx), key, "ext") + for _, key := range impKeyTypes { + reqJSON = jsonparser.Delete(reqJSON, "imp", fmt.Sprintf("[%d]", idx), key, "ext", "relevant") } + reqJSON = jsonparser.Delete(reqJSON, "imp", fmt.Sprintf("[%d]", idx), "ext", "context", "relevant") + } + + // Scrub previous prebid data (to not set cache on wrong servers) + // ext.prebid.[cache/targeting/aliases] + prebidKeyTypes := []string{"cache", "targeting", "aliases"} + for _, key := range prebidKeyTypes { + reqJSON = jsonparser.Delete(reqJSON, "ext", "prebid", key) } return reqJSON, nil } From 26f40326af5471a7cf86aa455ec870fff19c555d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Mon, 27 Nov 2023 09:21:02 +0100 Subject: [PATCH 8/9] relevant: use private scoped struct --- adapters/relevantdigital/relevantdigital.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/adapters/relevantdigital/relevantdigital.go b/adapters/relevantdigital/relevantdigital.go index 8cf10a26ad1..816edb91437 100644 --- a/adapters/relevantdigital/relevantdigital.go +++ b/adapters/relevantdigital/relevantdigital.go @@ -27,19 +27,19 @@ const relevant_domain = ".relevant-digital.com" const default_timeout = 1000 const default_bufffer_ms = 250 -type PrebidExt struct { +type prebidExt struct { StoredRequest struct { Id string `json:"id"` } `json:"storedrequest"` Debug bool `json:"debug"` } -type RelevantExt struct { +type relevantExt struct { Relevant struct { Count int `json:"count"` AdapterType string `json:"adapterType"` } `json:"relevant"` - Prebid PrebidExt `json:"prebid"` + Prebid prebidExt `json:"prebid"` } func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { @@ -54,7 +54,7 @@ func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server co } func patchBidRequestExt(prebidBidRequest *openrtb2.BidRequest, id string) error { - var bidRequestExt RelevantExt + var bidRequestExt relevantExt if len(prebidBidRequest.Ext) != 0 { if err := json.Unmarshal(prebidBidRequest.Ext, &bidRequestExt); err != nil { return &errortypes.FailedToRequestBids{ From ed8005b92c12785c7674b02b4ae15f2ba58b8daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Lindstr=C3=B6m?= Date: Tue, 28 Nov 2023 20:13:29 +0100 Subject: [PATCH 9/9] relevant: remove extraneous error check --- adapters/relevantdigital/relevantdigital.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/adapters/relevantdigital/relevantdigital.go b/adapters/relevantdigital/relevantdigital.go index 816edb91437..e8b1df8c100 100644 --- a/adapters/relevantdigital/relevantdigital.go +++ b/adapters/relevantdigital/relevantdigital.go @@ -127,10 +127,6 @@ func createBidRequest(prebidBidRequest *openrtb2.BidRequest, params []*openrtb_e patchBidImpExt(&bidRequestCopy.Imp[idx], params[idx].PlacementId) } - if err != nil { - return nil, err - } - return createJSONRequest(&bidRequestCopy) }