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 }