github.com/prebid/prebid-server@v0.275.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/v19/openrtb2" 10 "github.com/prebid/prebid-server/adapters" 11 "github.com/prebid/prebid-server/config" 12 "github.com/prebid/prebid-server/errortypes" 13 "github.com/prebid/prebid-server/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 } 109 reqsBidder = append(reqsBidder, reqBidder) 110 return 111 } 112 113 type amxBidExt struct { 114 StartDelay *int `json:"startdelay,omitempty"` 115 CreativeType *int `json:"ct,omitempty"` 116 } 117 118 // MakeBids will parse the bids from the AMX server 119 func (adapter *AMXAdapter) MakeBids(request *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 120 var errs []error 121 122 if http.StatusNoContent == response.StatusCode { 123 return nil, nil 124 } 125 126 if http.StatusBadRequest == response.StatusCode { 127 internalNBR := response.Headers.Get(nbrHeaderName) 128 return nil, []error{&errortypes.BadInput{ 129 Message: fmt.Sprintf("Invalid Request: 400. Error Code: %s", internalNBR), 130 }} 131 } 132 133 if http.StatusOK != response.StatusCode { 134 internalNBR := response.Headers.Get(nbrHeaderName) 135 return nil, []error{&errortypes.BadServerResponse{ 136 Message: fmt.Sprintf("Unexpected response: %d. Error Code: %s", response.StatusCode, internalNBR), 137 }} 138 } 139 140 var bidResp openrtb2.BidResponse 141 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 142 return nil, []error{err} 143 } 144 145 bidResponse := adapters.NewBidderResponseWithBidsCapacity(5) 146 147 for _, sb := range bidResp.SeatBid { 148 for _, bid := range sb.Bid { 149 bid := bid 150 bidExt, bidExtErr := getBidExt(bid.Ext) 151 if bidExtErr != nil { 152 errs = append(errs, bidExtErr) 153 continue 154 } 155 156 bidType := getMediaTypeForBid(bidExt) 157 b := &adapters.TypedBid{ 158 Bid: &bid, 159 BidType: bidType, 160 } 161 162 bidResponse.Bids = append(bidResponse.Bids, b) 163 } 164 } 165 166 return bidResponse, errs 167 } 168 169 func getBidExt(ext json.RawMessage) (amxBidExt, error) { 170 if len(ext) == 0 { 171 return amxBidExt{}, nil 172 } 173 174 var bidExt amxBidExt 175 err := json.Unmarshal(ext, &bidExt) 176 return bidExt, err 177 } 178 179 func getMediaTypeForBid(bidExt amxBidExt) openrtb_ext.BidType { 180 if bidExt.StartDelay != nil { 181 return openrtb_ext.BidTypeVideo 182 } 183 184 if bidExt.CreativeType != nil && *bidExt.CreativeType == 10 { 185 return openrtb_ext.BidTypeNative 186 } 187 188 return openrtb_ext.BidTypeBanner 189 }