diff --git a/config/bidderinfo.go b/config/bidderinfo.go index b4d7900196e..630952a6884 100644 --- a/config/bidderinfo.go +++ b/config/bidderinfo.go @@ -162,7 +162,7 @@ type SyncerEndpoint struct { // startup: // // {{.ExternalURL}} - This will be replaced with the host server's externally reachable http path. - // {{.SyncerKey}} - This will be replaced with the syncer key. + // {{.BidderName}} - This will be replaced with the bidder name. // {{.SyncType}} - This will be replaced with the sync type, either 'b' for iframe syncs or 'i' // for redirect/image syncs. // {{.UserMacro}} - This will be replaced with the bidder server's user id macro. diff --git a/endpoints/setuid.go b/endpoints/setuid.go index 905fa26b2f2..25fb246593a 100644 --- a/endpoints/setuid.go +++ b/endpoints/setuid.go @@ -39,13 +39,6 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use encoder := usersync.Base64Encoder{} decoder := usersync.Base64Decoder{} - // convert map of syncers by bidder to map of syncers by key - // - its safe to assume that if multiple bidders map to the same key, the syncers are interchangeable. - syncersByKey := make(map[string]usersync.Syncer, len(syncersByBidder)) - for _, v := range syncersByBidder { - syncersByKey[v.Key()] = v - } - return httprouter.Handle(func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { so := analytics.SetUIDObject{ Status: http.StatusOK, @@ -65,7 +58,7 @@ func NewSetUIDEndpoint(cfg *config.Configuration, syncersByBidder map[string]use query := r.URL.Query() - syncer, err := getSyncer(query, syncersByKey) + syncer, err := getSyncer(query, syncersByBidder) if err != nil { w.WriteHeader(http.StatusBadRequest) w.Write([]byte(err.Error())) @@ -313,14 +306,14 @@ func parseConsentFromGppStr(gppQueryValue string) (string, error) { return gdprConsent, nil } -func getSyncer(query url.Values, syncersByKey map[string]usersync.Syncer) (usersync.Syncer, error) { - key := query.Get("bidder") +func getSyncer(query url.Values, syncersByBidder map[string]usersync.Syncer) (usersync.Syncer, error) { + bidder := query.Get("bidder") - if key == "" { + if bidder == "" { return nil, errors.New(`"bidder" query param is required`) } - syncer, syncerExists := syncersByKey[key] + syncer, syncerExists := syncersByBidder[bidder] if !syncerExists { return nil, errors.New("The bidder name provided is not supported by Prebid Server") } diff --git a/endpoints/setuid_test.go b/endpoints/setuid_test.go index 9176041ed8e..65bc26559da 100644 --- a/endpoints/setuid_test.go +++ b/endpoints/setuid_test.go @@ -50,7 +50,7 @@ func TestSetUIDEndpoint(t *testing.T) { description: "Set uid for valid bidder", }, { - uri: "/setuid?bidder=adnxs&uid=123", + uri: "/setuid?bidder=appnexus&uid=123", syncersBidderNameToKey: map[string]string{"appnexus": "adnxs"}, existingSyncs: nil, gdprAllowsHostCookies: true, diff --git a/usersync/syncer.go b/usersync/syncer.go index 2e0e41027b5..fb14201f2dc 100644 --- a/usersync/syncer.go +++ b/usersync/syncer.go @@ -63,7 +63,7 @@ var ErrSyncerKeyRequired = errors.New("key is required") // NewSyncer creates a new Syncer from the provided configuration, or return an error if macro substition // fails or an endpoint url is invalid. -func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer) (Syncer, error) { +func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer, bidder string) (Syncer, error) { if syncerConfig.Key == "" { return nil, ErrSyncerKeyRequired } @@ -80,7 +80,7 @@ func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer) (Syncer, if syncerConfig.IFrame != nil { var err error - syncer.iframe, err = buildTemplate(syncerConfig.Key, setuidSyncTypeIFrame, hostConfig, syncerConfig.ExternalURL, *syncerConfig.IFrame) + syncer.iframe, err = buildTemplate(bidder, setuidSyncTypeIFrame, hostConfig, syncerConfig.ExternalURL, *syncerConfig.IFrame) if err != nil { return nil, fmt.Errorf("iframe %v", err) } @@ -91,7 +91,7 @@ func NewSyncer(hostConfig config.UserSync, syncerConfig config.Syncer) (Syncer, if syncerConfig.Redirect != nil { var err error - syncer.redirect, err = buildTemplate(syncerConfig.Key, setuidSyncTypeRedirect, hostConfig, syncerConfig.ExternalURL, *syncerConfig.Redirect) + syncer.redirect, err = buildTemplate(bidder, setuidSyncTypeRedirect, hostConfig, syncerConfig.ExternalURL, *syncerConfig.Redirect) if err != nil { return nil, fmt.Errorf("redirect %v", err) } @@ -114,13 +114,14 @@ func resolveDefaultSyncType(syncerConfig config.Syncer) SyncType { var ( macroRegexExternalHost = regexp.MustCompile(`{{\s*\.ExternalURL\s*}}`) macroRegexSyncerKey = regexp.MustCompile(`{{\s*\.SyncerKey\s*}}`) + macroRegexBidderName = regexp.MustCompile(`{{\s*\.BidderName\s*}}`) macroRegexSyncType = regexp.MustCompile(`{{\s*\.SyncType\s*}}`) macroRegexUserMacro = regexp.MustCompile(`{{\s*\.UserMacro\s*}}`) macroRegexRedirect = regexp.MustCompile(`{{\s*\.RedirectURL\s*}}`) macroRegex = regexp.MustCompile(`{{\s*\..*?\s*}}`) ) -func buildTemplate(key, syncTypeValue string, hostConfig config.UserSync, syncerExternalURL string, syncerEndpoint config.SyncerEndpoint) (*template.Template, error) { +func buildTemplate(bidderName, syncTypeValue string, hostConfig config.UserSync, syncerExternalURL string, syncerEndpoint config.SyncerEndpoint) (*template.Template, error) { redirectTemplate := syncerEndpoint.RedirectURL if redirectTemplate == "" { redirectTemplate = hostConfig.RedirectURL @@ -128,7 +129,8 @@ func buildTemplate(key, syncTypeValue string, hostConfig config.UserSync, syncer externalURL := chooseExternalURL(syncerEndpoint.ExternalURL, syncerExternalURL, hostConfig.ExternalURL) - redirectURL := macroRegexSyncerKey.ReplaceAllLiteralString(redirectTemplate, key) + redirectURL := macroRegexSyncerKey.ReplaceAllLiteralString(redirectTemplate, bidderName) + redirectURL = macroRegexBidderName.ReplaceAllLiteralString(redirectURL, bidderName) redirectURL = macroRegexSyncType.ReplaceAllLiteralString(redirectURL, syncTypeValue) redirectURL = macroRegexUserMacro.ReplaceAllLiteralString(redirectURL, syncerEndpoint.UserMacro) redirectURL = macroRegexExternalHost.ReplaceAllLiteralString(redirectURL, externalURL) @@ -136,7 +138,7 @@ func buildTemplate(key, syncTypeValue string, hostConfig config.UserSync, syncer url := macroRegexRedirect.ReplaceAllString(syncerEndpoint.URL, redirectURL) - templateName := strings.ToLower(key) + "_usersync_url" + templateName := strings.ToLower(bidderName) + "_usersync_url" return template.New(templateName).Parse(url) } diff --git a/usersync/syncer_test.go b/usersync/syncer_test.go index 6e727c3a49c..c309b899358 100644 --- a/usersync/syncer_test.go +++ b/usersync/syncer_test.go @@ -26,6 +26,7 @@ func TestNewSyncer(t *testing.T) { testCases := []struct { description string givenKey string + givenBidderName string givenIFrameConfig *config.SyncerEndpoint givenRedirectConfig *config.SyncerEndpoint givenExternalURL string @@ -37,6 +38,7 @@ func TestNewSyncer(t *testing.T) { { description: "Missing Key", givenKey: "", + givenBidderName: "", givenIFrameConfig: iframeConfig, givenRedirectConfig: nil, expectedError: "key is required", @@ -44,6 +46,7 @@ func TestNewSyncer(t *testing.T) { { description: "Missing Endpoints", givenKey: "a", + givenBidderName: "bidderA", givenIFrameConfig: nil, givenRedirectConfig: nil, expectedError: "at least one endpoint (iframe and/or redirect) is required", @@ -51,6 +54,7 @@ func TestNewSyncer(t *testing.T) { { description: "IFrame & Redirect Endpoints", givenKey: "a", + givenBidderName: "bidderA", givenIFrameConfig: iframeConfig, givenRedirectConfig: redirectConfig, expectedDefault: SyncTypeIFrame, @@ -60,13 +64,15 @@ func TestNewSyncer(t *testing.T) { { description: "IFrame - Parse Error", givenKey: "a", + givenBidderName: "bidderA", givenIFrameConfig: errParseConfig, givenRedirectConfig: nil, - expectedError: "iframe template: a_usersync_url:1: function \"malformed\" not defined", + expectedError: "iframe template: biddera_usersync_url:1: function \"malformed\" not defined", }, { description: "IFrame - Validation Error", givenKey: "a", + givenBidderName: "bidderA", givenIFrameConfig: errInvalidConfig, givenRedirectConfig: nil, expectedError: "iframe composed url: \"notAURL:http%3A%2F%2Fhost.com%2Fhost\" is invalid", @@ -74,13 +80,15 @@ func TestNewSyncer(t *testing.T) { { description: "Redirect - Parse Error", givenKey: "a", + givenBidderName: "bidderA", givenIFrameConfig: nil, givenRedirectConfig: errParseConfig, - expectedError: "redirect template: a_usersync_url:1: function \"malformed\" not defined", + expectedError: "redirect template: biddera_usersync_url:1: function \"malformed\" not defined", }, { description: "Redirect - Validation Error", givenKey: "a", + givenBidderName: "bidderA", givenIFrameConfig: nil, givenRedirectConfig: errInvalidConfig, expectedError: "redirect composed url: \"notAURL:http%3A%2F%2Fhost.com%2Fhost\" is invalid", @@ -88,6 +96,7 @@ func TestNewSyncer(t *testing.T) { { description: "Syncer Level External URL", givenKey: "a", + givenBidderName: "bidderA", givenExternalURL: "http://syncer.com", givenIFrameConfig: iframeConfig, givenRedirectConfig: redirectConfig, @@ -106,7 +115,7 @@ func TestNewSyncer(t *testing.T) { ExternalURL: test.givenExternalURL, } - result, err := NewSyncer(hostConfig, syncerConfig) + result, err := NewSyncer(hostConfig, syncerConfig, test.givenBidderName) if test.expectedError == "" { assert.NoError(t, err, test.description+":err") @@ -196,7 +205,7 @@ func TestBuildTemplate(t *testing.T) { expectedRendered: "hasNoComposedMacros,gdpr=A", }, { - description: "All Composed Macros", + description: "All Composed Macros - SyncerKey", givenSyncerEndpoint: config.SyncerEndpoint{ URL: "https://bidder.com/sync?redirect={{.RedirectURL}}", RedirectURL: "{{.ExternalURL}}/setuid?bidder={{.SyncerKey}}&f={{.SyncType}}&gdpr={{.GDPR}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&uid={{.UserMacro}}", @@ -205,6 +214,16 @@ func TestBuildTemplate(t *testing.T) { }, expectedRendered: "https://bidder.com/sync?redirect=http%3A%2F%2Fsyncer.com%2Fsetuid%3Fbidder%3DanyKey%26f%3Dx%26gdpr%3DA%26gpp%3DD%26gpp_sid%3D1%26uid%3D%24UID%24", }, + { + description: "All Composed Macros - BidderName", + givenSyncerEndpoint: config.SyncerEndpoint{ + URL: "https://bidder.com/sync?redirect={{.RedirectURL}}", + RedirectURL: "{{.ExternalURL}}/setuid?bidder={{.BidderName}}&f={{.SyncType}}&gdpr={{.GDPR}}&gpp={{.GPP}}&gpp_sid={{.GPPSID}}&uid={{.UserMacro}}", + ExternalURL: "http://syncer.com", + UserMacro: "$UID$", + }, + expectedRendered: "https://bidder.com/sync?redirect=http%3A%2F%2Fsyncer.com%2Fsetuid%3Fbidder%3DanyKey%26f%3Dx%26gdpr%3DA%26gpp%3DD%26gpp_sid%3D1%26uid%3D%24UID%24", + }, { description: "Redirect URL + External URL From Host", givenSyncerEndpoint: config.SyncerEndpoint{ diff --git a/usersync/syncersbuilder.go b/usersync/syncersbuilder.go index c521d36dde2..9a52a740f31 100644 --- a/usersync/syncersbuilder.go +++ b/usersync/syncersbuilder.go @@ -59,17 +59,16 @@ func BuildSyncers(hostConfig *config.Configuration, bidderInfos config.BidderInf continue } - syncer, err := NewSyncer(hostUserSyncConfig, primaryCfg.cfg) - if err != nil { - errs = append(errs, SyncerBuildError{ - Bidder: primaryCfg.name, - SyncerKey: key, - Err: err, - }) - continue - } - for _, bidder := range cfgGroup { + syncer, err := NewSyncer(hostUserSyncConfig, primaryCfg.cfg, bidder.name) + if err != nil { + errs = append(errs, SyncerBuildError{ + Bidder: primaryCfg.name, + SyncerKey: key, + Err: err, + }) + continue + } syncers[bidder.name] = syncer } } diff --git a/usersync/syncersbuilder_test.go b/usersync/syncersbuilder_test.go index b3672a501bb..fbc1863959b 100644 --- a/usersync/syncersbuilder_test.go +++ b/usersync/syncersbuilder_test.go @@ -48,7 +48,7 @@ func TestBuildSyncers(t *testing.T) { givenConfig: hostConfig, givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated}, expectedIFramesURLs: map[string]string{ - "bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fa%2Fhost", + "bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder1%2Fhost", }, }, { @@ -64,7 +64,7 @@ func TestBuildSyncers(t *testing.T) { givenConfig: hostConfig, givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAError}, expectedErrors: []string{ - "cannot create syncer for bidder bidder1 with key a: iframe template: a_usersync_url:1: function \"xRedirectURL\" not defined", + "cannot create syncer for bidder bidder1 with key a: iframe template: bidder1_usersync_url:1: function \"xRedirectURL\" not defined", }, }, { @@ -72,8 +72,8 @@ func TestBuildSyncers(t *testing.T) { givenConfig: hostConfig, givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated, "bidder2": infoKeyBPopulated}, expectedIFramesURLs: map[string]string{ - "bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fa%2Fhost", - "bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fb%2Fhost", + "bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder1%2Fhost", + "bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost", }, }, { @@ -81,8 +81,8 @@ func TestBuildSyncers(t *testing.T) { givenConfig: hostConfig, givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated, "bidder2": infoKeyAEmpty}, expectedIFramesURLs: map[string]string{ - "bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fa%2Fhost", - "bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fa%2Fhost", + "bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder1%2Fhost", + "bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost", }, }, { @@ -106,7 +106,8 @@ func TestBuildSyncers(t *testing.T) { givenConfig: hostConfig, givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAEmpty, "bidder2": infoKeyAError}, expectedErrors: []string{ - "cannot create syncer for bidder bidder2 with key a: iframe template: a_usersync_url:1: function \"xRedirectURL\" not defined", + "cannot create syncer for bidder bidder2 with key a: iframe template: bidder1_usersync_url:1: function \"xRedirectURL\" not defined", + "cannot create syncer for bidder bidder2 with key a: iframe template: bidder2_usersync_url:1: function \"xRedirectURL\" not defined", }, }, { @@ -114,7 +115,7 @@ func TestBuildSyncers(t *testing.T) { givenConfig: hostConfig, givenBidderInfos: map[string]config.BidderInfo{"bidder1": {}, "bidder2": infoKeyBPopulated}, expectedIFramesURLs: map[string]string{ - "bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fb%2Fhost", + "bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost", }, }, { @@ -122,7 +123,7 @@ func TestBuildSyncers(t *testing.T) { givenConfig: hostConfig, givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyADisabled, "bidder2": infoKeyBPopulated}, expectedIFramesURLs: map[string]string{ - "bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fb%2Fhost", + "bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost", }, }, { @@ -130,7 +131,7 @@ func TestBuildSyncers(t *testing.T) { givenConfig: hostConfig, givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyASupportsOnly, "bidder2": infoKeyBPopulated}, expectedIFramesURLs: map[string]string{ - "bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fb%2Fhost", + "bidder2": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhost.com%2Fbidder2%2Fhost", }, }, { @@ -138,7 +139,7 @@ func TestBuildSyncers(t *testing.T) { givenConfig: hostConfig, givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAError, "bidder2": infoKeyBEmpty}, expectedErrors: []string{ - "cannot create syncer for bidder bidder1 with key a: iframe template: a_usersync_url:1: function \"xRedirectURL\" not defined", + "cannot create syncer for bidder bidder1 with key a: iframe template: bidder1_usersync_url:1: function \"xRedirectURL\" not defined", "cannot create syncer for bidder bidder2 with key b: at least one endpoint (iframe and/or redirect) is required", }, }, @@ -147,7 +148,7 @@ func TestBuildSyncers(t *testing.T) { givenConfig: config.Configuration{ExternalURL: "http://host.com", UserSync: config.UserSync{ExternalURL: "http://hostoverride.com", RedirectURL: "{{.ExternalURL}}/{{.SyncerKey}}/host"}}, givenBidderInfos: map[string]config.BidderInfo{"bidder1": infoKeyAPopulated}, expectedIFramesURLs: map[string]string{ - "bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhostoverride.com%2Fa%2Fhost", + "bidder1": "https://bidder.com/iframe?redirect=http%3A%2F%2Fhostoverride.com%2Fbidder1%2Fhost", }, }, }