diff --git a/adapters/freewheelssp/freewheelssp.go b/adapters/freewheelssp/freewheelssp.go
new file mode 100644
index 00000000000..0f82952d3e1
--- /dev/null
+++ b/adapters/freewheelssp/freewheelssp.go
@@ -0,0 +1,103 @@
+package freewheelssp
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/prebid/openrtb/v17/openrtb2"
+ "github.com/prebid/prebid-server/adapters"
+ "github.com/prebid/prebid-server/config"
+ "github.com/prebid/prebid-server/errortypes"
+ "github.com/prebid/prebid-server/openrtb_ext"
+ "net/http"
+)
+
+type adapter struct {
+ endpoint string
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+ for i := 0; i < len(request.Imp); i++ {
+ imp := &request.Imp[i]
+ var bidderExt adapters.ExtImpBidder
+ if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+ return nil, []error{&errortypes.BadInput{
+ Message: fmt.Sprintf("Invalid imp.ext for impression index %d. Error Infomation: %s", i, err.Error()),
+ }}
+ }
+
+ var impExt openrtb_ext.ImpExtFreewheelSSP
+ if err := json.Unmarshal(bidderExt.Bidder, &impExt); err != nil {
+ return nil, []error{&errortypes.BadInput{
+ Message: fmt.Sprintf("Invalid imp.ext for impression index %d. Error Infomation: %s", i, err.Error()),
+ }}
+ }
+
+ var err error
+ if imp.Ext, err = json.Marshal(impExt); err != nil {
+ return nil, []error{&errortypes.BadInput{
+ Message: fmt.Sprintf("Unable to transfer requestImpExt to Json fomat, %s", err.Error()),
+ }}
+ }
+ }
+
+ requestJSON, err := json.Marshal(request)
+ if err != nil {
+ return nil, []error{&errortypes.BadInput{
+ Message: fmt.Sprintf("Unable to transfer request to Json fomat, %s", err.Error()),
+ }}
+ }
+
+ headers := http.Header{}
+ headers.Add("Componentid", "prebid-go")
+
+ requestData := &adapters.RequestData{
+ Method: "POST",
+ Uri: a.endpoint,
+ Body: requestJSON,
+ Headers: headers,
+ }
+ return []*adapters.RequestData{requestData}, nil
+}
+
+func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+ if response.StatusCode == http.StatusNoContent {
+ return nil, nil
+ }
+
+ if response.StatusCode != http.StatusOK {
+ return nil, []error{&errortypes.BadServerResponse{
+ Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
+ }}
+ }
+
+ var bidResp openrtb2.BidResponse
+ if err := json.Unmarshal(response.Body, &bidResp); err != nil {
+ return nil, []error{err}
+ }
+
+ cur := bidResp.Cur
+ bidResponse := &adapters.BidderResponse{
+ Currency: cur,
+ Bids: []*adapters.TypedBid{},
+ }
+
+ bidType := openrtb_ext.BidTypeVideo
+
+ for _, seatBid := range bidResp.SeatBid {
+ for i := range seatBid.Bid {
+ b := &adapters.TypedBid{
+ Bid: &seatBid.Bid[i],
+ BidType: bidType,
+ }
+ bidResponse.Bids = append(bidResponse.Bids, b)
+ }
+ }
+ return bidResponse, nil
+}
+
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+ bidder := &adapter{
+ config.Endpoint,
+ }
+ return bidder, nil
+}
diff --git a/adapters/freewheelssp/freewheelssp_test.go b/adapters/freewheelssp/freewheelssp_test.go
new file mode 100644
index 00000000000..5f06a29c2fd
--- /dev/null
+++ b/adapters/freewheelssp/freewheelssp_test.go
@@ -0,0 +1,19 @@
+package freewheelssp
+
+import (
+ "github.com/prebid/prebid-server/adapters/adapterstest"
+ "github.com/prebid/prebid-server/config"
+ "github.com/prebid/prebid-server/openrtb_ext"
+ "testing"
+)
+
+func TestJsonSamples(t *testing.T) {
+ bidder, buildErr := Builder(openrtb_ext.BidderFreewheelSSP, config.Adapter{
+ Endpoint: "https://testjsonsample.com"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+ if buildErr != nil {
+ t.Fatalf("Builder returned unexpected error %v", buildErr)
+ }
+
+ adapterstest.RunJSONBidderTest(t, "freewheelssptest", bidder)
+}
diff --git a/adapters/freewheelssp/freewheelssptest/exemplary/multi-imp.json b/adapters/freewheelssp/freewheelssptest/exemplary/multi-imp.json
new file mode 100644
index 00000000000..5ec36993375
--- /dev/null
+++ b/adapters/freewheelssp/freewheelssptest/exemplary/multi-imp.json
@@ -0,0 +1,144 @@
+{
+ "mockBidRequest": {
+ "id": "freewheelssp-test",
+ "site": {
+ "page": "prebid.org"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "video": {
+ "mimes": ["video/mp4"],
+ "w": 300,
+ "h": 250
+ },
+ "ext": {
+ "bidder": {
+ "zoneId": 12345
+ }
+ }
+ },
+ {
+ "id": "imp-2",
+ "video": {
+ "mimes": ["video/mp4"],
+ "w": 300,
+ "h": 250
+ },
+ "ext": {
+ "bidder": {
+ "zoneId": 12346
+ }
+ }
+ }
+ ]
+ },
+
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://testjsonsample.com",
+ "body":{
+ "id": "freewheelssp-test",
+ "site": {
+ "page": "prebid.org"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "video": {
+ "mimes": ["video/mp4"],
+ "w": 300,
+ "h": 250
+ },
+ "ext": {
+ "zoneId": 12345
+ }
+ },
+ {
+ "id": "imp-2",
+ "video": {
+ "mimes": ["video/mp4"],
+ "w": 300,
+ "h": 250
+ },
+ "ext": {
+ "zoneId": 12346
+ }
+ }
+ ]
+ },
+ "headers": {
+ "Componentid": [
+ "prebid-go"
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "freewheelssp-test",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "12345_freewheelssp-test_1",
+ "impid": "imp-1",
+ "price": 1.0,
+ "adid": "7857",
+ "adm": "",
+ "cid": "4001",
+ "crid": "7857"
+ },
+ {
+ "id": "12346_freewheelssp-test_2",
+ "impid": "imp-2",
+ "price": 1.0,
+ "adid": "7933",
+ "adm": "",
+ "cid": "3476",
+ "crid": "7933"
+ }
+ ],
+ "seat": "freewheelsspTv"
+ }
+ ],
+ "bidid": "freewheelssp-test",
+ "cur": "EUR"
+ }
+ }
+ }
+ ],
+
+ "expectedBidResponses": [
+ {
+ "currency": "CUR",
+ "bids": [
+ {
+ "bid": {
+ "id": "12345_freewheelssp-test_1",
+ "impid": "imp-1",
+ "price": 1.0,
+ "adid": "7857",
+ "adm": "",
+ "cid": "4001",
+ "crid": "7857"
+ },
+ "type": "video"
+ },
+ {
+ "bid": {
+ "id": "12346_freewheelssp-test_2",
+ "impid": "imp-2",
+ "price": 1.0,
+ "adid": "7933",
+ "adm": "",
+ "cid": "3476",
+ "crid": "7933"
+ },
+ "type": "video"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/freewheelssp/freewheelssptest/exemplary/single-imp.json b/adapters/freewheelssp/freewheelssptest/exemplary/single-imp.json
new file mode 100644
index 00000000000..1d55dfecaf0
--- /dev/null
+++ b/adapters/freewheelssp/freewheelssptest/exemplary/single-imp.json
@@ -0,0 +1,97 @@
+{
+ "mockBidRequest": {
+ "id": "freewheelssp-test",
+ "site": {
+ "page": "prebid.org"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "video": {
+ "mimes": ["video/mp4"],
+ "w": 300,
+ "h": 250
+ },
+ "ext": {
+ "bidder": {
+ "zoneId": 12345
+ }
+ }
+ }
+ ]
+ },
+
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://testjsonsample.com",
+ "body":{
+ "id": "freewheelssp-test",
+ "site": {
+ "page": "prebid.org"
+ },
+ "imp": [{
+ "id": "imp-1",
+ "video": {
+ "mimes": ["video/mp4"],
+ "w": 300,
+ "h": 250
+ },
+ "ext": {
+ "zoneId": 12345
+ }
+ }]
+ },
+ "headers": {
+ "Componentid": [
+ "prebid-go"
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "freewheelssp-test",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "12345_freewheelssp-test_1",
+ "impid": "imp-1",
+ "price": 1.0,
+ "adid": "7857",
+ "adm": "",
+ "cid": "4001",
+ "crid": "7857"
+ }
+ ],
+ "seat": "freewheelsspTv"
+ }
+ ],
+ "bidid": "freewheelssp-test",
+ "cur": "EUR"
+ }
+ }
+ }
+ ],
+
+ "expectedBidResponses": [
+ {
+ "currency": "CUR",
+ "bids": [
+ {
+ "bid": {
+ "id": "12345_freewheelssp-test_1",
+ "impid": "imp-1",
+ "price": 1.0,
+ "adid": "7857",
+ "adm": "",
+ "cid": "4001",
+ "crid": "7857"
+ },
+ "type": "video"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/freewheelssp/freewheelssptest/supplemental/204-bid-response.json b/adapters/freewheelssp/freewheelssptest/supplemental/204-bid-response.json
new file mode 100644
index 00000000000..a84fa4f1268
--- /dev/null
+++ b/adapters/freewheelssp/freewheelssptest/supplemental/204-bid-response.json
@@ -0,0 +1,60 @@
+{
+ "mockBidRequest": {
+ "id": "freewheelssp-test",
+ "site": {
+ "page": "prebid.org"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "video": {
+ "mimes": ["video/mp4"],
+ "w": 300,
+ "h": 250
+ },
+ "ext": {
+ "bidder": {
+ "zoneId": 12345
+ }
+ }
+ }
+ ]
+ },
+
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://testjsonsample.com",
+ "body":{
+ "id": "freewheelssp-test",
+ "site": {
+ "page": "prebid.org"
+ },
+ "imp": [{
+ "id": "imp-1",
+ "video": {
+ "mimes": ["video/mp4"],
+ "w": 300,
+ "h": 250
+ },
+ "ext": {
+ "zoneId": 12345
+ }
+ }]
+ },
+ "headers": {
+ "Componentid": [
+ "prebid-go"
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 204,
+ "body" : {}
+ }
+ }
+ ],
+
+ "expectedBidResponses": []
+}
+
diff --git a/adapters/freewheelssp/freewheelssptest/supplemental/503-bid-response.json b/adapters/freewheelssp/freewheelssptest/supplemental/503-bid-response.json
new file mode 100644
index 00000000000..475114d0230
--- /dev/null
+++ b/adapters/freewheelssp/freewheelssptest/supplemental/503-bid-response.json
@@ -0,0 +1,64 @@
+{
+ "mockBidRequest": {
+ "id": "freewheelssp-test",
+ "site": {
+ "page": "prebid.org"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "video": {
+ "mimes": ["video/mp4"],
+ "w": 300,
+ "h": 250
+ },
+ "ext": {
+ "bidder": {
+ "zoneId": 12345
+ }
+ }
+ }
+ ]
+ },
+
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://testjsonsample.com",
+ "body":{
+ "id": "freewheelssp-test",
+ "site": {
+ "page": "prebid.org"
+ },
+ "imp": [{
+ "id": "imp-1",
+ "video": {
+ "mimes": ["video/mp4"],
+ "w": 300,
+ "h": 250
+ },
+ "ext": {
+ "zoneId": 12345
+ }
+ }]
+ },
+ "headers": {
+ "Componentid": [
+ "prebid-go"
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 503,
+ "body" : {}
+ }
+ }
+ ],
+
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unexpected status code: 503. Run with request.debug = 1 for more info",
+ "comparison": "literal"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 26abc217248..f4d747333e3 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -71,6 +71,7 @@ import (
"github.com/prebid/prebid-server/adapters/engagebdr"
"github.com/prebid/prebid-server/adapters/eplanning"
"github.com/prebid/prebid-server/adapters/epom"
+ "github.com/prebid/prebid-server/adapters/freewheelssp"
"github.com/prebid/prebid-server/adapters/gamma"
"github.com/prebid/prebid-server/adapters/gamoshi"
"github.com/prebid/prebid-server/adapters/grid"
@@ -230,6 +231,8 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
openrtb_ext.BidderEPlanning: eplanning.Builder,
openrtb_ext.BidderEpom: epom.Builder,
openrtb_ext.BidderEVolution: evolution.Builder,
+ openrtb_ext.BidderFreewheelSSP: freewheelssp.Builder,
+ openrtb_ext.BidderFreewheelSSPOld: freewheelssp.Builder,
openrtb_ext.BidderGamma: gamma.Builder,
openrtb_ext.BidderGamoshi: gamoshi.Builder,
openrtb_ext.BidderGrid: grid.Builder,
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index e4c68fe62b1..b4baf3d7f4b 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -151,6 +151,8 @@ const (
BidderEPlanning BidderName = "eplanning"
BidderEpom BidderName = "epom"
BidderEVolution BidderName = "e_volution"
+ BidderFreewheelSSP BidderName = "freewheelssp"
+ BidderFreewheelSSPOld BidderName = "freewheel-ssp"
BidderGamma BidderName = "gamma"
BidderGamoshi BidderName = "gamoshi"
BidderGrid BidderName = "grid"
@@ -318,6 +320,8 @@ func CoreBidderNames() []BidderName {
BidderEPlanning,
BidderEpom,
BidderEVolution,
+ BidderFreewheelSSP,
+ BidderFreewheelSSPOld,
BidderGamma,
BidderGamoshi,
BidderGrid,
diff --git a/openrtb_ext/bidders_validate_test.go b/openrtb_ext/bidders_validate_test.go
index 2ee3dd7d806..530f260c761 100644
--- a/openrtb_ext/bidders_validate_test.go
+++ b/openrtb_ext/bidders_validate_test.go
@@ -50,7 +50,7 @@ func TestBidderUniquenessGatekeeping(t *testing.T) {
// - Exclude duplicates of adapters for the same bidder, as it's unlikely a publisher will use both.
var bidders []string
for _, bidder := range CoreBidderNames() {
- if bidder != BidderTripleliftNative && bidder != BidderAdkernelAdn {
+ if bidder != BidderTripleliftNative && bidder != BidderAdkernelAdn && bidder != BidderFreewheelSSPOld {
bidders = append(bidders, string(bidder))
}
}
diff --git a/openrtb_ext/imp_freewheelssp.go b/openrtb_ext/imp_freewheelssp.go
new file mode 100644
index 00000000000..11c22ff8154
--- /dev/null
+++ b/openrtb_ext/imp_freewheelssp.go
@@ -0,0 +1,5 @@
+package openrtb_ext
+
+type ImpExtFreewheelSSP struct {
+ ZoneId int `json:"zoneId"`
+}
diff --git a/static/bidder-info/freewheel-ssp.yaml b/static/bidder-info/freewheel-ssp.yaml
new file mode 100644
index 00000000000..f013445a820
--- /dev/null
+++ b/static/bidder-info/freewheel-ssp.yaml
@@ -0,0 +1,16 @@
+endpoint: "https://ads.stickyadstv.com/openrtb/dsp"
+maintainer:
+ email: prebid-maintainer@freewheel.com
+gvlVendorID: 285
+modifyingVastXmlAllowed: true
+capabilities:
+ app:
+ mediaTypes:
+ - video
+ site:
+ mediaTypes:
+ - video
+userSync:
+ iframe:
+ url: "https://ads.stickyadstv.com/pbs-user-sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}}"
+ userMacro: "{viewerid}"
diff --git a/static/bidder-info/freewheelssp.yaml b/static/bidder-info/freewheelssp.yaml
new file mode 100644
index 00000000000..5cef0de13b3
--- /dev/null
+++ b/static/bidder-info/freewheelssp.yaml
@@ -0,0 +1,16 @@
+endpoint: "https://ads.stickyadstv.com/openrtb/dsp"
+maintainer:
+ email: prebid-maintainer@freewheel.com
+gvlVendorID: 285
+modifyingVastXmlAllowed: true
+capabilities:
+ app:
+ mediaTypes:
+ - video
+ site:
+ mediaTypes:
+ - video
+userSync:
+ iframe:
+ url: "https://ads.stickyadstv.com/pbs-user-sync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&r={{.RedirectURL}}"
+ userMacro: "{viewerid}"
\ No newline at end of file
diff --git a/static/bidder-params/freewheel-ssp.json b/static/bidder-params/freewheel-ssp.json
new file mode 100644
index 00000000000..103aed03198
--- /dev/null
+++ b/static/bidder-params/freewheel-ssp.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "FreewheelSSP old Adapter Params",
+ "description": "A schema which validates params accepted by the FreewheelSSP adapter",
+ "type": "object",
+
+ "properties": {
+ "zoneId": {
+ "type": "integer",
+ "description": "Zone ID"
+ }
+ },
+
+ "required": ["zoneId"]
+}
diff --git a/static/bidder-params/freewheelssp.json b/static/bidder-params/freewheelssp.json
new file mode 100644
index 00000000000..6f93501a3e1
--- /dev/null
+++ b/static/bidder-params/freewheelssp.json
@@ -0,0 +1,15 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "FreewheelSSP Adapter Params",
+ "description": "A schema which validates params accepted by the FreewheelSSP adapter",
+ "type": "object",
+
+ "properties": {
+ "zoneId": {
+ "type": "integer",
+ "description": "Zone ID"
+ }
+ },
+
+ "required": ["zoneId"]
+}
\ No newline at end of file