github.com/prebid/prebid-server@v0.275.0/adapters/flipp/flipp.go (about) 1 package flipp 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 "strings" 9 10 "github.com/buger/jsonparser" 11 "github.com/gofrs/uuid" 12 "github.com/prebid/openrtb/v19/openrtb2" 13 "github.com/prebid/prebid-server/adapters" 14 "github.com/prebid/prebid-server/config" 15 "github.com/prebid/prebid-server/openrtb_ext" 16 ) 17 18 const ( 19 bannerType = "banner" 20 inlineDivName = "inline" 21 flippBidder = "flipp" 22 defaultCurrency = "USD" 23 ) 24 25 var ( 26 count int64 = 1 27 adTypes = []int64{4309, 641} 28 dtxTypes = []int64{5061} 29 ) 30 31 type adapter struct { 32 endpoint string 33 } 34 35 // Builder builds a new instance of the Flipp adapter for the given bidder with the given config. 36 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 37 bidder := &adapter{ 38 endpoint: config.Endpoint, 39 } 40 return bidder, nil 41 } 42 43 func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 44 adapterRequests := make([]*adapters.RequestData, 0, len(request.Imp)) 45 var errors []error 46 47 for _, imp := range request.Imp { 48 adapterReq, err := a.processImp(request, imp) 49 if err != nil { 50 errors = append(errors, err) 51 continue 52 } 53 adapterRequests = append(adapterRequests, adapterReq) 54 } 55 if len(adapterRequests) == 0 { 56 return nil, append(errors, fmt.Errorf("adapterRequest is empty")) 57 } 58 return adapterRequests, errors 59 } 60 61 func (a *adapter) makeRequest(request *openrtb2.BidRequest, campaignRequestBody CampaignRequestBody) (*adapters.RequestData, error) { 62 campaignRequestBodyJSON, err := json.Marshal(campaignRequestBody) 63 if err != nil { 64 return nil, err 65 } 66 67 headers := http.Header{} 68 headers.Add("Content-Type", "application/json") 69 if request.Device != nil && request.Device.UA != "" { 70 headers.Add("User-Agent", request.Device.UA) 71 } 72 return &adapters.RequestData{ 73 Method: http.MethodPost, 74 Uri: a.endpoint, 75 Body: campaignRequestBodyJSON, 76 Headers: headers, 77 }, err 78 } 79 80 func (a *adapter) processImp(request *openrtb2.BidRequest, imp openrtb2.Imp) (*adapters.RequestData, error) { 81 var flippExtParams openrtb_ext.ImpExtFlipp 82 params, _, _, err := jsonparser.Get(imp.Ext, "bidder") 83 if err != nil { 84 return nil, fmt.Errorf("flipp params not found. %v", err) 85 } 86 err = json.Unmarshal(params, &flippExtParams) 87 if err != nil { 88 return nil, fmt.Errorf("unable to extract flipp params. %v", err) 89 } 90 91 publisherUrl, err := url.Parse(request.Site.Page) 92 if err != nil { 93 return nil, fmt.Errorf("unable to parse site url. %v", err) 94 } 95 96 var contentCode string 97 if flippExtParams.Options.ContentCode != "" { 98 contentCode = flippExtParams.Options.ContentCode 99 } else if publisherUrl != nil { 100 contentCode = publisherUrl.Query().Get("flipp-content-code") 101 } 102 103 placement := Placement{ 104 DivName: inlineDivName, 105 SiteID: &flippExtParams.SiteID, 106 AdTypes: getAdTypes(flippExtParams.CreativeType), 107 ZoneIds: flippExtParams.ZoneIds, 108 Count: &count, 109 Prebid: buildPrebidRequest(flippExtParams, request, imp), 110 Properties: &Properties{ 111 ContentCode: &contentCode, 112 }, 113 Options: flippExtParams.Options, 114 } 115 116 var userIP string 117 if request.Device != nil && request.Device.IP != "" { 118 userIP = request.Device.IP 119 } else { 120 return nil, fmt.Errorf("no IP set in flipp bidder params or request device") 121 } 122 123 var userKey string 124 if request.User != nil && request.User.ID != "" { 125 userKey = request.User.ID 126 } else if flippExtParams.UserKey != "" { 127 userKey = flippExtParams.UserKey 128 } else { 129 uid, err := uuid.NewV4() 130 if err != nil { 131 return nil, fmt.Errorf("unable to generate user uuid. %v", err) 132 } 133 userKey = uid.String() 134 } 135 136 keywordsArray := strings.Split(request.Site.Keywords, ",") 137 138 campaignRequestBody := CampaignRequestBody{ 139 Placements: []*Placement{&placement}, 140 URL: request.Site.Page, 141 Keywords: keywordsArray, 142 IP: userIP, 143 User: &CampaignRequestBodyUser{ 144 Key: &userKey, 145 }, 146 } 147 148 adapterReq, err := a.makeRequest(request, campaignRequestBody) 149 if err != nil { 150 return nil, fmt.Errorf("make request failed with err %v", err) 151 } 152 153 return adapterReq, nil 154 } 155 156 func buildPrebidRequest(flippExtParams openrtb_ext.ImpExtFlipp, request *openrtb2.BidRequest, imp openrtb2.Imp) *PrebidRequest { 157 var height int64 158 var width int64 159 if imp.Banner != nil && len(imp.Banner.Format) > 0 { 160 height = imp.Banner.Format[0].H 161 width = imp.Banner.Format[0].W 162 } 163 prebidRequest := PrebidRequest{ 164 CreativeType: &flippExtParams.CreativeType, 165 PublisherNameIdentifier: &flippExtParams.PublisherNameIdentifier, 166 RequestID: &imp.ID, 167 Height: &height, 168 Width: &width, 169 } 170 return &prebidRequest 171 } 172 173 func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { 174 if adapters.IsResponseStatusCodeNoContent(responseData) { 175 return nil, nil 176 } 177 178 if err := adapters.CheckResponseStatusCodeForErrors(responseData); err != nil { 179 return nil, []error{err} 180 } 181 182 var campaignResponseBody CampaignResponseBody 183 if err := json.Unmarshal(responseData.Body, &campaignResponseBody); err != nil { 184 return nil, []error{err} 185 } 186 187 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) 188 bidResponse.Currency = defaultCurrency 189 for _, imp := range request.Imp { 190 for _, decision := range campaignResponseBody.Decisions.Inline { 191 if *decision.Prebid.RequestID == imp.ID { 192 b := &adapters.TypedBid{ 193 Bid: buildBid(decision, imp.ID), 194 BidType: openrtb_ext.BidType(bannerType), 195 } 196 bidResponse.Bids = append(bidResponse.Bids, b) 197 } 198 } 199 } 200 return bidResponse, nil 201 } 202 203 func getAdTypes(creativeType string) []int64 { 204 if creativeType == "DTX" { 205 return dtxTypes 206 } 207 return adTypes 208 } 209 210 func buildBid(decision *InlineModel, impId string) *openrtb2.Bid { 211 bid := &openrtb2.Bid{ 212 CrID: fmt.Sprint(decision.CreativeID), 213 Price: *decision.Prebid.Cpm, 214 AdM: *decision.Prebid.Creative, 215 ID: fmt.Sprint(decision.AdID), 216 ImpID: impId, 217 } 218 if len(decision.Contents) > 0 || decision.Contents[0] != nil || decision.Contents[0].Data != nil { 219 if decision.Contents[0].Data.Width != 0 { 220 bid.W = decision.Contents[0].Data.Width 221 } 222 bid.H = 0 223 } 224 return bid 225 }