github.com/prebid/prebid-server@v0.275.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/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  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  	}, errs
   135  }
   136  
   137  func getFirstMobileFuseExtension(request *openrtb2.BidRequest) (*openrtb_ext.ExtImpMobileFuse, []error) {
   138  	var mobileFuseImpExtension openrtb_ext.ExtImpMobileFuse
   139  	var errs []error
   140  
   141  	for _, imp := range request.Imp {
   142  		var bidder_imp_extension adapters.ExtImpBidder
   143  
   144  		err := json.Unmarshal(imp.Ext, &bidder_imp_extension)
   145  
   146  		if err != nil {
   147  			errs = append(errs, err)
   148  			continue
   149  		}
   150  
   151  		err = json.Unmarshal(bidder_imp_extension.Bidder, &mobileFuseImpExtension)
   152  
   153  		if err != nil {
   154  			errs = append(errs, err)
   155  			continue
   156  		}
   157  
   158  		break
   159  	}
   160  
   161  	return &mobileFuseImpExtension, errs
   162  }
   163  
   164  func getMobileFuseExtensionForImp(imp *openrtb2.Imp, mobileFuseImpExtension *openrtb_ext.ExtImpMobileFuse) error {
   165  	var bidder_imp_extension adapters.ExtImpBidder
   166  
   167  	err := json.Unmarshal(imp.Ext, &bidder_imp_extension)
   168  	if err != nil {
   169  		return err
   170  	}
   171  
   172  	return json.Unmarshal(bidder_imp_extension.Bidder, &mobileFuseImpExtension)
   173  }
   174  
   175  func (adapter *MobileFuseAdapter) getEndpoint(ext *openrtb_ext.ExtImpMobileFuse) (string, error) {
   176  	publisher_id := strconv.Itoa(ext.PublisherId)
   177  
   178  	url, err := macros.ResolveMacros(adapter.EndpointTemplate, macros.EndpointTemplateParams{PublisherID: publisher_id})
   179  	if err != nil {
   180  		return "", err
   181  	}
   182  
   183  	if ext.TagidSrc == "ext" {
   184  		url += "&tagid_src=ext"
   185  	}
   186  
   187  	return url, nil
   188  }
   189  
   190  func getValidImps(bidRequest *openrtb2.BidRequest, ext *openrtb_ext.ExtImpMobileFuse) ([]openrtb2.Imp, error) {
   191  	var validImps []openrtb2.Imp
   192  
   193  	for _, imp := range bidRequest.Imp {
   194  		if imp.Banner != nil || imp.Video != nil || imp.Native != nil {
   195  			err := getMobileFuseExtensionForImp(&imp, ext)
   196  			if err != nil {
   197  				return nil, err
   198  			}
   199  
   200  			imp.TagID = strconv.Itoa(ext.PlacementId)
   201  
   202  			var extSkadn ExtSkadn
   203  			err = json.Unmarshal(imp.Ext, &extSkadn)
   204  			if err != nil {
   205  				return nil, err
   206  			}
   207  
   208  			if extSkadn.Skadn != nil {
   209  				imp.Ext, err = json.Marshal(map[string]json.RawMessage{"skadn": extSkadn.Skadn})
   210  				if err != nil {
   211  					return nil, err
   212  				}
   213  			} else {
   214  				imp.Ext = nil
   215  			}
   216  
   217  			validImps = append(validImps, imp)
   218  		}
   219  	}
   220  
   221  	if len(validImps) == 0 {
   222  		return nil, fmt.Errorf("No valid imps")
   223  	}
   224  
   225  	return validImps, nil
   226  }
   227  
   228  func getBidType(bid openrtb2.Bid) openrtb_ext.BidType {
   229  	if bid.Ext != nil {
   230  		var bidExt BidExt
   231  		err := json.Unmarshal(bid.Ext, &bidExt)
   232  		if err == nil {
   233  			if bidExt.Mf.MediaType == "video" {
   234  				return openrtb_ext.BidTypeVideo
   235  			} else if bidExt.Mf.MediaType == "native" {
   236  				return openrtb_ext.BidTypeNative
   237  			}
   238  		}
   239  	}
   240  
   241  	return openrtb_ext.BidTypeBanner
   242  }