github.com/prebid/prebid-server/v2@v2.18.0/adapters/amx/amx.go (about) 1 package amx 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "net/url" 8 9 "github.com/prebid/openrtb/v20/openrtb2" 10 "github.com/prebid/prebid-server/v2/adapters" 11 "github.com/prebid/prebid-server/v2/config" 12 "github.com/prebid/prebid-server/v2/errortypes" 13 "github.com/prebid/prebid-server/v2/openrtb_ext" 14 ) 15 16 const nbrHeaderName = "x-nbr" 17 const adapterVersion = "pbs1.2" 18 19 // AMXAdapter is the AMX bid adapter 20 type AMXAdapter struct { 21 endpoint string 22 } 23 24 // Builder builds a new instance of the AMX adapter for the given bidder with the given config. 25 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 26 endpointURL, err := url.Parse(config.Endpoint) 27 if err != nil { 28 return nil, fmt.Errorf("invalid endpoint: %v", err) 29 } 30 31 qs, err := url.ParseQuery(endpointURL.RawQuery) 32 if err != nil { 33 return nil, fmt.Errorf("invalid query parameters in the endpoint: %v", err) 34 } 35 36 qs.Add("v", adapterVersion) 37 endpointURL.RawQuery = qs.Encode() 38 39 bidder := &AMXAdapter{ 40 endpoint: endpointURL.String(), 41 } 42 return bidder, nil 43 } 44 45 type amxExt struct { 46 Bidder openrtb_ext.ExtImpAMX `json:"bidder"` 47 } 48 49 func ensurePublisherWithID(pub *openrtb2.Publisher, publisherID string) openrtb2.Publisher { 50 if pub == nil { 51 return openrtb2.Publisher{ID: publisherID} 52 } 53 54 pubCopy := *pub 55 pubCopy.ID = publisherID 56 return pubCopy 57 } 58 59 // MakeRequests creates AMX adapter requests 60 func (adapter *AMXAdapter) MakeRequests(request *openrtb2.BidRequest, req *adapters.ExtraRequestInfo) (reqsBidder []*adapters.RequestData, errs []error) { 61 reqCopy := *request 62 63 var publisherID string 64 for idx, imp := range reqCopy.Imp { 65 var params amxExt 66 if err := json.Unmarshal(imp.Ext, ¶ms); err == nil { 67 if params.Bidder.TagID != "" { 68 publisherID = params.Bidder.TagID 69 } 70 71 // if it has an adUnitId, set as the tagid 72 if params.Bidder.AdUnitID != "" { 73 imp.TagID = params.Bidder.AdUnitID 74 reqCopy.Imp[idx] = imp 75 } 76 } 77 } 78 79 if publisherID != "" { 80 if reqCopy.App != nil { 81 publisher := ensurePublisherWithID(reqCopy.App.Publisher, publisherID) 82 appCopy := *request.App 83 appCopy.Publisher = &publisher 84 reqCopy.App = &appCopy 85 } 86 if reqCopy.Site != nil { 87 publisher := ensurePublisherWithID(reqCopy.Site.Publisher, publisherID) 88 siteCopy := *request.Site 89 siteCopy.Publisher = &publisher 90 reqCopy.Site = &siteCopy 91 } 92 } 93 94 encoded, err := json.Marshal(reqCopy) 95 if err != nil { 96 errs = append(errs, err) 97 return nil, errs 98 } 99 100 headers := http.Header{} 101 headers.Add("Content-Type", "application/json;charset=utf-8") 102 103 reqBidder := &adapters.RequestData{ 104 Method: "POST", 105 Uri: adapter.endpoint, 106 Body: encoded, 107 Headers: headers, 108 ImpIDs: openrtb_ext.GetImpIDs(reqCopy.Imp), 109 } 110 reqsBidder = append(reqsBidder, reqBidder) 111 return 112 } 113 114 type amxBidExt struct { 115 StartDelay *int `json:"startdelay,omitempty"` 116 CreativeType *int `json:"ct,omitempty"` 117 } 118 119 // MakeBids will parse the bids from the AMX server 120 func (adapter *AMXAdapter) MakeBids(request *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 121 var errs []error 122 123 if http.StatusNoContent == response.StatusCode { 124 return nil, nil 125 } 126 127 if http.StatusBadRequest == response.StatusCode { 128 internalNBR := response.Headers.Get(nbrHeaderName) 129 return nil, []error{&errortypes.BadInput{ 130 Message: fmt.Sprintf("Invalid Request: 400. Error Code: %s", internalNBR), 131 }} 132 } 133 134 if http.StatusOK != response.StatusCode { 135 internalNBR := response.Headers.Get(nbrHeaderName) 136 return nil, []error{&errortypes.BadServerResponse{ 137 Message: fmt.Sprintf("Unexpected response: %d. Error Code: %s", response.StatusCode, internalNBR), 138 }} 139 } 140 141 var bidResp openrtb2.BidResponse 142 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 143 return nil, []error{err} 144 } 145 146 bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) 147 148 for _, sb := range bidResp.SeatBid { 149 for _, bid := range sb.Bid { 150 bid := bid 151 bidExt, bidExtErr := getBidExt(bid.Ext) 152 if bidExtErr != nil { 153 errs = append(errs, bidExtErr) 154 continue 155 } 156 157 bidType := getMediaTypeForBid(bidExt) 158 b := &adapters.TypedBid{ 159 Bid: &bid, 160 BidType: bidType, 161 } 162 163 bidResponse.Bids = append(bidResponse.Bids, b) 164 } 165 } 166 167 return bidResponse, errs 168 } 169 170 func getBidExt(ext json.RawMessage) (amxBidExt, error) { 171 if len(ext) == 0 { 172 return amxBidExt{}, nil 173 } 174 175 var bidExt amxBidExt 176 err := json.Unmarshal(ext, &bidExt) 177 return bidExt, err 178 } 179 180 func getMediaTypeForBid(bidExt amxBidExt) openrtb_ext.BidType { 181 if bidExt.StartDelay != nil { 182 return openrtb_ext.BidTypeVideo 183 } 184 185 if bidExt.CreativeType != nil && *bidExt.CreativeType == 10 { 186 return openrtb_ext.BidTypeNative 187 } 188 189 return openrtb_ext.BidTypeBanner 190 }