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  }