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

     1  package kidoz
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"net/http"
     7  	"strconv"
     8  
     9  	"github.com/prebid/openrtb/v20/openrtb2"
    10  	"github.com/prebid/prebid-server/v2/adapters"
    11  	"github.com/prebid/prebid-server/v2/config"
    12  	"github.com/prebid/prebid-server/v2/errortypes"
    13  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    14  )
    15  
    16  type KidozAdapter struct {
    17  	endpoint string
    18  }
    19  
    20  func (a *KidozAdapter) MakeRequests(request *openrtb2.BidRequest, _ *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    21  	headers := http.Header{}
    22  	headers.Add("Content-Type", "application/json;charset=utf-8")
    23  	headers.Add("Accept", "application/json")
    24  	headers.Add("x-openrtb-version", "2.5")
    25  
    26  	impressions := request.Imp
    27  	result := make([]*adapters.RequestData, 0, len(impressions))
    28  	errs := make([]error, 0, len(impressions))
    29  
    30  	for i, impression := range impressions {
    31  		if impression.Banner == nil && impression.Video == nil {
    32  			errs = append(errs, &errortypes.BadInput{
    33  				Message: "Kidoz only supports banner or video ads",
    34  			})
    35  			continue
    36  		}
    37  
    38  		if impression.Banner != nil {
    39  			banner := impression.Banner
    40  			if banner.Format == nil {
    41  				errs = append(errs, &errortypes.BadInput{
    42  					Message: "banner format required",
    43  				})
    44  				continue
    45  			}
    46  			if len(banner.Format) == 0 {
    47  				errs = append(errs, &errortypes.BadInput{
    48  					Message: "banner format array is empty",
    49  				})
    50  				continue
    51  			}
    52  		}
    53  
    54  		if len(impression.Ext) == 0 {
    55  			errs = append(errs, errors.New("impression extensions required"))
    56  			continue
    57  		}
    58  		var bidderExt adapters.ExtImpBidder
    59  		err := json.Unmarshal(impression.Ext, &bidderExt)
    60  		if err != nil {
    61  			errs = append(errs, err)
    62  			continue
    63  		}
    64  		if len(bidderExt.Bidder) == 0 {
    65  			errs = append(errs, errors.New("bidder required"))
    66  			continue
    67  		}
    68  		var impressionExt openrtb_ext.ExtImpKidoz
    69  		err = json.Unmarshal(bidderExt.Bidder, &impressionExt)
    70  		if err != nil {
    71  			errs = append(errs, err)
    72  			continue
    73  		}
    74  		if impressionExt.AccessToken == "" {
    75  			errs = append(errs, errors.New("Kidoz access_token required"))
    76  			continue
    77  		}
    78  		if impressionExt.PublisherID == "" {
    79  			errs = append(errs, errors.New("Kidoz publisher_id required"))
    80  			continue
    81  		}
    82  
    83  		request.Imp = impressions[i : i+1]
    84  		body, err := json.Marshal(request)
    85  		if err != nil {
    86  			errs = append(errs, err)
    87  			continue
    88  		}
    89  		result = append(result, &adapters.RequestData{
    90  			Method:  "POST",
    91  			Uri:     a.endpoint,
    92  			Body:    body,
    93  			Headers: headers,
    94  			ImpIDs:  openrtb_ext.GetImpIDs(request.Imp),
    95  		})
    96  	}
    97  
    98  	request.Imp = impressions
    99  
   100  	if len(result) == 0 {
   101  		return nil, errs
   102  	}
   103  	return result, errs
   104  }
   105  
   106  func (a *KidozAdapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   107  	var errs []error
   108  
   109  	switch responseData.StatusCode {
   110  	case http.StatusNoContent:
   111  		fallthrough
   112  	case http.StatusServiceUnavailable:
   113  		return nil, nil
   114  
   115  	case http.StatusBadRequest:
   116  		fallthrough
   117  	case http.StatusUnauthorized:
   118  		fallthrough
   119  	case http.StatusForbidden:
   120  		return nil, []error{&errortypes.BadInput{
   121  			Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode) + " " + string(responseData.Body),
   122  		}}
   123  
   124  	case http.StatusOK:
   125  		break
   126  
   127  	default:
   128  		return nil, []error{&errortypes.BadServerResponse{
   129  			Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode) + " " + string(responseData.Body),
   130  		}}
   131  	}
   132  
   133  	var bidResponse openrtb2.BidResponse
   134  	err := json.Unmarshal(responseData.Body, &bidResponse)
   135  	if err != nil {
   136  		return nil, []error{&errortypes.BadServerResponse{
   137  			Message: err.Error(),
   138  		}}
   139  	}
   140  
   141  	response := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
   142  
   143  	for _, seatBid := range bidResponse.SeatBid {
   144  		for _, bid := range seatBid.Bid {
   145  			thisBid := bid
   146  			bidType := GetMediaTypeForImp(bid.ImpID, request.Imp)
   147  			if bidType == UndefinedMediaType {
   148  				errs = append(errs, &errortypes.BadServerResponse{
   149  					Message: "ignoring bid id=" + bid.ID + ", request doesn't contain any valid impression with id=" + bid.ImpID,
   150  				})
   151  				continue
   152  			}
   153  			response.Bids = append(response.Bids, &adapters.TypedBid{
   154  				Bid:     &thisBid,
   155  				BidType: bidType,
   156  			})
   157  		}
   158  	}
   159  
   160  	return response, errs
   161  }
   162  
   163  // Builder builds a new instance of the Kidoz adapter for the given bidder with the given config.
   164  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
   165  	bidder := &KidozAdapter{
   166  		endpoint: config.Endpoint,
   167  	}
   168  	return bidder, nil
   169  }
   170  
   171  const UndefinedMediaType = openrtb_ext.BidType("")
   172  
   173  func GetMediaTypeForImp(impID string, imps []openrtb2.Imp) openrtb_ext.BidType {
   174  	var bidType openrtb_ext.BidType = UndefinedMediaType
   175  	for _, impression := range imps {
   176  		if impression.ID != impID {
   177  			continue
   178  		}
   179  		switch {
   180  		case impression.Banner != nil:
   181  			bidType = openrtb_ext.BidTypeBanner
   182  		case impression.Video != nil:
   183  			bidType = openrtb_ext.BidTypeVideo
   184  		case impression.Native != nil:
   185  			bidType = openrtb_ext.BidTypeNative
   186  		case impression.Audio != nil:
   187  			bidType = openrtb_ext.BidTypeAudio
   188  		}
   189  		break
   190  	}
   191  	return bidType
   192  }