github.com/prebid/prebid-server/v2@v2.18.0/adapters/smartadserver/smartadserver.go (about) 1 package smartadserver 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 "path" 9 "strconv" 10 11 "github.com/prebid/openrtb/v20/openrtb2" 12 "github.com/prebid/prebid-server/v2/adapters" 13 "github.com/prebid/prebid-server/v2/config" 14 "github.com/prebid/prebid-server/v2/errortypes" 15 "github.com/prebid/prebid-server/v2/openrtb_ext" 16 ) 17 18 type SmartAdserverAdapter struct { 19 host string 20 Server config.Server 21 } 22 23 // Builder builds a new instance of the SmartAdserver adapter for the given bidder with the given config. 24 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 25 bidder := &SmartAdserverAdapter{ 26 host: config.Endpoint, 27 } 28 return bidder, nil 29 } 30 31 // MakeRequests makes the HTTP requests which should be made to fetch bids. 32 func (a *SmartAdserverAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 33 if len(request.Imp) == 0 { 34 return nil, []error{&errortypes.BadInput{ 35 Message: "No impression in the bid request", 36 }} 37 } 38 39 var adapterRequests []*adapters.RequestData 40 var errs []error 41 42 // We copy the original request. 43 smartRequest := *request 44 45 // We create or copy the Site object. 46 if smartRequest.Site == nil { 47 smartRequest.Site = &openrtb2.Site{} 48 } else { 49 site := *smartRequest.Site 50 smartRequest.Site = &site 51 } 52 53 // We create or copy the Publisher object. 54 if smartRequest.Site.Publisher == nil { 55 smartRequest.Site.Publisher = &openrtb2.Publisher{} 56 } else { 57 publisher := *smartRequest.Site.Publisher 58 smartRequest.Site.Publisher = &publisher 59 } 60 61 // We send one serialized "smartRequest" per impression of the original request. 62 for _, imp := range request.Imp { 63 var bidderExt adapters.ExtImpBidder 64 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 65 errs = append(errs, &errortypes.BadInput{ 66 Message: "Error parsing bidderExt object", 67 }) 68 continue 69 } 70 71 var smartadserverExt openrtb_ext.ExtImpSmartadserver 72 if err := json.Unmarshal(bidderExt.Bidder, &smartadserverExt); err != nil { 73 errs = append(errs, &errortypes.BadInput{ 74 Message: "Error parsing smartadserverExt parameters", 75 }) 76 continue 77 } 78 79 // Adding publisher id. 80 smartRequest.Site.Publisher.ID = strconv.Itoa(smartadserverExt.NetworkID) 81 82 // We send one request for each impression. 83 smartRequest.Imp = []openrtb2.Imp{imp} 84 85 var errMarshal error 86 if imp.Ext, errMarshal = json.Marshal(smartadserverExt); errMarshal != nil { 87 errs = append(errs, &errortypes.BadInput{ 88 Message: errMarshal.Error(), 89 }) 90 continue 91 } 92 93 reqJSON, err := json.Marshal(smartRequest) 94 if err != nil { 95 errs = append(errs, &errortypes.BadInput{ 96 Message: "Error parsing reqJSON object", 97 }) 98 continue 99 } 100 101 url, err := a.BuildEndpointURL(&smartadserverExt) 102 if url == "" { 103 errs = append(errs, err) 104 continue 105 } 106 107 headers := http.Header{} 108 headers.Add("Content-Type", "application/json;charset=utf-8") 109 headers.Add("Accept", "application/json") 110 adapterRequests = append(adapterRequests, &adapters.RequestData{ 111 Method: "POST", 112 Uri: url, 113 Body: reqJSON, 114 Headers: headers, 115 ImpIDs: openrtb_ext.GetImpIDs(smartRequest.Imp), 116 }) 117 } 118 return adapterRequests, errs 119 } 120 121 // MakeBids unpacks the server's response into Bids. 122 func (a *SmartAdserverAdapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 123 if response.StatusCode == http.StatusNoContent { 124 return nil, nil 125 } 126 127 if response.StatusCode == http.StatusBadRequest { 128 return nil, []error{&errortypes.BadInput{ 129 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 130 }} 131 } 132 133 if response.StatusCode != http.StatusOK { 134 return nil, []error{&errortypes.BadServerResponse{ 135 Message: "Unexpected status code: " + strconv.Itoa(response.StatusCode) + ". Run with request.debug = 1 for more info", 136 }} 137 } 138 139 var bidResp openrtb2.BidResponse 140 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 141 return nil, []error{err} 142 } 143 144 bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) 145 146 for _, sb := range bidResp.SeatBid { 147 for i := 0; i < len(sb.Bid); i++ { 148 bid := sb.Bid[i] 149 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 150 Bid: &bid, 151 BidType: getMediaTypeForImp(bid.ImpID, internalRequest.Imp), 152 }) 153 154 } 155 } 156 return bidResponse, []error{} 157 } 158 159 // BuildEndpointURL : Builds endpoint url 160 func (a *SmartAdserverAdapter) BuildEndpointURL(params *openrtb_ext.ExtImpSmartadserver) (string, error) { 161 uri, err := url.Parse(a.host) 162 if err != nil || uri.Scheme == "" || uri.Host == "" { 163 return "", &errortypes.BadInput{ 164 Message: "Malformed URL: " + a.host + ".", 165 } 166 } 167 168 uri.Path = path.Join(uri.Path, "api/bid") 169 uri.RawQuery = "callerId=5" 170 171 return uri.String(), nil 172 } 173 174 func getMediaTypeForImp(impID string, imps []openrtb2.Imp) openrtb_ext.BidType { 175 for _, imp := range imps { 176 if imp.ID == impID { 177 if imp.Video != nil { 178 return openrtb_ext.BidTypeVideo 179 } else if imp.Native != nil { 180 return openrtb_ext.BidTypeNative 181 } 182 return openrtb_ext.BidTypeBanner 183 } 184 } 185 return openrtb_ext.BidTypeBanner 186 }