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

     1  package deepintent
     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  const displayManager string = "di_prebid"
    16  const displayManagerVer string = "2.0.0"
    17  
    18  // DeepintentAdapter struct
    19  type DeepintentAdapter struct {
    20  	URI string
    21  }
    22  
    23  // Builder builds a new instance of the Deepintent adapter for the given bidder with the given config.
    24  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    25  	bidder := &DeepintentAdapter{
    26  		URI: config.Endpoint,
    27  	}
    28  	return bidder, nil
    29  }
    30  
    31  // MakeRequests which creates request object for Deepintent DSP
    32  func (d *DeepintentAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    33  	var errs []error
    34  	var deepintentExt openrtb_ext.ExtImpDeepintent
    35  	var err error
    36  
    37  	var adapterRequests []*adapters.RequestData
    38  
    39  	reqCopy := *request
    40  	for _, imp := range request.Imp {
    41  		reqCopy.Imp = []openrtb2.Imp{imp}
    42  
    43  		var bidderExt adapters.ExtImpBidder
    44  		if err = json.Unmarshal(reqCopy.Imp[0].Ext, &bidderExt); err != nil {
    45  			errs = append(errs, &errortypes.BadInput{
    46  				Message: fmt.Sprintf("Impression id=%s has an Error: %s", imp.ID, err.Error()),
    47  			})
    48  			continue
    49  		}
    50  
    51  		if err = json.Unmarshal(bidderExt.Bidder, &deepintentExt); err != nil {
    52  			errs = append(errs, &errortypes.BadInput{
    53  				Message: fmt.Sprintf("Impression id=%s, has invalid Ext", imp.ID),
    54  			})
    55  			continue
    56  		}
    57  
    58  		reqCopy.Imp[0].TagID = deepintentExt.TagID
    59  		reqCopy.Imp[0].DisplayManager = displayManager
    60  		reqCopy.Imp[0].DisplayManagerVer = displayManagerVer
    61  
    62  		adapterReq, errors := d.preprocess(reqCopy)
    63  		if errors != nil {
    64  			errs = append(errs, errors...)
    65  		}
    66  		if adapterReq != nil {
    67  			adapterRequests = append(adapterRequests, adapterReq)
    68  		}
    69  
    70  	}
    71  	return adapterRequests, errs
    72  }
    73  
    74  // MakeBids makes the bids
    75  func (d *DeepintentAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
    76  	var errs []error
    77  
    78  	if response.StatusCode == http.StatusNoContent {
    79  		return nil, nil
    80  	}
    81  
    82  	if response.StatusCode != http.StatusOK {
    83  		return nil, []error{&errortypes.BadServerResponse{
    84  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode),
    85  		}}
    86  	}
    87  
    88  	var bidResp openrtb2.BidResponse
    89  
    90  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
    91  		return nil, []error{err}
    92  	}
    93  
    94  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
    95  
    96  	for _, sb := range bidResp.SeatBid {
    97  		for i := range sb.Bid {
    98  			bidType, err := getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp)
    99  			if err != nil {
   100  				errs = append(errs, err)
   101  			} else {
   102  				b := &adapters.TypedBid{
   103  					Bid:     &sb.Bid[i],
   104  					BidType: bidType,
   105  				}
   106  				bidResponse.Bids = append(bidResponse.Bids, b)
   107  			}
   108  		}
   109  	}
   110  	return bidResponse, errs
   111  }
   112  
   113  func (d *DeepintentAdapter) preprocess(request openrtb2.BidRequest) (*adapters.RequestData, []error) {
   114  
   115  	var errs []error
   116  	impsCount := len(request.Imp)
   117  	resImps := make([]openrtb2.Imp, 0, impsCount)
   118  
   119  	for _, imp := range request.Imp {
   120  
   121  		if err := buildImpBanner(&imp); err != nil {
   122  			errs = append(errs, err)
   123  			continue
   124  		}
   125  		resImps = append(resImps, imp)
   126  	}
   127  	request.Imp = resImps
   128  	if errs != nil {
   129  		return nil, errs
   130  	}
   131  	reqJSON, err := json.Marshal(request)
   132  
   133  	if err != nil {
   134  		errs = append(errs, err)
   135  		return nil, errs
   136  	}
   137  
   138  	headers := http.Header{}
   139  	headers.Add("Content-Type", "application/json;charset=utf-8")
   140  	headers.Add("Accept", "application/json")
   141  	return &adapters.RequestData{
   142  		Method:  "POST",
   143  		Uri:     d.URI,
   144  		Body:    reqJSON,
   145  		Headers: headers,
   146  		ImpIDs:  openrtb_ext.GetImpIDs(request.Imp),
   147  	}, errs
   148  }
   149  
   150  func buildImpBanner(imp *openrtb2.Imp) error {
   151  
   152  	if imp.Banner == nil {
   153  		return &errortypes.BadInput{
   154  			Message: fmt.Sprintf("We need a Banner Object in the request"),
   155  		}
   156  	}
   157  
   158  	if imp.Banner.W == nil && imp.Banner.H == nil {
   159  		bannerCopy := *imp.Banner
   160  		banner := &bannerCopy
   161  
   162  		if len(banner.Format) == 0 {
   163  			return &errortypes.BadInput{
   164  				Message: fmt.Sprintf("At least one size is required"),
   165  			}
   166  		}
   167  		format := banner.Format[0]
   168  		banner.W = &format.W
   169  		banner.H = &format.H
   170  		imp.Banner = banner
   171  	}
   172  
   173  	return nil
   174  }
   175  
   176  func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
   177  	mediaType := openrtb_ext.BidTypeBanner
   178  	for _, imp := range imps {
   179  		if imp.ID == impID {
   180  			return mediaType, nil
   181  		}
   182  	}
   183  
   184  	// This shouldnt happen. Lets handle it just incase by returning an error.
   185  	return "", &errortypes.BadInput{
   186  		Message: fmt.Sprintf("Failed to find impression %s ", impID),
   187  	}
   188  }