github.com/prebid/prebid-server@v0.275.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/v19/openrtb2"
    11  	"github.com/prebid/prebid-server/adapters"
    12  	"github.com/prebid/prebid-server/config"
    13  	"github.com/prebid/prebid-server/errortypes"
    14  	"github.com/prebid/prebid-server/macros"
    15  	"github.com/prebid/prebid-server/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  	}, errs
   129  }
   130  
   131  // Builds enpoint url based on adapter-specific pub settings from imp.ext
   132  func (adapter *adapter) buildEndpointURL(params *openrtb_ext.ExtImpImds) (string, error) {
   133  	return macros.ResolveMacros(adapter.EndpointTemplate, macros.EndpointTemplateParams{AccountID: url.QueryEscape(params.SeatId), SourceId: url.QueryEscape(adapterVersion)})
   134  }
   135  
   136  func getExtImpObj(imp *openrtb2.Imp) (*openrtb_ext.ExtImpImds, error) {
   137  	var bidderExt adapters.ExtImpBidder
   138  	if err := json.Unmarshal(imp.Ext, &bidderExt); err != nil {
   139  		return nil, &errortypes.BadInput{
   140  			Message: err.Error(),
   141  		}
   142  	}
   143  
   144  	var imdsExt openrtb_ext.ExtImpImds
   145  	if err := json.Unmarshal(bidderExt.Bidder, &imdsExt); err != nil {
   146  		return nil, &errortypes.BadInput{
   147  			Message: err.Error(),
   148  		}
   149  	}
   150  
   151  	return &imdsExt, nil
   152  }
   153  
   154  // MakeBids make the bids for the bid response.
   155  func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   156  	const errorMessage string = "Unexpected status code: %d. Run with request.debug = 1 for more info"
   157  	switch {
   158  	case response.StatusCode == http.StatusNoContent:
   159  		return nil, nil
   160  	case response.StatusCode == http.StatusBadRequest:
   161  		return nil, []error{&errortypes.BadInput{
   162  			Message: fmt.Sprintf(errorMessage, response.StatusCode),
   163  		}}
   164  	case response.StatusCode != http.StatusOK:
   165  		return nil, []error{&errortypes.BadServerResponse{
   166  			Message: fmt.Sprintf(errorMessage, response.StatusCode),
   167  		}}
   168  	}
   169  
   170  	var bidResp openrtb2.BidResponse
   171  
   172  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   173  		return nil, []error{err}
   174  	}
   175  
   176  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
   177  
   178  	for _, sb := range bidResp.SeatBid {
   179  		for i := range sb.Bid {
   180  			var mediaType = getMediaTypeForImp(sb.Bid[i].ImpID, internalRequest.Imp)
   181  			if mediaType != openrtb_ext.BidTypeBanner && mediaType != openrtb_ext.BidTypeVideo {
   182  				continue
   183  			}
   184  			bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   185  				Bid:     &sb.Bid[i],
   186  				BidType: mediaType,
   187  			})
   188  		}
   189  	}
   190  	return bidResponse, nil
   191  }
   192  
   193  func getMediaTypeForImp(impId string, imps []openrtb2.Imp) openrtb_ext.BidType {
   194  	mediaType := openrtb_ext.BidTypeBanner
   195  	for _, imp := range imps {
   196  		if imp.ID == impId {
   197  			if imp.Banner != nil {
   198  				break
   199  			}
   200  			if imp.Video != nil {
   201  				mediaType = openrtb_ext.BidTypeVideo
   202  				break
   203  			}
   204  			if imp.Native != nil {
   205  				mediaType = openrtb_ext.BidTypeNative
   206  				break
   207  			}
   208  			if imp.Audio != nil {
   209  				mediaType = openrtb_ext.BidTypeAudio
   210  				break
   211  			}
   212  		}
   213  	}
   214  	return mediaType
   215  }