github.com/prebid/prebid-server/v2@v2.18.0/endpoints/info/bidders_detail.go (about) 1 package info 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "net/http" 7 "strings" 8 9 "github.com/golang/glog" 10 "github.com/julienschmidt/httprouter" 11 "github.com/prebid/prebid-server/v2/config" 12 "github.com/prebid/prebid-server/v2/openrtb_ext" 13 "github.com/prebid/prebid-server/v2/util/jsonutil" 14 ) 15 16 const ( 17 statusActive string = "ACTIVE" 18 statusDisabled string = "DISABLED" 19 ) 20 21 // NewBiddersDetailEndpoint builds a handler for the /info/bidders/<bidder> endpoint. 22 func NewBiddersDetailEndpoint(bidders config.BidderInfos, aliases map[string]string) httprouter.Handle { 23 responses, err := prepareBiddersDetailResponse(bidders, aliases) 24 if err != nil { 25 glog.Fatalf("error creating /info/bidders/<bidder> endpoint response: %v", err) 26 } 27 28 return func(w http.ResponseWriter, _ *http.Request, ps httprouter.Params) { 29 bidder := ps.ByName("bidderName") 30 31 coreBidderName, found := getNormalisedBidderName(bidder, aliases) 32 if !found { 33 w.WriteHeader(http.StatusNotFound) 34 } 35 if response, ok := responses[coreBidderName]; ok { 36 w.Header().Set("Content-Type", "application/json") 37 if _, err := w.Write(response); err != nil { 38 glog.Errorf("error writing response to /info/bidders/%s: %v", bidder, err) 39 } 40 } else { 41 w.WriteHeader(http.StatusNotFound) 42 } 43 } 44 } 45 46 func getNormalisedBidderName(bidderName string, aliases map[string]string) (string, bool) { 47 if strings.ToLower(bidderName) == "all" { 48 return "all", true 49 } 50 coreBidderName, ok := openrtb_ext.NormalizeBidderName(bidderName) 51 if !ok { //check default aliases if not found in coreBidders 52 if _, isDefaultAlias := aliases[bidderName]; isDefaultAlias { 53 return bidderName, true 54 } 55 return "", false 56 } 57 return coreBidderName.String(), true 58 } 59 60 func prepareBiddersDetailResponse(bidders config.BidderInfos, aliases map[string]string) (map[string][]byte, error) { 61 details, err := mapDetails(bidders, aliases) 62 if err != nil { 63 return nil, err 64 } 65 66 responses, err := marshalDetailsResponse(details) 67 if err != nil { 68 return nil, err 69 } 70 71 all, err := marshalAllResponse(responses) 72 if err != nil { 73 return nil, err 74 } 75 responses["all"] = all 76 77 return responses, nil 78 } 79 80 func mapDetails(bidders config.BidderInfos, aliases map[string]string) (map[string]bidderDetail, error) { 81 details := map[string]bidderDetail{} 82 83 for bidderName, bidderInfo := range bidders { 84 details[bidderName] = mapDetailFromConfig(bidderInfo) 85 } 86 87 for aliasName, bidderName := range aliases { 88 aliasBaseInfo, aliasBaseInfoFound := details[bidderName] 89 if !aliasBaseInfoFound { 90 return nil, fmt.Errorf("base adapter %s for alias %s not found", bidderName, aliasName) 91 } 92 93 aliasInfo := aliasBaseInfo 94 aliasInfo.AliasOf = bidderName 95 details[aliasName] = aliasInfo 96 } 97 98 return details, nil 99 } 100 101 func marshalDetailsResponse(details map[string]bidderDetail) (map[string][]byte, error) { 102 responses := map[string][]byte{} 103 104 for bidder, detail := range details { 105 json, err := jsonutil.Marshal(detail) 106 if err != nil { 107 return nil, fmt.Errorf("unable to marshal info for bidder %s: %v", bidder, err) 108 } 109 responses[bidder] = json 110 } 111 112 return responses, nil 113 } 114 115 func marshalAllResponse(responses map[string][]byte) ([]byte, error) { 116 responsesJSON := make(map[string]json.RawMessage, len(responses)) 117 118 for k, v := range responses { 119 responsesJSON[k] = json.RawMessage(v) 120 } 121 122 json, err := jsonutil.Marshal(responsesJSON) 123 if err != nil { 124 return nil, fmt.Errorf("unable to marshal info for bidder all: %v", err) 125 } 126 return json, nil 127 } 128 129 type bidderDetail struct { 130 Status string `json:"status"` 131 UsesHTTPS *bool `json:"usesHttps,omitempty"` 132 Maintainer *maintainer `json:"maintainer,omitempty"` 133 Capabilities *capabilities `json:"capabilities,omitempty"` 134 AliasOf string `json:"aliasOf,omitempty"` 135 } 136 137 type maintainer struct { 138 Email string `json:"email"` 139 } 140 141 type capabilities struct { 142 App *platform `json:"app,omitempty"` 143 Site *platform `json:"site,omitempty"` 144 DOOH *platform `json:"dooh,omitempty"` 145 } 146 147 type platform struct { 148 MediaTypes []string `json:"mediaTypes"` 149 } 150 151 func mapDetailFromConfig(c config.BidderInfo) bidderDetail { 152 var bidderDetail bidderDetail 153 154 if c.Maintainer != nil { 155 bidderDetail.Maintainer = &maintainer{ 156 Email: c.Maintainer.Email, 157 } 158 } 159 160 if c.IsEnabled() { 161 bidderDetail.Status = statusActive 162 163 usesHTTPS := strings.HasPrefix(strings.ToLower(c.Endpoint), "https://") 164 bidderDetail.UsesHTTPS = &usesHTTPS 165 166 if c.Capabilities != nil { 167 bidderDetail.Capabilities = &capabilities{} 168 169 if c.Capabilities.App != nil { 170 bidderDetail.Capabilities.App = &platform{ 171 MediaTypes: mapMediaTypes(c.Capabilities.App.MediaTypes), 172 } 173 } 174 175 if c.Capabilities.Site != nil { 176 bidderDetail.Capabilities.Site = &platform{ 177 MediaTypes: mapMediaTypes(c.Capabilities.Site.MediaTypes), 178 } 179 } 180 181 if c.Capabilities.DOOH != nil { 182 bidderDetail.Capabilities.DOOH = &platform{ 183 MediaTypes: mapMediaTypes(c.Capabilities.DOOH.MediaTypes), 184 } 185 } 186 } 187 } else { 188 bidderDetail.Status = statusDisabled 189 } 190 191 return bidderDetail 192 } 193 194 func mapMediaTypes(m []openrtb_ext.BidType) []string { 195 mediaTypes := make([]string, len(m)) 196 197 for i, v := range m { 198 mediaTypes[i] = string(v) 199 } 200 201 return mediaTypes 202 }