Coverage for polar/models/external_event.py: 91%
34 statements
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 17:15 +0000
« prev ^ index » next coverage.py v7.10.6, created at 2025-12-05 17:15 +0000
1from datetime import datetime 1ab
2from enum import StrEnum 1ab
3from typing import Any, Literal 1ab
5import stripe as stripe_lib 1ab
6from sqlalchemy import ( 1ab
7 TIMESTAMP,
8 Boolean,
9 ColumnElement,
10 String,
11 UniqueConstraint,
12 type_coerce,
13)
14from sqlalchemy.dialects.postgresql import JSONB 1ab
15from sqlalchemy.ext.hybrid import hybrid_property 1ab
16from sqlalchemy.orm import Mapped, mapped_column 1ab
18from polar.kit.db.models.base import RecordModel 1ab
19from polar.kit.extensions.sqlalchemy.types import StrEnumType 1ab
22class ExternalEventSource(StrEnum): 1ab
23 stripe = "stripe" 1ab
26class ExternalEvent(RecordModel): 1ab
27 __tablename__ = "external_events" 1ab
28 __table_args__ = (UniqueConstraint("source", "external_id"),) 1ab
30 source: Mapped[ExternalEventSource] = mapped_column( 1ab
31 StrEnumType(ExternalEventSource), nullable=False, index=True
32 )
33 handled_at: Mapped[datetime | None] = mapped_column( 1ab
34 TIMESTAMP(timezone=True), nullable=True, default=None, index=True
35 )
36 task_name: Mapped[str] = mapped_column(String, nullable=False, index=True) 1ab
37 external_id: Mapped[str] = mapped_column(String, nullable=False, index=True) 1ab
38 data: Mapped[dict[str, Any]] = mapped_column("data", JSONB, nullable=False) 1ab
40 @hybrid_property 1ab
41 def is_handled(self) -> bool: 1ab
42 return self.handled_at is not None
44 @is_handled.inplace.expression 1ab
45 @classmethod 1ab
46 def _is_handled_expression(cls) -> ColumnElement[bool]: 1ab
47 return type_coerce(cls.handled_at.is_not(None), Boolean)
49 __mapper_args__ = { 1ab
50 "polymorphic_on": "source",
51 }
54class StripeEvent(ExternalEvent): 1ab
55 source: Mapped[Literal[ExternalEventSource.stripe]] = mapped_column( # pyright: ignore 1ab
56 use_existing_column=True, default=ExternalEventSource.stripe
57 )
59 @property 1ab
60 def stripe_data(self) -> stripe_lib.Event: 1ab
61 return stripe_lib.Event.construct_from(self.data, key=None)
63 __mapper_args__ = { 1ab
64 "polymorphic_identity": ExternalEventSource.stripe,
65 "polymorphic_load": "inline",
66 }