From 9707177c43889feb73feaa7d74d525689a207d55 Mon Sep 17 00:00:00 2001 From: BradJKim Date: Wed, 2 Aug 2023 21:19:05 -0700 Subject: [PATCH 01/12] init commit, small changes in mesh/settings.py --- mesh/settings.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/mesh/settings.py b/mesh/settings.py index a307905c..b04f84b1 100644 --- a/mesh/settings.py +++ b/mesh/settings.py @@ -12,9 +12,6 @@ from pathlib import Path import os -from dotenv import load_dotenv - -load_dotenv() # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent From 8c6e693f7bc8221a43f62a22634477e5a5f1a4f8 Mon Sep 17 00:00:00 2001 From: BradJKim Date: Mon, 7 Aug 2023 17:55:01 -0700 Subject: [PATCH 02/12] Basic Settings page made and 2FactorAuth toggle added --- meshapp/src/Settings/settings-page.tsx | 31 ++++++++++++++++++++++++++ meshapp/src/index.tsx | 2 ++ 2 files changed, 33 insertions(+) create mode 100644 meshapp/src/Settings/settings-page.tsx diff --git a/meshapp/src/Settings/settings-page.tsx b/meshapp/src/Settings/settings-page.tsx new file mode 100644 index 00000000..da8b3846 --- /dev/null +++ b/meshapp/src/Settings/settings-page.tsx @@ -0,0 +1,31 @@ +import React, { useState, useEffect } from "react"; +import { + Divider, + FormGroup, + FormControlLabel, + Switch, + Typography } + from "@mui/material"; + +export default function Settings() { + return ( + + + Settings + + + + + + ) +}; + +const TwoFactorAuth = () => { + return ( + + + } label="2 Factor Authentication" /> + + + ) +} diff --git a/meshapp/src/index.tsx b/meshapp/src/index.tsx index e256e22c..860585f0 100644 --- a/meshapp/src/index.tsx +++ b/meshapp/src/index.tsx @@ -11,6 +11,7 @@ import { ThemeProvider } from "@emotion/react"; import ProfilePage from "./profile/profile-page"; import ForgotPassword from "./components/password-forms/forgot-password-form" import LoggedInHome from "./home/logged-in/LoggedInHome"; +import Settings from "./Settings/settings-page" import { exampleProfile, @@ -30,6 +31,7 @@ root.render( + ); From c28559100ba8da09d78da812e240f1b3b746d44d Mon Sep 17 00:00:00 2001 From: BradJKim Date: Sat, 19 Aug 2023 13:02:14 -0700 Subject: [PATCH 03/12] added type folder in settings and implemented up account_settings type file --- meshapp/src/Settings/types/account-settings.d.ts | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 meshapp/src/Settings/types/account-settings.d.ts diff --git a/meshapp/src/Settings/types/account-settings.d.ts b/meshapp/src/Settings/types/account-settings.d.ts new file mode 100644 index 00000000..c5c123ae --- /dev/null +++ b/meshapp/src/Settings/types/account-settings.d.ts @@ -0,0 +1,8 @@ +export type AccountSettings = { + accountID: number; + isVerified: boolean; + verificationToken: string; + hasContentFilterEnabled: boolean; + displayTheme: number; + is2FAEnabled: boolean; +}; \ No newline at end of file From b282259f1d5257a497118f4f4922d544b947dc2b Mon Sep 17 00:00:00 2001 From: BradJKim Date: Sat, 19 Aug 2023 13:40:32 -0700 Subject: [PATCH 04/12] implemented account_setting type into settings-page, changed TwoFactAuth render conditional to settings set by user --- meshapp/src/Settings/settings-page.tsx | 31 +++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/meshapp/src/Settings/settings-page.tsx b/meshapp/src/Settings/settings-page.tsx index da8b3846..4fc85aaf 100644 --- a/meshapp/src/Settings/settings-page.tsx +++ b/meshapp/src/Settings/settings-page.tsx @@ -7,25 +7,46 @@ import { Typography } from "@mui/material"; -export default function Settings() { +import { AccountSettings } from "./types/account-settings"; + +/** + * React component that renders the settings page + * Displays settings that are set by the user for their account + * + * @param props - Properties of component + * @param {number} props.accountID - user accoutn ID + * @param {boolean} props.isVerified - flag to check if user is verified + * @param {string} props.verificationToken - token for verification + * @param {boolean} props.hasContentFilterEnabled - flag to check if content is filtered + * @param {number} props.displayTheme - display theme + * @param {boolean} props.is2FAEnabled - flag to check if user is registered for 2FactAuth + */ +export default function Settings(props: AccountSettings) { return ( Settings - + ) }; -const TwoFactorAuth = () => { +/** + * Display user's 2FactAuth setting + * + * @param props - Properties of component + * @param {boolean} props.is2FAEnabled - flag to check if user is registered for 2FactAuth + */ +const TwoFactorAuth = (props: { is2FAEnabled: boolean }) => { + const {is2FAEnabled} = props return ( - } label="2 Factor Authentication" /> + : } label="2 Factor Authentication: " /> ) -} +} \ No newline at end of file From 478e5734e94c60304616654ed99390689e065765 Mon Sep 17 00:00:00 2001 From: BradJKim Date: Sun, 20 Aug 2023 20:16:33 -0700 Subject: [PATCH 05/12] url update for 2factauth, implemented class-based request handling and set up get request --- mesh/accountSettings/urls.py | 3 ++- mesh/accountSettings/views.py | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/mesh/accountSettings/urls.py b/mesh/accountSettings/urls.py index a0ddc08b..38386e4f 100644 --- a/mesh/accountSettings/urls.py +++ b/mesh/accountSettings/urls.py @@ -2,5 +2,6 @@ from . import views as accountsettings_views urlpatterns = [ - path("displayTheme", accountsettings_views.display_theme, name="display_theme") + path("displayTheme", accountsettings_views.display_theme, name="display_theme"), + path("twoFactAuth", accountsettings_views.two_fact_auth, name="two_fact_auth") ] diff --git a/mesh/accountSettings/views.py b/mesh/accountSettings/views.py index 3c07ac92..0ffac27c 100644 --- a/mesh/accountSettings/views.py +++ b/mesh/accountSettings/views.py @@ -1,5 +1,7 @@ from django.core.exceptions import ObjectDoesNotExist from django.http import JsonResponse +from django.views import View +from django.core import serializers from mesh.accountSettings.models import Settings @@ -29,3 +31,32 @@ def display_theme(request): response.update({"status": "error"}) response.update({"message": "An account does not exist with this account ID."}) return JsonResponse(response) + +class TwoFactAuthView(View): + """ + Handles HTTP requests related to Two Factor Authentication account settings. + Support Get request to retrieve setting and patch to change setting. + """ + def get(self, request, account_id, *args, **kwargs): + """ + Handles GET request + + Return JSON with Two Factor Authentication Settings for given account ID. + Returns a 404 error with error message if account does not exist. + """ + try: + settings_2FactAuth = Settings.objects.get(accountID=account_id).is2FAEnabled + settings_detail = serializers.serialize('json', [settings_2FactAuth]) + return JsonResponse(settings_detail, safe=False) + except ObjectDoesNotExist: + return JsonResponse({'error': 'An account does not exist with this account ID.'}, status=404) + + + def patch(): + """ + Handles Patch request + + t + """ + + From 386df50d35275a2b007af55d1991343484307ab1 Mon Sep 17 00:00:00 2001 From: BradJKim Date: Wed, 23 Aug 2023 15:24:01 -0700 Subject: [PATCH 06/12] url update for 2factauth, implemented class-based request handling and set up get request, updated url path in include class based request handling --- mesh/accountSettings/urls.py | 2 +- mesh/accountSettings/views.py | 27 ++++++++++++++++++++++----- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/mesh/accountSettings/urls.py b/mesh/accountSettings/urls.py index 38386e4f..64d78833 100644 --- a/mesh/accountSettings/urls.py +++ b/mesh/accountSettings/urls.py @@ -3,5 +3,5 @@ urlpatterns = [ path("displayTheme", accountsettings_views.display_theme, name="display_theme"), - path("twoFactAuth", accountsettings_views.two_fact_auth, name="two_fact_auth") + path("twoFactAuth", accountsettings_views.TwoFactAuthView.two_fact_auth, name="two_fact_auth") ] diff --git a/mesh/accountSettings/views.py b/mesh/accountSettings/views.py index 0ffac27c..7c5ea9c9 100644 --- a/mesh/accountSettings/views.py +++ b/mesh/accountSettings/views.py @@ -1,10 +1,12 @@ from django.core.exceptions import ObjectDoesNotExist -from django.http import JsonResponse +from django.http import JsonResponse, HttpResponse from django.views import View from django.core import serializers from mesh.accountSettings.models import Settings +from mesh.accounts.models import Account +import json def display_theme(request): if request.method == "GET": @@ -48,15 +50,30 @@ def get(self, request, account_id, *args, **kwargs): settings_2FactAuth = Settings.objects.get(accountID=account_id).is2FAEnabled settings_detail = serializers.serialize('json', [settings_2FactAuth]) return JsonResponse(settings_detail, safe=False) - except ObjectDoesNotExist: + except Account.DoesNotExist: return JsonResponse({'error': 'An account does not exist with this account ID.'}, status=404) + except Settings.DoesNotExist: + return JsonResponse({'error': 'Settings does not exist with this account ID.'}, status=404) - def patch(): + def patch(self, request, account_id, *args, **kwargs): """ - Handles Patch request + Handles Patch request. - t + Updates the 2FactAuth account setting information to either true or false. + + Returns 204 HTTP response upon success. + Returns 404 if Account or Settings is not found """ + try: + settings = Settings.objects.get(accountID=account_id) + data = json.loads(request.body) + settings.is2FAEnabled = data.get('is2FAEnabled', settings.is2FAEnabled) + settings.save() + return HttpResponse(status=204) + except Account.DoesNotExist: + return JsonResponse({'error': 'An account does not exist with this account ID.'}, status=404) + except Settings.DoesNotExist: + return JsonResponse({'error': 'Settings does not exist with this account ID.'}, status=404) From 8d31849496a45aa5a6002700b80c66fed007a239 Mon Sep 17 00:00:00 2001 From: BradJKim Date: Sun, 27 Aug 2023 13:56:22 -0700 Subject: [PATCH 07/12] changed design approach --- meshapp/src/Settings/settings-page.tsx | 100 +++++++++++++------------ meshapp/src/index.tsx | 2 +- 2 files changed, 53 insertions(+), 49 deletions(-) diff --git a/meshapp/src/Settings/settings-page.tsx b/meshapp/src/Settings/settings-page.tsx index 4fc85aaf..e910fa15 100644 --- a/meshapp/src/Settings/settings-page.tsx +++ b/meshapp/src/Settings/settings-page.tsx @@ -1,52 +1,56 @@ -import React, { useState, useEffect } from "react"; -import { - Divider, - FormGroup, - FormControlLabel, - Switch, - Typography } - from "@mui/material"; +import React, { useState, useEffect } from 'react'; +import { Switch, FormControlLabel, Container, Typography } from '@mui/material'; -import { AccountSettings } from "./types/account-settings"; +//import { AccountSettings } from "./types/account-settings"; +import { axiosInstance } from "../config/axiosConfig"; -/** - * React component that renders the settings page - * Displays settings that are set by the user for their account - * - * @param props - Properties of component - * @param {number} props.accountID - user accoutn ID - * @param {boolean} props.isVerified - flag to check if user is verified - * @param {string} props.verificationToken - token for verification - * @param {boolean} props.hasContentFilterEnabled - flag to check if content is filtered - * @param {number} props.displayTheme - display theme - * @param {boolean} props.is2FAEnabled - flag to check if user is registered for 2FactAuth - */ -export default function Settings(props: AccountSettings) { - return ( - - - Settings - - - - - - ) -}; +const SettingSwitch = (label: any, value: any, onChange: any) => { + return ( + } + label={label} + /> + ); +} -/** - * Display user's 2FactAuth setting - * - * @param props - Properties of component - * @param {boolean} props.is2FAEnabled - flag to check if user is registered for 2FactAuth - */ -const TwoFactorAuth = (props: { is2FAEnabled: boolean }) => { - const {is2FAEnabled} = props - return ( - - - : } label="2 Factor Authentication: " /> - - - ) +export default function SettingsPage() { + const [settings, setSettings] = useState({ + isVerified: false, + is2FAEnabled: false + }); + const [loading, setLoading] = useState(false); + + const handleToggleChange = (settingName: any) => (event: any) => { + setSettings({ ...settings, [settingName]: event.target.checked }); + }; + + useEffect(() => { + // Make a GET request to the backend API to retrieve account settings + axiosInstance.get('/accountSettings/twoFactAuth') + .then((response) => { + setSettings(response.data); + setLoading(false); + }) + .catch((error) => { + console.error('Error fetching account settings:', error); + setLoading(false); + }); + }, []); // Empty dependency array to run the effect once when the component mounts + + if (loading) { + return
Loading...
; + } + + return ( + + + Account Settings + + + + ); } \ No newline at end of file diff --git a/meshapp/src/index.tsx b/meshapp/src/index.tsx index 860585f0..e39337b2 100644 --- a/meshapp/src/index.tsx +++ b/meshapp/src/index.tsx @@ -31,7 +31,7 @@ root.render( - + ); From 4ce1f26143f27fc14c153e3bf606309747098252 Mon Sep 17 00:00:00 2001 From: BradJKim Date: Sun, 27 Aug 2023 14:26:57 -0700 Subject: [PATCH 08/12] changed backend requests to respond with all account settings --- meshapp/src/Settings/settings-page.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/meshapp/src/Settings/settings-page.tsx b/meshapp/src/Settings/settings-page.tsx index e910fa15..8a1d33fd 100644 --- a/meshapp/src/Settings/settings-page.tsx +++ b/meshapp/src/Settings/settings-page.tsx @@ -4,7 +4,7 @@ import { Switch, FormControlLabel, Container, Typography } from '@mui/material'; //import { AccountSettings } from "./types/account-settings"; import { axiosInstance } from "../config/axiosConfig"; -const SettingSwitch = (label: any, value: any, onChange: any) => { +function SettingSwitch(label: any, value: any, onChange: any) { return ( } @@ -15,10 +15,10 @@ const SettingSwitch = (label: any, value: any, onChange: any) => { export default function SettingsPage() { const [settings, setSettings] = useState({ - isVerified: false, + //isVerified: false, is2FAEnabled: false }); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); const handleToggleChange = (settingName: any) => (event: any) => { setSettings({ ...settings, [settingName]: event.target.checked }); @@ -33,7 +33,7 @@ export default function SettingsPage() { }) .catch((error) => { console.error('Error fetching account settings:', error); - setLoading(false); + setLoading(true); }); }, []); // Empty dependency array to run the effect once when the component mounts From a560b21fb11d603bd22436576fbe32025807d59c Mon Sep 17 00:00:00 2001 From: BradJKim Date: Thu, 31 Aug 2023 15:44:26 -0700 Subject: [PATCH 09/12] big fixing, SettingsProps Interface --- mesh/accountSettings/urls.py | 2 +- mesh/accountSettings/views.py | 13 +++++++++---- meshapp/src/Settings/settings-page.tsx | 26 +++++++++++++++++--------- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/mesh/accountSettings/urls.py b/mesh/accountSettings/urls.py index 64d78833..0ae82e44 100644 --- a/mesh/accountSettings/urls.py +++ b/mesh/accountSettings/urls.py @@ -3,5 +3,5 @@ urlpatterns = [ path("displayTheme", accountsettings_views.display_theme, name="display_theme"), - path("twoFactAuth", accountsettings_views.TwoFactAuthView.two_fact_auth, name="two_fact_auth") + path("settings", accountsettings_views.showSettings.as_view(), name="settings") ] diff --git a/mesh/accountSettings/views.py b/mesh/accountSettings/views.py index 7c5ea9c9..541003c3 100644 --- a/mesh/accountSettings/views.py +++ b/mesh/accountSettings/views.py @@ -34,7 +34,7 @@ def display_theme(request): response.update({"message": "An account does not exist with this account ID."}) return JsonResponse(response) -class TwoFactAuthView(View): +class showSettings(View): """ Handles HTTP requests related to Two Factor Authentication account settings. Support Get request to retrieve setting and patch to change setting. @@ -47,8 +47,8 @@ def get(self, request, account_id, *args, **kwargs): Returns a 404 error with error message if account does not exist. """ try: - settings_2FactAuth = Settings.objects.get(accountID=account_id).is2FAEnabled - settings_detail = serializers.serialize('json', [settings_2FactAuth]) + settings = Settings.objects.get(accountID=account_id) + settings_detail = serializers.serialize('json', [settings]) return JsonResponse(settings_detail, safe=False) except Account.DoesNotExist: return JsonResponse({'error': 'An account does not exist with this account ID.'}, status=404) @@ -68,7 +68,12 @@ def patch(self, request, account_id, *args, **kwargs): try: settings = Settings.objects.get(accountID=account_id) data = json.loads(request.body) - settings.is2FAEnabled = data.get('is2FAEnabled', settings.is2FAEnabled) + settings.accountID = data.get('accountID', settings) + settings.isVerified = data.get('isVerified', settings) + settings.verificationToken = data.get('verificationToken', settings) + settings.hasContentFilterEnabled = data.get('hasContentFilterEnabled', settings) + settings.displayTheme = data.get('displayTheme', settings) + settings.is2FAEnabled = data.get('is2FAEnabled', settings) settings.save() return HttpResponse(status=204) except Account.DoesNotExist: diff --git a/meshapp/src/Settings/settings-page.tsx b/meshapp/src/Settings/settings-page.tsx index 8a1d33fd..1c3de99d 100644 --- a/meshapp/src/Settings/settings-page.tsx +++ b/meshapp/src/Settings/settings-page.tsx @@ -4,29 +4,35 @@ import { Switch, FormControlLabel, Container, Typography } from '@mui/material'; //import { AccountSettings } from "./types/account-settings"; import { axiosInstance } from "../config/axiosConfig"; -function SettingSwitch(label: any, value: any, onChange: any) { +interface SettingsProps { + value: any, + label: string, + onChange: (event: any) => void, +} + +const SettingSwitch = (props: SettingsProps) => { return ( } - label={label} + control={} + label={props.label} /> ); } -export default function SettingsPage() { +const SettingsPage = () => { const [settings, setSettings] = useState({ //isVerified: false, is2FAEnabled: false }); - const [loading, setLoading] = useState(true); + const [loading, setLoading] = useState(false); const handleToggleChange = (settingName: any) => (event: any) => { setSettings({ ...settings, [settingName]: event.target.checked }); }; - useEffect(() => { + /* useEffect(() => { // Make a GET request to the backend API to retrieve account settings - axiosInstance.get('/accountSettings/twoFactAuth') + axiosInstance.get('/accountSettings/settings') .then((response) => { setSettings(response.data); setLoading(false); @@ -35,7 +41,7 @@ export default function SettingsPage() { console.error('Error fetching account settings:', error); setLoading(true); }); - }, []); // Empty dependency array to run the effect once when the component mounts + }, []); // Empty dependency array to run the effect once when the component mounts */ if (loading) { return
Loading...
; @@ -53,4 +59,6 @@ export default function SettingsPage() { /> ); -} \ No newline at end of file +} + +export default SettingsPage; \ No newline at end of file From 6e2aa4c5d9ccae6badeb7c8caf259fff28f69579 Mon Sep 17 00:00:00 2001 From: BradJKim Date: Fri, 1 Sep 2023 15:45:56 -0700 Subject: [PATCH 10/12] updated url to include account_id and created testing for get settings request --- mesh/accountSettings/urls.py | 5 ++++- mesh/accountSettings/views.py | 12 +++++------ mesh/tests/settings_tests.py | 40 +++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 7 deletions(-) diff --git a/mesh/accountSettings/urls.py b/mesh/accountSettings/urls.py index 0ae82e44..5d7ff09d 100644 --- a/mesh/accountSettings/urls.py +++ b/mesh/accountSettings/urls.py @@ -3,5 +3,8 @@ urlpatterns = [ path("displayTheme", accountsettings_views.display_theme, name="display_theme"), - path("settings", accountsettings_views.showSettings.as_view(), name="settings") + + # GET /api/settings/:account_id + # PATCH /api/settings/:account_id + path("settings/", accountsettings_views.showSettings.as_view(), name="settings") ] diff --git a/mesh/accountSettings/views.py b/mesh/accountSettings/views.py index 541003c3..2b3d5987 100644 --- a/mesh/accountSettings/views.py +++ b/mesh/accountSettings/views.py @@ -68,12 +68,12 @@ def patch(self, request, account_id, *args, **kwargs): try: settings = Settings.objects.get(accountID=account_id) data = json.loads(request.body) - settings.accountID = data.get('accountID', settings) - settings.isVerified = data.get('isVerified', settings) - settings.verificationToken = data.get('verificationToken', settings) - settings.hasContentFilterEnabled = data.get('hasContentFilterEnabled', settings) - settings.displayTheme = data.get('displayTheme', settings) - settings.is2FAEnabled = data.get('is2FAEnabled', settings) + settings.accountID = data.get('accountID') + settings.isVerified = data.get('isVerified') + settings.verificationToken = data.get('verificationToken') + settings.hasContentFilterEnabled = data.get('hasContentFilterEnabled') + settings.displayTheme = data.get('displayTheme') + settings.is2FAEnabled = data.get('is2FAEnabled') settings.save() return HttpResponse(status=204) except Account.DoesNotExist: diff --git a/mesh/tests/settings_tests.py b/mesh/tests/settings_tests.py index d8c097d8..fecedeaa 100644 --- a/mesh/tests/settings_tests.py +++ b/mesh/tests/settings_tests.py @@ -44,3 +44,43 @@ def test_no_account_display_theme(self): json_response = json.loads(response.content.decode("utf-8")) self.assertEquals(json_response.get("status"), "error") self.assertEquals(json_response.get("message"), "An account does not exist with this account ID.") + + """ + Settings Testing + """ + def test_get_settings(self): + """ + Test case to see if settings are retrieved from account settings + + GET request to /accountSettings/settings endpoint. + Test passes if 200 response status code is returned + """ + test_user = Account.objects.get(email="settingstest@gmail.com") + response = self.client.get(f"/settings/{test_user.accountID}/") + self.assertEqual(response.status_code, 200) + + '''def test_update_settings(self): + """ + Test case to see if settings are patched or update to account settings + + Patch request to /accountSettings/settings endpoint. + Test passes if 200 response status code is returned + """ + test_user_settings = Settings.objects.get(email="settingstest@gmail.com") + + new_account_data = { + "accountID": test_user, + "isVerified": False, + "verificationToken": None, + "hasContentFilterEnabled": False, + "displayTheme": "0", + "is2FAEnabled": False, + } + + """ json_response = json.loads(response.content.decode("utf-8")) """ + self.assertEqual(test_user_settings.accountID, ) + self.assertEqual(test_user_settings.isVerified, ) + self.assertEqual(test_user_settings.verificationToken, ) + self.assertEqual(test_user_settings.hasContentFilterEnabled, ) + self.assertEqual(test_user_settings.displayTheme, ) + self.assertEqual(test_user_settings.is2FAEnabled, )''' \ No newline at end of file From c769abfdecf7510fde66ece3d96faf3753ffe57a Mon Sep 17 00:00:00 2001 From: BradJKim Date: Wed, 13 Sep 2023 18:19:17 -0700 Subject: [PATCH 11/12] Completed GET and PATCH requests from frontend, working update on server side --- mesh/accountSettings/views.py | 2 +- mesh/settings.py | 9 +++- mesh/tests/settings_tests.py | 41 ++++++++++------ meshapp/src/Settings/settings-page.tsx | 49 ++++++++++++++----- .../src/Settings/tests/settings-examples.tsx | 10 ++++ .../src/Settings/types/account-settings.d.ts | 2 +- meshapp/src/index.tsx | 3 +- 7 files changed, 83 insertions(+), 33 deletions(-) create mode 100644 meshapp/src/Settings/tests/settings-examples.tsx diff --git a/mesh/accountSettings/views.py b/mesh/accountSettings/views.py index 2b3d5987..20e391e2 100644 --- a/mesh/accountSettings/views.py +++ b/mesh/accountSettings/views.py @@ -68,7 +68,7 @@ def patch(self, request, account_id, *args, **kwargs): try: settings = Settings.objects.get(accountID=account_id) data = json.loads(request.body) - settings.accountID = data.get('accountID') + settings.accountID = Account.objects.get(accountID=data.get('accountID')) settings.isVerified = data.get('isVerified') settings.verificationToken = data.get('verificationToken') settings.hasContentFilterEnabled = data.get('hasContentFilterEnabled') diff --git a/mesh/settings.py b/mesh/settings.py index b04f84b1..21012ad2 100644 --- a/mesh/settings.py +++ b/mesh/settings.py @@ -46,8 +46,8 @@ 'mesh.conversation', 'mesh.notifications', 'mesh.tags', - 'mesh.occupations' - + 'mesh.occupations', + 'corsheaders', ] # TODO: https://github.com/LetsMesh/Site/issues/202 @@ -55,6 +55,7 @@ MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', + 'corsheaders.middleware.CorsMiddleware', 'django.middleware.common.CommonMiddleware', # 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', @@ -62,6 +63,10 @@ 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] +ALLOWED_HOSTS=[ + +] + CORS_ORIGIN_WHITELIST = [ 'http://localhost:3000', ] diff --git a/mesh/tests/settings_tests.py b/mesh/tests/settings_tests.py index fecedeaa..7060fd06 100644 --- a/mesh/tests/settings_tests.py +++ b/mesh/tests/settings_tests.py @@ -9,7 +9,7 @@ class SettingsTest(TestCase): def setUp(self): self.client = Client() - test_account = Account.objects.create( + self.test_account = Account.objects.create( email="settingstest@gmail.com", encryptedPass=bytes("password_test", "utf-8"), phoneNum="1234567890", @@ -19,7 +19,7 @@ def setUp(self): isMentee=True ) Settings.objects.create( - accountID=test_account, + accountID=self.test_account, isVerified=False, verificationToken=None, hasContentFilterEnabled=False, @@ -52,23 +52,26 @@ def test_get_settings(self): """ Test case to see if settings are retrieved from account settings - GET request to /accountSettings/settings endpoint. + GET request to /settings/settings endpoint. Test passes if 200 response status code is returned """ test_user = Account.objects.get(email="settingstest@gmail.com") - response = self.client.get(f"/settings/{test_user.accountID}/") + response = self.client.get(f"/settings/settings/{test_user.accountID}/") self.assertEqual(response.status_code, 200) - '''def test_update_settings(self): + def test_update_settings(self): """ Test case to see if settings are patched or update to account settings - Patch request to /accountSettings/settings endpoint. - Test passes if 200 response status code is returned + Patch request to /settings/settings endpoint. + Test passes if 204 response status code is returned """ - test_user_settings = Settings.objects.get(email="settingstest@gmail.com") + test_user = Account.objects.get(email="settingstest@gmail.com") + test_user_settings = Settings.objects.get(accountID=test_user) + + print(f"Test User: {test_user}") - new_account_data = { + updated_account_data = { "accountID": test_user, "isVerified": False, "verificationToken": None, @@ -77,10 +80,16 @@ def test_get_settings(self): "is2FAEnabled": False, } - """ json_response = json.loads(response.content.decode("utf-8")) """ - self.assertEqual(test_user_settings.accountID, ) - self.assertEqual(test_user_settings.isVerified, ) - self.assertEqual(test_user_settings.verificationToken, ) - self.assertEqual(test_user_settings.hasContentFilterEnabled, ) - self.assertEqual(test_user_settings.displayTheme, ) - self.assertEqual(test_user_settings.is2FAEnabled, )''' \ No newline at end of file + json_data = json.dumps(updated_account_data, default=str) + + response = self.client.patch(f'/settings/settings/{test_user_settings.accountID}/', json_data, content_type='application/json') + self.assertEqual(response.status_code, 204) + + updated_test_user = Account.objects.get(email="settingstest@gmail.com") + updated_test_user_settings = Settings.objects.get(accountID=updated_test_user) + self.assertEqual(updated_test_user_settings.accountID, updated_account_data['accountID']) + self.assertEqual(updated_test_user_settings.isVerified, updated_account_data['isVerified']) + self.assertEqual(updated_test_user_settings.verificationToken, updated_account_data['verificationToken']) + self.assertEqual(updated_test_user_settings.hasContentFilterEnabled, updated_account_data['hasContentFilterEnabled']) + self.assertEqual(updated_test_user_settings.displayTheme, updated_account_data['displayTheme']) + self.assertEqual(updated_test_user_settings.is2FAEnabled, updated_account_data['is2FAEnabled']) \ No newline at end of file diff --git a/meshapp/src/Settings/settings-page.tsx b/meshapp/src/Settings/settings-page.tsx index 1c3de99d..85e3fed1 100644 --- a/meshapp/src/Settings/settings-page.tsx +++ b/meshapp/src/Settings/settings-page.tsx @@ -1,7 +1,7 @@ import React, { useState, useEffect } from 'react'; import { Switch, FormControlLabel, Container, Typography } from '@mui/material'; -//import { AccountSettings } from "./types/account-settings"; +import { AccountSettings } from "./types/account-settings" import { axiosInstance } from "../config/axiosConfig"; interface SettingsProps { @@ -19,29 +19,54 @@ const SettingSwitch = (props: SettingsProps) => { ); } -const SettingsPage = () => { +const SettingsPage = (props: AccountSettings) => { const [settings, setSettings] = useState({ - //isVerified: false, + accountID: props.accountID, + isVerified: props.isVerified, + verificationToken: props.verificationToken, + hasContentFilterEnabled: props.hasContentFilterEnabled, + displayTheme: props.displayTheme, is2FAEnabled: false }); const [loading, setLoading] = useState(false); - const handleToggleChange = (settingName: any) => (event: any) => { - setSettings({ ...settings, [settingName]: event.target.checked }); - }; + useEffect(() => { + // Make a GET request to update the settings page with user settings + axiosInstance.get("settings/settings/" + props.accountID + "/") // NOTE: settings/settings is old api, use accountSettings when merging + .then((response) => { + setLoading(false); + let settingsData = JSON.parse(response.data)[0]["fields"] + setSettings({...settings, + isVerified: settingsData.isVerified, + verificationToken: settingsData.verificationToken, + hasContentFilterEnabled: settingsData.hasContentFilterEnabled, + displayTheme: settingsData.displayTheme, + is2FAEnabled: settingsData.is2FAEnabled}); + }) + .catch((error) => { + console.error('Error patching account settings:', error); + setLoading(true); + }); + }, []); // Empty dependency array to run the effect once when the component mounts - /* useEffect(() => { - // Make a GET request to the backend API to retrieve account settings - axiosInstance.get('/accountSettings/settings') + useEffect(() => { + // Make a PATCH request to the backend API to update account settings + axiosInstance.patch("settings/settings/" + props.accountID + "/", {...settings}) // NOTE: Use accountSettings api name when merging .then((response) => { - setSettings(response.data); + console.log("Patch Response: " + response) setLoading(false); + console.log("After Set: " + settings.is2FAEnabled) }) .catch((error) => { - console.error('Error fetching account settings:', error); + console.error('Error patching account settings:', error); setLoading(true); }); - }, []); // Empty dependency array to run the effect once when the component mounts */ + }, [settings]) + + const handleToggleChange = (settingName: any) => (event: any) => { + console.log("Before Set: " + settings.is2FAEnabled) + setSettings({ ...settings, [settingName]: event.target.checked }); + }; if (loading) { return
Loading...
; diff --git a/meshapp/src/Settings/tests/settings-examples.tsx b/meshapp/src/Settings/tests/settings-examples.tsx new file mode 100644 index 00000000..3a965a66 --- /dev/null +++ b/meshapp/src/Settings/tests/settings-examples.tsx @@ -0,0 +1,10 @@ +import { AccountSettings } from "../types/account-settings" + +export const exampleSettings: AccountSettings = { + accountID: 2, + isVerified: false, + verificationToken: "", + hasContentFilterEnabled: false, + displayTheme: 0, + is2FAEnabled: true, +} \ No newline at end of file diff --git a/meshapp/src/Settings/types/account-settings.d.ts b/meshapp/src/Settings/types/account-settings.d.ts index c5c123ae..db848167 100644 --- a/meshapp/src/Settings/types/account-settings.d.ts +++ b/meshapp/src/Settings/types/account-settings.d.ts @@ -3,6 +3,6 @@ export type AccountSettings = { isVerified: boolean; verificationToken: string; hasContentFilterEnabled: boolean; - displayTheme: number; + displayTheme: char; is2FAEnabled: boolean; }; \ No newline at end of file diff --git a/meshapp/src/index.tsx b/meshapp/src/index.tsx index e39337b2..aa69e491 100644 --- a/meshapp/src/index.tsx +++ b/meshapp/src/index.tsx @@ -17,6 +17,7 @@ import { exampleProfile, exampleProfile2, } from "./profile/tests/profile-examples"; +import { exampleSettings } from "./Settings/tests/settings-examples"; import SignUp from "./components/SignUp/SignUp"; const root = ReactDOM.createRoot(document.getElementById("root")!); root.render( @@ -31,7 +32,7 @@ root.render( - + ); From a80019ffd8a91ade401093e06bb1719451cafee9 Mon Sep 17 00:00:00 2001 From: BradJKim Date: Fri, 15 Sep 2023 19:20:47 -0700 Subject: [PATCH 12/12] added comments and styled text to white --- meshapp/src/Settings/settings-page.tsx | 64 +++++++++++++++++++++----- 1 file changed, 52 insertions(+), 12 deletions(-) diff --git a/meshapp/src/Settings/settings-page.tsx b/meshapp/src/Settings/settings-page.tsx index 85e3fed1..978e0a8e 100644 --- a/meshapp/src/Settings/settings-page.tsx +++ b/meshapp/src/Settings/settings-page.tsx @@ -1,24 +1,61 @@ import React, { useState, useEffect } from 'react'; -import { Switch, FormControlLabel, Container, Typography } from '@mui/material'; +import { Switch, FormControlLabel, Container, Typography, createTheme, ThemeProvider } from '@mui/material'; import { AccountSettings } from "./types/account-settings" import { axiosInstance } from "../config/axiosConfig"; +const theme = createTheme({ + palette: { + mode: "light", + primary: { + main: "#1cba9a", + }, + }, + typography: { + h1: { + fontFamily: "cocogoose", + fontWeight: "bold", + color: "#ffffff", + }, + }, +}); + interface SettingsProps { value: any, label: string, onChange: (event: any) => void, } +/** + * React component that represents a single setting + * Represented through a React Switch + * + * @param props - properties of the component + */ const SettingSwitch = (props: SettingsProps) => { return ( - + } - label={props.label} - /> + label={{props.label}} + /> + ); } +/** + * React component to render settings page. + * Displays setting options for the account. + * + * @param props + * @param {number} props.accountID - ID for account that settings represent + * @param {boolean} props.isVerified - flag for is account if verified + * @param {string} props.verificationToken - Token for verification + * @param {boolean} props.hasContentFilterEnabled - flag for content filtering + * @param {char} props.displayTheme - Char for display theme + * @param {boolean} props.is2FAEnabled - flag for if account has TwoFactorAuthentication is enabled + * + */ const SettingsPage = (props: AccountSettings) => { const [settings, setSettings] = useState({ accountID: props.accountID, @@ -47,15 +84,19 @@ const SettingsPage = (props: AccountSettings) => { console.error('Error patching account settings:', error); setLoading(true); }); - }, []); // Empty dependency array to run the effect once when the component mounts + }, []); + /** + * NOTE: Second useEffect for sending patch request to update account settings each time settings is modified. + * The patch request is made every time the setting switch is changed. + * This may want to be changed in the future to ensure that only one + * patch request is made for multiple settings. + */ useEffect(() => { - // Make a PATCH request to the backend API to update account settings + // Make a PATCH request to the backend API to update account settings axiosInstance.patch("settings/settings/" + props.accountID + "/", {...settings}) // NOTE: Use accountSettings api name when merging - .then((response) => { - console.log("Patch Response: " + response) + .then(() => { setLoading(false); - console.log("After Set: " + settings.is2FAEnabled) }) .catch((error) => { console.error('Error patching account settings:', error); @@ -64,8 +105,7 @@ const SettingsPage = (props: AccountSettings) => { }, [settings]) const handleToggleChange = (settingName: any) => (event: any) => { - console.log("Before Set: " + settings.is2FAEnabled) - setSettings({ ...settings, [settingName]: event.target.checked }); + setSettings({ ...settings, [settingName]: event.target.checked }); // TODO: Implement Confirm Authentication for 2FactAuth }; if (loading) { @@ -74,7 +114,7 @@ const SettingsPage = (props: AccountSettings) => { return ( - + Account Settings