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

     1  package smartrtb
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"text/template"
     8  
     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/macros"
    14  	"github.com/prebid/prebid-server/openrtb_ext"
    15  )
    16  
    17  // Base adapter structure.
    18  type SmartRTBAdapter struct {
    19  	EndpointTemplate *template.Template
    20  }
    21  
    22  // Bid request extension appended to downstream request.
    23  // PubID are non-empty iff request.{App,Site} or
    24  // request.{App,Site}.Publisher are nil, respectively.
    25  type bidRequestExt struct {
    26  	PubID    string `json:"pub_id,omitempty"`
    27  	ZoneID   string `json:"zone_id,omitempty"`
    28  	ForceBid bool   `json:"force_bid,omitempty"`
    29  }
    30  
    31  // bidExt.CreativeType values.
    32  const (
    33  	creativeTypeBanner string = "BANNER"
    34  	creativeTypeVideo         = "VIDEO"
    35  	creativeTypeNative        = "NATIVE"
    36  	creativeTypeAudio         = "AUDIO"
    37  )
    38  
    39  // Bid response extension from downstream.
    40  type bidExt struct {
    41  	CreativeType string `json:"format"`
    42  }
    43  
    44  // Builder builds a new instance of the SmartRTB adapter for the given bidder with the given config.
    45  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    46  	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
    47  	if err != nil {
    48  		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
    49  	}
    50  
    51  	bidder := &SmartRTBAdapter{
    52  		EndpointTemplate: template,
    53  	}
    54  	return bidder, nil
    55  }
    56  
    57  func (adapter *SmartRTBAdapter) buildEndpointURL(pubID string) (string, error) {
    58  	endpointParams := macros.EndpointTemplateParams{PublisherID: pubID}
    59  	return macros.ResolveMacros(adapter.EndpointTemplate, endpointParams)
    60  }
    61  
    62  func parseExtImp(dst *bidRequestExt, imp *openrtb2.Imp) error {
    63  	var ext adapters.ExtImpBidder
    64  	if err := json.Unmarshal(imp.Ext, &ext); err != nil {
    65  		return &errortypes.BadInput{
    66  			Message: err.Error(),
    67  		}
    68  	}
    69  
    70  	var src openrtb_ext.ExtImpSmartRTB
    71  	if err := json.Unmarshal(ext.Bidder, &src); err != nil {
    72  		return &errortypes.BadInput{
    73  			Message: err.Error(),
    74  		}
    75  	}
    76  
    77  	if dst.PubID == "" {
    78  		dst.PubID = src.PubID
    79  	}
    80  
    81  	if src.ZoneID != "" {
    82  		imp.TagID = src.ZoneID
    83  	}
    84  	return nil
    85  }
    86  
    87  func (s *SmartRTBAdapter) MakeRequests(brq *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    88  	var imps []openrtb2.Imp
    89  	var err error
    90  	ext := bidRequestExt{}
    91  	nrImps := len(brq.Imp)
    92  	errs := make([]error, 0, nrImps)
    93  
    94  	for i := 0; i < nrImps; i++ {
    95  		imp := brq.Imp[i]
    96  		if imp.Banner == nil && imp.Video == nil {
    97  			continue
    98  		}
    99  
   100  		err = parseExtImp(&ext, &imp)
   101  		if err != nil {
   102  			errs = append(errs, err)
   103  			continue
   104  		}
   105  
   106  		imps = append(imps, imp)
   107  	}
   108  
   109  	if len(imps) == 0 {
   110  		return nil, errs
   111  	}
   112  
   113  	if ext.PubID == "" {
   114  		return nil, append(errs, &errortypes.BadInput{Message: "Cannot infer publisher ID from bid ext"})
   115  	}
   116  
   117  	brq.Ext, err = json.Marshal(ext)
   118  	if err != nil {
   119  		return nil, append(errs, err)
   120  	}
   121  
   122  	brq.Imp = imps
   123  
   124  	rq, err := json.Marshal(brq)
   125  	if err != nil {
   126  		return nil, append(errs, err)
   127  	}
   128  
   129  	url, err := s.buildEndpointURL(ext.PubID)
   130  	if err != nil {
   131  		return nil, append(errs, err)
   132  	}
   133  
   134  	headers := http.Header{}
   135  	headers.Add("Content-Type", "application/json;charset=utf-8")
   136  	headers.Add("Accept", "application/json")
   137  	headers.Add("x-openrtb-version", "2.5")
   138  	return []*adapters.RequestData{{
   139  		Method:  "POST",
   140  		Uri:     url,
   141  		Body:    rq,
   142  		Headers: headers,
   143  	}}, errs
   144  }
   145  
   146  func (s *SmartRTBAdapter) MakeBids(
   147  	brq *openrtb2.BidRequest, drq *adapters.RequestData,
   148  	rs *adapters.ResponseData,
   149  ) (*adapters.BidderResponse, []error) {
   150  	if rs.StatusCode == http.StatusNoContent {
   151  		return nil, nil
   152  	} else if rs.StatusCode == http.StatusBadRequest {
   153  		return nil, []error{&errortypes.BadInput{Message: "Invalid request."}}
   154  	} else if rs.StatusCode != http.StatusOK {
   155  		return nil, []error{&errortypes.BadServerResponse{
   156  			Message: fmt.Sprintf("Unexpected HTTP status %d.", rs.StatusCode),
   157  		}}
   158  	}
   159  
   160  	var brs openrtb2.BidResponse
   161  	if err := json.Unmarshal(rs.Body, &brs); err != nil {
   162  		return nil, []error{err}
   163  	}
   164  
   165  	rv := adapters.NewBidderResponseWithBidsCapacity(5)
   166  	for _, seat := range brs.SeatBid {
   167  		for i := range seat.Bid {
   168  			var ext bidExt
   169  			if err := json.Unmarshal(seat.Bid[i].Ext, &ext); err != nil {
   170  				return nil, []error{&errortypes.BadServerResponse{
   171  					Message: "Invalid bid extension from endpoint.",
   172  				}}
   173  			}
   174  
   175  			var btype openrtb_ext.BidType
   176  			switch ext.CreativeType {
   177  			case creativeTypeBanner:
   178  				btype = openrtb_ext.BidTypeBanner
   179  			case creativeTypeVideo:
   180  				btype = openrtb_ext.BidTypeVideo
   181  			default:
   182  				return nil, []error{&errortypes.BadServerResponse{
   183  					Message: fmt.Sprintf("Unsupported creative type %s.",
   184  						ext.CreativeType),
   185  				}}
   186  			}
   187  
   188  			seat.Bid[i].Ext = nil
   189  
   190  			rv.Bids = append(rv.Bids, &adapters.TypedBid{
   191  				Bid:     &seat.Bid[i],
   192  				BidType: btype,
   193  			})
   194  		}
   195  	}
   196  	return rv, nil
   197  }