Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 178 healy: Upsert user email to database #336

Merged
merged 20 commits into from
Nov 18, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,4 @@ def get_env_var(name: str) -> str:
UNIQUE_ISSUE_ID = "O/gitautoai/test#1"
USER_ID = -1
USER_NAME = "username-test"
EMAIL = "test@gitauto.com"
6 changes: 5 additions & 1 deletion services/gitauto_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
create_comment,
update_comment,
add_reaction_to_issue,
get_user_public_email,
)
from services.github.github_types import (
BaseArgs,
Expand All @@ -49,7 +50,6 @@

supabase_manager = SupabaseManager(url=SUPABASE_URL, key=SUPABASE_SERVICE_ROLE_KEY)


BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved
async def handle_gitauto(payload: GitHubLabeledPayload, trigger_type: str) -> None:
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved
"""Core functionality to create comments on issue, create PRs, and update progress."""
current_time: float = time.time()
Expand Down Expand Up @@ -84,6 +84,7 @@ async def handle_gitauto(payload: GitHubLabeledPayload, trigger_type: str) -> No
sender_id: int = payload["sender"]["id"]
is_automation: bool = sender_id == GITHUB_APP_USER_ID
sender_name: str = payload["sender"]["login"]
email: str = get_user_public_email(username=sender_name)
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved

# Extract other information
github_urls, other_urls = extract_urls(text=issue_body)
Expand Down Expand Up @@ -140,6 +141,7 @@ async def handle_gitauto(payload: GitHubLabeledPayload, trigger_type: str) -> No
user_id=sender_id,
installation_id=installation_id,
unique_issue_id=unique_issue_id,
email=email,
)
add_reaction_to_issue(
issue_number=issue_number, content="eyes", base_args=base_args
Expand Down Expand Up @@ -253,5 +255,7 @@ async def handle_gitauto(payload: GitHubLabeledPayload, trigger_type: str) -> No
token_input=token_input,
token_output=token_output,
total_seconds=int(end_time - current_time),
user_id=sender_id,
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved
email=email,
)
return
17 changes: 17 additions & 0 deletions services/github/github_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ def create_comment_on_issue_with_gitauto_button(payload: GitHubLabeledPayload) -
issue_number: int = payload["issue"]["number"]
user_id: int = payload["sender"]["id"]
user_name: str = payload["sender"]["login"]
email: str = get_user_public_email(username=user_name)
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved

supabase_manager = SupabaseManager(url=SUPABASE_URL, key=SUPABASE_SERVICE_ROLE_KEY)

Expand All @@ -247,6 +248,7 @@ def create_comment_on_issue_with_gitauto_button(payload: GitHubLabeledPayload) -
user_id=user_id,
user_name=user_name,
installation_id=installation_id,
email=email,
)
first_issue = True
elif supabase_manager.is_users_first_issue(
Expand Down Expand Up @@ -829,3 +831,18 @@ def update_comment_for_raised_errors(
update_comment(comment_url=comment_url, token=token, body=body)

raise RuntimeError("Error occurred")

@handle_exceptions(default_return_value=None, raise_on_error=False)
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved
def get_user_public_email(username: str):
url = f"https://api.github.com/users/{username}"
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved
headers = {
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved
"Accept": "application/vnd.github.v3+json"
}

response: requests.Response = requests.get(url, headers=headers)
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved
response.raise_for_status()
user_data: dict = response.json()

email: str = user_data.get('email')
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved

BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved
return email
45 changes: 36 additions & 9 deletions services/supabase/gitauto_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@

from datetime import datetime, timezone
from supabase import Client
from postgrest.base_request_builder import APIResponse
from services.stripe.customer import create_stripe_customer, subscribe_to_free_plan
from services.supabase.users_manager import UsersManager
from utils.handle_exceptions import handle_exceptions


Expand Down Expand Up @@ -40,6 +42,7 @@ def create_installation(
owner_id: int,
user_id: int,
user_name: str,
email: str,
) -> None:
"""Create owners record with stripe customerId, subscribe to free plan, create installation record, create users record on Installation Webhook event"""
# If owner doesn't exist in owners table, insert owner and stripe customer
Expand Down Expand Up @@ -69,6 +72,26 @@ def create_installation(
json={"owner_id": owner_id, "stripe_customer_id": customer_id}
).execute()

users_manager = UsersManager(client=self.client)
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved
email_to_store = None if str(email).lower().endswith("@users.noreply.github.com") else email
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved

response: APIResponse = self.client.table("users").select("email").eq("user_id", user_id).execute()

if response.data and len(response.data[0]) > 0:
# Update the email if it's different from the stored one
if email_to_store != response.data[0].get('email'):
self.client.table("users").update(
json={"email": email_to_store}
).eq("user_id", user_id).execute()
else:
# Create the user if it doesn't exist
users_manager.create_user(
user_id=user_id,
user_name=user_name,
installation_id=installation_id,
email=email_to_store,
)
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved

# Insert installation record
self.client.table(table_name="installations").insert(
json={
Expand All @@ -78,14 +101,7 @@ def create_installation(
"owner_id": owner_id,
}
).execute()

self.client.table(table_name="users").upsert(
json={
"user_id": user_id,
"user_name": user_name,
},
on_conflict="user_id",
).execute()

# Create User, and set is_selected to True if user has no selected account for this installation
is_selected = True
data, _ = (
Expand All @@ -108,7 +124,7 @@ def create_installation(

@handle_exceptions(default_return_value=None, raise_on_error=True)
def create_user_request(
self, user_id: int, installation_id: int, unique_issue_id: str
self, user_id: int, installation_id: int, unique_issue_id: str, email: str
) -> int:
"""Creates record in usage table for this user and issue."""
# If issue doesn't exist, create one
Expand All @@ -118,6 +134,7 @@ def create_user_request(
.eq(column="unique_id", value=unique_issue_id)
.execute()
)
# If no issue exists with that unique_issue_id, create one
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved
if not data[1]:
self.client.table(table_name="issues").insert(
json={
Expand All @@ -137,6 +154,16 @@ def create_user_request(
)
.execute()
)

if not str(email).lower().endswith("@users.noreply.github.com"):
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved
response: APIResponse = self.client.table("users").select("email").eq("user_id", user_id).execute()

if response.data and len(response.data[0]) > 0:
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved
if email != response.data[0].get('email'):
self.client.table("users").update(
json={"email": email}
).eq("user_id", user_id).execute()
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved

return data[1][0]["id"]

@handle_exceptions(default_return_value=None, raise_on_error=False)
Expand Down
6 changes: 4 additions & 2 deletions services/supabase/users_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ def __init__(self, client: Client) -> None:
self.client: Client = client

@handle_exceptions(default_return_value=None, raise_on_error=False)
def create_user(self, user_id: int, user_name: str, installation_id: int) -> None:
def create_user(self, user_id: int, user_name: str, installation_id: int, email: str) -> None:
"""Creates an account for the user in the users table"""
self.client.table(table_name="users").upsert(
json={
"user_id": user_id,
"user_name": user_name,
}
"email": email,
},
on_conflict="user_id"
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved
).execute()

self.client.table(table_name="user_installations").insert(
Expand Down
3 changes: 3 additions & 0 deletions services/webhook_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
create_comment_on_issue_with_gitauto_button,
get_installation_access_token,
# turn_on_issue,
get_user_public_email
)
from services.github.github_types import (
GitHubEventPayload,
Expand All @@ -38,6 +39,7 @@ async def handle_installation_created(payload: GitHubInstallationPayload) -> Non
user_id: int = payload["sender"]["id"]
user_name: str = payload["sender"]["login"]
token: str = get_installation_access_token(installation_id=installation_id)
email: str = get_user_public_email(username=user_name)
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved

# Create installation record in Supabase
supabase_manager.create_installation(
Expand All @@ -47,6 +49,7 @@ async def handle_installation_created(payload: GitHubInstallationPayload) -> Non
owner_id=owner_id,
user_id=user_id,
user_name=user_name,
email=email,
)

# Add issue templates to the repositories
Expand Down
18 changes: 13 additions & 5 deletions tests/services/supabase/test_gitauto_manager.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# run this file locally with: python -m tests.services.supabase.test_gitauto_manager
import os
from config import OWNER_TYPE
from config import (EMAIL, OWNER_TYPE)
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved
from services.supabase import SupabaseManager
from tests.services.supabase.wipe_data import (
wipe_installation_owner_user_data,
Expand All @@ -17,6 +17,7 @@ def test_create_update_user_request_works() -> None:
# using -1 to not conflict with real data
user_id = -1
installation_id = -1
user_name = "test"
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved

# Clean up at the beginning just in case a prior test failed to clean
wipe_installation_owner_user_data()
Expand All @@ -28,14 +29,16 @@ def test_create_update_user_request_works() -> None:
owner_name="gitautoai",
owner_id=-1,
user_id=user_id,
user_name="test",
user_name=user_name,
email=EMAIL,
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved
)

usage_record_id = supabase_manager.create_user_request(
user_id=user_id,
installation_id=installation_id,
# fake issue creation
unique_issue_id="U/gitautoai/test#01",
email=EMAIL,
)
assert isinstance(
usage_record_id,
Expand Down Expand Up @@ -65,6 +68,8 @@ def test_complete_and_update_usage_record_only_updates_one_record() -> None:
# using -1 to not conflict with real data
user_id = -1
installation_id = -1
user_name = "test"
BigOleHealz marked this conversation as resolved.
Show resolved Hide resolved
unique_issue_id = "U/gitautoai/test#01"

# Clean up at the beginning just in case a prior test failed to clean
wipe_installation_owner_user_data()
Expand All @@ -76,7 +81,8 @@ def test_complete_and_update_usage_record_only_updates_one_record() -> None:
owner_name="gitautoai",
owner_id=-1,
user_id=user_id,
user_name="test",
user_name=user_name,
email=EMAIL,
)

# Creating multiple usage records where is_completed = false.
Expand All @@ -85,14 +91,16 @@ def test_complete_and_update_usage_record_only_updates_one_record() -> None:
user_id=user_id,
installation_id=installation_id,
# fake issue creation
unique_issue_id="U/gitautoai/test#01",
unique_issue_id=unique_issue_id,
email=EMAIL,
)

usage_record_id = supabase_manager.create_user_request(
user_id=user_id,
installation_id=installation_id,
# fake issue creation
unique_issue_id="U/gitautoai/test#01",
unique_issue_id=unique_issue_id,
email=EMAIL,
)
assert isinstance(
usage_record_id,
Expand Down
6 changes: 6 additions & 0 deletions tests/services/supabase/test_users_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
INSTALLATION_ID,
UNIQUE_ISSUE_ID,
NEW_INSTALLATION_ID,
EMAIL,
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved
)
from services.stripe.customer import get_subscription
from services.supabase import SupabaseManager
Expand Down Expand Up @@ -47,12 +48,14 @@ def test_create_and_update_user_request_works() -> None:
owner_id=OWNER_ID,
user_id=USER_ID,
user_name=USER_NAME,
email=EMAIL,
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved
)

usage_record_id = supabase_manager.create_user_request(
user_id=USER_ID,
installation_id=INSTALLATION_ID,
unique_issue_id="U/gitautoai/nextjs-website#52",
email=EMAIL,
)
assert isinstance(usage_record_id, int)
hiroshinishio marked this conversation as resolved.
Show resolved Hide resolved
assert (
Expand Down Expand Up @@ -83,6 +86,7 @@ def test_how_many_requests_left() -> None:
owner_id=OWNER_ID,
user_id=USER_ID,
user_name=USER_NAME,
email=EMAIL,
)
# Testing 0 requests have been made on free tier
requests_left, request_count, end_date = (
Expand Down Expand Up @@ -154,6 +158,7 @@ def test_is_users_first_issue() -> None:
owner_id=OWNER_ID,
user_id=USER_ID,
user_name=USER_NAME,
email=EMAIL,
)
assert supabase_manager.is_users_first_issue(
user_id=USER_ID, installation_id=INSTALLATION_ID
Expand Down Expand Up @@ -189,6 +194,7 @@ def test_parse_subscription_object() -> None:
owner_id=OWNER_ID,
user_id=USER_ID,
user_name=USER_NAME,
email=EMAIL,
)

def assertion_test(customer_id: str, product_id: str):
Expand Down