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

     1  package pangle
     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  type adapter struct {
    16  	Endpoint string
    17  }
    18  
    19  type NetworkIDs struct {
    20  	AppID       string `json:"appid,omitempty"`
    21  	PlacementID string `json:"placementid,omitempty"`
    22  }
    23  
    24  type wrappedExtImpBidder struct {
    25  	*adapters.ExtImpBidder
    26  	AdType     int         `json:"adtype,omitempty"`
    27  	IsPrebid   bool        `json:"is_prebid,omitempty"`
    28  	NetworkIDs *NetworkIDs `json:"networkids,omitempty"`
    29  }
    30  
    31  type pangleBidExt struct {
    32  	Pangle *bidExt `json:"pangle,omitempty"`
    33  }
    34  
    35  type bidExt struct {
    36  	AdType *int `json:"adtype,omitempty"`
    37  }
    38  
    39  /* Builder */
    40  
    41  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    42  	bidder := &adapter{
    43  		Endpoint: config.Endpoint,
    44  	}
    45  
    46  	return bidder, nil
    47  }
    48  
    49  /* MakeRequests */
    50  
    51  func getAdType(imp openrtb2.Imp, parsedImpExt *wrappedExtImpBidder) int {
    52  	// video
    53  	if imp.Video != nil {
    54  		if parsedImpExt != nil && parsedImpExt.Prebid != nil && parsedImpExt.Prebid.IsRewardedInventory != nil && *parsedImpExt.Prebid.IsRewardedInventory == 1 {
    55  			return 7
    56  		}
    57  		if imp.Instl == 1 {
    58  			return 8
    59  		}
    60  	}
    61  	// banner
    62  	if imp.Banner != nil {
    63  		if imp.Instl == 1 {
    64  			return 2
    65  		} else {
    66  			return 1
    67  		}
    68  	}
    69  	// native
    70  	if imp.Native != nil && len(imp.Native.Request) > 0 {
    71  		return 5
    72  	}
    73  
    74  	return -1
    75  }
    76  
    77  func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    78  	var requests []*adapters.RequestData
    79  	var errs []error
    80  
    81  	requestCopy := *request
    82  	for _, imp := range request.Imp {
    83  		var impExt wrappedExtImpBidder
    84  		if err := json.Unmarshal(imp.Ext, &impExt); err != nil {
    85  			errs = append(errs, fmt.Errorf("failed unmarshalling imp ext (err)%s", err.Error()))
    86  			continue
    87  		}
    88  		// get token & networkIDs
    89  		var bidderImpExt openrtb_ext.ImpExtPangle
    90  		if err := json.Unmarshal(impExt.Bidder, &bidderImpExt); err != nil {
    91  			errs = append(errs, fmt.Errorf("failed unmarshalling bidder imp ext (err)%s", err.Error()))
    92  			continue
    93  		}
    94  		// detect and fill adtype
    95  		adType := getAdType(imp, &impExt)
    96  		if adType == -1 {
    97  			errs = append(errs, &errortypes.BadInput{Message: "not a supported adtype"})
    98  			continue
    99  		}
   100  		// remarshal imp.ext
   101  		impExt.AdType = adType
   102  		impExt.IsPrebid = true
   103  		if len(bidderImpExt.AppID) > 0 && len(bidderImpExt.PlacementID) > 0 {
   104  			impExt.NetworkIDs = &NetworkIDs{
   105  				AppID:       bidderImpExt.AppID,
   106  				PlacementID: bidderImpExt.PlacementID,
   107  			}
   108  		} else if len(bidderImpExt.AppID) > 0 || len(bidderImpExt.PlacementID) > 0 {
   109  			errs = append(errs, &errortypes.BadInput{Message: "only one of appid or placementid is provided"})
   110  			continue
   111  		}
   112  		if newImpExt, err := json.Marshal(impExt); err == nil {
   113  			imp.Ext = newImpExt
   114  		} else {
   115  			errs = append(errs, fmt.Errorf("failed re-marshalling imp ext"))
   116  			continue
   117  		}
   118  
   119  		requestCopy.Imp = []openrtb2.Imp{imp}
   120  		requestJSON, err := json.Marshal(requestCopy)
   121  		if err != nil {
   122  			errs = append(errs, err)
   123  			continue
   124  		}
   125  
   126  		requestData := &adapters.RequestData{
   127  			Method: "POST",
   128  			Uri:    a.Endpoint,
   129  			Body:   requestJSON,
   130  			Headers: http.Header{
   131  				"TOKEN":        []string{bidderImpExt.Token},
   132  				"Content-Type": []string{"application/json"},
   133  			},
   134  			ImpIDs: openrtb_ext.GetImpIDs(requestCopy.Imp),
   135  		}
   136  		requests = append(requests, requestData)
   137  	}
   138  
   139  	return requests, errs
   140  }
   141  
   142  /* MakeBids */
   143  
   144  func getMediaTypeForBid(bid *openrtb2.Bid) (openrtb_ext.BidType, error) {
   145  	if bid == nil {
   146  		return "", fmt.Errorf("the bid request object is nil")
   147  	}
   148  
   149  	var bidExt pangleBidExt
   150  	if err := json.Unmarshal(bid.Ext, &bidExt); err != nil {
   151  		return "", fmt.Errorf("invalid bid ext")
   152  	} else if bidExt.Pangle == nil || bidExt.Pangle.AdType == nil {
   153  		return "", fmt.Errorf("missing pangleExt/adtype in bid ext")
   154  	}
   155  
   156  	switch *bidExt.Pangle.AdType {
   157  	case 1:
   158  		return openrtb_ext.BidTypeBanner, nil
   159  	case 2:
   160  		return openrtb_ext.BidTypeBanner, nil
   161  	case 5:
   162  		return openrtb_ext.BidTypeNative, nil
   163  	case 7:
   164  		return openrtb_ext.BidTypeVideo, nil
   165  	case 8:
   166  		return openrtb_ext.BidTypeVideo, nil
   167  	}
   168  
   169  	return "", fmt.Errorf("unrecognized adtype in response")
   170  }
   171  
   172  func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   173  	if responseData.StatusCode == http.StatusNoContent {
   174  		return nil, nil
   175  	}
   176  
   177  	if responseData.StatusCode == http.StatusBadRequest {
   178  		err := &errortypes.BadInput{
   179  			Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
   180  		}
   181  		return nil, []error{err}
   182  	}
   183  
   184  	if responseData.StatusCode != http.StatusOK {
   185  		err := &errortypes.BadServerResponse{
   186  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode),
   187  		}
   188  		return nil, []error{err}
   189  	}
   190  
   191  	var response openrtb2.BidResponse
   192  	if err := json.Unmarshal(responseData.Body, &response); err != nil {
   193  		return nil, []error{err}
   194  	}
   195  
   196  	var errs []error
   197  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
   198  	bidResponse.Currency = response.Cur
   199  	for _, seatBid := range response.SeatBid {
   200  		for _, temp := range seatBid.Bid {
   201  			bid := temp // avoid taking address of a for loop variable
   202  			mediaType, err := getMediaTypeForBid(&bid)
   203  			if err != nil {
   204  				errs = append(errs, err)
   205  				continue
   206  			}
   207  			b := &adapters.TypedBid{
   208  				Bid:     &bid,
   209  				BidType: mediaType,
   210  			}
   211  			bidResponse.Bids = append(bidResponse.Bids, b)
   212  		}
   213  	}
   214  
   215  	return bidResponse, errs
   216  }