github.com/prebid/prebid-server/v2@v2.18.0/adapters/nextmillennium/nextmillennium.go (about) 1 package nextmillennium 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 8 "github.com/prebid/openrtb/v20/openrtb2" 9 "github.com/prebid/prebid-server/v2/adapters" 10 "github.com/prebid/prebid-server/v2/config" 11 "github.com/prebid/prebid-server/v2/errortypes" 12 "github.com/prebid/prebid-server/v2/openrtb_ext" 13 ) 14 15 type adapter struct { 16 endpoint string 17 nmmFlags []string 18 server config.Server 19 } 20 21 type nmExtPrebidStoredRequest struct { 22 ID string `json:"id"` 23 } 24 25 type server struct { 26 ExternalUrl string `json:"externalurl"` 27 GvlID int `json:"gvlid"` 28 DataCenter string `json:"datacenter"` 29 } 30 type nmExtPrebid struct { 31 StoredRequest nmExtPrebidStoredRequest `json:"storedrequest"` 32 Server *server `json:"server,omitempty"` 33 } 34 type nmExtNMM struct { 35 NmmFlags []string `json:"nmmFlags,omitempty"` 36 } 37 type nextMillJsonExt struct { 38 Prebid nmExtPrebid `json:"prebid"` 39 NextMillennium nmExtNMM `json:"nextMillennium,omitempty"` 40 } 41 42 // MakeRequests prepares request information for prebid-server core 43 func (adapter *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) { 44 resImps, err := getImpressionsInfo(request.Imp) 45 if len(err) > 0 { 46 return nil, err 47 } 48 49 result := make([]*adapters.RequestData, 0, len(resImps)) 50 for _, imp := range resImps { 51 bidRequest, err := adapter.buildAdapterRequest(request, imp) 52 if err != nil { 53 return nil, []error{err} 54 } 55 result = append(result, bidRequest) 56 } 57 58 return result, nil 59 } 60 61 func getImpressionsInfo(imps []openrtb2.Imp) (resImps []*openrtb_ext.ImpExtNextMillennium, errors []error) { 62 for _, imp := range imps { 63 impExt, err := getImpressionExt(&imp) 64 if err != nil { 65 errors = append(errors, err) 66 continue 67 } 68 69 resImps = append(resImps, impExt) 70 } 71 72 return 73 } 74 75 func getImpressionExt(imp *openrtb2.Imp) (*openrtb_ext.ImpExtNextMillennium, error) { 76 var bidderExt adapters.ExtImpBidder 77 if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil { 78 return nil, &errortypes.BadInput{ 79 Message: err.Error(), 80 } 81 } 82 var nextMillenniumExt openrtb_ext.ImpExtNextMillennium 83 if err := json.Unmarshal(bidderExt.Bidder, &nextMillenniumExt); err != nil { 84 return nil, &errortypes.BadInput{ 85 Message: err.Error(), 86 } 87 } 88 89 return &nextMillenniumExt, nil 90 } 91 92 func (adapter *adapter) buildAdapterRequest(prebidBidRequest *openrtb2.BidRequest, params *openrtb_ext.ImpExtNextMillennium) (*adapters.RequestData, error) { 93 newBidRequest := createBidRequest(prebidBidRequest, params, adapter.nmmFlags, adapter.server) 94 95 reqJSON, err := json.Marshal(newBidRequest) 96 if err != nil { 97 return nil, err 98 } 99 100 headers := http.Header{} 101 headers.Add("Content-Type", "application/json;charset=utf-8") 102 headers.Add("Accept", "application/json") 103 headers.Add("x-openrtb-version", "2.5") 104 105 return &adapters.RequestData{ 106 Method: "POST", 107 Uri: adapter.endpoint, 108 Body: reqJSON, 109 Headers: headers, 110 ImpIDs: openrtb_ext.GetImpIDs(newBidRequest.Imp)}, nil 111 } 112 113 func createBidRequest(prebidBidRequest *openrtb2.BidRequest, params *openrtb_ext.ImpExtNextMillennium, flags []string, serverParams config.Server) *openrtb2.BidRequest { 114 placementID := params.PlacementID 115 116 if params.GroupID != "" { 117 domain := "" 118 size := "" 119 120 if prebidBidRequest.Site != nil { 121 domain = prebidBidRequest.Site.Domain 122 } 123 if prebidBidRequest.App != nil { 124 domain = prebidBidRequest.App.Domain 125 } 126 127 if banner := prebidBidRequest.Imp[0].Banner; banner != nil { 128 if len(banner.Format) > 0 { 129 size = fmt.Sprintf("%dx%d", banner.Format[0].W, banner.Format[0].H) 130 } else if banner.W != nil && banner.H != nil { 131 size = fmt.Sprintf("%dx%d", *banner.W, *banner.H) 132 } 133 } 134 135 placementID = fmt.Sprintf("g%s;%s;%s", params.GroupID, size, domain) 136 } 137 ext := nextMillJsonExt{} 138 ext.Prebid.StoredRequest.ID = placementID 139 ext.NextMillennium.NmmFlags = flags 140 bidRequest := *prebidBidRequest 141 jsonExtCommon, err := json.Marshal(ext) 142 if err != nil { 143 return prebidBidRequest 144 } 145 bidRequest.Imp[0].Ext = jsonExtCommon 146 ext.Prebid.Server = &server{ 147 GvlID: serverParams.GvlID, 148 DataCenter: serverParams.DataCenter, 149 ExternalUrl: serverParams.ExternalUrl, 150 } 151 jsonExt, err := json.Marshal(ext) 152 if err != nil { 153 return &bidRequest 154 } 155 bidRequest.Ext = jsonExt 156 return &bidRequest 157 } 158 159 // MakeBids translates NextMillennium bid response to prebid-server specific format 160 func (adapter *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) { 161 var msg = "" 162 if response.StatusCode == http.StatusNoContent { 163 return nil, nil 164 } 165 if response.StatusCode != http.StatusOK { 166 msg = fmt.Sprintf("Unexpected http status code: %d", response.StatusCode) 167 return nil, []error{&errortypes.BadServerResponse{Message: msg}} 168 169 } 170 171 var bidResp openrtb2.BidResponse 172 if err := json.Unmarshal(response.Body, &bidResp); err != nil { 173 msg = fmt.Sprintf("Bad server response: %d", err) 174 return nil, []error{&errortypes.BadServerResponse{Message: msg}} 175 } 176 177 if len(bidResp.SeatBid) == 0 { 178 return nil, nil 179 } 180 181 bidResponse := adapters.NewBidderResponseWithBidsCapacity(1) 182 var errors []error 183 for _, sb := range bidResp.SeatBid { 184 for i := range sb.Bid { 185 bidType, err := getBidType(sb.Bid[i].MType) 186 if err != nil { 187 errors = append(errors, err) 188 continue 189 } 190 bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{ 191 Bid: &sb.Bid[i], 192 BidType: bidType, 193 }) 194 } 195 } 196 return bidResponse, errors 197 } 198 199 // Builder builds a new instance of the NextMillennium adapter for the given bidder with the given config. 200 func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) { 201 var info nmExtNMM 202 if config.ExtraAdapterInfo != "" { 203 if err := json.Unmarshal([]byte(config.ExtraAdapterInfo), &info); err != nil { 204 return nil, fmt.Errorf("invalid extra info: %v", err) 205 } 206 } 207 208 return &adapter{ 209 endpoint: config.Endpoint, 210 nmmFlags: info.NmmFlags, 211 server: server, 212 }, nil 213 } 214 215 func getBidType(mType openrtb2.MarkupType) (openrtb_ext.BidType, error) { 216 switch mType { 217 case openrtb2.MarkupBanner: 218 return openrtb_ext.BidTypeBanner, nil 219 case openrtb2.MarkupVideo: 220 return openrtb_ext.BidTypeVideo, nil 221 default: 222 return "", &errortypes.BadServerResponse{Message: fmt.Sprintf("Unsupported return mType: %v", mType)} 223 } 224 }