Skip to content

Commit f9529fd

Browse files
Peiling-Dingshunj-nb
authored andcommitted
MSP-change-till-2025-06-24
Load Bidders through Plugins (#50) * Load Bidders through Plugins * Separate core and msp bidders add adapter nova (#51) enforce fb bid floor (#52) fix floor check bug (#53) Add adapters to support facebook multicall (#54) Co-authored-by: Peiling Ding <[email protected]> Add nova multicall (#55) Co-authored-by: Peiling Ding <[email protected]> Load bidder based on so_path (#57) fb dyn config (#60) increase prometheus histogram buckets (#62) Integrating Prometheus Metrics on MSP side (#64) * Integrating Prometheus Metrics on MSP side * Fixing error message --------- Co-authored-by: Muhammad Ashhad Alam <[email protected]@Muhammads-MacBook-Pro.local> add config for scylla access for nova (#70) Signed-off-by: YifengW86 <[email protected]> add fb app security id (#71) mspai-config (#77) implement file db (#76) * implement file and db for prebid-server * move new functions to a new file fix msp analytics add Moloco (#87) fix fb auth id Add Moloco Native For Multicall (#89)
1 parent 6acbc9f commit f9529fd

File tree

22 files changed

+504
-32
lines changed

22 files changed

+504
-32
lines changed

adapters/audienceNetwork/facebook.go

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,15 @@ type facebookAdMarkup struct {
3838
}
3939

4040
type facebookReqExt struct {
41-
PlatformID string `json:"platformid"`
42-
AuthID string `json:"authentication_id"`
41+
PlatformID string `json:"platformid"`
42+
AuthID string `json:"authentication_id"`
43+
SecurityAppID string `json:"security_app_id,omitempty"`
44+
}
45+
46+
type ExtImpFB struct {
47+
AppSecret string `json:"app_secret"`
48+
PlatformID string `json:"platform_id"`
49+
SecurityAppID string `json:"security_app_id"`
4350
}
4451

4552
func (a *adapter) MakeRequests(request *openrtb2.BidRequest, reqInfo *adapters.ExtraRequestInfo) ([]*adapters.RequestData, []error) {
@@ -118,8 +125,9 @@ func (a *adapter) buildRequests(request *openrtb2.BidRequest) ([]*adapters.Reque
118125

119126
// The authentication ID is a sha256 hmac hash encoded as a hex string, based on
120127
// the app secret and the ID of the bid request
121-
func (a *adapter) makeAuthID(req *openrtb2.BidRequest) string {
122-
h := hmac.New(sha256.New, []byte(a.appSecret))
128+
129+
func (a *adapter) makeAuthID(req *openrtb2.BidRequest, appSecret string) string {
130+
h := hmac.New(sha256.New, []byte(appSecret))
123131
h.Write([]byte(req.ID))
124132

125133
return hex.EncodeToString(h.Sum(nil))
@@ -141,9 +149,26 @@ func (a *adapter) modifyRequest(out *openrtb2.BidRequest) error {
141149
// ID *BEFORE* we generate the auth ID since its a hash based on the request ID
142150
out.ID = imp.ID
143151

152+
platformId := a.platformID
153+
appSecret := a.appSecret
154+
securityAppId := ""
155+
156+
var bidderExt adapters.ExtImpBidder
157+
err = json.Unmarshal(imp.Ext, &bidderExt)
158+
if err == nil {
159+
var impressionExt ExtImpFB
160+
err = json.Unmarshal(bidderExt.Bidder, &impressionExt)
161+
if err == nil && len(impressionExt.PlatformID) > 0 && len(impressionExt.AppSecret) > 0 && len(impressionExt.SecurityAppID) > 0 {
162+
platformId = impressionExt.PlatformID
163+
appSecret = impressionExt.AppSecret
164+
securityAppId = impressionExt.SecurityAppID
165+
}
166+
}
167+
144168
reqExt := facebookReqExt{
145-
PlatformID: a.platformID,
146-
AuthID: a.makeAuthID(out),
169+
PlatformID: platformId,
170+
AuthID: a.makeAuthID(out, appSecret),
171+
SecurityAppID: securityAppId,
147172
}
148173

149174
if out.Ext, err = json.Marshal(reqExt); err != nil {
@@ -371,6 +396,18 @@ func (a *adapter) MakeBids(request *openrtb2.BidRequest, adapterRequest *adapter
371396
continue
372397
}
373398

399+
// TODO: move to msp repo and apply for all bidders
400+
var fbreq openrtb2.BidRequest
401+
err := json.Unmarshal(adapterRequest.Body, &fbreq)
402+
if err == nil {
403+
if len(fbreq.Imp) > 0 && fbreq.Imp[0].BidFloor > bid.Price {
404+
errs = append(errs, &errortypes.BadServerResponse{
405+
Message: fmt.Sprintf("bid price %f less than floor %f", bid.Price, fbreq.Imp[0].BidFloor),
406+
})
407+
continue
408+
}
409+
}
410+
374411
bid.AdID = obj.BidID
375412
bid.CrID = obj.BidID
376413

@@ -456,7 +493,13 @@ func (a *adapter) MakeTimeoutNotification(req *adapters.RequestData) (*adapters.
456493
}
457494
}
458495

459-
uri := fmt.Sprintf("https://www.facebook.com/audiencenetwork/nurl/?partner=%s&app=%s&auction=%s&ortb_loss_code=2", a.platformID, pubID, rID)
496+
platformId := a.platformID
497+
requestPlatformId, err := jsonparser.GetString(req.Body, "ext", "platformid")
498+
if err == nil {
499+
platformId = requestPlatformId
500+
}
501+
502+
uri := fmt.Sprintf("https://www.facebook.com/audiencenetwork/nurl/?partner=%s&app=%s&auction=%s&ortb_loss_code=2", platformId, pubID, rID)
460503
timeoutReq := adapters.RequestData{
461504
Method: "GET",
462505
Uri: uri,

analytics/build/build.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@ func New(analytics *config.Analytics) analytics.Runner {
4444
}
4545
}
4646

47+
customAdapters := mspLoadAnalyticsAdapterPlugins(analytics.Custom)
48+
for adapterName, adapter := range customAdapters {
49+
modules[adapterName] = adapter
50+
}
51+
4752
if analytics.Agma.Enabled {
4853
agmaModule, err := agma.NewModule(
4954
clients.GetDefaultHttpInstance(),

analytics/build/msp_plugin.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package build
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/golang/glog"
8+
"github.com/prebid/prebid-server/v3/analytics"
9+
mspPlugin "github.com/prebid/prebid-server/v3/msp/plugin"
10+
)
11+
12+
type PluginBuilder interface {
13+
Build(json.RawMessage) (analytics.Module, error)
14+
}
15+
16+
func mspLoadAnalyticsAdapterPlugins(cfg map[string]interface{}) enabledAnalytics {
17+
plugins := make(enabledAnalytics, 0)
18+
for name, cfgData := range cfg {
19+
builder, cfgJson, skip, err := mspPlugin.LoadBuilder[PluginBuilder](name, cfgData)
20+
21+
if skip {
22+
continue
23+
}
24+
25+
if err != nil {
26+
panic(err)
27+
}
28+
29+
plugin, err := builder.Build(cfgJson)
30+
if err != nil {
31+
panic(fmt.Sprintf("Failed to build Analytics Adapter plugin %s, error: %+v\n", name, err))
32+
} else {
33+
glog.Infof("Loaded Analytics Adapter plugin: %s\n", name)
34+
plugins[name] = plugin
35+
}
36+
}
37+
38+
return plugins
39+
}

config/adapter.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,7 @@ type Adapter struct {
1010
// needed for Facebook
1111
PlatformID string
1212
AppSecret string
13+
14+
// needed for Nova
15+
NovaScylla AdapterNovaScylla
1316
}

config/bidderinfo.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ type BidderInfo struct {
4545
// EndpointCompression determines, if set, the type of compression the bid request will undergo before being sent to the corresponding bid server
4646
EndpointCompression string `yaml:"endpointCompression" mapstructure:"endpointCompression"`
4747
OpenRTB *OpenRTBInfo `yaml:"openrtb" mapstructure:"openrtb"`
48+
49+
// For MSP Plugin extension only.
50+
MspSoPath string `mapstructure:"so_path,omitempty" json:"so_path,omitempty"`
51+
52+
// For Nova Adapter only
53+
NovaScylla AdapterNovaScylla `yaml:"scylla" mapstructure:"scylla"`
4854
}
4955

5056
type aliasNillableFields struct {
@@ -198,6 +204,13 @@ type SyncerEndpoint struct {
198204
UserMacro string `yaml:"userMacro" mapstructure:"user_macro"`
199205
}
200206

207+
// AdapterNovaScylla specifies the the scylla config for Nova Adapter
208+
type AdapterNovaScylla struct {
209+
Cluster string `yaml:"cluster" mapstructure:"cluster"`
210+
KeySpace string `yaml:"keyspace" mapstructure:"keyspace"`
211+
TimeOut int32 `yaml:"query_timeout" mapstructure:"query_timeout"`
212+
}
213+
201214
func (bi BidderInfo) IsEnabled() bool {
202215
return !bi.Disabled
203216
}
@@ -607,6 +620,7 @@ func applyBidderInfoConfigOverrides(configBidderInfos nillableFieldBidderInfos,
607620
if !exists {
608621
return nil, fmt.Errorf("error setting configuration for bidder %s: unknown bidder", bidderName)
609622
}
623+
610624
fsBidderInfo, exists := fsBidderInfos[string(normalizedBidderName)]
611625
if !exists {
612626
return nil, fmt.Errorf("error finding configuration for bidder %s: unknown bidder", bidderName)
@@ -659,6 +673,19 @@ func applyBidderInfoConfigOverrides(configBidderInfos nillableFieldBidderInfos,
659673
if configBidderInfo.bidderInfo.EndpointCompression != "" {
660674
mergedBidderInfo.EndpointCompression = configBidderInfo.bidderInfo.EndpointCompression
661675
}
676+
if configBidderInfo.bidderInfo.MspSoPath != "" {
677+
mergedBidderInfo.MspSoPath = configBidderInfo.bidderInfo.MspSoPath
678+
}
679+
if configBidderInfo.bidderInfo.NovaScylla.Cluster != "" {
680+
mergedBidderInfo.NovaScylla.Cluster = configBidderInfo.bidderInfo.NovaScylla.Cluster
681+
}
682+
if configBidderInfo.bidderInfo.NovaScylla.KeySpace != "" {
683+
mergedBidderInfo.NovaScylla.KeySpace = configBidderInfo.bidderInfo.NovaScylla.KeySpace
684+
}
685+
if configBidderInfo.bidderInfo.NovaScylla.TimeOut != 0 {
686+
mergedBidderInfo.NovaScylla.TimeOut = configBidderInfo.bidderInfo.NovaScylla.TimeOut
687+
}
688+
662689
if configBidderInfo.bidderInfo.OpenRTB != nil {
663690
mergedBidderInfo.OpenRTB = configBidderInfo.bidderInfo.OpenRTB
664691
}

config/config.go

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -36,21 +36,22 @@ type Configuration struct {
3636
GarbageCollectorThreshold int `mapstructure:"garbage_collector_threshold"`
3737
// StatusResponse is the string which will be returned by the /status endpoint when things are OK.
3838
// If empty, it will return a 204 with no content.
39-
StatusResponse string `mapstructure:"status_response"`
40-
AuctionTimeouts AuctionTimeouts `mapstructure:"auction_timeouts_ms"`
41-
TmaxAdjustments TmaxAdjustments `mapstructure:"tmax_adjustments"`
42-
CacheURL Cache `mapstructure:"cache"`
43-
ExtCacheURL ExternalCache `mapstructure:"external_cache"`
44-
RecaptchaSecret string `mapstructure:"recaptcha_secret"`
45-
HostCookie HostCookie `mapstructure:"host_cookie"`
46-
Metrics Metrics `mapstructure:"metrics"`
47-
StoredRequests StoredRequests `mapstructure:"stored_requests"`
48-
StoredRequestsAMP StoredRequests `mapstructure:"stored_amp_req"`
49-
CategoryMapping StoredRequests `mapstructure:"category_mapping"`
50-
VTrack VTrack `mapstructure:"vtrack"`
51-
Event Event `mapstructure:"event"`
52-
Accounts StoredRequests `mapstructure:"accounts"`
53-
UserSync UserSync `mapstructure:"user_sync"`
39+
StatusResponse string `mapstructure:"status_response"`
40+
AuctionTimeouts AuctionTimeouts `mapstructure:"auction_timeouts_ms"`
41+
TmaxAdjustments TmaxAdjustments `mapstructure:"tmax_adjustments"`
42+
CacheURL Cache `mapstructure:"cache"`
43+
ExtCacheURL ExternalCache `mapstructure:"external_cache"`
44+
RecaptchaSecret string `mapstructure:"recaptcha_secret"`
45+
HostCookie HostCookie `mapstructure:"host_cookie"`
46+
Metrics Metrics `mapstructure:"metrics"`
47+
MSPMetricsConfig MSPMetricsConfig `mapstructure:"msp_metrics"`
48+
StoredRequests StoredRequests `mapstructure:"stored_requests"`
49+
StoredRequestsAMP StoredRequests `mapstructure:"stored_amp_req"`
50+
CategoryMapping StoredRequests `mapstructure:"category_mapping"`
51+
VTrack VTrack `mapstructure:"vtrack"`
52+
Event Event `mapstructure:"event"`
53+
Accounts StoredRequests `mapstructure:"accounts"`
54+
UserSync UserSync `mapstructure:"user_sync"`
5455
// Note that StoredVideo refers to stored video requests, and has nothing to do with caching video creatives.
5556
StoredVideo StoredRequests `mapstructure:"stored_video_req"`
5657
StoredResponses StoredRequests `mapstructure:"stored_responses"`
@@ -454,6 +455,10 @@ type Analytics struct {
454455
File FileLogs `mapstructure:"file"`
455456
Agma AgmaAnalytics `mapstructure:"agma"`
456457
Pubstack Pubstack `mapstructure:"pubstack"`
458+
459+
// Made by MSP. This field is for loading analytics adapter
460+
// dynamically through Golang Plugin at runtime
461+
Custom map[string]interface{} `mapstructure:"custom"`
457462
}
458463

459464
type CurrencyConverter struct {
@@ -550,6 +555,12 @@ type Metrics struct {
550555
Disabled DisabledMetrics `mapstructure:"disabled_metrics"`
551556
}
552557

558+
type MSPMetricsConfig struct {
559+
Enabled bool `mapstructure:"enabled"`
560+
Port int `mapstructure:"port"`
561+
SoPath string `mapstructure:"so_path"`
562+
}
563+
553564
type DisabledMetrics struct {
554565
// True if we want to stop collecting account-to-adapter metrics
555566
AccountAdapterDetails bool `mapstructure:"account_adapter_details"`

endpoints/openrtb2/auction.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,6 +1647,20 @@ func getJsonSyntaxError(testJSON []byte) (bool, string) {
16471647
}
16481648

16491649
func (deps *endpointDeps) getStoredRequests(ctx context.Context, requestJson []byte, impInfo []ImpExtPrebidData) (string, bool, map[string]json.RawMessage, map[string]json.RawMessage, []error) {
1650+
if isDbFetched(requestJson) {
1651+
dbReqMap, dbImpMap, parseErrs := parseDbStoredMaps(requestJson)
1652+
if len(parseErrs) > 0 {
1653+
return "", false, nil, nil, parseErrs
1654+
}
1655+
1656+
// same parameters as normal
1657+
storedBidRequestId, hasStoredBidRequest, err := getStoredRequestId(requestJson)
1658+
if err != nil {
1659+
return "", false, nil, nil, []error{err}
1660+
}
1661+
1662+
return storedBidRequestId, hasStoredBidRequest, dbReqMap, dbImpMap, nil
1663+
}
16501664
// Parse the Stored Request IDs from the BidRequest and Imps.
16511665
storedBidRequestId, hasStoredBidRequest, err := getStoredRequestId(requestJson)
16521666
if err != nil {

endpoints/openrtb2/db_helper.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package openrtb2
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
7+
"github.com/buger/jsonparser"
8+
)
9+
10+
func isDbFetched(requestJson []byte) bool {
11+
val, dataType, _, err := jsonparser.Get(requestJson, "ext", "db_fetched")
12+
if err != nil {
13+
return false
14+
}
15+
if dataType == jsonparser.Boolean && string(val) == "true" {
16+
return true
17+
}
18+
return false
19+
}
20+
21+
func parseDbStoredMaps(requestJson []byte) (map[string]json.RawMessage, map[string]json.RawMessage, []error) {
22+
var errs []error
23+
storedReqMap := make(map[string]json.RawMessage)
24+
storedImpMap := make(map[string]json.RawMessage)
25+
26+
rawReqs, dt, _, err := jsonparser.Get(requestJson, "ext", "db_storedrequests")
27+
if err == nil && dt == jsonparser.Object {
28+
if e := json.Unmarshal(rawReqs, &storedReqMap); e != nil {
29+
errs = append(errs, fmt.Errorf("failed to unmarshal db_storedrequests: %v", e))
30+
}
31+
}
32+
33+
rawImps, dt2, _, err2 := jsonparser.Get(requestJson, "ext", "db_storedimps")
34+
if err2 == nil && dt2 == jsonparser.Object {
35+
if e := json.Unmarshal(rawImps, &storedImpMap); e != nil {
36+
errs = append(errs, fmt.Errorf("failed to unmarshal db_storedimps: %v", e))
37+
}
38+
}
39+
40+
return storedReqMap, storedImpMap, errs
41+
}

exchange/adapter_util.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ import (
1212

1313
func BuildAdapters(client *http.Client, cfg *config.Configuration, infos config.BidderInfos, me metrics.MetricsEngine) (map[openrtb_ext.BidderName]AdaptedBidder, map[openrtb_ext.BidderName]struct{}, []error) {
1414
server := config.Server{ExternalUrl: cfg.ExternalURL, GvlID: cfg.GDPR.HostVendorID, DataCenter: cfg.DataCenter}
15-
bidders, singleFormatBidders, errs := buildBidders(infos, newAdapterBuilders(), server)
15+
adapterBuilders := mspAddAdaptersFromPlugins(newAdapterBuilders(), infos)
16+
bidders, singleFormatBidders, errs := buildBidders(infos, adapterBuilders, server)
1617

1718
if len(errs) > 0 {
1819
return nil, nil, errs
@@ -91,6 +92,7 @@ func buildAdapterInfo(bidderInfo config.BidderInfo) config.Adapter {
9192
adapter.PlatformID = bidderInfo.PlatformID
9293
adapter.AppSecret = bidderInfo.AppSecret
9394
adapter.XAPI = bidderInfo.XAPI
95+
adapter.NovaScylla = bidderInfo.NovaScylla
9496
return adapter
9597
}
9698

exchange/msp_bidders.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package exchange
2+
3+
import (
4+
"github.com/golang/glog"
5+
"github.com/prebid/prebid-server/v3/adapters"
6+
"github.com/prebid/prebid-server/v3/config"
7+
"github.com/prebid/prebid-server/v3/openrtb_ext"
8+
9+
mspPlugin "github.com/prebid/prebid-server/v3/msp/plugin"
10+
)
11+
12+
type PluginBuilder interface {
13+
Build(openrtb_ext.BidderName, config.Adapter, config.Server) (adapters.Bidder, error)
14+
}
15+
16+
func mspLoadBidderAdapterPlugins(cfg config.BidderInfos) map[openrtb_ext.BidderName]adapters.Builder {
17+
plugins := make(map[openrtb_ext.BidderName]adapters.Builder)
18+
19+
for name, bidderInfo := range cfg {
20+
if bidderInfo.MspSoPath != "" {
21+
builder, err := mspPlugin.LoadBuilderFromPath[PluginBuilder](name, bidderInfo.MspSoPath)
22+
23+
if err != nil {
24+
panic(err)
25+
}
26+
27+
bidderName := openrtb_ext.BidderName(name)
28+
plugins[bidderName] = builder.Build
29+
glog.Infof("Loaded Bidder Adapter plugin: %s\n", name)
30+
}
31+
}
32+
33+
return plugins
34+
}
35+
36+
func mspAddAdaptersFromPlugins(adapters map[openrtb_ext.BidderName]adapters.Builder, cfg config.BidderInfos) map[openrtb_ext.BidderName]adapters.Builder {
37+
pluginAdatpers := mspLoadBidderAdapterPlugins(cfg)
38+
for key, val := range pluginAdatpers {
39+
adapters[key] = val
40+
}
41+
42+
return adapters
43+
}

0 commit comments

Comments
 (0)