From 5d43c551607ed668cba76baaee90ed9ed931e16c Mon Sep 17 00:00:00 2001 From: tradplus <58809719+tradplus@users.noreply.github.com> Date: Fri, 2 Aug 2024 14:42:03 +0800 Subject: [PATCH] New Adapter: TradPlus --- adapters/tradplus/params_test.go | 48 +++++ adapters/tradplus/tradplus.go | 197 ++++++++++++++++++ adapters/tradplus/tradplus_test.go | 29 +++ .../tradplustest/exemplary/no-bid.json | 53 +++++ .../tradplustest/exemplary/simple-banner.json | 88 ++++++++ .../exemplary/simple-native-1.1.json | 78 +++++++ .../tradplustest/exemplary/simple-native.json | 78 +++++++ .../tradplustest/exemplary/simple-video.json | 86 ++++++++ .../supplemental/bad_imp_ext.json | 21 ++ .../supplemental/bad_imp_ext_bidder.json | 27 +++ .../supplemental/bad_response.json | 58 ++++++ .../supplemental/empty_imp_ext_bidder.json | 30 +++ .../tradplustest/supplemental/status_400.json | 58 ++++++ .../tradplustest/supplemental/status_500.json | 58 ++++++ exchange/adapter_builders.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_tradplus.go | 7 + static/bidder-info/tradplus.yaml | 15 ++ static/bidder-params/tradplus.json | 20 ++ 19 files changed, 955 insertions(+) create mode 100644 adapters/tradplus/params_test.go create mode 100644 adapters/tradplus/tradplus.go create mode 100644 adapters/tradplus/tradplus_test.go create mode 100644 adapters/tradplus/tradplustest/exemplary/no-bid.json create mode 100644 adapters/tradplus/tradplustest/exemplary/simple-banner.json create mode 100644 adapters/tradplus/tradplustest/exemplary/simple-native-1.1.json create mode 100644 adapters/tradplus/tradplustest/exemplary/simple-native.json create mode 100644 adapters/tradplus/tradplustest/exemplary/simple-video.json create mode 100644 adapters/tradplus/tradplustest/supplemental/bad_imp_ext.json create mode 100644 adapters/tradplus/tradplustest/supplemental/bad_imp_ext_bidder.json create mode 100644 adapters/tradplus/tradplustest/supplemental/bad_response.json create mode 100644 adapters/tradplus/tradplustest/supplemental/empty_imp_ext_bidder.json create mode 100644 adapters/tradplus/tradplustest/supplemental/status_400.json create mode 100644 adapters/tradplus/tradplustest/supplemental/status_500.json create mode 100644 openrtb_ext/imp_tradplus.go create mode 100644 static/bidder-info/tradplus.yaml create mode 100644 static/bidder-params/tradplus.json diff --git a/adapters/tradplus/params_test.go b/adapters/tradplus/params_test.go new file mode 100644 index 00000000000..ab34b6a7c32 --- /dev/null +++ b/adapters/tradplus/params_test.go @@ -0,0 +1,48 @@ +package tradplus + +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-schemas. %v", err) + } + + for _, validParam := range validParams { + if err := validator.Validate(openrtb_ext.BidderTradPlus, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected tradplus params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the tradplus schema rejects all the imp.ext fields we don't support. +func TestInvalidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json-schemas. %v", err) + } + + for _, invalidParam := range invalidParams { + if err := validator.Validate(openrtb_ext.BidderTradPlus, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"accountId": "11233", "zoneId": ""}`, + `{"accountId": "aaa", "accountId": "us"}`, + `{"accountId": "aa", "accountId": "sin"}`, +} + +var invalidParams = []string{ + `{"accountId": ""}`, + `{"accountId": "", "zoneId": ""}`, + `{"accountId": "", "zoneId": "sin"}`, + `{"zoneId": "aaa"}`, +} diff --git a/adapters/tradplus/tradplus.go b/adapters/tradplus/tradplus.go new file mode 100644 index 00000000000..d8ad0a7efd0 --- /dev/null +++ b/adapters/tradplus/tradplus.go @@ -0,0 +1,197 @@ +package tradplus + +import ( + "encoding/json" + "fmt" + "net/http" + "text/template" + + "github.com/prebid/openrtb/v20/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 +} + +// Builder builds a new instance of the tradplus adapter for the given bidder with the given config. +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, + }, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + adapterRequest, errs := a.makeRequest(request) + if errs != nil { + return nil, errs + } + return []*adapters.RequestData{adapterRequest}, nil +} + +func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, []error) { + errs := validateTradPlusExt(request) + if errs != nil { + return nil, errs + } + + tradplusExt, err := getImpressionExt(&request.Imp[0]) + if err != nil { + return nil, []error{err} + } + + request.Imp[0].Ext = nil + + url, err := a.buildEndpointURL(tradplusExt) + if err != nil { + return nil, []error{err} + } + + err = transform(request) + if err != nil { + return nil, []error{err} + } + + reqBody, err := json.Marshal(request) + if err != nil { + return nil, []error{err} + } + + headers := http.Header{} + headers.Add("Content-Type", "application/json;charset=utf-8") + return &adapters.RequestData{ + Method: "POST", + Uri: url, + Body: reqBody, + Headers: headers, + ImpIDs: openrtb_ext.GetImpIDs(request.Imp), + }, nil +} + +func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpTradPlus, error) { + var bidderExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { + return nil, &errortypes.BadInput{ + Message: "Error parsing tradplusExt - " + err.Error(), + } + } + + var tradplusExt openrtb_ext.ExtImpTradPlus + if err := json.Unmarshal(bidderExt.Bidder, &tradplusExt); err != nil { + return nil, &errortypes.BadInput{ + Message: "Error parsing bidderExt - " + err.Error(), + } + } + + return &tradplusExt, nil +} + +func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtImpTradPlus) (string, error) { + endpointParams := macros.EndpointTemplateParams{ + AccountID: params.AccountID, + ZoneID: params.ZoneID, + } + return macros.ResolveMacros(a.endpoint, endpointParams) +} + +func transform(request *openrtb2.BidRequest) error { + for i, imp := range request.Imp { + if imp.Native != nil { + var nativeRequest map[string]interface{} + nativeCopyRequest := make(map[string]interface{}) + if err := json.Unmarshal([]byte(request.Imp[i].Native.Request), &nativeRequest); err != nil { + return err + } + _, exists := nativeRequest["native"] + if exists { + continue + } + nativeCopyRequest["native"] = nativeRequest + nativeReqByte, err := json.Marshal(nativeCopyRequest) + if err != nil { + return err + } + nativeCopy := *request.Imp[i].Native + nativeCopy.Request = string(nativeReqByte) + request.Imp[i].Native = &nativeCopy + } + } + return nil +} + +func validateTradPlusExt(request *openrtb2.BidRequest) []error { + var extImpTradPlus openrtb_ext.ExtImpTradPlus + var errs []error + for _, imp := range request.Imp { + var extBidder adapters.ExtImpBidder + err := json.Unmarshal(imp.Ext, &extBidder) + if err != nil { + errs = append(errs, err) + continue + } + err = json.Unmarshal(extBidder.Bidder, &extImpTradPlus) + if err != nil { + errs = append(errs, err) + continue + } + if extImpTradPlus.AccountID == "" { + errs = append(errs, fmt.Errorf("imp.ext.accountId required")) + continue + } + } + return errs +} + +// MakeBids make the bids for the bid response. +func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if adapters.IsResponseStatusCodeNoContent(response) { + return nil, nil + } + if err := adapters.CheckResponseStatusCodeForErrors(response); err != nil { + return nil, []error{&errortypes.BadInput{ + Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode), + }} + } + var bidResp openrtb2.BidResponse + if err := json.Unmarshal(response.Body, &bidResp); err != nil { + return nil, []error{err} + } + var errs []error + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(internalRequest.Imp)) + for _, sb := range bidResp.SeatBid { + for i := range sb.Bid { + mediaType, err := getMediaTypeForBid(sb.Bid[i]) + if err != nil { + errs = append(errs, err) + continue + } + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &sb.Bid[i], + BidType: mediaType, + }) + } + } + return bidResponse, errs +} + +func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { + switch bid.MType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner, nil + case openrtb2.MarkupNative: + return openrtb_ext.BidTypeNative, nil + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo, nil + default: + return "", fmt.Errorf("unrecognized bid type in response from tradplus for bid %s", bid.ImpID) + } +} \ No newline at end of file diff --git a/adapters/tradplus/tradplus_test.go b/adapters/tradplus/tradplus_test.go new file mode 100644 index 00000000000..84a8f2a4baf --- /dev/null +++ b/adapters/tradplus/tradplus_test.go @@ -0,0 +1,29 @@ +package tradplus + +import ( + "testing" + + "github.com/stretchr/testify/assert" + + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderTradPlus, config.Adapter{ + Endpoint: "https://{{.ZoneID}}adx.tradplusad.com/{{.AccountID}}/pserver"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "tradplustest", bidder) +} + +func TestEndpointTemplateMalformed(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderTradPlus, config.Adapter{ + Endpoint: "{{Malformed}}"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"}) + + assert.Error(t, buildErr) +} diff --git a/adapters/tradplus/tradplustest/exemplary/no-bid.json b/adapters/tradplus/tradplustest/exemplary/no-bid.json new file mode 100644 index 00000000000..96d51bf92fa --- /dev/null +++ b/adapters/tradplus/tradplustest/exemplary/no-bid.json @@ -0,0 +1,53 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "accountId": "fake-account-id", + "zoneId": "" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://adx.tradplusad.com/fake-account-id/pserver", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/tradplus/tradplustest/exemplary/simple-banner.json b/adapters/tradplus/tradplustest/exemplary/simple-banner.json new file mode 100644 index 00000000000..6b9df199009 --- /dev/null +++ b/adapters/tradplus/tradplustest/exemplary/simple-banner.json @@ -0,0 +1,88 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "accountId": "fake-account-id", + "zoneId": "us-" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://us-adx.tradplusad.com/fake-account-id/pserver", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "ttx", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some-ads", + "crid": "crid_testid", + "mtype": 1 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some-ads", + "crid": "crid_testid", + "mtype": 1 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/tradplus/tradplustest/exemplary/simple-native-1.1.json b/adapters/tradplus/tradplustest/exemplary/simple-native-1.1.json new file mode 100644 index 00000000000..c97c63286ec --- /dev/null +++ b/adapters/tradplus/tradplustest/exemplary/simple-native-1.1.json @@ -0,0 +1,78 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"native\":{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"required\":1,\"img\":{\"type\":3,\"wmin\":128,\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"]}},{\"id\":7,\"required\":1,\"data\":{\"type\":2,\"len\":120}}]}}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "accountId": "fake-account-id", + "zoneId": "" + } + } + } + ] + }, + "httpcalls": [ + { + "expectedRequest": { + "uri": "https://adx.tradplusad.com/fake-account-id/pserver", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"native\":{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"required\":1,\"img\":{\"type\":3,\"wmin\":128,\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"]}},{\"id\":7,\"required\":1,\"data\":{\"type\":2,\"len\":120}}]}}", + "ver": "1.2" + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "8400d766-58b3-47d4-80d7-6658b337d403", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid", + "mtype": 4 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8400d766-58b3-47d4-80d7-6658b337d403", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid", + "mtype": 4 + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/tradplus/tradplustest/exemplary/simple-native.json b/adapters/tradplus/tradplustest/exemplary/simple-native.json new file mode 100644 index 00000000000..a2b6872d9e8 --- /dev/null +++ b/adapters/tradplus/tradplustest/exemplary/simple-native.json @@ -0,0 +1,78 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"ver\":\"1.2\",\"context\":1,\"plcmttype\":4,\"plcmtcnt\":1,\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"required\":1,\"img\":{\"type\":3,\"wmin\":128,\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"]}},{\"id\":7,\"required\":1,\"data\":{\"type\":2,\"len\":120}}]}", + "ver": "1.2" + }, + "ext": { + "bidder": { + "accountId": "fake-account-id", + "zoneId": "" + } + } + } + ] + }, + "httpcalls": [ + { + "expectedRequest": { + "uri": "https://adx.tradplusad.com/fake-account-id/pserver", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{\"native\":{\"assets\":[{\"id\":2,\"required\":1,\"title\":{\"len\":90}},{\"id\":6,\"img\":{\"hmin\":128,\"mimes\":[\"image/jpg\",\"image/jpeg\",\"image/png\"],\"type\":3,\"wmin\":128},\"required\":1},{\"data\":{\"len\":120,\"type\":2},\"id\":7,\"required\":1}],\"context\":1,\"plcmtcnt\":1,\"plcmttype\":4,\"ver\":\"1.2\"}}", + "ver": "1.2" + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "bid": [ + { + "id": "8400d766-58b3-47d4-80d7-6658b337d403", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid", + "mtype": 4 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8400d766-58b3-47d4-80d7-6658b337d403", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some ads", + "crid": "crid_testid", + "mtype": 4 + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/tradplus/tradplustest/exemplary/simple-video.json b/adapters/tradplus/tradplustest/exemplary/simple-video.json new file mode 100644 index 00000000000..70328306357 --- /dev/null +++ b/adapters/tradplus/tradplustest/exemplary/simple-video.json @@ -0,0 +1,86 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 300, + "h": 250, + "mimes": [ + "video/mp4" + ] + }, + "ext": { + "bidder": { + "accountId": "fake-account-id", + "zoneId": "" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://adx.tradplusad.com/fake-account-id/pserver", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 300, + "h": 250, + "mimes": [ + "video/mp4" + ] + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "ttx", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some-ads", + "crid": "crid_testid", + "mtype": 2 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 1.2, + "adm": "some-ads", + "crid": "crid_testid", + "mtype": 2 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/tradplus/tradplustest/supplemental/bad_imp_ext.json b/adapters/tradplus/tradplustest/supplemental/bad_imp_ext.json new file mode 100644 index 00000000000..444e1e7a8d8 --- /dev/null +++ b/adapters/tradplus/tradplustest/supplemental/bad_imp_ext.json @@ -0,0 +1,21 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 300, "h": 50}] + }, + "ext": "aaa" + } + ] + }, + + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder", + "comparison": "literal" + } + ] +} diff --git a/adapters/tradplus/tradplustest/supplemental/bad_imp_ext_bidder.json b/adapters/tradplus/tradplustest/supplemental/bad_imp_ext_bidder.json new file mode 100644 index 00000000000..0450e84e811 --- /dev/null +++ b/adapters/tradplus/tradplustest/supplemental/bad_imp_ext_bidder.json @@ -0,0 +1,27 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": "aa" + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "json: cannot unmarshal string into Go value of type openrtb_ext.ExtImpTradPlus", + "comparison": "literal" + } + ] +} diff --git a/adapters/tradplus/tradplustest/supplemental/bad_response.json b/adapters/tradplus/tradplustest/supplemental/bad_response.json new file mode 100644 index 00000000000..29ef512126f --- /dev/null +++ b/adapters/tradplus/tradplustest/supplemental/bad_response.json @@ -0,0 +1,58 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "accountId": "fake-account-id", + "zoneId": "" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://adx.tradplusad.com/fake-account-id/pserver", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": "{\"id\":test-request-id" + } + } + ], + "expectedMakeBidsErrors": [ + { + "comparison": "literal", + "value": "json: cannot unmarshal string into Go value of type openrtb2.BidResponse" + } + ] +} diff --git a/adapters/tradplus/tradplustest/supplemental/empty_imp_ext_bidder.json b/adapters/tradplus/tradplustest/supplemental/empty_imp_ext_bidder.json new file mode 100644 index 00000000000..4a1f231902c --- /dev/null +++ b/adapters/tradplus/tradplustest/supplemental/empty_imp_ext_bidder.json @@ -0,0 +1,30 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "accountId": "", + "zoneId": "" + } + } + } + ] + }, + "expectedMakeRequestsErrors": [ + { + "value": "imp.ext.accountId required", + "comparison": "literal" + } + ] +} diff --git a/adapters/tradplus/tradplustest/supplemental/status_400.json b/adapters/tradplus/tradplustest/supplemental/status_400.json new file mode 100644 index 00000000000..0cc859a7cd8 --- /dev/null +++ b/adapters/tradplus/tradplustest/supplemental/status_400.json @@ -0,0 +1,58 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "accountId": "fake-account-id", + "zoneId": "" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://adx.tradplusad.com/fake-account-id/pserver", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 400, + "body": {} + } + } + ], + "expectedMakeBidsErrors": [ + { + "comparison": "literal", + "value": "Unexpected status code: 400." + } + ] +} diff --git a/adapters/tradplus/tradplustest/supplemental/status_500.json b/adapters/tradplus/tradplustest/supplemental/status_500.json new file mode 100644 index 00000000000..d3f08b4e17d --- /dev/null +++ b/adapters/tradplus/tradplustest/supplemental/status_500.json @@ -0,0 +1,58 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + }, + "ext": { + "bidder": { + "accountId": "fake-account-id", + "zoneId": "" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://adx.tradplusad.com/fake-account-id/pserver", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 50 + } + ] + } + } + ] + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 500, + "body": {} + } + } + ], + "expectedMakeBidsErrors": [ + { + "comparison": "literal", + "value": "Unexpected status code: 500." + } + ] +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index dd66efaf403..0a3d7db4a36 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -185,6 +185,7 @@ import ( "github.com/prebid/prebid-server/v2/adapters/telaria" "github.com/prebid/prebid-server/v2/adapters/theadx" "github.com/prebid/prebid-server/v2/adapters/tpmn" + "github.com/prebid/prebid-server/v2/adapters/tradplus" "github.com/prebid/prebid-server/v2/adapters/trafficgate" "github.com/prebid/prebid-server/v2/adapters/triplelift" "github.com/prebid/prebid-server/v2/adapters/triplelift_native" @@ -406,6 +407,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderTelaria: telaria.Builder, openrtb_ext.BidderTheadx: theadx.Builder, openrtb_ext.BidderTpmn: tpmn.Builder, + openrtb_ext.BidderTradPlus: tradplus.Builder, openrtb_ext.BidderTrafficGate: trafficgate.Builder, openrtb_ext.BidderTriplelift: triplelift.Builder, openrtb_ext.BidderTripleliftNative: triplelift_native.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 3f8ac7dbd77..7ffd1e0600f 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -203,6 +203,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderTelaria, BidderTheadx, BidderTpmn, + BidderTradPlus, BidderTrafficGate, BidderTriplelift, BidderTripleliftNative, @@ -521,6 +522,7 @@ const ( BidderTelaria BidderName = "telaria" BidderTheadx BidderName = "theadx" BidderTpmn BidderName = "tpmn" + BidderTradPlus BidderName = "tradplus" BidderTrafficGate BidderName = "trafficgate" BidderTriplelift BidderName = "triplelift" BidderTripleliftNative BidderName = "triplelift_native" diff --git a/openrtb_ext/imp_tradplus.go b/openrtb_ext/imp_tradplus.go new file mode 100644 index 00000000000..4f7139c88b4 --- /dev/null +++ b/openrtb_ext/imp_tradplus.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtImpTradPlus defines the contract for bidrequest.imp[i].ext.prebid.bidder.tradplus +type ExtImpTradPlus struct { + AccountID string `json:"accountId"` + ZoneID string `json:"zoneId"` +} diff --git a/static/bidder-info/tradplus.yaml b/static/bidder-info/tradplus.yaml new file mode 100644 index 00000000000..02d3e67ea14 --- /dev/null +++ b/static/bidder-info/tradplus.yaml @@ -0,0 +1,15 @@ +endpoint: "https://{{.ZoneID}}adx.tradplusad.com/{{.AccountID}}/pserver" + +maintainer: + email: "tpxcontact@tradplus.com" +capabilities: + app: + mediaTypes: + - banner + - video + - native + site: + mediaTypes: + - banner + - native + - video \ No newline at end of file diff --git a/static/bidder-params/tradplus.json b/static/bidder-params/tradplus.json new file mode 100644 index 00000000000..2f568bad074 --- /dev/null +++ b/static/bidder-params/tradplus.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "TradPlus Adapter Params", + "description": "A schema which validates params accepted by the TradPlus adapter", + "type": "object", + "properties": { + "accountId": { + "type": "string", + "description": "Account ID", + "minLength": 1 + }, + "zoneId": { + "type": "string", + "description": "Zone ID" + } + }, + "required": [ + "accountId" + ] +} \ No newline at end of file