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

     1  package sonobi
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"github.com/prebid/openrtb/v20/openrtb2"
     9  	"github.com/prebid/prebid-server/v2/adapters"
    10  	"github.com/prebid/prebid-server/v2/config"
    11  	"github.com/prebid/prebid-server/v2/errortypes"
    12  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    13  )
    14  
    15  // SonobiAdapter - Sonobi SonobiAdapter definition
    16  type SonobiAdapter struct {
    17  	URI string
    18  }
    19  
    20  // Builder builds a new instance of the Sonobi adapter for the given bidder with the given config.
    21  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    22  	bidder := &SonobiAdapter{
    23  		URI: config.Endpoint,
    24  	}
    25  	return bidder, nil
    26  }
    27  
    28  // MakeRequests Makes the OpenRTB request payload
    29  func (a *SonobiAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    30  	var errs []error
    31  	var sonobiExt openrtb_ext.ExtImpSonobi
    32  	var err error
    33  
    34  	var adapterRequests []*adapters.RequestData
    35  
    36  	// Sonobi currently only supports 1 imp per request to sonobi.
    37  	// Loop over the imps from the initial bid request to form many adapter requests to sonobi with only 1 imp.
    38  	for _, imp := range request.Imp {
    39  		// Make a copy as we don't want to change the original request
    40  		reqCopy := *request
    41  		reqCopy.Imp = append(make([]openrtb2.Imp, 0, 1), imp)
    42  
    43  		var bidderExt adapters.ExtImpBidder
    44  		if err = json.Unmarshal(reqCopy.Imp[0].Ext, &bidderExt); err != nil {
    45  			errs = append(errs, err)
    46  			continue
    47  		}
    48  
    49  		if err = json.Unmarshal(bidderExt.Bidder, &sonobiExt); err != nil {
    50  			errs = append(errs, err)
    51  			continue
    52  		}
    53  
    54  		reqCopy.Imp[0].TagID = sonobiExt.TagID
    55  
    56  		adapterReq, errors := a.makeRequest(&reqCopy)
    57  		if adapterReq != nil {
    58  			adapterRequests = append(adapterRequests, adapterReq)
    59  		}
    60  		errs = append(errs, errors...)
    61  	}
    62  
    63  	return adapterRequests, errs
    64  
    65  }
    66  
    67  // makeRequest helper method to crete the http request data
    68  func (a *SonobiAdapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, []error) {
    69  
    70  	var errs []error
    71  
    72  	reqJSON, err := json.Marshal(request)
    73  
    74  	if err != nil {
    75  		errs = append(errs, err)
    76  		return nil, errs
    77  	}
    78  
    79  	headers := http.Header{}
    80  	headers.Add("Content-Type", "application/json;charset=utf-8")
    81  	headers.Add("Accept", "application/json")
    82  	return &adapters.RequestData{
    83  		Method:  "POST",
    84  		Uri:     a.URI,
    85  		Body:    reqJSON,
    86  		Headers: headers,
    87  		ImpIDs:  openrtb_ext.GetImpIDs(request.Imp),
    88  	}, errs
    89  }
    90  
    91  // MakeBids makes the bids
    92  func (a *SonobiAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
    93  	var errs []error
    94  
    95  	if response.StatusCode == http.StatusNoContent {
    96  		return nil, nil
    97  	}
    98  
    99  	if response.StatusCode == http.StatusBadRequest {
   100  		return nil, []error{&errortypes.BadInput{
   101  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
   102  		}}
   103  	}
   104  
   105  	if response.StatusCode != http.StatusOK {
   106  		return nil, []error{&errortypes.BadServerResponse{
   107  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
   108  		}}
   109  	}
   110  
   111  	var bidResp openrtb2.BidResponse
   112  
   113  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   114  		return nil, []error{err}
   115  	}
   116  
   117  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)
   118  
   119  	for _, sb := range bidResp.SeatBid {
   120  		for i := range sb.Bid {
   121  			bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp)
   122  			if err != nil {
   123  				errs = append(errs, err)
   124  			} else {
   125  				b := &adapters.TypedBid{
   126  					Bid:     &sb.Bid[i],
   127  					BidType: bidType,
   128  				}
   129  				bidResponse.Bids = append(bidResponse.Bids, b)
   130  			}
   131  		}
   132  	}
   133  	return bidResponse, errs
   134  }
   135  
   136  func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
   137  	mediaType := openrtb_ext.BidTypeBanner
   138  	for _, imp := range imps {
   139  		if imp.ID == impID {
   140  			if imp.Banner == nil && imp.Video != nil {
   141  				mediaType = openrtb_ext.BidTypeVideo
   142  			}
   143  			return mediaType, nil
   144  		}
   145  	}
   146  
   147  	// This shouldnt happen. Lets handle it just incase by returning an error.
   148  	return "", &errortypes.BadInput{
   149  		Message: fmt.Sprintf("Failed to find impression \"%s\" ", impID),
   150  	}
   151  }