github.com/prebid/prebid-server@v0.275.0/adapters/conversant/conversant.go (about)

     1  package conversant
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"github.com/prebid/openrtb/v19/adcom1"
     9  	"github.com/prebid/openrtb/v19/openrtb2"
    10  	"github.com/prebid/prebid-server/adapters"
    11  	"github.com/prebid/prebid-server/config"
    12  	"github.com/prebid/prebid-server/errortypes"
    13  	"github.com/prebid/prebid-server/openrtb_ext"
    14  )
    15  
    16  type ConversantAdapter struct {
    17  	URI string
    18  }
    19  
    20  func (c *ConversantAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    21  	for i := 0; i < len(request.Imp); i++ {
    22  		var bidderExt adapters.ExtImpBidder
    23  		if err := json.Unmarshal(request.Imp[i].Ext, &bidderExt); err != nil {
    24  			return nil, []error{&errortypes.BadInput{
    25  				Message: fmt.Sprintf("Impression[%d] missing ext object", i),
    26  			}}
    27  		}
    28  
    29  		var cnvrExt openrtb_ext.ExtImpConversant
    30  		if err := json.Unmarshal(bidderExt.Bidder, &cnvrExt); err != nil {
    31  			return nil, []error{&errortypes.BadInput{
    32  				Message: fmt.Sprintf("Impression[%d] missing ext.bidder object", i),
    33  			}}
    34  		}
    35  
    36  		if cnvrExt.SiteID == "" {
    37  			return nil, []error{&errortypes.BadInput{
    38  				Message: fmt.Sprintf("Impression[%d] requires ext.bidder.site_id", i),
    39  			}}
    40  		}
    41  
    42  		if i == 0 {
    43  			if request.Site != nil {
    44  				tmpSite := *request.Site
    45  				request.Site = &tmpSite
    46  				request.Site.ID = cnvrExt.SiteID
    47  			} else if request.App != nil {
    48  				tmpApp := *request.App
    49  				request.App = &tmpApp
    50  				request.App.ID = cnvrExt.SiteID
    51  			}
    52  		}
    53  		parseCnvrParams(&request.Imp[i], cnvrExt)
    54  	}
    55  
    56  	//create the request body
    57  	data, err := json.Marshal(request)
    58  	if err != nil {
    59  		return nil, []error{&errortypes.BadInput{
    60  			Message: "Error in packaging request to JSON",
    61  		}}
    62  	}
    63  	headers := http.Header{}
    64  	headers.Add("Content-Type", "application/json;charset=utf-8")
    65  	headers.Add("Accept", "application/json")
    66  
    67  	return []*adapters.RequestData{{
    68  		Method:  "POST",
    69  		Uri:     c.URI,
    70  		Body:    data,
    71  		Headers: headers,
    72  	}}, nil
    73  }
    74  
    75  func parseCnvrParams(imp *openrtb2.Imp, cnvrExt openrtb_ext.ExtImpConversant) {
    76  	imp.DisplayManager = "prebid-s2s"
    77  	imp.DisplayManagerVer = "2.0.0"
    78  
    79  	if imp.BidFloor <= 0 && cnvrExt.BidFloor > 0 {
    80  		imp.BidFloor = cnvrExt.BidFloor
    81  	}
    82  
    83  	if len(cnvrExt.TagID) > 0 {
    84  		imp.TagID = cnvrExt.TagID
    85  	}
    86  
    87  	// Take care not to override the global secure flag
    88  	if (imp.Secure == nil || *imp.Secure == 0) && cnvrExt.Secure != nil {
    89  		imp.Secure = cnvrExt.Secure
    90  	}
    91  
    92  	var position *adcom1.PlacementPosition
    93  	if cnvrExt.Position != nil {
    94  		position = adcom1.PlacementPosition(*cnvrExt.Position).Ptr()
    95  	}
    96  	if imp.Banner != nil {
    97  		tmpBanner := *imp.Banner
    98  		imp.Banner = &tmpBanner
    99  		imp.Banner.Pos = position
   100  
   101  	} else if imp.Video != nil {
   102  		tmpVideo := *imp.Video
   103  		imp.Video = &tmpVideo
   104  		imp.Video.Pos = position
   105  
   106  		if len(cnvrExt.API) > 0 {
   107  			imp.Video.API = make([]adcom1.APIFramework, 0, len(cnvrExt.API))
   108  			for _, api := range cnvrExt.API {
   109  				imp.Video.API = append(imp.Video.API, adcom1.APIFramework(api))
   110  			}
   111  		}
   112  
   113  		// Include protocols, mimes, and max duration if specified
   114  		// These properties can also be specified in ad unit's video object,
   115  		// but are overridden if the custom params object also contains them.
   116  
   117  		if len(cnvrExt.Protocols) > 0 {
   118  			imp.Video.Protocols = make([]adcom1.MediaCreativeSubtype, 0, len(cnvrExt.Protocols))
   119  			for _, protocol := range cnvrExt.Protocols {
   120  				imp.Video.Protocols = append(imp.Video.Protocols, adcom1.MediaCreativeSubtype(protocol))
   121  			}
   122  		}
   123  
   124  		if len(cnvrExt.MIMEs) > 0 {
   125  			imp.Video.MIMEs = make([]string, len(cnvrExt.MIMEs))
   126  			copy(imp.Video.MIMEs, cnvrExt.MIMEs)
   127  		}
   128  
   129  		if cnvrExt.MaxDuration != nil {
   130  			imp.Video.MaxDuration = *cnvrExt.MaxDuration
   131  		}
   132  	}
   133  }
   134  
   135  func (c *ConversantAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   136  	if response.StatusCode == http.StatusNoContent {
   137  		return nil, nil // no bid response
   138  	}
   139  
   140  	if response.StatusCode != http.StatusOK {
   141  		return nil, []error{&errortypes.BadServerResponse{
   142  			Message: fmt.Sprintf("Unexpected status code: %d", response.StatusCode),
   143  		}}
   144  	}
   145  
   146  	var resp openrtb2.BidResponse
   147  	if err := json.Unmarshal(response.Body, &resp); err != nil {
   148  		return nil, []error{&errortypes.BadServerResponse{
   149  			Message: fmt.Sprintf("bad server response: %d. ", err),
   150  		}}
   151  	}
   152  
   153  	if len(resp.SeatBid) == 0 {
   154  		return nil, []error{&errortypes.BadServerResponse{
   155  			Message: "Empty bid request",
   156  		}}
   157  	}
   158  
   159  	bids := resp.SeatBid[0].Bid
   160  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bids))
   161  	for i := 0; i < len(bids); i++ {
   162  		bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   163  			Bid:     &bids[i],
   164  			BidType: getBidType(bids[i].ImpID, internalRequest.Imp),
   165  		})
   166  	}
   167  	return bidResponse, nil
   168  }
   169  
   170  func getBidType(impId string, imps []openrtb2.Imp) openrtb_ext.BidType {
   171  	bidType := openrtb_ext.BidTypeBanner
   172  	for _, imp := range imps {
   173  		if imp.ID == impId {
   174  			if imp.Video != nil {
   175  				bidType = openrtb_ext.BidTypeVideo
   176  			}
   177  			break
   178  		}
   179  	}
   180  	return bidType
   181  }
   182  
   183  // Builder builds a new instance of the Conversant adapter for the given bidder with the given config.
   184  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
   185  	bidder := &ConversantAdapter{
   186  		URI: config.Endpoint,
   187  	}
   188  	return bidder, nil
   189  }