github.com/prebid/prebid-server/v2@v2.18.0/adapters/adot/adot.go (about) 1 package adot 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strconv" 8 "strings" 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/openrtb_ext" 15 ) 16 17 type adapter struct { 18 endpoint string 19 } 20 21 type adotBidExt struct { 22 Adot bidExt `json:"adot"` 23 } 24 25 type bidExt struct { 26 MediaType string `json:"media_type"` 27 } 28 29 // Builder builds a new instance of the Adot adapter for the given bidder with the given config. 30 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 31 bidder := &adapter{ 32 endpoint: config.Endpoint, 33 } 34 return bidder, nil 35 } 36 37 // MakeRequests makes the HTTP requests which should be made to fetch bids. 38 func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 39 var reqJSON []byte 40 var publisherPath string 41 var err error 42 43 if reqJSON, err = json.Marshal(request); err != nil { 44 return nil, []error{fmt.Errorf("unable to marshal openrtb request (%v)", err)} 45 } 46 47 headers := http.Header{} 48 headers.Add("Content-Type", "application/json;charset=utf-8") 49 50 if adotExt := getImpAdotExt(&request.Imp[0]); adotExt != nil { 51 publisherPath = adotExt.PublisherPath 52 } else { 53 publisherPath = "" 54 } 55 56 endpoint := strings.Replace(a.endpoint, "{PUBLISHER_PATH}", publisherPath, -1) 57 58 return []*adapters.RequestData{{ 59 Method: "POST", 60 Uri: endpoint, 61 Body: reqJSON, 62 Headers: headers, 63 ImpIDs: openrtb_ext.GetImpIDs(request.Imp), 64 }}, nil 65 } 66 67 // MakeBids unpacks the server's response into Bids. 68 // The bidder return a status code 204 when it cannot delivery an ad. 69 func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 70 if response.StatusCode == http.StatusNoContent { 71 return nil, nil 72 } 73 74 if response.StatusCode == http.StatusBadRequest { 75 return nil, []error{&errortypes.BadInput{ 76 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 77 }} 78 } 79 80 if response.StatusCode != http.StatusOK { 81 return nil, []error{&errortypes.BadServerResponse{ 82 Message: fmt.Sprintf("Unexpected status code: %d. Run with request.debug = 1 for more info", response.StatusCode), 83 }} 84 } 85 86 var bidResp openrtb2.BidResponse 87 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 88 return nil, []error{err} 89 } 90 91 bidsCapacity := 1 92 if len(bidResp.SeatBid) > 0 { 93 bidsCapacity = len(bidResp.SeatBid[0].Bid) 94 } 95 bidResponse := adapters.NewBidderResponseWithBidsCapacity(bidsCapacity) 96 97 for _, sb := range bidResp.SeatBid { 98 for i := range sb.Bid { 99 if bidType, err := getMediaTypeForBid(&sb.Bid[i]); err == nil { 100 resolveMacros(&sb.Bid[i]) 101 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 102 Bid: &sb.Bid[i], 103 BidType: bidType, 104 }) 105 } 106 } 107 } 108 109 return bidResponse, nil 110 } 111 112 // getMediaTypeForBid determines which type of bid. 113 func getMediaTypeForBid(bid *openrtb2.Bid) (openrtb_ext.BidType, error) { 114 if bid == nil { 115 return "", fmt.Errorf("the bid request object is nil") 116 } 117 118 var impExt adotBidExt 119 if err := json.Unmarshal(bid.Ext, &impExt); err == nil { 120 switch impExt.Adot.MediaType { 121 case string(openrtb_ext.BidTypeBanner): 122 return openrtb_ext.BidTypeBanner, nil 123 case string(openrtb_ext.BidTypeVideo): 124 return openrtb_ext.BidTypeVideo, nil 125 case string(openrtb_ext.BidTypeNative): 126 return openrtb_ext.BidTypeNative, nil 127 } 128 } 129 130 return "", fmt.Errorf("unrecognized bid type in response from adot") 131 } 132 133 // resolveMacros resolves OpenRTB macros in nurl and adm 134 func resolveMacros(bid *openrtb2.Bid) { 135 if bid == nil { 136 return 137 } 138 price := strconv.FormatFloat(bid.Price, 'f', -1, 64) 139 bid.NURL = strings.Replace(bid.NURL, "${AUCTION_PRICE}", price, -1) 140 bid.AdM = strings.Replace(bid.AdM, "${AUCTION_PRICE}", price, -1) 141 } 142 143 // getImpAdotExt parses and return first imp ext or nil 144 func getImpAdotExt(imp *openrtb2.Imp) *openrtb_ext.ExtImpAdot { 145 var extImpAdot openrtb_ext.ExtImpAdot 146 var extBidder adapters.ExtImpBidder 147 err := json.Unmarshal(imp.Ext, &extBidder) 148 if err != nil { 149 return nil 150 } 151 err = json.Unmarshal(extBidder.Bidder, &extImpAdot) 152 if err != nil { 153 return nil 154 } 155 return &extImpAdot 156 }