diff --git a/adapters/dxkulture/dxkulture.go b/adapters/dxkulture/dxkulture.go
new file mode 100644
index 00000000000..5347c71a303
--- /dev/null
+++ b/adapters/dxkulture/dxkulture.go
@@ -0,0 +1,170 @@
+package dxkulture
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "net/url"
+
+ "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"
+
+ "github.com/prebid/openrtb/v19/openrtb2"
+)
+
+var markupTypeToBidType = map[openrtb2.MarkupType]openrtb_ext.BidType{
+ openrtb2.MarkupBanner: openrtb_ext.BidTypeBanner,
+ openrtb2.MarkupVideo: openrtb_ext.BidTypeVideo,
+}
+
+type adapter struct {
+ endpoint string
+}
+
+// Builder builds a new instance of the DXKulture adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
+ bidder := &adapter{
+ endpoint: config.Endpoint,
+ }
+ return bidder, nil
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+ impressions := request.Imp
+
+ adapterRequests := make([]*adapters.RequestData, 0, len(impressions))
+ var errs []error
+
+ for _, impression := range impressions {
+ impExt, err := parseExt(&impression)
+ if err != nil {
+ errs = append(errs, err)
+ continue
+ }
+
+ request.Imp = []openrtb2.Imp{impression}
+ body, err := json.Marshal(request)
+ if err != nil {
+ errs = append(errs, err)
+ continue
+ }
+
+ if request.Test == 1 {
+ impExt.PublisherId = "test"
+ }
+
+ params := url.Values{}
+ params.Add("publisher_id", impExt.PublisherId)
+ params.Add("placement_id", impExt.PlacementId)
+
+ adapterRequests = append(adapterRequests, &adapters.RequestData{
+ Method: http.MethodPost,
+ Uri: a.endpoint + "?" + params.Encode(),
+ Body: body,
+ Headers: getHeaders(request),
+ })
+ }
+
+ request.Imp = impressions
+ return adapterRequests, errs
+}
+
+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{err}
+ }
+
+ var ortbResponse openrtb2.BidResponse
+ err := json.Unmarshal(response.Body, &ortbResponse)
+ if err != nil {
+ return nil, []error{&errortypes.BadServerResponse{
+ Message: "Bad Server Response",
+ }}
+ }
+
+ var bidErrors []error
+
+ bidderResponse := adapters.NewBidderResponseWithBidsCapacity(1)
+ for _, seatBid := range ortbResponse.SeatBid {
+ for i := range seatBid.Bid {
+ bid := seatBid.Bid[i]
+ bidType, err := getBidType(&bid)
+ if err != nil {
+ bidErrors = append(bidErrors, err)
+ continue
+ }
+
+ bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
+ Bid: &bid,
+ BidType: bidType,
+ })
+ }
+ }
+
+ return bidderResponse, bidErrors
+}
+
+func getBidType(bid *openrtb2.Bid) (openrtb_ext.BidType, error) {
+ if bidType, ok := markupTypeToBidType[bid.MType]; ok {
+ return bidType, nil
+ }
+ return "", &errortypes.BadServerResponse{
+ Message: fmt.Sprintf("Unsupported MType %d", bid.MType),
+ }
+}
+
+func parseExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpDXKulture, error) {
+ var bidderExt adapters.ExtImpBidder
+
+ if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+ return nil, &errortypes.BadInput{
+ Message: fmt.Sprintf("Ignoring imp id=%s, error while decoding extImpBidder, err: %s", imp.ID, err),
+ }
+ }
+
+ impExt := openrtb_ext.ExtImpDXKulture{}
+ err := json.Unmarshal(bidderExt.Bidder, &impExt)
+ if err != nil {
+ return nil, &errortypes.BadInput{
+ Message: fmt.Sprintf("Ignoring imp id=%s, error while decoding impExt, err: %s", imp.ID, err),
+ }
+ }
+
+ return &impExt, nil
+}
+
+func getHeaders(request *openrtb2.BidRequest) http.Header {
+ headers := http.Header{}
+ headers.Add("Content-Type", "application/json;charset=utf-8")
+ headers.Add("Accept", "application/json")
+ headers.Add("X-Openrtb-Version", "2.5")
+
+ if request.Site != nil {
+ if request.Site.Ref != "" {
+ headers.Set("Referer", request.Site.Ref)
+ }
+ if request.Site.Domain != "" {
+ headers.Add("Origin", request.Site.Domain)
+ }
+ }
+
+ if request.Device != nil {
+ if len(request.Device.UA) > 0 {
+ headers.Add("User-Agent", request.Device.UA)
+ }
+
+ if len(request.Device.IPv6) > 0 {
+ headers.Add("X-Forwarded-For", request.Device.IPv6)
+ }
+
+ if len(request.Device.IP) > 0 {
+ headers.Add("X-Forwarded-For", request.Device.IP)
+ }
+ }
+ return headers
+}
diff --git a/adapters/dxkulture/dxkulture_test.go b/adapters/dxkulture/dxkulture_test.go
new file mode 100644
index 00000000000..9b2da4f27ab
--- /dev/null
+++ b/adapters/dxkulture/dxkulture_test.go
@@ -0,0 +1,17 @@
+package dxkulture
+
+import (
+ "testing"
+
+ "github.com/prebid/prebid-server/config"
+
+ "github.com/prebid/prebid-server/adapters/adapterstest"
+)
+
+func TestJsonSamples(t *testing.T) {
+ bidder, buildErr := Builder("dxkulture", config.Adapter{Endpoint: "https://ads.kulture.media/pbs"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+ if buildErr != nil {
+ t.Fatalf("Builder returned unexpected error %v", buildErr)
+ }
+ adapterstest.RunJSONBidderTest(t, "dxkulturetest", bidder)
+}
diff --git a/adapters/dxkulture/dxkulturetest/exemplary/banner.json b/adapters/dxkulture/dxkulturetest/exemplary/banner.json
new file mode 100644
index 00000000000..3b84881fc67
--- /dev/null
+++ b/adapters/dxkulture/dxkulturetest/exemplary/banner.json
@@ -0,0 +1,144 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "w": 300,
+ "h": 250
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "method": "GET",
+ "headers": {
+ "Referer": [
+ "http://site.com/ref"
+ ],
+ "Origin": [
+ "site.com"
+ ],
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ],
+ "User-Agent": [
+ "user-agent"
+ ],
+ "X-Forwarded-For": [
+ "1.2.3.4"
+ ],
+ "X-Openrtb-Version": [
+ "2.5"
+ ]
+ },
+ "uri": "https://ads.kulture.media/pbs?placement_id=placement123&publisher_id=pub123",
+ "body": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "w": 300,
+ "h": 250
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "cur": "USD",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "43271b2d-41c0-4093-8ba1-2105d9658e80",
+ "crid": "16329",
+ "adomain": [
+ "adomain.com"
+ ],
+ "price": 3,
+ "impid": "test-imp-id",
+ "adid": "2422",
+ "adm": "",
+ "mtype": 1
+ }
+ ],
+ "seat": "dxkulture"
+ }
+ ],
+ "bidid": "test-request-id",
+ "id": "test-request-id"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "43271b2d-41c0-4093-8ba1-2105d9658e80",
+ "crid": "16329",
+ "adomain": [
+ "adomain.com"
+ ],
+ "price": 3,
+ "impid": "test-imp-id",
+ "adid": "2422",
+ "adm": "",
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/dxkulture/dxkulturetest/exemplary/empty-site-domain-ref.json b/adapters/dxkulture/dxkulturetest/exemplary/empty-site-domain-ref.json
new file mode 100644
index 00000000000..630467ce96b
--- /dev/null
+++ b/adapters/dxkulture/dxkulturetest/exemplary/empty-site-domain-ref.json
@@ -0,0 +1,142 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "page": "http://site.com/page"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "method": "GET",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ],
+ "User-Agent": [
+ "user-agent"
+ ],
+ "X-Forwarded-For": [
+ "1.2.3.4"
+ ],
+ "X-Openrtb-Version": [
+ "2.5"
+ ]
+ },
+ "uri": "https://ads.kulture.media/pbs?placement_id=placement123&publisher_id=pub123",
+ "body": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "page": "http://site.com/page"
+ }
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "cur": "USD",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "43271b2d-41c0-4093-8ba1-2105d9658e80",
+ "crid": "16329",
+ "adomain": [
+ "adomain.com"
+ ],
+ "price": 3,
+ "impid": "test-imp-id",
+ "adid": "2422",
+ "adm": "",
+ "mtype": 2
+ }
+ ],
+ "seat": "dxkulture"
+ }
+ ],
+ "bidid": "test-request-id",
+ "id": "test-request-id"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "43271b2d-41c0-4093-8ba1-2105d9658e80",
+ "crid": "16329",
+ "adomain": [
+ "adomain.com"
+ ],
+ "price": 3,
+ "impid": "test-imp-id",
+ "adid": "2422",
+ "adm": "",
+ "mtype": 2
+ },
+ "type": "video"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/dxkulture/dxkulturetest/exemplary/ipv6.json b/adapters/dxkulture/dxkulturetest/exemplary/ipv6.json
new file mode 100644
index 00000000000..58f7e7fdf2c
--- /dev/null
+++ b/adapters/dxkulture/dxkulturetest/exemplary/ipv6.json
@@ -0,0 +1,142 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ipv6": "2001:0000:130F:0000:0000:09C0:876A:130B"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "page": "http://site.com/page"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "method": "GET",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ],
+ "User-Agent": [
+ "user-agent"
+ ],
+ "X-Forwarded-For": [
+ "2001:0000:130F:0000:0000:09C0:876A:130B"
+ ],
+ "X-Openrtb-Version": [
+ "2.5"
+ ]
+ },
+ "uri": "https://ads.kulture.media/pbs?placement_id=placement123&publisher_id=pub123",
+ "body": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ipv6": "2001:0000:130F:0000:0000:09C0:876A:130B"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "page": "http://site.com/page"
+ }
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "cur": "USD",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "43271b2d-41c0-4093-8ba1-2105d9658e80",
+ "crid": "16329",
+ "adomain": [
+ "adomain.com"
+ ],
+ "price": 3,
+ "impid": "test-imp-id",
+ "adid": "2422",
+ "adm": "",
+ "mtype": 2
+ }
+ ],
+ "seat": "dxkulture"
+ }
+ ],
+ "bidid": "test-request-id",
+ "id": "test-request-id"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "43271b2d-41c0-4093-8ba1-2105d9658e80",
+ "crid": "16329",
+ "adomain": [
+ "adomain.com"
+ ],
+ "price": 3,
+ "impid": "test-imp-id",
+ "adid": "2422",
+ "adm": "",
+ "mtype": 2
+ },
+ "type": "video"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/dxkulture/dxkulturetest/exemplary/video-test-request.json b/adapters/dxkulture/dxkulturetest/exemplary/video-test-request.json
new file mode 100644
index 00000000000..6a0746ad258
--- /dev/null
+++ b/adapters/dxkulture/dxkulturetest/exemplary/video-test-request.json
@@ -0,0 +1,154 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "test": 1,
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "method": "GET",
+ "headers": {
+ "Referer": [
+ "http://site.com/ref"
+ ],
+ "Origin": [
+ "site.com"
+ ],
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ],
+ "User-Agent": [
+ "user-agent"
+ ],
+ "X-Forwarded-For": [
+ "1.2.3.4"
+ ],
+ "X-Openrtb-Version": [
+ "2.5"
+ ]
+ },
+ "uri": "https://ads.kulture.media/pbs?placement_id=placement123&publisher_id=test",
+ "body": {
+ "id": "test-request-id",
+ "test": 1,
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "cur": "USD",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "43271b2d-41c0-4093-8ba1-2105d9658e80",
+ "crid": "16329",
+ "adomain": [
+ "adomain.com"
+ ],
+ "price": 3,
+ "impid": "test-imp-id",
+ "adid": "2422",
+ "adm": "",
+ "mtype": 2
+ }
+ ],
+ "seat": "dxkulture"
+ }
+ ],
+ "bidid": "test-request-id",
+ "id": "test-request-id"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "43271b2d-41c0-4093-8ba1-2105d9658e80",
+ "crid": "16329",
+ "adomain": [
+ "adomain.com"
+ ],
+ "price": 3,
+ "impid": "test-imp-id",
+ "adid": "2422",
+ "adm": "",
+ "mtype": 2
+ },
+ "type": "video"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/dxkulture/dxkulturetest/exemplary/video.json b/adapters/dxkulture/dxkulturetest/exemplary/video.json
new file mode 100644
index 00000000000..017dd5ea2bc
--- /dev/null
+++ b/adapters/dxkulture/dxkulturetest/exemplary/video.json
@@ -0,0 +1,152 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "method": "GET",
+ "headers": {
+ "Referer": [
+ "http://site.com/ref"
+ ],
+ "Origin": [
+ "site.com"
+ ],
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ],
+ "User-Agent": [
+ "user-agent"
+ ],
+ "X-Forwarded-For": [
+ "1.2.3.4"
+ ],
+ "X-Openrtb-Version": [
+ "2.5"
+ ]
+ },
+ "uri": "https://ads.kulture.media/pbs?placement_id=placement123&publisher_id=pub123",
+ "body": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "cur": "USD",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "43271b2d-41c0-4093-8ba1-2105d9658e80",
+ "crid": "16329",
+ "adomain": [
+ "adomain.com"
+ ],
+ "price": 3,
+ "impid": "test-imp-id",
+ "adid": "2422",
+ "adm": "",
+ "mtype": 2
+ }
+ ],
+ "seat": "dxkulture"
+ }
+ ],
+ "bidid": "test-request-id",
+ "id": "test-request-id"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "43271b2d-41c0-4093-8ba1-2105d9658e80",
+ "crid": "16329",
+ "adomain": [
+ "adomain.com"
+ ],
+ "price": 3,
+ "impid": "test-imp-id",
+ "adid": "2422",
+ "adm": "",
+ "mtype": 2
+ },
+ "type": "video"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/dxkulture/dxkulturetest/supplemental/invalid-imp-ext-bidder.json b/adapters/dxkulture/dxkulturetest/supplemental/invalid-imp-ext-bidder.json
new file mode 100644
index 00000000000..ae30b327030
--- /dev/null
+++ b/adapters/dxkulture/dxkulturetest/supplemental/invalid-imp-ext-bidder.json
@@ -0,0 +1,40 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": "not_json"
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ },
+ "expectedMakeRequestsErrors": [
+ {
+ "value": "Ignoring imp id=test-imp-id, error while decoding impExt, err: json: cannot unmarshal string into Go value of type openrtb_ext.ExtImpDXKulture",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/dxkulture/dxkulturetest/supplemental/invalid-imp-ext.json b/adapters/dxkulture/dxkulturetest/supplemental/invalid-imp-ext.json
new file mode 100644
index 00000000000..2587dc216d2
--- /dev/null
+++ b/adapters/dxkulture/dxkulturetest/supplemental/invalid-imp-ext.json
@@ -0,0 +1,38 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": "not_json"
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ },
+ "expectedMakeRequestsErrors": [
+ {
+ "value": "Ignoring imp id=test-imp-id, error while decoding extImpBidder, err: json: cannot unmarshal string into Go value of type adapters.ExtImpBidder",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/dxkulture/dxkulturetest/supplemental/invalid-response.json b/adapters/dxkulture/dxkulturetest/supplemental/invalid-response.json
new file mode 100644
index 00000000000..8fff1bb0375
--- /dev/null
+++ b/adapters/dxkulture/dxkulturetest/supplemental/invalid-response.json
@@ -0,0 +1,113 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "method": "GET",
+ "headers": {
+ "Referer": [
+ "http://site.com/ref"
+ ],
+ "Origin": [
+ "site.com"
+ ],
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ],
+ "User-Agent": [
+ "user-agent"
+ ],
+ "X-Forwarded-For": [
+ "1.2.3.4"
+ ],
+ "X-Openrtb-Version": [
+ "2.5"
+ ]
+ },
+ "uri": "https://ads.kulture.media/pbs?placement_id=placement123&publisher_id=pub123",
+ "body": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": "body"
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Bad Server Response",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/dxkulture/dxkulturetest/supplemental/no-mtype.json b/adapters/dxkulture/dxkulturetest/supplemental/no-mtype.json
new file mode 100644
index 00000000000..a56200fad50
--- /dev/null
+++ b/adapters/dxkulture/dxkulturetest/supplemental/no-mtype.json
@@ -0,0 +1,142 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "method": "GET",
+ "headers": {
+ "Referer": [
+ "http://site.com/ref"
+ ],
+ "Origin": [
+ "site.com"
+ ],
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ],
+ "User-Agent": [
+ "user-agent"
+ ],
+ "X-Forwarded-For": [
+ "1.2.3.4"
+ ],
+ "X-Openrtb-Version": [
+ "2.5"
+ ]
+ },
+ "uri": "https://ads.kulture.media/pbs?placement_id=placement123&publisher_id=pub123",
+ "body": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "cur": "USD",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "43271b2d-41c0-4093-8ba1-2105d9658e80",
+ "crid": "16329",
+ "adomain": [
+ "adomain.com"
+ ],
+ "price": 3,
+ "impid": "test-imp-id",
+ "adid": "2422",
+ "adm": ""
+ }
+ ],
+ "seat": "dxkulture"
+ }
+ ],
+ "bidid": "test-request-id",
+ "id": "test-request-id"
+ }
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unsupported MType 0",
+ "comparison": "literal"
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": []
+ }
+ ]
+
+}
diff --git a/adapters/dxkulture/dxkulturetest/supplemental/status-code-bad-request.json b/adapters/dxkulture/dxkulturetest/supplemental/status-code-bad-request.json
new file mode 100644
index 00000000000..f0f2ce2c47b
--- /dev/null
+++ b/adapters/dxkulture/dxkulturetest/supplemental/status-code-bad-request.json
@@ -0,0 +1,112 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "method": "GET",
+ "headers": {
+ "Referer": [
+ "http://site.com/ref"
+ ],
+ "Origin": [
+ "site.com"
+ ],
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ],
+ "User-Agent": [
+ "user-agent"
+ ],
+ "X-Forwarded-For": [
+ "1.2.3.4"
+ ],
+ "X-Openrtb-Version": [
+ "2.5"
+ ]
+ },
+ "uri": "https://ads.kulture.media/pbs?placement_id=placement123&publisher_id=pub123",
+ "body": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ }
+ },
+ "mockResponse": {
+ "status": 400
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/dxkulture/dxkulturetest/supplemental/status-code-no-content.json b/adapters/dxkulture/dxkulturetest/supplemental/status-code-no-content.json
new file mode 100644
index 00000000000..43c53f3d64c
--- /dev/null
+++ b/adapters/dxkulture/dxkulturetest/supplemental/status-code-no-content.json
@@ -0,0 +1,108 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "method": "GET",
+ "headers": {
+ "Referer": [
+ "http://site.com/ref"
+ ],
+ "Origin": [
+ "site.com"
+ ],
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ],
+ "User-Agent": [
+ "user-agent"
+ ],
+ "X-Forwarded-For": [
+ "1.2.3.4"
+ ],
+ "X-Openrtb-Version": [
+ "2.5"
+ ]
+ },
+ "uri": "https://ads.kulture.media/pbs?placement_id=placement123&publisher_id=pub123",
+ "body": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ }
+ },
+ "mockResponse": {
+ "status": 204
+ }
+ }
+ ],
+ "expectedBidResponses": [],
+ "expectedMakeBidsErrors": []
+}
diff --git a/adapters/dxkulture/dxkulturetest/supplemental/status-code-other-error.json b/adapters/dxkulture/dxkulturetest/supplemental/status-code-other-error.json
new file mode 100644
index 00000000000..3e1b0b33c1e
--- /dev/null
+++ b/adapters/dxkulture/dxkulturetest/supplemental/status-code-other-error.json
@@ -0,0 +1,112 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "method": "GET",
+ "headers": {
+ "Referer": [
+ "http://site.com/ref"
+ ],
+ "Origin": [
+ "site.com"
+ ],
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ],
+ "User-Agent": [
+ "user-agent"
+ ],
+ "X-Forwarded-For": [
+ "1.2.3.4"
+ ],
+ "X-Openrtb-Version": [
+ "2.5"
+ ]
+ },
+ "uri": "https://ads.kulture.media/pbs?placement_id=placement123&publisher_id=pub123",
+ "body": {
+ "id": "test-request-id",
+ "user": {
+ "buyeruid": "userId",
+ "yob": 1990
+ },
+ "device": {
+ "ua": "user-agent",
+ "ip": "1.2.3.4"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "w": 1920,
+ "h": 1080,
+ "mimes": [
+ "video/x-flv",
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "pub123",
+ "placementId": "placement123"
+ }
+ }
+ }
+ ],
+ "site": {
+ "domain": "site.com",
+ "page": "http://site.com/page",
+ "ref": "http://site.com/ref"
+ }
+ }
+ },
+ "mockResponse": {
+ "status": 505
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unexpected status code: 505. Run with request.debug = 1 for more info",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/dxkulture/params_test.go b/adapters/dxkulture/params_test.go
new file mode 100644
index 00000000000..838d092df8f
--- /dev/null
+++ b/adapters/dxkulture/params_test.go
@@ -0,0 +1,53 @@
+package dxkulture
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// This file actually intends to test static/bidder-params/dxkulture.json
+//
+// These also validate the format of the external API: request.imp[i].ext.prebid.bidder.dxkulture
+
+// TestValidParams makes sure that the dxkulture schema accepts all imp.ext fields which we intend to support.
+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.BidderDXKulture, json.RawMessage(validParam)); err != nil {
+ t.Errorf("Schema rejected dxkulture params: %s", validParam)
+ }
+ }
+}
+
+// TestInvalidParams makes sure that the dxkulture 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.BidderDXKulture, json.RawMessage(invalidParam)); err == nil {
+ t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+ }
+ }
+}
+
+var validParams = []string{
+ `{"publisherId": "pub", "placementId": "plac"}`,
+ `{"publisherId": "pub", "placementId": "plac", "a":1}`,
+}
+
+var invalidParams = []string{
+ `{"publisherId": "pub"}`,
+ `{"placementId": "plac"}`,
+ //malformed
+ `{"ub", "placementId": "plac"}`,
+ `{}`,
+}
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 395e4fbbc39..0fb84cc5898 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -75,6 +75,7 @@ import (
"github.com/prebid/prebid-server/adapters/definemedia"
"github.com/prebid/prebid-server/adapters/dianomi"
"github.com/prebid/prebid-server/adapters/dmx"
+ "github.com/prebid/prebid-server/adapters/dxkulture"
evolution "github.com/prebid/prebid-server/adapters/e_volution"
"github.com/prebid/prebid-server/adapters/emtv"
"github.com/prebid/prebid-server/adapters/engagebdr"
@@ -273,6 +274,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
openrtb_ext.BidderDefinemedia: definemedia.Builder,
openrtb_ext.BidderDianomi: dianomi.Builder,
openrtb_ext.BidderDmx: dmx.Builder,
+ openrtb_ext.BidderDXKulture: dxkulture.Builder,
openrtb_ext.BidderEmtv: emtv.Builder,
openrtb_ext.BidderEmxDigital: cadentaperturemx.Builder,
openrtb_ext.BidderEngageBDR: engagebdr.Builder,
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index e12316f7e72..33e0204ecf5 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -95,6 +95,7 @@ var coreBidderNames []BidderName = []BidderName{
BidderDefinemedia,
BidderDianomi,
BidderDmx,
+ BidderDXKulture,
BidderEmtv,
BidderEmxDigital,
BidderEngageBDR,
@@ -390,6 +391,7 @@ const (
BidderDefinemedia BidderName = "definemedia"
BidderDianomi BidderName = "dianomi"
BidderDmx BidderName = "dmx"
+ BidderDXKulture BidderName = "dxkulture"
BidderEmtv BidderName = "emtv"
BidderEmxDigital BidderName = "emx_digital"
BidderEngageBDR BidderName = "engagebdr"
diff --git a/openrtb_ext/imp_dxkulture.go b/openrtb_ext/imp_dxkulture.go
new file mode 100644
index 00000000000..4b507c55248
--- /dev/null
+++ b/openrtb_ext/imp_dxkulture.go
@@ -0,0 +1,7 @@
+package openrtb_ext
+
+// ExtImpDXKulture defines the contract for bidrequest.imp[i].ext.prebid.bidder.dxkulture
+type ExtImpDXKulture struct {
+ PublisherId string `json:"publisherId"`
+ PlacementId string `json:"placementId"`
+}
diff --git a/static/bidder-info/dxkulture.yaml b/static/bidder-info/dxkulture.yaml
new file mode 100644
index 00000000000..1af72e9fc33
--- /dev/null
+++ b/static/bidder-info/dxkulture.yaml
@@ -0,0 +1,16 @@
+endpoint: "https://ads.kulture.media/pbs"
+maintainer:
+ email: "devops@kulture.media"
+capabilities:
+ app:
+ mediaTypes:
+ - banner
+ - video
+ site:
+ mediaTypes:
+ - banner
+ - video
+userSync:
+ redirect:
+ url: "https://ads.kulture.media/usync?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&cb={{.RedirectURL}}"
+ userMacro: "$UID"
diff --git a/static/bidder-params/dxkulture.json b/static/bidder-params/dxkulture.json
new file mode 100644
index 00000000000..858eefd22e4
--- /dev/null
+++ b/static/bidder-params/dxkulture.json
@@ -0,0 +1,20 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "DXKulture Adapter Params",
+ "description": "A schema which validates params accepted by the DXKulture adapter",
+ "type": "object",
+ "properties": {
+ "publisherId": {
+ "type": "string",
+ "description": "The publisher id"
+ },
+ "placementId": {
+ "type": "string",
+ "description": "The placement id"
+ }
+ },
+ "required": [
+ "publisherId",
+ "placementId"
+ ]
+}