github.com/prebid/prebid-server/v2@v2.18.0/adapters/teads/teads.go (about)

     1  package teads
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"net/url"
     8  	"strconv"
     9  	"text/template"
    10  
    11  	"github.com/prebid/openrtb/v20/openrtb2"
    12  	"github.com/prebid/prebid-server/v2/adapters"
    13  	"github.com/prebid/prebid-server/v2/config"
    14  	"github.com/prebid/prebid-server/v2/errortypes"
    15  	"github.com/prebid/prebid-server/v2/macros"
    16  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    17  )
    18  
    19  // Builder builds a new instance of the Teads adapter for the given bidder with the given config.
    20  func Builder(_ openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    21  	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
    22  	if err != nil {
    23  		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
    24  	}
    25  
    26  	bidder := &adapter{
    27  		endpointTemplate: template,
    28  	}
    29  	return bidder, nil
    30  }
    31  
    32  func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    33  	if len(request.Imp) == 0 {
    34  		return nil, []error{&errortypes.BadInput{
    35  			Message: "No impression in the bid request",
    36  		}}
    37  	}
    38  
    39  	endpointURL, err := a.buildEndpointURL()
    40  	if endpointURL == "" {
    41  		return nil, []error{err}
    42  	}
    43  
    44  	if err := updateImpObject(request.Imp); err != nil {
    45  		return nil, []error{err}
    46  	}
    47  
    48  	reqJSON, err := json.Marshal(request)
    49  	if err != nil {
    50  		return nil, []error{&errortypes.BadInput{
    51  			Message: "Error parsing BidRequest object",
    52  		}}
    53  	}
    54  
    55  	headers := http.Header{}
    56  	headers.Add("Content-Type", "application/json;charset=utf-8")
    57  	return []*adapters.RequestData{{
    58  		Method:  "POST",
    59  		Uri:     endpointURL,
    60  		Body:    reqJSON,
    61  		Headers: headers,
    62  		ImpIDs:  openrtb_ext.GetImpIDs(request.Imp),
    63  	}}, []error{}
    64  }
    65  
    66  func updateImpObject(imps []openrtb2.Imp) error {
    67  	for i := range imps {
    68  		imp := &imps[i]
    69  
    70  		if imp.Banner != nil {
    71  			if len(imp.Banner.Format) != 0 {
    72  				bannerCopy := *imp.Banner
    73  				bannerCopy.H = &imp.Banner.Format[0].H
    74  				bannerCopy.W = &imp.Banner.Format[0].W
    75  				imp.Banner = &bannerCopy
    76  			}
    77  		}
    78  
    79  		var defaultImpExt defaultBidderImpExtension
    80  		if err := json.Unmarshal(imp.Ext, &defaultImpExt); err != nil {
    81  			return &errortypes.BadInput{
    82  				Message: "Error parsing Imp.Ext object",
    83  			}
    84  		}
    85  		if defaultImpExt.Bidder.PlacementId == 0 {
    86  			return &errortypes.BadInput{
    87  				Message: "placementId should not be 0.",
    88  			}
    89  		}
    90  		imp.TagID = strconv.Itoa(defaultImpExt.Bidder.PlacementId)
    91  		teadsImpExt := &teadsImpExtension{
    92  			KV: teadsKV{
    93  				PlacementId: defaultImpExt.Bidder.PlacementId,
    94  			},
    95  		}
    96  		if extJson, err := json.Marshal(teadsImpExt); err != nil {
    97  			return &errortypes.BadInput{
    98  				Message: "Error stringify Imp.Ext object",
    99  			}
   100  		} else {
   101  			imp.Ext = extJson
   102  		}
   103  	}
   104  	return nil
   105  }
   106  
   107  // Builds enpoint url based on adapter-specific pub settings from imp.ext
   108  func (a *adapter) buildEndpointURL() (string, error) {
   109  	endpointParams := macros.EndpointTemplateParams{}
   110  	host, err := macros.ResolveMacros(a.endpointTemplate, endpointParams)
   111  
   112  	if err != nil {
   113  		return "", &errortypes.BadInput{
   114  			Message: "Unable to parse endpoint url template: " + err.Error(),
   115  		}
   116  	}
   117  
   118  	thisURI, err := url.Parse(host)
   119  	if err != nil {
   120  		return "", &errortypes.BadInput{
   121  			Message: "Malformed URL: " + err.Error(),
   122  		}
   123  	}
   124  
   125  	return thisURI.String(), nil
   126  }
   127  
   128  func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, _ *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   129  	if adapters.IsResponseStatusCodeNoContent(response) {
   130  		return nil, nil
   131  	}
   132  
   133  	err := adapters.CheckResponseStatusCodeForErrors(response)
   134  	if err != nil {
   135  		return nil, []error{err}
   136  	}
   137  
   138  	var bidResp openrtb2.BidResponse
   139  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   140  		return nil, []error{err}
   141  	}
   142  
   143  	bidderResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid))
   144  
   145  	for _, sb := range bidResp.SeatBid {
   146  		for i := 0; i < len(sb.Bid); i++ {
   147  			bid := sb.Bid[i]
   148  
   149  			bidExtTeads, err := getTeadsRendererFromBidExt(bid.Ext)
   150  			if err != nil {
   151  				return nil, err
   152  			}
   153  			bidType, err := getMediaTypeForImp(bid.ImpID, internalRequest.Imp)
   154  			if err != nil {
   155  				return nil, err
   156  			}
   157  			bidderResponse.Bids = append(bidderResponse.Bids, &adapters.TypedBid{
   158  				Bid: &bid,
   159  				BidMeta: &openrtb_ext.ExtBidPrebidMeta{
   160  					RendererName:    bidExtTeads.Prebid.Meta.RendererName,
   161  					RendererVersion: bidExtTeads.Prebid.Meta.RendererVersion,
   162  				},
   163  				BidType: bidType,
   164  			})
   165  		}
   166  	}
   167  	if bidResp.Cur != "" {
   168  		bidderResponse.Currency = bidResp.Cur
   169  	}
   170  	return bidderResponse, nil
   171  }
   172  
   173  func getTeadsRendererFromBidExt(ext json.RawMessage) (*teadsBidExt, []error) {
   174  	var bidExtTeads teadsBidExt
   175  	if err := json.Unmarshal(ext, &bidExtTeads); err != nil {
   176  		return nil, []error{err}
   177  	}
   178  	if bidExtTeads.Prebid.Meta.RendererName == "" {
   179  		return nil, []error{&errortypes.BadInput{
   180  			Message: "RendererName should not be empty if present",
   181  		}}
   182  	}
   183  	if bidExtTeads.Prebid.Meta.RendererVersion == "" {
   184  		return nil, []error{&errortypes.BadInput{
   185  			Message: "RendererVersion should not be empty if present",
   186  		}}
   187  	}
   188  	return &bidExtTeads, nil
   189  }
   190  
   191  func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, []error) {
   192  	for _, imp := range imps {
   193  		if imp.ID == impID {
   194  			if imp.Video != nil {
   195  				return openrtb_ext.BidTypeVideo, nil
   196  			}
   197  			return openrtb_ext.BidTypeBanner, nil
   198  		}
   199  	}
   200  	return openrtb_ext.BidType(""), []error{&errortypes.BadInput{
   201  		Message: "Imp ids were not equals",
   202  	}}
   203  }