-
-
Notifications
You must be signed in to change notification settings - Fork 421
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* feat: add protocol buffers for events * chore: add EventDispatcher * chore: add WebhookEvent class * chore: emit events * feat: initial version of event listener * chore: emit user plan change with new timestamp * feat: emit metrics + add alias status to create event * chore: add newrelic decorator to functions * fix: event emitter fixes * fix: take null end_time into account * fix: avoid double-commits * chore: move UserDeleted event to User.delete method * db: add index to sync_event created_at and taken_time columns * chore: add index to model
- Loading branch information
1 parent
60ab8c1
commit 3e0b7bb
Showing
25 changed files
with
690 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
from abc import ABC, abstractmethod | ||
from app import config | ||
from app.db import Session | ||
from app.errors import ProtonPartnerNotSetUp | ||
from app.events.generated import event_pb2 | ||
from app.models import User, PartnerUser, SyncEvent | ||
from app.proton.utils import get_proton_partner | ||
from typing import Optional | ||
|
||
NOTIFICATION_CHANNEL = "simplelogin_sync_events" | ||
|
||
|
||
class Dispatcher(ABC): | ||
@abstractmethod | ||
def send(self, event: bytes): | ||
pass | ||
|
||
|
||
class PostgresDispatcher(Dispatcher): | ||
def send(self, event: bytes): | ||
instance = SyncEvent.create(content=event, flush=True) | ||
Session.execute(f"NOTIFY {NOTIFICATION_CHANNEL}, '{instance.id}';") | ||
|
||
@staticmethod | ||
def get(): | ||
return PostgresDispatcher() | ||
|
||
|
||
class EventDispatcher: | ||
@staticmethod | ||
def send_event( | ||
user: User, | ||
content: event_pb2.EventContent, | ||
dispatcher: Dispatcher = PostgresDispatcher.get(), | ||
skip_if_webhook_missing: bool = True, | ||
): | ||
if not config.EVENT_WEBHOOK and skip_if_webhook_missing: | ||
return | ||
|
||
partner_user = EventDispatcher.__partner_user(user.id) | ||
if not partner_user: | ||
return | ||
|
||
event = event_pb2.Event( | ||
user_id=user.id, | ||
external_user_id=partner_user.external_user_id, | ||
partner_id=partner_user.partner_id, | ||
content=content, | ||
) | ||
|
||
serialized = event.SerializeToString() | ||
dispatcher.send(serialized) | ||
|
||
@staticmethod | ||
def __partner_user(user_id: int) -> Optional[PartnerUser]: | ||
# Check if the current user has a partner_id | ||
try: | ||
proton_partner_id = get_proton_partner().id | ||
except ProtonPartnerNotSetUp: | ||
return None | ||
|
||
# It has. Retrieve the information for the PartnerUser | ||
return PartnerUser.get_by(user_id=user_id, partner_id=proton_partner_id) |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
from google.protobuf import descriptor as _descriptor | ||
from google.protobuf import message as _message | ||
from typing import ClassVar as _ClassVar, Mapping as _Mapping, Optional as _Optional, Union as _Union | ||
|
||
DESCRIPTOR: _descriptor.FileDescriptor | ||
|
||
class UserPlanChange(_message.Message): | ||
__slots__ = ("plan_end_time",) | ||
PLAN_END_TIME_FIELD_NUMBER: _ClassVar[int] | ||
plan_end_time: int | ||
def __init__(self, plan_end_time: _Optional[int] = ...) -> None: ... | ||
|
||
class UserDeleted(_message.Message): | ||
__slots__ = () | ||
def __init__(self) -> None: ... | ||
|
||
class AliasCreated(_message.Message): | ||
__slots__ = ("alias_id", "alias_email", "alias_note", "enabled") | ||
ALIAS_ID_FIELD_NUMBER: _ClassVar[int] | ||
ALIAS_EMAIL_FIELD_NUMBER: _ClassVar[int] | ||
ALIAS_NOTE_FIELD_NUMBER: _ClassVar[int] | ||
ENABLED_FIELD_NUMBER: _ClassVar[int] | ||
alias_id: int | ||
alias_email: str | ||
alias_note: str | ||
enabled: bool | ||
def __init__(self, alias_id: _Optional[int] = ..., alias_email: _Optional[str] = ..., alias_note: _Optional[str] = ..., enabled: bool = ...) -> None: ... | ||
|
||
class AliasStatusChange(_message.Message): | ||
__slots__ = ("alias_id", "alias_email", "enabled") | ||
ALIAS_ID_FIELD_NUMBER: _ClassVar[int] | ||
ALIAS_EMAIL_FIELD_NUMBER: _ClassVar[int] | ||
ENABLED_FIELD_NUMBER: _ClassVar[int] | ||
alias_id: int | ||
alias_email: str | ||
enabled: bool | ||
def __init__(self, alias_id: _Optional[int] = ..., alias_email: _Optional[str] = ..., enabled: bool = ...) -> None: ... | ||
|
||
class AliasDeleted(_message.Message): | ||
__slots__ = ("alias_id", "alias_email") | ||
ALIAS_ID_FIELD_NUMBER: _ClassVar[int] | ||
ALIAS_EMAIL_FIELD_NUMBER: _ClassVar[int] | ||
alias_id: int | ||
alias_email: str | ||
def __init__(self, alias_id: _Optional[int] = ..., alias_email: _Optional[str] = ...) -> None: ... | ||
|
||
class EventContent(_message.Message): | ||
__slots__ = ("user_plan_change", "user_deleted", "alias_created", "alias_status_change", "alias_deleted") | ||
USER_PLAN_CHANGE_FIELD_NUMBER: _ClassVar[int] | ||
USER_DELETED_FIELD_NUMBER: _ClassVar[int] | ||
ALIAS_CREATED_FIELD_NUMBER: _ClassVar[int] | ||
ALIAS_STATUS_CHANGE_FIELD_NUMBER: _ClassVar[int] | ||
ALIAS_DELETED_FIELD_NUMBER: _ClassVar[int] | ||
user_plan_change: UserPlanChange | ||
user_deleted: UserDeleted | ||
alias_created: AliasCreated | ||
alias_status_change: AliasStatusChange | ||
alias_deleted: AliasDeleted | ||
def __init__(self, user_plan_change: _Optional[_Union[UserPlanChange, _Mapping]] = ..., user_deleted: _Optional[_Union[UserDeleted, _Mapping]] = ..., alias_created: _Optional[_Union[AliasCreated, _Mapping]] = ..., alias_status_change: _Optional[_Union[AliasStatusChange, _Mapping]] = ..., alias_deleted: _Optional[_Union[AliasDeleted, _Mapping]] = ...) -> None: ... | ||
|
||
class Event(_message.Message): | ||
__slots__ = ("user_id", "external_user_id", "partner_id", "content") | ||
USER_ID_FIELD_NUMBER: _ClassVar[int] | ||
EXTERNAL_USER_ID_FIELD_NUMBER: _ClassVar[int] | ||
PARTNER_ID_FIELD_NUMBER: _ClassVar[int] | ||
CONTENT_FIELD_NUMBER: _ClassVar[int] | ||
user_id: int | ||
external_user_id: str | ||
partner_id: int | ||
content: EventContent | ||
def __init__(self, user_id: _Optional[int] = ..., external_user_id: _Optional[str] = ..., partner_id: _Optional[int] = ..., content: _Optional[_Union[EventContent, _Mapping]] = ...) -> None: ... |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.