diff --git a/events.py b/events.py index 3b7c15a4f00f9ab964e6f79bc99548953d7409fb..2e1963458890d2c31cc5bbfde9d044576c3d296e 100644 --- a/events.py +++ b/events.py @@ -10,6 +10,11 @@ def get_event_type(cloud_event: CloudEvent) -> str: def get_event_version(cloud_event: CloudEvent) -> str: return cloud_event.data['message']['attributes']['eventVersion'] +def _validate_event_type(cloud_event: CloudEvent, expected_event_type: str) -> None: + event_type = get_event_type(cloud_event) + if event_type != expected_event_type: + raise ValueError(f'Expected event of type {expected_event_type}, got {event_type}') + @dataclass class UserPayload: id: str @@ -43,13 +48,55 @@ class UserEvent: @dataclass class UserNameUpdatedEvent(UserEvent): def __init__(self, cloud_event: CloudEvent): - if get_event_type(cloud_event) != 'UserNameUpdated': - raise ValueError(f'Expected event of type UserNameUpdated, got {data['eventType']}') + _validate_event_type(cloud_event, 'UserNameUpdated') super().__init__(cloud_event) @dataclass class UserDeletedEvent(UserEvent): def __init__(self, cloud_event: CloudEvent): - if get_event_type(cloud_event) != 'UserDeleted': - raise ValueError(f'Expected event of type UserDeleted, got {data['eventType']}') + _validate_event_type(cloud_event, 'UserDeleted') super().__init__(cloud_event) + +@dataclass +class SpacePayload: + id: str + name: str + slug: str + avatar: str + avatar_default_color: str + + def __init__(self, data: dict): + self.id = data['space']['id'] + self.name = data['space']['name'] + self.slug = data['space']['slug'] + self.avatar = data['space']['avatar'] + self.avatar_default_color = data['space']['avatarDefaultColor'] + +@dataclass +class SpaceEvent: + event_type: str + event_version: str + space: SpacePayload + + def __init__(self, cloud_event: CloudEvent): + self.event_type = get_event_type(cloud_event) + self.event_version = get_event_version(cloud_event) + self.space = SpacePayload(json.loads(base64.b64decode(cloud_event.data['message']['data']))) + +@dataclass +class SpaceCreatedEvent(SpaceEvent): + def __init__(self, cloud_event: CloudEvent): + _validate_event_type(cloud_event, 'SpaceCreated') + super().__init__(cloud_event) + +@dataclass +class SpaceUpdatedEvent(SpaceEvent): + def __init__(self, cloud_event: CloudEvent): + _validate_event_type(cloud_event, 'SpaceUpdated') + super().__init__(cloud_event) + +@dataclass +class SpaceDeletedEvent(SpaceEvent): + def __init__(self, cloud_event: CloudEvent): + _validate_event_type(cloud_event, 'SpaceDeleted') + super().__init__(cloud_event) \ No newline at end of file diff --git a/main.py b/main.py index d828827008b80f7088bd069a5761acdb54d48639..ad19fc6a0909a66d2aaaf51940db77493d75dd29 100644 --- a/main.py +++ b/main.py @@ -1,10 +1,12 @@ import os import functions_framework +from typing import Union from dataclasses import asdict from cloudevents.http.event import CloudEvent -from events import UserNameUpdatedEvent, get_event_type, get_event_version, UserDeletedEvent +from events import UserNameUpdatedEvent, get_event_type, get_event_version, UserDeletedEvent, SpaceCreatedEvent, \ + SpaceUpdatedEvent, SpaceDeletedEvent from typesense_client import TypesenseClient from logging import getLogger @@ -28,13 +30,37 @@ def process_user_name_updated_event(client: TypesenseClient, event: UserNameUpda } } client.upsert(document) - logger.info(f'Updated user {event.user.id} in Typesense') + logger.info(f'Upserted user {event.user.id} in Typesense') def process_user_deleted_event(client: TypesenseClient, event: UserDeletedEvent): logger.debug(f'Processing {event}') client.delete(f"profile_{event.user.id}") logger.info(f'Deleted user {event.user.id} from Typesense') +def process_space_upserting_event(client: TypesenseClient, event: Union[SpaceCreatedEvent, SpaceUpdatedEvent]): + logger.debug(f'Processing {event}') + document = { + 'id': f'space_{event.space.id}', + 'type': 'space', + 'title_de': event.space.name, + 'title_en': event.space.name, + 'description_de': None, # TODO add on sending side + 'description_en': None, # TODO add on sending side + 'location': None, + 'location_lat_lng': None, + 'image_url': event.space.avatar, + 'link_locators': { + 'space': event.space.slug + } + } + client.upsert(document) + logger.info(f'Upserted space {event.space.id} in Typesense') + +def process_space_deleted_event(client: TypesenseClient, event: SpaceDeletedEvent): + logger.debug(f'Processing {event}') + client.delete(f"space_{event.space.id}") + logger.info(f'Deleted space {event.space.id} in Typesense') + def process_event(client: TypesenseClient, event: CloudEvent): type_version = (get_event_type(event), get_event_version(event)) match type_version: @@ -47,16 +73,13 @@ def process_event(client: TypesenseClient, event: CloudEvent): logger.debug('Ignoring UserEmailUpdated event') pass case ('SpaceCreated', '1.0.0'): - # TODO implement - logger.debug('Ignoring SpaceCreated event (yet to be implemented)') + process_space_upserting_event(client, SpaceCreatedEvent(event)) pass case ('SpaceUpdated', '1.0.0'): - # TODO implement - logger.debug('Ignoring SpaceCreated event (yet to be implemented)') + process_space_upserting_event(client, SpaceUpdatedEvent(event)) pass case ('SpaceDeleted', '1.0.0'): - # TODO implement - logger.debug('Ignoring SpaceCreated event (yet to be implemented)') + process_space_deleted_event(client, SpaceDeletedEvent(event)) pass case (eventType, eventVersion): logger.debug(f'Ignoring {eventType} ({eventVersion}) event.') diff --git a/test_main.py b/test_main.py index a5acd3d1a81990968d8255a10067107b788b3966..5ec5729b44cc735f7552c7d22f1b6e50bb669880 100644 --- a/test_main.py +++ b/test_main.py @@ -1,10 +1,10 @@ import base64 import json - from unittest.mock import patch -from cloudevents.http import CloudEvent +# noinspection PyPackageRequirements from cloudevents.http.event import CloudEvent + from main import process_event attributes = { @@ -24,6 +24,14 @@ user_payload = { 'avatar': 'https://ik.imagekit.io/holi/_DEV_/avatar/149a3d29-a0da-4f52-8e1c-660376b7084b_yv31UW7lD0.jpg', } +space_payload = { + 'id': '149a3d29-a0da-4f52-8e1c-660376b7084b', + 'name': 'Daniels Space', + 'slug': 'daniels-space', + 'avatar': 'https://ik.imagekit.io/holi/_DEV_/avatar/149a3d29-a0da-4f52-8e1c-660376b7084b_yv31UW7lD0.jpg', + 'avatarDefaultColor': 'ffffff', +} + def message_data(event_type, event_version, data): return { 'message': { @@ -46,10 +54,10 @@ def test_user_name_updated(mock_client): 'type': 'profile', 'title_de': user_payload['name'], 'title_en': user_payload['name'], - 'description_de': None, # TODO add on sending side - 'description_en': None, # TODO add on sending side - 'location': None, # TODO add on sending side - 'location_lat_lng': None, # TODO add on sending side + 'description_de': None, + 'description_en': None, + 'location': None, + 'location_lat_lng': None, 'image_url': user_payload['avatar'], 'link_locators': { 'profile': user_payload['id'], @@ -61,5 +69,40 @@ def test_user_deleted(mock_client): process_event(mock_client, CloudEvent(attributes, message_data('UserDeleted', '1.0.0', {'user': user_payload}))) mock_client.delete.assert_called_with(f"profile_{user_payload['id']}") -if __name__ == '__main__': - process_event() \ No newline at end of file +# noinspection DuplicatedCode +@patch('typesense.client.Client') +def test_space_created(mock_client): + process_event(mock_client, CloudEvent(attributes, message_data('SpaceCreated', '1.0.0', {'space': space_payload}))) + mock_client.upsert.assert_called_with({ + 'id': f'space_{space_payload['id']}', + 'type': 'space', + 'title_de': space_payload['name'], + 'title_en': space_payload['name'], + 'description_de': None, + 'description_en': None, + 'location': None, + 'location_lat_lng': None, + 'image_url': space_payload['avatar'], + 'link_locators': { + 'space': space_payload['slug'], + } + }) + +# noinspection DuplicatedCode +@patch('typesense.client.Client') +def test_space_updated(mock_client): + process_event(mock_client, CloudEvent(attributes, message_data('SpaceUpdated', '1.0.0', {'space': space_payload}))) + mock_client.upsert.assert_called_with({ + 'id': f'space_{space_payload['id']}', + 'type': 'space', + 'title_de': space_payload['name'], + 'title_en': space_payload['name'], + 'description_de': None, + 'description_en': None, + 'location': None, + 'location_lat_lng': None, + 'image_url': space_payload['avatar'], + 'link_locators': { + 'space': space_payload['slug'], + } + })