Skip to content
This repository has been archived by the owner on Apr 14, 2024. It is now read-only.

Commit

Permalink
Hotfix/disable state (#225)
Browse files Browse the repository at this point in the history
  • Loading branch information
OlegYurchik authored Nov 28, 2023
2 parents 6d78892 + 53a6d0c commit 62a6a66
Show file tree
Hide file tree
Showing 33 changed files with 389 additions and 201 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
**/*_pb2.py
**/*_pb2_grpc.py
**/*.proto
**/*.pyc
*.db
*.pyc
*.sqlite3
.env
.idea
Expand Down
44 changes: 22 additions & 22 deletions autotests/flow/test_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,47 +477,47 @@ async def test_waiting_email_about_accept_third_request_to_join(

@pytest.mark.dependency(depends=["TestProjectFlow::test_get_third_accepted_request_to_join"])
@pytest.mark.asyncio
async def test_create_random_request_to_join(
async def test_create_owner_request_to_join(
self,
random_id: uuid.UUID,
random_projects_rest_client: ProjectsRestClient,
oleg_id: uuid.UUID,
oleg_projects_rest_client: ProjectsRestClient,
):
position_id: uuid.UUID = self.CONTEXT["position_id"]

participant = await random_projects_rest_client.create_request_to_join_position(
participant = await oleg_projects_rest_client.create_request_to_join_position(
position_id=position_id,
)

self.CONTEXT["new_participant_id"] = participant.id

assert participant.position_id == position_id
assert participant.user_id == random_id
assert participant.user_id == oleg_id
assert participant.status == ParticipantStatusEnum.REQUEST

@pytest.mark.dependency(depends=["TestProjectFlow::test_create_random_request_to_join"])
@pytest.mark.dependency(depends=["TestProjectFlow::test_create_owner_request_to_join"])
@pytest.mark.asyncio
async def test_get_random_request_to_join(
async def test_get_owner_request_to_join(
self,
random_id: uuid.UUID,
random_projects_rest_client: ProjectsRestClient,
oleg_id: uuid.UUID,
oleg_projects_rest_client: ProjectsRestClient,
):
position_id: uuid.UUID = self.CONTEXT["position_id"]
participant_id: uuid.UUID = self.CONTEXT["new_participant_id"]

participant = await random_projects_rest_client.get_participant(
participant = await oleg_projects_rest_client.get_participant(
participant_id=participant_id,
)

assert participant.id == participant_id
assert participant.position_id == position_id
assert participant.user_id == random_id
assert participant.user_id == oleg_id
assert participant.status == ParticipantStatusEnum.REQUEST

@pytest.mark.dependency(depends=["TestProjectFlow::test_get_random_request_to_join"])
@pytest.mark.dependency(depends=["TestProjectFlow::test_get_owner_request_to_join"])
@pytest.mark.asyncio
async def test_random_accept_request_to_join(
async def test_owner_accept_request_to_join(
self,
random_id: uuid.UUID,
oleg_id: uuid.UUID,
oleg_projects_rest_client: ProjectsRestClient,
):
position_id: uuid.UUID = self.CONTEXT["position_id"]
Expand All @@ -530,29 +530,29 @@ async def test_random_accept_request_to_join(

assert participant.id == participant_id
assert participant.position_id == position_id
assert participant.user_id == random_id
assert participant.user_id == oleg_id
assert participant.status == ParticipantStatusEnum.JOINED

@pytest.mark.dependency(depeds=["TestProjectFlow::test_random_accept_request_to_join"])
@pytest.mark.dependency(depeds=["TestProjectFlow::test_owner_accept_request_to_join"])
@pytest.mark.asyncio
async def test_get_random_accepted_request_to_join(
async def test_get_owner_accepted_request_to_join(
self,
random_id: uuid.UUID,
random_projects_rest_client: ProjectsRestClient,
oleg_id: uuid.UUID,
oleg_projects_rest_client: ProjectsRestClient,
):
position_id: uuid.UUID = self.CONTEXT["position_id"]
participant_id: uuid.UUID = self.CONTEXT["new_participant_id"]

participant = await random_projects_rest_client.get_participant(
participant = await oleg_projects_rest_client.get_participant(
participant_id=participant_id,
)

assert participant.id == participant_id
assert participant.position_id == position_id
assert participant.user_id == random_id
assert participant.user_id == oleg_id
assert participant.status == ParticipantStatusEnum.JOINED

@pytest.mark.dependency(depends=["TestProjectFlow::test_get_random_accepted_request_to_join"])
@pytest.mark.dependency(depends=["TestProjectFlow::test_get_owner_accepted_request_to_join"])
@pytest.mark.asyncio
async def test_leave_position_by_participant(
self,
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ services:
ROOT_PATH: ${PROJECTS_ROOT_PATH:-/projects}
ALLOWED_ORIGINS: ${PROJECTS_ALLOWED_ORIGINS:-["http://localhost:3000"]}
PRODUCER_SERVERS: '["kafka:9091"]'
USERS_GRPC_HOST: users
USERS_GRPC_PORT: 50051
secrets:
- jwt_access_token_public_key
- jwt_access_token_private_key
Expand Down
8 changes: 4 additions & 4 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ httpx = "^0.25.0"
typer = {version = "^0.9.0", extras = ["all"]}
redis = ">=4.2.0rc1"
pydantic = {version = "^2.5.1", extras = ["email"]}
py-fast-grpc = "0.1.10"
py-fast-grpc = "^0.3.2"

[tool.poetry.extras]
sqlite = ["aiosqlite"]
Expand Down
10 changes: 5 additions & 5 deletions sapphire/common/api/schemas/paginated.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from typing import Any

from pydantic import BaseModel
from pydantic import BaseModel, NonNegativeInt, PositiveInt


class PaginatedResponse(BaseModel):
data: list[Any]
page: int
per_page: int
total_pages: int
total_items: int
page: PositiveInt
per_page: PositiveInt
total_pages: NonNegativeInt
total_items: NonNegativeInt
10 changes: 6 additions & 4 deletions sapphire/common/broker/models/projects.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import enum
import uuid

from pydantic import BaseModel, ConfigDict
from pydantic import BaseModel, ConfigDict, EmailStr


class ParticipantNotificationData(BaseModel):
model_config = ConfigDict(from_attributes=True)

user_id: uuid.UUID
position_id: uuid.UUID
project_id: uuid.UUID
owner_id: uuid.UUID
project_name: str
position_id: uuid.UUID
participant_id: uuid.UUID
participant_email: EmailStr
owner_id: uuid.UUID
owner_email: EmailStr


class ParticipantNotificationType(str, enum.Enum):
Expand Down
3 changes: 1 addition & 2 deletions sapphire/common/cache/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@ async def start(self):

async def stop(self):
if self.redis:
self.redis.close()
await self.redis.wait_closed()
await self.redis.close()
self.redis = None

async def set(self, key: str, value: Any):
Expand Down
14 changes: 14 additions & 0 deletions sapphire/common/internal_api/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
from pydantic import BaseModel, NonNegativeInt, PositiveInt


class ListBaseRequest(BaseModel):
page: PositiveInt = 1
per_page: PositiveInt = 10


class ListBaseResponse(BaseModel):
data: list
page: PositiveInt
per_page: PositiveInt
total_items: NonNegativeInt
total_pages: NonNegativeInt
5 changes: 4 additions & 1 deletion sapphire/projects/api/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from sapphire.common.jwt import get_jwt_methods
from sapphire.projects.broker.service import get_service as get_broker_service
from sapphire.projects.database import get_service as get_database_service
from sapphire.users.internal_api.client import get_client as get_users_internal_api_client

from .service import get_service

Expand All @@ -18,11 +19,13 @@ def run(ctx: typer.Context):
database_service = get_database_service(settings=settings)
jwt_methods = get_jwt_methods(settings=settings)
broker_service = get_broker_service(loop=loop, settings=settings)
users_internal_api_client = get_users_internal_api_client(settings=settings)
api_service = get_service(
database=database_service,
jwt_methods=jwt_methods,
settings=settings,
broker_service=broker_service
broker_service=broker_service,
users_internal_api_client=users_internal_api_client,
)

loop.run_until_complete(api_service.run())
Expand Down
24 changes: 23 additions & 1 deletion sapphire/projects/api/rest/participants/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from sapphire.projects.broker.service import ProjectsBrokerService
from sapphire.projects.database.models import Participant, ParticipantStatusEnum
from sapphire.projects.database.service import ProjectsDatabaseService
from sapphire.users.internal_api.client.service import UsersInternalAPIClient

from .dependencies import get_path_participant
from .schemas import (
Expand All @@ -27,6 +28,9 @@ async def create_participant(
) -> ParticipantResponse:
broker_service: ProjectsBrokerService = request.app.service.broker
database_service: ProjectsDatabaseService = request.app.service.database
users_internal_api_client: UsersInternalAPIClient = (
request.app.service.users_internal_api_client
)

async with database_service.transaction() as session:
position = await database_service.get_position(
Expand All @@ -53,6 +57,9 @@ async def create_participant(
detail="Participant already send request to project or joined in project",
)

participant_data = await users_internal_api_client.get_user(user_id=jwt_data.user_id)
owner_data = await users_internal_api_client.get_user(user_id=position.project.owner_id)

async with database_service.transaction() as session:
participant = await database_service.create_participant(
session=session,
Expand All @@ -62,6 +69,8 @@ async def create_participant(
await broker_service.send_participant_requested(
project=position.project,
participant=participant,
participant_email=participant_data.email,
owner_email=owner_data.email,
)
await broker_service.send_create_chat(
is_personal=True,
Expand All @@ -85,6 +94,9 @@ async def update_participant(
) -> ParticipantResponse:
broker_service: ProjectsBrokerService = request.app.service.broker
database_service: ProjectsDatabaseService = request.app.service.database
users_internal_api_client: UsersInternalAPIClient = (
request.app.service.users_internal_api_client
)

project_owner_nodes = {
# New expected status : Required current statuses
Expand All @@ -108,6 +120,11 @@ async def update_participant(
if participant.status not in required_statuses:
raise HTTPForbidden()

participant_data = await users_internal_api_client.get_user(user_id=participant.user_id)
owner_data = await users_internal_api_client.get_user(
user_id=participant.position.project.owner_id,
)

async with database_service.transaction() as session:
participant = await database_service.update_participant_status(
session=session,
Expand Down Expand Up @@ -137,7 +154,12 @@ async def update_participant(
.get(jwt_data.user_id, None)
)
if participant_notification_send:
await participant_notification_send(project=project, participant=participant)
await participant_notification_send(
project=project,
participant=participant,
participant_email=participant_data.email,
owner_email=owner_data.email,
)

return ParticipantResponse.model_validate(participant)

Expand Down
11 changes: 10 additions & 1 deletion sapphire/projects/api/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from sapphire.projects.broker.service import ProjectsBrokerService
from sapphire.projects.database.service import ProjectsDatabaseService
from sapphire.projects.settings import ProjectsSettings
from sapphire.users.internal_api.client.service import UsersInternalAPIClient

from . import health, router

Expand All @@ -20,6 +21,7 @@ def __init__(
database: ProjectsDatabaseService,
jwt_methods: JWTMethods,
broker_service: ProjectsBrokerService,
users_internal_api_client: UsersInternalAPIClient,
media_dir_path: pathlib.Path = pathlib.Path("/media"),
load_file_chunk_size: int = 1024 * 1024, # 1 Mb
version: str = "0.0.0.0",
Expand All @@ -31,6 +33,7 @@ def __init__(
self._database = database
self._jwt_methods = jwt_methods
self._broker_service = broker_service
self._users_internal_api_client = users_internal_api_client
self._media_dir_path = media_dir_path
self._load_file_chunk_size = load_file_chunk_size

Expand Down Expand Up @@ -65,6 +68,10 @@ def jwt_methods(self) -> JWTMethods:
def broker(self) -> ProjectsBrokerService:
return self._broker_service

@property
def users_internal_api_client(self) -> UsersInternalAPIClient:
return self._users_internal_api_client

@property
def media_dir_path(self) -> pathlib.Path:
return self._media_dir_path
Expand All @@ -78,12 +85,14 @@ def get_service(
database: ProjectsDatabaseService,
jwt_methods: JWTMethods,
settings: ProjectsSettings,
broker_service: ProjectsBrokerService
broker_service: ProjectsBrokerService,
users_internal_api_client: UsersInternalAPIClient,
) -> ProjectsAPIService:
return ProjectsAPIService(
database=database,
jwt_methods=jwt_methods,
broker_service=broker_service,
users_internal_api_client=users_internal_api_client,
media_dir_path=settings.media_dir_path,
load_file_chunk_size=settings.load_file_chunk_size,
version=get_version() or "0.0.0",
Expand Down
Loading

0 comments on commit 62a6a66

Please sign in to comment.