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 1/6] 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 From 3d6343e604e7be021fe1d422193139278576b035 Mon Sep 17 00:00:00 2001 From: tradplus <58809719+tradplus@users.noreply.github.com> Date: Fri, 6 Sep 2024 10:26:22 +0800 Subject: [PATCH 2/6] feat: delete validateTradPlusExt --- adapters/tradplus/tradplus.go | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/adapters/tradplus/tradplus.go b/adapters/tradplus/tradplus.go index d8ad0a7efd0..39ca798670f 100644 --- a/adapters/tradplus/tradplus.go +++ b/adapters/tradplus/tradplus.go @@ -39,10 +39,6 @@ func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.E } 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 { @@ -128,29 +124,6 @@ func transform(request *openrtb2.BidRequest) error { 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) { From ef7a58cfb4b5441d9e7140059d9e15d752ef1c25 Mon Sep 17 00:00:00 2001 From: tradplus <58809719+tradplus@users.noreply.github.com> Date: Fri, 18 Oct 2024 17:26:34 +0800 Subject: [PATCH 3/6] feat: change getImpressionExt code --- adapters/tradplus/tradplus.go | 6 ++++++ .../tradplus/tradplustest/supplemental/bad_imp_ext.json | 2 +- .../tradplustest/supplemental/bad_imp_ext_bidder.json | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/adapters/tradplus/tradplus.go b/adapters/tradplus/tradplus.go index 39ca798670f..61dd807104e 100644 --- a/adapters/tradplus/tradplus.go +++ b/adapters/tradplus/tradplus.go @@ -88,6 +88,12 @@ func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpTradPlus, error) { } } + if tradplusExt.AccountID == "" { + return nil, &errortypes.BadInput{ + Message: "imp.ext.accountId required", + } + } + return &tradplusExt, nil } diff --git a/adapters/tradplus/tradplustest/supplemental/bad_imp_ext.json b/adapters/tradplus/tradplustest/supplemental/bad_imp_ext.json index 444e1e7a8d8..cfb662696f1 100644 --- a/adapters/tradplus/tradplustest/supplemental/bad_imp_ext.json +++ b/adapters/tradplus/tradplustest/supplemental/bad_imp_ext.json @@ -14,7 +14,7 @@ "expectedMakeRequestsErrors": [ { - "value": "json: cannot unmarshal string into Go value of type adapters.ExtImpBidder", + "value": "Error parsing tradplusExt - 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 index 0450e84e811..514c3b97271 100644 --- a/adapters/tradplus/tradplustest/supplemental/bad_imp_ext_bidder.json +++ b/adapters/tradplus/tradplustest/supplemental/bad_imp_ext_bidder.json @@ -20,7 +20,7 @@ }, "expectedMakeRequestsErrors": [ { - "value": "json: cannot unmarshal string into Go value of type openrtb_ext.ExtImpTradPlus", + "value": "Error parsing bidderExt - json: cannot unmarshal string into Go value of type openrtb_ext.ExtImpTradPlus", "comparison": "literal" } ] From f84c5dae48827b918267294b4e2e37f6ddf5807e Mon Sep 17 00:00:00 2001 From: tradplus <58809719+tradplus@users.noreply.github.com> Date: Thu, 24 Oct 2024 11:53:50 +0800 Subject: [PATCH 4/6] feat: gofmt result modify --- adapters/tradplus/tradplus.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/tradplus/tradplus.go b/adapters/tradplus/tradplus.go index 61dd807104e..6934b9c29a1 100644 --- a/adapters/tradplus/tradplus.go +++ b/adapters/tradplus/tradplus.go @@ -173,4 +173,4 @@ func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { default: return "", fmt.Errorf("unrecognized bid type in response from tradplus for bid %s", bid.ImpID) } -} \ No newline at end of file +} From 07fb322fb08093c376ea563a29db7fe747958537 Mon Sep 17 00:00:00 2001 From: tradplus <58809719+tradplus@users.noreply.github.com> Date: Fri, 25 Oct 2024 15:19:32 +0800 Subject: [PATCH 5/6] feat: remove function transform and change some code by reviewer comment --- adapters/tradplus/params_test.go | 6 ++ adapters/tradplus/tradplus.go | 30 ------- .../exemplary/simple-native-1.1.json | 78 ------------------- .../tradplustest/exemplary/simple-native.json | 2 +- 4 files changed, 7 insertions(+), 109 deletions(-) delete mode 100644 adapters/tradplus/tradplustest/exemplary/simple-native-1.1.json diff --git a/adapters/tradplus/params_test.go b/adapters/tradplus/params_test.go index ab34b6a7c32..1fb0c96cb13 100644 --- a/adapters/tradplus/params_test.go +++ b/adapters/tradplus/params_test.go @@ -44,5 +44,11 @@ var invalidParams = []string{ `{"accountId": ""}`, `{"accountId": "", "zoneId": ""}`, `{"accountId": "", "zoneId": "sin"}`, + `{"accountId": 123}`, + `{"accountId": {"test":1}}`, + `{"accountId": true}`, + `{"accountId": null}`, `{"zoneId": "aaa"}`, + `{"zoneId": "aaa"}`, + `{"zoneId": null}`, } diff --git a/adapters/tradplus/tradplus.go b/adapters/tradplus/tradplus.go index 6934b9c29a1..d543f890ff2 100644 --- a/adapters/tradplus/tradplus.go +++ b/adapters/tradplus/tradplus.go @@ -52,11 +52,6 @@ func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestDa 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} @@ -105,31 +100,6 @@ func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtImpTradPlus) (string, 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 -} - // 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) { diff --git a/adapters/tradplus/tradplustest/exemplary/simple-native-1.1.json b/adapters/tradplus/tradplustest/exemplary/simple-native-1.1.json deleted file mode 100644 index c97c63286ec..00000000000 --- a/adapters/tradplus/tradplustest/exemplary/simple-native-1.1.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "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 index a2b6872d9e8..6dd785a753c 100644 --- a/adapters/tradplus/tradplustest/exemplary/simple-native.json +++ b/adapters/tradplus/tradplustest/exemplary/simple-native.json @@ -27,7 +27,7 @@ { "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\"}}", + "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" } } From cc91ac7d484dcde5e4826318ba10d797ac4a6a71 Mon Sep 17 00:00:00 2001 From: tradplus <58809719+tradplus@users.noreply.github.com> Date: Wed, 18 Dec 2024 19:41:13 +0800 Subject: [PATCH 6/6] feat: change some code by reviewer comment --- adapters/tradplus/tradplus.go | 6 ---- .../supplemental/empty_imp_ext_bidder.json | 30 ------------------- 2 files changed, 36 deletions(-) delete mode 100644 adapters/tradplus/tradplustest/supplemental/empty_imp_ext_bidder.json diff --git a/adapters/tradplus/tradplus.go b/adapters/tradplus/tradplus.go index 28082529bb8..c3277f96c1b 100644 --- a/adapters/tradplus/tradplus.go +++ b/adapters/tradplus/tradplus.go @@ -84,12 +84,6 @@ func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpTradPlus, error) { } } - if tradplusExt.AccountID == "" { - return nil, &errortypes.BadInput{ - Message: "imp.ext.accountId required", - } - } - return &tradplusExt, nil } diff --git a/adapters/tradplus/tradplustest/supplemental/empty_imp_ext_bidder.json b/adapters/tradplus/tradplustest/supplemental/empty_imp_ext_bidder.json deleted file mode 100644 index 4a1f231902c..00000000000 --- a/adapters/tradplus/tradplustest/supplemental/empty_imp_ext_bidder.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "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" - } - ] -}