github.com/prebid/prebid-server@v0.275.0/adapters/algorix/algorix.go (about)

     1  package algorix
     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  type adapter struct {
    19  	EndpointTemplate *template.Template
    20  }
    21  
    22  type algorixVideoExt struct {
    23  	Rewarded int `json:"rewarded"`
    24  }
    25  
    26  type algorixResponseBidExt struct {
    27  	MediaType string `json:"mediaType"`
    28  }
    29  
    30  // Builder builds a new instance of the AlgoriX adapter for the given bidder with the given config.
    31  func Builder(bidderName openrtb_ext.BidderName, config config.Adapter, server config.Server) (adapters.Bidder, error) {
    32  	endpoint, err := template.New("endpointTemplate").Parse(config.Endpoint)
    33  	if err != nil {
    34  		return nil, fmt.Errorf("unable to parse endpoint url template: %v", err)
    35  	}
    36  	bidder := &adapter{
    37  		EndpointTemplate: endpoint,
    38  	}
    39  	return bidder, nil
    40  }
    41  
    42  func (a *adapter) MakeRequests(request *openrtb2.BidRequest, requestInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
    43  	var adapterRequests []*adapters.RequestData
    44  	var errs []error
    45  
    46  	adapterRequest, err := a.makeRequest(request)
    47  	if err == nil {
    48  		adapterRequests = append(adapterRequests, adapterRequest)
    49  	} else {
    50  		errs = append(errs, err)
    51  	}
    52  	return adapterRequests, errs
    53  }
    54  
    55  func (a *adapter) makeRequest(request *openrtb2.BidRequest) (*adapters.RequestData, error) {
    56  	algorixExt, err := getImpAlgoriXExt(&request.Imp[0])
    57  
    58  	if err != nil {
    59  		return nil, &errortypes.BadInput{Message: "Invalid ExtImpAlgoriX value"}
    60  	}
    61  
    62  	endPoint, err := a.getEndPoint(algorixExt)
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	preProcess(request)
    68  	reqBody, err := json.Marshal(request)
    69  	if err != nil {
    70  		return nil, err
    71  	}
    72  
    73  	headers := http.Header{}
    74  	headers.Add("Content-Type", "application/json;charset=utf-8")
    75  	headers.Add("Accept", "application/json")
    76  	headers.Add("x-openrtb-version", "2.5")
    77  
    78  	return &adapters.RequestData{
    79  		Method:  "POST",
    80  		Uri:     endPoint,
    81  		Body:    reqBody,
    82  		Headers: headers,
    83  	}, nil
    84  }
    85  
    86  // get ImpAlgoriXExt From First Imp. Only check and get first Imp.Ext.Bidder to ExtImpAlgorix
    87  func getImpAlgoriXExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpAlgorix, error) {
    88  	var extImpAlgoriX openrtb_ext.ExtImpAlgorix
    89  	var extBidder adapters.ExtImpBidder
    90  	err := json.Unmarshal(imp.Ext, &extBidder)
    91  	if err != nil {
    92  		return nil, err
    93  	}
    94  	err = json.Unmarshal(extBidder.Bidder, &extImpAlgoriX)
    95  	if err != nil {
    96  		return nil, err
    97  	}
    98  	return &extImpAlgoriX, nil
    99  }
   100  
   101  func getRegionInfo(region string) string {
   102  	switch region {
   103  	case "APAC":
   104  		return "apac.xyz"
   105  	case "USE":
   106  		return "use.xyz"
   107  	case "EUC":
   108  		return "euc.xyz"
   109  	default:
   110  		return "xyz"
   111  	}
   112  }
   113  
   114  func (a *adapter) getEndPoint(ext *openrtb_ext.ExtImpAlgorix) (string, error) {
   115  	endPointParams := macros.EndpointTemplateParams{
   116  		SourceId:  url.PathEscape(ext.Sid),
   117  		AccountID: url.PathEscape(ext.Token),
   118  		Host:      url.PathEscape(getRegionInfo(ext.Region)),
   119  	}
   120  	return macros.ResolveMacros(a.EndpointTemplate, endPointParams)
   121  }
   122  
   123  func preProcess(request *openrtb2.BidRequest) {
   124  	for i := range request.Imp {
   125  		if request.Imp[i].Banner != nil {
   126  			banner := *request.Imp[i].Banner
   127  			if (banner.W == nil || banner.H == nil || *banner.W == 0 || *banner.H == 0) && len(banner.Format) > 0 {
   128  				firstFormat := banner.Format[0]
   129  				banner.W = &firstFormat.W
   130  				banner.H = &firstFormat.H
   131  				request.Imp[i].Banner = &banner
   132  			}
   133  		}
   134  		if request.Imp[i].Video != nil {
   135  			var impExt adapters.ExtImpBidder
   136  			err := json.Unmarshal(request.Imp[i].Ext, &impExt)
   137  			if err != nil {
   138  				continue
   139  			}
   140  			if impExt.Prebid != nil && impExt.Prebid.IsRewardedInventory != nil && *impExt.Prebid.IsRewardedInventory == 1 {
   141  				videoCopy := *request.Imp[i].Video
   142  				videoExt := algorixVideoExt{Rewarded: 1}
   143  				videoCopy.Ext, err = json.Marshal(&videoExt)
   144  				request.Imp[i].Video = &videoCopy
   145  			}
   146  		}
   147  	}
   148  }
   149  
   150  func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   151  	if response.StatusCode == http.StatusNoContent {
   152  		return nil, nil
   153  	}
   154  
   155  	if response.StatusCode == http.StatusBadRequest {
   156  		return nil, []error{&errortypes.BadInput{
   157  			Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode),
   158  		}}
   159  	}
   160  
   161  	if response.StatusCode != http.StatusOK {
   162  		return nil, []error{&errortypes.BadServerResponse{
   163  			Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode),
   164  		}}
   165  	}
   166  
   167  	var bidResp openrtb2.BidResponse
   168  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   169  		return nil, []error{err}
   170  	}
   171  
   172  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
   173  	var errs []error
   174  
   175  	for _, seatBid := range bidResp.SeatBid {
   176  		for idx := range seatBid.Bid {
   177  			mediaType, err := getBidType(seatBid.Bid[idx], internalRequest.Imp)
   178  			if err != nil {
   179  				errs = append(errs, err)
   180  			} else {
   181  				bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   182  					Bid:     &seatBid.Bid[idx],
   183  					BidType: mediaType,
   184  				})
   185  			}
   186  		}
   187  	}
   188  
   189  	return bidResponse, errs
   190  }
   191  
   192  func getBidType(bid openrtb2.Bid, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
   193  	var bidExt algorixResponseBidExt
   194  	err := json.Unmarshal(bid.Ext, &bidExt)
   195  	if err == nil {
   196  		switch bidExt.MediaType {
   197  		case "banner":
   198  			return openrtb_ext.BidTypeBanner, nil
   199  		case "native":
   200  			return openrtb_ext.BidTypeNative, nil
   201  		case "video":
   202  			return openrtb_ext.BidTypeVideo, nil
   203  		}
   204  	}
   205  	var mediaType openrtb_ext.BidType
   206  	var typeCnt = 0
   207  	for _, imp := range imps {
   208  		if imp.ID == bid.ImpID {
   209  			if imp.Banner != nil {
   210  				typeCnt += 1
   211  				mediaType = openrtb_ext.BidTypeBanner
   212  			}
   213  			if imp.Native != nil {
   214  				typeCnt += 1
   215  				mediaType = openrtb_ext.BidTypeNative
   216  			}
   217  			if imp.Video != nil {
   218  				typeCnt += 1
   219  				mediaType = openrtb_ext.BidTypeVideo
   220  			}
   221  		}
   222  	}
   223  	if typeCnt == 1 {
   224  		return mediaType, nil
   225  	}
   226  	return mediaType, fmt.Errorf("unable to fetch mediaType in multi-format: %s", bid.ImpID)
   227  }