github.com/prebid/prebid-server@v0.275.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/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  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  	}, errs
   147  }
   148  
   149  func buildImpBanner(imp *openrtb2.Imp) error {
   150  
   151  	if imp.Banner == nil {
   152  		return &errortypes.BadInput{
   153  			Message: fmt.Sprintf("We need a Banner Object in the request"),
   154  		}
   155  	}
   156  
   157  	if imp.Banner.W == nil && imp.Banner.H == nil {
   158  		bannerCopy := *imp.Banner
   159  		banner := &bannerCopy
   160  
   161  		if len(banner.Format) == 0 {
   162  			return &errortypes.BadInput{
   163  				Message: fmt.Sprintf("At least one size is required"),
   164  			}
   165  		}
   166  		format := banner.Format[0]
   167  		banner.W = &format.W
   168  		banner.H = &format.H
   169  		imp.Banner = banner
   170  	}
   171  
   172  	return nil
   173  }
   174  
   175  func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
   176  	mediaType := openrtb_ext.BidTypeBanner
   177  	for _, imp := range imps {
   178  		if imp.ID == impID {
   179  			return mediaType, nil
   180  		}
   181  	}
   182  
   183  	// This shouldnt happen. Lets handle it just incase by returning an error.
   184  	return "", &errortypes.BadInput{
   185  		Message: fmt.Sprintf("Failed to find impression %s ", impID),
   186  	}
   187  }