Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various different commands #35

Merged
merged 5 commits into from
Apr 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ dev/run: build/.certificate_sentinel
docker compose -f docker-compose.development.yml up -d
SOPH__CONFIG_YAML_FILE=configurations/dev.yaml poetry run python src/sophrosyne/main.py run

.PHONY: dev/db/up
dev/db/up:
docker compose -f docker-compose.development.yml up -d

.PHONY: dev/db/down
dev/db/down:
docker compose -f docker-compose.development.yml down
Expand Down
6 changes: 3 additions & 3 deletions alembic.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[alembic]
# path to migration scripts
script_location = migrations
script_location = src/sophrosyne/migrations

# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
# Uncomment the line below if you want the files to be prepended with date and time
Expand Down Expand Up @@ -58,7 +58,7 @@ version_path_separator = os # Use os.pathsep. Default configuration used for ne
# are written from script.py.mako
# output_encoding = utf-8

sqlalchemy.url = postgresql+asyncpg://localhost:5432/postgres
sqlalchemy.url = postgresql+asyncpg://postgres:postgres@localhost:5432/postgres


[post_write_hooks]
Expand Down Expand Up @@ -111,4 +111,4 @@ formatter = generic

[formatter_generic]
format = %(levelname)-5.5s [%(name)s] %(message)s
datefmt = %H:%M:%S
datefmt = %H:%MWARNu
1 change: 1 addition & 0 deletions configurations/dev.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
security:
site_key: '6fAkguc1RQkgdWwoLdslkQjA/N5ujPvGEmG2l97Gt+4='
salt: 'KULByt65QDNWy4BhtkOagb3td08q992ZfPWLthFw00s='
outgoing_tls_verify: False
development:
static_root_token: "thisisastaticroottoken"
logging:
Expand Down
1 change: 0 additions & 1 deletion migrations/README

This file was deleted.

5 changes: 4 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,13 @@ packages = [ "sophrosyne" ]
type = "markdown"
filename = "docs/api.md"

[tool.ruff]
extend-exclude = ["*_pb2.py", "*_pb2_grpc.py", "*_pb2.pyi", "*/migrations/versions/*.py"]

[tool.ruff.lint]
# Enable all `pydocstyle` rules, limiting to those that adhere to the
# Google convention via `convention = "google"`, below.
select = ["D", "I"]
select = ["D", "I", "F"]

[tool.ruff.lint.pydocstyle]
convention = "google"
Expand Down
4 changes: 1 addition & 3 deletions src/sophrosyne/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
api_router (APIRouter): The API router for the API.
"""

from typing import Annotated

from fastapi import APIRouter, Depends
from fastapi import APIRouter

from sophrosyne.api.routers.health import router as health_router
from sophrosyne.api.v1.api import api_router as v1_api_router
Expand Down
1 change: 0 additions & 1 deletion src/sophrosyne/api/v1/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
api_router (APIRouter): The API router for the v1 version of the API.
"""

from typing import Annotated

from fastapi import APIRouter, Depends

Expand Down
1 change: 0 additions & 1 deletion src/sophrosyne/api/v1/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
should be defined in the core.models module instead.
"""

from datetime import datetime
from typing import Annotated, Literal, Union

from pydantic import EmailStr, Field
Expand Down
8 changes: 5 additions & 3 deletions src/sophrosyne/api/v1/routers/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
from sophrosyne.api.v1.tags import Tags
from sophrosyne.core.models import Check, Profile

CHECK_NOT_FOUND: str = "Check not found"

router = APIRouter(dependencies=[Depends(require_admin)])


Expand Down Expand Up @@ -113,7 +115,7 @@ async def read_check(
result = await db_session.exec(select(Check).where(Check.name == req.name))
check = result.first()
if not check:
raise HTTPException(status_code=404, detail="Check not found")
raise HTTPException(status_code=404, detail=CHECK_NOT_FOUND)
return ChecksListCheckResponse.model_validate(
check, update={"profiles": [p.name for p in check.profiles]}
)
Expand Down Expand Up @@ -142,7 +144,7 @@ async def update_check(
result = await db_session.exec(select(Check).where(Check.name == req.name))
db_check = result.first()
if not db_check:
raise HTTPException(status_code=404, detail="Check not found")
raise HTTPException(status_code=404, detail=CHECK_NOT_FOUND)

if req.profiles is not None:
db_profiles = await db_session.exec(
Expand Down Expand Up @@ -175,7 +177,7 @@ async def delete_check(
result = await db_session.exec(select(Check).where(Check.name == req.name))
db_check = result.first()
if not db_check:
raise HTTPException(status_code=404, detail="Check not found")
raise HTTPException(status_code=404, detail=CHECK_NOT_FOUND)
await db_session.delete(db_check)
await db_session.commit()
return ChecksDeleteCheckResponse(ok=True)
10 changes: 5 additions & 5 deletions src/sophrosyne/api/v1/routers/profiles.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
router (APIRouter): The FastAPI router for the profiles API.
"""

from typing import Sequence

from fastapi import APIRouter, Depends, HTTPException, Query
from sqlmodel import col, select
from sqlmodel.ext.asyncio.session import AsyncSession
Expand All @@ -27,6 +25,8 @@

router = APIRouter(dependencies=[Depends(require_admin)])

PROFILE_NOT_FOUND: str = "Profile not found"


@router.post(
"/profiles/create-profile",
Expand Down Expand Up @@ -125,7 +125,7 @@ async def read_profile(
result = await db_session.exec(select(Profile).where(Profile.name == req.name))
profile = result.first()
if not profile:
raise HTTPException(status_code=404, detail="Profile not found")
raise HTTPException(status_code=404, detail=PROFILE_NOT_FOUND)
return ProfilesListProfileResponse.model_validate(
profile, update={"checks": [c.name for c in profile.checks]}
)
Expand Down Expand Up @@ -156,7 +156,7 @@ async def update_profile(
result = await db_session.exec(select(Profile).where(Profile.name == req.name))
db_profile = result.first()
if not db_profile:
raise HTTPException(status_code=404, detail="Profile not found")
raise HTTPException(status_code=404, detail=PROFILE_NOT_FOUND)

if req.checks is not None:
db_checks = await db_session.exec(
Expand Down Expand Up @@ -198,7 +198,7 @@ async def delete_profile(
result = await db_session.exec(select(Profile).where(Profile.name == req.name))
db_profile = result.first()
if not db_profile:
raise HTTPException(status_code=404, detail="Profile not found")
raise HTTPException(status_code=404, detail=PROFILE_NOT_FOUND)
await db_session.delete(db_profile)
await db_session.commit()
return ProfilesDeleteProfileResponse(ok=True)
3 changes: 1 addition & 2 deletions src/sophrosyne/api/v1/routers/safety.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,9 @@
router (APIRouter): The FastAPI router for the safety endpoints.
"""

from typing import Annotated, Literal, Union
from typing import Annotated

from fastapi import APIRouter, Body, Depends
from pydantic import Field

from sophrosyne.api.dependencies import auth_and_return_user, get_safety_service
from sophrosyne.api.v1.models import (
Expand Down
44 changes: 28 additions & 16 deletions src/sophrosyne/api/v1/routers/users.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession

from sophrosyne.api.dependencies import auth_and_return_user, get_db_session, require_admin
from sophrosyne.api.dependencies import (
auth_and_return_user,
get_db_session,
require_admin,
)
from sophrosyne.api.v1.models import (
UsersCreateUserRequest,
UsersCreateUserResponse,
Expand All @@ -28,22 +32,25 @@

router = APIRouter()

USER_NOT_FOUND = "User not found"


@router.post(
"/users/create-user", response_model=UsersCreateUserResponse, tags=[Tags.users]
"/users/create-user",
response_model=UsersCreateUserResponse,
tags=[Tags.users],
dependencies=[Depends(require_admin)],
)
async def create_user(
*,
db_session: AsyncSession = Depends(get_db_session),
user: UsersCreateUserRequest,
require_admin=Depends(require_admin),
):
"""Create a new user.

Args:
db_session (AsyncSession): The database session.
user (UsersCreateUserRequest): The request payload containing user data.
require_admin (bool): The admin requirement dependency.

Returns:
UsersCreateUserResponse: The newly created user.
Expand All @@ -62,22 +69,23 @@ async def create_user(


@router.get(
"/users/list-users", response_model=UsersListUsersResponse, tags=[Tags.users]
"/users/list-users",
response_model=UsersListUsersResponse,
tags=[Tags.users],
dependencies=Depends(require_admin),
)
async def read_users(
*,
db_session: AsyncSession = Depends(get_db_session),
offset: int = 0,
limit: int = Query(100, le=100),
require_admin=Depends(require_admin),
):
"""Retrieve a list of users from the database.

Args:
db_session (AsyncSession): The database session.
offset (int): The offset for pagination. Defaults to 0.
limit (int): The maximum number of users to retrieve. Defaults to 100.
require_admin (bool): The admin requirement dependency.

Returns:
UsersListUsersResponse: A list of user objects.
Expand Down Expand Up @@ -117,20 +125,22 @@ async def read_user(
result = await db_session.exec(select(User).where(User.name == req.name))
user = result.first()
if not user:
raise HTTPException(status_code=400, detail="User not found")
raise HTTPException(status_code=400, detail=USER_NOT_FOUND)
if current_user.name != user.name:
raise HTTPException(status_code=403, detail="User not found")
raise HTTPException(status_code=403, detail=USER_NOT_FOUND)
return user


@router.patch(
"/users/update-user", response_model=UsersUpdateUserResponse, tags=[Tags.users]
"/users/update-user",
response_model=UsersUpdateUserResponse,
tags=[Tags.users],
dependencies=[Depends(require_admin)],
)
async def update_user(
*,
db_session: AsyncSession = Depends(get_db_session),
req: UsersUpdateUserRequest,
require_admin=Depends(require_admin),
):
"""Update a user in the database.

Expand All @@ -147,7 +157,7 @@ async def update_user(
result = await db_session.exec(select(User).where(User.name == req.name))
db_user = result.first()
if not db_user:
raise HTTPException(status_code=400, detail="User not found")
raise HTTPException(status_code=400, detail=USER_NOT_FOUND)
user_data = req.model_dump(exclude_unset=True)
db_user.sqlmodel_update(user_data)
db_session.add(db_user)
Expand All @@ -157,13 +167,15 @@ async def update_user(


@router.delete(
"/users/delete-user", response_model=UsersDeleteUserResponse, tags=[Tags.users]
"/users/delete-user",
response_model=UsersDeleteUserResponse,
tags=[Tags.users],
dependencies=[Depends(require_admin)],
)
async def delete_user(
*,
db_session: AsyncSession = Depends(get_db_session),
req: UsersDeleteUserRequest,
require_admin=Depends(require_admin),
):
"""Delete a user from the database.

Expand All @@ -177,7 +189,7 @@ async def delete_user(
result = await db_session.exec(select(User).where(User.name == req.name))
db_user = result.first()
if not db_user:
raise HTTPException(status_code=400, detail="User not found")
raise HTTPException(status_code=400, detail=USER_NOT_FOUND)
await db_session.delete(db_user)
await db_session.commit()
return UsersDeleteUserResponse(ok=True)
Expand Down Expand Up @@ -208,7 +220,7 @@ async def rotate_user_token(
result = await db_session.exec(select(User).where(User.name == req.name))
db_user = result.first()
if not db_user:
raise HTTPException(status_code=400, detail="User not found")
raise HTTPException(status_code=400, detail=USER_NOT_FOUND)
if current_user.name != db_user.name and not current_user.is_admin:
raise HTTPException(status_code=403, detail="Not authorized")
token = new_token()
Expand Down
Loading
Loading