github.com/prebid/prebid-server/v2@v2.18.0/adapters/mobilefuse/mobilefuse.go (about) 1 package mobilefuse 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strconv" 8 "text/template" 9 10 "github.com/prebid/openrtb/v20/openrtb2" 11 "github.com/prebid/prebid-server/v2/adapters" 12 "github.com/prebid/prebid-server/v2/config" 13 "github.com/prebid/prebid-server/v2/errortypes" 14 "github.com/prebid/prebid-server/v2/macros" 15 "github.com/prebid/prebid-server/v2/openrtb_ext" 16 ) 17 18 type MobileFuseAdapter struct { 19 EndpointTemplate *template.Template 20 } 21 22 type ExtMf struct { 23 MediaType string `json:"media_type"` 24 } 25 26 type BidExt struct { 27 Mf ExtMf `json:"mf"` 28 } 29 30 type ExtSkadn struct { 31 Skadn json.RawMessage `json:"skadn"` 32 } 33 34 // Builder builds a new instance of the MobileFuse adapter for the given bidder with the given config. 35 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 36 template, err := template.New("endpointTemplate").Parse(config.Endpoint) 37 if err != nil { 38 return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) 39 } 40 41 bidder := &MobileFuseAdapter{ 42 EndpointTemplate: template, 43 } 44 return bidder, nil 45 } 46 47 func (adapter *MobileFuseAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 48 var adapterRequests []*adapters.RequestData 49 50 adapterRequest, errs := adapter.makeRequest(request) 51 52 if errs == nil { 53 adapterRequests = append(adapterRequests, adapterRequest) 54 } 55 56 return adapterRequests, errs 57 } 58 59 func (adapter *MobileFuseAdapter) MakeBids(incomingRequest *openrtb2.BidRequest, outgoingRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 60 if response.StatusCode == http.StatusNoContent { 61 return nil, nil 62 } 63 64 if response.StatusCode == http.StatusBadRequest { 65 return nil, []error{&errortypes.BadInput{ 66 Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode), 67 }} 68 } 69 70 if response.StatusCode != http.StatusOK { 71 return nil, []error{&errortypes.BadServerResponse{ 72 Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode), 73 }} 74 } 75 76 var incomingBidResponse openrtb2.BidResponse 77 78 if err := json.Unmarshal(response.Body, &incomingBidResponse); err != nil { 79 return nil, []error{err} 80 } 81 82 outgoingBidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 83 84 for _, seatbid := range incomingBidResponse.SeatBid { 85 for i := range seatbid.Bid { 86 bidType := getBidType(seatbid.Bid[i]) 87 seatbid.Bid[i].Ext = nil 88 89 outgoingBidResponse.Bids = append(outgoingBidResponse.Bids, &adapters.TypedBid{ 90 Bid: &seatbid.Bid[i], 91 BidType: bidType, 92 }) 93 } 94 } 95 96 return outgoingBidResponse, nil 97 } 98 99 func (adapter *MobileFuseAdapter) makeRequest(bidRequest *openrtb2.BidRequest) (*adapters.RequestData, []error) { 100 var errs []error 101 102 mobileFuseExtension, errs := getFirstMobileFuseExtension(bidRequest) 103 if errs != nil { 104 return nil, errs 105 } 106 107 endpoint, err := adapter.getEndpoint(mobileFuseExtension) 108 if err != nil { 109 return nil, append(errs, err) 110 } 111 112 validImps, err := getValidImps(bidRequest, mobileFuseExtension) 113 if err != nil { 114 errs = append(errs, err) 115 return nil, errs 116 } 117 118 mobileFuseBidRequest := *bidRequest 119 mobileFuseBidRequest.Imp = validImps 120 body, err := json.Marshal(mobileFuseBidRequest) 121 if err != nil { 122 return nil, append(errs, err) 123 } 124 125 headers := http.Header{} 126 headers.Add("Content-Type", "application/json;charset=utf-8") 127 headers.Add("Accept", "application/json") 128 129 return &adapters.RequestData{ 130 Method: "POST", 131 Uri: endpoint, 132 Body: body, 133 Headers: headers, 134 ImpIDs: openrtb_ext.GetImpIDs(mobileFuseBidRequest.Imp), 135 }, errs 136 } 137 138 func getFirstMobileFuseExtension(request *openrtb2.BidRequest) (*openrtb_ext.ExtImpMobileFuse, []error) { 139 var mobileFuseImpExtension openrtb_ext.ExtImpMobileFuse 140 var errs []error 141 142 for _, imp := range request.Imp { 143 var bidder_imp_extension adapters.ExtImpBidder 144 145 err := json.Unmarshal(imp.Ext, &bidder_imp_extension) 146 147 if err != nil { 148 errs = append(errs, err) 149 continue 150 } 151 152 err = json.Unmarshal(bidder_imp_extension.Bidder, &mobileFuseImpExtension) 153 154 if err != nil { 155 errs = append(errs, err) 156 continue 157 } 158 159 break 160 } 161 162 return &mobileFuseImpExtension, errs 163 } 164 165 func getMobileFuseExtensionForImp(imp *openrtb2.Imp, mobileFuseImpExtension *openrtb_ext.ExtImpMobileFuse) error { 166 var bidder_imp_extension adapters.ExtImpBidder 167 168 err := json.Unmarshal(imp.Ext, &bidder_imp_extension) 169 if err != nil { 170 return err 171 } 172 173 return json.Unmarshal(bidder_imp_extension.Bidder, &mobileFuseImpExtension) 174 } 175 176 func (adapter *MobileFuseAdapter) getEndpoint(ext *openrtb_ext.ExtImpMobileFuse) (string, error) { 177 publisher_id := strconv.Itoa(ext.PublisherId) 178 179 url, err := macros.ResolveMacros(adapter.EndpointTemplate, macros.EndpointTemplateParams{PublisherID: publisher_id}) 180 if err != nil { 181 return "", err 182 } 183 184 if ext.TagidSrc == "ext" { 185 url += "&tagid_src=ext" 186 } 187 188 return url, nil 189 } 190 191 func getValidImps(bidRequest *openrtb2.BidRequest, ext *openrtb_ext.ExtImpMobileFuse) ([]openrtb2.Imp, error) { 192 var validImps []openrtb2.Imp 193 194 for _, imp := range bidRequest.Imp { 195 if imp.Banner != nil || imp.Video != nil || imp.Native != nil { 196 err := getMobileFuseExtensionForImp(&imp, ext) 197 if err != nil { 198 return nil, err 199 } 200 201 imp.TagID = strconv.Itoa(ext.PlacementId) 202 203 var extSkadn ExtSkadn 204 err = json.Unmarshal(imp.Ext, &extSkadn) 205 if err != nil { 206 return nil, err 207 } 208 209 if extSkadn.Skadn != nil { 210 imp.Ext, err = json.Marshal(map[string]json.RawMessage{"skadn": extSkadn.Skadn}) 211 if err != nil { 212 return nil, err 213 } 214 } else { 215 imp.Ext = nil 216 } 217 218 validImps = append(validImps, imp) 219 } 220 } 221 222 if len(validImps) == 0 { 223 return nil, fmt.Errorf("No valid imps") 224 } 225 226 return validImps, nil 227 } 228 229 func getBidType(bid openrtb2.Bid) openrtb_ext.BidType { 230 if bid.Ext != nil { 231 var bidExt BidExt 232 err := json.Unmarshal(bid.Ext, &bidExt) 233 if err == nil { 234 if bidExt.Mf.MediaType == "video" { 235 return openrtb_ext.BidTypeVideo 236 } else if bidExt.Mf.MediaType == "native" { 237 return openrtb_ext.BidTypeNative 238 } 239 } 240 } 241 242 return openrtb_ext.BidTypeBanner 243 }