Skip to content

Commit

Permalink
Feature first login src (#1241)
Browse files Browse the repository at this point in the history
Adding source in user database for analytics.
  • Loading branch information
luciferlinx101 authored Sep 18, 2023
1 parent eaf44a8 commit f118e90
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 27 deletions.
69 changes: 46 additions & 23 deletions gui/pages/_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ import {
validateAccessToken,
checkEnvironment,
addUser,
installToolkitTemplate, installAgentTemplate, installKnowledgeTemplate
installToolkitTemplate, installAgentTemplate, installKnowledgeTemplate, getFirstSignup
} from "@/pages/api/DashboardService";
import {githubClientId} from "@/pages/api/apiConfig";
import {
getGithubClientId
} from "@/pages/api/DashboardService";
import {useRouter} from 'next/router';
import querystring from 'querystring';
import {refreshUrl, loadingTextEffect} from "@/utils/utils";
import {refreshUrl, loadingTextEffect, getUTMParametersFromURL, setLocalStorageValue} from "@/utils/utils";
import MarketplacePublic from "./Content/Marketplace/MarketplacePublic"
import {toast} from "react-toastify";

Expand Down Expand Up @@ -101,12 +101,7 @@ export default function App() {
}

useEffect(() => {
if (window.location.href.toLowerCase().includes('marketplace')) {
setShowMarketplace(true);
} else {
installFromMarketplace();
}

handleMarketplace()
loadingTextEffect('Initializing SuperAGI', setLoadingText, 500);

checkEnvironment()
Expand All @@ -124,34 +119,28 @@ export default function App() {
const parsedParams = querystring.parse(queryParams);
let access_token = parsedParams.access_token || null;

const utmParams = getUTMParametersFromURL();
if (utmParams)
sessionStorage.setItem('utm_source', utmParams.utm_source);
const signupSource = sessionStorage.getItem('utm_source');

if (typeof window !== 'undefined' && access_token) {
localStorage.setItem('accessToken', access_token);
refreshUrl();
}

validateAccessToken()
.then((response) => {
setUserName(response.data.name || '');
if(signupSource) {
handleSignUpSource(signupSource)
}
fetchOrganisation(response.data.id);
})
.catch((error) => {
console.error('Error validating access token:', error);
});
} else {
const userData = {
"name": "SuperAGI User",
"email": "super6@agi.com",
"password": "pass@123",
}

addUser(userData)
.then((response) => {
setUserName(response.data.name);
fetchOrganisation(response.data.id);
})
.catch((error) => {
console.error('Error adding user:', error);
});
handleLocalEnviroment()
}
})
.catch((error) => {
Expand Down Expand Up @@ -197,6 +186,40 @@ export default function App() {
}
}

const handleLocalEnviroment = () => {
const userData = {
"name": "SuperAGI User",
"email": "super6@agi.com",
"password": "pass@123",
}

addUser(userData)
.then((response) => {
setUserName(response.data.name);
fetchOrganisation(response.data.id);
})
.catch((error) => {
console.error('Error adding user:', error);
});
};
const handleSignUpSource = (signup) => {
getFirstSignup(signup)
.then((response) => {
sessionStorage.removeItem('utm_source');
})
.catch((error) => {
console.error('Error validating source:', error);
})
};

const handleMarketplace = () => {
if (window.location.href.toLowerCase().includes('marketplace')) {
setShowMarketplace(true);
} else {
installFromMarketplace();
}
};

useEffect(() => {
const clearLocalStorage = () => {
Object.keys(localStorage).forEach((key) => {
Expand Down
6 changes: 5 additions & 1 deletion gui/pages/api/DashboardService.js
Original file line number Diff line number Diff line change
Expand Up @@ -395,4 +395,8 @@ export const getKnowledgeMetrics = (knowledgeName) => {

export const getKnowledgeLogs = (knowledgeName) => {
return api.get(`analytics/knowledge/${knowledgeName}/logs`)
}
}

export const getFirstSignup = (source) => {
return api.post(`/users/first_login_source/${source}`,);
};
18 changes: 17 additions & 1 deletion gui/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -542,4 +542,20 @@ export const convertWaitingPeriod = (waitingPeriod) => {
// hour: 'numeric',
// minute: 'numeric'
// });
// }
// }

export const getUTMParametersFromURL = () => {
const params = new URLSearchParams(window.location.search);

const utmParams = {
utm_source: params.get('utm_source') || '',
utm_medium: params.get('utm_medium') || '',
utm_campaign: params.get('utm_campaign') || '',
};

if (!utmParams.utm_source && !utmParams.utm_medium && !utmParams.utm_campaign) {
return null;
}

return utmParams;
}
28 changes: 28 additions & 0 deletions migrations/versions/3867bb00a495_added_first_login_source.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""added_first_login_source
Revision ID: 3867bb00a495
Revises: 661ec8a4c32e
Create Date: 2023-09-15 02:06:24.006555
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '3867bb00a495'
down_revision = '661ec8a4c32e'
branch_labels = None
depends_on = None


def upgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('users', sa.Column('first_login_source', sa.String(), nullable=True))
# ### end Alembic commands ###


def downgrade() -> None:
# ### commands auto generated by Alembic - please adjust! ###
op.drop_column('users', 'first_login_source')
# ### end Alembic commands ###
18 changes: 17 additions & 1 deletion superagi/controllers/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
from superagi.models.user import User
from fastapi import APIRouter

from superagi.helper.auth import check_auth
from superagi.helper.auth import check_auth, get_current_user
from superagi.lib.logger import logger

# from superagi.types.db import UserBase, UserIn, UserOut

router = APIRouter()


class UserBase(BaseModel):
name: str
email: str
Expand All @@ -42,6 +44,7 @@ class UserIn(UserBase):
class Config:
orm_mode = True


# CRUD Operations
@router.post("/add", response_model=UserOut, status_code=201)
def create_user(user: UserIn,
Expand Down Expand Up @@ -126,3 +129,16 @@ def update_user(user_id: int,

db.session.commit()
return db_user


@router.post("/first_login_source/{source}")
def update_first_login_source(source: str, Authorize: AuthJWT = Depends(check_auth)):
""" Update first login source of the user """
user = get_current_user(Authorize)
# valid_sources = ['google', 'github', 'email']
if user.first_login_source is None or user.first_login_source == '':
user.first_login_source = source
db.session.commit()
db.session.flush()
logger.info("User : ",user)
return user
3 changes: 2 additions & 1 deletion superagi/models/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class User(DBBaseModel):
email = Column(String, unique=True)
password = Column(String)
organisation_id = Column(Integer)
first_login_source = Column(String)

def __repr__(self):
"""
Expand All @@ -34,4 +35,4 @@ def __repr__(self):
"""

return f"User(id={self.id}, name='{self.name}', email='{self.email}', password='{self.password}'," \
f"organisation_id={self.organisation_id})"
f"organisation_id={self.organisation_id}, first_login_source={self.first_login_source})"
37 changes: 37 additions & 0 deletions tests/unit_tests/controllers/test_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
from unittest.mock import patch

import pytest
from fastapi.testclient import TestClient

from main import app
from superagi.models.user import User

client = TestClient(app)

# Define a fixture for an authenticated user
@pytest.fixture
def authenticated_user():
# Create a mock user object with necessary attributes
user = User()

# Set user attributes
user.id = 1 # User ID
user.username = "testuser" # User's username
user.email = "super6@agi.com" # User's email
user.first_login_source = None # User's first login source
user.token = "mock-jwt-token"

return user

# Test case for updating first login source when it's not set
def test_update_first_login_source(authenticated_user):
with patch('superagi.helper.auth.db') as mock_auth_db:
source = "github" # Specify the source you want to set

mock_auth_db.session.query.return_value.filter.return_value.first.return_value = authenticated_user
response = client.post(f"users/first_login_source/{source}", headers={"Authorization": f"Bearer {authenticated_user.token}"})

# Verify the HTTP response
assert response.status_code == 200
assert "first_login_source" in response.json() # Check if the "first_login_source" field is in the response
assert response.json()["first_login_source"] == "github" # Check if the "source" field equals "github"

0 comments on commit f118e90

Please sign in to comment.