From 2d81f7671b656fa82f567714a2a6f758ebba877a Mon Sep 17 00:00:00 2001 From: Daniel Bimschas <daniel@bimschas.com> Date: Sat, 7 Dec 2024 23:22:59 +0100 Subject: [PATCH] HOLI-10718: add updating Typesense --- events.py | 6 ++--- main.py | 38 +++++++++++++++++++++++------- requirements.txt | 1 + terraform/environments/function.tf | 9 +++---- test_main.py | 31 ++++++++++++++++++++---- typesense_client.py | 24 +++++++++++++++++++ 6 files changed, 89 insertions(+), 20 deletions(-) create mode 100644 typesense_client.py diff --git a/events.py b/events.py index 1819e3f..dabc674 100644 --- a/events.py +++ b/events.py @@ -27,15 +27,15 @@ class UserPayload: @dataclass -class UserEvent(UserPayload): +class UserEvent: event_type: str event_version: str + user: UserPayload def __init__(self, cloud_event: CloudEvent): self.event_type = get_event_type(cloud_event) self.event_version = get_event_version(cloud_event) - data = cloud_event.data['message']['data'] - super().__init__(json.loads(base64.b64decode(data))) + self.user = UserPayload(json.loads(base64.b64decode(cloud_event.data['message']['data']))) @dataclass class UserNameUpdatedEvent(UserEvent): diff --git a/main.py b/main.py index 1ee6075..4550ddd 100644 --- a/main.py +++ b/main.py @@ -1,23 +1,45 @@ +import os import functions_framework + +from dataclasses import asdict from cloudevents.http.event import CloudEvent + from events import UserNameUpdatedEvent, get_event_type, get_event_version, UserDeletedEvent -from dataclasses import asdict +from typesense_client import TypesenseClient -def process_user_name_updated_event(event: UserNameUpdatedEvent): +def process_user_name_updated_event(client: TypesenseClient, event: UserNameUpdatedEvent): print(f'process_user_name_updated_event: {asdict(event)}') + document = { + 'id': f'profile_{event.user.id}', + 'type': 'profile', + 'title_de': event.user.name, + 'title_en': event.user.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 + 'image_url': event.user.avatar, + 'link_locators': { + 'profile': event.user.id + } + } + response = client.upsert(document) + print(f'process_user_name_updated_event: {response}') -def process_user_deleted_event(event: UserDeletedEvent): +def process_user_deleted_event(client: TypesenseClient, event: UserDeletedEvent): print(f'process_user_deleted_event: {asdict(event)}') + response = client.delete(f"profile_{event.user.id}") + print(f'process_user_deleted_event response: {response}') -def process_event(event: CloudEvent): +def process_event(client: TypesenseClient, event: CloudEvent): type_version = (get_event_type(event), get_event_version(event)) match type_version: case ('UserNameUpdated', '1.0.0'): - return process_user_name_updated_event(UserNameUpdatedEvent(event)) + process_user_name_updated_event(client, UserNameUpdatedEvent(event)) case ('UserDeleted', '1.0.0'): - return process_user_deleted_event(UserDeletedEvent(event)) + process_user_deleted_event(client, UserDeletedEvent(event)) @functions_framework.cloud_event def process_message(event: CloudEvent): - #print(f"Received message: {event}") - process_event(event) + client = TypesenseClient() + process_event(client, event) diff --git a/requirements.txt b/requirements.txt index 6c9d154..3874904 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,3 @@ functions_framework==3.8.2 pytest==8.3.4 +typesense==0.21.0 \ No newline at end of file diff --git a/terraform/environments/function.tf b/terraform/environments/function.tf index 9bc1683..0f63338 100644 --- a/terraform/environments/function.tf +++ b/terraform/environments/function.tf @@ -39,10 +39,11 @@ resource "google_cloudfunctions2_function" "holi-search-integration" { max_instance_request_concurrency = 5 environment_variables = { - ENVIRONMENT = local.environment_name - TYPESENSE_HOST = "t9vemwkibxajcorqp-1.a1.typesense.net" - TYPESENSE_PORT = "443" - TYPESENSE_PROTOCOL = "https" + ENVIRONMENT = local.environment_name + TYPESENSE_HOST = "t9vemwkibxajcorqp-1.a1.typesense.net" + TYPESENSE_PORT = "443" + TYPESENSE_PROTOCOL = "https" + TYPESENSE_COLLECTION_NAME = local.environment_name == "production" ? "production_all" : "staging_all" } vpc_connector = data.terraform_remote_state.holi_infra_state.outputs.vpc_access_connector_name vpc_connector_egress_settings = "PRIVATE_RANGES_ONLY" diff --git a/test_main.py b/test_main.py index c136ddb..a5acd3d 100644 --- a/test_main.py +++ b/test_main.py @@ -1,9 +1,11 @@ import base64 import json +from unittest.mock import patch + from cloudevents.http import CloudEvent from cloudevents.http.event import CloudEvent -from main import process_message +from main import process_event attributes = { 'id': '12833783708309476', @@ -36,9 +38,28 @@ def message_data(event_type, event_version, data): } } -def test_process_message(): - process_message(CloudEvent(attributes, message_data('UserNameUpdated', '1.0.0', {'user': user_payload}))) - process_message(CloudEvent(attributes, message_data('UserDeleted', '1.0.0', {'user': user_payload}))) +@patch('typesense.client.Client') +def test_user_name_updated(mock_client): + process_event(mock_client, CloudEvent(attributes, message_data('UserNameUpdated', '1.0.0', {'user': user_payload}))) + mock_client.upsert.assert_called_with({ + 'id': f'profile_{user_payload["id"]}', + '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 + 'image_url': user_payload['avatar'], + 'link_locators': { + 'profile': user_payload['id'], + } + }) + +@patch('typesense.client.Client') +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__': - test_process_message() \ No newline at end of file + process_event() \ No newline at end of file diff --git a/typesense_client.py b/typesense_client.py new file mode 100644 index 0000000..bd0f640 --- /dev/null +++ b/typesense_client.py @@ -0,0 +1,24 @@ +from typesense.client import Client + +# thin wrapper to simplify main and testing code +class TypesenseClient(object): + client: Client + + def __init__(self): + self.client = Client({ + "api_key": (os.getenv("TYPESENSE_ADMIN_API_KEY")), + "nodes": [ + { + "host": (os.getenv("TYPESENSE_HOST")), + "port": (os.getenv("TYPESENSE_PORT")), + "protocol": (os.getenv("TYPESENSE_PROTOCOL")), + } + ], + "connection_timeout_seconds": 60 * 60, + }) + + def upsert(self, document): + return self.client.collections[os.getenv("TYPESENSE_COLLECTION_NAME")].documents.upsert(document) + + def delete(self, id: str): + return self.client.collections[os.getenv("TYPESENSE_COLLECTION_NAME")].documents[id].delete() \ No newline at end of file -- GitLab