Skip to content

Commit

Permalink
server: create Customer model and start to link logic to it
Browse files Browse the repository at this point in the history
  • Loading branch information
frankie567 committed Dec 3, 2024
1 parent f3c74d4 commit 33c981f
Show file tree
Hide file tree
Showing 36 changed files with 398 additions and 414 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from alembic import op

# Polar Custom Imports
import polar.checkout.tax
import polar.kit.tax

# revision identifiers, used by Alembic.
revision = "e4473617a8e9"
Expand All @@ -25,7 +25,7 @@ def upgrade() -> None:
"checkouts",
sa.Column(
"customer_tax_id",
polar.checkout.tax.TaxIDType(astext_type=sa.Text()),
polar.kit.tax.TaxIDType(astext_type=sa.Text()),
nullable=True,
),
)
Expand Down
6 changes: 3 additions & 3 deletions server/polar/benefit/benefits/ads.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any, cast

from polar.auth.models import AuthSubject
from polar.models import Organization, User
from polar.models import Customer, Organization, User
from polar.models.benefit import BenefitAds, BenefitAdsProperties
from polar.models.benefit_grant import BenefitGrantAdsProperties

Expand All @@ -14,7 +14,7 @@ class BenefitAdsService(
async def grant(
self,
benefit: BenefitAds,
user: User,
customer: Customer,
grant_properties: BenefitGrantAdsProperties,
*,
update: bool = False,
Expand All @@ -28,7 +28,7 @@ async def grant(
async def revoke(
self,
benefit: BenefitAds,
user: User,
customer: Customer,
grant_properties: BenefitGrantAdsProperties,
*,
attempt: int = 1,
Expand Down
49 changes: 14 additions & 35 deletions server/polar/benefit/benefits/articles.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@

from polar.auth.models import AuthSubject
from polar.locker import Locker
from polar.models import ArticlesSubscription, Benefit, BenefitGrant, Organization, User
from polar.models import (
ArticlesSubscription,
Benefit,
BenefitGrant,
Customer,
Organization,
User,
)
from polar.models.benefit import BenefitArticles, BenefitArticlesProperties, BenefitType
from polar.models.benefit_grant import BenefitGrantArticlesProperties

Expand All @@ -22,53 +29,25 @@ class BenefitArticlesService(
async def grant(
self,
benefit: BenefitArticles,
user: User,
customer: Customer,
grant_properties: BenefitGrantArticlesProperties,
*,
update: bool = False,
attempt: int = 1,
) -> BenefitGrantArticlesProperties:
async with self._acquire_lock(user):
organization_id = benefit.organization_id

articles_subscription = await self._get_articles_subscription(
user.id, organization_id
)
if articles_subscription is None:
articles_subscription = ArticlesSubscription(
user_id=user.id, organization_id=organization_id
)

articles_subscription.deleted_at = None
articles_subscription.paid_subscriber = benefit.properties["paid_articles"]
self.session.add(articles_subscription)
await self.session.commit()
# This benefit will be deleted before this PR lands, no need to update the logic

return {}

async def revoke(
self,
benefit: BenefitArticles,
user: User,
customer: Customer,
grant_properties: BenefitGrantArticlesProperties,
*,
attempt: int = 1,
) -> BenefitGrantArticlesProperties:
async with self._acquire_lock(user):
organization_id = benefit.organization_id

articles_subscription = await self._get_articles_subscription(
user.id, organization_id
)
if articles_subscription is not None:
# Revoke only if there are no other grant giving access to those articles
# Possible in case of Free -> Premium upgrade or multiple subscriptions
articles_grants = await self._get_articles_grants(
user.id, organization_id
)
if len(articles_grants) == 1:
await self.session.delete(articles_subscription)
await self.session.commit()
# This benefit will be deleted before this PR lands, no need to update the logic

return {}

Expand All @@ -95,13 +74,13 @@ async def _get_articles_subscription(
return result.scalar_one_or_none()

async def _get_articles_grants(
self, user_id: uuid.UUID, organization_id: uuid.UUID
self, customer_id: uuid.UUID, organization_id: uuid.UUID
) -> Sequence[BenefitGrant]:
statement = (
select(BenefitGrant)
.join(BenefitGrant.benefit)
.where(
BenefitGrant.user_id == user_id,
BenefitGrant.customer_id == customer_id,
BenefitGrant.is_granted.is_(True),
Benefit.type == BenefitType.articles,
Benefit.organization_id == organization_id,
Expand Down
22 changes: 11 additions & 11 deletions server/polar/benefit/benefits/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

from polar.auth.models import AuthSubject
from polar.exceptions import PolarError, PolarRequestValidationError, ValidationError
from polar.models import Benefit, Organization, User
from polar.models import Benefit, Customer, Organization, User
from polar.models.benefit import BenefitProperties
from polar.notifications.notification import (
BenefitPreconditionErrorNotificationContextualPayload,
Expand Down Expand Up @@ -93,27 +93,27 @@ def __init__(self, session: AsyncSession, redis: Redis) -> None:
async def grant(
self,
benefit: B,
user: User,
customer: Customer,
grant_properties: BGP,
*,
update: bool = False,
attempt: int = 1,
) -> BGP:
"""
Executes the logic to grant a benefit to a backer.
Executes the logic to grant a benefit to a customer.
Args:
benefit: The Benefit to grant.
user: The backer user.
grant_properties: Stored properties for this specific benefit and user.
customer: The customer.
grant_properties: Stored properties for this specific benefit and customer.
Might be available at this stage if we're updating
an already granted benefit.
update: Whether we are updating an already granted benefit.
attempt: Number of times we attempted to grant the benefit.
Useful for the worker to implement retry logic.
Returns:
A dictionary with data to store for this specific benefit and user.
A dictionary with data to store for this specific benefit and customer.
For example, it can be useful to store external identifiers
that may help when updating the grant or revoking it.
**Existing properties will be overriden, so be sure to include all the data
Expand All @@ -129,23 +129,23 @@ async def grant(
async def revoke(
self,
benefit: B,
user: User,
customer: Customer,
grant_properties: BGP,
*,
attempt: int = 1,
) -> BGP:
"""
Executes the logic to revoke a benefit from a backer.
Executes the logic to revoke a benefit from a customer.
Args:
benefit: The Benefit to revoke.
user: The backer user.
grant_properties: Stored properties for this specific benefit and user.
customer: The customer.
grant_properties: Stored properties for this specific benefit and customer.
attempt: Number of times we attempted to revoke the benefit.
Useful for the worker to implement retry logic.
Returns:
A dictionary with data to store for this specific benefit and user.
A dictionary with data to store for this specific benefit and customer.
For example, it can be useful to store external identifiers
that may help when updating the grant or revoking it.
**Existing properties will be overriden, so be sure to include all the data
Expand Down
6 changes: 3 additions & 3 deletions server/polar/benefit/benefits/custom.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import Any, cast

from polar.auth.models import AuthSubject
from polar.models import Organization, User
from polar.models import Customer, Organization, User
from polar.models.benefit import BenefitCustom, BenefitCustomProperties
from polar.models.benefit_grant import BenefitGrantCustomProperties

Expand All @@ -16,7 +16,7 @@ class BenefitCustomService(
async def grant(
self,
benefit: BenefitCustom,
user: User,
customer: Customer,
grant_properties: BenefitGrantCustomProperties,
*,
update: bool = False,
Expand All @@ -27,7 +27,7 @@ async def grant(
async def revoke(
self,
benefit: BenefitCustom,
user: User,
customer: Customer,
grant_properties: BenefitGrantCustomProperties,
*,
attempt: int = 1,
Expand Down
14 changes: 8 additions & 6 deletions server/polar/benefit/benefits/discord.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from polar.integrations.discord.service import discord_bot as discord_bot_service
from polar.integrations.discord.service import discord_user as discord_user_service
from polar.logging import Logger
from polar.models import Organization, User
from polar.models import Customer, Organization, User
from polar.models.benefit import BenefitDiscord, BenefitDiscordProperties
from polar.models.benefit_grant import BenefitGrantDiscordProperties
from polar.notifications.notification import (
Expand Down Expand Up @@ -70,15 +70,15 @@ class BenefitDiscordService(
async def grant(
self,
benefit: BenefitDiscord,
user: User,
customer: Customer,
grant_properties: BenefitGrantDiscordProperties,
*,
update: bool = False,
attempt: int = 1,
) -> BenefitGrantDiscordProperties:
bound_logger = log.bind(
benefit_id=str(benefit.id),
user_id=str(user.id),
customer_id=str(customer.id),
)
bound_logger.debug("Grant benefit")

Expand All @@ -94,7 +94,9 @@ async def grant(
bound_logger.debug(
"Revoke before granting because guild or role have changed"
)
await self.revoke(benefit, user, grant_properties, attempt=attempt)
await self.revoke(benefit, customer, grant_properties, attempt=attempt)

# TODO: we need to revamp this, since we now need to get an account from a Customer instead of a User

try:
account = await discord_user_service.get_oauth_account(self.session, user)
Expand Down Expand Up @@ -133,14 +135,14 @@ async def grant(
async def revoke(
self,
benefit: BenefitDiscord,
user: User,
customer: Customer,
grant_properties: BenefitGrantDiscordProperties,
*,
attempt: int = 1,
) -> BenefitGrantDiscordProperties:
bound_logger = log.bind(
benefit_id=str(benefit.id),
user_id=str(user.id),
customer_id=str(customer.id),
)

guild_id = grant_properties.get("guild_id")
Expand Down
10 changes: 5 additions & 5 deletions server/polar/benefit/benefits/downloadables.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from polar.auth.models import AuthSubject
from polar.benefit import schemas as benefit_schemas
from polar.logging import Logger
from polar.models import Organization, User
from polar.models import Customer, Organization, User
from polar.models.benefit import BenefitDownloadables, BenefitDownloadablesProperties
from polar.models.benefit_grant import BenefitGrantDownloadablesProperties
from polar.user.service.downloadables import downloadable as downloadable_service
Expand All @@ -35,7 +35,7 @@ class BenefitDownloadablesService(
async def grant(
self,
benefit: BenefitDownloadables,
user: User,
customer: Customer,
grant_properties: BenefitGrantDownloadablesProperties,
*,
update: bool = False,
Expand All @@ -49,7 +49,7 @@ async def grant(
for file_id in file_ids:
downloadable = await downloadable_service.grant_for_benefit_file(
self.session,
user=user,
customer=customer,
benefit_id=benefit.id,
file_id=file_id,
)
Expand All @@ -63,14 +63,14 @@ async def grant(
async def revoke(
self,
benefit: BenefitDownloadables,
user: User,
customer: Customer,
grant_properties: BenefitGrantDownloadablesProperties,
*,
attempt: int = 1,
) -> BenefitGrantDownloadablesProperties:
await downloadable_service.revoke_for_benefit(
self.session,
user=user,
customer=customer,
benefit_id=benefit.id,
)
return {}
Expand Down
14 changes: 9 additions & 5 deletions server/polar/benefit/benefits/github_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
github_repository_benefit_user_service,
)
from polar.logging import Logger
from polar.models import Organization, User
from polar.models import Customer, Organization, User
from polar.models.benefit import (
BenefitGitHubRepository,
BenefitGitHubRepositoryProperties,
Expand Down Expand Up @@ -80,15 +80,15 @@ class BenefitGitHubRepositoryService(
async def grant(
self,
benefit: BenefitGitHubRepository,
user: User,
customer: Customer,
grant_properties: BenefitGrantGitHubRepositoryProperties,
*,
update: bool = False,
attempt: int = 1,
) -> BenefitGrantGitHubRepositoryProperties:
bound_logger = log.bind(
benefit_id=str(benefit.id),
user_id=str(user.id),
user_id=str(customer.id),
)
bound_logger.debug("Grant benefit")

Expand All @@ -98,6 +98,8 @@ async def grant(
repository_name = benefit.properties["repository_name"]
permission = benefit.properties["permission"]

# TODO: we need to revamp this, since we now need to get an account from a Customer instead of a User

# When inviting users: Use the users identity from the "main" Polar GitHub App
oauth_account = user.get_oauth_account(OAuthPlatform.github)
if oauth_account is None or oauth_account.account_username is None:
Expand Down Expand Up @@ -159,14 +161,14 @@ async def grant(
async def revoke(
self,
benefit: BenefitGitHubRepository,
user: User,
customer: Customer,
grant_properties: BenefitGrantGitHubRepositoryProperties,
*,
attempt: int = 1,
) -> BenefitGrantGitHubRepositoryProperties:
bound_logger = log.bind(
benefit_id=str(benefit.id),
user_id=str(user.id),
customer_id=str(customer.id),
)

if benefit.properties["repository_id"]:
Expand All @@ -178,6 +180,8 @@ async def revoke(
repository_owner = benefit.properties["repository_owner"]
repository_name = benefit.properties["repository_name"]

# TODO: we need to revamp this, since we now need to get an account from a Customer instead of a User

oauth_account = user.get_oauth_account(OAuthPlatform.github)
if oauth_account is None or oauth_account.account_username is None:
raise
Expand Down
Loading

0 comments on commit 33c981f

Please sign in to comment.