github.com/prebid/prebid-server@v0.275.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/v19/openrtb2"
    10  	"github.com/prebid/prebid-server/adapters"
    11  	"github.com/prebid/prebid-server/config"
    12  	"github.com/prebid/prebid-server/errortypes"
    13  	"github.com/prebid/prebid-server/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  		})
    95  	}
    96  
    97  	request.Imp = impressions
    98  
    99  	if len(result) == 0 {
   100  		return nil, errs
   101  	}
   102  	return result, errs
   103  }
   104  
   105  func (a *KidozAdapter) MakeBids(request *openrtb2.BidRequest, _ *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   106  	var errs []error
   107  
   108  	switch responseData.StatusCode {
   109  	case http.StatusNoContent:
   110  		fallthrough
   111  	case http.StatusServiceUnavailable:
   112  		return nil, nil
   113  
   114  	case http.StatusBadRequest:
   115  		fallthrough
   116  	case http.StatusUnauthorized:
   117  		fallthrough
   118  	case http.StatusForbidden:
   119  		return nil, []error{&errortypes.BadInput{
   120  			Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode) + " " + string(responseData.Body),
   121  		}}
   122  
   123  	case http.StatusOK:
   124  		break
   125  
   126  	default:
   127  		return nil, []error{&errortypes.BadServerResponse{
   128  			Message: "unexpected status code: " + strconv.Itoa(responseData.StatusCode) + " " + string(responseData.Body),
   129  		}}
   130  	}
   131  
   132  	var bidResponse openrtb2.BidResponse
   133  	err := json.Unmarshal(responseData.Body, &bidResponse)
   134  	if err != nil {
   135  		return nil, []error{&errortypes.BadServerResponse{
   136  			Message: err.Error(),
   137  		}}
   138  	}
   139  
   140  	response := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp))
   141  
   142  	for _, seatBid := range bidResponse.SeatBid {
   143  		for _, bid := range seatBid.Bid {
   144  			thisBid := bid
   145  			bidType := GetMediaTypeForImp(bid.ImpID, request.Imp)
   146  			if bidType == UndefinedMediaType {
   147  				errs = append(errs, &errortypes.BadServerResponse{
   148  					Message: "ignoring bid id=" + bid.ID + ", request doesn't contain any valid impression with id=" + bid.ImpID,
   149  				})
   150  				continue
   151  			}
   152  			response.Bids = append(response.Bids, &adapters.TypedBid{
   153  				Bid:     &thisBid,
   154  				BidType: bidType,
   155  			})
   156  		}
   157  	}
   158  
   159  	return response, errs
   160  }
   161  
   162  // Builder builds a new instance of the Kidoz adapter for the given bidder with the given config.
   163  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
   164  	bidder := &KidozAdapter{
   165  		endpoint: config.Endpoint,
   166  	}
   167  	return bidder, nil
   168  }
   169  
   170  const UndefinedMediaType = openrtb_ext.BidType("")
   171  
   172  func GetMediaTypeForImp(impID string, imps []openrtb2.Imp) openrtb_ext.BidType {
   173  	var bidType openrtb_ext.BidType = UndefinedMediaType
   174  	for _, impression := range imps {
   175  		if impression.ID != impID {
   176  			continue
   177  		}
   178  		switch {
   179  		case impression.Banner != nil:
   180  			bidType = openrtb_ext.BidTypeBanner
   181  		case impression.Video != nil:
   182  			bidType = openrtb_ext.BidTypeVideo
   183  		case impression.Native != nil:
   184  			bidType = openrtb_ext.BidTypeNative
   185  		case impression.Audio != nil:
   186  			bidType = openrtb_ext.BidTypeAudio
   187  		}
   188  		break
   189  	}
   190  	return bidType
   191  }