From d55e5a3de70c6633b3dfe68360d055b20b952d27 Mon Sep 17 00:00:00 2001 From: Cole Michaels Date: Mon, 20 Apr 2026 11:31:46 +0900 Subject: [PATCH 1/7] fixed docker health check bug on wrong port, added batch delete, removed double call to init. --- ChatServer/docker-compose.yml | 2 +- .../repository/PendingMessageRepository.kt | 25 ++++++++++++++----- .../main/kotlin/plugins/ConversationRoutes.kt | 1 - .../PendingMessageRepositoryTest.kt | 6 ++--- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/ChatServer/docker-compose.yml b/ChatServer/docker-compose.yml index 385e8b7..c97c3d5 100644 --- a/ChatServer/docker-compose.yml +++ b/ChatServer/docker-compose.yml @@ -6,6 +6,6 @@ services: - "8080:8080" env_file: .env healthcheck: - test: [ "CMD", "curl", "-f", "http://localhost:8000/health" ] + test: [ "CMD", "curl", "-f", "http://localhost:8080/health" ] interval: 30s retries: 3 \ No newline at end of file diff --git a/ChatServer/src/main/kotlin/data/repository/PendingMessageRepository.kt b/ChatServer/src/main/kotlin/data/repository/PendingMessageRepository.kt index b208bdb..08f3d80 100644 --- a/ChatServer/src/main/kotlin/data/repository/PendingMessageRepository.kt +++ b/ChatServer/src/main/kotlin/data/repository/PendingMessageRepository.kt @@ -5,9 +5,11 @@ import org.chatserver.models.ChatMessage import org.slf4j.LoggerFactory import software.amazon.awssdk.services.dynamodb.DynamoDbClient import software.amazon.awssdk.services.dynamodb.model.AttributeValue -import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest +import software.amazon.awssdk.services.dynamodb.model.BatchWriteItemRequest +import software.amazon.awssdk.services.dynamodb.model.DeleteRequest import software.amazon.awssdk.services.dynamodb.model.PutItemRequest import software.amazon.awssdk.services.dynamodb.model.QueryRequest +import software.amazon.awssdk.services.dynamodb.model.WriteRequest import java.time.Instant import java.util.UUID @@ -61,11 +63,22 @@ class PendingMessageRepository(dynamoClient: DynamoDbClient) : AbstractDynamoCom ) } - response.items().forEach { item -> - dynamoClient.deleteItem( - DeleteItemRequest.builder() - .tableName(tableName) - .key(mapOf(partitionKey to item[partitionKey]!!, sortKey to item[sortKey]!!)) + response.items().chunked(25).forEach { chunk -> + dynamoClient.batchWriteItem( + BatchWriteItemRequest.builder() + .requestItems( + mapOf( + tableName to chunk.map { item -> + WriteRequest.builder() + .deleteRequest( + DeleteRequest.builder() + .key(mapOf(partitionKey to item[partitionKey]!!, sortKey to item[sortKey]!!)) + .build(), + ) + .build() + }, + ), + ) .build(), ) } diff --git a/ChatServer/src/main/kotlin/plugins/ConversationRoutes.kt b/ChatServer/src/main/kotlin/plugins/ConversationRoutes.kt index 5ba115f..c2fc4a9 100644 --- a/ChatServer/src/main/kotlin/plugins/ConversationRoutes.kt +++ b/ChatServer/src/main/kotlin/plugins/ConversationRoutes.kt @@ -16,7 +16,6 @@ fun Application.configureConversationRoutes( internalApiKey: String = System.getenv("INTERNAL_API_KEY") ?: error("INTERNAL_API_KEY env var not set"), ) { val conversationRegistry by inject() - conversationRegistry.init() routing { post("/conversations") { diff --git a/ChatServer/src/test/kotlin/data/repository/PendingMessageRepositoryTest.kt b/ChatServer/src/test/kotlin/data/repository/PendingMessageRepositoryTest.kt index 9a4ca09..820ac26 100644 --- a/ChatServer/src/test/kotlin/data/repository/PendingMessageRepositoryTest.kt +++ b/ChatServer/src/test/kotlin/data/repository/PendingMessageRepositoryTest.kt @@ -6,8 +6,8 @@ import io.mockk.verify import org.chatserver.models.ChatMessage import software.amazon.awssdk.services.dynamodb.DynamoDbClient import software.amazon.awssdk.services.dynamodb.model.AttributeValue +import software.amazon.awssdk.services.dynamodb.model.BatchWriteItemRequest import software.amazon.awssdk.services.dynamodb.model.CreateTableRequest -import software.amazon.awssdk.services.dynamodb.model.DeleteItemRequest import software.amazon.awssdk.services.dynamodb.model.DescribeTableRequest import software.amazon.awssdk.services.dynamodb.model.PutItemRequest import software.amazon.awssdk.services.dynamodb.model.QueryRequest @@ -98,7 +98,7 @@ class PendingMessageRepositoryTest { repository.fetchAndClear("bob") - verify(exactly = 2) { dynamoClient.deleteItem(any()) } + verify(exactly = 1) { dynamoClient.batchWriteItem(any()) } } @Test @@ -109,6 +109,6 @@ class PendingMessageRepositoryTest { val messages = repository.fetchAndClear("alice") assertTrue(messages.isEmpty()) - verify(exactly = 0) { dynamoClient.deleteItem(any()) } + verify(exactly = 0) { dynamoClient.batchWriteItem(any()) } } } From 7df025ac88457a7fdb2d49401f2a632108c5c278 Mon Sep 17 00:00:00 2001 From: Cole Michaels Date: Mon, 20 Apr 2026 11:44:44 +0900 Subject: [PATCH 2/7] updated tests and minor refactor. --- PresenceServer/Dockerfile | 2 +- .../__pycache__/presence.cpython-314.pyc | Bin 2633 -> 2633 bytes PresenceServer/app/routers/presence.py | 2 +- .../conftest.cpython-314-pytest-9.0.2.pyc | Bin 3171 -> 3179 bytes ...test_presence.cpython-314-pytest-9.0.2.pyc | Bin 15289 -> 17740 bytes PresenceServer/tests/conftest.py | 16 ++++++++-------- PresenceServer/tests/test_presence.py | 11 +++++++++++ 7 files changed, 21 insertions(+), 10 deletions(-) diff --git a/PresenceServer/Dockerfile b/PresenceServer/Dockerfile index b592cf9..160ebd9 100644 --- a/PresenceServer/Dockerfile +++ b/PresenceServer/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.14-slim AS build +FROM python:3.14-slim WORKDIR /app COPY requirements.txt . diff --git a/PresenceServer/app/routers/__pycache__/presence.cpython-314.pyc b/PresenceServer/app/routers/__pycache__/presence.cpython-314.pyc index 3c35cccc38ab1aeba822109189acc99cb43c822e..d2f221a2a5c4b7f0393620e498b9ee56e0989f76 100644 GIT binary patch delta 22 ccmX>pa#Dm>n~#@^0SH!2c$&$%k=KR`06_5ttpET3 delta 22 ccmX>pa#Dm>n~#@^0SMf$+{iTF$ZNv|06-xHuK)l5 diff --git a/PresenceServer/app/routers/presence.py b/PresenceServer/app/routers/presence.py index e872120..1dc7a15 100644 --- a/PresenceServer/app/routers/presence.py +++ b/PresenceServer/app/routers/presence.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, Depends from redis.asyncio import Redis -from app.dependencies import get_current_user_id, verify_internal_key # get_current_user_id used by GET /{user_id} +from app.dependencies import get_current_user_id, verify_internal_key from app.redis import get_redis from app.repos.presence import set_online, is_online, are_online from app.schemas.presence import HeartbeatRequest, PresenceResponse, BatchPresenceRequest, BatchPresenceResponse diff --git a/PresenceServer/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc b/PresenceServer/tests/__pycache__/conftest.cpython-314-pytest-9.0.2.pyc index d507b9f635b89d2f959e63d945cb14400d276dc4..d0ac97c1c464bf1b6e330f6d36b192b9a31f34bc 100644 GIT binary patch delta 1520 zcmbtT&2Jk;6rYdX@p>J91aD-wYHK$kZk<%QkWyN#wAlzDRiUx=V5w+PWZ4_x)~%yi z8$~^|R=9BBfEjV&KghiY#LX`M0O9~LQmF(S5SM&_RBpVt-nb5x=%H^lZ+`RMo40S~ z_vY8^pV|0gB5o3p$6x&3Sm?!1(Wku&cTFdfA?@@>qI|LU?Hs+>dzxRs1qPlJ$IOuB z(A3U2bdD5bN~3a#PQ)?59LA9as!F<~Lf)bd$&jY8O)I$POFg3=YL>M}Z|#viI@G0C zZ}>hY>8cd79~rGrJSiJ>Z&=v8SH0_P1_7JL)}>OjRjW4rm8D}#47U2Hp8o+q{3&UD zHuaCM!TM7!sEh+BGMR))PJ{BX#I3wc!8Ej9{=;nDlWFkbRr$F2d6)dns6O*7mhNsG zfq|$FeekxN0tAUM-B46(&6e-U1T2E1g!#>_@d0E8d?{698}-L<*jbW;V)16SsY5|C zcYKc(vK(R;vjKphj#h8ZjxTBJeL1IR(ASm=7fd7nI z6*ojQ;8*R6QdtXFk*9ITIZ#7Ztp%-9$mc@}MXlNJ?ziOya3h6&4-_`qp+@y{hl1+X zUxF-N|K`CD3pdqF*I!b-6nCPP<z`ye1hx~k*_2>9+1snBAX-GSxxFtqs*f^59(GiGGg!NXwGIM_ON$@S6v7x zOLp#8JME9X`ew*o5h8H-HBiXiZwo6UzTPw4^6si(ze)QhwKbz318^S&x9VDndofsrd#HShRGnl8CRR`vGt3j2+$W~*g-I3m?P;4QIbxbxc zEQdzvUaQ_|dMok@Eb*E7Ep-sfMGgh0*8Y@wV`~0Vg3cdt!m|4e(Eco8sUK<03f{xN_I3cF delta 1419 zcmZ{j&u<%55XWcU?mn;AKN81j90w(K5}d|$B_wD|U8ShqsDeY)ggv+ySCN%$z-?%v zXB!0x(MaJR2=Bm!6SoK<@h4yxj{Fh_Doa%kh(pDpZY?CZb!P2Yc7bQ@SKl`?yE~ru z**~X$pN%}Ng! zA@67hmHt6|G=Ge@@q4WMe8oHsaN{NMQhq*Wyc9_m z?cqDhk<(o{HgKl~JmXDno0eyKmVRo_-jH_`k6Jm5DYd+HQgx5-%n_skH*qJ+v$J0Jgh@Q>Ps@OzqS(N z6SP+>UAjQU^^evXPU$)6p5YewEkXRLt95eE4QtnFEz>88J0Pcwy12@ zEHc|!TD^AV=1$eWUsq=-oS>`r8A;IFUnW{J&m~%OER!7xg{%7W&?gTLuNPORu(^Wn zCA)dmAm0M%Ua?v-!ajlSWwSMrV}BW0ugKM8_#xn`5r2pd`>0@z+mzkxA0)jNfDteV z)9yR}108`j0HaS@afGyTD0mP_IXrNu20R>aBRnJE#vI{Bl*E7=lh`tcxEZPZ4{jm> zH|D>%F#~Qa=H^_$P4DEGgduQ(vuYO__bZM1`~KDjBl93YW|<1r6ibSQ4^U-U=1Fel zrINgPm4*MZV$u~4Mde8H#t;hpE!H?s1oX~-Z1BQ92nEUpW!nJbLnQRhf7L0kA6~cI z1#H^r3cDFL*hi%+t>#38{Vcks*__LfZ<)v{rJD)2qUesrTQP_Iv|ydFDVtB}r_OhG z+M(_0e!ZdmUbl>+;#P(!r)}F_`CxaevE#p`inL{RYIjLG1|psSdm1c zm=DQ;^r0YqkbFz_WKSjpK|Rcr7D5(85A_lZU+k%KRp#a1-}(K2=XVb0p8wXpPYXor z)@sxgK5w7>ivLqDX$>T{RMp3GdMc$0YY_P`u~AO#=KP^qw!y+?d7YvF{TuMgGD5o2 zwADzHNZMNQnjtr!rq%%8t9_(DUFhs)NE9Z#0hHIgHl_!Ql6~;eJAC5nB!lS(wMR4) zm2?_ryw#|$`m2}$6r3!qS}Gyu?;ykJruv8+TS4#%83kkG4jdYaWyLfk0%fpR>Vqkt zpwvX@Iuh2V7mUw|%uB#g5>#m-1*l!D2kYi>-Cw^vPMo?~ zr8hP%hBc5-kHDJ-75s1?>uG^Z%LI~a3)41A zNnQPOvQaI(V~Z5=bzBR~`AcCxXop2k*K|8Bit#(+1CwJj$qAgJQ(E2{!s+tLvb=ql zw`Ux}c4OvR^uuhXwI{>d_xRo{+Y6T~?w9wGV^J^tfT>+YUXXSr{j$Y_bcbze#^5Z3 zZ;jE)E_5U#67)+jD8aA2)mMfGK_Yo%}w&XOX F{sRGI<(&Wk delta 287 zcmX@p#kjMaPn(aIivb8auiVI7!#R;pf-z>IdJHe4GJ_`1OOS+LGSlP&UXhI(4#}`+ zau@kb78Ed;%xA#H%vGd3d4q=1!1n01-(*VkN_8Ajwdq2PAHB*yQG?l;)(`6=edsj6hs$H~Ei?4c8YQHb&*544?o2 DR8vZ^ diff --git a/PresenceServer/tests/conftest.py b/PresenceServer/tests/conftest.py index 1d6de55..4a15fe8 100644 --- a/PresenceServer/tests/conftest.py +++ b/PresenceServer/tests/conftest.py @@ -7,6 +7,12 @@ from app.redis import get_redis +def _make_redis_override(redis): + async def override(): + return redis + return override + + @pytest_asyncio.fixture async def fake_redis(): redis = FakeRedis() @@ -16,16 +22,13 @@ async def fake_redis(): @pytest_asyncio.fixture async def client(fake_redis): - async def override_get_redis(): - return fake_redis - async def override_get_current_user_id(): return "test-user-id" async def override_verify_internal_key(): return None - app.dependency_overrides[get_redis] = override_get_redis + app.dependency_overrides[get_redis] = _make_redis_override(fake_redis) app.dependency_overrides[get_current_user_id] = override_get_current_user_id app.dependency_overrides[verify_internal_key] = override_verify_internal_key @@ -37,10 +40,7 @@ async def override_verify_internal_key(): @pytest_asyncio.fixture async def unauthed_client(fake_redis): - async def override_get_redis(): - return fake_redis - - app.dependency_overrides[get_redis] = override_get_redis + app.dependency_overrides[get_redis] = _make_redis_override(fake_redis) async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac: yield ac diff --git a/PresenceServer/tests/test_presence.py b/PresenceServer/tests/test_presence.py index 7ab2245..718935a 100644 --- a/PresenceServer/tests/test_presence.py +++ b/PresenceServer/tests/test_presence.py @@ -68,3 +68,14 @@ async def test_batch_presence_wrong_key(unauthed_client): headers={"x-internal-key": "wrongkey"}, ) assert resp.status_code == 403 + + +async def test_health(client): + resp = await client.get("/health") + assert resp.status_code == 200 + assert resp.json() == {"status": "ok"} + + +async def test_heartbeat_missing_key(unauthed_client): + resp = await unauthed_client.post("/presence/heartbeat", json={"user_ids": ["user-1"]}) + assert resp.status_code == 422 From 821196440a5931bb2c0b714ad3035636c02299bb Mon Sep 17 00:00:00 2001 From: Cole Michaels Date: Mon, 20 Apr 2026 11:52:57 +0900 Subject: [PATCH 3/7] remove get_user() removed TokenData, added get_conversation_message() updated tests. --- ApiServer/app/chat_client.py | 11 +++++++++++ ApiServer/app/dependencies.py | 4 +--- ApiServer/app/repos/users.py | 9 --------- ApiServer/app/routers/conversations.py | 15 +++------------ ApiServer/app/schemas/token.py | 4 ---- ApiServer/tests/test_conversations.py | 17 ++++------------- 6 files changed, 19 insertions(+), 41 deletions(-) delete mode 100644 ApiServer/app/repos/users.py diff --git a/ApiServer/app/chat_client.py b/ApiServer/app/chat_client.py index 803a1ca..b1a3fe0 100644 --- a/ApiServer/app/chat_client.py +++ b/ApiServer/app/chat_client.py @@ -36,3 +36,14 @@ async def remove_conversation_member(conversation_id: str, user_id: str): ) if response.status_code != 204: raise HTTPException(status_code=502, detail="Failed to remove member on chat server") + + +async def get_conversation_messages(conversation_id: str) -> list: + async with httpx.AsyncClient() as client: + response = await client.get( + f"{settings.chat_server_url}/conversations/{conversation_id}/messages", + headers=_INTERNAL_HEADERS, + ) + if not response.is_success: + raise HTTPException(status_code=502, detail="Failed to fetch messages") + return response.json() diff --git a/ApiServer/app/dependencies.py b/ApiServer/app/dependencies.py index 7321b37..2bdd950 100644 --- a/ApiServer/app/dependencies.py +++ b/ApiServer/app/dependencies.py @@ -7,7 +7,6 @@ from app.config import settings from app.database import AsyncSessionLocal from app.models.user import User -from app.schemas.token import TokenData oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login") @@ -31,11 +30,10 @@ async def get_current_user( user_id: str = payload.get("sub") if user_id is None: raise credentials_exception - token_data = TokenData(user_id=user_id) except JWTError: raise credentials_exception - result = await db.execute(select(User).where(User.id == token_data.user_id)) + result = await db.execute(select(User).where(User.id == user_id)) user = result.scalar_one_or_none() if user is None: raise credentials_exception diff --git a/ApiServer/app/repos/users.py b/ApiServer/app/repos/users.py deleted file mode 100644 index f8e21cb..0000000 --- a/ApiServer/app/repos/users.py +++ /dev/null @@ -1,9 +0,0 @@ -from sqlalchemy import select -from sqlalchemy.ext.asyncio import AsyncSession - -from app.models.user import User - - -async def get_user(user_id: str, db: AsyncSession) -> User | None: - result = await db.execute(select(User).where(User.id == user_id)) - return result.scalar_one_or_none() diff --git a/ApiServer/app/routers/conversations.py b/ApiServer/app/routers/conversations.py index 277225a..532be6a 100644 --- a/ApiServer/app/routers/conversations.py +++ b/ApiServer/app/routers/conversations.py @@ -1,8 +1,6 @@ -import httpx +from fastapi import APIRouter, Depends -from fastapi import APIRouter, Depends, HTTPException - -from app.config import settings +from app.chat_client import get_conversation_messages from app.dependencies import get_current_user from app.models.user import User @@ -14,11 +12,4 @@ async def get_messages( conversation_id: str, _: User = Depends(get_current_user), ): - async with httpx.AsyncClient() as client: - resp = await client.get( - f"{settings.chat_server_url}/conversations/{conversation_id}/messages", - headers={"x-internal-key": settings.internal_api_key}, - ) - if not resp.is_success: - raise HTTPException(status_code=502, detail="Failed to fetch messages") - return resp.json() + return await get_conversation_messages(conversation_id) diff --git a/ApiServer/app/schemas/token.py b/ApiServer/app/schemas/token.py index 6b41ca7..3fded6a 100644 --- a/ApiServer/app/schemas/token.py +++ b/ApiServer/app/schemas/token.py @@ -4,7 +4,3 @@ class Token(BaseModel): access_token: str token_type: str - - -class TokenData(BaseModel): - user_id: str | None = None diff --git a/ApiServer/tests/test_conversations.py b/ApiServer/tests/test_conversations.py index a393124..3400edc 100644 --- a/ApiServer/tests/test_conversations.py +++ b/ApiServer/tests/test_conversations.py @@ -1,6 +1,5 @@ -from unittest.mock import AsyncMock, MagicMock, patch - import pytest +from unittest.mock import AsyncMock, patch from tests.conftest import register_and_login @@ -11,17 +10,9 @@ def mock_chat_server_messages(): {"fromUserId": "alice-id", "conversationId": "conv-1", "content": "hello", "sentAt": "2024-01-01T00:00:00Z"}, {"fromUserId": "bob-id", "conversationId": "conv-1", "content": "hi", "sentAt": "2024-01-01T00:00:01Z"}, ] - mock_response = MagicMock() - mock_response.is_success = True - mock_response.json.return_value = messages - - mock_client = AsyncMock() - mock_client.__aenter__ = AsyncMock(return_value=mock_client) - mock_client.__aexit__ = AsyncMock(return_value=False) - mock_client.get = AsyncMock(return_value=mock_response) - - with patch("app.routers.conversations.httpx.AsyncClient", return_value=mock_client): - yield mock_response.json.return_value + with patch("app.routers.conversations.get_conversation_messages", new_callable=AsyncMock) as mock: + mock.return_value = messages + yield messages async def test_get_messages(client, mock_chat_server_messages): From c3425e2ba00fdbbc60716027015afce2036af0e3 Mon Sep 17 00:00:00 2001 From: Cole Michaels Date: Mon, 20 Apr 2026 12:04:02 +0900 Subject: [PATCH 4/7] remove get_user() removed TokenData, added get_conversation_message() updated tests. --- .../__pycache__/chat_client.cpython-314.pyc | Bin 3317 -> 4350 bytes .../__pycache__/dependencies.cpython-314.pyc | Bin 2588 -> 2453 bytes .../__pycache__/conversations.cpython-314.pyc | Bin 1821 -> 1016 bytes .../schemas/__pycache__/token.cpython-314.pyc | Bin 1149 -> 726 bytes ...conversations.cpython-314-pytest-9.0.2.pyc | Bin 5222 -> 4854 bytes 5 files changed, 0 insertions(+), 0 deletions(-) diff --git a/ApiServer/app/__pycache__/chat_client.cpython-314.pyc b/ApiServer/app/__pycache__/chat_client.cpython-314.pyc index bd3cf3a8cc817ae4ca07ae2280e3fd49745a6f25..a365aaf09a49acc8161accc102f559646ac408fe 100644 GIT binary patch delta 1102 zcmZ{j%}*0i5Wx5CZoAvv(w5LtXzdr@OGzjQe(_;wqA|n(A`&l3YP&#V`N-SC0TUsb z7>x&mkBf=%;75dbF{z1(@lyT*4kVa{c=HY+q&IbD3y37V?ECG!nR)YO-^}h;|Bg7K zjxvRVackvQ`hm3Us3LT^alBmha>I5nmvLp4VN%O!Wy>$Wk~P*(CW#}LBTGE-vJNrk zFN3XxiX_z&S$!-yGd-_UBauzdOw&kiQB0Bm2GHhgoSd^`XWOC|FZ^M$hUL)1d)G z5aC4#h$!G#vh0Z(CNcI=eS4T(&ms>l=4j>NmIZ2sh55||E7h}ebeh^gb86yfgK1z= zOI(qYiGLUI<>n5)zK@tI>I8EVZ~t(}AUuZ2?>%I1Y$F00CG54en{OA{PisBl*&QVk zG2>~umI)yaRqydT- zyV9SE}4YD zrCL-#P)&%J`srUQVV#aTsTuFi@P}IQqUTx9^UE8e zZ`aA%?B4svJviV|O}vM^Cvm_0K>%sBH6F3#y(Aux^Hn0sXH-z<8%aDQ=9@ey_XdIM z0RqSnaVudmm4FaNvG+`FM!T09yM&-&9#^EuShQqbs?8X?FB zOtp7=4<_G*Lx>L^;XDXQz9y9+#*ZZfvV%*h4pR}a*926>%LBm-#)KI6;cD0dZEzjY zfqw;)d`~_LkI|n5f0NCPaVk@SzJxN>v&1x!5qOfISj=Yqk;93aZCC4VyUWn4b((Iw z>M-YkAzFM#xs!6DnO}uA^O&ar1;PKh?8r~93(!l{Jx7>Cf)~_66RM3NXG<^{h3r;#~sm;IS}QMCJKyy07;8UCuCDs%udiU!}-mn;7CtXM&d mp!R+MHLB@;hsuiM+9xfxiWTum7w;|c2feI*BxKkJzw;a4JYt~$ diff --git a/ApiServer/app/__pycache__/dependencies.cpython-314.pyc b/ApiServer/app/__pycache__/dependencies.cpython-314.pyc index 03483a0af8803d33b4dd7fb1b745719de018e1a5..593214e9f9f19f6dfa9ca5f928270c7960a68631 100644 GIT binary patch delta 605 zcmYLHO=uHA6n?YWo!M-&Ys}`SxY0BXO=&%7q9VmAgl!cHmSx#v( z=Nc85Io1Ekb{--dDJCyt?0l9|Xf4CxAm#h5?d@*ViUQxK7N>+Fta5h`enSqWkA(X& zW(*TNepRzWg~wkQuy#Z^I_64PmxMnt%;N*Tj^D8b`4C&+At%Qgig~sWOIF5X6u0eI zLQ(q1JH=S>KYB8ItxPG3r#K~cs8~7d;X4U{B6w08dAI@-u##b?@iZcA4tG(Sy~GYn zl1X-vYN$9$w&5v z%p%OblX)^sK*}0wtfrpPZ+)uS`{x$+&BX(Kk*%uNuG~a_!dZUoe#tJQXARvp;O&Km z#FO0`vP<%Ay~y#NVc5mQUQywdW24z>a+?xmuE7eK{>(9z7) h`K|6ouM^y$*EuJCLAb=;CoAkna#H6b8BJ`Y{|ojsdUyZ; delta 781 zcmYLHO=}ZD7@kS?D<7N0=Cd{ZT$+#;D%Gf9i&)Zw3dZfS^pv=)$;K4ZY@MCpQSsDk z7`%yyCp{OziwDh76r@67tp}w)AQeCG=FHY5FUvg7`_8j3^Sm>UlTT8y#h4rhG+w(O zwl4AgSPs7H7w>J}0$S)=@@fjFra_&b1zK2(T!c&r08VSsEWnvMoCWoC`G3{RGJbY0 zpvhUF#j>E2=s=v+;tD7w`rnCMf^JB+?^@k;6PtTISb=NH=1Cz96>R;!=rw_M$yy1AcJ z4`h}65LeD`z`xD{?eJ*2asfVH8?Pwf?aEq&CIbOh%G^NAGp@uc%fZ1iM|riBBe$g& z8>h^joh7Hywzf@Y$#a01eOI>49&VpCye$i{qj_h12@t!c6C2X%w%zP?tjp*Mt+7|? V+#r>Z68s8^q!pTz{deU3^AA#YrUn22 diff --git a/ApiServer/app/routers/__pycache__/conversations.cpython-314.pyc b/ApiServer/app/routers/__pycache__/conversations.cpython-314.pyc index 931a930764284a3b1186c9d9963d3a6293d30002..d26e67531658c55e2da19a30a9bb292125b95141 100644 GIT binary patch delta 540 zcmYjNO-oxr6utL-n!Na;p~k0`1`Py4z=at=yb>U8{pzgeb5jt?sy=Ug0xo76ky|>+h+aCv7>jxL%5q4bf zzPd>iwy&d4?}IBhBG~hbeNNceBx5^ycWggS4*t1D+3?ct>!hBjO*( XUBGPj+?qqf}}O(|6nAql09RHbHHB2*<^ne`;z;@H;A z7$reSp&(Jjq2dBJgiw*njU(5D3rDw&lxS%W2oUNmA*i`<;EkPala4)a-)G*udEfqI zhY7e2t!?-_3cw$d2v=km+5R4pWw;6snE)Zb$^^+PLdmN_&1*vIl{|GKlGlaa!nKKL z-VjC$k4(h!rZ9V9MmYwK?nDQPW1K--a*6m%yzpEb-wU1C5NXph>M@uxLh2N8%x-Yv z-B3wY$k48e=x|6M?3nZpw}6l^%nw76zBL- zkNwf!1Fm2C*q*1H7w0JVq)WSQfrhw>d;28>%P<93mDfH4C?W3%yhF;wA=4;nFGrc) z4H$r}psVUbNe9+bwgTefi&QQcGf<9B=(9C8Uld%L%>?J{a&4ZXM-^%pSnV411h>b_ zMR9473p#I)*8NMAp|^IiUbk5*f_#4f2J4GK!gY()YE2XcbzSx>UfqG4A3?ATI}j(` zTN2#wJhZ9e_s?j2D}vu`l!N!!5hUazKgGTMHNGzcy{P`g>B8QVcu1~e5axWCxN1Xb zsIC%n&Jzc&VfYrvvU(w6LS30s3SkYm)mv~{4~c@b|I|ugl}`aeA<>rYh1p$VE-4FP+6rY(MD|W-txTrW!ZV@l z$v2EeA4K88b=>E0pGiZ1+rAb?CkiG!wuEs$0>07e8XNi++Wc(xR50rNS$x!*0Mr zjdLua26w$$i3WO!3Ot&N1(C84^@Sil$``BNc*Vz_3{-4TCKHD>kM-(eYhszY4OR(K zp?$Yl_g#ELVEWwU4bQ{AW3m7O?Hb3H*BHhWm<(y4d+mEQMtPlMT;TZebebKIvV

M&%Svl`)2dt=w<}E(%YbRnLD7S%p2N{3aLK4YSV;7>gLGJ zksBlTlRdZ9=FsF?@?z7txDmH*p8ewNm%~l1YsZ+r;59h?8m7pKGTqB^jmG9Hv#z zr<_Hlz9rf7nEMJJlry(AvjV+6c36+RPX_--U;#L6dc2);UJa&6bR6jQpC6MLa-N z#f(70p@?^K9+O;x0FYh83MBkAnQw6vRHh{6m1HItF~fw51VJpYDqbM#7Kcr4eoARh ls$G#3kP9-YSOiFXU}j`wyvrc>g^h_(q{H(Y1CRpi0sv{)AFTiY delta 565 zcmX|;&r1SP5XWbBS6!pEpK66{KLkUH2=Z1$hakFmctk{G%c346(|EoPom}crmmujM z=;|%#?p<~wiRbKuvs<|0-fklp(CrJIH|j;oE?*+dkk zK^SomfgEOZ&I2f21e1^sHMSnGd6&c#4m43(l&K+MO-8PFG`l`Tu?!IMtO+eeG1bu}pJ)BhajhtwQbF?5)0*t3rSlT*bHXqc$u)hjc<~{GwA5>#VpOpJUS`36;;~HOCCx46FOD6 zlg?d!m~XBeRtv}F;;NjcEswG`MR?Z{@eS-&P{YxS%@)wo>O-8o3V)H@WV^y2w;*Kc diff --git a/ApiServer/tests/__pycache__/test_conversations.cpython-314-pytest-9.0.2.pyc b/ApiServer/tests/__pycache__/test_conversations.cpython-314-pytest-9.0.2.pyc index 4418d77087c505578eac2f129a31445d309045d9..c4e263833dd3e83c00e467baf118de1d014eff71 100644 GIT binary patch delta 916 zcmY+CT~E_c7{||PyRO~VZcJoMUItEH%F9pzV|-tN$)*X>ndl8;mRYd6b<61)f+iwr zVxrzG^%HpKoe7D=7%wn>0Aetb-f&?s@eag@7hZUtAx2NzU;q7|^PHFG>6h*w9cr(t z_z-JXpRJpHLRD?VwA!`bz-f_+<0!+Wy(x)GDVfSK#J>5oFXgBH7|Mzl8X@lgmJuRo z=#NE}j3`YMd)4?OCh(_9*uXRkx1?GX61ccP3{OH+Pru6>R;r zE~6QA564hOfoq8084BOdMXo)l#L;oS1(|r5H?gKV^W5FJ+k<+!oFJtNHHe+am#k^_ z)~Q*t?8KBobN@QIqG8+mlwoVw4OqslTwX5}^ofEos__ICBd`-UKnz+TR&H7^R19_o zX*bLjilzKb8wRztxg!JDOGW23Kjy1rhL=6I>BNLH=Y7HD;K<6Qu}^aLqnusVjn(D@ zD^he-ZFsD1dysD&vmSPTkh8z+CKh);k&NP({-nqA#>do>$DeGFmqKzfy6{tYFNzWy zLuBWj|G6wM>GJGgPBHMb-4$-wC5tpOQwzCbzF-;_C9oZ`7Z7ox%30j%-YK?@PC*dy7Y34LJi{V8ja3J!5`%Z8{(}u*o;;t)5hj;25GYLtu;>ID@p%pRneN#(UC5=cZjNHEJJwQmI;WD=IzJ<^lUJVTOVQ=$8{k}9*&sX3M$I6@$Idia9nX`jK-$M^0>U&)D zXPArdvP&7iut?~+$H-6_N||$YoQ$ie{6fC&VO%P#R^?%)ugc z4xuEnI2EOk>R;foz^9Of^Sp(%xO)fR>b$FAFG5V(B6j6Qb)#io`XpOzHg(cyQIj}& ztzq9_eU(}b+tELywAt4EO<&ruOq;G~5`h#v*>WmQt5!1|#|wPqG;9)O!-OTl<|>sc z^EFAO;z^*}7G;vJn{<}IX|R(NH`$xjy6Je)O?I3WVmeJ0(e%Q;S@WYg5Gw6{!(Erh zm=f9J{NM)p3!NjAZn0b1Q8V3AKakv+)(3&ab|BFYWFC!9xY=%TH#L2?*iT*P7W;wm z-Q@AR`F?U9jH9~=_1i1`#6q`t`<+LU@^kFmgN1>#_KUQ(qf88x^tO^7D7kGV_fX6| z<&k_DvsrdOl-B-tCRqxfFN)(!7;ni(7jwa_j0`##UOX>t%}Go6{@46Zf&f)TWcNM! zjs!(RMj6~-_^ULDfq9HUi+jTUXA<~0G66`r_rh=EDfho{9VgrmB4a_Qtcm-dMq+`^ zIFl<@kJTed`py8zh^T-x;5C2-$TDcMADNtlfgC^woC3TKcmwbzU={%XfEFTo&?&%M zfHQzO#KMz#kY^bjDC6n0-$=rp8kq?$v5_fvb>zqBh$miaSsT={9dgQjWpuU!vAt-^ zwkS0ns>2!kwc;0(UlX3tya{uj0`v=U9Tb86^Fr%Zo7Jqjzg4g&1j2oXo-iVkVDQ9q zn~m$O4f8x%VJ6!pE1}MLc^1q?z? Date: Mon, 20 Apr 2026 12:04:33 +0900 Subject: [PATCH 5/7] cleaned up types removed a forced render. --- ChatClient/src/components/chat/MessageBubble.tsx | 13 +++---------- ChatClient/src/types/index.ts | 6 ------ 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/ChatClient/src/components/chat/MessageBubble.tsx b/ChatClient/src/components/chat/MessageBubble.tsx index 55dfb55..c7dbcca 100644 --- a/ChatClient/src/components/chat/MessageBubble.tsx +++ b/ChatClient/src/components/chat/MessageBubble.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState } from 'react' +import { useEffect } from 'react' import type { StoredMessage } from '../../types' import { useUserCacheStore } from '../../store/userCacheStore' import { getUser } from '../../lib/api' @@ -21,23 +21,16 @@ function formatTime(sentAt: string): string { export function MessageBubble({ message, currentUserId }: MessageBubbleProps) { const isMine = message.fromUserId === currentUserId - const getCache = useUserCacheStore((s) => s.getUser) const addUser = useUserCacheStore((s) => s.addUser) const cached = useUserCacheStore((s) => s.cache[message.fromUserId]) - const [, forceRender] = useState(0) useEffect(() => { if (!cached) { - getUser(message.fromUserId) - .then((user) => { - addUser(user) - forceRender((n) => n + 1) - }) - .catch(() => {}) + getUser(message.fromUserId).then(addUser).catch(() => {}) } }, [message.fromUserId]) - const username = getCache(message.fromUserId)?.username ?? message.fromUserId + const username = cached?.username ?? message.fromUserId return (
diff --git a/ChatClient/src/types/index.ts b/ChatClient/src/types/index.ts index 665f264..6327e37 100644 --- a/ChatClient/src/types/index.ts +++ b/ChatClient/src/types/index.ts @@ -17,12 +17,6 @@ export interface GroupResponse { created_at: string } -export interface GroupMemberResponse { - group_id: string - user_id: string - joined_at: string -} - export interface StoredMessage { fromUserId: string conversationId: string From 756d8132fc066179b8f136a1626dd394f6b7cfe6 Mon Sep 17 00:00:00 2001 From: Cole Michaels Date: Mon, 20 Apr 2026 12:17:24 +0900 Subject: [PATCH 6/7] removed terraform.tfvars update hardcoded project names. --- ChatInfra/modules/dynamodb/main.tf | 10 +++++----- ChatInfra/terraform.tfvars | 3 --- 2 files changed, 5 insertions(+), 8 deletions(-) delete mode 100644 ChatInfra/terraform.tfvars diff --git a/ChatInfra/modules/dynamodb/main.tf b/ChatInfra/modules/dynamodb/main.tf index 5867f2e..0940369 100644 --- a/ChatInfra/modules/dynamodb/main.tf +++ b/ChatInfra/modules/dynamodb/main.tf @@ -12,7 +12,7 @@ resource "aws_dynamodb_table" "server_registry" { enabled = true } - tags = { Name = "chatsystem-server-registry" } + tags = { Name = "${var.project_name}-server-registry" } } resource "aws_dynamodb_table" "user_connections" { @@ -29,7 +29,7 @@ resource "aws_dynamodb_table" "user_connections" { enabled = true } - tags = { Name = "chatsystem-user-connections" } + tags = { Name = "${var.project_name}-user-connections" } } resource "aws_dynamodb_table" "conversation_members" { @@ -58,7 +58,7 @@ resource "aws_dynamodb_table" "conversation_members" { enabled = true } - tags = { Name = "chatsystem-conversation-members" } + tags = { Name = "${var.project_name}-conversation-members" } } resource "aws_dynamodb_table" "messages" { @@ -81,7 +81,7 @@ resource "aws_dynamodb_table" "messages" { enabled = true } - tags = { Name = "chatsystem-messages" } + tags = { Name = "${var.project_name}-messages" } } resource "aws_dynamodb_table" "pending_messages" { @@ -104,5 +104,5 @@ resource "aws_dynamodb_table" "pending_messages" { enabled = true } - tags = { Name = "chatsystem-pending-messages" } + tags = { Name = "${var.project_name}-pending-messages" } } diff --git a/ChatInfra/terraform.tfvars b/ChatInfra/terraform.tfvars deleted file mode 100644 index 55731da..0000000 --- a/ChatInfra/terraform.tfvars +++ /dev/null @@ -1,3 +0,0 @@ -aws_region = "us-west-1" -project_name = "chatsystem" -db_name = "chatsystem" \ No newline at end of file From 70275f81959a94a7e31f04bd202ebb6302c10953 Mon Sep 17 00:00:00 2001 From: Cole Michaels Date: Tue, 21 Apr 2026 13:35:47 +0900 Subject: [PATCH 7/7] ran fmt. --- .idea/workspace.xml | 32 ++++++++----------- .../repository/PendingMessageRepository.kt | 19 +++++------ 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/.idea/workspace.xml b/.idea/workspace.xml index d2358c7..1aa3f63 100644 --- a/.idea/workspace.xml +++ b/.idea/workspace.xml @@ -2,13 +2,7 @@ - - - - - - - + - { + "keyToString": { + "Gradle./home/cole/Projects/ChatSystem/ChatServer [ktlintFormat].executor": "Run", + "Gradle./home/cole/Projects/ChatSystem/ChatServer [test clean].executor": "Run", + "ModuleVcsDetector.initialDetectionPerformed": "true", + "RunOnceActivity.ShowReadmeOnStart": "true", + "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true", + "RunOnceActivity.git.unshallow": "true", + "git-widget-placeholder": "feature/cleanup-codebase-for-stability", + "kotlin-language-version-configured": "true", + "last_opened_file_path": "/home/cole/Projects/ChatSystem" } -}]]> +} diff --git a/ChatServer/src/main/kotlin/data/repository/PendingMessageRepository.kt b/ChatServer/src/main/kotlin/data/repository/PendingMessageRepository.kt index 08f3d80..9baf64e 100644 --- a/ChatServer/src/main/kotlin/data/repository/PendingMessageRepository.kt +++ b/ChatServer/src/main/kotlin/data/repository/PendingMessageRepository.kt @@ -68,15 +68,16 @@ class PendingMessageRepository(dynamoClient: DynamoDbClient) : AbstractDynamoCom BatchWriteItemRequest.builder() .requestItems( mapOf( - tableName to chunk.map { item -> - WriteRequest.builder() - .deleteRequest( - DeleteRequest.builder() - .key(mapOf(partitionKey to item[partitionKey]!!, sortKey to item[sortKey]!!)) - .build(), - ) - .build() - }, + tableName to + chunk.map { item -> + WriteRequest.builder() + .deleteRequest( + DeleteRequest.builder() + .key(mapOf(partitionKey to item[partitionKey]!!, sortKey to item[sortKey]!!)) + .build(), + ) + .build() + }, ), ) .build(),