github.com/prebid/prebid-server@v0.275.0/adapters/smartyads/smartyads.go (about) 1 package smartyads 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strconv" 8 "text/template" 9 10 "github.com/prebid/openrtb/v19/openrtb2" 11 "github.com/prebid/prebid-server/adapters" 12 "github.com/prebid/prebid-server/config" 13 "github.com/prebid/prebid-server/errortypes" 14 "github.com/prebid/prebid-server/macros" 15 "github.com/prebid/prebid-server/openrtb_ext" 16 ) 17 18 type SmartyAdsAdapter struct { 19 endpoint *template.Template 20 } 21 22 // Builder builds a new instance of the SmartyAds adapter for the given bidder with the given config. 23 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 24 template, err := template.New("endpointTemplate").Parse(config.Endpoint) 25 if err != nil { 26 return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) 27 } 28 29 bidder := &SmartyAdsAdapter{ 30 endpoint: template, 31 } 32 return bidder, nil 33 } 34 35 func GetHeaders(request *openrtb2.BidRequest) *http.Header { 36 headers := http.Header{} 37 headers.Add("Content-Type", "application/json;charset=utf-8") 38 headers.Add("Accept", "application/json") 39 headers.Add("X-Openrtb-Version", "2.5") 40 41 if request.Device != nil { 42 if len(request.Device.UA) > 0 { 43 headers.Add("User-Agent", request.Device.UA) 44 } 45 46 if len(request.Device.IPv6) > 0 { 47 headers.Add("X-Forwarded-For", request.Device.IPv6) 48 } 49 50 if len(request.Device.IP) > 0 { 51 headers.Add("X-Forwarded-For", request.Device.IP) 52 } 53 54 if len(request.Device.Language) > 0 { 55 headers.Add("Accept-Language", request.Device.Language) 56 } 57 58 if request.Device.DNT != nil { 59 headers.Add("Dnt", strconv.Itoa(int(*request.Device.DNT))) 60 } 61 } 62 63 return &headers 64 } 65 66 func (a *SmartyAdsAdapter) MakeRequests( 67 openRTBRequest *openrtb2.BidRequest, 68 reqInfo *adapters.ExtraRequestInfo, 69 ) ( 70 requestsToBidder []*adapters.RequestData, 71 errs []error, 72 ) { 73 74 var errors []error 75 var smartyadsExt *openrtb_ext.ExtSmartyAds 76 var err error 77 78 for i, imp := range openRTBRequest.Imp { 79 smartyadsExt, err = a.getImpressionExt(&imp) 80 if err != nil { 81 errors = append(errors, err) 82 break 83 } 84 openRTBRequest.Imp[i].Ext = nil 85 } 86 87 if len(errors) > 0 { 88 return nil, errors 89 } 90 91 url, err := a.buildEndpointURL(smartyadsExt) 92 if err != nil { 93 return nil, []error{err} 94 } 95 96 reqJSON, err := json.Marshal(openRTBRequest) 97 if err != nil { 98 return nil, []error{err} 99 } 100 101 return []*adapters.RequestData{{ 102 Method: http.MethodPost, 103 Body: reqJSON, 104 Uri: url, 105 Headers: *GetHeaders(openRTBRequest), 106 }}, nil 107 } 108 109 func (a *SmartyAdsAdapter) getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtSmartyAds, error) { 110 var bidderExt adapters.ExtImpBidder 111 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 112 return nil, &errortypes.BadInput{ 113 Message: "ext.bidder not provided", 114 } 115 } 116 var smartyadsExt openrtb_ext.ExtSmartyAds 117 if err := json.Unmarshal(bidderExt.Bidder, &smartyadsExt); err != nil { 118 return nil, &errortypes.BadInput{ 119 Message: "ext.bidder not provided", 120 } 121 } 122 return &smartyadsExt, nil 123 } 124 125 func (a *SmartyAdsAdapter) buildEndpointURL(params *openrtb_ext.ExtSmartyAds) (string, error) { 126 endpointParams := macros.EndpointTemplateParams{Host: params.Host, SourceId: params.SourceID, AccountID: params.AccountID} 127 return macros.ResolveMacros(a.endpoint, endpointParams) 128 } 129 130 func (a *SmartyAdsAdapter) CheckResponseStatusCodes(response *adapters.ResponseData) error { 131 if response.StatusCode == http.StatusNoContent { 132 return &errortypes.BadInput{Message: "No bid response"} 133 } 134 135 if response.StatusCode == http.StatusBadRequest { 136 return &errortypes.BadInput{ 137 Message: fmt.Sprintf("Unexpected status code: [ %d ]", response.StatusCode), 138 } 139 } 140 141 if response.StatusCode == http.StatusServiceUnavailable { 142 return &errortypes.BadInput{ 143 Message: fmt.Sprintf("Something went wrong, please contact your Account Manager. Status Code: [ %d ] ", response.StatusCode), 144 } 145 } 146 147 if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices { 148 return &errortypes.BadInput{ 149 Message: fmt.Sprintf("Something went wrong, please contact your Account Manager. Status Code: [ %d ] ", response.StatusCode), 150 } 151 } 152 153 return nil 154 } 155 156 func (a *SmartyAdsAdapter) MakeBids( 157 openRTBRequest *openrtb2.BidRequest, 158 requestToBidder *adapters.RequestData, 159 bidderRawResponse *adapters.ResponseData, 160 ) ( 161 bidderResponse *adapters.BidderResponse, 162 errs []error, 163 ) { 164 httpStatusError := a.CheckResponseStatusCodes(bidderRawResponse) 165 if httpStatusError != nil { 166 return nil, []error{httpStatusError} 167 } 168 169 responseBody := bidderRawResponse.Body 170 var bidResp openrtb2.BidResponse 171 if err := json.Unmarshal(responseBody, &bidResp); err != nil { 172 return nil, []error{&errortypes.BadServerResponse{ 173 Message: "Bad Server Response", 174 }} 175 } 176 177 if len(bidResp.SeatBid) == 0 { 178 return nil, []error{&errortypes.BadServerResponse{ 179 Message: "Empty SeatBid array", 180 }} 181 } 182 183 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) 184 sb := bidResp.SeatBid[0] 185 186 for _, bid := range sb.Bid { 187 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 188 Bid: &bid, 189 BidType: getMediaTypeForImp(bid.ImpID, openRTBRequest.Imp), 190 }) 191 } 192 return bidResponse, nil 193 } 194 195 func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType { 196 mediaType := openrtb_ext.BidTypeBanner 197 for _, imp := range imps { 198 if imp.ID == impId { 199 if imp.Video != nil { 200 mediaType = openrtb_ext.BidTypeVideo 201 } else if imp.Native != nil { 202 mediaType = openrtb_ext.BidTypeNative 203 } 204 return mediaType 205 } 206 } 207 return mediaType 208 }