github.com/prebid/prebid-server@v0.275.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/v19/openrtb2"
     9  	"github.com/prebid/prebid-server/adapters"
    10  	"github.com/prebid/prebid-server/config"
    11  	"github.com/prebid/prebid-server/errortypes"
    12  	"github.com/prebid/prebid-server/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  	}, errs
    88  }
    89  
    90  // MakeBids makes the bids
    91  func (a *SonobiAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
    92  	var errs []error
    93  
    94  	if response.StatusCode == http.StatusNoContent {
    95  		return nil, nil
    96  	}
    97  
    98  	if response.StatusCode == http.StatusBadRequest {
    99  		return nil, []error{&errortypes.BadInput{
   100  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
   101  		}}
   102  	}
   103  
   104  	if response.StatusCode != http.StatusOK {
   105  		return nil, []error{&errortypes.BadServerResponse{
   106  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
   107  		}}
   108  	}
   109  
   110  	var bidResp openrtb2.BidResponse
   111  
   112  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   113  		return nil, []error{err}
   114  	}
   115  
   116  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)
   117  
   118  	for _, sb := range bidResp.SeatBid {
   119  		for i := range sb.Bid {
   120  			bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp)
   121  			if err != nil {
   122  				errs = append(errs, err)
   123  			} else {
   124  				b := &adapters.TypedBid{
   125  					Bid:     &sb.Bid[i],
   126  					BidType: bidType,
   127  				}
   128  				bidResponse.Bids = append(bidResponse.Bids, b)
   129  			}
   130  		}
   131  	}
   132  	return bidResponse, errs
   133  }
   134  
   135  func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
   136  	mediaType := openrtb_ext.BidTypeBanner
   137  	for _, imp := range imps {
   138  		if imp.ID == impID {
   139  			if imp.Banner == nil && imp.Video != nil {
   140  				mediaType = openrtb_ext.BidTypeVideo
   141  			}
   142  			return mediaType, nil
   143  		}
   144  	}
   145  
   146  	// This shouldnt happen. Lets handle it just incase by returning an error.
   147  	return "", &errortypes.BadInput{
   148  		Message: fmt.Sprintf("Failed to find impression \"%s\" ", impID),
   149  	}
   150  }