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