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

     1  package mobilefuse
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  	"strconv"
     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  type MobileFuseAdapter struct {
    19  	EndpointTemplate *template.Template
    20  }
    21  
    22  type ExtMf struct {
    23  	MediaType string `json:"media_type"`
    24  }
    25  
    26  type BidExt struct {
    27  	Mf ExtMf `json:"mf"`
    28  }
    29  
    30  type ExtSkadn struct {
    31  	Skadn json.RawMessage `json:"skadn"`
    32  }
    33  
    34  // Builder builds a new instance of the MobileFuse adapter for the given bidder with the given config.
    35  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    36  	template, err := template.New("endpointTemplate").Parse(config.Endpoint)
    37  	if err != nil {
    38  		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
    39  	}
    40  
    41  	bidder := &MobileFuseAdapter{
    42  		EndpointTemplate: template,
    43  	}
    44  	return bidder, nil
    45  }
    46  
    47  func (adapter *MobileFuseAdapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    48  	var adapterRequests []*adapters.RequestData
    49  
    50  	adapterRequest, errs := adapter.makeRequest(request)
    51  
    52  	if errs == nil {
    53  		adapterRequests = append(adapterRequests, adapterRequest)
    54  	}
    55  
    56  	return adapterRequests, errs
    57  }
    58  
    59  func (adapter *MobileFuseAdapter) MakeBids(incomingRequest *openrtb2.BidRequest, outgoingRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
    60  	if response.StatusCode == http.StatusNoContent {
    61  		return nil, nil
    62  	}
    63  
    64  	if response.StatusCode == http.StatusBadRequest {
    65  		return nil, []error{&errortypes.BadInput{
    66  			Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode),
    67  		}}
    68  	}
    69  
    70  	if response.StatusCode != http.StatusOK {
    71  		return nil, []error{&errortypes.BadServerResponse{
    72  			Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode),
    73  		}}
    74  	}
    75  
    76  	var incomingBidResponse openrtb2.BidResponse
    77  
    78  	if err := json.Unmarshal(response.Body, &incomingBidResponse); err != nil {
    79  		return nil, []error{err}
    80  	}
    81  
    82  	outgoingBidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
    83  
    84  	for _, seatbid := range incomingBidResponse.SeatBid {
    85  		for i := range seatbid.Bid {
    86  			bidType := getBidType(seatbid.Bid[i])
    87  			seatbid.Bid[i].Ext = nil
    88  
    89  			outgoingBidResponse.Bids = append(outgoingBidResponse.Bids, &adapters.TypedBid{
    90  				Bid:     &seatbid.Bid[i],
    91  				BidType: bidType,
    92  			})
    93  		}
    94  	}
    95  
    96  	return outgoingBidResponse, nil
    97  }
    98  
    99  func (adapter *MobileFuseAdapter) makeRequest(bidRequest *openrtb2.BidRequest) (*adapters.RequestData, []error) {
   100  	var errs []error
   101  
   102  	mobileFuseExtension, errs := getFirstMobileFuseExtension(bidRequest)
   103  	if errs != nil {
   104  		return nil, errs
   105  	}
   106  
   107  	endpoint, err := adapter.getEndpoint(mobileFuseExtension)
   108  	if err != nil {
   109  		return nil, append(errs, err)
   110  	}
   111  
   112  	validImps, err := getValidImps(bidRequest, mobileFuseExtension)
   113  	if err != nil {
   114  		errs = append(errs, err)
   115  		return nil, errs
   116  	}
   117  
   118  	mobileFuseBidRequest := *bidRequest
   119  	mobileFuseBidRequest.Imp = validImps
   120  	body, err := json.Marshal(mobileFuseBidRequest)
   121  	if err != nil {
   122  		return nil, append(errs, err)
   123  	}
   124  
   125  	headers := http.Header{}
   126  	headers.Add("Content-Type", "application/json;charset=utf-8")
   127  	headers.Add("Accept", "application/json")
   128  
   129  	return &adapters.RequestData{
   130  		Method:  "POST",
   131  		Uri:     endpoint,
   132  		Body:    body,
   133  		Headers: headers,
   134  		ImpIDs:  openrtb_ext.GetImpIDs(mobileFuseBidRequest.Imp),
   135  	}, errs
   136  }
   137  
   138  func getFirstMobileFuseExtension(request *openrtb2.BidRequest) (*openrtb_ext.ExtImpMobileFuse, []error) {
   139  	var mobileFuseImpExtension openrtb_ext.ExtImpMobileFuse
   140  	var errs []error
   141  
   142  	for _, imp := range request.Imp {
   143  		var bidder_imp_extension adapters.ExtImpBidder
   144  
   145  		err := json.Unmarshal(imp.Ext, &bidder_imp_extension)
   146  
   147  		if err != nil {
   148  			errs = append(errs, err)
   149  			continue
   150  		}
   151  
   152  		err = json.Unmarshal(bidder_imp_extension.Bidder, &mobileFuseImpExtension)
   153  
   154  		if err != nil {
   155  			errs = append(errs, err)
   156  			continue
   157  		}
   158  
   159  		break
   160  	}
   161  
   162  	return &mobileFuseImpExtension, errs
   163  }
   164  
   165  func getMobileFuseExtensionForImp(imp *openrtb2.Imp, mobileFuseImpExtension *openrtb_ext.ExtImpMobileFuse) error {
   166  	var bidder_imp_extension adapters.ExtImpBidder
   167  
   168  	err := json.Unmarshal(imp.Ext, &bidder_imp_extension)
   169  	if err != nil {
   170  		return err
   171  	}
   172  
   173  	return json.Unmarshal(bidder_imp_extension.Bidder, &mobileFuseImpExtension)
   174  }
   175  
   176  func (adapter *MobileFuseAdapter) getEndpoint(ext *openrtb_ext.ExtImpMobileFuse) (string, error) {
   177  	publisher_id := strconv.Itoa(ext.PublisherId)
   178  
   179  	url, err := macros.ResolveMacros(adapter.EndpointTemplate, macros.EndpointTemplateParams{PublisherID: publisher_id})
   180  	if err != nil {
   181  		return "", err
   182  	}
   183  
   184  	if ext.TagidSrc == "ext" {
   185  		url += "&tagid_src=ext"
   186  	}
   187  
   188  	return url, nil
   189  }
   190  
   191  func getValidImps(bidRequest *openrtb2.BidRequest, ext *openrtb_ext.ExtImpMobileFuse) ([]openrtb2.Imp, error) {
   192  	var validImps []openrtb2.Imp
   193  
   194  	for _, imp := range bidRequest.Imp {
   195  		if imp.Banner != nil || imp.Video != nil || imp.Native != nil {
   196  			err := getMobileFuseExtensionForImp(&imp, ext)
   197  			if err != nil {
   198  				return nil, err
   199  			}
   200  
   201  			imp.TagID = strconv.Itoa(ext.PlacementId)
   202  
   203  			var extSkadn ExtSkadn
   204  			err = json.Unmarshal(imp.Ext, &extSkadn)
   205  			if err != nil {
   206  				return nil, err
   207  			}
   208  
   209  			if extSkadn.Skadn != nil {
   210  				imp.Ext, err = json.Marshal(map[string]json.RawMessage{"skadn": extSkadn.Skadn})
   211  				if err != nil {
   212  					return nil, err
   213  				}
   214  			} else {
   215  				imp.Ext = nil
   216  			}
   217  
   218  			validImps = append(validImps, imp)
   219  		}
   220  	}
   221  
   222  	if len(validImps) == 0 {
   223  		return nil, fmt.Errorf("No valid imps")
   224  	}
   225  
   226  	return validImps, nil
   227  }
   228  
   229  func getBidType(bid openrtb2.Bid) openrtb_ext.BidType {
   230  	if bid.Ext != nil {
   231  		var bidExt BidExt
   232  		err := json.Unmarshal(bid.Ext, &bidExt)
   233  		if err == nil {
   234  			if bidExt.Mf.MediaType == "video" {
   235  				return openrtb_ext.BidTypeVideo
   236  			} else if bidExt.Mf.MediaType == "native" {
   237  				return openrtb_ext.BidTypeNative
   238  			}
   239  		}
   240  	}
   241  
   242  	return openrtb_ext.BidTypeBanner
   243  }