github.com/prebid/prebid-server/v2@v2.18.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/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 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  		ImpIDs:  openrtb_ext.GetImpIDs(request.Imp),
    84  	}, nil
    85  }
    86  
    87  // get ImpAlgoriXExt From First Imp. Only check and get first Imp.Ext.Bidder to ExtImpAlgorix
    88  func getImpAlgoriXExt(imp *openrtb2.Imp) (*openrtb_ext.ExtImpAlgorix, error) {
    89  	var extImpAlgoriX openrtb_ext.ExtImpAlgorix
    90  	var extBidder adapters.ExtImpBidder
    91  	err := json.Unmarshal(imp.Ext, &extBidder)
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	err = json.Unmarshal(extBidder.Bidder, &extImpAlgoriX)
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  	return &extImpAlgoriX, nil
   100  }
   101  
   102  func getRegionInfo(region string) string {
   103  	switch region {
   104  	case "APAC":
   105  		return "apac.xyz"
   106  	case "USE":
   107  		return "use.xyz"
   108  	case "EUC":
   109  		return "euc.xyz"
   110  	default:
   111  		return "xyz"
   112  	}
   113  }
   114  
   115  func (a *adapter) getEndPoint(ext *openrtb_ext.ExtImpAlgorix) (string, error) {
   116  	endPointParams := macros.EndpointTemplateParams{
   117  		SourceId:  url.PathEscape(ext.Sid),
   118  		AccountID: url.PathEscape(ext.Token),
   119  		Host:      url.PathEscape(getRegionInfo(ext.Region)),
   120  	}
   121  	return macros.ResolveMacros(a.EndpointTemplate, endPointParams)
   122  }
   123  
   124  func preProcess(request *openrtb2.BidRequest) {
   125  	for i := range request.Imp {
   126  		if request.Imp[i].Banner != nil {
   127  			banner := *request.Imp[i].Banner
   128  			if (banner.W == nil || banner.H == nil || *banner.W == 0 || *banner.H == 0) && len(banner.Format) > 0 {
   129  				firstFormat := banner.Format[0]
   130  				banner.W = &firstFormat.W
   131  				banner.H = &firstFormat.H
   132  				request.Imp[i].Banner = &banner
   133  			}
   134  		}
   135  		if request.Imp[i].Video != nil {
   136  			var impExt adapters.ExtImpBidder
   137  			err := json.Unmarshal(request.Imp[i].Ext, &impExt)
   138  			if err != nil {
   139  				continue
   140  			}
   141  			if impExt.Prebid != nil && impExt.Prebid.IsRewardedInventory != nil && *impExt.Prebid.IsRewardedInventory == 1 {
   142  				videoCopy := *request.Imp[i].Video
   143  				videoExt := algorixVideoExt{Rewarded: 1}
   144  				videoCopy.Ext, err = json.Marshal(&videoExt)
   145  				request.Imp[i].Video = &videoCopy
   146  			}
   147  		}
   148  	}
   149  }
   150  
   151  func (a *adapter) MakeBids(internalRequest *openrtb2.BidRequest, externalRequest *adapters.RequestData, response *adapters.ResponseData) (*adapters.BidderResponse, []error) {
   152  	if response.StatusCode == http.StatusNoContent {
   153  		return nil, nil
   154  	}
   155  
   156  	if response.StatusCode == http.StatusBadRequest {
   157  		return nil, []error{&errortypes.BadInput{
   158  			Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode),
   159  		}}
   160  	}
   161  
   162  	if response.StatusCode != http.StatusOK {
   163  		return nil, []error{&errortypes.BadServerResponse{
   164  			Message: fmt.Sprintf("Unexpected status code: %d.", response.StatusCode),
   165  		}}
   166  	}
   167  
   168  	var bidResp openrtb2.BidResponse
   169  	if err := json.Unmarshal(response.Body, &bidResp); err != nil {
   170  		return nil, []error{err}
   171  	}
   172  
   173  	bidResponse := adapters.NewBidderResponseWithBidsCapacity(1)
   174  	var errs []error
   175  
   176  	for _, seatBid := range bidResp.SeatBid {
   177  		for idx := range seatBid.Bid {
   178  			mediaType, err := getBidType(seatBid.Bid[idx], internalRequest.Imp)
   179  			if err != nil {
   180  				errs = append(errs, err)
   181  			} else {
   182  				bidResponse.Bids = append(bidResponse.Bids, &adapters.TypedBid{
   183  					Bid:     &seatBid.Bid[idx],
   184  					BidType: mediaType,
   185  				})
   186  			}
   187  		}
   188  	}
   189  
   190  	return bidResponse, errs
   191  }
   192  
   193  func getBidType(bid openrtb2.Bid, imps []openrtb2.Imp) (openrtb_ext.BidType, error) {
   194  	var bidExt algorixResponseBidExt
   195  	err := json.Unmarshal(bid.Ext, &bidExt)
   196  	if err == nil {
   197  		switch bidExt.MediaType {
   198  		case "banner":
   199  			return openrtb_ext.BidTypeBanner, nil
   200  		case "native":
   201  			return openrtb_ext.BidTypeNative, nil
   202  		case "video":
   203  			return openrtb_ext.BidTypeVideo, nil
   204  		}
   205  	}
   206  	var mediaType openrtb_ext.BidType
   207  	var typeCnt = 0
   208  	for _, imp := range imps {
   209  		if imp.ID == bid.ImpID {
   210  			if imp.Banner != nil {
   211  				typeCnt += 1
   212  				mediaType = openrtb_ext.BidTypeBanner
   213  			}
   214  			if imp.Native != nil {
   215  				typeCnt += 1
   216  				mediaType = openrtb_ext.BidTypeNative
   217  			}
   218  			if imp.Video != nil {
   219  				typeCnt += 1
   220  				mediaType = openrtb_ext.BidTypeVideo
   221  			}
   222  		}
   223  	}
   224  	if typeCnt == 1 {
   225  		return mediaType, nil
   226  	}
   227  	return mediaType, fmt.Errorf("unable to fetch mediaType in multi-format: %s", bid.ImpID)
   228  }