diff --git a/adapters/sspBC/sspbc.go b/adapters/sspBC/sspbc.go
index ae7d5f40e14..4a12ddaa51e 100644
--- a/adapters/sspBC/sspbc.go
+++ b/adapters/sspBC/sspbc.go
@@ -1,135 +1,70 @@
package sspBC
import (
- "bytes"
"encoding/json"
- "errors"
"fmt"
- "html/template"
"net/http"
"net/url"
- "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/metrics"
"github.com/prebid/prebid-server/v3/openrtb_ext"
"github.com/prebid/prebid-server/v3/util/jsonutil"
)
const (
- adapterVersion = "5.8"
- impFallbackSize = "1x1"
- requestTypeStandard = 1
- requestTypeOneCode = 2
- requestTypeTest = 3
- prebidServerIntegrationType = "4"
+ adapterVersion = "6.0"
)
-var (
- errSiteNill = errors.New("site cannot be nill")
- errImpNotFound = errors.New("imp not found")
- errNotSupportedFormat = errors.New("bid format is not supported")
+type (
+ adapter struct {
+ endpoint string
+ }
+ requestInfo struct {
+ PbsEntryPoint metrics.RequestType
+ }
+ requestData struct {
+ Request *openrtb2.BidRequest `json:"bidRequest"`
+ RequestInfo *requestInfo `json:"requestInfo"`
+ }
)
-// mcAd defines the MC payload for banner ads.
-type mcAd struct {
- Id string `json:"id"`
- Seat string `json:"seat"`
- SeatBid []openrtb2.SeatBid `json:"seatbid"`
-}
-
-// adSlotData defines struct used for the oneCode detection.
-type adSlotData struct {
- PbSlot string `json:"pbslot"`
- PbSize string `json:"pbsize"`
-}
-
-// templatePayload represents the banner template payload.
-type templatePayload struct {
- SiteId string `json:"siteid"`
- SlotId string `json:"slotid"`
- AdLabel string `json:"adlabel"`
- PubId string `json:"pubid"`
- Page string `json:"page"`
- Referer string `json:"referer"`
- McAd mcAd `json:"mcad"`
- Inver string `json:"inver"`
-}
-
-// requestImpExt represents the ext field of the request imp field.
-type requestImpExt struct {
- Data adSlotData `json:"data"`
-}
-
-// responseExt represents ext data added by proxy.
-type responseExt struct {
- AdLabel string `json:"adlabel"`
- PublisherId string `json:"pubid"`
- SiteId string `json:"siteid"`
- SlotId string `json:"slotid"`
-}
-
-type adapter struct {
- endpoint string
- bannerTemplate *template.Template
-}
-
// ---------------ADAPTER INTERFACE------------------
// Builder builds a new instance of the sspBC adapter
-func Builder(_ openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
- // HTML template used to create banner ads
- const bannerHTML = `
`
-
- bannerTemplate, err := template.New("banner").Parse(bannerHTML)
+func Builder(_ openrtb_ext.BidderName, config config.Adapter, _ config.Server) (adapters.Bidder, error) {
+ endpoint, err := buildAdapterEndpoint(config.Endpoint, adapterVersion)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("unable to build sspbc adapter endpoint: %w", err)
}
bidder := &adapter{
- endpoint: config.Endpoint,
- bannerTemplate: bannerTemplate,
+ endpoint: endpoint,
}
return bidder, nil
}
-func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
- formattedRequest, err := formatSspBcRequest(request)
- if err != nil {
- return nil, []error{err}
- }
-
- requestJSON, err := json.Marshal(formattedRequest)
- if err != nil {
- return nil, []error{err}
+func (a *adapter) MakeRequests(request *openrtb2.BidRequest, extraRequestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
+ sspBcRequest := &requestData{
+ Request: request,
+ RequestInfo: &requestInfo{
+ PbsEntryPoint: extraRequestInfo.PbsEntryPoint,
+ },
}
- requestURL, err := url.Parse(a.endpoint)
+ requestJSON, err := json.Marshal(sspBcRequest)
if err != nil {
return nil, []error{err}
}
- // add query parameters to request
- queryParams := requestURL.Query()
- queryParams.Add("bdver", adapterVersion)
- queryParams.Add("inver", prebidServerIntegrationType)
- requestURL.RawQuery = queryParams.Encode()
-
requestData := &adapters.RequestData{
Method: http.MethodPost,
- Uri: requestURL.String(),
+ Uri: a.endpoint,
Body: requestJSON,
- ImpIDs: getImpIDs(formattedRequest.Imp),
+ ImpIDs: openrtb_ext.GetImpIDs(request.Imp),
}
return []*adapters.RequestData{requestData}, nil
@@ -142,7 +77,7 @@ func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest
if externalResponse.StatusCode != http.StatusOK {
err := &errortypes.BadServerResponse{
- Message: fmt.Sprintf("Unexpected status code: %d.", externalResponse.StatusCode),
+ Message: fmt.Sprintf("unexpected status code: %d.", externalResponse.StatusCode),
}
return nil, []error{err}
}
@@ -157,253 +92,49 @@ func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest
var errors []error
for _, seatBid := range response.SeatBid {
- for _, bid := range seatBid.Bid {
- if err := a.impToBid(internalRequest, seatBid, bid, bidResponse); err != nil {
- errors = append(errors, err)
+ for i := range seatBid.Bid {
+ bid := seatBid.Bid[i]
+ bidType, err := getBidType(bid)
+ if err != nil {
+ return nil, []error{err}
}
- }
- }
-
- return bidResponse, errors
-}
-
-func (a *adapter) impToBid(internalRequest *openrtb2.BidRequest, seatBid openrtb2.SeatBid, bid openrtb2.Bid,
- bidResponse *adapters.BidderResponse) error {
- var bidType openrtb_ext.BidType
-
- /*
- Determine bid type
- At this moment we only check if bid contains Adm property
-
- Later updates will check for video & native data
- */
- if bid.AdM != "" {
- bidType = openrtb_ext.BidTypeBanner
- }
-
- /*
- Recover original ImpID
- (stored on request in TagID)
- */
- impID, err := getOriginalImpID(bid.ImpID, internalRequest.Imp)
- if err != nil {
- return err
- }
- bid.ImpID = impID
-
- // read additional data from proxy
- var bidDataExt responseExt
- if err := jsonutil.Unmarshal(bid.Ext, &bidDataExt); err != nil {
- return err
- }
- /*
- use correct ad creation method for a detected bid type
- right now, we are only creating banner ads
- if type is not detected / supported, throw error
- */
- if bidType != openrtb_ext.BidTypeBanner {
- return errNotSupportedFormat
- }
-
- var adCreationError error
- bid.AdM, adCreationError = a.createBannerAd(bid, bidDataExt, internalRequest, seatBid.Seat)
- if adCreationError != nil {
- return adCreationError
- }
- // append bid to responses
- bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
- Bid: &bid,
- BidType: bidType,
- })
-
- return nil
-}
-
-func getOriginalImpID(impID string, imps []openrtb2.Imp) (string, error) {
- for _, imp := range imps {
- if imp.ID == impID {
- return imp.TagID, nil
- }
- }
-
- return "", errImpNotFound
-}
-
-func (a *adapter) createBannerAd(bid openrtb2.Bid, ext responseExt, request *openrtb2.BidRequest, seat string) (string, error) {
- if strings.Contains(bid.AdM, "") {
- // Banner ad is already formatted
- return bid.AdM, nil
- }
-
- // create McAd payload
- var mcad = mcAd{
- Id: request.ID,
- Seat: seat,
- SeatBid: []openrtb2.SeatBid{
- {Bid: []openrtb2.Bid{bid}},
- },
- }
-
- bannerData := &templatePayload{
- SiteId: ext.SiteId,
- SlotId: ext.SlotId,
- AdLabel: ext.AdLabel,
- PubId: ext.PublisherId,
- Page: request.Site.Page,
- Referer: request.Site.Ref,
- McAd: mcad,
- Inver: prebidServerIntegrationType,
- }
-
- var filledTemplate bytes.Buffer
- if err := a.bannerTemplate.Execute(&filledTemplate, bannerData); err != nil {
- return "", err
- }
-
- return filledTemplate.String(), nil
-}
-
-func getImpSize(imp openrtb2.Imp) string {
- if imp.Banner == nil || len(imp.Banner.Format) == 0 {
- return impFallbackSize
- }
-
- var (
- areaMax int64
- impSize = impFallbackSize
- )
- for _, size := range imp.Banner.Format {
- area := size.W * size.H
- if area > areaMax {
- areaMax = area
- impSize = fmt.Sprintf("%dx%d", size.W, size.H)
+ bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
+ Bid: &bid,
+ BidType: bidType,
+ })
}
}
- return impSize
-}
-
-// getBidParameters reads additional data for this imp (site id , placement id, test)
-// Errors in parameters do not break imp flow, and thus are not returned
-func getBidParameters(imp openrtb2.Imp) openrtb_ext.ExtImpSspbc {
- var extBidder adapters.ExtImpBidder
- var extSSP openrtb_ext.ExtImpSspbc
-
- if err := jsonutil.Unmarshal(imp.Ext, &extBidder); err == nil {
- _ = jsonutil.Unmarshal(extBidder.Bidder, &extSSP)
- }
-
- return extSSP
+ return bidResponse, errors
}
-// getRequestType checks what kind of request we have. It can either be:
-// - a standard request, where all Imps have complete site / placement data
-// - a oneCodeRequest, where site / placement data has to be determined by server
-// - a test request, where server returns fixed example ads
-func getRequestType(request *openrtb2.BidRequest) int {
- incompleteImps := 0
-
- for _, imp := range request.Imp {
- // Read data for this imp
- extSSP := getBidParameters(imp)
-
- if extSSP.IsTest != 0 {
- return requestTypeTest
- }
-
- if extSSP.SiteId == "" || extSSP.Id == "" {
- incompleteImps += 1
+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.MarkupAudio:
+ return openrtb_ext.BidTypeAudio, nil
+ case openrtb2.MarkupNative:
+ return openrtb_ext.BidTypeNative, nil
+ default:
+ return "", &errortypes.BadServerResponse{
+ Message: fmt.Sprintf("unsupported MType: %d.", bid.MType),
}
}
-
- if incompleteImps > 0 {
- return requestTypeOneCode
- }
-
- return requestTypeStandard
}
-func formatSspBcRequest(request *openrtb2.BidRequest) (*openrtb2.BidRequest, error) {
- if request.Site == nil {
- return nil, errSiteNill
- }
-
- var siteID string
-
- // determine what kind of request we are dealing with
- requestType := getRequestType(request)
-
- for i, imp := range request.Imp {
- // read ext data for the impression
- extSSP := getBidParameters(imp)
-
- // store SiteID
- if extSSP.SiteId != "" {
- siteID = extSSP.SiteId
- }
-
- // save current imp.id (adUnit name) as imp.tagid
- // we will recover it in makeBids
- imp.TagID = imp.ID
-
- // if there is a placement id, and this is not a oneCodeRequest, use it in imp.id
- if extSSP.Id != "" && requestType != requestTypeOneCode {
- imp.ID = extSSP.Id
- }
-
- // check imp size and update e.ext - send pbslot, pbsize
- // inability to set bid.ext will cause request to be invalid
- impSize := getImpSize(imp)
- impExt := requestImpExt{
- Data: adSlotData{
- PbSlot: imp.TagID,
- PbSize: impSize,
- },
- }
-
- impExtJSON, err := json.Marshal(impExt)
- if err != nil {
- return nil, err
- }
- imp.Ext = impExtJSON
- // save updated imp
- request.Imp[i] = imp
- }
-
- siteCopy := *request.Site
- request.Site = &siteCopy
-
- /*
- update site ID
- for oneCode request it has to be blank
- for other requests it should be equal to
- SiteId from one of the bids
- */
- if requestType == requestTypeOneCode || siteID == "" {
- request.Site.ID = ""
- } else {
- request.Site.ID = siteID
- }
-
- // add domain info
- if siteURL, err := url.Parse(request.Site.Page); err == nil {
- request.Site.Domain = siteURL.Hostname()
- }
-
- // set TEST Flag
- if requestType == requestTypeTest {
- request.Test = 1
+func buildAdapterEndpoint(endpoint string, adapterVersion string) (string, error) {
+ endpointURL, err := url.Parse(endpoint)
+ if err != nil {
+ return "", fmt.Errorf("unable to parse endpoint URL: %w", err)
}
- return request, nil
-}
+ params := endpointURL.Query()
+ params.Add("bdver", adapterVersion)
+ endpointURL.RawQuery = params.Encode()
-// getImpIDs uses imp.TagID instead of imp.ID as formattedRequest stores imp.ID in imp.TagID
-func getImpIDs(imps []openrtb2.Imp) []string {
- impIDs := make([]string, len(imps))
- for i := range imps {
- impIDs[i] = imps[i].TagID
- }
- return impIDs
+ return endpointURL.String(), nil
}
diff --git a/adapters/sspBC/sspbctest/exemplary/banner-fromtemplate.json b/adapters/sspBC/sspbctest/exemplary/banner-fromtemplate.json
deleted file mode 100644
index a8af9496124..00000000000
--- a/adapters/sspBC/sspbctest/exemplary/banner-fromtemplate.json
+++ /dev/null
@@ -1,153 +0,0 @@
-{
- "mockBidRequest": {
- "id": "test-request",
- "imp": [
- {
- "id": "slot",
- "ext": {
- "bidder": {
- "siteId": "237503",
- "id": "005"
- }
- },
- "secure": 1,
- "banner": {
- "format": [
- {
- "w": 300,
- "h": 250
- }
- ]
- }
- }
- ],
- "site": {
- "domain": "test.page",
- "page": "https://test.page/",
- "ref": "https://test.referer/"
- },
- "regs": {
- "ext": {
- "gdpr": 1
- }
- },
- "user": {
- "ext": {
- "consent": "test_consent"
- },
- "buyeruid": "test_user"
- }
- },
- "httpCalls": [
- {
- "expectedRequest": {
- "uri": "http://ssp.wp.test/bidder/?bdver=5.8&inver=4",
- "body": {
- "id": "test-request",
- "imp": [
- {
- "id": "005",
- "banner": {
- "format": [
- {
- "w": 300,
- "h": 250
- }
- ]
- },
- "tagid": "slot",
- "secure": 1,
- "ext": {
- "data": {
- "pbslot": "slot",
- "pbsize": "300x250"
- }
- }
- }
- ],
- "site": {
- "id": "237503",
- "domain": "test.page",
- "page": "https://test.page/",
- "ref": "https://test.referer/"
- },
- "user": {
- "ext": {
- "consent": "test_consent"
- },
- "buyeruid": "test_user"
- },
- "regs": {
- "ext": {
- "gdpr": 1
- }
- }
- },
- "impIDs":["slot"]
- },
- "mockResponse": {
- "status": 200,
- "body": {
- "cur": "USD",
- "id": "test-request",
- "seatbid": [
- {
- "bid": [
- {
- "adm": "some-test-ad",
- "adomain": [
- "sspbc-test"
- ],
- "crid": "1234",
- "ext": {
- "adlabel": "Reklama",
- "pubid": "431",
- "siteid": "237503",
- "slotid": "005",
- "tagid": "slot"
- },
- "w": 300,
- "h": 250,
- "id": "response-005",
- "impid": "005",
- "price": 20
- }
- ],
- "seat": "sspbc-test"
- }
- ],
- "sn": "sspbc-test"
- }
- }
- }
- ],
- "expectedBidResponses": [
- {
- "currency": "USD",
- "bids": [
- {
- "bid": {
- "id": "response-005",
- "impid": "slot",
- "price": 20,
- "adm": "<\/title>