github.com/prebid/prebid-server@v0.275.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/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  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  		}
   135  		requests = append(requests, requestData)
   136  	}
   137  
   138  	return requests, errs
   139  }
   140  
   141  /* MakeBids */
   142  
   143  func getMediaTypeForBid(bid *openrtb2.Bid) (openrtb_ext.BidType, error) {
   144  	if bid == nil {
   145  		return "", fmt.Errorf("the bid request object is nil")
   146  	}
   147  
   148  	var bidExt pangleBidExt
   149  	if err := json.Unmarshal(bid.Ext, &bidExt); err != nil {
   150  		return "", fmt.Errorf("invalid bid ext")
   151  	} else if bidExt.Pangle == nil || bidExt.Pangle.AdType == nil {
   152  		return "", fmt.Errorf("missing pangleExt/adtype in bid ext")
   153  	}
   154  
   155  	switch *bidExt.Pangle.AdType {
   156  	case 1:
   157  		return openrtb_ext.BidTypeBanner, nil
   158  	case 2:
   159  		return openrtb_ext.BidTypeBanner, nil
   160  	case 5:
   161  		return openrtb_ext.BidTypeNative, nil
   162  	case 7:
   163  		return openrtb_ext.BidTypeVideo, nil
   164  	case 8:
   165  		return openrtb_ext.BidTypeVideo, nil
   166  	}
   167  
   168  	return "", fmt.Errorf("unrecognized adtype in response")
   169  }
   170  
   171  func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   172  	if responseData.StatusCode == http.StatusNoContent {
   173  		return nil, nil
   174  	}
   175  
   176  	if responseData.StatusCode == http.StatusBadRequest {
   177  		err := &errortypes.BadInput{
   178  			Message: "Unexpected status code: 400. Bad request from publisher. Run with request.debug = 1 for more info.",
   179  		}
   180  		return nil, []error{err}
   181  	}
   182  
   183  	if responseData.StatusCode != http.StatusOK {
   184  		err := &errortypes.BadServerResponse{
   185  			Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info.", responseData.StatusCode),
   186  		}
   187  		return nil, []error{err}
   188  	}
   189  
   190  	var response openrtb2.BidResponse
   191  	if err := json.Unmarshal(responseData.Body, &response); err != nil {
   192  		return nil, []error{err}
   193  	}
   194  
   195  	var errs []error
   196  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
   197  	bidResponse.Currency = response.Cur
   198  	for _, seatBid := range response.SeatBid {
   199  		for _, temp := range seatBid.Bid {
   200  			bid := temp // avoid taking address of a for loop variable
   201  			mediaType, err := getMediaTypeForBid(&bid)
   202  			if err != nil {
   203  				errs = append(errs, err)
   204  				continue
   205  			}
   206  			b := &adapters.TypedBid{
   207  				Bid:     &bid,
   208  				BidType: mediaType,
   209  			}
   210  			bidResponse.Bids = append(bidResponse.Bids, b)
   211  		}
   212  	}
   213  
   214  	return bidResponse, errs
   215  }