github.com/prebid/prebid-server@v0.275.0/adapters/conversant/conversant.go (about) 1 package conversant 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 8 "github.com/prebid/openrtb/v19/adcom1" 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 ConversantAdapter struct { 17 URI string 18 } 19 20 func (c *ConversantAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 21 for i := 0; i < len(request.Imp); i++ { 22 var bidderExt adapters.ExtImpBidder 23 if err := json.Unmarshal(request.Imp[i].Ext, &bidderExt); err != nil { 24 return nil, []error{&errortypes.BadInput{ 25 Message: fmt.Sprintf("Impression[%d] missing ext object", i), 26 }} 27 } 28 29 var cnvrExt openrtb_ext.ExtImpConversant 30 if err := json.Unmarshal(bidderExt.Bidder, &cnvrExt); err != nil { 31 return nil, []error{&errortypes.BadInput{ 32 Message: fmt.Sprintf("Impression[%d] missing ext.bidder object", i), 33 }} 34 } 35 36 if cnvrExt.SiteID == "" { 37 return nil, []error{&errortypes.BadInput{ 38 Message: fmt.Sprintf("Impression[%d] requires ext.bidder.site_id", i), 39 }} 40 } 41 42 if i == 0 { 43 if request.Site != nil { 44 tmpSite := *request.Site 45 request.Site = &tmpSite 46 request.Site.ID = cnvrExt.SiteID 47 } else if request.App != nil { 48 tmpApp := *request.App 49 request.App = &tmpApp 50 request.App.ID = cnvrExt.SiteID 51 } 52 } 53 parseCnvrParams(&request.Imp[i], cnvrExt) 54 } 55 56 //create the request body 57 data, err := json.Marshal(request) 58 if err != nil { 59 return nil, []error{&errortypes.BadInput{ 60 Message: "Error in packaging request to JSON", 61 }} 62 } 63 headers := http.Header{} 64 headers.Add("Content-Type", "application/json;charset=utf-8") 65 headers.Add("Accept", "application/json") 66 67 return []*adapters.RequestData{{ 68 Method: "POST", 69 Uri: c.URI, 70 Body: data, 71 Headers: headers, 72 }}, nil 73 } 74 75 func parseCnvrParams(imp *openrtb2.Imp, cnvrExt openrtb_ext.ExtImpConversant) { 76 imp.DisplayManager = "prebid-s2s" 77 imp.DisplayManagerVer = "2.0.0" 78 79 if imp.BidFloor <= 0 && cnvrExt.BidFloor > 0 { 80 imp.BidFloor = cnvrExt.BidFloor 81 } 82 83 if len(cnvrExt.TagID) > 0 { 84 imp.TagID = cnvrExt.TagID 85 } 86 87 // Take care not to override the global secure flag 88 if (imp.Secure == nil || *imp.Secure == 0) && cnvrExt.Secure != nil { 89 imp.Secure = cnvrExt.Secure 90 } 91 92 var position *adcom1.PlacementPosition 93 if cnvrExt.Position != nil { 94 position = adcom1.PlacementPosition(*cnvrExt.Position).Ptr() 95 } 96 if imp.Banner != nil { 97 tmpBanner := *imp.Banner 98 imp.Banner = &tmpBanner 99 imp.Banner.Pos = position 100 101 } else if imp.Video != nil { 102 tmpVideo := *imp.Video 103 imp.Video = &tmpVideo 104 imp.Video.Pos = position 105 106 if len(cnvrExt.API) > 0 { 107 imp.Video.API = make([]adcom1.APIFramework, 0, len(cnvrExt.API)) 108 for _, api := range cnvrExt.API { 109 imp.Video.API = append(imp.Video.API, adcom1.APIFramework(api)) 110 } 111 } 112 113 // Include protocols, mimes, and max duration if specified 114 // These properties can also be specified in ad unit's video object, 115 // but are overridden if the custom params object also contains them. 116 117 if len(cnvrExt.Protocols) > 0 { 118 imp.Video.Protocols = make([]adcom1.MediaCreativeSubtype, 0, len(cnvrExt.Protocols)) 119 for _, protocol := range cnvrExt.Protocols { 120 imp.Video.Protocols = append(imp.Video.Protocols, adcom1.MediaCreativeSubtype(protocol)) 121 } 122 } 123 124 if len(cnvrExt.MIMEs) > 0 { 125 imp.Video.MIMEs = make([]string, len(cnvrExt.MIMEs)) 126 copy(imp.Video.MIMEs, cnvrExt.MIMEs) 127 } 128 129 if cnvrExt.MaxDuration != nil { 130 imp.Video.MaxDuration = *cnvrExt.MaxDuration 131 } 132 } 133 } 134 135 func (c *ConversantAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 136 if response.StatusCode == http.StatusNoContent { 137 return nil, nil // no bid response 138 } 139 140 if response.StatusCode != http.StatusOK { 141 return nil, []error{&errortypes.BadServerResponse{ 142 Message: fmt.Sprintf("Unexpected status code: %d", response.StatusCode), 143 }} 144 } 145 146 var resp openrtb2.BidResponse 147 if err := json.Unmarshal(response.Body, &resp); err != nil { 148 return nil, []error{&errortypes.BadServerResponse{ 149 Message: fmt.Sprintf("bad server response: %d. ", err), 150 }} 151 } 152 153 if len(resp.SeatBid) == 0 { 154 return nil, []error{&errortypes.BadServerResponse{ 155 Message: "Empty bid request", 156 }} 157 } 158 159 bids := resp.SeatBid[0].Bid 160 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bids)) 161 for i := 0; i < len(bids); i++ { 162 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 163 Bid: &bids[i], 164 BidType: getBidType(bids[i].ImpID, internalRequest.Imp), 165 }) 166 } 167 return bidResponse, nil 168 } 169 170 func getBidType(impId string, imps []openrtb2.Imp) openrtb_ext.BidType { 171 bidType := openrtb_ext.BidTypeBanner 172 for _, imp := range imps { 173 if imp.ID == impId { 174 if imp.Video != nil { 175 bidType = openrtb_ext.BidTypeVideo 176 } 177 break 178 } 179 } 180 return bidType 181 } 182 183 // Builder builds a new instance of the Conversant adapter for the given bidder with the given config. 184 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 185 bidder := &ConversantAdapter{ 186 URI: config.Endpoint, 187 } 188 return bidder, nil 189 }