From 50a52f14ba3dcbe78bcd15bb2edf5d558fd61e7c Mon Sep 17 00:00:00 2001 From: Anthony Richir Date: Tue, 3 Feb 2026 21:05:09 +0100 Subject: [PATCH 1/3] Add Proxistore adapter with unit tests, schema, and configuration --- .../workflows/scripts/codepath-notification | 1 + adapters/proxistore/params_test.go | 51 ++++++ adapters/proxistore/proxistore.go | 154 ++++++++++++++++++ adapters/proxistore/proxistore_test.go | 27 +++ .../exemplary/multi-imp-banner.json | 147 +++++++++++++++++ .../exemplary/simple-banner.json | 128 +++++++++++++++ .../supplemental/bad-response.json | 61 +++++++ .../supplemental/missing-params.json | 27 +++ .../supplemental/status-204.json | 55 +++++++ .../supplemental/status-400.json | 61 +++++++ .../supplemental/status-500.json | 61 +++++++ exchange/adapter_builders.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_proxistore.go | 6 + static/bidder-info/proxistore.yaml | 24 +++ static/bidder-params/proxistore.json | 19 +++ 16 files changed, 826 insertions(+) create mode 100644 adapters/proxistore/params_test.go create mode 100644 adapters/proxistore/proxistore.go create mode 100644 adapters/proxistore/proxistore_test.go create mode 100644 adapters/proxistore/proxistoretest/exemplary/multi-imp-banner.json create mode 100644 adapters/proxistore/proxistoretest/exemplary/simple-banner.json create mode 100644 adapters/proxistore/proxistoretest/supplemental/bad-response.json create mode 100644 adapters/proxistore/proxistoretest/supplemental/missing-params.json create mode 100644 adapters/proxistore/proxistoretest/supplemental/status-204.json create mode 100644 adapters/proxistore/proxistoretest/supplemental/status-400.json create mode 100644 adapters/proxistore/proxistoretest/supplemental/status-500.json create mode 100644 openrtb_ext/imp_proxistore.go create mode 100644 static/bidder-info/proxistore.yaml create mode 100644 static/bidder-params/proxistore.json diff --git a/.github/workflows/scripts/codepath-notification b/.github/workflows/scripts/codepath-notification index 8a3bae8daca..b917c6d2019 100644 --- a/.github/workflows/scripts/codepath-notification +++ b/.github/workflows/scripts/codepath-notification @@ -19,3 +19,4 @@ adapters/ix|imp_ix|ix.json|ix.yaml: pdu-supply-prebid@indexexchange.com medianet: prebid@media.net gumgum: prebid@gumgum.com kargo: kraken@kargo.com +proxistore: proxistore-technics@proxistore.com diff --git a/adapters/proxistore/params_test.go b/adapters/proxistore/params_test.go new file mode 100644 index 00000000000..bb1ddba43a2 --- /dev/null +++ b/adapters/proxistore/params_test.go @@ -0,0 +1,51 @@ +package proxistore + +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 schema. %v", err) + } + + for _, p := range validParams { + if err := validator.Validate(openrtb_ext.BidderProxistore, 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.BidderProxistore, json.RawMessage(p)); err == nil { + t.Errorf("Schema allowed invalid params: %s", p) + } + } +} + +var validParams = []string{ + `{"website": "example.com", "language": "fr"}`, + `{"website": "test-site", "language": "en"}`, + `{"website": "publisher.be", "language": "nl"}`, +} + +var invalidParams = []string{ + `{}`, + `{"website": "example.com"}`, + `{"language": "fr"}`, + `{"website": "", "language": "fr"}`, + `{"website": "example.com", "language": ""}`, + `{"website": 123, "language": "fr"}`, + `{"website": "example.com", "language": 456}`, + `null`, +} diff --git a/adapters/proxistore/proxistore.go b/adapters/proxistore/proxistore.go new file mode 100644 index 00000000000..604e451d4c3 --- /dev/null +++ b/adapters/proxistore/proxistore.go @@ -0,0 +1,154 @@ +package proxistore + +import ( + "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 adapter struct { + endpoint string +} + +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + bidder := &adapter{ + endpoint: config.Endpoint, + } + return bidder, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errors []error + + // Validate that all impressions have required bidder params + for _, imp := range request.Imp { + var extBidder adapters.ExtImpBidder + if err := jsonutil.Unmarshal(imp.Ext, &extBidder); err != nil { + errors = append(errors, &errortypes.BadInput{ + Message: fmt.Sprintf("Error parsing imp[%s].ext: %s", imp.ID, err.Error()), + }) + continue + } + + var bidderExt openrtb_ext.ExtImpProxistore + if err := jsonutil.Unmarshal(extBidder.Bidder, &bidderExt); err != nil { + errors = append(errors, &errortypes.BadInput{ + Message: fmt.Sprintf("Error parsing imp[%s].ext.bidder: %s", imp.ID, err.Error()), + }) + continue + } + + if bidderExt.Website == "" || bidderExt.Language == "" { + errors = append(errors, &errortypes.BadInput{ + Message: fmt.Sprintf("imp[%s]: website and language are required bidder params", imp.ID), + }) + } + } + + if len(errors) > 0 { + return nil, errors + } + + requestJSON, err := jsonutil.Marshal(request) + if err != nil { + return nil, []error{err} + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json") + headers.Add("Accept", "application/json") + headers.Add("X-Prebid-Source", "server") + + requestData := &adapters.RequestData{ + Method: "POST", + Uri: a.endpoint, + Body: requestJSON, + Headers: headers, + ImpIDs: openrtb_ext.GetImpIDs(request.Imp), + } + + return []*adapters.RequestData{requestData}, nil +} + +func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if responseData.StatusCode == http.StatusNoContent { + return nil, nil + } + + if responseData.StatusCode == http.StatusBadRequest { + err := &errortypes.BadInput{ + Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.", + } + return nil, []error{err} + } + + if responseData.StatusCode != http.StatusOK { + err := &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode), + } + return nil, []error{err} + } + + var response openrtb2.BidResponse + if err := jsonutil.Unmarshal(responseData.Body, &response); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) + bidResponse.Currency = response.Cur + + var errors []error + for _, seatBid := range response.SeatBid { + for i, bid := range seatBid.Bid { + bidType, err := getMediaTypeForBid(bid, request.Imp) + if err != nil { + errors = append(errors, err) + continue + } + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &seatBid.Bid[i], + BidType: bidType, + }) + } + } + + return bidResponse, errors +} + +func getMediaTypeForBid(bid openrtb2.Bid, imps []openrtb2.Imp) (openrtb_ext.BidType, error) { + // First try to get type from bid.ext.prebid.type + if bid.Ext != nil { + var bidExt openrtb_ext.ExtBid + if err := jsonutil.Unmarshal(bid.Ext, &bidExt); err == nil && bidExt.Prebid != nil { + return openrtb_ext.ParseBidType(string(bidExt.Prebid.Type)) + } + } + + // Fall back to determining type from the impression + for _, imp := range imps { + if imp.ID == bid.ImpID { + if imp.Banner != nil { + return openrtb_ext.BidTypeBanner, nil + } + if imp.Video != nil { + return openrtb_ext.BidTypeVideo, nil + } + if imp.Audio != nil { + return openrtb_ext.BidTypeAudio, nil + } + if imp.Native != nil { + return openrtb_ext.BidTypeNative, nil + } + } + } + + return "", &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Failed to determine media type for bid %s (imp %s)", bid.ID, bid.ImpID), + } +} diff --git a/adapters/proxistore/proxistore_test.go b/adapters/proxistore/proxistore_test.go new file mode 100644 index 00000000000..b0a039d6e23 --- /dev/null +++ b/adapters/proxistore/proxistore_test.go @@ -0,0 +1,27 @@ +package proxistore + +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/stretchr/testify/assert" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder( + openrtb_ext.BidderProxistore, + config.Adapter{ + Endpoint: "https://api.proxistore.com/v3/rtb/openrtb", + }, + config.Server{ + ExternalUrl: "http://hosturl.com", + GvlID: 1, + DataCenter: "2", + }, + ) + + assert.NoError(t, buildErr) + adapterstest.RunJSONBidderTest(t, "proxistoretest", bidder) +} diff --git a/adapters/proxistore/proxistoretest/exemplary/multi-imp-banner.json b/adapters/proxistore/proxistoretest/exemplary/multi-imp-banner.json new file mode 100644 index 00000000000..0953d6a8217 --- /dev/null +++ b/adapters/proxistore/proxistoretest/exemplary/multi-imp-banner.json @@ -0,0 +1,147 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "Mozilla/5.0" + }, + "site": { + "domain": "example.com", + "page": "https://example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + }, + { + "id": "imp-2", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://api.proxistore.com/v3/rtb/openrtb", + "body": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "Mozilla/5.0" + }, + "site": { + "domain": "example.com", + "page": "https://example.com/page" + }, + "imp": [ + { + "id": "imp-1", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + }, + { + "id": "imp-2", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "impIDs": ["imp-1", "imp-2"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "bid-1", + "impid": "imp-1", + "price": 1.50, + "adm": "
Ad 1
", + "crid": "creative-1", + "w": 300, + "h": 250 + }, + { + "id": "bid-2", + "impid": "imp-2", + "price": 0.80, + "adm": "
Ad 2
", + "crid": "creative-2", + "w": 728, + "h": 90 + } + ], + "seat": "proxistore" + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "bid-1", + "impid": "imp-1", + "price": 1.50, + "adm": "
Ad 1
", + "crid": "creative-1", + "w": 300, + "h": 250 + }, + "type": "banner" + }, + { + "bid": { + "id": "bid-2", + "impid": "imp-2", + "price": 0.80, + "adm": "
Ad 2
", + "crid": "creative-2", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/proxistore/proxistoretest/exemplary/simple-banner.json b/adapters/proxistore/proxistoretest/exemplary/simple-banner.json new file mode 100644 index 00000000000..5dbea3f9336 --- /dev/null +++ b/adapters/proxistore/proxistoretest/exemplary/simple-banner.json @@ -0,0 +1,128 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + }, + "site": { + "id": "site-id", + "domain": "example.com", + "page": "https://example.com/page" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + {"w": 300, "h": 250}, + {"w": 300, "h": 600} + ] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ], + "regs": { + "gdpr": 1 + }, + "user": { + "ext": { + "consent": "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://api.proxistore.com/v3/rtb/openrtb", + "body": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + }, + "site": { + "id": "site-id", + "domain": "example.com", + "page": "https://example.com/page" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + {"w": 300, "h": 250}, + {"w": 300, "h": 600} + ] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ], + "regs": { + "gdpr": 1 + }, + "user": { + "ext": { + "consent": "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA" + } + } + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 1.23, + "adm": "
Ad creative
", + "adomain": ["advertiser.com"], + "crid": "creative-123", + "w": 300, + "h": 250 + } + ], + "seat": "proxistore" + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 1.23, + "adm": "
Ad creative
", + "adomain": ["advertiser.com"], + "crid": "creative-123", + "w": 300, + "h": 250 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/proxistore/proxistoretest/supplemental/bad-response.json b/adapters/proxistore/proxistoretest/supplemental/bad-response.json new file mode 100644 index 00000000000..850d729e252 --- /dev/null +++ b/adapters/proxistore/proxistoretest/supplemental/bad-response.json @@ -0,0 +1,61 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://api.proxistore.com/v3/rtb/openrtb", + "body": { + "id": "test-request-id", + "site": { + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": "not valid json" + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "expect { or n, but found \"", + "comparison": "literal" + } + ] +} diff --git a/adapters/proxistore/proxistoretest/supplemental/missing-params.json b/adapters/proxistore/proxistoretest/supplemental/missing-params.json new file mode 100644 index 00000000000..c03b071ad7b --- /dev/null +++ b/adapters/proxistore/proxistoretest/supplemental/missing-params.json @@ -0,0 +1,27 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com" + } + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "imp[test-imp-id]: website and language are required bidder params", + "comparison": "literal" + } + ] +} diff --git a/adapters/proxistore/proxistoretest/supplemental/status-204.json b/adapters/proxistore/proxistoretest/supplemental/status-204.json new file mode 100644 index 00000000000..1375e9c65fa --- /dev/null +++ b/adapters/proxistore/proxistoretest/supplemental/status-204.json @@ -0,0 +1,55 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://api.proxistore.com/v3/rtb/openrtb", + "body": { + "id": "test-request-id", + "site": { + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/proxistore/proxistoretest/supplemental/status-400.json b/adapters/proxistore/proxistoretest/supplemental/status-400.json new file mode 100644 index 00000000000..7ffd7aa8220 --- /dev/null +++ b/adapters/proxistore/proxistoretest/supplemental/status-400.json @@ -0,0 +1,61 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://api.proxistore.com/v3/rtb/openrtb", + "body": { + "id": "test-request-id", + "site": { + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 400, + "body": {} + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.", + "comparison": "literal" + } + ] +} diff --git a/adapters/proxistore/proxistoretest/supplemental/status-500.json b/adapters/proxistore/proxistoretest/supplemental/status-500.json new file mode 100644 index 00000000000..597cad1caa7 --- /dev/null +++ b/adapters/proxistore/proxistoretest/supplemental/status-500.json @@ -0,0 +1,61 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://api.proxistore.com/v3/rtb/openrtb", + "body": { + "id": "test-request-id", + "site": { + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 500, + "body": {} + } + } + ], + "expectedBidResponses": [], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Run with request.debug = 1 for more info.", + "comparison": "literal" + } + ] +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 9a2aaa0bd21..1d339f6b05d 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -183,6 +183,7 @@ import ( "github.com/prebid/prebid-server/v3/adapters/pangle" "github.com/prebid/prebid-server/v3/adapters/pgamssp" "github.com/prebid/prebid-server/v3/adapters/playdigo" + "github.com/prebid/prebid-server/v3/adapters/proxistore" "github.com/prebid/prebid-server/v3/adapters/pubmatic" "github.com/prebid/prebid-server/v3/adapters/pubnative" "github.com/prebid/prebid-server/v3/adapters/pubrise" @@ -452,6 +453,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderPangle: pangle.Builder, openrtb_ext.BidderPGAMSsp: pgamssp.Builder, openrtb_ext.BidderPlaydigo: playdigo.Builder, + openrtb_ext.BidderProxistore: proxistore.Builder, openrtb_ext.BidderPubmatic: pubmatic.Builder, openrtb_ext.BidderPubnative: pubnative.Builder, openrtb_ext.BidderPubrise: pubrise.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 8cd543c1f16..eff4582a9ba 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -201,6 +201,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderPangle, BidderPGAMSsp, BidderPlaydigo, + BidderProxistore, BidderPubmatic, BidderPubrise, BidderPubnative, @@ -574,6 +575,7 @@ const ( BidderPangle BidderName = "pangle" BidderPGAMSsp BidderName = "pgamssp" BidderPlaydigo BidderName = "playdigo" + BidderProxistore BidderName = "proxistore" BidderPubmatic BidderName = "pubmatic" BidderPubrise BidderName = "pubrise" BidderPubnative BidderName = "pubnative" diff --git a/openrtb_ext/imp_proxistore.go b/openrtb_ext/imp_proxistore.go new file mode 100644 index 00000000000..6c6b5bd5f03 --- /dev/null +++ b/openrtb_ext/imp_proxistore.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ExtImpProxistore struct { + Website string `json:"website"` + Language string `json:"language"` +} diff --git a/static/bidder-info/proxistore.yaml b/static/bidder-info/proxistore.yaml new file mode 100644 index 00000000000..53bcb4edc99 --- /dev/null +++ b/static/bidder-info/proxistore.yaml @@ -0,0 +1,24 @@ +# We have the following regional endpoint domains: us-east and us-west +# Please deploy this config in each of your datacenters with the appropriate regional subdomain +endpoint: "https://api.proxistore.com/v3/rtb/openrtb" +geoscope: + - EUR +maintainer: + email: support@proxistore.com +gvlVendorID: 418 +openrtb: + version: 2.6 +capabilities: + app: + mediaTypes: + - banner + site: + mediaTypes: + - banner +userSync: + redirect: + url: https://abs.proxistore.com/v3/rtb/sync/prebid-server/redirect?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect={{.RedirectURL}} + userMacro: $UID + iframe: + url: https://abs.proxistore.com/v3/rtb/sync/prebid-server/iframe?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect={{.RedirectURL}} + userMacro: $UID \ No newline at end of file diff --git a/static/bidder-params/proxistore.json b/static/bidder-params/proxistore.json new file mode 100644 index 00000000000..d9360a49ced --- /dev/null +++ b/static/bidder-params/proxistore.json @@ -0,0 +1,19 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Proxistore Adapter Params", + "description": "A schema which validates params accepted by the Proxistore adapter", + "type": "object", + "properties": { + "website": { + "type": "string", + "minLength": 1, + "description": "Publisher website identifier" + }, + "language": { + "type": "string", + "minLength": 1, + "description": "Language code (e.g., 'fr', 'en')" + } + }, + "required": ["website", "language"] +} \ No newline at end of file From bbc8f150b08a1ebb4dddf8a3e5834dc9565a468e Mon Sep 17 00:00:00 2001 From: Anthony Richir Date: Tue, 3 Feb 2026 21:34:03 +0100 Subject: [PATCH 2/3] Refactor Proxistore adapter to use `mtype` for media type determination and update related test files. --- adapters/proxistore/proxistore.go | 43 ++++++------------- .../exemplary/multi-imp-banner.json | 12 ++++-- .../exemplary/simple-banner.json | 6 ++- 3 files changed, 26 insertions(+), 35 deletions(-) diff --git a/adapters/proxistore/proxistore.go b/adapters/proxistore/proxistore.go index 604e451d4c3..3a93c70cd3d 100644 --- a/adapters/proxistore/proxistore.go +++ b/adapters/proxistore/proxistore.go @@ -106,7 +106,7 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.R var errors []error for _, seatBid := range response.SeatBid { for i, bid := range seatBid.Bid { - bidType, err := getMediaTypeForBid(bid, request.Imp) + bidType, err := getMediaTypeForBid(bid) if err != nil { errors = append(errors, err) continue @@ -121,34 +121,19 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.R return bidResponse, errors } -func getMediaTypeForBid(bid openrtb2.Bid, imps []openrtb2.Imp) (openrtb_ext.BidType, error) { - // First try to get type from bid.ext.prebid.type - if bid.Ext != nil { - var bidExt openrtb_ext.ExtBid - if err := jsonutil.Unmarshal(bid.Ext, &bidExt); err == nil && bidExt.Prebid != nil { - return openrtb_ext.ParseBidType(string(bidExt.Prebid.Type)) +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 "", &errortypes.BadServerResponse{ + Message: fmt.Sprintf("unsupported MType %d for bid %s", bid.MType, bid.ID), } } - - // Fall back to determining type from the impression - for _, imp := range imps { - if imp.ID == bid.ImpID { - if imp.Banner != nil { - return openrtb_ext.BidTypeBanner, nil - } - if imp.Video != nil { - return openrtb_ext.BidTypeVideo, nil - } - if imp.Audio != nil { - return openrtb_ext.BidTypeAudio, nil - } - if imp.Native != nil { - return openrtb_ext.BidTypeNative, nil - } - } - } - - return "", &errortypes.BadServerResponse{ - Message: fmt.Sprintf("Failed to determine media type for bid %s (imp %s)", bid.ID, bid.ImpID), - } } diff --git a/adapters/proxistore/proxistoretest/exemplary/multi-imp-banner.json b/adapters/proxistore/proxistoretest/exemplary/multi-imp-banner.json index 0953d6a8217..cab78da49bb 100644 --- a/adapters/proxistore/proxistoretest/exemplary/multi-imp-banner.json +++ b/adapters/proxistore/proxistoretest/exemplary/multi-imp-banner.json @@ -93,7 +93,8 @@ "adm": "
Ad 1
", "crid": "creative-1", "w": 300, - "h": 250 + "h": 250, + "mtype": 1 }, { "id": "bid-2", @@ -102,7 +103,8 @@ "adm": "
Ad 2
", "crid": "creative-2", "w": 728, - "h": 90 + "h": 90, + "mtype": 1 } ], "seat": "proxistore" @@ -125,7 +127,8 @@ "adm": "
Ad 1
", "crid": "creative-1", "w": 300, - "h": 250 + "h": 250, + "mtype": 1 }, "type": "banner" }, @@ -137,7 +140,8 @@ "adm": "
Ad 2
", "crid": "creative-2", "w": 728, - "h": 90 + "h": 90, + "mtype": 1 }, "type": "banner" } diff --git a/adapters/proxistore/proxistoretest/exemplary/simple-banner.json b/adapters/proxistore/proxistoretest/exemplary/simple-banner.json index 5dbea3f9336..a3006eed94d 100644 --- a/adapters/proxistore/proxistoretest/exemplary/simple-banner.json +++ b/adapters/proxistore/proxistoretest/exemplary/simple-banner.json @@ -94,7 +94,8 @@ "adomain": ["advertiser.com"], "crid": "creative-123", "w": 300, - "h": 250 + "h": 250, + "mtype": 1 } ], "seat": "proxistore" @@ -118,7 +119,8 @@ "adomain": ["advertiser.com"], "crid": "creative-123", "w": 300, - "h": 250 + "h": 250, + "mtype": 1 }, "type": "banner" } From 975bbee02948eaf0b7d96deac91f85e0312a4a63 Mon Sep 17 00:00:00 2001 From: Anthony Richir Date: Thu, 12 Feb 2026 21:09:34 +0100 Subject: [PATCH 3/3] Add exemplary and supplemental Proxistore test cases for all media types and update bidder info configs --- .../exemplary/simple-audio.json | 108 ++++++++++++++ .../exemplary/simple-banner-app.json | 138 ++++++++++++++++++ .../exemplary/simple-native.json | 104 +++++++++++++ .../exemplary/simple-video.json | 118 +++++++++++++++ .../supplemental/unsupported-mtype.json | 86 +++++++++++ static/bidder-info/proxistore.yaml | 10 +- 6 files changed, 561 insertions(+), 3 deletions(-) create mode 100644 adapters/proxistore/proxistoretest/exemplary/simple-audio.json create mode 100644 adapters/proxistore/proxistoretest/exemplary/simple-banner-app.json create mode 100644 adapters/proxistore/proxistoretest/exemplary/simple-native.json create mode 100644 adapters/proxistore/proxistoretest/exemplary/simple-video.json create mode 100644 adapters/proxistore/proxistoretest/supplemental/unsupported-mtype.json diff --git a/adapters/proxistore/proxistoretest/exemplary/simple-audio.json b/adapters/proxistore/proxistoretest/exemplary/simple-audio.json new file mode 100644 index 00000000000..4bcea393646 --- /dev/null +++ b/adapters/proxistore/proxistoretest/exemplary/simple-audio.json @@ -0,0 +1,108 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + }, + "site": { + "id": "site-id", + "domain": "example.com", + "page": "https://example.com/page" + }, + "imp": [ + { + "id": "test-imp-id", + "audio": { + "mimes": ["audio/mp3"], + "minduration": 5, + "maxduration": 30 + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://api.proxistore.com/v3/rtb/openrtb", + "body": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + }, + "site": { + "id": "site-id", + "domain": "example.com", + "page": "https://example.com/page" + }, + "imp": [ + { + "id": "test-imp-id", + "audio": { + "mimes": ["audio/mp3"], + "minduration": 5, + "maxduration": 30 + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 1.50, + "adm": "", + "adomain": ["advertiser.com"], + "crid": "creative-audio-123", + "mtype": 3 + } + ], + "seat": "proxistore" + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 1.50, + "adm": "", + "adomain": ["advertiser.com"], + "crid": "creative-audio-123", + "mtype": 3 + }, + "type": "audio" + } + ] + } + ] +} diff --git a/adapters/proxistore/proxistoretest/exemplary/simple-banner-app.json b/adapters/proxistore/proxistoretest/exemplary/simple-banner-app.json new file mode 100644 index 00000000000..bfc01eca1ca --- /dev/null +++ b/adapters/proxistore/proxistoretest/exemplary/simple-banner-app.json @@ -0,0 +1,138 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36" + }, + "app": { + "id": "app-id", + "bundle": "com.example.app", + "name": "Example App", + "domain": "example.com", + "publisher": { + "id": "pub-123" + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + {"w": 320, "h": 50}, + {"w": 300, "h": 250} + ] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ], + "regs": { + "gdpr": 1 + }, + "user": { + "ext": { + "consent": "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://api.proxistore.com/v3/rtb/openrtb", + "body": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36" + }, + "app": { + "id": "app-id", + "bundle": "com.example.app", + "name": "Example App", + "domain": "example.com", + "publisher": { + "id": "pub-123" + } + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + {"w": 320, "h": 50}, + {"w": 300, "h": 250} + ] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ], + "regs": { + "gdpr": 1 + }, + "user": { + "ext": { + "consent": "COvFyGBOvFyGBAbAAAENAPCAAOAAAAAAAAAAAEEUACCKAAA" + } + } + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 0.85, + "adm": "
App banner creative
", + "adomain": ["advertiser.com"], + "crid": "creative-456", + "w": 320, + "h": 50, + "mtype": 1 + } + ], + "seat": "proxistore" + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 0.85, + "adm": "
App banner creative
", + "adomain": ["advertiser.com"], + "crid": "creative-456", + "w": 320, + "h": 50, + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/proxistore/proxistoretest/exemplary/simple-native.json b/adapters/proxistore/proxistoretest/exemplary/simple-native.json new file mode 100644 index 00000000000..b0146495cd5 --- /dev/null +++ b/adapters/proxistore/proxistoretest/exemplary/simple-native.json @@ -0,0 +1,104 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + }, + "site": { + "id": "site-id", + "domain": "example.com", + "page": "https://example.com/page" + }, + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"ver\":\"1.1\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":90}}]}" + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://api.proxistore.com/v3/rtb/openrtb", + "body": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + }, + "site": { + "id": "site-id", + "domain": "example.com", + "page": "https://example.com/page" + }, + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"ver\":\"1.1\",\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":90}}]}" + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 1.80, + "adm": "{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"https://example.com\"},\"assets\":[{\"id\":1,\"title\":{\"text\":\"Ad Title\"}}]}}", + "adomain": ["advertiser.com"], + "crid": "creative-native-123", + "mtype": 4 + } + ], + "seat": "proxistore" + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 1.80, + "adm": "{\"native\":{\"ver\":\"1.1\",\"link\":{\"url\":\"https://example.com\"},\"assets\":[{\"id\":1,\"title\":{\"text\":\"Ad Title\"}}]}}", + "adomain": ["advertiser.com"], + "crid": "creative-native-123", + "mtype": 4 + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/proxistore/proxistoretest/exemplary/simple-video.json b/adapters/proxistore/proxistoretest/exemplary/simple-video.json new file mode 100644 index 00000000000..192eb60295f --- /dev/null +++ b/adapters/proxistore/proxistoretest/exemplary/simple-video.json @@ -0,0 +1,118 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + }, + "site": { + "id": "site-id", + "domain": "example.com", + "page": "https://example.com/page" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [1, 2], + "w": 640, + "h": 480, + "minduration": 5, + "maxduration": 30 + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://api.proxistore.com/v3/rtb/openrtb", + "body": { + "id": "test-request-id", + "device": { + "ip": "123.123.123.123", + "ua": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + }, + "site": { + "id": "site-id", + "domain": "example.com", + "page": "https://example.com/page" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": ["video/mp4"], + "protocols": [1, 2], + "w": 640, + "h": 480, + "minduration": 5, + "maxduration": 30 + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 2.50, + "adm": "", + "adomain": ["advertiser.com"], + "crid": "creative-video-123", + "w": 640, + "h": 480, + "mtype": 2 + } + ], + "seat": "proxistore" + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [ + { + "bid": { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 2.50, + "adm": "", + "adomain": ["advertiser.com"], + "crid": "creative-video-123", + "w": 640, + "h": 480, + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/proxistore/proxistoretest/supplemental/unsupported-mtype.json b/adapters/proxistore/proxistoretest/supplemental/unsupported-mtype.json new file mode 100644 index 00000000000..5141e24f1de --- /dev/null +++ b/adapters/proxistore/proxistoretest/supplemental/unsupported-mtype.json @@ -0,0 +1,86 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "site": { + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://api.proxistore.com/v3/rtb/openrtb", + "body": { + "id": "test-request-id", + "site": { + "domain": "example.com" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 250}] + }, + "ext": { + "bidder": { + "website": "example.com", + "language": "fr" + } + } + } + ] + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "test-bid-id", + "impid": "test-imp-id", + "price": 1.23, + "adm": "
Ad creative
", + "crid": "creative-123", + "w": 300, + "h": 250, + "mtype": 99 + } + ], + "seat": "proxistore" + } + ], + "cur": "EUR" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "EUR", + "bids": [] + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unsupported MType 99 for bid test-bid-id", + "comparison": "literal" + } + ] +} diff --git a/static/bidder-info/proxistore.yaml b/static/bidder-info/proxistore.yaml index 53bcb4edc99..186e41ce551 100644 --- a/static/bidder-info/proxistore.yaml +++ b/static/bidder-info/proxistore.yaml @@ -1,8 +1,6 @@ -# We have the following regional endpoint domains: us-east and us-west -# Please deploy this config in each of your datacenters with the appropriate regional subdomain endpoint: "https://api.proxistore.com/v3/rtb/openrtb" geoscope: - - EUR + - EEA maintainer: email: support@proxistore.com gvlVendorID: 418 @@ -12,9 +10,15 @@ capabilities: app: mediaTypes: - banner + - video + - native + - audio site: mediaTypes: - banner + - video + - native + - audio userSync: redirect: url: https://abs.proxistore.com/v3/rtb/sync/prebid-server/redirect?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&redirect={{.RedirectURL}}