diff --git a/adapters/pangle/pangle.go b/adapters/pangle/pangle.go new file mode 100644 index 00000000000..ddaf3a67b4c --- /dev/null +++ b/adapters/pangle/pangle.go @@ -0,0 +1,201 @@ +package pangle + +import ( + "encoding/json" + "fmt" + "net/http" + + "github.com/mxmCherry/openrtb" + "github.com/prebid/prebid-server/adapters" + "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/errortypes" + "github.com/prebid/prebid-server/openrtb_ext" +) + +type pangleAdapter struct { + Endpoint string +} + +type wrappedExtImpBidder struct { + *adapters.ExtImpBidder + AdType int `json:"adtype,omitempty"` +} + +type pangleBidExt struct { + Pangle *bidExt `json:"pangle,omitempty"` +} + +type bidExt struct { + AdType *int `json:"adtype,omitempty"` +} + +/* Builder */ + +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter) (adapters.Bidder, error) { + bidder := &pangleAdapter{ + Endpoint: config.Endpoint, + } + + return bidder, nil +} + +/* MakeRequests */ + +func getAdType(imp openrtb.Imp, parsedImpExt *wrappedExtImpBidder) (adType int) { + adType = -1 + // video + if imp.Video != nil { + if parsedImpExt != nil && parsedImpExt.Prebid != nil && parsedImpExt.Prebid.IsRewardedInventory == 1 { + adType = 7 + return + } + if imp.Instl == 1 { + adType = 8 + return + } + } + // banner + if imp.Banner != nil { + if imp.Instl == 1 { + adType = 2 + } else { + adType = 1 + } + return + } + // native + if imp.Native != nil && len(imp.Native.Request) > 0 { + adType = 5 + return + } + + return +} + +func (pa *pangleAdapter) MakeRequests(request *openrtb.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var requests []*adapters.RequestData + var errs []error + + requestCopy := *request + for _, imp := range request.Imp { + var impExt wrappedExtImpBidder + if err := json.Unmarshal(imp.Ext, &impExt); err != nil { + errs = append(errs, fmt.Errorf("failed unmarshalling imp ext (err)%s", err.Error())) + continue + } + // detect and fill adtype + if adType := getAdType(imp, &impExt); adType == -1 { + errs = append(errs, fmt.Errorf("not a supported adtype")) + continue + } else { + impExt.AdType = adType + if newImpExt, err := json.Marshal(impExt); err == nil { + imp.Ext = newImpExt + } else { + errs = append(errs, fmt.Errorf("failed re-marshalling imp ext with adtype")) + continue + } + } + // for setting token + var bidderImpExt openrtb_ext.ImpExtPangle + if err := json.Unmarshal(impExt.Bidder, &bidderImpExt); err != nil { + errs = append(errs, fmt.Errorf("failed unmarshalling bidder imp ext (err)%s", err.Error())) + continue + } + + requestCopy.Imp = []openrtb.Imp{imp} + requestJSON, err := json.Marshal(requestCopy) + if err != nil { + errs = append(errs, err) + continue + } + + requestData := &adapters.RequestData{ + Method: "POST", + Uri: pa.Endpoint, + Body: requestJSON, + Headers: http.Header{ + "TOKEN": []string{bidderImpExt.Token}, + "Content-Type": []string{"application/json"}, + }, + } + requests = append(requests, requestData) + } + + return requests, errs +} + +/* MakeBids */ + +func getMediaTypeForBid(bid *openrtb.Bid) (openrtb_ext.BidType, error) { + if bid == nil { + return "", fmt.Errorf("the bid request object is nil") + } + + var bidExt pangleBidExt + if err := json.Unmarshal(bid.Ext, &bidExt); err != nil { + return "", fmt.Errorf("invalid bid ext") + } else if bidExt.Pangle == nil || bidExt.Pangle.AdType == nil { + return "", fmt.Errorf("missing pangleExt/adtype in bid ext") + } + + switch *bidExt.Pangle.AdType { + case 1: + return openrtb_ext.BidTypeBanner, nil + case 2: + return openrtb_ext.BidTypeBanner, nil + case 5: + return openrtb_ext.BidTypeNative, nil + case 7: + return openrtb_ext.BidTypeVideo, nil + case 8: + return openrtb_ext.BidTypeVideo, nil + } + + return "", fmt.Errorf("unrecognized adtype in response") +} + +func (p *pangleAdapter) MakeBids(request *openrtb.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { + if responseData.StatusCode == http.StatusNoContent { + return nil, nil + } + + if responseData.StatusCode == http.StatusBadRequest { + err := &errortypes.BadInput{ + Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.", + } + return nil, []error{err} + } + + if responseData.StatusCode != http.StatusOK { + err := &errortypes.BadServerResponse{ + Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode), + } + return nil, []error{err} + } + + var response openrtb.BidResponse + if err := json.Unmarshal(responseData.Body, &response); err != nil { + return nil, []error{err} + } + + var errs []error + bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) + bidResponse.Currency = response.Cur + for _, seatBid := range response.SeatBid { + for _, bid := range seatBid.Bid { + mediaType, err := getMediaTypeForBid(&bid) + if err != nil { + errs = append(errs, err) + continue + } + b := &adapters.TypedBid{ + Bid: &bid, + BidType: mediaType, + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + + return bidResponse, errs +} diff --git a/adapters/pangle/pangle_test.go b/adapters/pangle/pangle_test.go new file mode 100644 index 00000000000..268fc268e1d --- /dev/null +++ b/adapters/pangle/pangle_test.go @@ -0,0 +1,22 @@ +package pangle + +import ( + "testing" + + "github.com/prebid/prebid-server/adapters/adapterstest" + "github.com/prebid/prebid-server/config" + "github.com/prebid/prebid-server/openrtb_ext" +) + +func TestJsonSamples(t *testing.T) { + conf := config.Adapter{ + Endpoint: "https://pangle.io/api/get_ads", + ExtraAdapterInfo: "{\"token\": \"123\"}", + } + bidder, buildErr := Builder(openrtb_ext.BidderPangle, conf) + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "pangletest", bidder) +} diff --git a/adapters/pangle/pangletest/exemplary/app_banner.json b/adapters/pangle/pangletest/exemplary/app_banner.json new file mode 100644 index 00000000000..3fa410e5b7f --- /dev/null +++ b/adapters/pangle/pangletest/exemplary/app_banner.json @@ -0,0 +1,127 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "token": "123" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pangle.io/api/get_ads", + "headers": { + "Content-Type": [ + "application/json" + ], + "TOKEN": [ + "123" + ] + }, + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "adtype": 1, + "bidder": { + "token": "123" + }, + "prebid": null + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "seat-id", + "bid": [ + { + "id": "1", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 250, + "w": 300, + "ext": { + "pangle": { + "adtype": 1 + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "1", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 300, + "h": 250, + "ext": { + "pangle": { + "adtype": 1 + } + } + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/pangle/pangletest/exemplary/app_banner_instl.json b/adapters/pangle/pangletest/exemplary/app_banner_instl.json new file mode 100644 index 00000000000..585d155a057 --- /dev/null +++ b/adapters/pangle/pangletest/exemplary/app_banner_instl.json @@ -0,0 +1,129 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "instl": 1, + "ext": { + "bidder": { + "token": "123" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pangle.io/api/get_ads", + "headers": { + "Content-Type": [ + "application/json" + ], + "TOKEN": [ + "123" + ] + }, + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "instl": 1, + "ext": { + "adtype": 2, + "bidder": { + "token": "123" + }, + "prebid": null + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "seat-id", + "bid": [ + { + "id": "1", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 250, + "w": 300, + "ext": { + "pangle": { + "adtype": 2 + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "1", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 300, + "h": 250, + "ext": { + "pangle": { + "adtype": 2 + } + } + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/pangle/pangletest/exemplary/app_native.json b/adapters/pangle/pangletest/exemplary/app_native.json new file mode 100644 index 00000000000..2502baa4f9f --- /dev/null +++ b/adapters/pangle/pangletest/exemplary/app_native.json @@ -0,0 +1,117 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{}" + }, + "ext": { + "bidder": { + "token": "123" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pangle.io/api/get_ads", + "headers": { + "Content-Type": [ + "application/json" + ], + "TOKEN": [ + "123" + ] + }, + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "native": { + "request": "{}" + }, + "ext": { + "adtype": 5, + "bidder": { + "token": "123" + }, + "prebid": null + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "seat-id", + "bid": [ + { + "id": "1", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 250, + "w": 300, + "ext": { + "pangle": { + "adtype": 5 + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "1", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 300, + "h": 250, + "ext": { + "pangle": { + "adtype": 5 + } + } + }, + "type": "native" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/pangle/pangletest/exemplary/app_video_instl.json b/adapters/pangle/pangletest/exemplary/app_video_instl.json new file mode 100644 index 00000000000..d5af392fd91 --- /dev/null +++ b/adapters/pangle/pangletest/exemplary/app_video_instl.json @@ -0,0 +1,141 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 1024, + "h": 576, + "ext": { + "foo": "bar" + } + }, + "instl": 1, + "ext": { + "bidder": { + "token": "123" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pangle.io/api/get_ads", + "headers": { + "Content-Type": [ + "application/json" + ], + "TOKEN": [ + "123" + ] + }, + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 1024, + "h": 576, + "ext": { + "foo": "bar" + } + }, + "instl": 1, + "ext": { + "adtype": 8, + "bidder": { + "token": "123" + }, + "prebid": null + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "seat-id", + "bid": [ + { + "id": "1", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 250, + "w": 300, + "ext": { + "pangle": { + "adtype": 8 + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "1", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 300, + "h": 250, + "ext": { + "pangle": { + "adtype": 8 + } + } + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/pangle/pangletest/exemplary/app_video_rewarded.json b/adapters/pangle/pangletest/exemplary/app_video_rewarded.json new file mode 100644 index 00000000000..2dbf08b944e --- /dev/null +++ b/adapters/pangle/pangletest/exemplary/app_video_rewarded.json @@ -0,0 +1,146 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 1024, + "h": 576, + "ext": { + "foo": "bar" + } + }, + "ext": { + "prebid": { + "is_rewarded_inventory": 1 + }, + "bidder": { + "token": "123" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pangle.io/api/get_ads", + "headers": { + "Content-Type": [ + "application/json" + ], + "TOKEN": [ + "123" + ] + }, + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 1024, + "h": 576, + "ext": { + "foo": "bar" + } + }, + "ext": { + "adtype": 7, + "prebid": { + "bidder": null, + "is_rewarded_inventory": 1, + "storedrequest": null + }, + "bidder": { + "token": "123" + } + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "seat-id", + "bid": [ + { + "id": "1", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 250, + "w": 300, + "ext": { + "pangle": { + "adtype": 7 + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "1", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.5, + "adm": "some-test-ad", + "crid": "test-crid", + "w": 300, + "h": 250, + "ext": { + "pangle": { + "adtype": 7 + } + } + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/pangle/pangletest/params/race/banner.json b/adapters/pangle/pangletest/params/race/banner.json new file mode 100644 index 00000000000..afd1684b04e --- /dev/null +++ b/adapters/pangle/pangletest/params/race/banner.json @@ -0,0 +1,3 @@ +{ + "token": "123" +} \ No newline at end of file diff --git a/adapters/pangle/pangletest/params/race/native.json b/adapters/pangle/pangletest/params/race/native.json new file mode 100644 index 00000000000..afd1684b04e --- /dev/null +++ b/adapters/pangle/pangletest/params/race/native.json @@ -0,0 +1,3 @@ +{ + "token": "123" +} \ No newline at end of file diff --git a/adapters/pangle/pangletest/params/race/video.json b/adapters/pangle/pangletest/params/race/video.json new file mode 100644 index 00000000000..afd1684b04e --- /dev/null +++ b/adapters/pangle/pangletest/params/race/video.json @@ -0,0 +1,3 @@ +{ + "token": "123" +} \ No newline at end of file diff --git a/adapters/pangle/pangletest/supplemental/unrecognized_adtype.json b/adapters/pangle/pangletest/supplemental/unrecognized_adtype.json new file mode 100644 index 00000000000..451c0ed1909 --- /dev/null +++ b/adapters/pangle/pangletest/supplemental/unrecognized_adtype.json @@ -0,0 +1,110 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "bidder": { + "token": "123" + } + } + } + ] + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://pangle.io/api/get_ads", + "headers": { + "Content-Type": [ + "application/json" + ], + "TOKEN": [ + "123" + ] + }, + "body": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 300, + "h": 250 + } + ] + }, + "ext": { + "adtype": 1, + "bidder": { + "token": "123" + }, + "prebid": null + } + } + ] + } + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "seat-id", + "bid": [ + { + "id": "1", + "impid": "test-imp-id", + "adid": "11110126", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "test-crid", + "h": 250, + "w": 300, + "ext": { + "pangle": { + "adtype": 100 + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + ], + "expectedMakeBidsErrors": [ + { + "value": "unrecognized adtype in response", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/pangle/pangletest/supplemental/unsupported_adtype.json b/adapters/pangle/pangletest/supplemental/unsupported_adtype.json new file mode 100644 index 00000000000..bcd916ca322 --- /dev/null +++ b/adapters/pangle/pangletest/supplemental/unsupported_adtype.json @@ -0,0 +1,48 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "app": { + "bundle": "com.prebid" + }, + "device": { + "ifa": "87857b31-8942-4646-ae80-ab9c95bf3fab" + }, + "imp": [ + { + "id": "test-imp-id", + "video": { + "mimes": [ + "video/mp4" + ], + "protocols": [ + 2, + 5 + ], + "w": 1024, + "h": 576, + "ext": { + "foo": "bar" + } + }, + "instl": 0, + "ext": { + "bidder": { + "token": "123" + } + } + } + ] + }, + "httpCalls": [ + ], + "expectedBidResponses": [ + ], + "expectedMakeRequestsErrors": [ + { + "value": "not a supported adtype", + "comparison": "literal" + } + ], + "expectedMakeBidsErrors": [ + ] + } \ No newline at end of file diff --git a/adapters/pangle/param_test.go b/adapters/pangle/param_test.go new file mode 100644 index 00000000000..7b037bd52d6 --- /dev/null +++ b/adapters/pangle/param_test.go @@ -0,0 +1,45 @@ +package pangle + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/openrtb_ext" +) + +var validParams = []string{ + `{"token": "SomeAccessToken"}`, +} + +var invalidParams = []string{ + `{"token": ""}`, + `{"token": 42}`, + `{"token": null}`, + `{}`, +} + +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.BidderPangle, 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.BidderPangle, json.RawMessage(p)); err == nil { + t.Errorf("Schema allowed invalid params: %s", p) + } + } +} diff --git a/config/config.go b/config/config.go index ae1f62e90b4..74fe3c1a240 100755 --- a/config/config.go +++ b/config/config.go @@ -860,6 +860,7 @@ func SetupViper(v *viper.Viper, filename string) { v.SetDefault("adapters.nobid.endpoint", "https://ads.servenobid.com/ortb_adreq?tek=pbs&ver=1") v.SetDefault("adapters.openx.endpoint", "http://rtb.openx.net/prebid") v.SetDefault("adapters.orbidder.endpoint", "https://orbidder.otto.de/openrtb2") + v.SetDefault("adapters.pangle.disabled", true) v.SetDefault("adapters.pubmatic.endpoint", "https://hbopenbid.pubmatic.com/translator?source=prebid-server") v.SetDefault("adapters.pubnative.endpoint", "http://dsp.pubnative.net/bid/v1/request") v.SetDefault("adapters.pulsepoint.endpoint", "http://bid.contextweb.com/header/s/ortb/prebid-s2s") diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index f05a4d817fe..44247a459bd 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -64,6 +64,7 @@ import ( "github.com/prebid/prebid-server/adapters/nobid" "github.com/prebid/prebid-server/adapters/openx" "github.com/prebid/prebid-server/adapters/orbidder" + "github.com/prebid/prebid-server/adapters/pangle" "github.com/prebid/prebid-server/adapters/pubmatic" "github.com/prebid/prebid-server/adapters/pubnative" "github.com/prebid/prebid-server/adapters/pulsepoint" @@ -167,6 +168,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderNoBid: nobid.Builder, openrtb_ext.BidderOpenx: openx.Builder, openrtb_ext.BidderOrbidder: orbidder.Builder, + openrtb_ext.BidderPangle: pangle.Builder, openrtb_ext.BidderPubmatic: pubmatic.Builder, openrtb_ext.BidderPubnative: pubnative.Builder, openrtb_ext.BidderPulsepoint: pulsepoint.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index 576b1d1ac6f..b7a05a69ad0 100755 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -104,6 +104,7 @@ const ( BidderNoBid BidderName = "nobid" BidderOpenx BidderName = "openx" BidderOrbidder BidderName = "orbidder" + BidderPangle BidderName = "pangle" BidderPubmatic BidderName = "pubmatic" BidderPubnative BidderName = "pubnative" BidderPulsepoint BidderName = "pulsepoint" @@ -205,6 +206,7 @@ func CoreBidderNames() []BidderName { BidderNoBid, BidderOpenx, BidderOrbidder, + BidderPangle, BidderPubmatic, BidderPubnative, BidderPulsepoint, diff --git a/openrtb_ext/imp_pangle.go b/openrtb_ext/imp_pangle.go new file mode 100644 index 00000000000..cc32dcf1d01 --- /dev/null +++ b/openrtb_ext/imp_pangle.go @@ -0,0 +1,5 @@ +package openrtb_ext + +type ImpExtPangle struct { + Token string `json:"token"` +} diff --git a/static/bidder-info/pangle.yaml b/static/bidder-info/pangle.yaml new file mode 100644 index 00000000000..b8b1e1c2e42 --- /dev/null +++ b/static/bidder-info/pangle.yaml @@ -0,0 +1,9 @@ +maintainer: + email: pangle_dsp@bytedance.com +modifyingVastXmlAllowed: true +capabilities: + app: + mediaTypes: + - banner + - video + - native \ No newline at end of file diff --git a/static/bidder-params/pangle.json b/static/bidder-params/pangle.json new file mode 100644 index 00000000000..8e50443a026 --- /dev/null +++ b/static/bidder-params/pangle.json @@ -0,0 +1,16 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Pangle Adapter Params", + "description": "A schema which validates params accepted by the Pangle adapter", + "type": "object", + "properties": { + "token": { + "type": "string", + "description": "Access Token", + "pattern": ".+" + } + }, + "required": [ + "token" + ] +} \ No newline at end of file diff --git a/usersync/usersyncers/syncer_test.go b/usersync/usersyncers/syncer_test.go index 73f0e8861c5..a3dbf7eccfb 100755 --- a/usersync/usersyncers/syncer_test.go +++ b/usersync/usersyncers/syncer_test.go @@ -108,6 +108,7 @@ func TestNewSyncerMap(t *testing.T) { openrtb_ext.BidderMobfoxpb: true, openrtb_ext.BidderMobileFuse: true, openrtb_ext.BidderOrbidder: true, + openrtb_ext.BidderPangle: true, openrtb_ext.BidderPubnative: true, openrtb_ext.BidderRevcontent: true, openrtb_ext.BidderSilverMob: true,