github.com/prebid/prebid-server/v2@v2.18.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/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 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 ImpIDs: openrtb_ext.GetImpIDs(openRTBRequest.Imp), 107 }}, nil 108 } 109 110 func (a *SmartyAdsAdapter) getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ExtSmartyAds, error) { 111 var bidderExt adapters.ExtImpBidder 112 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 113 return nil, &errortypes.BadInput{ 114 Message: "ext.bidder not provided", 115 } 116 } 117 var smartyadsExt openrtb_ext.ExtSmartyAds 118 if err := json.Unmarshal(bidderExt.Bidder, &smartyadsExt); err != nil { 119 return nil, &errortypes.BadInput{ 120 Message: "ext.bidder not provided", 121 } 122 } 123 return &smartyadsExt, nil 124 } 125 126 func (a *SmartyAdsAdapter) buildEndpointURL(params *openrtb_ext.ExtSmartyAds) (string, error) { 127 endpointParams := macros.EndpointTemplateParams{Host: params.Host, SourceId: params.SourceID, AccountID: params.AccountID} 128 return macros.ResolveMacros(a.endpoint, endpointParams) 129 } 130 131 func (a *SmartyAdsAdapter) CheckResponseStatusCodes(response *adapters.ResponseData) error { 132 if response.StatusCode == http.StatusNoContent { 133 return &errortypes.BadInput{Message: "No bid response"} 134 } 135 136 if response.StatusCode == http.StatusBadRequest { 137 return &errortypes.BadInput{ 138 Message: fmt.Sprintf("Unexpected status code: [ %d ]", response.StatusCode), 139 } 140 } 141 142 if response.StatusCode == http.StatusServiceUnavailable { 143 return &errortypes.BadInput{ 144 Message: fmt.Sprintf("Something went wrong, please contact your Account Manager. Status Code: [ %d ] ", response.StatusCode), 145 } 146 } 147 148 if response.StatusCode < http.StatusOK || response.StatusCode >= http.StatusMultipleChoices { 149 return &errortypes.BadInput{ 150 Message: fmt.Sprintf("Something went wrong, please contact your Account Manager. Status Code: [ %d ] ", response.StatusCode), 151 } 152 } 153 154 return nil 155 } 156 157 func (a *SmartyAdsAdapter) MakeBids( 158 openRTBRequest *openrtb2.BidRequest, 159 requestToBidder *adapters.RequestData, 160 bidderRawResponse *adapters.ResponseData, 161 ) ( 162 bidderResponse *adapters.BidderResponse, 163 errs []error, 164 ) { 165 httpStatusError := a.CheckResponseStatusCodes(bidderRawResponse) 166 if httpStatusError != nil { 167 return nil, []error{httpStatusError} 168 } 169 170 responseBody := bidderRawResponse.Body 171 var bidResp openrtb2.BidResponse 172 if err := json.Unmarshal(responseBody, &bidResp); err != nil { 173 return nil, []error{&errortypes.BadServerResponse{ 174 Message: "Bad Server Response", 175 }} 176 } 177 178 if len(bidResp.SeatBid) == 0 { 179 return nil, []error{&errortypes.BadServerResponse{ 180 Message: "Empty SeatBid array", 181 }} 182 } 183 184 bidResponse := adapters.NewBidderResponseWithBidsCapacity(len(bidResp.SeatBid[0].Bid)) 185 sb := bidResp.SeatBid[0] 186 187 for _, bid := range sb.Bid { 188 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 189 Bid: &bid, 190 BidType: getMediaTypeForImp(bid.ImpID, openRTBRequest.Imp), 191 }) 192 } 193 return bidResponse, nil 194 } 195 196 func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType { 197 mediaType := openrtb_ext.BidTypeBanner 198 for _, imp := range imps { 199 if imp.ID == impId { 200 if imp.Video != nil { 201 mediaType = openrtb_ext.BidTypeVideo 202 } else if imp.Native != nil { 203 mediaType = openrtb_ext.BidTypeNative 204 } 205 return mediaType 206 } 207 } 208 return mediaType 209 }