From 791aa8c610942c11b2b6bfdbc938ed3fce1c8d87 Mon Sep 17 00:00:00 2001 From: Hayden <64056131+hay-kot@users.noreply.github.com> Date: Thu, 25 Nov 2021 14:17:02 -0900 Subject: [PATCH] =?UTF-8?q?=20feat(backend):=20=E2=9C=A8=20refactor/fix=20?= =?UTF-8?q?group=20management=20for=20admins=20(#838)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(frontend): :bug: update dialog implementation to simplify state management * test(backend): :white_check_mark: refactor test fixtures + admin group tests * chore(backend): :hammer: add launcher.json for python debugging (tests) * fix typing * feat(backend): :sparkles: refactor/fix group management for admins * feat(frontend): :sparkles: add/fix admin group management * add LDAP checker Co-authored-by: hay-kot --- .vscode/launch.json | 33 ++++ frontend/api/admin-api.ts | 3 + frontend/api/admin/admin-about.ts | 1 + frontend/api/admin/admin-groups.ts | 54 +++++++ frontend/api/admin/admin-users.ts | 4 +- .../Domain/Group/GroupPreferencesEditor.vue | 99 ++++++++++++ .../Domain/Recipe/RecipeActionMenu.vue | 5 +- .../components/Domain/Recipe/RecipeAssets.vue | 12 +- .../Domain/Recipe/RecipeContextMenu.vue | 21 +-- .../components/Domain/Recipe/RecipeTools.vue | 15 +- frontend/components/global/AutoForm.vue | 1 + .../global/BaseCardSectionTitle.vue | 7 +- frontend/components/global/BaseDialog.vue | 31 +++- frontend/pages/admin/backups.vue | 19 ++- frontend/pages/admin/manage/groups/_id.vue | 96 ++++++++++++ .../admin/manage/{group => groups}/index.vue | 100 +++++++----- frontend/pages/admin/manage/users/_id.vue | 2 +- frontend/pages/admin/manage/users/index.vue | 35 +++-- frontend/pages/admin/site-settings.vue | 15 +- frontend/pages/admin/toolbox/foods.vue | 39 +++-- frontend/pages/admin/toolbox/units.vue | 69 +++++---- frontend/pages/meal-plan/planner.vue | 9 +- frontend/pages/user/group/members.vue | 2 +- .../pages/user/group/recipe-data/index.vue | 11 +- mealie/core/security.py | 4 +- mealie/core/settings/settings.py | 4 +- mealie/routes/admin/__init__.py | 7 +- mealie/routes/admin/admin_about.py | 1 + mealie/routes/handlers.py | 3 +- mealie/schema/admin/about.py | 1 + mealie/schema/group/group.py | 9 ++ mealie/schema/mapper.py | 18 +++ mealie/services/admin/admin_group_service.py | 59 ++++++++ mealie/services/admin/admin_user_service.py | 4 +- tests/conftest.py | 142 +----------------- tests/fixtures/__init__.py | 4 + tests/fixtures/fixture_admin.py | 41 +++++ tests/fixtures/fixture_recipe.py | 18 +++ tests/fixtures/fixture_routes.py | 8 + tests/fixtures/fixture_users.py | 91 +++++++++++ .../admin_tests/test_admin_user_actions.py | 2 +- .../admin_tests/test_group_admin_actions.py | 79 +++++++--- tests/integration_tests/test_import_routes.py | 2 +- .../test_migration_routes.py | 2 +- .../user_recipe_tests/test_recipe_crud.py | 2 +- .../user_tests/test_user_api_token.py | 2 +- .../user_tests/test_user_images.py | 2 +- .../user_tests/test_user_login.py | 2 +- tests/utils/__init__.py | 4 + tests/{ => utils}/app_routes.py | 0 tests/utils/assertion_helpers.py | 6 +- tests/utils/user_login.py | 12 ++ 52 files changed, 881 insertions(+), 331 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 frontend/api/admin/admin-groups.ts create mode 100644 frontend/components/Domain/Group/GroupPreferencesEditor.vue create mode 100644 frontend/pages/admin/manage/groups/_id.vue rename frontend/pages/admin/manage/{group => groups}/index.vue (53%) create mode 100644 mealie/schema/group/group.py create mode 100644 mealie/schema/mapper.py create mode 100644 mealie/services/admin/admin_group_service.py create mode 100644 tests/fixtures/__init__.py create mode 100644 tests/fixtures/fixture_admin.py create mode 100644 tests/fixtures/fixture_recipe.py create mode 100644 tests/fixtures/fixture_routes.py create mode 100644 tests/fixtures/fixture_users.py rename tests/{ => utils}/app_routes.py (100%) create mode 100644 tests/utils/user_login.py diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000000..41b390fac6aa --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,33 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python: FastAPI", + "type": "python", + "request": "launch", + "module": "uvicorn", + "args": ["mealie.app:app"], + "justMyCode": false, + "jinja": true + }, + { + "name": "Python: Current File", + "type": "python", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "justMyCode": false + }, + { + "name": "Debug Tests", + "type": "python", + "request": "test", + "console": "integratedTerminal", + "justMyCode": false, + "env": { "PYTEST_ADDOPTS": "--no-cov" } + } + ] +} diff --git a/frontend/api/admin-api.ts b/frontend/api/admin-api.ts index 951faf5aee63..db978ca416cb 100644 --- a/frontend/api/admin-api.ts +++ b/frontend/api/admin-api.ts @@ -1,6 +1,7 @@ import { AdminAboutAPI } from "./admin/admin-about"; import { AdminTaskAPI } from "./admin/admin-tasks"; import { AdminUsersApi } from "./admin/admin-users"; +import { AdminGroupsApi } from "./admin/admin-groups"; import { ApiRequestInstance } from "~/types/api"; export class AdminAPI { @@ -8,6 +9,7 @@ export class AdminAPI { public about: AdminAboutAPI; public serverTasks: AdminTaskAPI; public users: AdminUsersApi; + public groups: AdminGroupsApi; constructor(requests: ApiRequestInstance) { if (AdminAPI.instance instanceof AdminAPI) { @@ -17,6 +19,7 @@ export class AdminAPI { this.about = new AdminAboutAPI(requests); this.serverTasks = new AdminTaskAPI(requests); this.users = new AdminUsersApi(requests); + this.groups = new AdminGroupsApi(requests); Object.freeze(this); AdminAPI.instance = this; diff --git a/frontend/api/admin/admin-about.ts b/frontend/api/admin/admin-about.ts index 01517f7ea3f7..dac818f28c73 100644 --- a/frontend/api/admin/admin-about.ts +++ b/frontend/api/admin/admin-about.ts @@ -31,6 +31,7 @@ export interface CheckAppConfig { emailReady: boolean; baseUrlSet: boolean; isSiteSecure: boolean; + ldapReady: boolean; } export class AdminAboutAPI extends BaseAPI { diff --git a/frontend/api/admin/admin-groups.ts b/frontend/api/admin/admin-groups.ts new file mode 100644 index 000000000000..5e76544db917 --- /dev/null +++ b/frontend/api/admin/admin-groups.ts @@ -0,0 +1,54 @@ +import { BaseCRUDAPI } from "../_base"; +import { UserRead } from "./admin-users"; +const prefix = "/api"; + +export interface Token { + name: string; + id: number; + createdAt: Date; +} + +export interface Preferences { + privateGroup: boolean; + firstDayOfWeek: number; + recipePublic: boolean; + recipeShowNutrition: boolean; + recipeShowAssets: boolean; + recipeLandscapeView: boolean; + recipeDisableComments: boolean; + recipeDisableAmount: boolean; + groupId: number; + id: number; +} + +export interface GroupCreate { + name: string; +} + +export interface GroupRead extends GroupCreate { + id: number; + categories: any[]; + webhooks: any[]; + users: UserRead[]; + preferences: Preferences; +} + +export interface AdminGroupUpdate { + name: string; + id: number; + preferences: Preferences; +} + +const routes = { + adminUsers: `${prefix}/admin/groups`, + adminUsersId: (id: number) => `${prefix}/admin/groups/${id}`, +}; + +export class AdminGroupsApi extends BaseCRUDAPI { + baseRoute: string = routes.adminUsers; + itemRoute = routes.adminUsersId; + + async updateOne(id: number, payload: AdminGroupUpdate) { + return await this.requests.put(this.itemRoute(id), payload); + } +} diff --git a/frontend/api/admin/admin-users.ts b/frontend/api/admin/admin-users.ts index 314a383fbd46..3f15d5efa024 100644 --- a/frontend/api/admin/admin-users.ts +++ b/frontend/api/admin/admin-users.ts @@ -2,7 +2,7 @@ import { BaseCRUDAPI } from "../_base"; const prefix = "/api"; -interface UserCreate { +export interface UserCreate { username: string; fullName: string; email: string; @@ -21,7 +21,7 @@ export interface UserToken { createdAt: Date; } -interface UserRead extends UserToken { +export interface UserRead extends UserToken { id: number; groupId: number; favoriteRecipes: any[]; diff --git a/frontend/components/Domain/Group/GroupPreferencesEditor.vue b/frontend/components/Domain/Group/GroupPreferencesEditor.vue new file mode 100644 index 000000000000..64c3dc8000a5 --- /dev/null +++ b/frontend/components/Domain/Group/GroupPreferencesEditor.vue @@ -0,0 +1,99 @@ + + + + + \ No newline at end of file diff --git a/frontend/components/Domain/Recipe/RecipeActionMenu.vue b/frontend/components/Domain/Recipe/RecipeActionMenu.vue index fafe17566f0f..cd9e6123e88f 100644 --- a/frontend/components/Domain/Recipe/RecipeActionMenu.vue +++ b/frontend/components/Domain/Recipe/RecipeActionMenu.vue @@ -8,7 +8,7 @@ style="z-index: 2; position: sticky" >
- -