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

     1  package trafficgate
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"text/template"
     8  
     9  	"github.com/prebid/openrtb/v20/openrtb2"
    10  	"github.com/prebid/prebid-server/v2/adapters"
    11  	"github.com/prebid/prebid-server/v2/config"
    12  	"github.com/prebid/prebid-server/v2/errortypes"
    13  	"github.com/prebid/prebid-server/v2/macros"
    14  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    15  )
    16  
    17  type adapter struct {
    18  	EndpointTemplate *template.Template
    19  }
    20  
    21  type BidResponseExt struct {
    22  	Prebid struct {
    23  		Type string
    24  	}
    25  }
    26  
    27  func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    28  
    29  	headers := http.Header{
    30  		"Content-Type": {"application/json"},
    31  		"Accept":       {"application/json"},
    32  	}
    33  
    34  	// Pull the host and source ID info from the bidder params.
    35  	reqImps, err := splitImpressions(request.Imp)
    36  	if err != nil {
    37  		return nil, []error{err}
    38  	}
    39  
    40  	requests := []*adapters.RequestData{}
    41  
    42  	var errs []error
    43  	for reqExt, reqImp := range reqImps {
    44  		request.Imp = reqImp
    45  		reqJson, err := json.Marshal(request)
    46  
    47  		if err != nil {
    48  			errs = append(errs, err)
    49  			continue
    50  		}
    51  
    52  		urlParams := macros.EndpointTemplateParams{Host: reqExt.Host}
    53  		url, err := macros.ResolveMacros(a.EndpointTemplate, urlParams)
    54  
    55  		if err != nil {
    56  			errs = append(errs, err)
    57  			continue
    58  		}
    59  
    60  		request := adapters.RequestData{
    61  			Method:  "POST",
    62  			Uri:     url,
    63  			Body:    reqJson,
    64  			Headers: headers,
    65  			ImpIDs:  openrtb_ext.GetImpIDs(request.Imp)}
    66  
    67  		requests = append(requests, &request)
    68  	}
    69  
    70  	return requests, errs
    71  }
    72  
    73  /*
    74  internal original request in OpenRTB, external = result of us having converted it (what comes out of MakeRequests)
    75  */
    76  func (a *adapter) MakeBids(
    77  	internalRequest *openrtb2.BidRequest,
    78  	externalRequest *adapters.RequestData,
    79  	response *adapters.ResponseData,
    80  ) (*adapters.BidderResponse, []error) {
    81  
    82  	if response.StatusCode == http.StatusNoContent {
    83  		return nil, nil
    84  	}
    85  
    86  	if response.StatusCode != http.StatusOK {
    87  		return nil, []error{&errortypes.BadServerResponse{
    88  			Message: fmt.Sprintf("Error response with status %d", response.StatusCode),
    89  		}}
    90  	}
    91  
    92  	var bidResp openrtb2.BidResponse
    93  
    94  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
    95  		return nil, []error{&errortypes.BadServerResponse{
    96  			Message: err.Error(),
    97  		}}
    98  	}
    99  
   100  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(internalRequest.Imp))
   101  	bidResponse.Currency = bidResp.Cur
   102  
   103  	for _, seatBid := range bidResp.SeatBid {
   104  		for i := range seatBid.Bid {
   105  			var bidExt BidResponseExt
   106  			if err := json.Unmarshal(seatBid.Bid[i].Ext, &bidExt); err != nil {
   107  				return nil, []error{&errortypes.BadServerResponse{
   108  					Message: fmt.Sprintf("Missing response ext"),
   109  				}}
   110  			}
   111  			if len(bidExt.Prebid.Type) < 1 {
   112  				return nil, []error{&errortypes.BadServerResponse{
   113  					Message: fmt.Sprintf("Unable to read bid.ext.prebid.type"),
   114  				}}
   115  			}
   116  			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   117  				Bid:     &seatBid.Bid[i],
   118  				BidType: getMediaTypeForImp(bidExt.Prebid.Type),
   119  			})
   120  		}
   121  	}
   122  
   123  	return bidResponse, nil
   124  }
   125  
   126  func splitImpressions(imps []openrtb2.Imp) (map[openrtb_ext.ExtImpTrafficGate][]openrtb2.Imp, error) {
   127  
   128  	var multipleImps = make(map[openrtb_ext.ExtImpTrafficGate][]openrtb2.Imp)
   129  
   130  	for _, imp := range imps {
   131  		bidderParams, err := getBidderParams(&imp)
   132  		if err != nil {
   133  			return nil, err
   134  		}
   135  
   136  		multipleImps[*bidderParams] = append(multipleImps[*bidderParams], imp)
   137  	}
   138  
   139  	return multipleImps, nil
   140  }
   141  
   142  func getBidderParams(imp *openrtb2.Imp) (*openrtb_ext.ExtImpTrafficGate, error) {
   143  	var bidderExt adapters.ExtImpBidder
   144  	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
   145  		return nil, &errortypes.BadInput{
   146  			Message: fmt.Sprintf("Missing bidder ext"),
   147  		}
   148  	}
   149  	var TrafficGateExt openrtb_ext.ExtImpTrafficGate
   150  	if err := json.Unmarshal(bidderExt.Bidder, &TrafficGateExt); err != nil {
   151  		return nil, &errortypes.BadInput{
   152  			Message: fmt.Sprintf("Bidder parameters required"),
   153  		}
   154  	}
   155  
   156  	return &TrafficGateExt, nil
   157  }
   158  
   159  func getMediaTypeForImp(bidType string) openrtb_ext.BidType {
   160  	switch bidType {
   161  	case "video":
   162  		return openrtb_ext.BidTypeVideo
   163  	case "native":
   164  		return openrtb_ext.BidTypeNative
   165  	case "audio":
   166  		return openrtb_ext.BidTypeAudio
   167  	}
   168  	return openrtb_ext.BidTypeBanner
   169  }
   170  
   171  // Builder builds a new instance of the TrafficGate adapter for the given bidder with the given config.
   172  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
   173  	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
   174  	if err != nil {
   175  		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
   176  	}
   177  
   178  	bidder := &adapter{
   179  		EndpointTemplate: template,
   180  	}
   181  	return bidder, nil
   182  }