github.com/prebid/prebid-server/v2@v2.18.0/adapters/datablocks/datablocks.go (about) 1 package datablocks 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 DatablocksAdapter struct { 19 EndpointTemplate *template.Template 20 } 21 22 func (a *DatablocksAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 23 24 errs := make([]error, 0, len(request.Imp)) 25 headers := http.Header{ 26 "Content-Type": {"application/json"}, 27 "Accept": {"application/json"}, 28 } 29 30 // Pull the source ID info from the bidder params. 31 reqImps, err := splitImpressions(request.Imp) 32 33 if err != nil { 34 errs = append(errs, err) 35 } 36 37 requests := []*adapters.RequestData{} 38 39 for reqExt, reqImp := range reqImps { 40 request.Imp = reqImp 41 reqJson, err := json.Marshal(request) 42 43 if err != nil { 44 errs = append(errs, err) 45 continue 46 } 47 48 urlParams := macros.EndpointTemplateParams{SourceId: strconv.Itoa(reqExt.SourceId)} 49 url, err := macros.ResolveMacros(a.EndpointTemplate, urlParams) 50 51 if err != nil { 52 errs = append(errs, err) 53 continue 54 } 55 56 request := adapters.RequestData{ 57 Method: "POST", 58 Uri: url, 59 Body: reqJson, 60 Headers: headers, 61 ImpIDs: openrtb_ext.GetImpIDs(request.Imp)} 62 63 requests = append(requests, &request) 64 } 65 66 return requests, errs 67 } 68 69 /* 70 internal original request in OpenRTB, external = result of us having converted it (what comes out of MakeRequests) 71 */ 72 func (a *DatablocksAdapter) MakeBids( 73 internalRequest *openrtb2.BidRequest, 74 externalRequest *adapters.RequestData, 75 response *adapters.ResponseData, 76 ) (*adapters.BidderResponse, []error) { 77 78 if response.StatusCode == http.StatusNoContent { 79 return nil, nil 80 } 81 82 if response.StatusCode != http.StatusOK { 83 return nil, []error{&errortypes.BadServerResponse{ 84 Message: fmt.Sprintf("ERR, response with status %d", response.StatusCode), 85 }} 86 } 87 88 var bidResp openrtb2.BidResponse 89 90 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 91 return nil, []error{err} 92 } 93 94 bidResponse := adapters.NewBidderResponse() 95 bidResponse.Currency = bidResp.Cur 96 97 for _, seatBid := range bidResp.SeatBid { 98 for i := 0; i < len(seatBid.Bid); i++ { 99 bid := seatBid.Bid[i] 100 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 101 Bid: &bid, 102 BidType: getMediaType(bid.ImpID, internalRequest.Imp), 103 }) 104 } 105 } 106 107 return bidResponse, nil 108 } 109 110 func splitImpressions(imps []openrtb2.Imp) (map[openrtb_ext.ExtImpDatablocks][]openrtb2.Imp, error) { 111 112 var m = make(map[openrtb_ext.ExtImpDatablocks][]openrtb2.Imp) 113 114 for _, imp := range imps { 115 bidderParams, err := getBidderParams(&imp) 116 if err != nil { 117 return nil, err 118 } 119 120 v, ok := m[*bidderParams] 121 if ok { 122 m[*bidderParams] = append(v, imp) 123 } else { 124 m[*bidderParams] = []openrtb2.Imp{imp} 125 } 126 } 127 128 return m, nil 129 } 130 131 func getBidderParams(imp *openrtb2.Imp) (*openrtb_ext.ExtImpDatablocks, error) { 132 var bidderExt adapters.ExtImpBidder 133 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 134 return nil, &errortypes.BadInput{ 135 Message: fmt.Sprintf("Missing bidder ext: %s", err.Error()), 136 } 137 } 138 var datablocksExt openrtb_ext.ExtImpDatablocks 139 if err := json.Unmarshal(bidderExt.Bidder, &datablocksExt); err != nil { 140 return nil, &errortypes.BadInput{ 141 Message: fmt.Sprintf("Cannot Resolve sourceId: %s", err.Error()), 142 } 143 } 144 145 if datablocksExt.SourceId < 1 { 146 return nil, &errortypes.BadInput{ 147 Message: "Invalid/Missing SourceId", 148 } 149 } 150 151 return &datablocksExt, nil 152 } 153 154 func getMediaType(impID string, imps []openrtb2.Imp) openrtb_ext.BidType { 155 156 bidType := openrtb_ext.BidTypeBanner 157 158 for _, imp := range imps { 159 if imp.ID == impID { 160 if imp.Video != nil { 161 bidType = openrtb_ext.BidTypeVideo 162 break 163 } else if imp.Native != nil { 164 bidType = openrtb_ext.BidTypeNative 165 break 166 } else { 167 bidType = openrtb_ext.BidTypeBanner 168 break 169 } 170 } 171 } 172 173 return bidType 174 } 175 176 // Builder builds a new instance of the Datablocks adapter for the given bidder with the given config. 177 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 178 template, err := template.New("endpointTemplate").Parse(config.Endpoint) 179 if err != nil { 180 return nil, fmt.Errorf("unable to parse endpoint url template: %v", err) 181 } 182 183 bidder := &DatablocksAdapter{ 184 EndpointTemplate: template, 185 } 186 return bidder, nil 187 }