Skip to content

Commit

Permalink
ability to route traffic via custom traffic source params
Browse files Browse the repository at this point in the history
  • Loading branch information
EricFrancis12 committed Sep 29, 2024
1 parent 0d6e629 commit bf3ccf3
Show file tree
Hide file tree
Showing 25 changed files with 546 additions and 130 deletions.
21 changes: 12 additions & 9 deletions api/t.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,19 @@ func T(w http.ResponseWriter, r *http.Request) {
ipInfoData = <-ipidch
}

trafficSource := <-tsch
tsTokens := trafficSource.MakeTokens(*r.URL)

publicClickId := pkg.NewPublicClickID()
dest, _ := campaign.DetermineViewDestination(pkg.DestinationOpts{
R: *r,
Ctx: ctx,
Storer: *tStorer,
SavedFlow: savedFlow,
UserAgent: userAgent,
IpInfoData: ipInfoData,
PublicClickId: publicClickId,
R: *r,
Ctx: ctx,
Storer: *tStorer,
SavedFlow: savedFlow,
UserAgent: userAgent,
IpInfoData: ipInfoData,
TrafficSourceTokens: tsTokens,
PublicClickId: publicClickId,
})

anch := make(chan pkg.AffiliateNetwork)
Expand All @@ -93,7 +97,6 @@ func T(w http.ResponseWriter, r *http.Request) {
if !visitorNeedsIpInfoData {
ipInfoData = <-ipidch
}
trafficSource := <-tsch
affiliateNetwork := <-anch

// Save click to db
Expand All @@ -106,7 +109,7 @@ func T(w http.ResponseWriter, r *http.Request) {
ClickTime: getClicktime(dest, timestamp),
ViewOutputURL: dest.URL,
ClickOutputURL: getClickOutputURL(dest),
Tokens: trafficSource.MakeTokens(*r.URL),
Tokens: tsTokens,
IP: r.RemoteAddr,
Isp: ipInfoData.Org,
UserAgent: r.UserAgent(),
Expand Down
4 changes: 2 additions & 2 deletions cypress/e2e/dashboard.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ describe("Testing dashboard functionality", () => {
cy.get("[data-cy='password-input']").type(Cypress.env(Env.ROOT_PASSWORD));
cy.get("[data-cy='submit-button']").click();

cy.wait(1000 * 20);
cy.wait(1000 * 10);

cy.url().should("eq", "http://localhost:3000/dashboard");

const { name } = returnFirstOrThrow(seedData.campaignSeeds, "Campaign seed");
cy.get(`[data-cy='${name}']`).click();
cy.get("[data-cy='report-button']").click();

cy.wait(1000 * 20);
cy.wait(1000 * 10);

const { customTokens } = returnFirstOrThrow(seedData.trafficSourceSeeds, "Traffic Source seed");
for (const token of customTokens) {
Expand Down
29 changes: 24 additions & 5 deletions cypress/e2e/redirects.cy.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { makeCampaignUrl, makeClickUrl, makePostbackUrl } from "../../src/lib/utils";
import { ECookieName, Env } from "../../src/lib/types";
import { returnAtIndexOrThrow, returnFirstOrThrow, testUserAgent } from "../../prisma/seedData";
import { ECustomTokenParam, returnAtIndexOrThrow, returnFirstOrThrow, testUserAgent, testZoneId } from "../../prisma/seedData";
import seedData from "../../prisma/seedData";
const { campaignSeeds, landingPageSeeds, offerSeeds, trafficSourceSeeds } = seedData;

Expand Down Expand Up @@ -48,16 +48,35 @@ describe("Testing campaign redirects", () => {
cy.url().should("eq", Cypress.env(Env.CATCH_ALL_REDIRECT_URL));
});

it("redirects to the correct rule route", () => {
it("redirects to the correct rule route per user agent header", () => {
cy.visit(
makeCampaignUrl("http:", "localhost", "3001", publicId, customTokens),
makeCampaignUrl("http:", "localhost", "3001", publicId, []),
{
headers: {
"user-agent": testUserAgent,
"User-Agent": testUserAgent,
},
});
},
);

const { url } = returnAtIndexOrThrow(offerSeeds, 1, "Offer seed");
cy.url().should("eq", url);
});

it("redirects to the correct rule route per custom traffic source token", () => {
const tokens = [
{
queryParam: ECustomTokenParam.ZONE_ID,
value: testZoneId,
},
{
queryParam: ECustomTokenParam.BANNER_ID,
value: "",
},
];

cy.visit(makeCampaignUrl("http:", "localhost", "3001", publicId, tokens));

const { url } = returnAtIndexOrThrow(offerSeeds, 2, "Offer seed");
cy.url().should("eq", url);
});
});
23 changes: 12 additions & 11 deletions pkg/campaign.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ func (s *Storer) GetCampaignByPublicId(ctx context.Context, publicId string) (Ca
return c, nil
}

func (c *Campaign) SelectViewRoute(r http.Request, userAgent useragent.UserAgent, ipInfoData IPInfoData) Route {
return selectViewRoute(c.FlowMainRoute, c.FlowRuleRoutes, r, userAgent, ipInfoData)
func (c *Campaign) SelectViewRoute(r http.Request, userAgent useragent.UserAgent, ipInfoData IPInfoData, tokens []Token) Route {
return selectViewRoute(c.FlowMainRoute, c.FlowRuleRoutes, r, userAgent, ipInfoData, tokens)
}

// Checks if the click triggered any rule routes, and if not returns the main route
Expand All @@ -99,13 +99,14 @@ func (c *Campaign) SelectOfferID(ids []int) (int, error) {
}

type DestinationOpts struct {
R http.Request
Ctx context.Context
Storer Storer
SavedFlow SavedFlow
UserAgent useragent.UserAgent
IpInfoData IPInfoData
PublicClickId string
R http.Request
Ctx context.Context
Storer Storer
SavedFlow SavedFlow
UserAgent useragent.UserAgent
IpInfoData IPInfoData
TrafficSourceTokens []Token
PublicClickId string
}

func (do *DestinationOpts) TokenMatcherMap() URLTokenMatcherMap {
Expand Down Expand Up @@ -138,9 +139,9 @@ func (c *Campaign) DetermineViewDestination(opts DestinationOpts) (Destination,
} else if c.FlowType == db.FlowTypeBuiltIn || c.FlowType == db.FlowTypeSaved {
route := Route{}
if c.FlowType == db.FlowTypeBuiltIn {
route = c.SelectViewRoute(opts.R, opts.UserAgent, opts.IpInfoData)
route = c.SelectViewRoute(opts.R, opts.UserAgent, opts.IpInfoData, opts.TrafficSourceTokens)
} else {
route = opts.SavedFlow.SelectViewRoute(opts.R, opts.UserAgent, opts.IpInfoData)
route = opts.SavedFlow.SelectViewRoute(opts.R, opts.UserAgent, opts.IpInfoData, opts.TrafficSourceTokens)
}

path, err := route.WeightedSelectPath()
Expand Down
214 changes: 214 additions & 0 deletions pkg/campaign_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package pkg

import (
"net/http"
"testing"

"github.com/EricFrancis12/evoclick/prisma/db"
"github.com/mileusna/useragent"
"github.com/stretchr/testify/assert"
)

func TestCampaign(t *testing.T) {
var mainRoute = Route{
// The LogicalRelation field is irrelevant for the mainRoute.
// It is being defined here as an "anchor" to ensure accurate comparison of mainRoute == ruleRoute.
// mainRoute -> LogicalRelationOr
// ruleRoute -> LogicalRelationAnd
LogicalRelation: LogicalRelationOr,
Paths: []Path{
{
IsActive: true,
Weight: 50,
LandingPageIDs: []int{1, 2, 3},
OfferIDs: []int{1, 2, 3},
},
},
}

t.Run("Redirects to main route over rule routes by default", func(t *testing.T) {
ruleRoute := Route{
IsActive: true,
LogicalRelation: LogicalRelationAnd,
}
campaign := Campaign{
FlowMainRoute: mainRoute,
FlowRuleRoutes: []Route{ruleRoute},
}

selectedViewRoute := campaign.SelectViewRoute(http.Request{}, useragent.UserAgent{}, IPInfoData{}, []Token{})

assert.Equal(t, selectedViewRoute, ruleRoute)
assert.Equal(t, selectedViewRoute, campaign.FlowRuleRoutes[0])
assert.NotEqual(t, selectedViewRoute, mainRoute)
assert.NotEqual(t, selectedViewRoute, campaign.FlowMainRoute)

selectedClickRoute := campaign.SelectClickRoute(Click{})

assert.Equal(t, selectedClickRoute, ruleRoute)
assert.Equal(t, selectedClickRoute, campaign.FlowRuleRoutes[0])
assert.NotEqual(t, selectedClickRoute, mainRoute)
assert.NotEqual(t, selectedClickRoute, campaign.FlowMainRoute)
})
t.Run("Test campaign.SelectViewRoute() with non-custom RuleName", func(t *testing.T) {
var (
ruleRoute = Route{
IsActive: true,
LogicalRelation: LogicalRelationAnd,
Rules: []Rule{
{
RuleName: RuleNameOS,
Data: []string{"Windows"},
Includes: true,
},
},
Paths: []Path{
{
IsActive: true,
Weight: 100,
},
},
}
)

campaign := Campaign{
FlowMainRoute: mainRoute,
FlowRuleRoutes: []Route{ruleRoute},
}

selectedRoute := campaign.SelectViewRoute(http.Request{}, useragent.UserAgent{OS: "Windows"}, IPInfoData{}, []Token{})

assert.Equal(t, selectedRoute, ruleRoute)
assert.Equal(t, selectedRoute, campaign.FlowRuleRoutes[0])
assert.NotEqual(t, selectedRoute, mainRoute)
assert.NotEqual(t, selectedRoute, campaign.FlowMainRoute)
})

t.Run("Test campaign.SelectViewRoute() with custom RuleName (from traffic source custom tokens)", func(t *testing.T) {
var (
queryParam = "zone_id"
value = "87654321"
ruleRoute = Route{
IsActive: true,
LogicalRelation: LogicalRelationAnd,
Rules: []Rule{
{
RuleName: toCustomRuleName(queryParam),
Data: []string{value},
Includes: true,
},
},
Paths: []Path{
{
IsActive: true,
Weight: 100,
},
},
}
)

campaign := Campaign{
FlowMainRoute: mainRoute,
FlowRuleRoutes: []Route{ruleRoute},
}

tokens := []Token{
{
QueryParam: queryParam,
Value: value,
},
}

selectedRoute := campaign.SelectViewRoute(http.Request{}, useragent.UserAgent{}, IPInfoData{}, tokens)

assert.Equal(t, selectedRoute, ruleRoute)
assert.Equal(t, selectedRoute, campaign.FlowRuleRoutes[0])
assert.NotEqual(t, selectedRoute, mainRoute)
assert.NotEqual(t, selectedRoute, campaign.FlowMainRoute)
})

t.Run("Test campaign.SelectClickRoute() with non-custom RuleName", func(t *testing.T) {
var (
ruleRoute = Route{
IsActive: true,
LogicalRelation: LogicalRelationAnd,
Rules: []Rule{
{
RuleName: RuleNameOS,
Data: []string{"Windows"},
Includes: true,
},
},
Paths: []Path{
{
IsActive: true,
Weight: 100,
},
},
}
)

campaign := Campaign{
FlowMainRoute: mainRoute,
FlowRuleRoutes: []Route{ruleRoute},
}

click := Click{
InnerClick: db.InnerClick{
Os: "Windows",
},
}

selectedRoute := campaign.SelectClickRoute(click)

assert.Equal(t, selectedRoute, ruleRoute)
assert.Equal(t, selectedRoute, campaign.FlowRuleRoutes[0])
assert.NotEqual(t, selectedRoute, mainRoute)
assert.NotEqual(t, selectedRoute, campaign.FlowMainRoute)
})

t.Run("Test campaign.SelectClickRoute() with custom RuleName (from traffic source custom tokens)", func(t *testing.T) {
var (
queryParam = "zone_id"
value = "87654321"
ruleRoute = Route{
IsActive: true,
LogicalRelation: LogicalRelationAnd,
Rules: []Rule{
{
RuleName: toCustomRuleName(queryParam),
Data: []string{value},
Includes: true,
},
},
Paths: []Path{
{
IsActive: true,
Weight: 100,
},
},
}
)

campaign := Campaign{
FlowMainRoute: mainRoute,
FlowRuleRoutes: []Route{ruleRoute},
}

click := Click{
Tokens: []Token{
{
QueryParam: queryParam,
Value: value,
},
},
}

selectedRoute := campaign.SelectClickRoute(click)

assert.Equal(t, selectedRoute, ruleRoute)
assert.Equal(t, selectedRoute, campaign.FlowRuleRoutes[0])
assert.NotEqual(t, selectedRoute, mainRoute)
assert.NotEqual(t, selectedRoute, campaign.FlowMainRoute)
})
}
4 changes: 2 additions & 2 deletions pkg/flow.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ func (s *Storer) GetSavedFlowById(ctx context.Context, id int) (SavedFlow, error
return fl, nil
}

func (sf *SavedFlow) SelectViewRoute(r http.Request, userAgent useragent.UserAgent, ipInfoData IPInfoData) Route {
return selectViewRoute(sf.MainRoute, sf.RuleRoutes, r, userAgent, ipInfoData)
func (sf *SavedFlow) SelectViewRoute(r http.Request, userAgent useragent.UserAgent, ipInfoData IPInfoData, tokens []Token) Route {
return selectViewRoute(sf.MainRoute, sf.RuleRoutes, r, userAgent, ipInfoData, tokens)
}

func (sf *SavedFlow) SelectClickRoute(click Click) Route {
Expand Down
Loading

0 comments on commit bf3ccf3

Please sign in to comment.