github.com/prebid/prebid-server/v2@v2.18.0/adapters/imds/imds.go (about) 1 package imds 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 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 const adapterVersion string = "pbs-go/1.0.0" 19 20 type adapter struct { 21 EndpointTemplate *template.Template 22 } 23 24 type SyncEndpointTemplateParams struct { 25 SeatId string 26 TagId string 27 } 28 29 type ReqExt struct { 30 SeatId string `json:"seatId"` 31 } 32 33 // Builder builds a new instance of the Imds adapter for the given bidder with the given config. 34 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 35 template, err := template.New("endpointTemplate").Parse(config.Endpoint) 36 if err != nil { 37 return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) 38 } 39 40 bidder := &adapter{ 41 EndpointTemplate: template, 42 } 43 return bidder, nil 44 } 45 46 func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 47 var errs []error 48 var bidRequests []*adapters.RequestData 49 50 adapterReq, errors := a.makeRequest(request) 51 if adapterReq != nil { 52 bidRequests = append(bidRequests, adapterReq) 53 } 54 errs = append(errs, errors...) 55 56 return bidRequests, errs 57 } 58 59 func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, []error) { 60 var errs []error 61 var validImps []openrtb2.Imp 62 var re *ReqExt 63 var firstExtImp *openrtb_ext.ExtImpImds = nil 64 65 for _, imp := range request.Imp { 66 validExtImpObj, err := getExtImpObj(&imp) // getExtImpObj returns {seatId:"", tagId:""} 67 if err != nil { 68 errs = append(errs, err) 69 continue 70 } 71 // if the bid request is missing seatId or TagId then ignore it 72 if validExtImpObj.SeatId == "" || validExtImpObj.TagId == "" { 73 errs = append(errs, &errortypes.BadServerResponse{ 74 Message: fmt.Sprintf("Invalid Impression"), 75 }) 76 continue 77 } 78 // right here is where we need to take out the tagId and then add it to imp 79 imp.TagID = validExtImpObj.TagId 80 validImps = append(validImps, imp) 81 if firstExtImp == nil { 82 firstExtImp = validExtImpObj 83 } 84 } 85 86 if len(validImps) == 0 { 87 return nil, errs 88 } 89 90 var err error 91 92 if firstExtImp == nil || firstExtImp.SeatId == "" || firstExtImp.TagId == "" { 93 return nil, append(errs, &errortypes.BadServerResponse{ 94 Message: fmt.Sprintf("Invalid Impression"), 95 }) 96 } 97 // this is where the empty seatId is filled 98 re = &ReqExt{SeatId: firstExtImp.SeatId} 99 100 // create JSON Request Body 101 request.Imp = validImps 102 request.Ext, err = json.Marshal(re) 103 if err != nil { 104 return nil, append(errs, err) 105 } 106 107 reqJSON, err := json.Marshal(request) 108 if err != nil { 109 return nil, append(errs, err) 110 } 111 112 // set Request Headers 113 headers := http.Header{} 114 headers.Add("Content-Type", "application/json;charset=utf-8") 115 headers.Add("Accept", "application/json") 116 117 // create Request Uri 118 reqUri, err := a.buildEndpointURL(firstExtImp) 119 if err != nil { 120 return nil, append(errs, err) 121 } 122 123 return &adapters.RequestData{ 124 Method: http.MethodPost, 125 Uri: reqUri, 126 Body: reqJSON, 127 Headers: headers, 128 ImpIDs: openrtb_ext.GetImpIDs(request.Imp), 129 }, errs 130 } 131 132 // Builds enpoint url based on adapter-specific pub settings from imp.ext 133 func (adapter *adapter) buildEndpointURL(params *openrtb_ext.ExtImpImds) (string, error) { 134 return macros.ResolveMacros(adapter.EndpointTemplate, macros.EndpointTemplateParams{AccountID: url.QueryEscape(params.SeatId), SourceId: url.QueryEscape(adapterVersion)}) 135 } 136 137 func getExtImpObj(imp *openrtb2.Imp) (*openrtb_ext.ExtImpImds, error) { 138 var bidderExt adapters.ExtImpBidder 139 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 140 return nil, &errortypes.BadInput{ 141 Message: err.Error(), 142 } 143 } 144 145 var imdsExt openrtb_ext.ExtImpImds 146 if err := json.Unmarshal(bidderExt.Bidder, &imdsExt); err != nil { 147 return nil, &errortypes.BadInput{ 148 Message: err.Error(), 149 } 150 } 151 152 return &imdsExt, nil 153 } 154 155 // MakeBids make the bids for the bid response. 156 func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 157 const errorMessage string = "Unexpected status code: %d. Run with request.debug = 1 for more info" 158 switch { 159 case response.StatusCode == http.StatusNoContent: 160 return nil, nil 161 case response.StatusCode == http.StatusBadRequest: 162 return nil, []error{&errortypes.BadInput{ 163 Message: fmt.Sprintf(errorMessage, response.StatusCode), 164 }} 165 case response.StatusCode != http.StatusOK: 166 return nil, []error{&errortypes.BadServerResponse{ 167 Message: fmt.Sprintf(errorMessage, response.StatusCode), 168 }} 169 } 170 171 var bidResp openrtb2.BidResponse 172 173 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 174 return nil, []error{err} 175 } 176 177 bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 178 179 for _, sb := range bidResp.SeatBid { 180 for i := range sb.Bid { 181 var mediaType = getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp) 182 if mediaType != openrtb_ext.BidTypeBanner && mediaType != openrtb_ext.BidTypeVideo { 183 continue 184 } 185 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 186 Bid: &sb.Bid[i], 187 BidType: mediaType, 188 }) 189 } 190 } 191 return bidResponse, nil 192 } 193 194 func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType { 195 mediaType := openrtb_ext.BidTypeBanner 196 for _, imp := range imps { 197 if imp.ID == impId { 198 if imp.Banner != nil { 199 break 200 } 201 if imp.Video != nil { 202 mediaType = openrtb_ext.BidTypeVideo 203 break 204 } 205 if imp.Native != nil { 206 mediaType = openrtb_ext.BidTypeNative 207 break 208 } 209 if imp.Audio != nil { 210 mediaType = openrtb_ext.BidTypeAudio 211 break 212 } 213 } 214 } 215 return mediaType 216 }