feat: return members in GET /users/<ref>
This commit is contained in:
		
							parent
							
								
									03e7fb0bb2
								
							
						
					
					
						commit
						cb19049b97
					
				
					 9 changed files with 91 additions and 8 deletions
				
			
		|  | @ -1,2 +1,3 @@ | |||
| from .base import Base | ||||
| from .user import User, Token, AuthMethod, FediverseApp | ||||
| from .member import Member | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| from sqlalchemy.orm import DeclarativeBase | ||||
| 
 | ||||
| 
 | ||||
| class Base(DeclarativeBase): | ||||
|     pass | ||||
|  |  | |||
							
								
								
									
										28
									
								
								foxnouns/db/member.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								foxnouns/db/member.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| from typing import Any | ||||
| 
 | ||||
| from sqlalchemy import Text, BigInteger, ForeignKey | ||||
| from sqlalchemy.dialects.postgresql import JSONB | ||||
| from sqlalchemy.orm import Mapped, mapped_column, relationship | ||||
| 
 | ||||
| from .base import Base | ||||
| from .snowflake import Snowflake | ||||
| from .user import User | ||||
| 
 | ||||
| 
 | ||||
| class Member(Base): | ||||
|     __tablename__ = "members" | ||||
| 
 | ||||
|     id: Mapped[int] = mapped_column( | ||||
|         BigInteger(), primary_key=True, default=Snowflake.generate_int | ||||
|     ) | ||||
|     name: Mapped[str] = mapped_column(Text(), nullable=False) | ||||
| 
 | ||||
|     display_name: Mapped[str | None] = mapped_column(Text(), nullable=True) | ||||
|     bio: Mapped[str | None] = mapped_column(Text(), nullable=True) | ||||
| 
 | ||||
|     names: Mapped[list[Any]] = mapped_column(JSONB(), nullable=False, default=[]) | ||||
|     pronouns: Mapped[list[Any]] = mapped_column(JSONB(), nullable=False, default=[]) | ||||
|     fields: Mapped[list[Any]] = mapped_column(JSONB(), nullable=False, default=[]) | ||||
| 
 | ||||
|     user_id: Mapped[int] = mapped_column(ForeignKey("users.id")) | ||||
|     user: Mapped[User] = relationship(back_populates="members", lazy="immediate") | ||||
|  | @ -1,8 +1,9 @@ | |||
| from datetime import datetime | ||||
| import enum | ||||
| from typing import Any | ||||
| 
 | ||||
| from sqlalchemy import Text, Integer, BigInteger, ForeignKey, DateTime | ||||
| from sqlalchemy.dialects.postgresql import ARRAY | ||||
| from sqlalchemy.dialects.postgresql import ARRAY, JSONB | ||||
| from sqlalchemy.orm import Mapped, mapped_column, relationship | ||||
| 
 | ||||
| from .base import Base | ||||
|  | @ -19,17 +20,27 @@ class User(Base): | |||
|     display_name: Mapped[str | None] = mapped_column(Text(), nullable=True) | ||||
|     bio: Mapped[str | None] = mapped_column(Text(), nullable=True) | ||||
| 
 | ||||
|     names: Mapped[list[Any]] = mapped_column(JSONB(), nullable=False, default=[]) | ||||
|     pronouns: Mapped[list[Any]] = mapped_column(JSONB(), nullable=False, default=[]) | ||||
|     fields: Mapped[list[Any]] = mapped_column(JSONB(), nullable=False, default=[]) | ||||
| 
 | ||||
|     tokens: Mapped[list["Token"]] = relationship( | ||||
|         back_populates="user", cascade="all, delete-orphan" | ||||
|     ) | ||||
|     auth_methods: Mapped[list["AuthMethod"]] = relationship( | ||||
|         back_populates="user", cascade="all, delete-orphan" | ||||
|     ) | ||||
|     members: Mapped[list["Member"]] = relationship( | ||||
|         back_populates="user", cascade="all, delete-orphan" | ||||
|     ) | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return f"User(id={self.id!r}, username={self.username!r})" | ||||
| 
 | ||||
| 
 | ||||
| from .member import Member | ||||
| 
 | ||||
| 
 | ||||
| class Token(Base): | ||||
|     __tablename__ = "tokens" | ||||
| 
 | ||||
|  | @ -40,7 +51,7 @@ class Token(Base): | |||
|     scopes: Mapped[list[str]] = mapped_column(ARRAY(Text), nullable=False) | ||||
| 
 | ||||
|     user_id: Mapped[int] = mapped_column(ForeignKey("users.id")) | ||||
|     user: Mapped[User] = relationship(back_populates="tokens") | ||||
|     user: Mapped[User] = relationship(back_populates="tokens", lazy="immediate") | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return f"Token(id={self.id!r}, user={self.user_id!r})" | ||||
|  | @ -78,12 +89,12 @@ class AuthMethod(Base): | |||
|     remote_username: Mapped[str | None] = mapped_column(Text(), nullable=True) | ||||
| 
 | ||||
|     user_id: Mapped[int] = mapped_column(ForeignKey("users.id")) | ||||
|     user: Mapped[User] = relationship(back_populates="auth_methods") | ||||
|     user: Mapped[User] = relationship(back_populates="auth_methods", lazy="immediate") | ||||
| 
 | ||||
|     fediverse_app_id: Mapped[int] = mapped_column( | ||||
|         ForeignKey("fediverse_apps.id"), nullable=True | ||||
|     ) | ||||
|     fediverse_app: Mapped["FediverseApp"] = relationship() | ||||
|     fediverse_app: Mapped["FediverseApp"] = relationship(lazy="immediate") | ||||
| 
 | ||||
| 
 | ||||
| class FediverseInstanceType(enum.IntEnum): | ||||
|  |  | |||
|  | @ -4,9 +4,11 @@ from itsdangerous import BadSignature | |||
| from itsdangerous.url_safe import URLSafeTimedSerializer | ||||
| from sqlalchemy.ext.asyncio import AsyncSession | ||||
| from sqlalchemy import select, insert | ||||
| from sqlalchemy.orm import selectinload | ||||
| from quart import g | ||||
| 
 | ||||
| from .user import User, Token | ||||
| from .member import Member | ||||
| from foxnouns.exceptions import ForbiddenError, ErrorCode | ||||
| from foxnouns.settings import SECRET_KEY | ||||
| 
 | ||||
|  | @ -15,7 +17,7 @@ async def user_from_ref(session: AsyncSession, user_ref: str): | |||
|     """Returns a user from a `user_ref` value. If `user_ref` is `@me`, returns the current user. | ||||
|     Otherwise, tries to convert the user to a snowflake ID and queries that. Otherwise, returns a user with that username. | ||||
|     """ | ||||
|     query = select(User) | ||||
|     query = select(User).options(selectinload(User.members)) | ||||
| 
 | ||||
|     if user_ref == "@me": | ||||
|         if "user" in g: | ||||
|  | @ -37,6 +39,13 @@ async def user_from_ref(session: AsyncSession, user_ref: str): | |||
|     return await session.scalar(query) | ||||
| 
 | ||||
| 
 | ||||
| async def user_members(session: AsyncSession, user: User): | ||||
|     query = select(Member).where(Member.user_id == user.id) | ||||
| 
 | ||||
|     res = await session.scalars(query) | ||||
|     return res.all() | ||||
| 
 | ||||
| 
 | ||||
| serializer = URLSafeTimedSerializer(SECRET_KEY) | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,6 @@ | |||
| from typing import Any | ||||
| 
 | ||||
| from pydantic import BaseModel, field_validator | ||||
| 
 | ||||
| 
 | ||||
| class BasePatchModel(BaseModel): | ||||
|     model_config = {"from_attributes": True} | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										17
									
								
								foxnouns/models/fields.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								foxnouns/models/fields.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| from pydantic import BaseModel, Field | ||||
| 
 | ||||
| 
 | ||||
| class FieldEntry(BaseModel): | ||||
|     value: str = Field(max_length=128) | ||||
|     status: str | ||||
| 
 | ||||
| 
 | ||||
| class ProfileField(BaseModel): | ||||
|     name: str = Field(max_length=128) | ||||
|     entries: list[FieldEntry] | ||||
| 
 | ||||
| 
 | ||||
| class PronounEntry(BaseModel): | ||||
|     value: str = Field(max_length=128) | ||||
|     status: str | ||||
|     display: str | None = Field(max_length=128, default=None) | ||||
							
								
								
									
										7
									
								
								foxnouns/models/member.py
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								foxnouns/models/member.py
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | |||
| from pydantic import Field | ||||
| 
 | ||||
| from .user import BaseMemberModel, BaseUserModel | ||||
| 
 | ||||
| 
 | ||||
| class FullMemberModel(BaseMemberModel): | ||||
|     user: BaseUserModel | ||||
|  | @ -3,12 +3,22 @@ from pydantic import Field | |||
| from . import BaseSnowflakeModel | ||||
| 
 | ||||
| 
 | ||||
| class UserModel(BaseSnowflakeModel): | ||||
| class BaseUserModel(BaseSnowflakeModel): | ||||
|     name: str = Field(alias="username") | ||||
|     display_name: str | None | ||||
|     bio: str | None | ||||
| 
 | ||||
| 
 | ||||
| class UserModel(BaseUserModel): | ||||
|     members: list["BaseMemberModel"] = Field(default=[]) | ||||
| 
 | ||||
| 
 | ||||
| class BaseMemberModel(BaseSnowflakeModel): | ||||
|     name: str | ||||
|     display_name: str | None | ||||
|     bio: str | None | ||||
| 
 | ||||
| 
 | ||||
| class SelfUserModel(UserModel): | ||||
|     pass | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue