diff --git a/adapters/nanointeractive/nanointeractive.go b/adapters/nanointeractive/nanointeractive.go
new file mode 100644
index 00000000000..72832893af1
--- /dev/null
+++ b/adapters/nanointeractive/nanointeractive.go
@@ -0,0 +1,172 @@
+package nanointeractive
+
+import (
+ "encoding/json"
+ "fmt"
+ "github.com/mxmCherry/openrtb"
+ "github.com/prebid/prebid-server/adapters"
+ "github.com/prebid/prebid-server/errortypes"
+ "github.com/prebid/prebid-server/openrtb_ext"
+ "net/http"
+)
+
+type NanoInteractiveAdapter struct {
+ endpoint string
+}
+
+func (a *NanoInteractiveAdapter) Name() string {
+ return "Nano"
+}
+
+func (a *NanoInteractiveAdapter) SkipNoCookies() bool {
+ return false
+}
+
+func (a *NanoInteractiveAdapter) MakeRequests(bidRequest *openrtb.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+
+ var errs []error
+ var validImps []openrtb.Imp
+
+ var adapterRequests []*adapters.RequestData
+ var referer string = ""
+
+ for i := 0; i < len(bidRequest.Imp); i++ {
+
+ ref, err := checkImp(&bidRequest.Imp[i])
+
+ // If the parsing is failed, remove imp and add the error.
+ if err != nil {
+ errs = append(errs, err)
+ continue
+ }
+ if referer == "" && ref != "" {
+ referer = ref
+ }
+ validImps = append(validImps, bidRequest.Imp[i])
+ }
+
+ if len(validImps) == 0 {
+ errs = append(errs, fmt.Errorf("no impressions in the bid request"))
+ return nil, errs
+ }
+
+ // set referer origin
+ if referer != "" {
+ if bidRequest.Site == nil {
+ bidRequest.Site = &openrtb.Site{}
+ }
+ bidRequest.Site.Ref = referer
+ }
+
+ bidRequest.Imp = validImps
+
+ reqJSON, err := json.Marshal(bidRequest)
+ if err != nil {
+ errs = append(errs, err)
+ return nil, errs
+ }
+
+ 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 bidRequest.Device != nil {
+ headers.Add("User-Agent", bidRequest.Device.UA)
+ headers.Add("X-Forwarded-For", bidRequest.Device.IP)
+ }
+ if bidRequest.Site != nil {
+ headers.Add("Referer", bidRequest.Site.Page)
+ }
+
+ // set user's cookie
+ if bidRequest.User != nil && bidRequest.User.BuyerUID != "" {
+ headers.Add("Cookie", "Nano="+bidRequest.User.BuyerUID)
+ }
+
+ adapterRequests = append(adapterRequests, &adapters.RequestData{
+ Method: "POST",
+ Uri: a.endpoint,
+ Body: reqJSON,
+ Headers: headers,
+ })
+
+ return adapterRequests, errs
+}
+
+func (a *NanoInteractiveAdapter) MakeBids(
+ internalRequest *openrtb.BidRequest,
+ externalRequest *adapters.RequestData,
+ response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+
+ if response.StatusCode == http.StatusNoContent {
+ return nil, nil
+ } else if response.StatusCode == http.StatusBadRequest {
+ return nil, []error{adapters.BadInput("Invalid request.")}
+ } else if response.StatusCode != http.StatusOK {
+ return nil, []error{&errortypes.BadServerResponse{
+ Message: fmt.Sprintf("unexpected HTTP status %d.", response.StatusCode),
+ }}
+ }
+
+ var openRtbBidResponse openrtb.BidResponse
+
+ if err := json.Unmarshal(response.Body, &openRtbBidResponse); err != nil {
+ return nil, []error{&errortypes.BadServerResponse{
+ Message: fmt.Sprintf("bad server body response"),
+ }}
+ }
+
+ bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(openRtbBidResponse.SeatBid[0].Bid))
+ bidResponse.Currency = openRtbBidResponse.Cur
+
+ sb := openRtbBidResponse.SeatBid[0]
+ for i := 0; i < len(sb.Bid); i++ {
+ if !(sb.Bid[i].Price > 0) {
+ continue
+ }
+ bid := sb.Bid[i]
+ bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+ Bid: &bid,
+ BidType: openrtb_ext.BidTypeBanner,
+ })
+ }
+ return bidResponse, nil
+}
+
+func checkImp(imp *openrtb.Imp) (string, error) {
+ // We support only banner impression
+ if imp.Banner == nil {
+ return "", fmt.Errorf("invalid MediaType. NanoInteractive only supports Banner type. ImpID=%s", imp.ID)
+ }
+
+ var bidderExt adapters.ExtImpBidder
+ if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
+ return "", fmt.Errorf("ext not provided; ImpID=%s", imp.ID)
+ }
+
+ var nanoExt openrtb_ext.ExtImpNanoInteractive
+ if err := json.Unmarshal(bidderExt.Bidder, &nanoExt); err != nil {
+ return "", fmt.Errorf("ext.bidder not provided; ImpID=%s", imp.ID)
+ }
+ if nanoExt.Pid == "" {
+ return "", fmt.Errorf("pid is empty; ImpID=%s", imp.ID)
+ }
+
+ if nanoExt.Ref != "" {
+ return string(nanoExt.Ref), nil
+ }
+
+ return "", nil
+}
+
+func NewNanoIneractiveBidder(endpoint string) *NanoInteractiveAdapter {
+ return &NanoInteractiveAdapter{
+ endpoint: endpoint,
+ }
+}
+
+func NewNanoInteractiveAdapter(uri string) *NanoInteractiveAdapter {
+ return &NanoInteractiveAdapter{
+ endpoint: uri,
+ }
+}
diff --git a/adapters/nanointeractive/nanointeractive_test.go b/adapters/nanointeractive/nanointeractive_test.go
new file mode 100644
index 00000000000..fa7069a5da3
--- /dev/null
+++ b/adapters/nanointeractive/nanointeractive_test.go
@@ -0,0 +1,10 @@
+package nanointeractive
+
+import (
+ "github.com/prebid/prebid-server/adapters/adapterstest"
+ "testing"
+)
+
+func TestJsonSamples(t *testing.T) {
+ adapterstest.RunJSONBidderTest(t, "nanointeractivetest", NewNanoIneractiveBidder("https://ad.audiencemanager.de/hbs"))
+}
diff --git a/adapters/nanointeractive/nanointeractivetest/exemplary/simple-banner.json b/adapters/nanointeractive/nanointeractivetest/exemplary/simple-banner.json
new file mode 100644
index 00000000000..20cc70b6785
--- /dev/null
+++ b/adapters/nanointeractive/nanointeractivetest/exemplary/simple-banner.json
@@ -0,0 +1,90 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "58bfec94eb0a1916fa380163"
+ }
+ }
+ }
+ ]
+ },
+
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://ad.audiencemanager.de/hbs",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{ "w": 300,"h": 250}
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "58bfec94eb0a1916fa380163"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "seat": "nanointeractive",
+ "bid": [{
+ "id": "1",
+ "impid": "test-imp-id",
+ "price": 0.4580126,
+ "adm": "",
+ "adid": "test_ad_id",
+ "adomain": ["audiencemanager.de"],
+ "cid": "test_cid",
+ "crid": "test_banner_crid",
+ "h": 250,
+ "w": 300
+ }]
+ }
+ ],
+ "bidid": "5a7789eg2662b524d8d7264a96",
+ "cur": "EUR"
+ }
+ }
+ }
+ ],
+
+ "expectedBids": [
+ {
+ "bid": {
+ "id": "1",
+ "impid": "test-imp-id",
+ "price": 0.4580126,
+ "adm": "",
+ "adid": "test_ad_id",
+ "adomain": ["yahoo.com"],
+ "cid": "test_cid",
+ "crid": "test_banner_crid",
+ "h": 250,
+ "w": 300
+ },
+ "ext": {
+ "prebid": {
+ "type": "banner"
+ }
+ }
+ }
+ ]
+}
diff --git a/adapters/nanointeractive/nanointeractivetest/params/race/banner.json b/adapters/nanointeractive/nanointeractivetest/params/race/banner.json
new file mode 100644
index 00000000000..bb35ea8488a
--- /dev/null
+++ b/adapters/nanointeractive/nanointeractivetest/params/race/banner.json
@@ -0,0 +1,3 @@
+{
+ "pid": "58bfec94eb0a1916fa380163"
+}
diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/bad_response.json b/adapters/nanointeractive/nanointeractivetest/supplemental/bad_response.json
new file mode 100644
index 00000000000..587c952a042
--- /dev/null
+++ b/adapters/nanointeractive/nanointeractivetest/supplemental/bad_response.json
@@ -0,0 +1,63 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "213"
+ }
+ }
+ }
+ ]
+ },
+
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://ad.audiencemanager.de/hbs",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "213"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": "{\"id\"data.lost"
+ }
+ }
+ ],
+
+ "expectedMakeBidsErrors": [
+ {
+ "value": "bad server body response",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/invalid-params.json b/adapters/nanointeractive/nanointeractivetest/supplemental/invalid-params.json
new file mode 100644
index 00000000000..631dc99e5a8
--- /dev/null
+++ b/adapters/nanointeractive/nanointeractivetest/supplemental/invalid-params.json
@@ -0,0 +1,81 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id-1",
+ "banner": {},
+ "ext": {
+ "bidder": {}
+ }
+ },
+ {
+ "id": "test-imp-id-2",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+
+ }
+ },
+ {
+ "id": "test-imp-id-3",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ }
+ },
+ {
+ "id": "test-imp-id-4",
+ "video": {},
+ "ext": {
+ "bidder": {}
+ }
+ },
+ {
+ "id": "test-imp-id-5",
+ "audio": {
+ "startdelay": 0,
+ "api": []
+ },
+ "ext": {
+ "bidder": {}
+ }
+ }
+ ],
+ "site": {
+ "id": "siteID",
+ "publisher": {
+ "id": "1234"
+ }
+ },
+ "device": {
+ "os": "android"
+ }
+ },
+ "expectedMakeRequestsErrors": [
+ {
+ "value": "pid is empty; ImpID=test-imp-id-1",
+ "comparison": "literal"
+ },
+ {
+ "value": "ext.bidder not provided; ImpID=test-imp-id-2",
+ "comparison": "literal"
+ },
+ {
+ "value": "ext not provided; ImpID=test-imp-id-3",
+ "comparison": "literal"
+ },
+ {
+ "value": "invalid MediaType. NanoInteractive only supports Banner type. ImpID=test-imp-id-4",
+ "comparison": "literal"
+ },
+ {
+ "value": "invalid MediaType. NanoInteractive only supports Banner type. ImpID=test-imp-id-5",
+ "comparison": "literal"
+ },
+ {
+ "value": "no impressions in the bid request",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/multi-param.json b/adapters/nanointeractive/nanointeractivetest/supplemental/multi-param.json
new file mode 100644
index 00000000000..27e7bec1f5f
--- /dev/null
+++ b/adapters/nanointeractive/nanointeractivetest/supplemental/multi-param.json
@@ -0,0 +1,151 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "58bfec94eb0a1916fa380163",
+ "ref": "https://nanointeractive.com"
+ }
+ }
+ },
+ {
+ "id": "test-imp-id2",
+ "banner": {
+ "format": [{"w": 300, "h": 250}]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "58bfec94eb0a1916fa380163",
+ "nq": ["search query"],
+ "category": "Automotive",
+ "subId": "a23",
+ "ref": "https://nanointeractive.com"
+ }
+ }
+ }
+ ],
+ "device": {
+ "ip": "127.0.0.1",
+ "ua": "user_agent"
+ },
+ "user": {
+ "buyeruid": "userId"
+ }
+ },
+
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://ad.audiencemanager.de/hbs",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [{ "w": 300,"h": 250}
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "58bfec94eb0a1916fa380163",
+ "ref": "https://nanointeractive.com"
+ }
+ }
+ },
+ {
+ "id": "test-imp-id2",
+ "banner": {
+ "format": [{ "w": 300,"h": 250}
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "58bfec94eb0a1916fa380163",
+ "nq": ["search query"],
+ "category": "Automotive",
+ "subId": "a23",
+ "ref": "https://nanointeractive.com"
+ }
+ }
+ }
+ ],
+ "site": {
+ "ref": "https://nanointeractive.com"
+ },
+ "device": {
+ "ip": "127.0.0.1",
+ "ua": "user_agent"
+ },
+ "user": {
+ "buyeruid": "userId"
+ }
+ }
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "seat": "nanointeractive",
+ "bid": [{
+ "id": "1",
+ "impid": "test-imp-id",
+ "price": 0.4580126,
+ "adm": "",
+ "adid": "test_ad_id",
+ "adomain": ["audiencemanager.de"],
+ "cid": "test_cid",
+ "crid": "test_banner_crid",
+ "h": 250,
+ "w": 300
+ },{
+ "id": "2",
+ "impid": "test-imp-id2",
+ "price": 0,
+ "adm": "",
+ "adid": "test_ad_id",
+ "adomain": ["audiencemanager.de"],
+ "cid": "test_cid",
+ "crid": "test_banner_crid",
+ "h": 250,
+ "w": 300
+ }]
+ }
+ ],
+ "bidid": "5a7789eg2662b524d8d7264a96",
+ "cur": "EUR"
+ }
+ }
+ }
+ ],
+
+ "expectedBids": [
+ {
+ "bid": {
+ "id": "1",
+ "impid": "test-imp-id",
+ "price": 0.4580126,
+ "adm": "",
+ "adid": "test_ad_id",
+ "adomain": ["yahoo.com"],
+ "cid": "test_cid",
+ "crid": "test_banner_crid",
+ "h": 250,
+ "w": 300
+ },
+ "ext": {
+ "prebid": {
+ "type": "banner"
+ }
+ }
+ }
+ ]
+}
diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/status_204.json b/adapters/nanointeractive/nanointeractivetest/supplemental/status_204.json
new file mode 100644
index 00000000000..ed4d8ff38b8
--- /dev/null
+++ b/adapters/nanointeractive/nanointeractivetest/supplemental/status_204.json
@@ -0,0 +1,58 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "123"
+ }
+ }
+ }
+ ]
+ },
+
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://ad.audiencemanager.de/hbs",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "123"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 204,
+ "body": {}
+ }
+ }
+ ],
+
+ "expectedBidResponses": []
+}
diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/status_400.json b/adapters/nanointeractive/nanointeractivetest/supplemental/status_400.json
new file mode 100644
index 00000000000..f02bd478656
--- /dev/null
+++ b/adapters/nanointeractive/nanointeractivetest/supplemental/status_400.json
@@ -0,0 +1,63 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "123"
+ }
+ }
+ }
+ ]
+ },
+
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://ad.audiencemanager.de/hbs",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "123"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 400,
+ "body": {}
+ }
+ }
+ ],
+
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Invalid request.",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/nanointeractive/nanointeractivetest/supplemental/status_418.json b/adapters/nanointeractive/nanointeractivetest/supplemental/status_418.json
new file mode 100644
index 00000000000..b7ed65da2af
--- /dev/null
+++ b/adapters/nanointeractive/nanointeractivetest/supplemental/status_418.json
@@ -0,0 +1,63 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "123"
+ }
+ }
+ }
+ ]
+ },
+
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://ad.audiencemanager.de/hbs",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "pid": "123"
+ }
+ }
+ }
+ ]
+ }
+ },
+ "mockResponse": {
+ "status": 418,
+ "body": {}
+ }
+ }
+ ],
+
+ "expectedMakeBidsErrors": [
+ {
+ "value": "unexpected HTTP status 418.",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/nanointeractive/params_test.go b/adapters/nanointeractive/params_test.go
new file mode 100644
index 00000000000..b290f3d94b1
--- /dev/null
+++ b/adapters/nanointeractive/params_test.go
@@ -0,0 +1,63 @@
+package nanointeractive
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/prebid/prebid-server/openrtb_ext"
+)
+
+// This file actually intends to test static/bidder-params/nanointeractive.json
+//
+// These also validate the format of the external API: request.imp[i].ext.nanointeracive
+
+// TestValidParams makes sure that the NanoInteractive 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.BidderNanoInteractive, json.RawMessage(validParam)); err != nil {
+ t.Errorf("Schema rejected NanoInteractive params: %s", validParam)
+ }
+ }
+}
+
+// TestInvalidParams makes sure that the Marsmedia 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.BidderNanoInteractive, json.RawMessage(invalidParam)); err == nil {
+ t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+ }
+ }
+}
+
+var validParams = []string{
+ `{"pid": "dafad098"}`,
+ `{"pid":"dfasfda","nq":["search query"]}`,
+ `{"pid":"dfasfda","nq":["search query"],"subId":"any string value","category":"any string value"}`,
+}
+
+var invalidParams = []string{
+ `{"pid":123}`,
+ `{"pid":"12323","nq":"search query not an array"}`,
+ `{"pid":"12323","category":1}`,
+ `{"pid":"12323","subId":23}`,
+ ``,
+ `null`,
+ `true`,
+ `9`,
+ `1.2`,
+ `[]`,
+ `{}`,
+ `placementId`,
+ `zone`,
+ `zoneId`,
+}
diff --git a/adapters/nanointeractive/usersync.go b/adapters/nanointeractive/usersync.go
new file mode 100644
index 00000000000..e6227436fb2
--- /dev/null
+++ b/adapters/nanointeractive/usersync.go
@@ -0,0 +1,12 @@
+package nanointeractive
+
+import (
+ "text/template"
+
+ "github.com/prebid/prebid-server/adapters"
+ "github.com/prebid/prebid-server/usersync"
+)
+
+func NewNanoInteractiveSyncer(temp *template.Template) usersync.Usersyncer {
+ return adapters.NewSyncer("nanointeractive", 72, temp, adapters.SyncTypeRedirect)
+}
diff --git a/adapters/nanointeractive/usersync_test.go b/adapters/nanointeractive/usersync_test.go
new file mode 100644
index 00000000000..ec9787bc20d
--- /dev/null
+++ b/adapters/nanointeractive/usersync_test.go
@@ -0,0 +1,36 @@
+package nanointeractive
+
+import (
+ "github.com/prebid/prebid-server/privacy/ccpa"
+ "github.com/prebid/prebid-server/privacy/gdpr"
+ "testing"
+ "text/template"
+
+ "github.com/prebid/prebid-server/privacy"
+ "github.com/stretchr/testify/assert"
+)
+
+func TestNewNanoInteractiveSyncer(t *testing.T) {
+ syncURL := "https://ad.audiencemanager.de/hbs/cookie_sync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri=http%3A%2F%2Flocalhost%2Fsetuid%3Fbidder%3Dnanointeractive%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID"
+ syncURLTemplate := template.Must(
+ template.New("sync-template").Parse(syncURL),
+ )
+
+ userSync := NewNanoInteractiveSyncer(syncURLTemplate)
+ syncInfo, err := userSync.GetUsersyncInfo(
+ privacy.Policies{
+ GDPR: gdpr.Policy{
+ Signal: "1",
+ Consent: "BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw",
+ },
+ CCPA: ccpa.Policy{
+ Value: "1NYN",
+ },
+ })
+
+ assert.NoError(t, err)
+ assert.Equal(t, "https://ad.audiencemanager.de/hbs/cookie_sync?gdpr=1&consent=BONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw&us_privacy=1NYN&redirectUri=http%3A%2F%2Flocalhost%2Fsetuid%3Fbidder%3Dnanointeractive%26gdpr%3D1%26gdpr_consent%3DBONciguONcjGKADACHENAOLS1rAHDAFAAEAASABQAMwAeACEAFw%26uid%3D%24UID", syncInfo.URL)
+ assert.Equal(t, "redirect", syncInfo.Type)
+ assert.EqualValues(t, 72, userSync.GDPRVendorID())
+ assert.Equal(t, false, syncInfo.SupportCORS)
+}
diff --git a/config/config.go b/config/config.go
index d4edab2b53f..999b1870b54 100644
--- a/config/config.go
+++ b/config/config.go
@@ -521,6 +521,7 @@ func (cfg *Configuration) setDerivedDefaults() {
setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderLockerDome, "https://lockerdome.com/usync/prebidserver?pid="+cfg.Adapters["lockerdome"].PlatformID+"&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dlockerdome%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7B%7Buid%7D%7D")
setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMarsmedia, "https://dmp.rtbsrv.com/dmp/profiles/cm?p_id=179&gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmarsmedia%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUUID%7D")
setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderMgid, "https://cm.mgid.com/m?cdsp=363893&adu="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dmgid%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%7Bmuidn%7D")
+ setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderNanoInteractive, "https://ad.audiencemanager.de/hbs/cookie_sync?gdpr={{.GDPR}}&consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirectUri="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dnanointeractive%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24UID")
setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderOpenx, "https://rtb.openx.net/sync/prebid?r="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dopenx%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%24%7BUID%7D")
setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderPubmatic, "https://ads.pubmatic.com/AdServer/js/user_sync.html?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&predirect="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dpubmatic%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D")
setDefaultUsersync(cfg.Adapters, openrtb_ext.BidderPulsepoint, "https://bh.contextweb.com/rtset?pid=561205&ev=1&rurl="+url.QueryEscape(externalURL)+"%2Fsetuid%3Fbidder%3Dpulsepoint%26gdpr%3D{{.GDPR}}%26gdpr_consent%3D{{.GDPRConsent}}%26uid%3D%25%25VGUID%25%25")
@@ -713,6 +714,7 @@ func SetupViper(v *viper.Viper, filename string) {
v.SetDefault("adapters.lockerdome.endpoint", "https://lockerdome.com/ladbid/prebidserver/openrtb2")
v.SetDefault("adapters.marsmedia.endpoint", "https://bid306.rtbsrv.com/bidder/?bid=f3xtet")
v.SetDefault("adapters.mgid.endpoint", "https://prebid.mgid.com/prebid/")
+ v.SetDefault("adapters.nanointeractive.endpoint", "https://ad.audiencemanager.de/hbs")
v.SetDefault("adapters.openx.endpoint", "http://rtb.openx.net/prebid")
v.SetDefault("adapters.pubmatic.endpoint", "https://hbopenbid.pubmatic.com/translator?source=prebid-server")
v.SetDefault("adapters.pubnative.endpoint", "http://dsp.pubnative.net/bid/v1/request")
diff --git a/exchange/adapter_map.go b/exchange/adapter_map.go
index 05f44e24b66..f7b970c571b 100644
--- a/exchange/adapter_map.go
+++ b/exchange/adapter_map.go
@@ -39,6 +39,7 @@ import (
"github.com/prebid/prebid-server/adapters/lockerdome"
"github.com/prebid/prebid-server/adapters/marsmedia"
"github.com/prebid/prebid-server/adapters/mgid"
+ "github.com/prebid/prebid-server/adapters/nanointeractive"
"github.com/prebid/prebid-server/adapters/openx"
"github.com/prebid/prebid-server/adapters/pubmatic"
"github.com/prebid/prebid-server/adapters/pubnative"
@@ -96,21 +97,22 @@ func newAdapterMap(client *http.Client, cfg *config.Configuration, infos adapter
client,
cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].PlatformID,
cfg.Adapters[strings.ToLower(string(openrtb_ext.BidderFacebook))].AppSecret),
- openrtb_ext.BidderGamma: gamma.NewGammaBidder(cfg.Adapters[string(openrtb_ext.BidderGamma)].Endpoint),
- openrtb_ext.BidderGamoshi: gamoshi.NewGamoshiBidder(cfg.Adapters[string(openrtb_ext.BidderGamoshi)].Endpoint),
- openrtb_ext.BidderGrid: grid.NewGridBidder(cfg.Adapters[string(openrtb_ext.BidderGrid)].Endpoint),
- openrtb_ext.BidderGumGum: gumgum.NewGumGumBidder(cfg.Adapters[string(openrtb_ext.BidderGumGum)].Endpoint),
- openrtb_ext.BidderImprovedigital: improvedigital.NewImprovedigitalBidder(cfg.Adapters[string(openrtb_ext.BidderImprovedigital)].Endpoint),
- openrtb_ext.BidderKidoz: kidoz.NewKidozBidder(cfg.Adapters[string(openrtb_ext.BidderKidoz)].Endpoint),
- openrtb_ext.BidderKubient: kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint),
- openrtb_ext.BidderLockerDome: lockerdome.NewLockerDomeBidder(cfg.Adapters[string(openrtb_ext.BidderLockerDome)].Endpoint),
- openrtb_ext.BidderMarsmedia: marsmedia.NewMarsmediaBidder(cfg.Adapters[string(openrtb_ext.BidderMarsmedia)].Endpoint),
- openrtb_ext.BidderMgid: mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint),
- openrtb_ext.BidderOpenx: openx.NewOpenxBidder(cfg.Adapters[string(openrtb_ext.BidderOpenx)].Endpoint),
- openrtb_ext.BidderPubmatic: pubmatic.NewPubmaticBidder(client, cfg.Adapters[string(openrtb_ext.BidderPubmatic)].Endpoint),
- openrtb_ext.BidderPubnative: pubnative.NewPubnativeBidder(cfg.Adapters[string(openrtb_ext.BidderPubnative)].Endpoint),
- openrtb_ext.BidderRhythmone: rhythmone.NewRhythmoneBidder(cfg.Adapters[string(openrtb_ext.BidderRhythmone)].Endpoint),
- openrtb_ext.BidderRTBHouse: rtbhouse.NewRTBHouseBidder(cfg.Adapters[string(openrtb_ext.BidderRTBHouse)].Endpoint),
+ openrtb_ext.BidderGamma: gamma.NewGammaBidder(cfg.Adapters[string(openrtb_ext.BidderGamma)].Endpoint),
+ openrtb_ext.BidderGamoshi: gamoshi.NewGamoshiBidder(cfg.Adapters[string(openrtb_ext.BidderGamoshi)].Endpoint),
+ openrtb_ext.BidderGrid: grid.NewGridBidder(cfg.Adapters[string(openrtb_ext.BidderGrid)].Endpoint),
+ openrtb_ext.BidderGumGum: gumgum.NewGumGumBidder(cfg.Adapters[string(openrtb_ext.BidderGumGum)].Endpoint),
+ openrtb_ext.BidderImprovedigital: improvedigital.NewImprovedigitalBidder(cfg.Adapters[string(openrtb_ext.BidderImprovedigital)].Endpoint),
+ openrtb_ext.BidderKidoz: kidoz.NewKidozBidder(cfg.Adapters[string(openrtb_ext.BidderKidoz)].Endpoint),
+ openrtb_ext.BidderKubient: kubient.NewKubientBidder(cfg.Adapters[string(openrtb_ext.BidderKubient)].Endpoint),
+ openrtb_ext.BidderLockerDome: lockerdome.NewLockerDomeBidder(cfg.Adapters[string(openrtb_ext.BidderLockerDome)].Endpoint),
+ openrtb_ext.BidderMarsmedia: marsmedia.NewMarsmediaBidder(cfg.Adapters[string(openrtb_ext.BidderMarsmedia)].Endpoint),
+ openrtb_ext.BidderMgid: mgid.NewMgidBidder(cfg.Adapters[string(openrtb_ext.BidderMgid)].Endpoint),
+ openrtb_ext.BidderNanoInteractive: nanointeractive.NewNanoIneractiveBidder(cfg.Adapters[string(openrtb_ext.BidderNanoInteractive)].Endpoint),
+ openrtb_ext.BidderOpenx: openx.NewOpenxBidder(cfg.Adapters[string(openrtb_ext.BidderOpenx)].Endpoint),
+ openrtb_ext.BidderPubmatic: pubmatic.NewPubmaticBidder(client, cfg.Adapters[string(openrtb_ext.BidderPubmatic)].Endpoint),
+ openrtb_ext.BidderPubnative: pubnative.NewPubnativeBidder(cfg.Adapters[string(openrtb_ext.BidderPubnative)].Endpoint),
+ openrtb_ext.BidderRhythmone: rhythmone.NewRhythmoneBidder(cfg.Adapters[string(openrtb_ext.BidderRhythmone)].Endpoint),
+ openrtb_ext.BidderRTBHouse: rtbhouse.NewRTBHouseBidder(cfg.Adapters[string(openrtb_ext.BidderRTBHouse)].Endpoint),
openrtb_ext.BidderRubicon: rubicon.NewRubiconBidder(
client,
cfg.Adapters[string(openrtb_ext.BidderRubicon)].Endpoint,
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index e3f186db333..ec9745563ef 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -57,6 +57,7 @@ const (
BidderLockerDome BidderName = "lockerdome"
BidderMarsmedia BidderName = "marsmedia"
BidderMgid BidderName = "mgid"
+ BidderNanoInteractive BidderName = "nanointeractive"
BidderOpenx BidderName = "openx"
BidderPubmatic BidderName = "pubmatic"
BidderPubnative BidderName = "pubnative"
@@ -119,6 +120,7 @@ var BidderMap = map[string]BidderName{
"lockerdome": BidderLockerDome,
"marsmedia": BidderMarsmedia,
"mgid": BidderMgid,
+ "nanointeractive": BidderNanoInteractive,
"openx": BidderOpenx,
"pubmatic": BidderPubmatic,
"pubnative": BidderPubnative,
diff --git a/openrtb_ext/imp_nanointeractive.go b/openrtb_ext/imp_nanointeractive.go
new file mode 100644
index 00000000000..28db5be0d07
--- /dev/null
+++ b/openrtb_ext/imp_nanointeractive.go
@@ -0,0 +1,10 @@
+package openrtb_ext
+
+// ExtImpNanoInteractive defines the contract for bidrequest.imp[i].ext.nanointeractive
+type ExtImpNanoInteractive struct {
+ Pid string `json:"pid"`
+ Nq []string `json:"nq, omitempty"`
+ Category string `json:"category, omitempty"`
+ SubId string `json:"subId, omitempty"`
+ Ref string `json:"ref, omitempty"`
+}
diff --git a/static/bidder-info/nanointeractive.yaml b/static/bidder-info/nanointeractive.yaml
new file mode 100644
index 00000000000..244e7602950
--- /dev/null
+++ b/static/bidder-info/nanointeractive.yaml
@@ -0,0 +1,9 @@
+maintainer:
+ email: "development@nanointeractive.com"
+capabilities:
+ app:
+ mediaTypes:
+ - banner
+ site:
+ mediaTypes:
+ - banner
diff --git a/static/bidder-params/nanointeractive.json b/static/bidder-params/nanointeractive.json
new file mode 100644
index 00000000000..707dff2fa50
--- /dev/null
+++ b/static/bidder-params/nanointeractive.json
@@ -0,0 +1,32 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "NanoInteractive Adapter Params",
+ "description": "A schema which validates params accepted by the NanoInteractive adapter",
+ "type": "object",
+ "properties": {
+ "pid": {
+ "type": "string",
+ "description": "Placement idd"
+ },
+ "nq": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "search queries"
+ },
+ "category": {
+ "type": "string",
+ "description": "IAB Category"
+ },
+ "subId": {
+ "type": "string",
+ "description": "any segment value provided by publisher"
+ },
+ "ref" : {
+ "type": "string",
+ "description": "referer"
+ }
+ },
+ "required": ["pid"]
+}
\ No newline at end of file
diff --git a/usersync/usersyncers/syncer.go b/usersync/usersyncers/syncer.go
index c7ad70b7eff..be0392f2dbb 100644
--- a/usersync/usersyncers/syncer.go
+++ b/usersync/usersyncers/syncer.go
@@ -34,6 +34,7 @@ import (
"github.com/prebid/prebid-server/adapters/lockerdome"
"github.com/prebid/prebid-server/adapters/marsmedia"
"github.com/prebid/prebid-server/adapters/mgid"
+ "github.com/prebid/prebid-server/adapters/nanointeractive"
"github.com/prebid/prebid-server/adapters/openx"
"github.com/prebid/prebid-server/adapters/pubmatic"
"github.com/prebid/prebid-server/adapters/pulsepoint"
@@ -96,6 +97,7 @@ func NewSyncerMap(cfg *config.Configuration) map[openrtb_ext.BidderName]usersync
insertIntoMap(cfg, syncers, openrtb_ext.BidderLockerDome, lockerdome.NewLockerDomeSyncer)
insertIntoMap(cfg, syncers, openrtb_ext.BidderMarsmedia, marsmedia.NewMarsmediaSyncer)
insertIntoMap(cfg, syncers, openrtb_ext.BidderMgid, mgid.NewMgidSyncer)
+ insertIntoMap(cfg, syncers, openrtb_ext.BidderNanoInteractive, nanointeractive.NewNanoInteractiveSyncer)
insertIntoMap(cfg, syncers, openrtb_ext.BidderOpenx, openx.NewOpenxSyncer)
insertIntoMap(cfg, syncers, openrtb_ext.BidderPubmatic, pubmatic.NewPubmaticSyncer)
insertIntoMap(cfg, syncers, openrtb_ext.BidderPulsepoint, pulsepoint.NewPulsepointSyncer)
diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go
index 3de64ec1eb0..383e24d82cf 100644
--- a/usersync/usersyncers/syncer_test.go
+++ b/usersync/usersyncers/syncer_test.go
@@ -43,6 +43,7 @@ func TestNewSyncerMap(t *testing.T) {
string(openrtb_ext.BidderLockerDome): syncConfig,
string(openrtb_ext.BidderMarsmedia): syncConfig,
string(openrtb_ext.BidderMgid): syncConfig,
+ string(openrtb_ext.BidderNanoInteractive): syncConfig,
string(openrtb_ext.BidderOpenx): syncConfig,
string(openrtb_ext.BidderPubmatic): syncConfig,
string(openrtb_ext.BidderPulsepoint): syncConfig,