Skip to content

feat: Consolidate Admin User APIs #5050

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

Open
wants to merge 8 commits into
base: mealie-next
Choose a base branch
from
6 changes: 3 additions & 3 deletions frontend/composables/use-user.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useAsync, ref } from "@nuxtjs/composition-api";
import { useUserApi } from "~/composables/api";
import { useAdminApi } from "~/composables/api";
import { UserIn, UserOut } from "~/lib/api/types/user";

/*
Expand All @@ -9,7 +9,7 @@ to control whether the object is substantiated... but some of the others rely on
*/

export const useAllUsers = function () {
const api = useUserApi();
const api = useAdminApi();
const loading = ref(false);

function getAllUsers() {
Expand Down Expand Up @@ -47,7 +47,7 @@ export const useAllUsers = function () {
};

export const useUser = function (refreshFunc: CallableFunction | null = null) {
const api = useUserApi();
const api = useAdminApi();
const loading = ref(false);

function getUser(id: string) {
Expand Down
7 changes: 6 additions & 1 deletion mealie/routes/admin/admin_management_users.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from functools import cached_property

from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, status
from pydantic import UUID4

from mealie.core import security
Expand Down Expand Up @@ -42,6 +42,11 @@ def get_all(self, q: PaginationQuery = Depends(PaginationQuery)):

@router.post("", response_model=UserOut, status_code=201)
def create_one(self, data: UserIn):
if self.repos.users.get_by_username(data.username):
raise HTTPException(status.HTTP_409_CONFLICT, {"message": self.t("exceptions.username-conflict-error")})
elif self.repos.users.get_one(data.email, "email"):
raise HTTPException(status.HTTP_409_CONFLICT, {"message": self.t("exceptions.email-conflict-error")})

data.password = security.hash_password(data.password)
return self.mixins.create_one(data)

Expand Down
1 change: 0 additions & 1 deletion mealie/routes/users/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

router.include_router(registration.router, prefix=user_prefix, tags=["Users: Registration"])
router.include_router(crud.user_router)
router.include_router(crud.admin_router)
router.include_router(forgot_password.router, prefix=user_prefix, tags=["Users: Passwords"])
router.include_router(images.router, prefix=user_prefix, tags=["Users: Images"])
router.include_router(api_tokens.router)
Expand Down
45 changes: 5 additions & 40 deletions mealie/routes/users/crud.py
Original file line number Diff line number Diff line change
@@ -1,52 +1,17 @@
from fastapi import Depends, HTTPException, status
from fastapi import HTTPException, status
from pydantic import UUID4

from mealie.core.security import hash_password
from mealie.core.security.providers.credentials_provider import CredentialsProvider
from mealie.db.models.users.users import AuthMethod
from mealie.routes._base import BaseAdminController, BaseUserController, controller
from mealie.routes._base.mixins import HttpRepo
from mealie.routes._base.routers import AdminAPIRouter, UserAPIRouter
from mealie.routes._base import BaseUserController, controller
from mealie.routes._base.routers import UserAPIRouter
from mealie.routes.users._helpers import assert_user_change_allowed
from mealie.schema.response import ErrorResponse, SuccessResponse
from mealie.schema.response.pagination import PaginationQuery
from mealie.schema.user import ChangePassword, UserBase, UserIn, UserOut
from mealie.schema.user.user import UserPagination, UserRatings, UserRatingSummary
from mealie.schema.user import ChangePassword, UserBase, UserOut
from mealie.schema.user.user import UserRatings, UserRatingSummary

user_router = UserAPIRouter(prefix="/users", tags=["Users: CRUD"])
admin_router = AdminAPIRouter(prefix="/users", tags=["Users: Admin CRUD"])


@controller(admin_router)
class AdminUserController(BaseAdminController):
@property
def mixins(self) -> HttpRepo:
return HttpRepo[UserIn, UserOut, UserBase](self.repos.users, self.logger)

@admin_router.get("", response_model=UserPagination)
def get_all(self, q: PaginationQuery = Depends(PaginationQuery)):
"""Returns all users from all groups"""

response = self.repos.users.page_all(
pagination=q,
override=UserOut,
)

response.set_pagination_guides(admin_router.url_path_for("get_all"), q.model_dump())
return response

@admin_router.post("", response_model=UserOut, status_code=201)
def create_user(self, new_user: UserIn):
new_user.password = hash_password(new_user.password)
return self.mixins.create_one(new_user)

@admin_router.get("/{item_id}", response_model=UserOut)
def get_user(self, item_id: UUID4):
return self.mixins.get_one(item_id)

@admin_router.delete("/{item_id}")
def delete_user(self, item_id: UUID4):
self.mixins.delete_one(item_id)


@controller(user_router)
Expand Down
8 changes: 4 additions & 4 deletions tests/fixtures/fixture_users.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def h2_user(session: Session, admin_token, api_client: TestClient, unique_user:
"admin": False,
"tokens": [],
}
response = api_client.post(api_routes.users, json=user_data, headers=admin_token)
response = api_client.post(api_routes.admin_users, json=user_data, headers=admin_token)
assert response.status_code == 201

# Log in as this user
Expand Down Expand Up @@ -135,7 +135,7 @@ def g2_user(session: Session, admin_token, api_client: TestClient):
}

api_client.post(api_routes.admin_groups, json={"name": group}, headers=admin_token)
response = api_client.post(api_routes.users, json=create_data, headers=admin_token)
response = api_client.post(api_routes.admin_users, json=create_data, headers=admin_token)

assert response.status_code == 201

Expand Down Expand Up @@ -258,7 +258,7 @@ def user_tuple(session: Session, admin_token, api_client: TestClient) -> Generat
users_out = []

for usr in [create_data_1, create_data_2]:
response = api_client.post(api_routes.users, json=usr, headers=admin_token)
response = api_client.post(api_routes.admin_users, json=usr, headers=admin_token)
assert response.status_code == 201

# Log in as this user
Expand Down Expand Up @@ -312,7 +312,7 @@ def user_token(admin_token, api_client: TestClient):
"tokens": [],
}

response = api_client.post(api_routes.users, json=create_data, headers=admin_token)
response = api_client.post(api_routes.admin_users, json=create_data, headers=admin_token)

assert response.status_code == 201

Expand Down
4 changes: 2 additions & 2 deletions tests/integration_tests/user_tests/test_user_api_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def test_api_token_private(api_client: TestClient, admin_token):
response = api_client.post(api_routes.users_api_tokens, json={"name": "Test API Token"}, headers=admin_token)
assert response.status_code == 201

response = api_client.get(api_routes.users, headers=admin_token, params={"perPage": -1})
response = api_client.get(api_routes.admin_users, headers=admin_token, params={"perPage": -1})
assert response.status_code == 200
for user in response.json()["items"]:
for user_token in user["tokens"] or []:
Expand All @@ -39,7 +39,7 @@ def test_api_token_private(api_client: TestClient, admin_token):


def test_use_token(api_client: TestClient, long_live_token):
response = api_client.get(api_routes.users, headers=long_live_token)
response = api_client.get(api_routes.admin_users, headers=long_live_token)

assert response.status_code == 200

Expand Down
6 changes: 4 additions & 2 deletions tests/integration_tests/user_tests/test_user_crud.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,11 @@ def test_user_update(api_client: TestClient, unique_user: TestUser, admin_user:


def test_admin_updates(api_client: TestClient, admin_user: TestUser, unique_user: TestUser):
response = api_client.get(api_routes.users_item_id(unique_user.user_id), headers=admin_user.token)
response = api_client.get(api_routes.admin_users_item_id(unique_user.user_id), headers=admin_user.token)
assert response.status_code == 200
user = response.json()
response = api_client.get(api_routes.users_item_id(admin_user.user_id), headers=admin_user.token)

response = api_client.get(api_routes.admin_users_item_id(admin_user.user_id), headers=admin_user.token)
admin = response.json()

# admin updating themselves
Expand Down
Loading