From 99431ad83fa7546d8e8d60acc3feae77c815f37f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Abdulbaki=20=C3=87am?= <71071893+bakicam@users.noreply.github.com> Date: Wed, 3 Jul 2024 20:38:17 +0300 Subject: [PATCH] New Adapter: Admatic (#3654) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Faruk Çam --- adapters/admatic/admatic.go | 138 +++++++++++++ adapters/admatic/admatic_test.go | 29 +++ .../admatic/admatictest/exemplary/banner.json | 96 ++++++++++ .../admatictest/exemplary/multiple-imps.json | 181 ++++++++++++++++++ .../admatic/admatictest/exemplary/native.json | 86 +++++++++ .../exemplary/optional-params.json | 100 ++++++++++ .../admatic/admatictest/exemplary/video.json | 94 +++++++++ .../admatictest/supplemental/bad-request.json | 66 +++++++ .../multiple-imps-with-error.json | 119 ++++++++++++ .../response-200-without-body.json | 64 +++++++ .../supplemental/response-204.json | 59 ++++++ .../supplemental/server-error.json | 65 +++++++ adapters/admatic/params_test.go | 56 ++++++ exchange/adapter_builders.go | 2 + openrtb_ext/bidders.go | 2 + openrtb_ext/imp_admatic.go | 6 + static/bidder-info/admatic.yaml | 20 ++ static/bidder-params/admatic.json | 20 ++ 18 files changed, 1203 insertions(+) create mode 100644 adapters/admatic/admatic.go create mode 100644 adapters/admatic/admatic_test.go create mode 100644 adapters/admatic/admatictest/exemplary/banner.json create mode 100644 adapters/admatic/admatictest/exemplary/multiple-imps.json create mode 100644 adapters/admatic/admatictest/exemplary/native.json create mode 100644 adapters/admatic/admatictest/exemplary/optional-params.json create mode 100644 adapters/admatic/admatictest/exemplary/video.json create mode 100644 adapters/admatic/admatictest/supplemental/bad-request.json create mode 100644 adapters/admatic/admatictest/supplemental/multiple-imps-with-error.json create mode 100644 adapters/admatic/admatictest/supplemental/response-200-without-body.json create mode 100644 adapters/admatic/admatictest/supplemental/response-204.json create mode 100644 adapters/admatic/admatictest/supplemental/server-error.json create mode 100644 adapters/admatic/params_test.go create mode 100644 openrtb_ext/imp_admatic.go create mode 100644 static/bidder-info/admatic.yaml create mode 100644 static/bidder-params/admatic.json diff --git a/adapters/admatic/admatic.go b/adapters/admatic/admatic.go new file mode 100644 index 00000000000..94d966dbfbe --- /dev/null +++ b/adapters/admatic/admatic.go @@ -0,0 +1,138 @@ +package admatic + +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 +} + +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + endpointTemplate, err := template.New("endpointTemplate").Parse(config.Endpoint) + if err != nil { + return nil, fmt.Errorf("unable to parse endpoint template: %v", err) + } + + bidder := &adapter{ + endpoint: endpointTemplate, + } + return bidder, nil +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var requests []*adapters.RequestData + var errs []error + + requestCopy := *request + for _, imp := range request.Imp { + requestCopy.Imp = []openrtb2.Imp{imp} + + endpoint, err := a.buildEndpointFromRequest(&imp) + if err != nil { + errs = append(errs, err) + continue + } + + requestJSON, err := json.Marshal(requestCopy) + if err != nil { + errs = append(errs, err) + continue + } + + request := &adapters.RequestData{ + Method: http.MethodPost, + Body: requestJSON, + Uri: endpoint, + ImpIDs: openrtb_ext.GetImpIDs(requestCopy.Imp), + } + + requests = append(requests, request) + } + + return requests, errs +} + +func (a *adapter) buildEndpointFromRequest(imp *openrtb2.Imp) (string, error) { + var impExt adapters.ExtImpBidder + if err := json.Unmarshal(imp.Ext, &impExt); err != nil { + return "", &errortypes.BadInput{ + Message: fmt.Sprintf("Failed to deserialize bidder impression extension: %v", err), + } + } + + var admaticExt openrtb_ext.ImpExtAdmatic + if err := json.Unmarshal(impExt.Bidder, &admaticExt); err != nil { + return "", &errortypes.BadInput{ + Message: fmt.Sprintf("Failed to deserialize AdMatic extension: %v", err), + } + } + + endpointParams := macros.EndpointTemplateParams{ + Host: admaticExt.Host, + } + + return macros.ResolveMacros(a.endpoint, endpointParams) +} + +func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if adapters.IsResponseStatusCodeNoContent(responseData) { + return nil, nil + } + + err := adapters.CheckResponseStatusCodeForErrors(responseData) + if err != nil { + return nil, []error{err} + } + var response openrtb2.BidResponse + if err := json.Unmarshal(responseData.Body, &response); err != nil { + return nil, []error{err} + } + + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) + if len(response.Cur) != 0 { + bidResponse.Currency = response.Cur + } + + for _, seatBid := range response.SeatBid { + for i := range seatBid.Bid { + + bidMediaType, err := getMediaTypeForBid(seatBid.Bid[i].ImpID, request.Imp) + if err != nil { + return nil, []error{err} + } + bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ + Bid: &seatBid.Bid[i], + BidType: bidMediaType, + }) + } + } + return bidResponse, nil +} + +func getMediaTypeForBid(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) { + for _, imp := range imps { + if imp.ID == impID { + if imp.Banner != nil { + return openrtb_ext.BidTypeBanner, nil + } else if imp.Video != nil { + return openrtb_ext.BidTypeVideo, nil + } else if imp.Native != nil { + return openrtb_ext.BidTypeNative, nil + } + } + } + return "", &errortypes.BadServerResponse{ + Message: fmt.Sprintf("The impression with ID %s is not present into the request", impID), + } +} diff --git a/adapters/admatic/admatic_test.go b/adapters/admatic/admatic_test.go new file mode 100644 index 00000000000..d9e34c1eec7 --- /dev/null +++ b/adapters/admatic/admatic_test.go @@ -0,0 +1,29 @@ +package admatic + +import ( + "testing" + + "github.com/prebid/prebid-server/v2/adapters/adapterstest" + "github.com/prebid/prebid-server/v2/config" + "github.com/prebid/prebid-server/v2/openrtb_ext" + "github.com/stretchr/testify/assert" +) + +func TestJsonSamples(t *testing.T) { + bidder, buildErr := Builder(openrtb_ext.BidderAdmatic, config.Adapter{ + Endpoint: "http://pbs.admatic.com.tr?host={{.Host}}"}, + config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1281, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "admatictest", bidder) +} + +func TestEndpointTemplateMalformed(t *testing.T) { + _, buildErr := Builder(openrtb_ext.BidderAdmatic, config.Adapter{ + Endpoint: "host={{Host}}"}, config.Server{ExternalUrl: "http://hosturl.com"}) + + assert.Error(t, buildErr) +} diff --git a/adapters/admatic/admatictest/exemplary/banner.json b/adapters/admatic/admatictest/exemplary/banner.json new file mode 100644 index 00000000000..5950034a61e --- /dev/null +++ b/adapters/admatic/admatictest/exemplary/banner.json @@ -0,0 +1,96 @@ +{ + "mockBidRequest": { + "id": "test-request-id-banner", + "imp": [ + { + "id": "test-imp-id-banner", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://pbs.admatic.com.tr?host=layer.serve.admatic.com.tr", + "body": { + "id": "test-request-id-banner", + "imp": [ + { + "id": "test-imp-id-banner", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + "impIDs": ["test-imp-id-banner"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id-banner", + "seatbid": [ + { + "seat": "admatic", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-banner", + "price": 0.5, + "adm": "some-test-ad-banner", + "crid": "crid_10", + "w": 728, + "h": 90 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-banner", + "price": 0.5, + "adm": "some-test-ad-banner", + "crid": "crid_10", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/admatic/admatictest/exemplary/multiple-imps.json b/adapters/admatic/admatictest/exemplary/multiple-imps.json new file mode 100644 index 00000000000..8738fbb7f95 --- /dev/null +++ b/adapters/admatic/admatictest/exemplary/multiple-imps.json @@ -0,0 +1,181 @@ +{ + "mockBidRequest": { + "id": "test-request-id-banner", + "imp": [ + { + "id": "test-imp-id-banner", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + }, + { + "id": "test-imp-id-banner2", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer2.serve.admatic.com.tr", + "networkId": 123456 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://pbs.admatic.com.tr?host=layer.serve.admatic.com.tr", + "body": { + "id": "test-request-id-banner", + "imp": [ + { + "id": "test-imp-id-banner", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + "impIDs": ["test-imp-id-banner"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id-banner", + "seatbid": [ + { + "seat": "admatic", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-banner", + "price": 0.5, + "adm": "some-test-ad-banner", + "crid": "crid_10", + "w": 728, + "h": 90 + } + ] + } + ], + "cur": "USD" + } + } + }, + { + "expectedRequest": { + "uri": "http://pbs.admatic.com.tr?host=layer2.serve.admatic.com.tr", + "body": { + "id": "test-request-id-banner", + "imp": [ + { + "id": "test-imp-id-banner2", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer2.serve.admatic.com.tr", + "networkId": 123456 + } + } + } + ] + }, + "impIDs": ["test-imp-id-banner2"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id-banner", + "seatbid": [ + { + "seat": "admatic", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e801", + "impid": "test-imp-id-banner2", + "price": 0.5, + "adm": "some-test-ad-banner2", + "crid": "crid_11", + "w": 728, + "h": 90 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-banner", + "price": 0.5, + "adm": "some-test-ad-banner", + "crid": "crid_10", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + }, + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e801", + "impid": "test-imp-id-banner2", + "price": 0.5, + "adm": "some-test-ad-banner2", + "crid": "crid_11", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/admatic/admatictest/exemplary/native.json b/adapters/admatic/admatictest/exemplary/native.json new file mode 100644 index 00000000000..455406d05c2 --- /dev/null +++ b/adapters/admatic/admatictest/exemplary/native.json @@ -0,0 +1,86 @@ +{ + "mockBidRequest": { + "id": "test-request-id-native", + "imp": [ + { + "id": "test-imp-id-native", + "native": { + "request": "" + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://pbs.admatic.com.tr?host=layer.serve.admatic.com.tr", + "body": { + "id": "test-request-id-native", + "imp": [ + { + "id": "test-imp-id-native", + "native": { + "request": "" + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + "impIDs": ["test-imp-id-native"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id-native", + "seatbid": [ + { + "seat": "admatic", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-native", + "price": 0.5, + "adm": "some-test-ad-native", + "crid": "crid_10", + "w": 728, + "h": 90 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-native", + "price": 0.5, + "adm": "some-test-ad-native", + "crid": "crid_10", + "w": 728, + "h": 90 + }, + "type": "native" + } + ] + } + ] +} diff --git a/adapters/admatic/admatictest/exemplary/optional-params.json b/adapters/admatic/admatictest/exemplary/optional-params.json new file mode 100644 index 00000000000..e1652c30899 --- /dev/null +++ b/adapters/admatic/admatictest/exemplary/optional-params.json @@ -0,0 +1,100 @@ +{ + "mockBidRequest": { + "id": "test-request-id-banner", + "imp": [ + { + "id": "test-imp-id-banner", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345, + "bidFloor": 0.1, + "isTest": false + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://pbs.admatic.com.tr?host=layer.serve.admatic.com.tr", + "body": { + "id": "test-request-id-banner", + "imp": [ + { + "id": "test-imp-id-banner", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345, + "bidFloor": 0.1, + "isTest": false + } + } + } + ] + }, + "impIDs": ["test-imp-id-banner"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id-banner", + "seatbid": [ + { + "seat": "admatic", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-banner", + "price": 0.5, + "adm": "some-test-ad-banner", + "crid": "crid_10", + "w": 728, + "h": 90 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-banner", + "price": 0.5, + "adm": "some-test-ad-banner", + "crid": "crid_10", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/admatic/admatictest/exemplary/video.json b/adapters/admatic/admatictest/exemplary/video.json new file mode 100644 index 00000000000..58f5e89810b --- /dev/null +++ b/adapters/admatic/admatictest/exemplary/video.json @@ -0,0 +1,94 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-video-id", + "video": { + "mimes": ["video/mp4"], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://pbs.admatic.com.tr?host=layer.serve.admatic.com.tr", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-video-id", + "video": { + "mimes": ["video/mp4"], + "w": 300, + "h": 250 + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + "impIDs": ["test-video-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id-test-video-id", + "seatbid": [ + { + "seat": "test-seat", + "bid": [ + { + "id": "5dce6055-a93c-1fd0-8c29-14afc3e510fd", + "impid": "test-video-id", + "price": 0.1529, + "nurl": "test-win", + "adm": "test-video", + "adid": "92-288", + "adomain": ["advertiserdomain.com"], + "crid": "288", + "w": 300, + "h": 250 + } + ] + } + ] + } + } + } + ], + "expectedBidResponses": [ + { + "bids": [ + { + "bid": { + "id": "5dce6055-a93c-1fd0-8c29-14afc3e510fd", + "impid": "test-video-id", + "price": 0.1529, + "nurl": "test-win", + "adm": "test-video", + "adid": "92-288", + "adomain": ["advertiserdomain.com"], + "crid": "288", + "w": 300, + "h": 250 + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/admatic/admatictest/supplemental/bad-request.json b/adapters/admatic/admatictest/supplemental/bad-request.json new file mode 100644 index 00000000000..90f6ebd6c9d --- /dev/null +++ b/adapters/admatic/admatictest/supplemental/bad-request.json @@ -0,0 +1,66 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://pbs.admatic.com.tr?host=layer.serve.admatic.com.tr", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 400, + "headers": {} + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/adapters/admatic/admatictest/supplemental/multiple-imps-with-error.json b/adapters/admatic/admatictest/supplemental/multiple-imps-with-error.json new file mode 100644 index 00000000000..c080e57c7e5 --- /dev/null +++ b/adapters/admatic/admatictest/supplemental/multiple-imps-with-error.json @@ -0,0 +1,119 @@ +{ + "mockBidRequest": { + "id": "test-request-id-banner", + "imp": [ + { + "id": "test-imp-id-banner", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + }, + { + "id": "test-imp-id-banner2", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": {}, + "networkId": 12345 + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://pbs.admatic.com.tr?host=layer.serve.admatic.com.tr", + "body": { + "id": "test-request-id-banner", + "imp": [ + { + "id": "test-imp-id-banner", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + "impIDs": ["test-imp-id-banner"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id-banner", + "seatbid": [ + { + "seat": "admatic", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-banner", + "price": 0.5, + "adm": "some-test-ad-banner", + "crid": "crid_10", + "w": 728, + "h": 90 + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedMakeRequestsErrors": [ + { + "value": "Failed to deserialize AdMatic extension: json: cannot unmarshal object into Go struct field ImpExtAdmatic.host of type string", + "comparison": "literal" + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id-banner", + "price": 0.5, + "adm": "some-test-ad-banner", + "crid": "crid_10", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] +} diff --git a/adapters/admatic/admatictest/supplemental/response-200-without-body.json b/adapters/admatic/admatictest/supplemental/response-200-without-body.json new file mode 100644 index 00000000000..8db31ad713f --- /dev/null +++ b/adapters/admatic/admatictest/supplemental/response-200-without-body.json @@ -0,0 +1,64 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://pbs.admatic.com.tr?host=layer.serve.admatic.com.tr", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 200 + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "unexpected end of JSON input", + "comparison": "literal" + } + ] +} diff --git a/adapters/admatic/admatictest/supplemental/response-204.json b/adapters/admatic/admatictest/supplemental/response-204.json new file mode 100644 index 00000000000..849cc85e89b --- /dev/null +++ b/adapters/admatic/admatictest/supplemental/response-204.json @@ -0,0 +1,59 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://pbs.admatic.com.tr?host=layer.serve.admatic.com.tr", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 204 + } + } + ], + "expectedBidResponses": [] +} diff --git a/adapters/admatic/admatictest/supplemental/server-error.json b/adapters/admatic/admatictest/supplemental/server-error.json new file mode 100644 index 00000000000..91bb22733bc --- /dev/null +++ b/adapters/admatic/admatictest/supplemental/server-error.json @@ -0,0 +1,65 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "http://pbs.admatic.com.tr?host=layer.serve.admatic.com.tr", + "body": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "host": "layer.serve.admatic.com.tr", + "networkId": 12345 + } + } + } + ] + }, + "impIDs": ["test-imp-id"] + }, + "mockResponse": { + "status": 500, + "headers": {} + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 500. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] +} diff --git a/adapters/admatic/params_test.go b/adapters/admatic/params_test.go new file mode 100644 index 00000000000..817ee92e803 --- /dev/null +++ b/adapters/admatic/params_test.go @@ -0,0 +1,56 @@ +package admatic + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v2/openrtb_ext" +) + +func TestValidParams(t *testing.T) { + validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params") + if err != nil { + t.Fatalf("Failed to fetch the json schema. %v", err) + } + + for _, p := range validParams { + if err := validator.Validate(openrtb_ext.BidderAdmatic, 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.BidderAdmatic, json.RawMessage(p)); err == nil { + t.Errorf("Schema allowed invalid params: %s", p) + } + } +} + +var validParams = []string{ + `{ "host": "layer.serve.admatic.com.tr", + "networkId": 1111, + "ext": { + "key1": "value1", + "key2": "value2" + } + }`, + `{"host": "layer.serve.admatic.com.tr", "networkId": 1111}`, +} + +var invalidParams = []string{ + `{"ext": { + "key1": "value1", + "key2": "value2" + }`, + `{}`, + `{"host": 123, "networkId":"1111"}`, + `{"host": "layer.serve.admatic.com.tr", "networkId":"1111"}`, + `{"host": 1111, "networkId":1111}`, +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index abb8cefcea5..fdbc0dbf213 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -13,6 +13,7 @@ import ( "github.com/prebid/prebid-server/v2/adapters/adkernel" "github.com/prebid/prebid-server/v2/adapters/adkernelAdn" "github.com/prebid/prebid-server/v2/adapters/adman" + "github.com/prebid/prebid-server/v2/adapters/admatic" "github.com/prebid/prebid-server/v2/adapters/admixer" "github.com/prebid/prebid-server/v2/adapters/adnuntius" "github.com/prebid/prebid-server/v2/adapters/adocean" @@ -228,6 +229,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderAdkernel: adkernel.Builder, openrtb_ext.BidderAdkernelAdn: adkernelAdn.Builder, openrtb_ext.BidderAdman: adman.Builder, + openrtb_ext.BidderAdmatic: admatic.Builder, openrtb_ext.BidderAdmixer: admixer.Builder, openrtb_ext.BidderAdnuntius: adnuntius.Builder, openrtb_ext.BidderAdOcean: adocean.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index bc3679267a9..5af05d90b6f 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -29,6 +29,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderAdkernel, BidderAdkernelAdn, BidderAdman, + BidderAdmatic, BidderAdmixer, BidderAdnuntius, BidderAdOcean, @@ -344,6 +345,7 @@ const ( BidderAdkernel BidderName = "adkernel" BidderAdkernelAdn BidderName = "adkernelAdn" BidderAdman BidderName = "adman" + BidderAdmatic BidderName = "admatic" BidderAdmixer BidderName = "admixer" BidderAdnuntius BidderName = "adnuntius" BidderAdOcean BidderName = "adocean" diff --git a/openrtb_ext/imp_admatic.go b/openrtb_ext/imp_admatic.go new file mode 100644 index 00000000000..89e9acc1f72 --- /dev/null +++ b/openrtb_ext/imp_admatic.go @@ -0,0 +1,6 @@ +package openrtb_ext + +type ImpExtAdmatic struct { + Host string `json:"host"` + NetworkId int `json:"networkId"` +} diff --git a/static/bidder-info/admatic.yaml b/static/bidder-info/admatic.yaml new file mode 100644 index 00000000000..a63e910b3fe --- /dev/null +++ b/static/bidder-info/admatic.yaml @@ -0,0 +1,20 @@ +endpoint: "http://pbs.admatic.com.tr?host={{.Host}}" +maintainer: + email: "prebid@admatic.com.tr" +gvlVendorID: 1281 +capabilities: + app: + mediaTypes: + - banner + - video + - native + site: + mediaTypes: + - banner + - video + - native +userSync: + # admatic supports user syncing, but requires configuration by the host. contact this + # bidder directly at the email address in this file to ask about enabling user sync. + supports: + - iframe diff --git a/static/bidder-params/admatic.json b/static/bidder-params/admatic.json new file mode 100644 index 00000000000..016a95cfb0a --- /dev/null +++ b/static/bidder-params/admatic.json @@ -0,0 +1,20 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "AdMatic Adapter Params", + "description": "A schema which validates params accepted by the AdMatic adapter", + "type": "object", + "properties": { + "host": { + "type": "string", + "description": "Host Name" + }, + "networkId": { + "type": "integer", + "description": "AdMatic Network Id" + } + }, + "required": [ + "host", + "networkId" + ] +} \ No newline at end of file