github.com/prebid/prebid-server/v2@v2.18.0/adapters/imds/imds.go (about)

     1  package imds
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"net/url"
     8  	"text/template"
     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/macros"
    15  	"github.com/prebid/prebid-server/v2/openrtb_ext"
    16  )
    17  
    18  const adapterVersion string = "pbs-go/1.0.0"
    19  
    20  type adapter struct {
    21  	EndpointTemplate *template.Template
    22  }
    23  
    24  type SyncEndpointTemplateParams struct {
    25  	SeatId string
    26  	TagId  string
    27  }
    28  
    29  type ReqExt struct {
    30  	SeatId string `json:"seatId"`
    31  }
    32  
    33  // Builder builds a new instance of the Imds adapter for the given bidder with the given config.
    34  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    35  	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
    36  	if err != nil {
    37  		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
    38  	}
    39  
    40  	bidder := &adapter{
    41  		EndpointTemplate: template,
    42  	}
    43  	return bidder, nil
    44  }
    45  
    46  func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    47  	var errs []error
    48  	var bidRequests []*adapters.RequestData
    49  
    50  	adapterReq, errors := a.makeRequest(request)
    51  	if adapterReq != nil {
    52  		bidRequests = append(bidRequests, adapterReq)
    53  	}
    54  	errs = append(errs, errors...)
    55  
    56  	return bidRequests, errs
    57  }
    58  
    59  func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, []error) {
    60  	var errs []error
    61  	var validImps []openrtb2.Imp
    62  	var re *ReqExt
    63  	var firstExtImp *openrtb_ext.ExtImpImds = nil
    64  
    65  	for _, imp := range request.Imp {
    66  		validExtImpObj, err := getExtImpObj(&imp) // getExtImpObj returns {seatId:"", tagId:""}
    67  		if err != nil {
    68  			errs = append(errs, err)
    69  			continue
    70  		}
    71  		// if the bid request is missing seatId or TagId then ignore it
    72  		if validExtImpObj.SeatId == "" || validExtImpObj.TagId == "" {
    73  			errs = append(errs, &errortypes.BadServerResponse{
    74  				Message: fmt.Sprintf("Invalid Impression"),
    75  			})
    76  			continue
    77  		}
    78  		// right here is where we need to take out the tagId and then add it to imp
    79  		imp.TagID = validExtImpObj.TagId
    80  		validImps = append(validImps, imp)
    81  		if firstExtImp == nil {
    82  			firstExtImp = validExtImpObj
    83  		}
    84  	}
    85  
    86  	if len(validImps) == 0 {
    87  		return nil, errs
    88  	}
    89  
    90  	var err error
    91  
    92  	if firstExtImp == nil || firstExtImp.SeatId == "" || firstExtImp.TagId == "" {
    93  		return nil, append(errs, &errortypes.BadServerResponse{
    94  			Message: fmt.Sprintf("Invalid Impression"),
    95  		})
    96  	}
    97  	// this is where the empty seatId is filled
    98  	re = &ReqExt{SeatId: firstExtImp.SeatId}
    99  
   100  	// create JSON Request Body
   101  	request.Imp = validImps
   102  	request.Ext, err = json.Marshal(re)
   103  	if err != nil {
   104  		return nil, append(errs, err)
   105  	}
   106  
   107  	reqJSON, err := json.Marshal(request)
   108  	if err != nil {
   109  		return nil, append(errs, err)
   110  	}
   111  
   112  	// set Request Headers
   113  	headers := http.Header{}
   114  	headers.Add("Content-Type", "application/json;charset=utf-8")
   115  	headers.Add("Accept", "application/json")
   116  
   117  	// create Request Uri
   118  	reqUri, err := a.buildEndpointURL(firstExtImp)
   119  	if err != nil {
   120  		return nil, append(errs, err)
   121  	}
   122  
   123  	return &adapters.RequestData{
   124  		Method:  http.MethodPost,
   125  		Uri:     reqUri,
   126  		Body:    reqJSON,
   127  		Headers: headers,
   128  		ImpIDs:  openrtb_ext.GetImpIDs(request.Imp),
   129  	}, errs
   130  }
   131  
   132  // Builds enpoint url based on adapter-specific pub settings from imp.ext
   133  func (adapter *adapter) buildEndpointURL(params *openrtb_ext.ExtImpImds) (string, error) {
   134  	return macros.ResolveMacros(adapter.EndpointTemplate, macros.EndpointTemplateParams{AccountID: url.QueryEscape(params.SeatId), SourceId: url.QueryEscape(adapterVersion)})
   135  }
   136  
   137  func getExtImpObj(imp *openrtb2.Imp) (*openrtb_ext.ExtImpImds, error) {
   138  	var bidderExt adapters.ExtImpBidder
   139  	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
   140  		return nil, &errortypes.BadInput{
   141  			Message: err.Error(),
   142  		}
   143  	}
   144  
   145  	var imdsExt openrtb_ext.ExtImpImds
   146  	if err := json.Unmarshal(bidderExt.Bidder, &imdsExt); err != nil {
   147  		return nil, &errortypes.BadInput{
   148  			Message: err.Error(),
   149  		}
   150  	}
   151  
   152  	return &imdsExt, nil
   153  }
   154  
   155  // MakeBids make the bids for the bid response.
   156  func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   157  	const errorMessage string = "Unexpected status code: %d. Run with request.debug = 1 for more info"
   158  	switch {
   159  	case response.StatusCode == http.StatusNoContent:
   160  		return nil, nil
   161  	case response.StatusCode == http.StatusBadRequest:
   162  		return nil, []error{&errortypes.BadInput{
   163  			Message: fmt.Sprintf(errorMessage, response.StatusCode),
   164  		}}
   165  	case response.StatusCode != http.StatusOK:
   166  		return nil, []error{&errortypes.BadServerResponse{
   167  			Message: fmt.Sprintf(errorMessage, response.StatusCode),
   168  		}}
   169  	}
   170  
   171  	var bidResp openrtb2.BidResponse
   172  
   173  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   174  		return nil, []error{err}
   175  	}
   176  
   177  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
   178  
   179  	for _, sb := range bidResp.SeatBid {
   180  		for i := range sb.Bid {
   181  			var mediaType = getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp)
   182  			if mediaType != openrtb_ext.BidTypeBanner && mediaType != openrtb_ext.BidTypeVideo {
   183  				continue
   184  			}
   185  			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   186  				Bid:     &sb.Bid[i],
   187  				BidType: mediaType,
   188  			})
   189  		}
   190  	}
   191  	return bidResponse, nil
   192  }
   193  
   194  func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType {
   195  	mediaType := openrtb_ext.BidTypeBanner
   196  	for _, imp := range imps {
   197  		if imp.ID == impId {
   198  			if imp.Banner != nil {
   199  				break
   200  			}
   201  			if imp.Video != nil {
   202  				mediaType = openrtb_ext.BidTypeVideo
   203  				break
   204  			}
   205  			if imp.Native != nil {
   206  				mediaType = openrtb_ext.BidTypeNative
   207  				break
   208  			}
   209  			if imp.Audio != nil {
   210  				mediaType = openrtb_ext.BidTypeAudio
   211  				break
   212  			}
   213  		}
   214  	}
   215  	return mediaType
   216  }