diff --git a/adapters/insticator/insticator.go b/adapters/insticator/insticator.go new file mode 100644 index 0000000000..fc8487971b --- /dev/null +++ b/adapters/insticator/insticator.go @@ -0,0 +1,319 @@ +package insticator + +import ( + "fmt" + "net/http" + "strings" + + "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" + "github.com/prebid/prebid-server/v3/util/jsonutil" + "github.com/prebid/prebid-server/v3/util/mathutil" +) + +type ext struct { + Insticator impInsticatorExt `json:"insticator"` +} + +type impInsticatorExt struct { + AdUnitId string `json:"adUnitId"` + PublisherId string `json:"publisherId"` +} + +type adapter struct { + endpoint string +} + +type reqExt struct { + Insticator *reqInsticatorExt `json:"insticator,omitempty"` +} + +type reqInsticatorExt struct { + Caller []insticatorCaller `json:"caller,omitempty"` +} + +type insticatorCaller struct { + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` +} + +// caller Info used to track Prebid Server +// as one of the hops in the request to exchange +var caller = insticatorCaller{"Prebid-Server", "n/a"} + +type bidExt struct { + Insticator bidInsticatorExt `json:"insticator,omitempty"` +} + +type bidInsticatorExt struct { + MediaType string `json:"mediaType,omitempty"` +} + +// Builder builds a new instance of the Insticator adapter with the given config. +func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { + bidder := &adapter{ + endpoint: config.Endpoint, + } + return bidder, nil +} + +// getMediaTypeForBid figures out which media type this bid is for +func getMediaTypeForBid(bid *openrtb2.Bid) openrtb_ext.BidType { + switch bid.MType { + case openrtb2.MarkupBanner: + return openrtb_ext.BidTypeBanner + case openrtb2.MarkupVideo: + return openrtb_ext.BidTypeVideo + default: + return openrtb_ext.BidTypeBanner + } +} + +func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { + var errs []error + var adapterRequests []*adapters.RequestData + var groupedImps = make(map[string][]openrtb2.Imp) + + reqExt, err := makeReqExt(request) + if err != nil { + errs = append(errs, err) + } + + request.Ext = reqExt + + // Create a copy of the request to avoid modifying the original request + requestCopy := *request + isPublisherIdPopulated := false // Flag to track if populatePublisherId has been called + + for i := 0; i < len(request.Imp); i++ { + impCopy, impKey, publisherId, err := makeImps(request.Imp[i]) + if err != nil { + errs = append(errs, err) + continue + } + + // Populate publisher.id from imp extension + if !isPublisherIdPopulated { + populatePublisherId(publisherId, &requestCopy) + isPublisherIdPopulated = true + } + + resolvedBidFloor, errFloor := resolveBidFloor(impCopy.BidFloor, impCopy.BidFloorCur, requestInfo) + if errFloor != nil { + errs = append(errs, &errortypes.BadInput{ + Message: fmt.Sprintf("Error in converting the provided bid floor currency from %s to USD", + impCopy.BidFloorCur), + }) + continue + } + if resolvedBidFloor > 0 { + impCopy.BidFloor = resolvedBidFloor + impCopy.BidFloorCur = "USD" + } + + groupedImps[impKey] = append(groupedImps[impKey], impCopy) + } + + for _, impList := range groupedImps { + if adapterReq, err := a.makeRequest(&requestCopy, impList); err == nil { + adapterRequests = append(adapterRequests, adapterReq) + } else { + errs = append(errs, err) + } + } + return adapterRequests, errs +} + +func (a *adapter) makeRequest(request *openrtb2.BidRequest, impList []openrtb2.Imp) (*adapters.RequestData, error) { + request.Imp = impList + + reqJSON, err := jsonutil.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") + + if request.Device != nil { + if len(request.Device.UA) > 0 { + headers.Add("User-Agent", request.Device.UA) + } + + if len(request.Device.IPv6) > 0 { + headers.Set("X-Forwarded-For", request.Device.IPv6) + } + + if len(request.Device.IP) > 0 { + headers.Set("X-Forwarded-For", request.Device.IP) + headers.Add("IP", request.Device.IP) + } + } + + return &adapters.RequestData{ + Method: "POST", + 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 response.Cur != "" { + bidResponse.Currency = response.Cur + } + for _, seatBid := range response.SeatBid { + for i := range seatBid.Bid { + bid := &seatBid.Bid[i] + bidType := getMediaTypeForBid(bid) + b := &adapters.TypedBid{ + Bid: &seatBid.Bid[i], + BidType: bidType, + } + bidResponse.Bids = append(bidResponse.Bids, b) + } + } + return bidResponse, nil +} + +func makeImps(imp openrtb2.Imp) (openrtb2.Imp, string, string, error) { + var bidderExt adapters.ExtImpBidder + if err := jsonutil.Unmarshal(imp.Ext, &bidderExt); err != nil { + return openrtb2.Imp{}, "", "", &errortypes.BadInput{ + Message: err.Error(), + } + } + + var insticatorExt openrtb_ext.ExtImpInsticator + if err := jsonutil.Unmarshal(bidderExt.Bidder, &insticatorExt); err != nil { + return openrtb2.Imp{}, "", "", &errortypes.BadInput{ + Message: err.Error(), + } + } + + // Directly construct the impExt + impExt := ext{ + Insticator: impInsticatorExt{ + AdUnitId: insticatorExt.AdUnitId, + PublisherId: insticatorExt.PublisherId, + }, + } + + impExtJSON, err := jsonutil.Marshal(impExt) + if err != nil { + return openrtb2.Imp{}, "", "", &errortypes.BadInput{ + Message: err.Error(), + } + } + imp.Ext = impExtJSON + + // Validate Video if it exists + if imp.Video != nil { + if err := validateVideoParams(imp.Video); err != nil { + return openrtb2.Imp{}, insticatorExt.AdUnitId, insticatorExt.PublisherId, &errortypes.BadInput{ + Message: err.Error(), + } + } + } + + // Return the imp, AdUnitId, and no error + return imp, insticatorExt.AdUnitId, insticatorExt.PublisherId, nil +} + +func makeReqExt(request *openrtb2.BidRequest) ([]byte, error) { + var reqExt reqExt + + if len(request.Ext) > 0 { + if err := jsonutil.Unmarshal(request.Ext, &reqExt); err != nil { + return nil, err + } + } + + if reqExt.Insticator == nil { + reqExt.Insticator = &reqInsticatorExt{} + } + + if reqExt.Insticator.Caller == nil { + reqExt.Insticator.Caller = make([]insticatorCaller, 0) + } + + reqExt.Insticator.Caller = append(reqExt.Insticator.Caller, caller) + + return jsonutil.Marshal(reqExt) +} + +func resolveBidFloor(bidFloor float64, bidFloorCur string, reqInfo *adapters.ExtraRequestInfo) (float64, error) { + if bidFloor > 0 && bidFloorCur != "" && strings.ToUpper(bidFloorCur) != "USD" { + floor, err := reqInfo.ConvertCurrency(bidFloor, bidFloorCur, "USD") + return mathutil.RoundTo4Decimals(floor), err + } + + return bidFloor, nil +} + +func validateVideoParams(video *openrtb2.Video) error { + if video.W == nil || *video.W == 0 || video.H == nil || *video.H == 0 || video.MIMEs == nil { + return &errortypes.BadInput{ + Message: "One or more invalid or missing video field(s) w, h, mimes", + } + } + + return nil +} + +// populatePublisherId function populates site.publisher.id or app.publisher.id +func populatePublisherId(publisherId string, request *openrtb2.BidRequest) { + + // Populate site.publisher.id if request.Site is not nil + if request.Site != nil { + // Make a shallow copy of Site if it already exists + siteCopy := *request.Site + request.Site = &siteCopy + + // Make a shallow copy of Publisher if it already exists + if request.Site.Publisher != nil { + publisherCopy := *request.Site.Publisher + request.Site.Publisher = &publisherCopy + } else { + request.Site.Publisher = &openrtb2.Publisher{} + } + + request.Site.Publisher.ID = publisherId + } + + // Populate app.publisher.id if request.App is not nil + if request.App != nil { + // Make a shallow copy of App if it already exists + appCopy := *request.App + request.App = &appCopy + + // Make a shallow copy of Publisher if it already exists + if request.App.Publisher != nil { + publisherCopy := *request.App.Publisher + request.App.Publisher = &publisherCopy + } else { + request.App.Publisher = &openrtb2.Publisher{} + } + + request.App.Publisher.ID = publisherId + } +} diff --git a/adapters/insticator/insticator_test.go b/adapters/insticator/insticator_test.go new file mode 100644 index 0000000000..b557aedce4 --- /dev/null +++ b/adapters/insticator/insticator_test.go @@ -0,0 +1,21 @@ +package insticator + +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.BidderInsticator, config.Adapter{ + Endpoint: "https://insticator.example.com/v1/pbs"}, + config.Server{ExternalUrl: "https://insticator.example.com/v1/pbs", GvlID: 1, DataCenter: "2"}) + + if buildErr != nil { + t.Fatalf("Builder returned unexpected error %v", buildErr) + } + + adapterstest.RunJSONBidderTest(t, "insticatortest", bidder) +} diff --git a/adapters/insticator/insticatortest/exemplary/app-banner.json b/adapters/insticator/insticatortest/exemplary/app-banner.json new file mode 100644 index 0000000000..4c71760a73 --- /dev/null +++ b/adapters/insticator/insticatortest/exemplary/app-banner.json @@ -0,0 +1,127 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "app": { + "publisher": { + "id": "test-publisher-id" + } + }, + "device": { + "ua": "", + "ip": "1.1.1.2", + "ipv6": "" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "device":{ + "ip": "1.1.1.2" + }, + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "insticator": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "app": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "insticator", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "mtype": 1, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }] + } + ], + "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": "crid_10", + "w": 728, + "h": 90, + "mtype": 1, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/insticator/insticatortest/exemplary/app-video.json b/adapters/insticator/insticatortest/exemplary/app-video.json new file mode 100644 index 0000000000..2f0143fe96 --- /dev/null +++ b/adapters/insticator/insticatortest/exemplary/app-video.json @@ -0,0 +1,173 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [ + 2 + ], + "placement": 1, + "startdelay": -2, + "playbackmethod": [ + 2 + ], + "mimes": [ + "foo", + "bar" + ] + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "app": { + "publisher": { + "id": "test-publisher-id" + } + }, + "device": { + "ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "ip": "1.22.22.1", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "User-Agent": [ + "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + ], + "X-Forwarded-For": [ + "1.22.22.1" + ], + "Ip": [ + "1.22.22.1" + ] + }, + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "device": { + "ip": "1.22.22.1", + "ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + }, + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [ + 2 + ], + "placement": 1, + "startdelay": -2, + "playbackmethod": [ + 2 + ], + "mimes": [ + "foo", + "bar" + ] + }, + "ext": { + "insticator": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "app": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "insticator", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-vast-ad", + "mtype": 2, + "crid": "crid_10", + "h": 90, + "w": 728, + "ext": { + "insticator": { + "mediaType": "video" + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-vast-ad", + "crid": "crid_10", + "w": 728, + "mtype": 2, + "h": 90, + "ext": { + "insticator": { + "mediaType": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/insticator/insticatortest/exemplary/multi-format.json b/adapters/insticator/insticatortest/exemplary/multi-format.json new file mode 100644 index 0000000000..1fedfe8dc9 --- /dev/null +++ b/adapters/insticator/insticatortest/exemplary/multi-format.json @@ -0,0 +1,121 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "bidder": { + "adUnitId": "inview", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "insticator": { + "adUnitId": "inview", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "insticator", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728 + }] + } + ], + "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": "crid_10", + "w": 728, + "h": 90 + }, + "type": "banner" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/insticator/insticatortest/exemplary/multi-imps.json b/adapters/insticator/insticatortest/exemplary/multi-imps.json new file mode 100644 index 0000000000..eb744ced11 --- /dev/null +++ b/adapters/insticator/insticatortest/exemplary/multi-imps.json @@ -0,0 +1,329 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id1", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "adUnitId": "inview", + "publisherId": "test-publisher-id" + } + } + }, + { + "id": "test-imp-id2", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "adUnitId": "inview", + "publisherId": "test-publisher-id" + } + } + }, + { + "id": "test-imp-id3", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "adUnitId": "inview", + "publisherId": "test-publisher-id" + } + } + }, + { + "id": "test-imp-id4", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "adUnitId": "siab", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id1", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "insticator": { + "adUnitId": "inview", + "publisherId": "test-publisher-id" + } + } + }, + { + "id":"test-imp-id2", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "insticator": { + "adUnitId": "inview", + "publisherId": "test-publisher-id" + } + } + }, + { + "id":"test-imp-id3", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "insticator": { + "adUnitId": "inview", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs":["test-imp-id1","test-imp-id2", "test-imp-id3"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "insticator", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id1", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }, + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id2", + "price": 0.600000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }, + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id3", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + } + ] + } + ], + "cur": "USD" + } + } + }, + { + "expectedRequest": { + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id4", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "insticator": { + "adUnitId": "siab", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs":["test-imp-id4"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "insticator", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id4", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id1", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 728, + "h": 90, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }, + "type": "banner" + }, + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id2", + "price": 0.6, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 728, + "h": 90, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }, + "type": "banner" + }, + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id3", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 728, + "h": 90, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }, + "type": "banner" + } + ] + }, + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id4", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 728, + "h": 90, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/insticator/insticatortest/exemplary/site-banner.json b/adapters/insticator/insticatortest/exemplary/site-banner.json new file mode 100644 index 0000000000..4586fdc176 --- /dev/null +++ b/adapters/insticator/insticatortest/exemplary/site-banner.json @@ -0,0 +1,152 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + }, + "device": { + "ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "User-Agent": [ + "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + ], + "X-Forwarded-For": [ + "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + ] + }, + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "device": { + "ipv6": "2001:0db8:85a3:0000:0000:8a2e:0370:7334", + "ua": "Mozilla/5.0 (Linux; Android 8.0.0; SM-G960F Build/R16NW) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36" + }, + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "insticator": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "insticator", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "mtype": 1, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + } + ] + } + ], + "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": "crid_10", + "w": 728, + "h": 90, + "mtype": 1, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/insticator/insticatortest/exemplary/site-video.json b/adapters/insticator/insticatortest/exemplary/site-video.json new file mode 100644 index 0000000000..bd7c40af20 --- /dev/null +++ b/adapters/insticator/insticatortest/exemplary/site-video.json @@ -0,0 +1,130 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "placement": 1, + "startdelay": -2, + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "placement": 1, + "startdelay": -2, + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "insticator": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "insticator", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-vast-ad", + "mtype": 2, + "crid": "crid_10", + "h": 90, + "w": 728, + "ext": { + "insticator": { + "mediaType": "video" + } + } + }] + } + ], + "cur": "USD" + } + } + } + ], + + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-vast-ad", + "crid": "crid_10", + "w": 728, + "mtype": 2, + "h": 90, + "ext": { + "insticator": { + "mediaType": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} diff --git a/adapters/insticator/insticatortest/supplemental/204.json b/adapters/insticator/insticatortest/supplemental/204.json new file mode 100644 index 0000000000..f0a43defb1 --- /dev/null +++ b/adapters/insticator/insticatortest/supplemental/204.json @@ -0,0 +1,71 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "insticator": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 204, + "body": {} + } + } + ], + "expectedBidResponses": [] + } + \ No newline at end of file diff --git a/adapters/insticator/insticatortest/supplemental/app-pubid-absent.json b/adapters/insticator/insticatortest/supplemental/app-pubid-absent.json new file mode 100644 index 0000000000..0fcafa58b1 --- /dev/null +++ b/adapters/insticator/insticatortest/supplemental/app-pubid-absent.json @@ -0,0 +1,120 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "adUnitId": "fake-app-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "app": { + "id": "test-app-id", + "bundle": "test-app.com" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "insticator": { + "adUnitId": "fake-app-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "app": { + "id": "test-app-id", + "bundle": "test-app.com", + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "insticator", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "mtype": 1, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }] + } + ], + "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": "crid_10", + "w": 728, + "h": 90, + "mtype": 1, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/insticator/insticatortest/supplemental/bad-imp-ext.json b/adapters/insticator/insticatortest/supplemental/bad-imp-ext.json new file mode 100644 index 0000000000..2e0507791a --- /dev/null +++ b/adapters/insticator/insticatortest/supplemental/bad-imp-ext.json @@ -0,0 +1,29 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": "invalid ext" + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + + "expectedMakeRequestsErrors": [ + { + "value": "expect { or n, but found \"", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/adapters/insticator/insticatortest/supplemental/currency-conversion-fail.json b/adapters/insticator/insticatortest/supplemental/currency-conversion-fail.json new file mode 100644 index 0000000000..4c7082f47d --- /dev/null +++ b/adapters/insticator/insticatortest/supplemental/currency-conversion-fail.json @@ -0,0 +1,52 @@ +{ + "mockBidRequest": { + "cur": [ + "GBP" + ], + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + }, + "bidfloor": 1, + "bidfloorcur": "GBP" + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + }, + "ext": { + "prebid": { + "currency": { + "rates": { + "INR": { + "USD": 2 + } + }, + "usepbsrates": false + } + } + } + }, + "expectedMakeRequestsErrors": [ + { + "value": "Error in converting the provided bid floor currency from GBP to USD", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/insticator/insticatortest/supplemental/currency-conversion.json b/adapters/insticator/insticatortest/supplemental/currency-conversion.json new file mode 100644 index 0000000000..9ce11dcdfb --- /dev/null +++ b/adapters/insticator/insticatortest/supplemental/currency-conversion.json @@ -0,0 +1,152 @@ +{ + "mockBidRequest": { + "cur": [ + "GBP" + ], + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + }, + "bidfloor": 1, + "bidfloorcur": "GBP" + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + }, + "ext": { + "prebid": { + "currency": { + "rates": { + "GBP": { + "USD": 2 + } + }, + "usepbsrates": false + } + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "cur": [ + "GBP" + ], + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [ + { + "w": 728, + "h": 90 + } + ] + }, + "ext": { + "insticator": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + }, + "bidfloor": 2, + "bidfloorcur": "USD" + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "insticator", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "mtype": 1, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + } + ] + } + ], + "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": "crid_10", + "w": 728, + "h": 90, + "mtype": 1, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/insticator/insticatortest/supplemental/device-validation.json b/adapters/insticator/insticatortest/supplemental/device-validation.json new file mode 100644 index 0000000000..a493e1b28a --- /dev/null +++ b/adapters/insticator/insticatortest/supplemental/device-validation.json @@ -0,0 +1,168 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [ + 2 + ], + "placement": 1, + "startdelay": -2, + "playbackmethod": [ + 2 + ], + "mimes": [ + "foo", + "bar" + ] + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "app": { + "publisher": { + "id": "test-publisher-id" + } + }, + "device": { + "ipv6": "", + "ip": "1.22.22.1", + "ua": "" + } + }, + "httpCalls": [ + { + "expectedRequest": { + "headers": { + "Content-Type": [ + "application/json;charset=utf-8" + ], + "Accept": [ + "application/json" + ], + "X-Forwarded-For": [ + "1.22.22.1" + ], + "Ip": [ + "1.22.22.1" + ] + }, + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "device": { + "ip": "1.22.22.1" + }, + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [ + 2 + ], + "placement": 1, + "startdelay": -2, + "playbackmethod": [ + 2 + ], + "mimes": [ + "foo", + "bar" + ] + }, + "ext": { + "insticator": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "app": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "insticator", + "bid": [ + { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-vast-ad", + "mtype": 2, + "crid": "crid_10", + "h": 90, + "w": 728, + "ext": { + "insticator": { + "mediaType": "video" + } + } + } + ] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.5, + "adm": "some-test-vast-ad", + "crid": "crid_10", + "w": 728, + "mtype": 2, + "h": 90, + "ext": { + "insticator": { + "mediaType": "video" + } + } + }, + "type": "video" + } + ] + } + ] +} \ No newline at end of file diff --git a/adapters/insticator/insticatortest/supplemental/multi-imp-mixed-validation.json b/adapters/insticator/insticatortest/supplemental/multi-imp-mixed-validation.json new file mode 100644 index 0000000000..18fa069aed --- /dev/null +++ b/adapters/insticator/insticatortest/supplemental/multi-imp-mixed-validation.json @@ -0,0 +1,152 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id1", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + }, + { + "id": "test-imp-id2", + "video": { + "mimes": ["video/mp4"], + "minduration": 5, + "maxduration": 30, + "protocols": [2, 3, 5, 6], + "w": 640, + "h": 480 + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id1", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "insticator": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + }, + { + "id":"test-imp-id2", + "video": { + "mimes": ["video/mp4"], + "minduration": 5, + "maxduration": 30, + "protocols": [2, 3, 5, 6], + "w": 640, + "h": 480 + }, + "ext": { + "insticator": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs":["test-imp-id1", "test-imp-id2"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "insticator", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id1", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "mtype": 1, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }] + } + ], + "cur": "USD" + } + } + } + ], + "expectedBidResponses": [ + { + "currency": "USD", + "bids": [ + { + "bid": { + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id1", + "price": 0.5, + "adm": "some-test-ad", + "crid": "crid_10", + "w": 728, + "h": 90, + "mtype": 1, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/insticator/insticatortest/supplemental/request-ext-unmarshal-fail.json b/adapters/insticator/insticatortest/supplemental/request-ext-unmarshal-fail.json new file mode 100644 index 0000000000..1ea883a11c --- /dev/null +++ b/adapters/insticator/insticatortest/supplemental/request-ext-unmarshal-fail.json @@ -0,0 +1,41 @@ +{ +"mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [2], + "placement": 1, + "startdelay": -2, + "playbackmethod": [2], + "mimes": ["foo", "bar"] + }, + "ext": { + "bidder": "invalid" + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + }, + "ext": { + "insticator": "invalid" + } + }, + + "expectedMakeRequestsErrors": [ + { + "value": "cannot unmarshal insticator.reqExt.Insticator: expect { or n, but found \"", + "comparison": "literal" + }, + { + "value": "expect { or n, but found \"", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/insticator/insticatortest/supplemental/response-unmarshal-fail.json b/adapters/insticator/insticatortest/supplemental/response-unmarshal-fail.json new file mode 100644 index 0000000000..a3b40b084e --- /dev/null +++ b/adapters/insticator/insticatortest/supplemental/response-unmarshal-fail.json @@ -0,0 +1,102 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [ + 2 + ], + "placement": 1, + "startdelay": -2, + "playbackmethod": [ + 2 + ], + "mimes": [ + "foo", + "bar" + ] + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90, + "protocols": [ + 2 + ], + "placement": 1, + "startdelay": -2, + "playbackmethod": [ + 2 + ], + "mimes": [ + "foo", + "bar" + ] + }, + "ext": { + "insticator": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs": [ + "test-imp-id" + ] + }, + "mockResponse": { + "status": 200, + "body": "invalid json" + } + } + ], + "expectedMakeBidsErrors": [ + { + "value": "expect { or n, but found \"", + "comparison": "literal" + } + ] +} \ No newline at end of file diff --git a/adapters/insticator/insticatortest/supplemental/site-pubid-absent.json b/adapters/insticator/insticatortest/supplemental/site-pubid-absent.json new file mode 100644 index 0000000000..00c694a197 --- /dev/null +++ b/adapters/insticator/insticatortest/supplemental/site-pubid-absent.json @@ -0,0 +1,120 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "id": "test-site-id", + "domain": "test-site.com" + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "insticator": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "id": "test-site-id", + "domain": "test-site.com", + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 200, + "body": { + "id": "test-request-id", + "seatbid": [ + { + "seat": "insticator", + "bid": [{ + "id": "8ee514f1-b2b8-4abb-89fd-084437d1e800", + "impid": "test-imp-id", + "price": 0.500000, + "adm": "some-test-ad", + "crid": "crid_10", + "h": 90, + "w": 728, + "mtype": 1, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }] + } + ], + "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": "crid_10", + "w": 728, + "h": 90, + "mtype": 1, + "ext": { + "insticator": { + "mediaType": "banner" + } + } + }, + "type": "banner" + } + ] + } + ] + } + \ No newline at end of file diff --git a/adapters/insticator/insticatortest/supplemental/status-not-ok.json b/adapters/insticator/insticatortest/supplemental/status-not-ok.json new file mode 100644 index 0000000000..e530896e2d --- /dev/null +++ b/adapters/insticator/insticatortest/supplemental/status-not-ok.json @@ -0,0 +1,86 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "bidder": { + "adUnitId": "fake-invalid-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + + "httpCalls": [ + { + "expectedRequest": { + "uri": "https://insticator.example.com/v1/pbs", + "body": { + "ext": { + "insticator": { + "caller": [ + { + "name": "Prebid-Server", + "version": "n/a" + } + ] + } + }, + "id": "test-request-id", + "imp": [ + { + "id":"test-imp-id", + "banner": { + "format": [{"w": 728, "h": 90}] + }, + "ext": { + "insticator": { + "adUnitId": "fake-invalid-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + "impIDs":["test-imp-id"] + }, + "mockResponse": { + "status": 400, + "body": { + "error": { + "message": "Validation failed", + "details": [ + { + "message": "site.id is invalid" + } + ] + } + } + } + } + ], + + "expectedMakeBidsErrors": [ + { + "value": "Unexpected status code: 400. Run with request.debug = 1 for more info", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/adapters/insticator/insticatortest/supplemental/video-validation-fail.json b/adapters/insticator/insticatortest/supplemental/video-validation-fail.json new file mode 100644 index 0000000000..553b0f68c6 --- /dev/null +++ b/adapters/insticator/insticatortest/supplemental/video-validation-fail.json @@ -0,0 +1,33 @@ +{ + "mockBidRequest": { + "id": "test-request-id", + "imp": [ + { + "id": "test-imp-id", + "video": { + "w": 728, + "h": 90 + }, + "ext": { + "bidder": { + "adUnitId": "fake-site-id", + "publisherId": "test-publisher-id" + } + } + } + ], + "site": { + "publisher": { + "id": "test-publisher-id" + } + } + }, + + "expectedMakeRequestsErrors": [ + { + "value": "One or more invalid or missing video field(s) w, h, mimes", + "comparison": "literal" + } + ] + } + \ No newline at end of file diff --git a/adapters/insticator/params_test.go b/adapters/insticator/params_test.go new file mode 100644 index 0000000000..47d2bdf44f --- /dev/null +++ b/adapters/insticator/params_test.go @@ -0,0 +1,57 @@ +package insticator + +import ( + "encoding/json" + "testing" + + "github.com/prebid/prebid-server/v3/openrtb_ext" +) + +// This file actually intends to test static/bidder-params/Insticator.json +// +// These also validate the format of the external API: request.imp[i].ext.prebid.bidder.Insticator + +// TestValidParams makes sure that the Insticator 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.BidderInsticator, json.RawMessage(validParam)); err != nil { + t.Errorf("Schema rejected Insticator params: %s", validParam) + } + } +} + +// TestInvalidParams makes sure that the Insticator 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.BidderInsticator, json.RawMessage(invalidParam)); err == nil { + t.Errorf("Schema allowed unexpected params: %s", invalidParam) + } + } +} + +var validParams = []string{ + `{"publisherId": "inview", "adUnitId": "fakesiteid1"}`, + `{"publisherId": "siab", "adUnitId": "fakesiteid2"}`, + `{"publisherId": "inview", "adUnitId": "foo.ba"}`, +} + +var invalidParams = []string{ + `{"publisherId": "inview"}`, // Missing adUnitId + `{"publisherId": 123, "adUnitId": "fakesiteid2"}`, // publisherId should be a string + `{"adUnitId": "fakesiteid3"}`, // Missing publisherId + `{"publisherId": "inview", "adUnitId": 456}`, // adUnitId should be a string + `{"publisherId": null, "adUnitId": "fakesiteid5"}`, // Null publisherId + `{"publisherId": "inview", "adUnitId": null}`, // Null adUnitId + `{"publisherId": true, "adUnitId": "fakesiteid6"}`, // publisherId should be a string, got boolean + `{"publisherId": "inview", "adUnitId": [1, 2, 3]}`, // adUnitId should be a string, got array +} diff --git a/exchange/adapter_builders.go b/exchange/adapter_builders.go index 67e28286f0..c9703cb69f 100755 --- a/exchange/adapter_builders.go +++ b/exchange/adapter_builders.go @@ -109,6 +109,7 @@ import ( "github.com/prebid/prebid-server/v3/adapters/improvedigital" "github.com/prebid/prebid-server/v3/adapters/infytv" "github.com/prebid/prebid-server/v3/adapters/inmobi" + "github.com/prebid/prebid-server/v3/adapters/insticator" "github.com/prebid/prebid-server/v3/adapters/interactiveoffers" "github.com/prebid/prebid-server/v3/adapters/invibes" "github.com/prebid/prebid-server/v3/adapters/iqx" @@ -339,6 +340,7 @@ func newAdapterBuilders() map[openrtb_ext.BidderName]adapters.Builder { openrtb_ext.BidderImprovedigital: improvedigital.Builder, openrtb_ext.BidderInfyTV: infytv.Builder, openrtb_ext.BidderInMobi: inmobi.Builder, + openrtb_ext.BidderInsticator: insticator.Builder, openrtb_ext.BidderInteractiveoffers: interactiveoffers.Builder, openrtb_ext.BidderInvibes: invibes.Builder, openrtb_ext.BidderIQX: iqx.Builder, diff --git a/openrtb_ext/bidders.go b/openrtb_ext/bidders.go index f7706ce5c2..fda02f29b0 100644 --- a/openrtb_ext/bidders.go +++ b/openrtb_ext/bidders.go @@ -126,6 +126,7 @@ var coreBidderNames []BidderName = []BidderName{ BidderImprovedigital, BidderInfyTV, BidderInMobi, + BidderInsticator, BidderInteractiveoffers, BidderInvibes, BidderIQX, @@ -455,6 +456,7 @@ const ( BidderImprovedigital BidderName = "improvedigital" BidderInfyTV BidderName = "infytv" BidderInMobi BidderName = "inmobi" + BidderInsticator BidderName = "insticator" BidderInteractiveoffers BidderName = "interactiveoffers" BidderInvibes BidderName = "invibes" BidderIQX BidderName = "iqx" diff --git a/openrtb_ext/imp_insticator.go b/openrtb_ext/imp_insticator.go new file mode 100644 index 0000000000..cb33223dfe --- /dev/null +++ b/openrtb_ext/imp_insticator.go @@ -0,0 +1,7 @@ +package openrtb_ext + +// ExtImpInsticator defines the contract for bidrequest.imp[i].ext.prebid.bidder.insticator +type ExtImpInsticator struct { + AdUnitId string `json:"adUnitId,omitempty"` + PublisherId string `json:"publisherId,omitempty"` +} diff --git a/static/bidder-info/insticator.yaml b/static/bidder-info/insticator.yaml new file mode 100644 index 0000000000..907ff4a50d --- /dev/null +++ b/static/bidder-info/insticator.yaml @@ -0,0 +1,17 @@ +endpoint: "https://ex.ingage.tech/v1/prebidserver" +maintainer: + email: "prebid@insticator.com" +gvlVendorID: 910 +capabilities: + app: + mediaTypes: + - banner + - video + site: + mediaTypes: + - banner + - video +userSync: + iframe: + url: "https://usync.ingage.tech?gdpr={{.GDPR}}&gdpr_consent={{.GDPRConsent}}&us_privacy={{.USPrivacy}}&redirect={{.RedirectURL}}" + userMacro: "$UID" diff --git a/static/bidder-params/insticator.json b/static/bidder-params/insticator.json new file mode 100644 index 0000000000..358ec37f57 --- /dev/null +++ b/static/bidder-params/insticator.json @@ -0,0 +1,21 @@ + +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "title": "Insticator Adapter Params", + "description": "A schema which validates params accepted by Insticator", + + "type": "object", + "properties": { + "adUnitId": { + "type": "string", + "description": "Ad Unit Id", + "minLength": 1 + }, + "publisherId": { + "type": "string", + "description": "Publisher Id", + "minLength": 1 + } + }, + "required": ["adUnitId", "publisherId"] + } \ No newline at end of file diff --git a/util/mathutil/mathutil.go b/util/mathutil/mathutil.go new file mode 100644 index 0000000000..126244501d --- /dev/null +++ b/util/mathutil/mathutil.go @@ -0,0 +1,9 @@ +// Package mathutil provides utility functions for mathematical operations. +package mathutil + +import "math" + +// RoundTo4Decimals rounds a float64 value to 4 decimal places. +func RoundTo4Decimals(amount float64) float64 { + return math.Round(amount*10000) / 10000 +} diff --git a/util/mathutil/mathutil_test.go b/util/mathutil/mathutil_test.go new file mode 100644 index 0000000000..545b28b3f4 --- /dev/null +++ b/util/mathutil/mathutil_test.go @@ -0,0 +1,49 @@ +package mathutil + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestRoundTo4Decimals(t *testing.T) { + t.Run("positive-number", func(t *testing.T) { + r := RoundTo4Decimals(123.456789) + assert.Equal(t, 123.4568, r) + }) + + t.Run("negative-number", func(t *testing.T) { + r := RoundTo4Decimals(-123.456789) + assert.Equal(t, -123.4568, r) + }) + + t.Run("already-rounded", func(t *testing.T) { + r := RoundTo4Decimals(123.4567) + assert.Equal(t, 123.4567, r) + }) + + t.Run("round-up", func(t *testing.T) { + r := RoundTo4Decimals(123.45675) + assert.Equal(t, 123.4568, r) + }) + + t.Run("round-down", func(t *testing.T) { + r := RoundTo4Decimals(123.45674) + assert.Equal(t, 123.4567, r) + }) + + t.Run("small-number", func(t *testing.T) { + r := RoundTo4Decimals(0.00005) + assert.Equal(t, 0.0001, r) + }) + + t.Run("negative-small-number", func(t *testing.T) { + r := RoundTo4Decimals(-0.00005) + assert.Equal(t, -0.0001, r) + }) + + t.Run("zero", func(t *testing.T) { + r := RoundTo4Decimals(0) + assert.Equal(t, 0.0, r) + }) +}