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

     1  package pulsepoint
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strconv"
     8  
     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  	"github.com/prebid/openrtb/v20/openrtb2"
    15  )
    16  
    17  type PulsePointAdapter struct {
    18  	URI string
    19  }
    20  
    21  // Builds an instance of PulsePointAdapter
    22  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    23  	bidder := &PulsePointAdapter{
    24  		URI: config.Endpoint,
    25  	}
    26  	return bidder, nil
    27  }
    28  
    29  func (a *PulsePointAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    30  	errs := make([]error, 0, len(request.Imp))
    31  
    32  	var err error
    33  	pubID := ""
    34  	imps := make([]openrtb2.Imp, 0, len(request.Imp))
    35  	for i := 0; i < len(request.Imp); i++ {
    36  		imp := request.Imp[i]
    37  		var bidderExt adapters.ExtImpBidder
    38  		if err = json.Unmarshal(imp.Ext, &bidderExt); err != nil {
    39  			errs = append(errs, &errortypes.BadInput{
    40  				Message: err.Error(),
    41  			})
    42  			continue
    43  		}
    44  		var pulsepointExt openrtb_ext.ExtImpPulsePoint
    45  		if err = json.Unmarshal(bidderExt.Bidder, &pulsepointExt); err != nil {
    46  			errs = append(errs, &errortypes.BadInput{
    47  				Message: err.Error(),
    48  			})
    49  			continue
    50  		}
    51  		// parse pubid and keep it for reference
    52  		if pubID == "" && pulsepointExt.PubID > 0 {
    53  			pubID = strconv.Itoa(pulsepointExt.PubID)
    54  		}
    55  		// tag id to be sent
    56  		imp.TagID = strconv.Itoa(pulsepointExt.TagID)
    57  		imps = append(imps, imp)
    58  	}
    59  
    60  	// verify there are valid impressions
    61  	if len(imps) == 0 {
    62  		return nil, errs
    63  	}
    64  
    65  	// add the publisher id from ext to the site.pub.id or app.pub.id
    66  	if request.Site != nil {
    67  		site := *request.Site
    68  		if site.Publisher != nil {
    69  			publisher := *site.Publisher
    70  			publisher.ID = pubID
    71  			site.Publisher = &publisher
    72  		} else {
    73  			site.Publisher = &openrtb2.Publisher{ID: pubID}
    74  		}
    75  		request.Site = &site
    76  	} else if request.App != nil {
    77  		app := *request.App
    78  		if app.Publisher != nil {
    79  			publisher := *app.Publisher
    80  			publisher.ID = pubID
    81  			app.Publisher = &publisher
    82  		} else {
    83  			app.Publisher = &openrtb2.Publisher{ID: pubID}
    84  		}
    85  		request.App = &app
    86  	}
    87  
    88  	request.Imp = imps
    89  	reqJSON, err := json.Marshal(request)
    90  	if err != nil {
    91  		errs = append(errs, err)
    92  		return nil, errs
    93  	}
    94  	headers := http.Header{}
    95  	headers.Add("Content-Type", "application/json;charset=utf-8")
    96  	headers.Add("Accept", "application/json")
    97  	return []*adapters.RequestData{{
    98  		Method:  "POST",
    99  		Uri:     a.URI,
   100  		Body:    reqJSON,
   101  		Headers: headers,
   102  		ImpIDs:  openrtb_ext.GetImpIDs(request.Imp),
   103  	}}, errs
   104  }
   105  
   106  func (a *PulsePointAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   107  	// passback
   108  	if response.StatusCode == http.StatusNoContent {
   109  		return nil, nil
   110  	}
   111  	// bad requests
   112  	if response.StatusCode == http.StatusBadRequest {
   113  		return nil, []error{&errortypes.BadInput{
   114  			Message: fmt.Sprintf("Bad user input: HTTP status %d", response.StatusCode),
   115  		}}
   116  	}
   117  	// error
   118  	if response.StatusCode != http.StatusOK {
   119  		return nil, []error{&errortypes.BadServerResponse{
   120  			Message: fmt.Sprintf("Bad server response: HTTP status %d", response.StatusCode),
   121  		}}
   122  	}
   123  	// parse response
   124  	var bidResp openrtb2.BidResponse
   125  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   126  		return nil, []error{err}
   127  	}
   128  
   129  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(5)
   130  	// map imps by id
   131  	impsByID := make(map[string]openrtb2.Imp)
   132  	for i := 0; i < len(internalRequest.Imp); i++ {
   133  		impsByID[internalRequest.Imp[i].ID] = internalRequest.Imp[i]
   134  	}
   135  
   136  	var errs []error
   137  	for _, sb := range bidResp.SeatBid {
   138  		for i := 0; i < len(sb.Bid); i++ {
   139  			bid := sb.Bid[i]
   140  			imp := impsByID[bid.ImpID]
   141  			bidType := getBidType(imp)
   142  			if &imp != nil && bidType != "" {
   143  				bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   144  					Bid:     &bid,
   145  					BidType: bidType,
   146  				})
   147  			}
   148  		}
   149  	}
   150  	return bidResponse, errs
   151  }
   152  
   153  func getBidType(imp openrtb2.Imp) openrtb_ext.BidType {
   154  	// derive the bidtype purely from the impression itself
   155  	if imp.Banner != nil {
   156  		return openrtb_ext.BidTypeBanner
   157  	} else if imp.Video != nil {
   158  		return openrtb_ext.BidTypeVideo
   159  	} else if imp.Audio != nil {
   160  		return openrtb_ext.BidTypeAudio
   161  	} else if imp.Native != nil {
   162  		return openrtb_ext.BidTypeNative
   163  	}
   164  	return ""
   165  }