github.com/prebid/prebid-server@v0.275.0/adapters/outbrain/outbrain.go (about)

     1  package outbrain
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"github.com/prebid/openrtb/v19/native1"
     9  	nativeResponse "github.com/prebid/openrtb/v19/native1/response"
    10  	"github.com/prebid/openrtb/v19/openrtb2"
    11  	"github.com/prebid/prebid-server/adapters"
    12  	"github.com/prebid/prebid-server/config"
    13  	"github.com/prebid/prebid-server/errortypes"
    14  	"github.com/prebid/prebid-server/openrtb_ext"
    15  )
    16  
    17  type adapter struct {
    18  	endpoint string
    19  }
    20  
    21  // Builder builds a new instance of the Outbrain adapter for the given bidder with the given config.
    22  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    23  	bidder := &adapter{
    24  		endpoint: config.Endpoint,
    25  	}
    26  	return bidder, nil
    27  }
    28  
    29  func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    30  	reqCopy := *request
    31  
    32  	var errs []error
    33  	var outbrainExt openrtb_ext.ExtImpOutbrain
    34  	for i := 0; i < len(reqCopy.Imp); i++ {
    35  		imp := reqCopy.Imp[i]
    36  
    37  		var bidderExt adapters.ExtImpBidder
    38  		if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
    39  			errs = append(errs, err)
    40  			continue
    41  		}
    42  		if err := json.Unmarshal(bidderExt.Bidder, &outbrainExt); err != nil {
    43  			errs = append(errs, err)
    44  			continue
    45  		}
    46  		if outbrainExt.TagId != "" {
    47  			imp.TagID = outbrainExt.TagId
    48  			reqCopy.Imp[i] = imp
    49  		}
    50  	}
    51  
    52  	publisher := &openrtb2.Publisher{
    53  		ID:     outbrainExt.Publisher.Id,
    54  		Name:   outbrainExt.Publisher.Name,
    55  		Domain: outbrainExt.Publisher.Domain,
    56  	}
    57  	if reqCopy.Site != nil {
    58  		siteCopy := *reqCopy.Site
    59  		siteCopy.Publisher = publisher
    60  		reqCopy.Site = &siteCopy
    61  	} else if reqCopy.App != nil {
    62  		appCopy := *reqCopy.App
    63  		appCopy.Publisher = publisher
    64  		reqCopy.App = &appCopy
    65  	}
    66  
    67  	if outbrainExt.BCat != nil {
    68  		reqCopy.BCat = outbrainExt.BCat
    69  	}
    70  	if outbrainExt.BAdv != nil {
    71  		reqCopy.BAdv = outbrainExt.BAdv
    72  	}
    73  
    74  	requestJSON, err := json.Marshal(reqCopy)
    75  	if err != nil {
    76  		errs = append(errs, err)
    77  		return nil, errs
    78  	}
    79  
    80  	requestData := &adapters.RequestData{
    81  		Method: "POST",
    82  		Uri:    a.endpoint,
    83  		Body:   requestJSON,
    84  	}
    85  
    86  	return []*adapters.RequestData{requestData}, nil
    87  }
    88  
    89  func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
    90  	if responseData.StatusCode == http.StatusNoContent {
    91  		return nil, nil
    92  	}
    93  
    94  	if responseData.StatusCode == http.StatusBadRequest {
    95  		err := &errortypes.BadInput{
    96  			Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
    97  		}
    98  		return nil, []error{err}
    99  	}
   100  
   101  	if responseData.StatusCode != http.StatusOK {
   102  		err := &errortypes.BadServerResponse{
   103  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode),
   104  		}
   105  		return nil, []error{err}
   106  	}
   107  
   108  	var response openrtb2.BidResponse
   109  	if err := json.Unmarshal(responseData.Body, &response); err != nil {
   110  		return nil, []error{err}
   111  	}
   112  
   113  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
   114  	bidResponse.Currency = response.Cur
   115  
   116  	var errs []error
   117  	for _, seatBid := range response.SeatBid {
   118  		for i := range seatBid.Bid {
   119  			bid := seatBid.Bid[i]
   120  			bidType, err := getMediaTypeForImp(bid.ImpID, request.Imp)
   121  			if err != nil {
   122  				errs = append(errs, err)
   123  				continue
   124  			}
   125  			if bidType == openrtb_ext.BidTypeNative {
   126  				var nativePayload nativeResponse.Response
   127  				if err := json.Unmarshal(json.RawMessage(bid.AdM), &nativePayload); err != nil {
   128  					errs = append(errs, err)
   129  					continue
   130  				}
   131  				transformEventTrackers(&nativePayload)
   132  				nativePayloadJson, err := json.Marshal(nativePayload)
   133  				if err != nil {
   134  					errs = append(errs, err)
   135  					continue
   136  				}
   137  				bid.AdM = string(nativePayloadJson)
   138  			}
   139  
   140  			b := &adapters.TypedBid{
   141  				Bid:     &bid,
   142  				BidType: bidType,
   143  			}
   144  			bidResponse.Bids = append(bidResponse.Bids, b)
   145  		}
   146  	}
   147  
   148  	return bidResponse, errs
   149  }
   150  
   151  func getMediaTypeForImp(impID string, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
   152  	for _, imp := range imps {
   153  		if imp.ID == impID {
   154  			if imp.Native != nil {
   155  				return openrtb_ext.BidTypeNative, nil
   156  			} else if imp.Banner != nil {
   157  				return openrtb_ext.BidTypeBanner, nil
   158  			} else if imp.Video != nil {
   159  				return openrtb_ext.BidTypeVideo, nil
   160  			}
   161  		}
   162  	}
   163  
   164  	return "", &errortypes.BadInput{
   165  		Message: fmt.Sprintf("Failed to find native/banner/video impression \"%s\" ", impID),
   166  	}
   167  }
   168  
   169  func transformEventTrackers(nativePayload *nativeResponse.Response) {
   170  	// the native-trk.js library used to trigger the trackers currently doesn't support the native 1.2 eventtrackers,
   171  	// so transform them to the deprecated imptrackers and jstracker
   172  	for _, eventTracker := range nativePayload.EventTrackers {
   173  		if eventTracker.Event != native1.EventTypeImpression {
   174  			continue
   175  		}
   176  		switch eventTracker.Method {
   177  		case native1.EventTrackingMethodImage:
   178  			nativePayload.ImpTrackers = append(nativePayload.ImpTrackers, eventTracker.URL)
   179  		case native1.EventTrackingMethodJS:
   180  			nativePayload.JSTracker = fmt.Sprintf("<script src=\"%s\"></script>", eventTracker.URL)
   181  		}
   182  	}
   183  	nativePayload.EventTrackers = nil
   184  }