github.com/prebid/prebid-server/v2@v2.18.0/adapters/adview/adview.go (about) 1 package adview 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strings" 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 adapter struct { 19 endpoint *template.Template 20 } 21 22 type adviewBidExt struct { 23 BidType int `json:"formattype,omitempty"` 24 } 25 26 // Builder builds a new instance of the adview adapter for the given bidder with the given config. 27 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 28 endpointTemplate, err := template.New("endpointTemplate").Parse(config.Endpoint) 29 if err != nil { 30 return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) 31 } 32 33 bidder := &adapter{ 34 endpoint: endpointTemplate, 35 } 36 return bidder, nil 37 } 38 39 func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 40 41 var requests []*adapters.RequestData 42 var errors []error 43 44 //must copy the original request. 45 requestCopy := *request 46 for _, imp := range request.Imp { 47 var bidderExt adapters.ExtImpBidder 48 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 49 errors = append(errors, &errortypes.BadInput{ 50 Message: fmt.Sprintf("invalid imp.ext, %s", err.Error()), 51 }) 52 continue 53 } 54 //use adview 55 var advImpExt openrtb_ext.ExtImpAdView 56 if err := json.Unmarshal(bidderExt.Bidder, &advImpExt); err != nil { 57 errors = append(errors, &errortypes.BadInput{ 58 Message: fmt.Sprintf("invalid bidderExt.Bidder, %s", err.Error()), 59 }) 60 continue 61 } 62 63 imp.TagID = advImpExt.MasterTagID //tagid means posid 64 //for adview bid request 65 if imp.Banner != nil { 66 if len(imp.Banner.Format) != 0 { 67 bannerCopy := *imp.Banner 68 bannerCopy.H = &imp.Banner.Format[0].H 69 bannerCopy.W = &imp.Banner.Format[0].W 70 imp.Banner = &bannerCopy 71 } 72 } 73 74 // Check if imp comes with bid floor amount defined in a foreign currency 75 if imp.BidFloor > 0 && imp.BidFloorCur != "" && strings.ToUpper(imp.BidFloorCur) != "USD" { 76 // Convert to US dollars 77 convertedValue, err := requestInfo.ConvertCurrency(imp.BidFloor, imp.BidFloorCur, "USD") 78 if err != nil { 79 errors = append(errors, err) 80 continue 81 } 82 // Update after conversion. All imp elements inside request.Imp are shallow copies 83 // therefore, their non-pointer values are not shared memory and are safe to modify. 84 imp.BidFloorCur = "USD" 85 imp.BidFloor = convertedValue 86 } 87 88 // Set the CUR of bid to USD after converting all floors 89 requestCopy.Cur = []string{"USD"} 90 requestCopy.Imp = []openrtb2.Imp{imp} 91 92 url, err := a.buildEndpointURL(&advImpExt) 93 if err != nil { 94 errors = append(errors, err) 95 continue 96 } 97 98 reqJSON, err := json.Marshal(requestCopy) //request 99 if err != nil { 100 errors = append(errors, err) 101 continue 102 } 103 104 requestData := &adapters.RequestData{ 105 Method: http.MethodPost, 106 Uri: url, 107 Body: reqJSON, 108 ImpIDs: openrtb_ext.GetImpIDs(requestCopy.Imp), 109 } 110 requests = append(requests, requestData) 111 } 112 return requests, errors 113 } 114 115 func (a *adapter) MakeBids(request *openrtb2.BidRequest, requestData *adapters.RequestData, responseData *adapters.ResponseData) (*adapters.BidderResponse, []error) { 116 if responseData.StatusCode == http.StatusNoContent { 117 return nil, nil 118 } 119 120 if responseData.StatusCode == http.StatusBadRequest { 121 err := &errortypes.BadInput{ 122 Message: "Unexpected status code: 400. Bad request from publisher.", 123 } 124 return nil, []error{err} 125 } 126 127 if responseData.StatusCode != http.StatusOK { 128 err := &errortypes.BadServerResponse{ 129 Message: fmt.Sprintf("Unexpected status code: %d.", responseData.StatusCode), 130 } 131 return nil, []error{err} 132 } 133 134 var response openrtb2.BidResponse 135 if err := json.Unmarshal(responseData.Body, &response); err != nil { 136 return nil, []error{err} 137 } 138 139 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(request.Imp)) 140 //we just support USD for resp 141 bidResponse.Currency = "USD" 142 143 var errors []error 144 for _, seatBid := range response.SeatBid { 145 for i, bid := range seatBid.Bid { 146 bidType, err := getMediaTypeForBid(bid) 147 if err != nil { 148 errors = append(errors, err) 149 continue 150 } 151 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 152 Bid: &seatBid.Bid[i], 153 BidType: bidType, 154 }) 155 } 156 } 157 158 return bidResponse, errors 159 } 160 161 // Builds endpoint url based on adapter-specific pub settings from imp.ext 162 func (a *adapter) buildEndpointURL(params *openrtb_ext.ExtImpAdView) (string, error) { 163 endpointParams := macros.EndpointTemplateParams{AccountID: params.AccountID} 164 return macros.ResolveMacros(a.endpoint, endpointParams) 165 } 166 167 func getMediaTypeForBid(bid openrtb2.Bid) (openrtb_ext.BidType, error) { 168 switch bid.MType { 169 case openrtb2.MarkupBanner: 170 return openrtb_ext.BidTypeBanner, nil 171 case openrtb2.MarkupVideo: 172 return openrtb_ext.BidTypeVideo, nil 173 case openrtb2.MarkupNative: 174 return openrtb_ext.BidTypeNative, nil 175 default: 176 return "", fmt.Errorf("Unable to fetch mediaType in impID: %s, mType: %d", bid.ImpID, bid.MType) 177 } 178 }