Skip to content

Commit f5f6439

Browse files
New Adapter: Synapse HX (prebid#4795)
1 parent e02a53a commit f5f6439

20 files changed

Lines changed: 1798 additions & 0 deletions

adapters/synapseHX/params_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package synapseHX
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
"github.com/prebid/prebid-server/v4/openrtb_ext"
8+
)
9+
10+
func TestValidParams(t *testing.T) {
11+
validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
12+
if err != nil {
13+
t.Fatalf("Failed to fetch json-schemas. %v", err)
14+
}
15+
16+
for _, param := range validParams {
17+
if err := validator.Validate(openrtb_ext.BidderSynapseHX, json.RawMessage(param)); err != nil {
18+
t.Errorf("Schema rejected valid params: %s", param)
19+
}
20+
}
21+
}
22+
23+
func TestInvalidParams(t *testing.T) {
24+
validator, err := openrtb_ext.NewBidderParamsValidator("../../static/bidder-params")
25+
if err != nil {
26+
t.Fatalf("Failed to fetch json-schemas. %v", err)
27+
}
28+
29+
for _, param := range invalidParams {
30+
if err := validator.Validate(openrtb_ext.BidderSynapseHX, json.RawMessage(param)); err == nil {
31+
t.Errorf("Schema allowed invalid params: %s", param)
32+
}
33+
}
34+
}
35+
36+
var validParams = []string{
37+
`{"tenantId":"a"}`,
38+
`{"tenantId":"8cd85aed-25a6-4db0-ad98-4a3af1f7601c"}`,
39+
}
40+
41+
var invalidParams = []string{
42+
`{}`,
43+
`{"tenantId":1}`,
44+
`{"tenantId":""}`,
45+
}

adapters/synapseHX/synapseHX.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package synapseHX
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
"net/url"
7+
8+
"github.com/prebid/openrtb/v20/openrtb2"
9+
"github.com/prebid/prebid-server/v4/adapters"
10+
"github.com/prebid/prebid-server/v4/config"
11+
"github.com/prebid/prebid-server/v4/errortypes"
12+
"github.com/prebid/prebid-server/v4/openrtb_ext"
13+
"github.com/prebid/prebid-server/v4/util/jsonutil"
14+
)
15+
16+
type adapter struct {
17+
endpoint string
18+
}
19+
20+
func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
21+
bidder := &adapter{
22+
endpoint: config.Endpoint,
23+
}
24+
25+
return bidder, nil
26+
}
27+
28+
func (adapter *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
29+
var requests []*adapters.RequestData
30+
31+
var tenantId string
32+
33+
if len(request.Imp) == 0 {
34+
return nil, []error{fmt.Errorf("request contains no impressions")}
35+
}
36+
37+
var bidderExt adapters.ExtImpBidder
38+
if err := jsonutil.Unmarshal(request.Imp[0].Ext, &bidderExt); err != nil {
39+
return nil, []error{fmt.Errorf("failed to unmarshal bidder ext: %w", err)}
40+
}
41+
42+
var ext openrtb_ext.ExtImpSynapseHX
43+
if err := jsonutil.Unmarshal(bidderExt.Bidder, &ext); err != nil {
44+
return nil, []error{fmt.Errorf("failed to unmarshal bidder parameters: %w", err)}
45+
}
46+
47+
tenantId = ext.TenantID
48+
49+
requestBody, err := jsonutil.Marshal(request)
50+
if err != nil {
51+
return nil, []error{fmt.Errorf("failed to marshal request: %w", err)}
52+
}
53+
54+
url, _ := url.Parse(adapter.endpoint)
55+
q := url.Query()
56+
q.Set("pid", tenantId)
57+
url.RawQuery = q.Encode()
58+
59+
requestData := &adapters.RequestData{
60+
Method: http.MethodPost,
61+
Uri: url.String(),
62+
Body: requestBody,
63+
Headers: buildRequestHeaders(),
64+
ImpIDs: openrtb_ext.GetImpIDs(request.Imp),
65+
}
66+
67+
requests = append(requests, requestData)
68+
69+
return requests, nil
70+
}
71+
72+
func (adapter *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
73+
if adapters.IsResponseStatusCodeNoContent(response) {
74+
return nil, nil
75+
}
76+
77+
if err := adapters.CheckResponseStatusCodeForErrors(response); err != nil {
78+
return nil, []error{err}
79+
}
80+
81+
var bidResponse openrtb2.BidResponse
82+
if err := jsonutil.Unmarshal(response.Body, &bidResponse); err != nil {
83+
return nil, []error{&errortypes.BadInput{Message: fmt.Sprintf("failed to unmarshal response body: %v", err)}}
84+
}
85+
86+
if len(bidResponse.SeatBid) == 0 || len(bidResponse.SeatBid[0].Bid) == 0 {
87+
return nil, nil
88+
}
89+
90+
var errs []error
91+
bidderResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResponse.SeatBid[0].Bid))
92+
93+
for i := range bidResponse.SeatBid {
94+
for j := range bidResponse.SeatBid[i].Bid {
95+
bid := &bidResponse.SeatBid[i].Bid[j]
96+
bidType, err := getMediaTypeForBid(bid)
97+
98+
if err != nil {
99+
errs = append(errs, err)
100+
} else {
101+
bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
102+
Bid: bid,
103+
BidType: bidType,
104+
})
105+
}
106+
}
107+
}
108+
109+
return bidderResponse, errs
110+
}
111+
112+
func buildRequestHeaders() http.Header {
113+
headers := http.Header{}
114+
headers.Add("Content-Type", "application/json;charset=utf-8")
115+
headers.Add("Accept", "application/json")
116+
headers.Add("X-Openrtb-Version", "2.6")
117+
118+
return headers
119+
}
120+
121+
func getMediaTypeForBid(bid *openrtb2.Bid) (openrtb_ext.BidType, error) {
122+
if bid.MType != 0 {
123+
switch bid.MType {
124+
case openrtb2.MarkupBanner:
125+
return openrtb_ext.BidTypeBanner, nil
126+
case openrtb2.MarkupVideo:
127+
return openrtb_ext.BidTypeVideo, nil
128+
default:
129+
return "", fmt.Errorf("unsupported media type %d", bid.MType)
130+
}
131+
}
132+
133+
if bid.Ext != nil {
134+
var bidExt openrtb_ext.ExtBid
135+
err := jsonutil.Unmarshal(bid.Ext, &bidExt)
136+
if err == nil && bidExt.Prebid != nil {
137+
switch bidExt.Prebid.Type {
138+
case openrtb_ext.BidTypeBanner, openrtb_ext.BidTypeVideo:
139+
return bidExt.Prebid.Type, nil
140+
default:
141+
return "", fmt.Errorf("unsupported media type \"%s\"", bidExt.Prebid.Type)
142+
}
143+
}
144+
}
145+
146+
return "", fmt.Errorf("failed to parse impression \"%s\" mediatype", bid.ImpID)
147+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package synapseHX
2+
3+
import (
4+
"testing"
5+
6+
"github.com/prebid/prebid-server/v4/adapters/adapterstest"
7+
"github.com/prebid/prebid-server/v4/config"
8+
"github.com/prebid/prebid-server/v4/openrtb_ext"
9+
)
10+
11+
func TestJsonSamples(t *testing.T) {
12+
bidder, buildErr := Builder(openrtb_ext.BidderSynapseHX, config.Adapter{
13+
Endpoint: "http://localhost:8080/bidder/"}, config.Server{})
14+
if buildErr != nil {
15+
t.Fatalf("Builder returned unexpected error %v", buildErr)
16+
}
17+
adapterstest.RunJSONBidderTest(t, "synapseHXtest", bidder)
18+
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
{
2+
"mockBidRequest": {
3+
"id": "auction-req-id",
4+
"imp": [
5+
{
6+
"id": "imp-id",
7+
"ext": {
8+
"bidder": {
9+
"tenantId": "a",
10+
"adUnitId": "b"
11+
}
12+
},
13+
"banner": {
14+
"w": 320,
15+
"h": 480,
16+
"pos": 7,
17+
"mimes": [
18+
"image/jpg",
19+
"image/gif",
20+
"text/html"
21+
],
22+
"format": [
23+
{
24+
"w": 320,
25+
"h": 480
26+
}
27+
],
28+
"api": [
29+
5,
30+
7
31+
],
32+
"vcm": 1,
33+
"id": "1"
34+
}
35+
}
36+
],
37+
"site": {
38+
"page": "https://example.com",
39+
"publisher": {
40+
"id": "publisher-id"
41+
},
42+
"id": "site-id"
43+
},
44+
"device": {
45+
"ua": "Mozilla/5.0"
46+
}
47+
},
48+
"httpCalls": [
49+
{
50+
"expectedRequest": {
51+
"headers": {
52+
"Content-Type": [
53+
"application/json;charset=utf-8"
54+
],
55+
"Accept": [
56+
"application/json"
57+
],
58+
"X-Openrtb-Version": [
59+
"2.6"
60+
]
61+
},
62+
"uri": "http://localhost:8080/bidder/?pid=a",
63+
"body": {
64+
"id": "auction-req-id",
65+
"imp": [
66+
{
67+
"id": "imp-id",
68+
"ext": {
69+
"bidder": {
70+
"tenantId": "a",
71+
"adUnitId": "b"
72+
}
73+
},
74+
"banner": {
75+
"w": 320,
76+
"h": 480,
77+
"pos": 7,
78+
"mimes": [
79+
"image/jpg",
80+
"image/gif",
81+
"text/html"
82+
],
83+
"format": [
84+
{
85+
"w": 320,
86+
"h": 480
87+
}
88+
],
89+
"api": [
90+
5,
91+
7
92+
],
93+
"vcm": 1,
94+
"id": "1"
95+
}
96+
}
97+
],
98+
"site": {
99+
"page": "https://example.com",
100+
"publisher": {
101+
"id": "publisher-id"
102+
},
103+
"id": "site-id"
104+
},
105+
"device": {
106+
"ua": "Mozilla/5.0"
107+
}
108+
},
109+
"impIDs": [
110+
"imp-id"
111+
]
112+
},
113+
"mockResponse": {
114+
"status": 200,
115+
"headers": {
116+
"Content-Type": [
117+
"application/json;charset=utf-8"
118+
],
119+
"X-Openrtb-Version": [
120+
"2.6"
121+
]
122+
},
123+
"body": {
124+
"id": "bid-resp-id",
125+
"seatbid": [
126+
{
127+
"bid": [
128+
{
129+
"id": "bid-item-id",
130+
"impid": "imp-id",
131+
"price": 0.04,
132+
"adm": "<a href='https://advertiser.com/click' target='_blank'><img src='https://advertiser.com/banner.jpg' width='320' height='50' alt='Ad'></a>",
133+
"adomain": [
134+
"example.com"
135+
],
136+
"mtype": 1
137+
}
138+
],
139+
"seat": "seat-1"
140+
}
141+
],
142+
"cur": "USD"
143+
}
144+
}
145+
}
146+
],
147+
"expectedBidResponses": [
148+
{
149+
"id": "bid-resp-id",
150+
"bids": [
151+
{
152+
"bid": {
153+
"id": "bid-item-id",
154+
"impid": "imp-id",
155+
"price": 0.04,
156+
"adm": "<a href='https://advertiser.com/click' target='_blank'><img src='https://advertiser.com/banner.jpg' width='320' height='50' alt='Ad'></a>",
157+
"adomain": [
158+
"example.com"
159+
],
160+
"mtype": 1
161+
},
162+
"type": "banner"
163+
}
164+
]
165+
}
166+
]
167+
}

0 commit comments

Comments
 (0)