diff --git a/.github/workflows/code-path-changes.yml b/.github/workflows/code-path-changes.yml
index 04b49c4a1f4..23e0c19db6d 100644
--- a/.github/workflows/code-path-changes.yml
+++ b/.github/workflows/code-path-changes.yml
@@ -14,6 +14,9 @@ env:
GITHUB_PR_NUMBER: ${{ github.event.pull_request.number }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+permissions:
+ contents: read
+
jobs:
notify:
runs-on: ubuntu-latest
diff --git a/adapters/connatix/connatix.go b/adapters/connatix/connatix.go
index 74ff5956848..d2f900d9c22 100644
--- a/adapters/connatix/connatix.go
+++ b/adapters/connatix/connatix.go
@@ -199,7 +199,8 @@ func buildRequestImp(imp *openrtb2.Imp, ext impExtIncoming, displayManagerVer st
impExt := impExt{
Connatix: impExtConnatix{
- PlacementId: ext.Bidder.PlacementId,
+ PlacementId: ext.Bidder.PlacementId,
+ ViewabilityPercentage: ext.Bidder.ViewabilityPercentage,
},
}
diff --git a/adapters/connatix/connatixtest/supplemental/success-build-display-manager-version.json b/adapters/connatix/connatixtest/supplemental/success-build-display-manager-version.json
index 7baffa28ac7..bf74b77a03a 100644
--- a/adapters/connatix/connatixtest/supplemental/success-build-display-manager-version.json
+++ b/adapters/connatix/connatixtest/supplemental/success-build-display-manager-version.json
@@ -43,7 +43,8 @@
},
"ext": {
"bidder": {
- "placementId": "some-placement-id"
+ "placementId": "some-placement-id",
+ "viewabilityPercentage": 0.6
}
}
}
@@ -78,7 +79,8 @@
"displaymanagerver": "test-1.0.0",
"ext": {
"connatix": {
- "placementId": "some-placement-id"
+ "placementId": "some-placement-id",
+ "viewabilityPercentage": 0.6
}
}
}
diff --git a/adapters/connatix/models.go b/adapters/connatix/models.go
index 55d475f9638..95317d7e23a 100644
--- a/adapters/connatix/models.go
+++ b/adapters/connatix/models.go
@@ -17,7 +17,8 @@ type impExt struct {
}
type impExtConnatix struct {
- PlacementId string `json:"placementId,omitempty"`
+ PlacementId string `json:"placementId,omitempty"`
+ ViewabilityPercentage float64 `json:"viewabilityPercentage,omitempty"`
}
type bidExt struct {
diff --git a/adapters/loopme/loopme.go b/adapters/loopme/loopme.go
new file mode 100644
index 00000000000..d4b9d7906fc
--- /dev/null
+++ b/adapters/loopme/loopme.go
@@ -0,0 +1,116 @@
+package loopme
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+
+ "github.com/prebid/openrtb/v20/openrtb2"
+ "github.com/prebid/prebid-server/v3/adapters"
+ "github.com/prebid/prebid-server/v3/config"
+ "github.com/prebid/prebid-server/v3/errortypes"
+ "github.com/prebid/prebid-server/v3/openrtb_ext"
+)
+
+type adapter struct {
+ endpoint string
+}
+
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+ errs := make([]error, 0, len(request.Imp))
+
+ reqDatas := make([]*adapters.RequestData, 0, len(request.Imp))
+
+ for _, imp := range request.Imp {
+ requestCopy := *request
+ requestCopy.Imp = []openrtb2.Imp{imp}
+ reqJSON, err := json.Marshal(requestCopy)
+ if err != nil {
+ errs = append(errs, err)
+ continue
+ }
+
+ headers := http.Header{}
+ headers.Add("Content-Type", "application/json;charset=utf-8")
+ headers.Add("Accept", "application/json")
+ reqDatas = append(reqDatas, &adapters.RequestData{
+ Method: http.MethodPost,
+ Uri: a.endpoint,
+ Body: reqJSON,
+ Headers: headers,
+ ImpIDs: openrtb_ext.GetImpIDs(requestCopy.Imp),
+ })
+
+ }
+ return reqDatas, errs
+}
+
+func (a *adapter) MakeBids(bidReq *openrtb2.BidRequest, reqData *adapters.RequestData, respData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+ if adapters.IsResponseStatusCodeNoContent(respData) {
+ return nil, nil
+ }
+
+ if err := adapters.CheckResponseStatusCodeForErrors(respData); err != nil {
+ return nil, []error{err}
+ }
+
+ var bidResp openrtb2.BidResponse
+ if err := json.Unmarshal(respData.Body, &bidResp); err != nil {
+ return nil, []error{err}
+ }
+
+ if len(bidResp.SeatBid) == 0 || len(bidResp.SeatBid[0].Bid) == 0 {
+ return nil, nil
+ }
+
+ errs := make([]error, 0, len(bidResp.SeatBid[0].Bid))
+ resp := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid))
+
+ for _, sb := range bidResp.SeatBid {
+ for i := range sb.Bid {
+ bid := &sb.Bid[i]
+ bidType, err := getBidType(bid)
+ if err != nil {
+ errs = append(errs, err)
+ continue
+ }
+ resp.Bids = append(resp.Bids, &adapters.TypedBid{
+ Bid: bid,
+ BidType: bidType,
+ })
+ }
+ }
+
+ if len(resp.Bids) == 0 {
+ return nil, errs
+ }
+ if len(bidResp.Cur) != 0 {
+ resp.Currency = bidResp.Cur
+ }
+ return resp, errs
+}
+
+func getBidType(bid *openrtb2.Bid) (openrtb_ext.BidType, error) {
+ switch bid.MType {
+ case openrtb2.MarkupBanner:
+ return openrtb_ext.BidTypeBanner, nil
+ case openrtb2.MarkupVideo:
+ return openrtb_ext.BidTypeVideo, nil
+ case openrtb2.MarkupNative:
+ return openrtb_ext.BidTypeNative, nil
+ case openrtb2.MarkupAudio:
+ return openrtb_ext.BidTypeAudio, nil
+ default:
+ return "", &errortypes.BadServerResponse{
+ Message: fmt.Sprintf("Unsupported MType %d", bid.MType),
+ }
+ }
+}
+
+// Builder builds a new instance of the Loopme adapter for the given bidder with the given config.
+func Builder(bidderName openrtb_ext.BidderName, cfg config.Adapter, serverCfg config.Server) (adapters.Bidder, error) {
+ bidder := &adapter{
+ endpoint: cfg.Endpoint,
+ }
+ return bidder, nil
+}
diff --git a/adapters/loopme/loopme_test.go b/adapters/loopme/loopme_test.go
new file mode 100644
index 00000000000..ca293c45cee
--- /dev/null
+++ b/adapters/loopme/loopme_test.go
@@ -0,0 +1,29 @@
+package loopme
+
+import (
+ "testing"
+
+ "github.com/prebid/prebid-server/v3/adapters/adapterstest"
+ "github.com/prebid/prebid-server/v3/config"
+ "github.com/prebid/prebid-server/v3/openrtb_ext"
+)
+
+func TestJsonSamples(t *testing.T) {
+ bidder, buildErr := Builder(
+ openrtb_ext.BidderLoopme,
+ config.Adapter{
+ Endpoint: "http://loopme.example.com",
+ },
+ config.Server{
+ ExternalUrl: "http://hosturl.com",
+ GvlID: 109,
+ DataCenter: "2",
+ },
+ )
+
+ if buildErr != nil {
+ t.Fatalf("Builder returned unexpected error %v", buildErr)
+ }
+
+ adapterstest.RunJSONBidderTest(t, "loopmetest", bidder)
+}
diff --git a/adapters/loopme/loopmetest/exemplary/app-formats.json b/adapters/loopme/loopmetest/exemplary/app-formats.json
new file mode 100644
index 00000000000..4c70a4b5536
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/app-formats.json
@@ -0,0 +1,152 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "native": {
+ "request": "{json string 1}",
+ "ver": "1.2"
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "native": {
+ "request": "{json string 1}",
+ "ver": "1.2"
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "seat": "loopme",
+ "bid": [
+ {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.500000,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 728,
+ "h": 90,
+ "mtype": 1
+ }
+ ]
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.5,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 728,
+ "h": 90,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/loopme/loopmetest/exemplary/app-imps.json b/adapters/loopme/loopmetest/exemplary/app-imps.json
new file mode 100644
index 00000000000..477639d9305
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/app-imps.json
@@ -0,0 +1,343 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ },
+ {
+ "id": "imp-2",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ },
+ {
+ "id": "imp-3",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "imp-1",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "imp-1"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "loopme",
+ "bid": [
+ {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 0.500000,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 1024,
+ "h": 576,
+ "mtype": 2
+ }
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "imp-2",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "imp-2"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "loopme",
+ "bid": [
+ {
+ "id": "bid-2",
+ "impid": "imp-2",
+ "price": 0.500000,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 1024,
+ "h": 576,
+ "mtype": 2
+ }
+ ]
+ }
+ ]
+ }
+ }
+ },
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "imp-3",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "imp-3"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "loopme",
+ "bid": [
+ {
+ "id": "bid-3",
+ "impid": "imp-3",
+ "price": 0.500000,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 728,
+ "h": 90,
+ "mtype": 1
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-1",
+ "impid": "imp-1",
+ "price": 0.5,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 1024,
+ "h": 576,
+ "mtype": 2
+ },
+ "type": "video"
+ }
+ ]
+ },
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-2",
+ "impid": "imp-2",
+ "price": 0.5,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 1024,
+ "h": 576,
+ "mtype": 2
+ },
+ "type": "video"
+ }
+ ]
+ },
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "bid-3",
+ "impid": "imp-3",
+ "price": 0.5,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 728,
+ "h": 90,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
+
+
+
diff --git a/adapters/loopme/loopmetest/exemplary/no-bid.json b/adapters/loopme/loopmetest/exemplary/no-bid.json
new file mode 100644
index 00000000000..a35004eb6a7
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/no-bid.json
@@ -0,0 +1,90 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 204,
+ "body": {
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": []
+}
diff --git a/adapters/loopme/loopmetest/exemplary/no-seat-bid.json b/adapters/loopme/loopmetest/exemplary/no-seat-bid.json
new file mode 100644
index 00000000000..054a1d8a298
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/no-seat-bid.json
@@ -0,0 +1,97 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "seat": "loopme",
+ "bid": []
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/loopme/loopmetest/exemplary/no-seat.json b/adapters/loopme/loopmetest/exemplary/no-seat.json
new file mode 100644
index 00000000000..c5497b3957e
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/no-seat.json
@@ -0,0 +1,92 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": []
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": []
+}
\ No newline at end of file
diff --git a/adapters/loopme/loopmetest/exemplary/simple-app-audio.json b/adapters/loopme/loopmetest/exemplary/simple-app-audio.json
new file mode 100644
index 00000000000..7c51d8cd063
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/simple-app-audio.json
@@ -0,0 +1,108 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "audio": {
+ "mimes": [
+ "audio/mp4"
+ ],
+ "protocols": [
+ 9,
+ 10
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "audio": {
+ "mimes": [
+ "audio/mp4"
+ ],
+ "protocols": [
+ 9,
+ 10
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "seat": "loopme",
+ "bid": [
+ {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.500000,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "mtype": 3
+ }
+ ]
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.5,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "mtype": 3
+ },
+ "type": "audio"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/adapters/loopme/loopmetest/exemplary/simple-app-banner.json b/adapters/loopme/loopmetest/exemplary/simple-app-banner.json
new file mode 100644
index 00000000000..6b3ab6c9752
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/simple-app-banner.json
@@ -0,0 +1,118 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "seat": "loopme",
+ "bid": [
+ {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.500000,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 728,
+ "h": 90,
+ "mtype": 1
+ }
+ ]
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.5,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 728,
+ "h": 90,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/loopme/loopmetest/exemplary/simple-app-native.json b/adapters/loopme/loopmetest/exemplary/simple-app-native.json
new file mode 100644
index 00000000000..629d07c2f70
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/simple-app-native.json
@@ -0,0 +1,97 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "native": {
+ "request": "{\"ver\":\"1.2\",\"context\":2,\"contextsubtype\":20,\"plcmttype\":11,\"plcmtcnt\":1,\"aurlsupport\":1,\"durlsupport\":1,\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":836,\"hmin\":627,\"type\":3}}]}"
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [{
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "native": {
+ "request": "{\"ver\":\"1.2\",\"context\":2,\"contextsubtype\":20,\"plcmttype\":11,\"plcmtcnt\":1,\"aurlsupport\":1,\"durlsupport\":1,\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":836,\"hmin\":627,\"type\":3}}]}"
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.500000,
+ "adm": "native-json",
+ "crid": "test-crid",
+ "mtype": 4
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }],
+
+ "expectedBidResponses": [{
+ "currency": "USD",
+ "bids": [{
+ "bid": {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.500000,
+ "adm": "native-json",
+ "crid": "test-crid",
+ "mtype": 4
+ },
+ "type": "native"
+ }]
+ }]
+}
diff --git a/adapters/loopme/loopmetest/exemplary/simple-app-video.json b/adapters/loopme/loopmetest/exemplary/simple-app-video.json
new file mode 100644
index 00000000000..d90c7f2733a
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/simple-app-video.json
@@ -0,0 +1,128 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "app": {
+ "bundle": "com.prebid"
+ },
+ "device": {
+ "ifa": "ec943cb9-61ec-460f-a925-6489c3fcc4e3"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "loopme",
+ "bid": [
+ {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.500000,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 1024,
+ "h": 576,
+ "mtype": 2
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.5,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 1024,
+ "h": 576,
+ "mtype": 2
+ },
+ "type": "video"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/loopme/loopmetest/exemplary/simple-site-audio.json b/adapters/loopme/loopmetest/exemplary/simple-site-audio.json
new file mode 100644
index 00000000000..8e586afd1de
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/simple-site-audio.json
@@ -0,0 +1,108 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "audio": {
+ "mimes": [
+ "audio/mp4"
+ ],
+ "protocols": [
+ 9,
+ 10
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "audio": {
+ "mimes": [
+ "audio/mp4"
+ ],
+ "protocols": [
+ 9,
+ 10
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "seat": "loopme",
+ "bid": [
+ {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.500000,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "mtype": 3
+ }
+ ]
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.5,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "mtype": 3
+ },
+ "type": "audio"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/adapters/loopme/loopmetest/exemplary/simple-site-banner.json b/adapters/loopme/loopmetest/exemplary/simple-site-banner.json
new file mode 100644
index 00000000000..d3d87e813af
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/simple-site-banner.json
@@ -0,0 +1,118 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "seat": "loopme",
+ "bid": [
+ {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.500000,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 728,
+ "h": 90,
+ "mtype": 1
+ }
+ ]
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.5,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 728,
+ "h": 90,
+ "mtype": 1
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/loopme/loopmetest/exemplary/simple-site-native.json b/adapters/loopme/loopmetest/exemplary/simple-site-native.json
new file mode 100644
index 00000000000..ef8cf46b820
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/simple-site-native.json
@@ -0,0 +1,97 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "native": {
+ "request": "{\"ver\":\"1.2\",\"context\":2,\"contextsubtype\":20,\"plcmttype\":11,\"plcmtcnt\":1,\"aurlsupport\":1,\"durlsupport\":1,\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":836,\"hmin\":627,\"type\":3}}]}"
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [{
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "native": {
+ "request": "{\"ver\":\"1.2\",\"context\":2,\"contextsubtype\":20,\"plcmttype\":11,\"plcmtcnt\":1,\"aurlsupport\":1,\"durlsupport\":1,\"assets\":[{\"id\":123,\"required\":1,\"title\":{\"len\":140}},{\"id\":128,\"required\":0,\"img\":{\"wmin\":836,\"hmin\":627,\"type\":3}}]}"
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.500000,
+ "adm": "native-json",
+ "crid": "test-crid",
+ "mtype": 4
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }],
+
+ "expectedBidResponses": [{
+ "currency": "USD",
+ "bids": [{
+ "bid": {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.500000,
+ "adm": "native-json",
+ "crid": "test-crid",
+ "mtype": 4
+ },
+ "type": "native"
+ }]
+ }]
+}
diff --git a/adapters/loopme/loopmetest/exemplary/simple-site-video.json b/adapters/loopme/loopmetest/exemplary/simple-site-video.json
new file mode 100644
index 00000000000..b5b70d2cc46
--- /dev/null
+++ b/adapters/loopme/loopmetest/exemplary/simple-site-video.json
@@ -0,0 +1,128 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "headers": {
+ "Accept": [
+ "application/json"
+ ],
+ "Content-Type": [
+ "application/json;charset=utf-8"
+ ]
+ },
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ],
+ "protocols": [
+ 2,
+ 3,
+ 5,
+ 6
+ ],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "cur": "USD",
+ "seatbid": [
+ {
+ "seat": "loopme",
+ "bid": [
+ {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.500000,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 1024,
+ "h": 576,
+ "mtype": 2
+ }
+ ]
+ }
+ ]
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.5,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 1024,
+ "h": 576,
+ "mtype": 2
+ },
+ "type": "video"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/loopme/loopmetest/supplemental/resp-bad-json.json b/adapters/loopme/loopmetest/supplemental/resp-bad-json.json
new file mode 100644
index 00000000000..0c73aa4a431
--- /dev/null
+++ b/adapters/loopme/loopmetest/supplemental/resp-bad-json.json
@@ -0,0 +1,76 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": "this is not a valid json"
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "json: cannot unmarshal*",
+ "comparison": "regex"
+ }
+ ]
+}
diff --git a/adapters/loopme/loopmetest/supplemental/resp-bad-markuptype.json b/adapters/loopme/loopmetest/supplemental/resp-bad-markuptype.json
new file mode 100644
index 00000000000..58a6b438b79
--- /dev/null
+++ b/adapters/loopme/loopmetest/supplemental/resp-bad-markuptype.json
@@ -0,0 +1,96 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "seat": "loopme",
+ "bid": [
+ {
+ "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800",
+ "impid": "test-imp-id",
+ "price": 0.500000,
+ "adm": "some-test-ad",
+ "crid": "test-crid",
+ "w": 728,
+ "h": 90,
+ "mtype": 0
+ }
+ ]
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unsupported MType 0",
+ "comparison": "literal"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/adapters/loopme/loopmetest/supplemental/status-400.json b/adapters/loopme/loopmetest/supplemental/status-400.json
new file mode 100644
index 00000000000..e112103abb0
--- /dev/null
+++ b/adapters/loopme/loopmetest/supplemental/status-400.json
@@ -0,0 +1,76 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 400,
+ "body": "invalid params"
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unexpected status code: 400. Run with request.debug = 1 for more info",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/loopme/loopmetest/supplemental/status-500.json b/adapters/loopme/loopmetest/supplemental/status-500.json
new file mode 100644
index 00000000000..f25c9f321d1
--- /dev/null
+++ b/adapters/loopme/loopmetest/supplemental/status-500.json
@@ -0,0 +1,76 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "mockResponse": {
+ "status": 500,
+ "body": "Internal Server Error"
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unexpected status code: 500. Run with request.debug = 1 for more info",
+ "comparison": "literal"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/adapters/loopme/loopmetest/supplemental/status-503.json b/adapters/loopme/loopmetest/supplemental/status-503.json
new file mode 100644
index 00000000000..97bd77753c7
--- /dev/null
+++ b/adapters/loopme/loopmetest/supplemental/status-503.json
@@ -0,0 +1,76 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "http://loopme.example.com",
+ "body": {
+ "id": "test-request-id",
+ "site": {
+ "page": "prebid.org"
+ },
+ "user": {
+ "buyeruid": "be5e209ad46927520000000000000000"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 728,
+ "h": 90
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "publisherId": "10000000"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": [
+ "test-imp-id"
+ ]
+ },
+ "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/adapters/loopme/params_test.go b/adapters/loopme/params_test.go
new file mode 100644
index 00000000000..12ddf8e9856
--- /dev/null
+++ b/adapters/loopme/params_test.go
@@ -0,0 +1,84 @@
+package loopme
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/prebid/prebid-server/v3/openrtb_ext"
+)
+
+// This file actually intends to test static/bidder-params/loopme.json
+//
+// These also validate the format of the external API: request.imp[i].ext.prebid.bidder.loopme
+
+// TestValidParams makes sure that the loopme 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.BidderLoopme, json.RawMessage(validParam)); err != nil {
+ t.Errorf("Schema rejected loopme params: %s", validParam)
+ }
+ }
+}
+
+// TestInvalidParams makes sure that the loopme 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.BidderLoopme, json.RawMessage(invalidParam)); err == nil {
+ t.Errorf("Schema allowed unexpected params: %s", invalidParam)
+ }
+ }
+}
+
+var validParams = []string{
+ `{"publisherId": "10000000"}`,
+ `{"publisherId": "10000001", "bundleId": "4321"}`,
+ `{"publisherId": "10000002", "placementId": "8888"}`,
+ `{"publisherId": "10000003", "bundleId": "5432", "placementId": "7777"}`,
+}
+
+var invalidParams = []string{
+ ``,
+ `null`,
+ `undefined`,
+ `0`,
+ `{}`,
+ `[]`,
+ `{"publisherId": ""}`,
+ `{"placementId": ""}`,
+ `{"bundleId": ""}`,
+ `{"publisherId": "", "placementId": ""}`,
+ `{"publisherId": "", "bundleId": ""}`,
+ `{"placementId": "", "bundleId": ""}`,
+ `{"publisherId": "", "placementId": "", "bundleId": ""}`,
+ `{"publisherId": 0}`,
+ `{"placementId": 0}`,
+ `{"bundleId": 0}`,
+ `{"publisherId": 0, "placementId": 0}`,
+ `{"publisherId": 0, "bundleId": 0}`,
+ `{"placementId": 0, "bundleId": 0}`,
+ `{"publisherId": 0, "placementId": 0, "bundleId": 0}`,
+ `{"publisherId": "10000000", "placementId": 0}`,
+ `{"publisherId": "10000000", "placementId": 100000}`,
+ `{"publisherId": "10000000", "bundleId": 0}`,
+ `{"publisherId": "10000000", "bundleId": 100000}`,
+ `{"placementId": "10000000", "bundleId": 0}`,
+ `{"placementId": "10000000", "bundleId": 100000}`,
+ `{"publisherId": "10000000", "placementId": "", "bundleId": ""}`,
+ `{"publisherId": "", "placementId": "100000", "bundleId": ""}`,
+ `{"publisherId": "", "placementId": "", "bundleId": "bundle_id_test"}`,
+ `{"unknownField": "value"}`,
+ `{"bundleId": []}`,
+ `{"placementId": {}}`,
+ `{"publisherId": null}`,
+ `{"bundleId": null}`,
+}
diff --git a/adapters/nextmillennium/nextmillennium.go b/adapters/nextmillennium/nextmillennium.go
index cae3e03be19..1b3cdeefdd3 100644
--- a/adapters/nextmillennium/nextmillennium.go
+++ b/adapters/nextmillennium/nextmillennium.go
@@ -11,8 +11,11 @@ import (
"github.com/prebid/prebid-server/v3/errortypes"
"github.com/prebid/prebid-server/v3/openrtb_ext"
"github.com/prebid/prebid-server/v3/util/jsonutil"
+ "github.com/prebid/prebid-server/v3/version"
)
+const NM_ADAPTER_VERSION = "v1.0.0"
+
type adapter struct {
endpoint string
nmmFlags []string
@@ -33,7 +36,9 @@ type nmExtPrebid struct {
Server *server `json:"server,omitempty"`
}
type nmExtNMM struct {
- NmmFlags []string `json:"nmmFlags,omitempty"`
+ NmmFlags []string `json:"nmmFlags,omitempty"`
+ ServerVersion string `json:"server_version,omitempty"`
+ AdapterVersion string `json:"nm_version,omitempty"`
}
type nextMillJsonExt struct {
Prebid nmExtPrebid `json:"prebid"`
@@ -149,6 +154,8 @@ func createBidRequest(prebidBidRequest *openrtb2.BidRequest, params *openrtb_ext
DataCenter: serverParams.DataCenter,
ExternalUrl: serverParams.ExternalUrl,
}
+ ext.NextMillennium.AdapterVersion = NM_ADAPTER_VERSION
+ ext.NextMillennium.ServerVersion = version.Ver
jsonExt, err := json.Marshal(ext)
if err != nil {
return &bidRequest
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-empty-group-id.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-empty-group-id.json
index 9b54c58a0bf..f44bf6b01f7 100644
--- a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-empty-group-id.json
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-empty-group-id.json
@@ -33,7 +33,9 @@
"body":{
"id": "testid",
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "7819"
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id.json
index d8f6915bf28..4962cb01b32 100644
--- a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id.json
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id.json
@@ -44,7 +44,9 @@
"body":{
"id": "testid",
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "g7819;320x250;www.example.com"
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id_app.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id_app.json
index 2f0d9789b0c..97152efd359 100644
--- a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id_app.json
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-group-id_app.json
@@ -40,7 +40,9 @@
"domain": "www.example.com"
},
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "g7819;320x250;www.example.com"
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-only-width.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-only-width.json
index 35cb9bb7581..42f148ce836 100644
--- a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-only-width.json
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-only-width.json
@@ -29,7 +29,9 @@
"domain": "www.example.com"
},
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "g7819;;www.example.com"
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-wh.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-wh.json
index 6206b4a2828..81fef905bfd 100644
--- a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-wh.json
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-with-wh.json
@@ -30,7 +30,9 @@
"domain": "www.example.com"
},
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "g7819;320x250;www.example.com"
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-domain.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-domain.json
index ddefb32fa90..ab4d9682045 100644
--- a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-domain.json
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-domain.json
@@ -34,7 +34,9 @@
"body":{
"id": "testid",
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "g7819;320x250;"
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-size.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-size.json
index 337876ad0e5..da8bd4bcbea 100644
--- a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-size.json
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner-wo-size.json
@@ -32,7 +32,9 @@
"body":{
"id": "testid",
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "g7819;320x250;"
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner.json
index 2f60bb95916..f28d4ab15b1 100644
--- a/adapters/nextmillennium/nextmillenniumtest/exemplary/banner.json
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/banner.json
@@ -34,7 +34,9 @@
"body":{
"id": "testid",
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "7819"
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/empty-banner-obj.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/empty-banner-obj.json
index cadef9cdb5c..f3eb360e2e0 100644
--- a/adapters/nextmillennium/nextmillenniumtest/exemplary/empty-banner-obj.json
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/empty-banner-obj.json
@@ -21,7 +21,9 @@
"body":{
"id": "testid",
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "g7819;;"
diff --git a/adapters/nextmillennium/nextmillenniumtest/exemplary/video.json b/adapters/nextmillennium/nextmillenniumtest/exemplary/video.json
index ff96522166a..e31aa1e0ac2 100644
--- a/adapters/nextmillennium/nextmillenniumtest/exemplary/video.json
+++ b/adapters/nextmillennium/nextmillenniumtest/exemplary/video.json
@@ -30,7 +30,9 @@
"body":{
"id": "testid",
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "7819"
diff --git a/adapters/nextmillennium/nextmillenniumtest/supplemental/empty-seatbid.json b/adapters/nextmillennium/nextmillenniumtest/supplemental/empty-seatbid.json
index e75d787e28a..02331a1d931 100644
--- a/adapters/nextmillennium/nextmillenniumtest/supplemental/empty-seatbid.json
+++ b/adapters/nextmillennium/nextmillenniumtest/supplemental/empty-seatbid.json
@@ -33,7 +33,9 @@
"body": {
"id": "testid",
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "7819"
diff --git a/adapters/nextmillennium/nextmillenniumtest/supplemental/error-response.json b/adapters/nextmillennium/nextmillenniumtest/supplemental/error-response.json
index b2d69c3ebc7..fdf6d11943e 100644
--- a/adapters/nextmillennium/nextmillenniumtest/supplemental/error-response.json
+++ b/adapters/nextmillennium/nextmillenniumtest/supplemental/error-response.json
@@ -29,7 +29,9 @@
"body": {
"id": "testid",
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "7819"
diff --git a/adapters/nextmillennium/nextmillenniumtest/supplemental/no-content.json b/adapters/nextmillennium/nextmillenniumtest/supplemental/no-content.json
index d7ad3f64fcd..d2641f62eab 100644
--- a/adapters/nextmillennium/nextmillenniumtest/supplemental/no-content.json
+++ b/adapters/nextmillennium/nextmillenniumtest/supplemental/no-content.json
@@ -29,7 +29,9 @@
"body": {
"id": "testid",
"ext": {
- "nextMillennium": {},
+ "nextMillennium": {
+ "nm_version": "v1.0.0"
+ },
"prebid": {
"storedrequest": {
"id": "7819"
diff --git a/adapters/smoot/params_test.go b/adapters/smoot/params_test.go
new file mode 100644
index 00000000000..ef0ec2149ba
--- /dev/null
+++ b/adapters/smoot/params_test.go
@@ -0,0 +1,57 @@
+package smoot
+
+import (
+ "encoding/json"
+ "testing"
+
+ "github.com/prebid/prebid-server/v3/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.BidderSmoot, 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.BidderSmoot, json.RawMessage(p)); err == nil {
+ t.Errorf("Schema allowed invalid params: %s", p)
+ }
+ }
+}
+
+var validParams = []string{
+ `{"placementId": "test"}`,
+ `{"placementId": "1"}`,
+ `{"endpointId": "test"}`,
+ `{"endpointId": "1"}`,
+}
+
+var invalidParams = []string{
+ `{"placementId": 42}`,
+ `{"endpointId": 42}`,
+ `{"placementId": "1", "endpointId": "1"}`,
+ ``,
+ `null`,
+ `true`,
+ `5`,
+ `4.2`,
+ `[]`,
+ `{}`,
+ `{"placementId": ""}`,
+ `{"endpointId": ""}`,
+ `{"placementId": "", "endpointId": ""}`,
+}
diff --git a/adapters/smoot/smoot.go b/adapters/smoot/smoot.go
new file mode 100644
index 00000000000..e23c2de602d
--- /dev/null
+++ b/adapters/smoot/smoot.go
@@ -0,0 +1,152 @@
+package smoot
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+
+ "github.com/prebid/openrtb/v20/openrtb2"
+ "github.com/prebid/prebid-server/v3/adapters"
+ "github.com/prebid/prebid-server/v3/config"
+ "github.com/prebid/prebid-server/v3/openrtb_ext"
+ "github.com/prebid/prebid-server/v3/util/jsonutil"
+)
+
+type adapter struct {
+ endpoint string
+}
+
+type reqBodyExt struct {
+ SmootBidderExt reqBodyExtBidder `json:"bidder"`
+}
+
+type reqBodyExtBidder struct {
+ Type string `json:"type"`
+ PlacementID string `json:"placementId,omitempty"`
+ EndpointID string `json:"endpointId,omitempty"`
+}
+
+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) {
+ var errs []error
+ var adapterRequests []*adapters.RequestData
+
+ reqCopy := *request
+ for _, imp := range request.Imp {
+ reqCopy.Imp = []openrtb2.Imp{imp}
+
+ var bidderExt adapters.ExtImpBidder
+ var smootExt openrtb_ext.ImpExtSmoot
+
+ if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil {
+ errs = append(errs, err)
+ continue
+ }
+ if err := jsonutil.Unmarshal(bidderExt.Bidder, &smootExt); err != nil {
+ errs = append(errs, err)
+ continue
+ }
+
+ impExt := reqBodyExt{SmootBidderExt: reqBodyExtBidder{}}
+
+ if smootExt.PlacementID != "" {
+ impExt.SmootBidderExt.PlacementID = smootExt.PlacementID
+ impExt.SmootBidderExt.Type = "publisher"
+ } else {
+ impExt.SmootBidderExt.EndpointID = smootExt.EndpointID
+ impExt.SmootBidderExt.Type = "network"
+ }
+
+ finalyImpExt, err := json.Marshal(impExt)
+ if err != nil {
+ errs = append(errs, err)
+ continue
+ }
+
+ reqCopy.Imp[0].Ext = finalyImpExt
+
+ adapterReq, err := a.makeRequest(&reqCopy)
+ if err != nil {
+ errs = append(errs, err)
+ continue
+ }
+
+ adapterRequests = append(adapterRequests, adapterReq)
+ }
+
+ return adapterRequests, errs
+}
+
+func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, error) {
+ reqJSON, err := json.Marshal(request)
+ if err != nil {
+ return nil, err
+ }
+
+ headers := http.Header{}
+ headers.Add("Content-Type", "application/json;charset=utf-8")
+ headers.Add("Accept", "application/json")
+ return &adapters.RequestData{
+ Method: http.MethodPost,
+ Uri: a.endpoint,
+ Body: reqJSON,
+ Headers: headers,
+ ImpIDs: openrtb_ext.GetImpIDs(request.Imp),
+ }, nil
+}
+
+func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
+ if adapters.IsResponseStatusCodeNoContent(responseData) {
+ return nil, nil
+ }
+
+ if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil {
+ return nil, []error{err}
+ }
+
+ var response openrtb2.BidResponse
+ if err := jsonutil.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 {
+ bidType, err := getBidType(&seatBid.Bid[i])
+ if err != nil {
+ return nil, []error{err}
+ }
+
+ b := &adapters.TypedBid{
+ Bid: &seatBid.Bid[i],
+ BidType: bidType,
+ }
+ bidResponse.Bids = append(bidResponse.Bids, b)
+ }
+ }
+ return bidResponse, nil
+}
+
+func getBidType(bid *openrtb2.Bid) (openrtb_ext.BidType, error) {
+ // determinate media type by bid response field mtype
+ switch bid.MType {
+ case openrtb2.MarkupBanner:
+ return openrtb_ext.BidTypeBanner, nil
+ case openrtb2.MarkupVideo:
+ return openrtb_ext.BidTypeVideo, nil
+ case openrtb2.MarkupNative:
+ return openrtb_ext.BidTypeNative, nil
+ }
+
+ return "", fmt.Errorf("could not define media type for impression: %s", bid.ImpID)
+}
diff --git a/adapters/smoot/smoot_test.go b/adapters/smoot/smoot_test.go
new file mode 100644
index 00000000000..46c65862414
--- /dev/null
+++ b/adapters/smoot/smoot_test.go
@@ -0,0 +1,20 @@
+package smoot
+
+import (
+ "testing"
+
+ "github.com/prebid/prebid-server/v3/adapters/adapterstest"
+ "github.com/prebid/prebid-server/v3/config"
+ "github.com/prebid/prebid-server/v3/openrtb_ext"
+)
+
+func TestJsonSamples(t *testing.T) {
+ bidder, buildErr := Builder(openrtb_ext.BidderSmoot, config.Adapter{
+ Endpoint: "https://fake.test.io/pserver"}, config.Server{ExternalUrl: "http://hosturl.com", GvlID: 1, DataCenter: "2"})
+
+ if buildErr != nil {
+ t.Fatalf("Builder returned unexpected error %v", buildErr)
+ }
+
+ adapterstest.RunJSONBidderTest(t, "smoottest", bidder)
+}
diff --git a/adapters/smoot/smoottest/exemplary/endpointId.json b/adapters/smoot/smoottest/exemplary/endpointId.json
new file mode 100644
index 00000000000..ddfdba67c1d
--- /dev/null
+++ b/adapters/smoot/smoottest/exemplary/endpointId.json
@@ -0,0 +1,136 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ },
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "tagid": "test",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "endpointId": "test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://fake.test.io/pserver",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "tagid": "test",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "endpointId": "test",
+ "type": "network"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ }
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test_bid_id",
+ "impid": "test-imp-id",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 1,
+ "w": 300,
+ "h": 250,
+ "ext": {
+ "prebid": {
+ "type": "banner"
+ }
+ }
+ }
+ ],
+ "seat": "smoot"
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "bids": [
+ {
+ "bid": {
+ "id": "test_bid_id",
+ "impid": "test-imp-id",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 1,
+ "w": 300,
+ "h": 250,
+ "ext": {
+ "prebid": {
+ "type": "banner"
+ }
+ }
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/smoot/smoottest/exemplary/multi-format.json b/adapters/smoot/smoottest/exemplary/multi-format.json
new file mode 100644
index 00000000000..b928d00429f
--- /dev/null
+++ b/adapters/smoot/smoottest/exemplary/multi-format.json
@@ -0,0 +1,95 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ },
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "tagid": "test",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "video": {
+ "mimes": ["video/mp4"],
+ "protocols": [2, 5],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "endpointId": "test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://fake.test.io/pserver",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "tagid": "test",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "video": {
+ "mimes": ["video/mp4"],
+ "protocols": [2, 5],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "endpointId": "test",
+ "type": "network"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ }
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 204
+ }
+ }
+ ],
+ "expectedBidResponses": []
+}
diff --git a/adapters/smoot/smoottest/exemplary/multi-imp.json b/adapters/smoot/smoottest/exemplary/multi-imp.json
new file mode 100644
index 00000000000..e47253c35d8
--- /dev/null
+++ b/adapters/smoot/smoottest/exemplary/multi-imp.json
@@ -0,0 +1,253 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ },
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id1",
+ "tagid": "test",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "endpointId": "test"
+ }
+ }
+ },
+ {
+ "id": "test-imp-id2",
+ "tagid": "test",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "endpointId": "test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://fake.test.io/pserver",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id1",
+ "tagid": "test",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "endpointId": "test",
+ "type": "network"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ }
+ },
+ "impIDs": ["test-imp-id1"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test_bid_id",
+ "impid": "test-imp-id1",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 1,
+ "w": 300,
+ "h": 250,
+ "ext": {
+ "prebid": {
+ "type": "banner"
+ }
+ }
+ }
+ ],
+ "seat": "smoot"
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ },
+ {
+ "expectedRequest": {
+ "uri": "https://fake.test.io/pserver",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id2",
+ "tagid": "test",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "endpointId": "test",
+ "type": "network"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ }
+ },
+ "impIDs": ["test-imp-id2"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test_bid_id",
+ "impid": "test-imp-id2",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 1,
+ "w": 300,
+ "h": 250,
+ "ext": {
+ "prebid": {
+ "type": "banner"
+ }
+ }
+ }
+ ],
+ "seat": "smoot"
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "bids": [
+ {
+ "bid": {
+ "id": "test_bid_id",
+ "impid": "test-imp-id1",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 1,
+ "w": 300,
+ "h": 250,
+ "ext": {
+ "prebid": {
+ "type": "banner"
+ }
+ }
+ },
+ "type": "banner"
+ }
+ ]
+ },
+ {
+ "bids": [
+ {
+ "bid": {
+ "id": "test_bid_id",
+ "impid": "test-imp-id2",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 1,
+ "w": 300,
+ "h": 250,
+ "ext": {
+ "prebid": {
+ "type": "banner"
+ }
+ }
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/smoot/smoottest/exemplary/simple-app-banner.json b/adapters/smoot/smoottest/exemplary/simple-app-banner.json
new file mode 100644
index 00000000000..14f1d9e4b9d
--- /dev/null
+++ b/adapters/smoot/smoottest/exemplary/simple-app-banner.json
@@ -0,0 +1,136 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ },
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "tagid": "test",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://fake.test.io/pserver",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "tagid": "test",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test",
+ "type": "publisher"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ }
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test_bid_id",
+ "impid": "test-imp-id",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 1,
+ "w": 300,
+ "h": 250,
+ "ext": {
+ "prebid": {
+ "type": "banner"
+ }
+ }
+ }
+ ],
+ "seat": "smoot"
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "bids": [
+ {
+ "bid": {
+ "id": "test_bid_id",
+ "impid": "test-imp-id",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 1,
+ "w": 300,
+ "h": 250,
+ "ext": {
+ "prebid": {
+ "type": "banner"
+ }
+ }
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/smoot/smoottest/exemplary/simple-native.json b/adapters/smoot/smoottest/exemplary/simple-native.json
new file mode 100644
index 00000000000..26cdbf45efb
--- /dev/null
+++ b/adapters/smoot/smoottest/exemplary/simple-native.json
@@ -0,0 +1,120 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ },
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "tagid": "test",
+ "native": {
+ "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":492,\"hmin\":328,\"type\":3,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":4,\"required\":0,\"data\":{\"type\":6}},{\"id\":5,\"required\":0,\"data\":{\"type\":7}},{\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+ "ver": "1.1"
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://fake.test.io/pserver",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "tagid": "test",
+ "native": {
+ "request": "{\"ver\":\"1.1\",\"layout\":1,\"adunit\":2,\"plcmtcnt\":6,\"plcmttype\":4,\"assets\":[{\"id\":1,\"required\":1,\"title\":{\"len\":75}},{\"id\":2,\"required\":1,\"img\":{\"wmin\":492,\"hmin\":328,\"type\":3,\"mimes\":[\"image/jpeg\",\"image/jpg\",\"image/png\"]}},{\"id\":4,\"required\":0,\"data\":{\"type\":6}},{\"id\":5,\"required\":0,\"data\":{\"type\":7}},{\"id\":6,\"required\":0,\"data\":{\"type\":1,\"len\":20}}]}",
+ "ver": "1.1"
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test",
+ "type": "publisher"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ }
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test_bid_id",
+ "impid": "test-imp-id",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 4,
+ "w": 300,
+ "h": 250,
+ "ext": {
+ "prebid": {
+ "type": "native"
+ }
+ }
+ }
+ ],
+ "seat": "smoot"
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "bids": [
+ {
+ "bid": {
+ "id": "test_bid_id",
+ "impid": "test-imp-id",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 4,
+ "w": 300,
+ "h": 250,
+ "ext": {
+ "prebid": {
+ "type": "native"
+ }
+ }
+ },
+ "type": "native"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/smoot/smoottest/exemplary/simple-video.json b/adapters/smoot/smoottest/exemplary/simple-video.json
new file mode 100644
index 00000000000..cef316585da
--- /dev/null
+++ b/adapters/smoot/smoottest/exemplary/simple-video.json
@@ -0,0 +1,121 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ },
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "tagid": "test",
+ "video": {
+ "mimes": ["video/mp4"],
+ "protocols": [2, 5],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test"
+ }
+ }
+ }
+ ]
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://fake.test.io/pserver",
+ "body": {
+ "id": "test-request-id",
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "iPad"
+ },
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "tagid": "test",
+ "video": {
+ "mimes": ["video/mp4"],
+ "protocols": [2, 5],
+ "w": 1024,
+ "h": 576
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test",
+ "type": "publisher"
+ }
+ }
+ }
+ ]
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test_bid_id",
+ "impid": "test-imp-id",
+ "price": 0.27543,
+ "adm": "00:01:00",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 2,
+ "ext": {
+ "prebid": {
+ "type": "video"
+ }
+ }
+ }
+ ],
+ "seat": "smoot"
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "currency": "USD",
+ "bids": [
+ {
+ "bid": {
+ "id": "test_bid_id",
+ "impid": "test-imp-id",
+ "price": 0.27543,
+ "adm": "00:01:00",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 2,
+ "ext": {
+ "prebid": {
+ "type": "video"
+ }
+ }
+ },
+ "type": "video"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/smoot/smoottest/exemplary/simple-web-banner.json b/adapters/smoot/smoottest/exemplary/simple-web-banner.json
new file mode 100644
index 00000000000..6494b15ef37
--- /dev/null
+++ b/adapters/smoot/smoottest/exemplary/simple-web-banner.json
@@ -0,0 +1,136 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "tagid": "test",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test"
+ }
+ }
+ }
+ ],
+ "site": {
+ "id": "1",
+ "domain": "test.com"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Ubuntu"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://fake.test.io/pserver",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "tagid": "test",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test",
+ "type": "publisher"
+ }
+ }
+ }
+ ],
+ "site": {
+ "id": "1",
+ "domain": "test.com"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ua": "Ubuntu"
+ }
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test_bid_id",
+ "impid": "test-imp-id",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 1,
+ "w": 468,
+ "h": 60,
+ "ext": {
+ "prebid": {
+ "type": "banner"
+ }
+ }
+ }
+ ],
+ "seat": "smoot"
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedBidResponses": [
+ {
+ "bids": [
+ {
+ "bid": {
+ "id": "test_bid_id",
+ "impid": "test-imp-id",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "mtype": 1,
+ "w": 468,
+ "h": 60,
+ "ext": {
+ "prebid": {
+ "type": "banner"
+ }
+ }
+ },
+ "type": "banner"
+ }
+ ]
+ }
+ ]
+}
diff --git a/adapters/smoot/smoottest/supplemental/bad-media-type.json b/adapters/smoot/smoottest/supplemental/bad-media-type.json
new file mode 100644
index 00000000000..4fb67fe5187
--- /dev/null
+++ b/adapters/smoot/smoottest/supplemental/bad-media-type.json
@@ -0,0 +1,85 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "ext": {
+ "bidder": {
+ "placementId": "test"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://fake.test.io/pserver",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "ext": {
+ "bidder": {
+ "placementId": "test",
+ "type": "publisher"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+ }
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": {
+ "id": "test-request-id",
+ "seatbid": [
+ {
+ "bid": [
+ {
+ "id": "test_bid_id",
+ "impid": "test-imp-id",
+ "price": 0.27543,
+ "adm": "",
+ "cid": "test_cid",
+ "crid": "test_crid",
+ "dealid": "test_dealid",
+ "w": 300,
+ "h": 250,
+ "ext": {}
+ }
+ ],
+ "seat": "smoot"
+ }
+ ],
+ "cur": "USD"
+ }
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "could not define media type for impression: test-imp-id",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/smoot/smoottest/supplemental/bad-response.json b/adapters/smoot/smoottest/supplemental/bad-response.json
new file mode 100644
index 00000000000..74b209d4ca8
--- /dev/null
+++ b/adapters/smoot/smoottest/supplemental/bad-response.json
@@ -0,0 +1,87 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://fake.test.io/pserver",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test",
+ "type": "publisher"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+ }
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 200,
+ "body": ""
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "expect { or n, but found \"",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/smoot/smoottest/supplemental/invalid-bidder.json b/adapters/smoot/smoottest/supplemental/invalid-bidder.json
new file mode 100644
index 00000000000..27588a6c340
--- /dev/null
+++ b/adapters/smoot/smoottest/supplemental/invalid-bidder.json
@@ -0,0 +1,31 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": {
+ "bidder": 999
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ }
+ },
+ "expectedMakeRequestsErrors": [
+ {
+ "value": "expect { or n, but found 9",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/smoot/smoottest/supplemental/invalid-ext.json b/adapters/smoot/smoottest/supplemental/invalid-ext.json
new file mode 100644
index 00000000000..237eb4d9e27
--- /dev/null
+++ b/adapters/smoot/smoottest/supplemental/invalid-ext.json
@@ -0,0 +1,29 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ }
+ ]
+ },
+ "ext": 123
+ }
+ ]
+ },
+ "expectedMakeRequestsErrors": [
+ {
+ "value": "expect { or n, but found 1",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adapters/smoot/smoottest/supplemental/status-204.json b/adapters/smoot/smoottest/supplemental/status-204.json
new file mode 100644
index 00000000000..4387effb5d5
--- /dev/null
+++ b/adapters/smoot/smoottest/supplemental/status-204.json
@@ -0,0 +1,82 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://fake.test.io/pserver",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test",
+ "type": "publisher"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+ }
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 204,
+ "body": {}
+ }
+ }
+ ],
+ "expectedBidResponses": []
+}
diff --git a/adapters/smoot/smoottest/supplemental/status-not-200.json b/adapters/smoot/smoottest/supplemental/status-not-200.json
new file mode 100644
index 00000000000..bb4b627bf77
--- /dev/null
+++ b/adapters/smoot/smoottest/supplemental/status-not-200.json
@@ -0,0 +1,87 @@
+{
+ "mockBidRequest": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+ }
+ },
+ "httpCalls": [
+ {
+ "expectedRequest": {
+ "uri": "https://fake.test.io/pserver",
+ "body": {
+ "id": "test-request-id",
+ "imp": [
+ {
+ "id": "test-imp-id",
+ "banner": {
+ "format": [
+ {
+ "w": 300,
+ "h": 250
+ },
+ {
+ "w": 300,
+ "h": 600
+ }
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "placementId": "test",
+ "type": "publisher"
+ }
+ }
+ }
+ ],
+ "app": {
+ "id": "1",
+ "bundle": "com.wls.testwlsapplication"
+ },
+ "device": {
+ "ip": "123.123.123.123",
+ "ifa": "sdjfksdf-dfsds-dsdg-dsgg"
+ }
+ },
+ "impIDs": ["test-imp-id"]
+ },
+ "mockResponse": {
+ "status": 404,
+ "body": {}
+ }
+ }
+ ],
+ "expectedMakeBidsErrors": [
+ {
+ "value": "Unexpected status code: 404. Run with request.debug = 1 for more info",
+ "comparison": "literal"
+ }
+ ]
+}
diff --git a/adservertargeting/requestlookup.go b/adservertargeting/requestlookup.go
index 31c1a9d939e..68299716515 100644
--- a/adservertargeting/requestlookup.go
+++ b/adservertargeting/requestlookup.go
@@ -7,7 +7,6 @@ import (
"strings"
"github.com/buger/jsonparser"
- "github.com/pkg/errors"
"github.com/prebid/prebid-server/v3/openrtb_ext"
)
@@ -93,7 +92,7 @@ func getValueFromQueryParam(path string, queryParams url.Values) (json.RawMessag
if val != "" {
return json.RawMessage(val), nil
} else {
- return nil, errors.Errorf("value not found for path: %s", path)
+ return nil, fmt.Errorf("value not found for path: %s", path)
}
}
return nil, nil
diff --git a/adservertargeting/respdataprocessor.go b/adservertargeting/respdataprocessor.go
index 4b18d64b44b..c3ad25aa62a 100644
--- a/adservertargeting/respdataprocessor.go
+++ b/adservertargeting/respdataprocessor.go
@@ -5,7 +5,6 @@ import (
"fmt"
"strings"
- "github.com/pkg/errors"
"github.com/prebid/openrtb/v20/openrtb2"
"github.com/prebid/prebid-server/v3/openrtb_ext"
"github.com/prebid/prebid-server/v3/util/jsonutil"
@@ -170,7 +169,7 @@ func getRespData(bidderResp *openrtb2.BidResponse, field string) (string, error)
return fmt.Sprint(bidderResp.NBR.Val()), nil
default:
- return "", errors.Errorf("key not found for field in bid response: %s", field)
+ return "", fmt.Errorf("key not found for field in bid response: %s", field)
}
}
diff --git a/adservertargeting/utils.go b/adservertargeting/utils.go
index dd70bf1b667..7cd4a826b98 100644
--- a/adservertargeting/utils.go
+++ b/adservertargeting/utils.go
@@ -1,10 +1,10 @@
package adservertargeting
import (
+ "fmt"
"strings"
"github.com/buger/jsonparser"
- "github.com/pkg/errors"
"github.com/prebid/prebid-server/v3/errortypes"
"github.com/prebid/prebid-server/v3/openrtb_ext"
)
@@ -40,12 +40,12 @@ func typedLookup(data []byte, path string, keys ...string) ([]byte, error) {
if err != nil && err != jsonparser.KeyPathNotFoundError {
return nil, err
} else if err != nil && err == jsonparser.KeyPathNotFoundError {
- return nil, errors.Errorf("value not found for path: %s", path)
+ return nil, fmt.Errorf("value not found for path: %s", path)
}
if verifyType(dataType) {
return value, nil
}
- return nil, errors.Errorf("incorrect value type for path: %s, value can only be string or number", path)
+ return nil, fmt.Errorf("incorrect value type for path: %s, value can only be string or number", path)
}
func verifyType(dataType jsonparser.ValueType) bool {
diff --git a/config/account.go b/config/account.go
index a54da82d212..310fd021dce 100644
--- a/config/account.go
+++ b/config/account.go
@@ -159,6 +159,7 @@ type AccountGDPR struct {
PurposeConfigs map[consentconstants.Purpose]*AccountGDPRPurpose
PurposeOneTreatment AccountGDPRPurposeOneTreatment `mapstructure:"purpose_one_treatment" json:"purpose_one_treatment"`
SpecialFeature1 AccountGDPRSpecialFeature `mapstructure:"special_feature1" json:"special_feature1"`
+ EEACountries []string `mapstructure:"eea_countries" json:"eea_countries"`
}
// EnabledForChannelType indicates whether GDPR is turned on at the account level for the specified channel type
diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go
index 41dd58ff4d2..1c2907a2454 100755
--- a/exchange/adapter_builders.go
+++ b/exchange/adapter_builders.go
@@ -136,6 +136,7 @@ import (
"github.com/prebid/prebid-server/v3/adapters/lockerdome"
"github.com/prebid/prebid-server/v3/adapters/logan"
"github.com/prebid/prebid-server/v3/adapters/logicad"
+ "github.com/prebid/prebid-server/v3/adapters/loopme"
"github.com/prebid/prebid-server/v3/adapters/loyal"
"github.com/prebid/prebid-server/v3/adapters/lunamedia"
"github.com/prebid/prebid-server/v3/adapters/mabidder"
@@ -196,6 +197,7 @@ import (
"github.com/prebid/prebid-server/v3/adapters/smartx"
"github.com/prebid/prebid-server/v3/adapters/smartyads"
"github.com/prebid/prebid-server/v3/adapters/smilewanted"
+ "github.com/prebid/prebid-server/v3/adapters/smoot"
"github.com/prebid/prebid-server/v3/adapters/smrtconnect"
"github.com/prebid/prebid-server/v3/adapters/sonobi"
"github.com/prebid/prebid-server/v3/adapters/sovrn"
@@ -381,6 +383,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
openrtb_ext.BidderLockerDome: lockerdome.Builder,
openrtb_ext.BidderLogan: logan.Builder,
openrtb_ext.BidderLogicad: logicad.Builder,
+ openrtb_ext.BidderLoopme: loopme.Builder,
openrtb_ext.BidderLoyal: loyal.Builder,
openrtb_ext.BidderLunaMedia: lunamedia.Builder,
openrtb_ext.BidderMabidder: mabidder.Builder,
@@ -442,6 +445,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder {
openrtb_ext.BidderSmartx: smartx.Builder,
openrtb_ext.BidderSmartyAds: smartyads.Builder,
openrtb_ext.BidderSmileWanted: smilewanted.Builder,
+ openrtb_ext.BidderSmoot: smoot.Builder,
openrtb_ext.BidderSmrtconnect: smrtconnect.Builder,
openrtb_ext.BidderSonobi: sonobi.Builder,
openrtb_ext.BidderSovrn: sovrn.Builder,
diff --git a/exchange/exchange.go b/exchange/exchange.go
index 9ab91ee9ea3..daddf9debc2 100644
--- a/exchange/exchange.go
+++ b/exchange/exchange.go
@@ -316,8 +316,11 @@ func (e *exchange) HoldAuction(ctx context.Context, r *AuctionRequest, debugLog
recordImpMetrics(r.BidRequestWrapper, e.me)
+ // Retrieve EEA countries configuration from either host or account settings
+ eeaCountries := selectEEACountries(e.privacyConfig.GDPR.EEACountries, r.Account.GDPR.EEACountries)
+
// Make our best guess if GDPR applies
- gdprDefaultValue := e.parseGDPRDefaultValue(r.BidRequestWrapper)
+ gdprDefaultValue := e.parseGDPRDefaultValue(r.BidRequestWrapper, eeaCountries)
gdprSignal, err := getGDPR(r.BidRequestWrapper)
if err != nil {
return nil, err
@@ -571,7 +574,7 @@ func buildMultiBidMap(prebid *openrtb_ext.ExtRequestPrebid) map[string]openrtb_e
return multiBidMap
}
-func (e *exchange) parseGDPRDefaultValue(r *openrtb_ext.RequestWrapper) gdpr.Signal {
+func (e *exchange) parseGDPRDefaultValue(r *openrtb_ext.RequestWrapper, eeaCountries []string) gdpr.Signal {
gdprDefaultValue := e.gdprDefaultValue
var geo *openrtb2.Geo
@@ -582,12 +585,11 @@ func (e *exchange) parseGDPRDefaultValue(r *openrtb_ext.RequestWrapper) gdpr.Sig
}
if geo != nil {
- // If we have a country set, and it is on the list, we assume GDPR applies if not set on the request.
- // Otherwise we assume it does not apply as long as it appears "valid" (is 3 characters long).
- if _, found := e.privacyConfig.GDPR.EEACountriesMap[strings.ToUpper(geo.Country)]; found {
+ // If the country is in the EEA list, GDPR applies.
+ // Otherwise, if the country code is properly formatted (3 characters), GDPR does not apply.
+ if isEEACountry(geo.Country, eeaCountries) {
gdprDefaultValue = gdpr.SignalYes
} else if len(geo.Country) == 3 {
- // The country field is formatted properly as a three character country code
gdprDefaultValue = gdpr.SignalNo
}
}
@@ -1622,3 +1624,17 @@ func setSeatNonBid(bidResponseExt *openrtb_ext.ExtBidResponse, seatNonBidBuilder
bidResponseExt.Prebid.SeatNonBid = seatNonBidBuilder.Slice()
return bidResponseExt
}
+
+func isEEACountry(country string, eeaCountries []string) bool {
+ if len(eeaCountries) == 0 {
+ return false
+ }
+
+ country = strings.ToUpper(country)
+ for _, c := range eeaCountries {
+ if strings.ToUpper(c) == country {
+ return true
+ }
+ }
+ return false
+}
diff --git a/exchange/exchange_test.go b/exchange/exchange_test.go
index 87b53b101e0..7edbec64d7e 100644
--- a/exchange/exchange_test.go
+++ b/exchange/exchange_test.go
@@ -2114,9 +2114,10 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
}
var s struct{}
- eeac := make(map[string]struct{})
- for _, c := range []string{"FIN", "FRA", "GUF"} {
- eeac[c] = s
+ eeacMap := make(map[string]struct{})
+ eeac := []string{"FIN", "FRA", "GUF"}
+ for _, c := range eeac {
+ eeacMap[c] = s
}
var gdprDefaultValue string
@@ -2136,7 +2137,8 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
GDPR: config.GDPR{
Enabled: spec.GDPREnabled,
DefaultValue: gdprDefaultValue,
- EEACountriesMap: eeac,
+ EEACountries: eeac,
+ EEACountriesMap: eeacMap,
TCF2: config.TCF2{
Enabled: spec.GDPREnabled,
},
@@ -2192,6 +2194,7 @@ func runSpec(t *testing.T, filename string, spec *exchangeSpec) {
PriceFloors: config.AccountPriceFloors{Enabled: spec.AccountFloorsEnabled, EnforceDealFloors: spec.AccountEnforceDealFloors},
Privacy: spec.AccountPrivacy,
Validations: spec.AccountConfigBidValidation,
+ GDPR: config.AccountGDPR{EEACountries: spec.AccountEEACountries},
},
UserSyncs: mockIdFetcher(spec.IncomingRequest.Usersyncs),
ImpExtInfoMap: impExtInfoMap,
@@ -5518,6 +5521,7 @@ type exchangeSpec struct {
Server exchangeServer `json:"server,omitempty"`
AccountPrivacy config.AccountPrivacy `json:"accountPrivacy,omitempty"`
ORTBVersion map[string]string `json:"ortbversion"`
+ AccountEEACountries []string `json:"account_eea_countries"`
}
type multiBidSpec struct {
@@ -6351,6 +6355,61 @@ func TestBidsToUpdate(t *testing.T) {
}
}
+func TestIsEEACountry(t *testing.T) {
+ eeaCountries := []string{"FRA", "DEU", "ITA", "ESP", "NLD"}
+
+ tests := []struct {
+ name string
+ country string
+ eeaList []string
+ expected bool
+ }{
+ {
+ name: "Country_in_EEA",
+ country: "FRA",
+ eeaList: eeaCountries,
+ expected: true,
+ },
+ {
+ name: "Country_in_EEA_lowercase",
+ country: "fra",
+ eeaList: eeaCountries,
+ expected: true,
+ },
+ {
+ name: "Country_not_in_EEA",
+ country: "USA",
+ eeaList: eeaCountries,
+ expected: false,
+ },
+ {
+ name: "Empty_country_string",
+ country: "",
+ eeaList: eeaCountries,
+ expected: false,
+ },
+ {
+ name: "EEA_list_is_empty",
+ country: "FRA",
+ eeaList: []string{},
+ expected: false,
+ },
+ {
+ name: "EEA_list_is_nil",
+ country: "FRA",
+ eeaList: nil,
+ expected: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ result := isEEACountry(tt.country, tt.eeaList)
+ assert.Equal(t, tt.expected, result)
+ })
+ }
+}
+
type mockRequestValidator struct {
errors []error
}
diff --git a/exchange/exchangetest/gdpr-geo-eu-on-with-account-eea-countries.json b/exchange/exchangetest/gdpr-geo-eu-on-with-account-eea-countries.json
new file mode 100644
index 00000000000..a9b469fb11a
--- /dev/null
+++ b/exchange/exchangetest/gdpr-geo-eu-on-with-account-eea-countries.json
@@ -0,0 +1,75 @@
+{
+ "assume_gdpr_applies": true,
+ "account_eea_countries": ["POL"],
+ "gdpr_enabled": true,
+ "incomingRequest": {
+ "ortbRequest": {
+ "id": "some-request-id",
+ "site": {
+ "page": "test.somepage.com"
+ },
+ "imp": [
+ {
+ "id": "my-imp-id",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "prebid": {
+ "bidder": {
+ "appnexus": {
+ "placementId": 1
+ }
+ }
+ }
+ }
+ }
+ ],
+ "user": {
+ "buyeruid": "some-buyer-id",
+ "geo": {
+ "country": "POL"
+ }
+ }
+ }
+ },
+ "outgoingRequests": {
+ "appnexus": {
+ "expectRequest": {
+ "ortbRequest": {
+ "id": "some-request-id",
+ "site": {
+ "page": "test.somepage.com"
+ },
+ "imp": [
+ {
+ "id": "my-imp-id",
+ "video": {
+ "mimes": [
+ "video/mp4"
+ ]
+ },
+ "ext": {
+ "bidder": {
+ "placementId": 1
+ }
+ }
+ }
+ ],
+ "user": {
+ "geo": {
+ "country": "POL"
+ }
+ }
+ }
+ },
+ "mockResponse": {
+ "errors": [
+ "appnexus-error"
+ ]
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/exchange/gdpr.go b/exchange/gdpr.go
index 866f33baed2..8c58a77b468 100644
--- a/exchange/gdpr.go
+++ b/exchange/gdpr.go
@@ -40,3 +40,12 @@ func enforceGDPR(signal gdpr.Signal, defaultValue gdpr.Signal, channelEnabled bo
gdprApplies := signal == gdpr.SignalYes || (signal == gdpr.SignalAmbiguous && defaultValue == gdpr.SignalYes)
return gdprApplies && channelEnabled
}
+
+// SelectEEACountries selects the EEA countries based on host and account configurations.
+// Account-level configuration takes precedence over the host-level configuration.
+func selectEEACountries(hostEEACountries []string, accountEEACountries []string) []string {
+ if accountEEACountries != nil {
+ return accountEEACountries
+ }
+ return hostEEACountries
+}
diff --git a/exchange/gdpr_test.go b/exchange/gdpr_test.go
index b2d175c2ad0..1ef69d498b1 100644
--- a/exchange/gdpr_test.go
+++ b/exchange/gdpr_test.go
@@ -148,6 +148,65 @@ func TestGetConsent(t *testing.T) {
}
}
+func TestSelectEEACountries(t *testing.T) {
+ tests := []struct {
+ description string
+ hostEEACountries []string
+ accountEEACountries []string
+ expected []string
+ }{
+ {
+ description: "Account_EEA_countries_provided",
+ hostEEACountries: []string{"UK", "DE"},
+ accountEEACountries: []string{"FR", "IT"},
+ expected: []string{"FR", "IT"},
+ },
+ {
+ description: "Account_is_nil",
+ hostEEACountries: []string{"UK"},
+ accountEEACountries: nil,
+ expected: []string{"UK"},
+ },
+ {
+ description: "Both_nil",
+ hostEEACountries: nil,
+ accountEEACountries: nil,
+ expected: nil,
+ },
+ {
+ description: "Account_is_empty_slice",
+ hostEEACountries: []string{"UK"},
+ accountEEACountries: []string{},
+ expected: []string{},
+ },
+ {
+ description: "Host_is_nil",
+ hostEEACountries: nil,
+ accountEEACountries: []string{"DE"},
+ expected: []string{"DE"},
+ },
+ {
+ description: "Host_and_account_both_non-nil",
+ hostEEACountries: []string{"UK"},
+ accountEEACountries: []string{"FR"},
+ expected: []string{"FR"},
+ },
+ {
+ description: "Host_is_empty_slice,_account_is_nil",
+ hostEEACountries: []string{},
+ accountEEACountries: nil,
+ expected: []string{},
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.description, func(t *testing.T) {
+ result := selectEEACountries(tt.hostEEACountries, tt.accountEEACountries)
+ assert.Equal(t, tt.expected, result)
+ })
+ }
+}
+
var upsv1Section mockGPPSection = mockGPPSection{sectionID: 6, value: "1YNY"}
var tcf1Section mockGPPSection = mockGPPSection{sectionID: 2, value: "BOS2bx5OS2bx5ABABBAAABoAAAAAFA"}
diff --git a/go.mod b/go.mod
index c7069e32b0f..ef05f217116 100644
--- a/go.mod
+++ b/go.mod
@@ -25,7 +25,6 @@ require (
github.com/lib/pq v1.10.4
github.com/mitchellh/copystructure v1.2.0
github.com/modern-go/reflect2 v1.0.2
- github.com/pkg/errors v0.9.1
github.com/prebid/go-gdpr v1.12.0
github.com/prebid/go-gpp v0.2.0
github.com/prebid/openrtb/v20 v20.3.0
@@ -63,6 +62,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.1 // indirect
+ github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/common v0.32.1 // indirect
github.com/prometheus/procfs v0.7.3 // indirect
diff --git a/modules/fiftyonedegrees/devicedetection/config.go b/modules/fiftyonedegrees/devicedetection/config.go
index a5c302bcff5..7a29812d35a 100644
--- a/modules/fiftyonedegrees/devicedetection/config.go
+++ b/modules/fiftyonedegrees/devicedetection/config.go
@@ -2,11 +2,10 @@ package devicedetection
import (
"encoding/json"
+ "fmt"
"os"
"github.com/51Degrees/device-detection-go/v4/dd"
- "github.com/pkg/errors"
-
"github.com/prebid/prebid-server/v3/util/jsonutil"
)
@@ -65,7 +64,7 @@ func (c *config) getPerformanceProfile() dd.PerformanceProfile {
func parseConfig(data json.RawMessage) (config, error) {
var cfg config
if err := jsonutil.UnmarshalValid(data, &cfg); err != nil {
- return cfg, errors.Wrap(err, "failed to parse config")
+ return cfg, fmt.Errorf("failed to parse config: %w", err)
}
return cfg, nil
}
@@ -73,7 +72,7 @@ func parseConfig(data json.RawMessage) (config, error) {
func validateConfig(cfg config) error {
_, err := os.Stat(cfg.DataFile.Path)
if err != nil {
- return errors.Wrap(err, "error opening hash file path")
+ return fmt.Errorf("error opening hash file path: %w", err)
}
return nil
diff --git a/modules/fiftyonedegrees/devicedetection/device_detector.go b/modules/fiftyonedegrees/devicedetection/device_detector.go
index 8369d343d34..5c23deb9637 100644
--- a/modules/fiftyonedegrees/devicedetection/device_detector.go
+++ b/modules/fiftyonedegrees/devicedetection/device_detector.go
@@ -1,9 +1,10 @@
package devicedetection
import (
+ "fmt"
+
"github.com/51Degrees/device-detection-go/v4/dd"
"github.com/51Degrees/device-detection-go/v4/onpremise"
- "github.com/pkg/errors"
)
type engine interface {
@@ -28,7 +29,7 @@ func newDeviceDetector(cfg *dd.ConfigHash, moduleConfig *config) (*defaultDevice
engineOptions...,
)
if err != nil {
- return nil, errors.Wrap(err, "Failed to create onpremise engine.")
+ return nil, fmt.Errorf("failed to create onpremise engine: %w", err)
}
deviceDetector := &defaultDeviceDetector{
@@ -147,7 +148,7 @@ func (x defaultDeviceDetector) getSupportedHeaders() []dd.EvidenceKey {
func (x defaultDeviceDetector) getDeviceInfo(evidence []onpremise.Evidence, ua string) (*deviceInfo, error) {
results, err := x.engine.Process(evidence)
if err != nil {
- return nil, errors.Wrap(err, "Failed to process evidence")
+ return nil, fmt.Errorf("failed to process evidence: %w", err)
}
defer results.Free()
diff --git a/modules/fiftyonedegrees/devicedetection/device_info_extractor.go b/modules/fiftyonedegrees/devicedetection/device_info_extractor.go
index 1c913e21696..89ff3714eea 100644
--- a/modules/fiftyonedegrees/devicedetection/device_info_extractor.go
+++ b/modules/fiftyonedegrees/devicedetection/device_info_extractor.go
@@ -1,10 +1,10 @@
package devicedetection
import (
+ "fmt"
"strconv"
"github.com/golang/glog"
- "github.com/pkg/errors"
)
// deviceInfoExtractor is a struct that contains the methods to extract device information
@@ -61,7 +61,7 @@ func (x deviceInfoExtractor) extract(results Results, ua string) (*deviceInfo, e
geoLocation, _ := strconv.ParseBool(x.getValue(results, deviceInfoGeoLocation))
deviceId, err := results.DeviceId()
if err != nil {
- return nil, errors.Wrap(err, "Failed to get device id.")
+ return nil, fmt.Errorf("failed to get device id: %w", err)
}
hardwareModel := x.getValue(results, deviceInfoHardwareModel)
hardwareFamily := x.getValue(results, deviceInfoHardwareFamily)
diff --git a/modules/fiftyonedegrees/devicedetection/evidence_extractor.go b/modules/fiftyonedegrees/devicedetection/evidence_extractor.go
index a99a921e75f..5bec2f4a79b 100644
--- a/modules/fiftyonedegrees/devicedetection/evidence_extractor.go
+++ b/modules/fiftyonedegrees/devicedetection/evidence_extractor.go
@@ -1,12 +1,12 @@
package devicedetection
import (
+ "errors"
+ "fmt"
"net/http"
- "github.com/51Degrees/device-detection-go/v4/onpremise"
- "github.com/pkg/errors"
-
"github.com/51Degrees/device-detection-go/v4/dd"
+ "github.com/51Degrees/device-detection-go/v4/onpremise"
"github.com/prebid/prebid-server/v3/hooks/hookstage"
)
@@ -62,11 +62,11 @@ func (x *defaultEvidenceExtractor) extract(ctx hookstage.ModuleContext) ([]onpre
suaStrings, err := x.getEvidenceStrings(ctx[evidenceFromSuaCtxKey])
if err != nil {
- return nil, "", errors.Wrap(err, "error extracting sua evidence")
+ return nil, "", fmt.Errorf("error extracting sua evidence: %w", err)
}
headerString, err := x.getEvidenceStrings(ctx[evidenceFromHeadersCtxKey])
if err != nil {
- return nil, "", errors.Wrap(err, "error extracting header evidence")
+ return nil, "", fmt.Errorf("error extracting header evidence: %w", err)
}
// Merge evidence from headers and SUA, sua has higher priority
diff --git a/modules/fiftyonedegrees/devicedetection/module.go b/modules/fiftyonedegrees/devicedetection/module.go
index 80eed36efda..78537f999d5 100644
--- a/modules/fiftyonedegrees/devicedetection/module.go
+++ b/modules/fiftyonedegrees/devicedetection/module.go
@@ -3,11 +3,11 @@ package devicedetection
import (
"context"
"encoding/json"
+ "fmt"
"net/http"
"github.com/51Degrees/device-detection-go/v4/dd"
"github.com/51Degrees/device-detection-go/v4/onpremise"
- "github.com/pkg/errors"
"github.com/prebid/prebid-server/v3/hooks/hookstage"
"github.com/prebid/prebid-server/v3/modules/moduledeps"
)
@@ -35,12 +35,12 @@ func configHashFromConfig(cfg *config) *dd.ConfigHash {
func Builder(rawConfig json.RawMessage, _ moduledeps.ModuleDeps) (interface{}, error) {
cfg, err := parseConfig(rawConfig)
if err != nil {
- return Module{}, errors.Wrap(err, "failed to parse config")
+ return Module{}, fmt.Errorf("failed to parse config: %w", err)
}
err = validateConfig(cfg)
if err != nil {
- return nil, errors.Wrap(err, "invalid config")
+ return nil, fmt.Errorf("invalid config: %w", err)
}
configHash := configHashFromConfig(&cfg)
@@ -50,7 +50,7 @@ func Builder(rawConfig json.RawMessage, _ moduledeps.ModuleDeps) (interface{}, e
&cfg,
)
if err != nil {
- return nil, errors.Wrap(err, "failed to create device detector")
+ return nil, fmt.Errorf("failed to create device detector: %w", err)
}
return Module{
diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go
index d647e6b9d24..44c0a306486 100644
--- a/openrtb_ext/bidders.go
+++ b/openrtb_ext/bidders.go
@@ -153,6 +153,7 @@ var coreBidderNames []BidderName = []BidderName{
BidderLockerDome,
BidderLogan,
BidderLogicad,
+ BidderLoopme,
BidderLoyal,
BidderLunaMedia,
BidderMabidder,
@@ -214,6 +215,7 @@ var coreBidderNames []BidderName = []BidderName{
BidderSmartx,
BidderSmartyAds,
BidderSmileWanted,
+ BidderSmoot,
BidderSmrtconnect,
BidderSonobi,
BidderSovrn,
@@ -503,6 +505,7 @@ const (
BidderLockerDome BidderName = "lockerdome"
BidderLogan BidderName = "logan"
BidderLogicad BidderName = "logicad"
+ BidderLoopme BidderName = "loopme"
BidderLoyal BidderName = "loyal"
BidderLunaMedia BidderName = "lunamedia"
BidderMabidder BidderName = "mabidder"
@@ -564,6 +567,7 @@ const (
BidderSmartx BidderName = "smartx"
BidderSmartyAds BidderName = "smartyads"
BidderSmileWanted BidderName = "smilewanted"
+ BidderSmoot BidderName = "smoot"
BidderSmrtconnect BidderName = "smrtconnect"
BidderSonobi BidderName = "sonobi"
BidderSovrn BidderName = "sovrn"
diff --git a/openrtb_ext/imp_connatix.go b/openrtb_ext/imp_connatix.go
index 18d7a713237..7649d6db143 100644
--- a/openrtb_ext/imp_connatix.go
+++ b/openrtb_ext/imp_connatix.go
@@ -1,5 +1,6 @@
package openrtb_ext
type ExtImpConnatix struct {
- PlacementId string `json:"placementId"`
+ PlacementId string `json:"placementId"`
+ ViewabilityPercentage float64 `json:"viewabilityPercentage"`
}
diff --git a/openrtb_ext/imp_loopme.go b/openrtb_ext/imp_loopme.go
new file mode 100644
index 00000000000..b99ffc2556f
--- /dev/null
+++ b/openrtb_ext/imp_loopme.go
@@ -0,0 +1,8 @@
+package openrtb_ext
+
+// ExtImpLoopme defines the contract for bidrequest.imp[i].ext.prebid.bidder.loopme
+type ExtImpLoopme struct {
+ PublisherId string `json:"publisherId"`
+ BundleId string `json:"bundleId"`
+ PlacementId string `json:"placementId"`
+}
diff --git a/openrtb_ext/imp_smoot.go b/openrtb_ext/imp_smoot.go
new file mode 100644
index 00000000000..3ecc4f4001b
--- /dev/null
+++ b/openrtb_ext/imp_smoot.go
@@ -0,0 +1,6 @@
+package openrtb_ext
+
+type ImpExtSmoot struct {
+ PlacementID string `json:"placementId"`
+ EndpointID string `json:"endpointId"`
+}
diff --git a/server/server.go b/server/server.go
index 9461f3f451d..257ccea8821 100644
--- a/server/server.go
+++ b/server/server.go
@@ -27,6 +27,7 @@ func Listen(cfg *config.Configuration, handler http.Handler, adminHandler http.H
stopAdmin := make(chan os.Signal)
stopMain := make(chan os.Signal)
stopPrometheus := make(chan os.Signal)
+ stopChannels := []chan<- os.Signal{stopMain}
done := make(chan struct{})
if cfg.UnixSocketEnable && len(cfg.UnixSocketName) > 0 { // start the unix_socket server if config enable-it.
@@ -54,6 +55,7 @@ func Listen(cfg *config.Configuration, handler http.Handler, adminHandler http.H
}
if cfg.Admin.Enabled {
+ stopChannels = append(stopChannels, stopAdmin)
adminServer := newAdminServer(cfg, adminHandler)
go shutdownAfterSignals(adminServer, stopAdmin, done)
@@ -70,6 +72,7 @@ func Listen(cfg *config.Configuration, handler http.Handler, adminHandler http.H
prometheusListener net.Listener
prometheusServer = newPrometheusServer(cfg, metrics)
)
+ stopChannels = append(stopChannels, stopPrometheus)
go shutdownAfterSignals(prometheusServer, stopPrometheus, done)
if prometheusListener, err = newTCPListener(prometheusServer.Addr, nil); err != nil {
glog.Errorf("Error listening for TCP connections on %s: %v for prometheus server", prometheusServer.Addr, err)
@@ -77,11 +80,10 @@ func Listen(cfg *config.Configuration, handler http.Handler, adminHandler http.H
}
go runServer(prometheusServer, "Prometheus", prometheusListener)
- wait(stopSignals, done, stopMain, stopAdmin, stopPrometheus)
- } else {
- wait(stopSignals, done, stopMain, stopAdmin)
}
+ wait(stopSignals, done, stopChannels...)
+
return
}
diff --git a/static/bidder-info/addigi.yaml b/static/bidder-info/addigi.yaml
new file mode 100644
index 00000000000..8514d5c5500
--- /dev/null
+++ b/static/bidder-info/addigi.yaml
@@ -0,0 +1,2 @@
+endpoint: "https://addigi-prebid.attekmi.com/pbserver/?seat={{.AccountID}}&token={{.SourceId}}"
+aliasOf: "smarthub"
diff --git a/static/bidder-info/adt.yaml b/static/bidder-info/adt.yaml
new file mode 100644
index 00000000000..29520d5e515
--- /dev/null
+++ b/static/bidder-info/adt.yaml
@@ -0,0 +1,4 @@
+aliasOf: "admatic"
+maintainer:
+ email: "publisher@adtarget.com.tr"
+gvlVendorID: 779
\ No newline at end of file
diff --git a/static/bidder-info/copper6ssp.yaml b/static/bidder-info/copper6ssp.yaml
index d1b3186dc2d..2f3ef14c768 100644
--- a/static/bidder-info/copper6ssp.yaml
+++ b/static/bidder-info/copper6ssp.yaml
@@ -1,4 +1,4 @@
-endpoint: "https://endpoint.copper6.com/"
+endpoint: "https://endpoint.copper6.com/pserver"
gvlVendorID: 1356
maintainer:
email: "info@copper6.com"
diff --git a/static/bidder-info/loopme.yaml b/static/bidder-info/loopme.yaml
new file mode 100644
index 00000000000..8a7e019382b
--- /dev/null
+++ b/static/bidder-info/loopme.yaml
@@ -0,0 +1,20 @@
+endpoint: http://prebid.loopmertb.com
+geoscope:
+ - global
+maintainer:
+ email: "prebid@loopme.com"
+gvlVendorID: 109
+capabilities:
+ app:
+ mediaTypes:
+ - banner
+ - video
+ - native
+ - audio
+ site:
+ mediaTypes:
+ - banner
+ - video
+ - native
+ - audio
+
diff --git a/static/bidder-info/pubrise.yaml b/static/bidder-info/pubrise.yaml
index fe5e6cd6d40..6d6b070ab38 100644
--- a/static/bidder-info/pubrise.yaml
+++ b/static/bidder-info/pubrise.yaml
@@ -1,4 +1,4 @@
-endpoint: "https://backend.pubrise.ai/"
+endpoint: "https://backend.pubrise.ai/pserver"
maintainer:
email: "prebid@pubrise.ai"
capabilities:
diff --git a/static/bidder-info/smoot.yaml b/static/bidder-info/smoot.yaml
new file mode 100644
index 00000000000..43a3381f6db
--- /dev/null
+++ b/static/bidder-info/smoot.yaml
@@ -0,0 +1,18 @@
+endpoint: 'https://endpoint1.smoot.ai/pserver'
+maintainer:
+ email: 'info@smoot.ai'
+capabilities:
+ site:
+ mediaTypes:
+ - banner
+ - video
+ - native
+ app:
+ mediaTypes:
+ - banner
+ - video
+ - native
+userSync:
+ redirect:
+ url: 'https://usync.smxconv.com/pbserver?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&redir={{.RedirectURL}}'
+ userMacro: '[UID]'
diff --git a/static/bidder-info/sspBC.yaml b/static/bidder-info/sspBC.yaml
index 572105d2407..97da8b497cd 100644
--- a/static/bidder-info/sspBC.yaml
+++ b/static/bidder-info/sspBC.yaml
@@ -1,4 +1,4 @@
-endpoint: "https://ssp.wp.pl/bidder/"
+endpoint: "https://ssp.wp.pl/v1/bidder/prebidserver"
maintainer:
email: "prebid-dev@grupawp.pl"
gvlVendorID: 676
diff --git a/static/bidder-params/connatix.json b/static/bidder-params/connatix.json
index 4003b0583b1..86ca65273f0 100644
--- a/static/bidder-params/connatix.json
+++ b/static/bidder-params/connatix.json
@@ -8,6 +8,10 @@
"type": "string",
"minLength": 1,
"description": "Placement ID"
+ },
+ "viewabilityPercentage": {
+ "type": "number",
+ "description": "Declared viewability percentage (values from 0 to 1, where 1 = 100%)"
}
},
"required": ["placementId"]
diff --git a/static/bidder-params/loopme.json b/static/bidder-params/loopme.json
new file mode 100644
index 00000000000..5ea22ec7ba5
--- /dev/null
+++ b/static/bidder-params/loopme.json
@@ -0,0 +1,24 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "Loopme Adapter Params",
+ "description": "A schema which validates params accepted by the Loopme adapter",
+ "type": "object",
+ "properties": {
+ "publisherId": {
+ "type": "string",
+ "description": "An id which identifies Loopme partner",
+ "minLength": 1
+ },
+ "bundleId": {
+ "type": "string",
+ "description": "An id which identifies app/site in Loopme",
+ "minLength": 1
+ },
+ "placementId": {
+ "type": "string",
+ "description": "A placement id in Loopme",
+ "minLength": 1
+ }
+ },
+ "required": ["publisherId"]
+}
diff --git a/static/bidder-params/smoot.json b/static/bidder-params/smoot.json
new file mode 100644
index 00000000000..46027757776
--- /dev/null
+++ b/static/bidder-params/smoot.json
@@ -0,0 +1,19 @@
+{
+ "$schema": "http://json-schema.org/draft-04/schema#",
+ "title": "Smoot Adapter Params",
+ "description": "A schema which validates params accepted by the Smoot adapter",
+ "type": "object",
+ "properties": {
+ "placementId": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Placement ID"
+ },
+ "endpointId": {
+ "type": "string",
+ "minLength": 1,
+ "description": "Endpoint ID"
+ }
+ },
+ "oneOf": [{ "required": ["placementId"] }, { "required": ["endpointId"] }]
+}