Praneeth Yerrapragada commited on
Commit
d4e18c8
1 Parent(s): 4192f93

fix: fix users endpoints

Browse files
app/api/routers/user.py CHANGED
@@ -3,7 +3,7 @@ from fastapi import APIRouter, Depends, HTTPException
3
  from sqlalchemy.ext.asyncio import AsyncSession
4
 
5
  from app.engine.postgresdb import get_db_session
6
- from app.schema.index import UserCreate, User as UserSchema
7
  from app.model.user import User as UserModel
8
 
9
 
@@ -13,7 +13,7 @@ logger = logging.getLogger(__name__)
13
 
14
  @r.post(
15
  "/",
16
- response_model=UserSchema,
17
  responses={
18
  200: {"description": "New user created"},
19
  400: {"description": "Bad request"},
@@ -23,63 +23,67 @@ logger = logging.getLogger(__name__)
23
  )
24
  async def create_user(user: UserCreate, db: AsyncSession = Depends(get_db_session)):
25
  try:
26
- logger.info(f"Checking if user exists: {user.dict()}")
27
  db_user = await UserModel.get(db, email=user.email)
28
  if db_user and not db_user.is_deleted:
29
  raise HTTPException(status_code=409, detail="User already exists")
30
 
31
- logger.info(f"Creating user: {user.dict()}")
32
- db_user = await UserModel.create(db, **user.dict())
33
- return db_user
34
  except Exception as e:
35
  raise HTTPException(status_code=500, detail=str(e))
36
 
37
 
38
  @r.get(
39
- "/{user_id}",
40
- response_model=UserSchema,
41
  responses={
42
  200: {"description": "User found"},
43
  404: {"description": "User not found"},
44
  500: {"description": "Internal server error"},
45
  },
46
  )
47
- async def get_user(user_id: int, db: AsyncSession = Depends(get_db_session)):
48
- user = await UserModel.get(db, id=user_id)
49
  if not user:
50
  raise HTTPException(status_code=404, detail="User not found")
51
  return user
52
 
53
 
54
  @r.put(
55
- "/{user_id}",
56
- response_model=UserSchema,
57
  responses={
58
  200: {"description": "User updated"},
59
  404: {"description": "User not found"},
60
  500: {"description": "Internal server error"},
61
  },
62
  )
63
- async def update_user(user_id: int, user_payload: UserCreate, db: AsyncSession = Depends(get_db_session)):
64
- user = await UserModel.get(db, id=user_id)
65
- if not user:
66
- raise HTTPException(status_code=404, detail="User not found")
67
- user = await UserModel.update(db, id=user_id, **user_payload.dict())
68
- return user
 
 
 
 
69
 
70
 
71
  @r.delete(
72
- "/{user_id}",
73
- response_model=UserSchema,
74
  responses={
75
  200: {"description": "User deleted"},
76
  404: {"description": "User not found"},
77
  500: {"description": "Internal server error"},
78
  },
79
  )
80
- async def delete_user(user_id: int, db: AsyncSession = Depends(get_db_session)):
81
- user = await UserModel.get(db, id=user_id)
82
  if not user:
83
  raise HTTPException(status_code=404, detail="User not found")
84
- user = await UserModel.delete(db, id=user_id)
 
85
  return user
 
3
  from sqlalchemy.ext.asyncio import AsyncSession
4
 
5
  from app.engine.postgresdb import get_db_session
6
+ from app.schema.index import UserCreate, User as UserSchema, UserResponse, UserUpdate
7
  from app.model.user import User as UserModel
8
 
9
 
 
13
 
14
  @r.post(
15
  "/",
16
+ response_model=UserResponse,
17
  responses={
18
  200: {"description": "New user created"},
19
  400: {"description": "Bad request"},
 
23
  )
24
  async def create_user(user: UserCreate, db: AsyncSession = Depends(get_db_session)):
25
  try:
 
26
  db_user = await UserModel.get(db, email=user.email)
27
  if db_user and not db_user.is_deleted:
28
  raise HTTPException(status_code=409, detail="User already exists")
29
 
30
+ await UserModel.create(db, **user.dict())
31
+ user = await UserModel.get(db, email=user.email)
32
+ return user
33
  except Exception as e:
34
  raise HTTPException(status_code=500, detail=str(e))
35
 
36
 
37
  @r.get(
38
+ "/{email}",
39
+ response_model=UserResponse,
40
  responses={
41
  200: {"description": "User found"},
42
  404: {"description": "User not found"},
43
  500: {"description": "Internal server error"},
44
  },
45
  )
46
+ async def get_user(email: str, db: AsyncSession = Depends(get_db_session)):
47
+ user = await UserModel.get(db, email=email)
48
  if not user:
49
  raise HTTPException(status_code=404, detail="User not found")
50
  return user
51
 
52
 
53
  @r.put(
54
+ "/{email}",
55
+ response_model=UserResponse,
56
  responses={
57
  200: {"description": "User updated"},
58
  404: {"description": "User not found"},
59
  500: {"description": "Internal server error"},
60
  },
61
  )
62
+ async def update_user(email: str, user_payload: UserUpdate, db: AsyncSession = Depends(get_db_session)):
63
+ try:
64
+ user = await UserModel.get(db, email=email)
65
+ if not user:
66
+ raise HTTPException(status_code=404, detail="User not found")
67
+ await UserModel.update(db, id=user.id, **user_payload.dict())
68
+ user = await UserModel.get(db, email=email)
69
+ return user
70
+ except Exception as e:
71
+ raise HTTPException(status_code=500, detail=str(e))
72
 
73
 
74
  @r.delete(
75
+ "/{email}",
76
+ response_model=UserResponse,
77
  responses={
78
  200: {"description": "User deleted"},
79
  404: {"description": "User not found"},
80
  500: {"description": "Internal server error"},
81
  },
82
  )
83
+ async def delete_user(email: str, db: AsyncSession = Depends(get_db_session)):
84
+ user = await UserModel.get(db, email=email)
85
  if not user:
86
  raise HTTPException(status_code=404, detail="User not found")
87
+ await UserModel.delete(db, email=email)
88
+ user = await UserModel.get(db, email=email)
89
  return user
app/model/base.py CHANGED
@@ -1,10 +1,15 @@
1
  from datetime import datetime, timezone
2
- from sqlalchemy import Column, DateTime, Integer
 
3
 
4
  from app.engine.postgresdb import Base
5
 
6
 
7
  class BaseModel:
8
- id = Column(Integer, primary_key=True, index=True)
9
- created_at = Column(DateTime, default=datetime.now(timezone.utc))
10
- updated_at = Column(DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
 
 
 
 
 
1
  from datetime import datetime, timezone
2
+ from sqlalchemy import DateTime
3
+ from sqlalchemy.orm import Mapped, mapped_column
4
 
5
  from app.engine.postgresdb import Base
6
 
7
 
8
  class BaseModel:
9
+ id: Mapped[int] = mapped_column(primary_key=True, index=True, autoincrement=True)
10
+ created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=datetime.now(timezone.utc))
11
+ updated_at: Mapped[datetime] = mapped_column(
12
+ DateTime(timezone=True),
13
+ default=datetime.now(timezone.utc),
14
+ onupdate=datetime.now(timezone.utc),
15
+ )
app/model/user.py CHANGED
@@ -1,3 +1,4 @@
 
1
  from sqlalchemy.orm import relationship, Mapped, mapped_column
2
  from sqlalchemy.sql import expression as sql
3
  from sqlalchemy.ext.asyncio import AsyncSession
@@ -5,6 +6,8 @@ from sqlalchemy.ext.asyncio import AsyncSession
5
  from app.model.base import BaseModel
6
  from app.engine.postgresdb import Base
7
 
 
 
8
 
9
  class User(Base, BaseModel):
10
  __tablename__ = "users"
@@ -18,47 +21,40 @@ class User(Base, BaseModel):
18
 
19
  @classmethod
20
  async def create(cls: "type[User]", db: AsyncSession, **kwargs) -> "User":
21
- query = sql.insert(cls).values(**kwargs).returning(cls.id)
22
- users = await db.execute(query)
 
 
 
23
  await db.commit()
24
- return users.first()
25
 
26
  @classmethod
27
  async def update(cls: "type[User]", db: AsyncSession, id: int, **kwargs) -> "User":
28
- query = (
29
- sql.update(cls)
30
- .where(cls.id == id)
31
- .values(**kwargs)
32
- .execution_options(synchronize_session="fetch")
33
- .returning(cls.id)
34
- )
35
- users = await db.execute(query)
36
  await db.commit()
37
- return users.first()
38
-
39
- @classmethod
40
- async def get(cls: "type[User]", db: AsyncSession, id: int) -> "User":
41
- query = sql.select(cls).where(cls.id == id)
42
- users = await db.execute(query)
43
- (user,) = users.first()
44
  return user
45
 
46
  @classmethod
47
  async def get(cls: "type[User]", db: AsyncSession, email: str) -> "User":
 
48
  query = sql.select(cls).where(cls.email == email)
49
- users = await db.execute(query)
50
- (user,) = users.first()
51
- return user
 
52
 
53
  @classmethod
54
- async def delete(cls: "type[User]", db: AsyncSession, id: int) -> "User":
55
  query = (
56
  sql.update(cls)
57
- .where(cls.id == id)
58
  .values(is_deleted=True)
59
  .execution_options(synchronize_session="fetch")
60
- .returning(cls.id)
61
  )
62
- users = await db.execute(query)
 
63
  await db.commit()
64
- return users.first()
 
1
+ import logging
2
  from sqlalchemy.orm import relationship, Mapped, mapped_column
3
  from sqlalchemy.sql import expression as sql
4
  from sqlalchemy.ext.asyncio import AsyncSession
 
6
  from app.model.base import BaseModel
7
  from app.engine.postgresdb import Base
8
 
9
+ logger = logging.getLogger(__name__)
10
+
11
 
12
  class User(Base, BaseModel):
13
  __tablename__ = "users"
 
21
 
22
  @classmethod
23
  async def create(cls: "type[User]", db: AsyncSession, **kwargs) -> "User":
24
+ logging.info(f"Creating user: {kwargs}")
25
+ query = sql.insert(cls).values(**kwargs)
26
+ users = await db.scalars(query)
27
+ user = users.first()
28
+ logging.info(f"User created: {users.first()}")
29
  await db.commit()
30
+ return user
31
 
32
  @classmethod
33
  async def update(cls: "type[User]", db: AsyncSession, id: int, **kwargs) -> "User":
34
+ query = sql.update(cls).where(cls.id == id).values(**kwargs).execution_options(synchronize_session="fetch")
35
+ users = await db.scalars(query)
36
+ user = users.first()
 
 
 
 
 
37
  await db.commit()
 
 
 
 
 
 
 
38
  return user
39
 
40
  @classmethod
41
  async def get(cls: "type[User]", db: AsyncSession, email: str) -> "User":
42
+ logging.info(f"Getting user: {email}")
43
  query = sql.select(cls).where(cls.email == email)
44
+ logging.info(f"Query: {query}")
45
+ users = await db.scalars(query)
46
+ logging.info(f"Users: {users}")
47
+ return users.first()
48
 
49
  @classmethod
50
+ async def delete(cls: "type[User]", db: AsyncSession, email: str) -> "User":
51
  query = (
52
  sql.update(cls)
53
+ .where(cls.email == email)
54
  .values(is_deleted=True)
55
  .execution_options(synchronize_session="fetch")
 
56
  )
57
+ users = await db.scalars(query)
58
+ user = users.first()
59
  await db.commit()
60
+ return user
app/schema/base.py CHANGED
@@ -2,7 +2,11 @@ from datetime import datetime
2
  from pydantic import BaseModel
3
 
4
 
5
- class BaseModel(BaseModel):
 
 
 
 
6
  id: int
7
  created_at: datetime
8
  updated_at: datetime
 
2
  from pydantic import BaseModel
3
 
4
 
5
+ class PydanticBaseModel(BaseModel):
6
+ pass
7
+
8
+
9
+ class BaseModel(PydanticBaseModel):
10
  id: int
11
  created_at: datetime
12
  updated_at: datetime
app/schema/index.py CHANGED
@@ -1,8 +1,8 @@
1
  from enum import Enum
2
  from datetime import datetime
3
- from typing import List
4
 
5
- from app.schema.base import BaseModel
6
 
7
 
8
  class TransactionType(str, Enum):
@@ -10,12 +10,25 @@ class TransactionType(str, Enum):
10
  EXPENSE = "expense"
11
 
12
 
13
- class UserCreate(BaseModel):
14
  name: str
15
  email: str
16
  hashed_password: str
17
 
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  class User(BaseModel):
20
  name: str
21
  email: str
 
1
  from enum import Enum
2
  from datetime import datetime
3
+ from typing import List, Optional
4
 
5
+ from app.schema.base import BaseModel, PydanticBaseModel
6
 
7
 
8
  class TransactionType(str, Enum):
 
10
  EXPENSE = "expense"
11
 
12
 
13
+ class UserCreate(PydanticBaseModel):
14
  name: str
15
  email: str
16
  hashed_password: str
17
 
18
 
19
+ class UserUpdate(PydanticBaseModel):
20
+ name: str
21
+ email: str
22
+ hashed_password: str
23
+
24
+
25
+ class UserResponse(PydanticBaseModel):
26
+ id: int
27
+ name: str
28
+ email: str
29
+ is_deleted: bool
30
+
31
+
32
  class User(BaseModel):
33
  name: str
34
  email: str
migration/versions/7ea44cbc5b1f_default_timezone.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """default_timezone
2
+
3
+ Revision ID: 7ea44cbc5b1f
4
+ Revises: 8feaedca36f9
5
+ Create Date: 2024-06-02 14:36:01.552518
6
+
7
+ """
8
+
9
+ from typing import Sequence, Union
10
+
11
+ from alembic import op
12
+ import sqlalchemy as sa
13
+ from sqlalchemy.dialects import postgresql
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision: str = "7ea44cbc5b1f"
17
+ down_revision: Union[str, None] = "8feaedca36f9"
18
+ branch_labels: Union[str, Sequence[str], None] = None
19
+ depends_on: Union[str, Sequence[str], None] = None
20
+
21
+
22
+ def upgrade() -> None:
23
+ # ### commands auto generated by Alembic - please adjust! ###
24
+ op.alter_column(
25
+ "transactions",
26
+ "created_at",
27
+ existing_type=postgresql.TIMESTAMP(),
28
+ type_=sa.DateTime(timezone=True),
29
+ nullable=False,
30
+ )
31
+ op.alter_column(
32
+ "transactions",
33
+ "updated_at",
34
+ existing_type=postgresql.TIMESTAMP(),
35
+ type_=sa.DateTime(timezone=True),
36
+ nullable=False,
37
+ )
38
+ op.alter_column(
39
+ "users", "created_at", existing_type=postgresql.TIMESTAMP(), type_=sa.DateTime(timezone=True), nullable=False
40
+ )
41
+ op.alter_column(
42
+ "users", "updated_at", existing_type=postgresql.TIMESTAMP(), type_=sa.DateTime(timezone=True), nullable=False
43
+ )
44
+ # ### end Alembic commands ###
45
+
46
+
47
+ def downgrade() -> None:
48
+ # ### commands auto generated by Alembic - please adjust! ###
49
+ op.alter_column(
50
+ "users", "updated_at", existing_type=sa.DateTime(timezone=True), type_=postgresql.TIMESTAMP(), nullable=True
51
+ )
52
+ op.alter_column(
53
+ "users", "created_at", existing_type=sa.DateTime(timezone=True), type_=postgresql.TIMESTAMP(), nullable=True
54
+ )
55
+ op.alter_column(
56
+ "transactions",
57
+ "updated_at",
58
+ existing_type=sa.DateTime(timezone=True),
59
+ type_=postgresql.TIMESTAMP(),
60
+ nullable=True,
61
+ )
62
+ op.alter_column(
63
+ "transactions",
64
+ "created_at",
65
+ existing_type=sa.DateTime(timezone=True),
66
+ type_=postgresql.TIMESTAMP(),
67
+ nullable=True,
68
+ )
69
+ # ### end Alembic commands ###