From ef67d2abbf53a9d237981054d4aca83a52f3efdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20L=C3=B6fgren?= <viclo211@student.liu.se> Date: Wed, 28 Apr 2021 11:19:10 +0000 Subject: [PATCH] Resolve "Replace None with sentinel" --- .../PresentationEditorPage.test.tsx | 11 +++ .../PresentationEditorPage.tsx | 6 +- .../src/pages/presentationEditor/styled.tsx | 4 +- client/src/pages/views/OperatorViewPage.tsx | 85 +++++++++---------- server/app/apis/alternatives.py | 15 ++-- server/app/apis/answers.py | 19 +++-- server/app/apis/auth.py | 4 +- server/app/apis/competitions.py | 33 +++---- server/app/apis/components.py | 38 +++++---- server/app/apis/media.py | 3 +- server/app/apis/questions.py | 18 ++-- server/app/apis/slides.py | 17 ++-- server/app/apis/teams.py | 13 +-- server/app/apis/users.py | 24 +++--- server/app/core/parsers.py | 17 ++++ server/app/database/controller/edit.py | 17 +--- 16 files changed, 181 insertions(+), 143 deletions(-) diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx index c645724e..5426152f 100644 --- a/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx +++ b/client/src/pages/presentationEditor/PresentationEditorPage.test.tsx @@ -27,8 +27,19 @@ it('renders presentation editor', () => { ], }, } + const typesRes: any = { + data: { + view_types: [ + { + name: '', + id: 0, + }, + ], + }, + } ;(mockedAxios.get as jest.Mock).mockImplementation((path: string, params?: any) => { if (path.startsWith('/api/competitions')) return Promise.resolve(competitionRes) + if (path.startsWith('/api/misc/types')) return Promise.resolve(typesRes) return Promise.resolve(citiesRes) }) render( diff --git a/client/src/pages/presentationEditor/PresentationEditorPage.tsx b/client/src/pages/presentationEditor/PresentationEditorPage.tsx index 65720dc9..80d35c5f 100644 --- a/client/src/pages/presentationEditor/PresentationEditorPage.tsx +++ b/client/src/pages/presentationEditor/PresentationEditorPage.tsx @@ -93,9 +93,9 @@ const PresentationEditorPage: React.FC = () => { const competition = useAppSelector((state) => state.editor.competition) const competitionLoading = useAppSelector((state) => state.editor.loading) useEffect(() => { + dispatch(getTypes()) dispatch(getEditorCompetition(competitionId)) dispatch(getCities()) - dispatch(getTypes()) }, []) const setActiveSlideId = (id: number) => { @@ -177,7 +177,7 @@ const PresentationEditorPage: React.FC = () => { Applicera ändringar på samtliga vyer </Typography> <ViewButton - activeView={activeViewTypeName === 'Audience'} + $activeView={activeViewTypeName === 'Audience'} variant="contained" color="secondary" onClick={() => changeView('Audience')} @@ -185,7 +185,7 @@ const PresentationEditorPage: React.FC = () => { Åskådarvy </ViewButton> <ViewButton - activeView={activeViewTypeName === 'Team'} + $activeView={activeViewTypeName === 'Team'} variant="contained" color="secondary" onClick={() => changeView('Team')} diff --git a/client/src/pages/presentationEditor/styled.tsx b/client/src/pages/presentationEditor/styled.tsx index ea266c5e..f68eb166 100644 --- a/client/src/pages/presentationEditor/styled.tsx +++ b/client/src/pages/presentationEditor/styled.tsx @@ -8,12 +8,12 @@ export const ToolBarContainer = styled(Toolbar)` ` interface ViewButtonProps { - activeView: boolean + $activeView: boolean } export const ViewButton = styled(Button)<ViewButtonProps>` margin-right: 8px; - background: ${(props) => (props.activeView ? '#5a0017' : undefined)}; + background: ${(props) => (props.$activeView ? '#5a0017' : undefined)}; ` export const ViewButtonClicked = styled(Button)` diff --git a/client/src/pages/views/OperatorViewPage.tsx b/client/src/pages/views/OperatorViewPage.tsx index 29f20917..e965a3ec 100644 --- a/client/src/pages/views/OperatorViewPage.tsx +++ b/client/src/pages/views/OperatorViewPage.tsx @@ -143,44 +143,42 @@ const OperatorViewPage: React.FC = () => { > <DialogTitle id="responsive-dialog-title">{'Koder för tävlingen'}</DialogTitle> <DialogContent> - <DialogContentText> - <ListItem> - <ListItemText primary={`Domare: ${presentation.code}`} /> - <Tooltip title="Kopiera kod" arrow> - <Button - onClick={() => { - navigator.clipboard.writeText(presentation.code) - }} - > - <FileCopyIcon fontSize="small" /> - </Button> - </Tooltip> - </ListItem> - <ListItem> - <ListItemText primary={`Tävlande: ${presentation.code}`} /> - <Tooltip title="Kopiera kod" arrow> - <Button - onClick={() => { - navigator.clipboard.writeText(presentation.code) - }} - > - <FileCopyIcon fontSize="small" /> - </Button> - </Tooltip> - </ListItem> - <ListItem> - <ListItemText primary={`Publik: ${presentation.code}`} /> - <Tooltip title="Kopiera kod" arrow> - <Button - onClick={() => { - navigator.clipboard.writeText(presentation.code) - }} - > - <FileCopyIcon fontSize="small" /> - </Button> - </Tooltip> - </ListItem> - </DialogContentText> + <ListItem> + <ListItemText primary={`Domare: ${presentation.code}`} /> + <Tooltip title="Kopiera kod" arrow> + <Button + onClick={() => { + navigator.clipboard.writeText(presentation.code) + }} + > + <FileCopyIcon fontSize="small" /> + </Button> + </Tooltip> + </ListItem> + <ListItem> + <ListItemText primary={`Tävlande: ${presentation.code}`} /> + <Tooltip title="Kopiera kod" arrow> + <Button + onClick={() => { + navigator.clipboard.writeText(presentation.code) + }} + > + <FileCopyIcon fontSize="small" /> + </Button> + </Tooltip> + </ListItem> + <ListItem> + <ListItemText primary={`Publik: ${presentation.code}`} /> + <Tooltip title="Kopiera kod" arrow> + <Button + onClick={() => { + navigator.clipboard.writeText(presentation.code) + }} + > + <FileCopyIcon fontSize="small" /> + </Button> + </Tooltip> + </ListItem> </DialogContent> <DialogActions> <Button autoFocus onClick={handleClose} color="primary"> @@ -304,11 +302,12 @@ const OperatorViewPage: React.FC = () => { }} > <List> - {teams.map((team) => ( - <ListItem key={team.id}> - {team.name} score: {team.question_answers}{' '} - </ListItem> - ))} + {teams && + teams.map((team) => ( + <ListItem key={team.id}> + {team.name} score: {team.question_answers}{' '} + </ListItem> + ))} </List> </Popover> </OperatorContainer> diff --git a/server/app/apis/alternatives.py b/server/app/apis/alternatives.py index 0ce74d53..94f6d6b1 100644 --- a/server/app/apis/alternatives.py +++ b/server/app/apis/alternatives.py @@ -4,14 +4,19 @@ from app.apis import item_response, list_response, protect_route from app.core.dto import QuestionAlternativeDTO from flask_restx import Resource from flask_restx import reqparse +from app.core.parsers import sentinel api = QuestionAlternativeDTO.api schema = QuestionAlternativeDTO.schema list_schema = QuestionAlternativeDTO.list_schema -question_alternative_parser = reqparse.RequestParser() -question_alternative_parser.add_argument("text", type=str, default=None, location="json") -question_alternative_parser.add_argument("value", type=int, default=None, location="json") +alternative_parser_add = reqparse.RequestParser() +alternative_parser_add.add_argument("text", type=str, required=True, location="json") +alternative_parser_add.add_argument("value", type=int, required=True, location="json") + +alternative_parser_edit = reqparse.RequestParser() +alternative_parser_edit.add_argument("text", type=str, default=sentinel, location="json") +alternative_parser_edit.add_argument("value", type=int, default=sentinel, location="json") @api.route("") @@ -24,7 +29,7 @@ class QuestionAlternativeList(Resource): @protect_route(allowed_roles=["*"]) def post(self, competition_id, slide_id, question_id): - args = question_alternative_parser.parse_args(strict=True) + args = alternative_parser_add.parse_args(strict=True) item = dbc.add.question_alternative(**args, question_id=question_id) return item_response(schema.dump(item)) @@ -39,7 +44,7 @@ class QuestionAlternatives(Resource): @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id, question_id, alternative_id): - args = question_alternative_parser.parse_args(strict=True) + args = alternative_parser_edit.parse_args(strict=True) item = dbc.get.question_alternative(competition_id, slide_id, question_id, alternative_id) item = dbc.edit.default(item, **args) return item_response(schema.dump(item)) diff --git a/server/app/apis/answers.py b/server/app/apis/answers.py index 8f72d3bd..3990e4e1 100644 --- a/server/app/apis/answers.py +++ b/server/app/apis/answers.py @@ -4,19 +4,20 @@ from app.apis import item_response, list_response, protect_route from app.core.dto import QuestionAnswerDTO from flask_restx import Resource from flask_restx import reqparse +from app.core.parsers import sentinel api = QuestionAnswerDTO.api schema = QuestionAnswerDTO.schema list_schema = QuestionAnswerDTO.list_schema -question_answer_parser = reqparse.RequestParser() -question_answer_parser.add_argument("answer", type=str, required=True, location="json") -question_answer_parser.add_argument("score", type=int, required=True, location="json") -question_answer_parser.add_argument("question_id", type=int, required=True, location="json") +answer_parser_add = reqparse.RequestParser() +answer_parser_add.add_argument("answer", type=str, required=True, location="json") +answer_parser_add.add_argument("score", type=int, required=True, location="json") +answer_parser_add.add_argument("question_id", type=int, required=True, location="json") -question_answer_edit_parser = reqparse.RequestParser() -question_answer_edit_parser.add_argument("answer", type=str, default=None, location="json") -question_answer_edit_parser.add_argument("score", type=int, default=None, location="json") +answer_parser_edit = reqparse.RequestParser() +answer_parser_edit.add_argument("answer", type=str, default=sentinel, location="json") +answer_parser_edit.add_argument("score", type=int, default=sentinel, location="json") @api.route("") @@ -29,7 +30,7 @@ class QuestionAnswerList(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def post(self, competition_id, team_id): - args = question_answer_parser.parse_args(strict=True) + args = answer_parser_add.parse_args(strict=True) item = dbc.add.question_answer(**args, team_id=team_id) return item_response(schema.dump(item)) @@ -44,7 +45,7 @@ class QuestionAnswers(Resource): @protect_route(allowed_roles=["*"], allowed_views=["*"]) def put(self, competition_id, team_id, answer_id): - args = question_answer_edit_parser.parse_args(strict=True) + args = answer_parser_edit.parse_args(strict=True) item = dbc.get.question_answer(competition_id, team_id, answer_id) item = dbc.edit.default(item, **args) return item_response(schema.dump(item)) diff --git a/server/app/apis/auth.py b/server/app/apis/auth.py index 9c9ed24a..d97eab2a 100644 --- a/server/app/apis/auth.py +++ b/server/app/apis/auth.py @@ -21,14 +21,14 @@ list_schema = AuthDTO.list_schema login_parser = reqparse.RequestParser() login_parser.add_argument("email", type=inputs.email(), required=True, location="json") -login_parser.add_argument("password", required=True, location="json") +login_parser.add_argument("password", type=str, required=True, location="json") create_user_parser = login_parser.copy() create_user_parser.add_argument("city_id", type=int, required=True, location="json") create_user_parser.add_argument("role_id", type=int, required=True, location="json") login_code_parser = reqparse.RequestParser() -login_code_parser.add_argument("code", type=str, location="json") +login_code_parser.add_argument("code", type=str, required=True, location="json") def get_user_claims(item_user): diff --git a/server/app/apis/competitions.py b/server/app/apis/competitions.py index d1fd9572..c5ff37c9 100644 --- a/server/app/apis/competitions.py +++ b/server/app/apis/competitions.py @@ -6,32 +6,35 @@ from app.core.dto import CompetitionDTO from app.database.models import Competition from flask_restx import Resource from flask_restx import reqparse -from app.core.parsers import search_parser +from app.core.parsers import search_parser, sentinel api = CompetitionDTO.api schema = CompetitionDTO.schema rich_schema = CompetitionDTO.rich_schema list_schema = CompetitionDTO.list_schema -competition_parser = reqparse.RequestParser() -competition_parser.add_argument("name", type=str, location="json") -competition_parser.add_argument("year", type=int, location="json") -competition_parser.add_argument("city_id", type=int, location="json") +competition_parser_add = reqparse.RequestParser() +competition_parser_add.add_argument("name", type=str, required=True, location="json") +competition_parser_add.add_argument("year", type=int, required=True, location="json") +competition_parser_add.add_argument("city_id", type=int, required=True, location="json") -competition_edit_parser = competition_parser.copy() -competition_edit_parser.add_argument("background_image_id", default=None, type=int, location="json") +competition_parser_edit = reqparse.RequestParser() +competition_parser_edit.add_argument("name", type=str, default=sentinel, location="json") +competition_parser_edit.add_argument("year", type=int, default=sentinel, location="json") +competition_parser_edit.add_argument("city_id", type=int, default=sentinel, location="json") +competition_parser_edit.add_argument("background_image_id", default=sentinel, type=int, location="json") -competition_search_parser = search_parser.copy() -competition_search_parser.add_argument("name", type=str, default=None, location="args") -competition_search_parser.add_argument("year", type=int, default=None, location="args") -competition_search_parser.add_argument("city_id", type=int, default=None, location="args") +competition_parser_search = search_parser.copy() +competition_parser_search.add_argument("name", type=str, default=sentinel, location="args") +competition_parser_search.add_argument("year", type=int, default=sentinel, location="args") +competition_parser_search.add_argument("city_id", type=int, default=sentinel, location="args") @api.route("") class CompetitionsList(Resource): @protect_route(allowed_roles=["*"]) def post(self): - args = competition_parser.parse_args(strict=True) + args = competition_parser_add.parse_args(strict=True) # Add competition item = dbc.add.competition(**args) @@ -52,9 +55,9 @@ class Competitions(Resource): @protect_route(allowed_roles=["*"]) def put(self, competition_id): - args = competition_edit_parser.parse_args(strict=True) + args = competition_parser_edit.parse_args(strict=True) item = dbc.get.one(Competition, competition_id) - item = dbc.edit.competition(item, **args) + item = dbc.edit.default(item, **args) return item_response(schema.dump(item)) @@ -70,7 +73,7 @@ class Competitions(Resource): class CompetitionSearch(Resource): @protect_route(allowed_roles=["*"]) def get(self): - args = competition_search_parser.parse_args(strict=True) + args = competition_parser_search.parse_args(strict=True) items, total = dbc.search.competition(**args) return list_response(list_schema.dump(items), total) diff --git a/server/app/apis/components.py b/server/app/apis/components.py index 43018695..32ab03c7 100644 --- a/server/app/apis/components.py +++ b/server/app/apis/components.py @@ -4,25 +4,29 @@ from app.apis import item_response, list_response, protect_route from app.core.dto import ComponentDTO from flask_restx import Resource from flask_restx import reqparse +from app.core.parsers import sentinel api = ComponentDTO.api schema = ComponentDTO.schema list_schema = ComponentDTO.list_schema +component_parser_add = reqparse.RequestParser() +component_parser_add.add_argument("x", type=str, default=0, location="json") +component_parser_add.add_argument("y", type=int, default=0, location="json") +component_parser_add.add_argument("w", type=int, default=1, location="json") +component_parser_add.add_argument("h", type=int, default=1, location="json") +component_parser_add.add_argument("type_id", type=int, required=True, location="json") +component_parser_add.add_argument("view_type_id", type=int, required=True, location="json") +component_parser_add.add_argument("text", type=str, default=None, location="json") +component_parser_add.add_argument("media_id", type=str, default=None, location="json") -component_parser = reqparse.RequestParser() -component_parser.add_argument("x", type=str, default=None, location="json") -component_parser.add_argument("y", type=int, default=None, location="json") -component_parser.add_argument("w", type=int, default=None, location="json") -component_parser.add_argument("h", type=int, default=None, location="json") - -component_edit_parser = component_parser.copy() -component_edit_parser.add_argument("text", type=str, location="json") -component_edit_parser.add_argument("media_id", type=str, location="json") - -component_create_parser = component_edit_parser.copy() -component_create_parser.add_argument("type_id", type=int, required=True, location="json") -component_create_parser.add_argument("view_type_id", type=int, required=True, location="json") +component_parser_edit = reqparse.RequestParser() +component_parser_edit.add_argument("x", type=str, default=sentinel, location="json") +component_parser_edit.add_argument("y", type=int, default=sentinel, location="json") +component_parser_edit.add_argument("w", type=int, default=sentinel, location="json") +component_parser_edit.add_argument("h", type=int, default=sentinel, location="json") +component_parser_edit.add_argument("text", type=str, default=sentinel, location="json") +component_parser_edit.add_argument("media_id", type=str, default=sentinel, location="json") @api.route("/<component_id>") @@ -35,10 +39,10 @@ class ComponentByID(Resource): @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id, component_id): - args = component_edit_parser.parse_args(strict=True) + args = component_parser_edit.parse_args(strict=True) item = dbc.get.component(competition_id, slide_id, component_id) - args_without_none = {key: value for key, value in args.items() if value is not None} - item = dbc.edit.default(item, **args_without_none) + args_without_sentinel = {key: value for key, value in args.items() if value is not sentinel} + item = dbc.edit.default(item, **args_without_sentinel) return item_response(schema.dump(item)) @protect_route(allowed_roles=["*"]) @@ -58,6 +62,6 @@ class ComponentList(Resource): @protect_route(allowed_roles=["*"]) def post(self, competition_id, slide_id): - args = component_create_parser.parse_args() + args = component_parser_add.parse_args() item = dbc.add.component(slide_id=slide_id, **args) return item_response(schema.dump(item)) diff --git a/server/app/apis/media.py b/server/app/apis/media.py index c7de8c4d..49d20608 100644 --- a/server/app/apis/media.py +++ b/server/app/apis/media.py @@ -10,6 +10,7 @@ from flask_restx import Resource from flask_uploads import UploadNotAllowed from sqlalchemy import exc import app.core.files as files +from app.core.parsers import sentinel api = MediaDTO.api image_set = MediaDTO.image_set @@ -17,7 +18,7 @@ schema = MediaDTO.schema list_schema = MediaDTO.list_schema media_parser_search = search_parser.copy() -media_parser_search.add_argument("filename", type=str, default=None, location="args") +media_parser_search.add_argument("filename", type=str, default=sentinel, location="args") @api.route("/images") diff --git a/server/app/apis/questions.py b/server/app/apis/questions.py index de40a310..b14849d2 100644 --- a/server/app/apis/questions.py +++ b/server/app/apis/questions.py @@ -4,15 +4,21 @@ from app.apis import item_response, list_response, protect_route from app.core.dto import QuestionDTO from flask_restx import Resource from flask_restx import reqparse +from app.core.parsers import sentinel api = QuestionDTO.api schema = QuestionDTO.schema list_schema = QuestionDTO.list_schema -question_parser = reqparse.RequestParser() -question_parser.add_argument("name", type=str, default=None, location="json") -question_parser.add_argument("total_score", type=int, default=None, location="json") -question_parser.add_argument("type_id", type=int, default=None, location="json") +question_parser_add = reqparse.RequestParser() +question_parser_add.add_argument("name", type=str, default=None, location="json") +question_parser_add.add_argument("total_score", type=int, default=None, location="json") +question_parser_add.add_argument("type_id", type=int, required=True, location="json") + +question_parser_edit = reqparse.RequestParser() +question_parser_edit.add_argument("name", type=str, default=sentinel, location="json") +question_parser_edit.add_argument("total_score", type=int, default=sentinel, location="json") +question_parser_edit.add_argument("type_id", type=int, default=sentinel, location="json") @api.route("/questions") @@ -34,7 +40,7 @@ class QuestionListForSlide(Resource): @protect_route(allowed_roles=["*"]) def post(self, competition_id, slide_id): - args = question_parser.parse_args(strict=True) + args = question_parser_add.parse_args(strict=True) item = dbc.add.question(slide_id=slide_id, **args) return item_response(schema.dump(item)) @@ -49,7 +55,7 @@ class QuestionById(Resource): @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id, question_id): - args = question_parser.parse_args(strict=True) + args = question_parser_edit.parse_args(strict=True) item_question = dbc.get.question(competition_id, slide_id, question_id) item_question = dbc.edit.default(item_question, **args) diff --git a/server/app/apis/slides.py b/server/app/apis/slides.py index 52553796..52ce4cce 100644 --- a/server/app/apis/slides.py +++ b/server/app/apis/slides.py @@ -4,16 +4,17 @@ from app.apis import item_response, list_response, protect_route from app.core.dto import SlideDTO from flask_restx import Resource from flask_restx import reqparse +from app.core.parsers import sentinel api = SlideDTO.api schema = SlideDTO.schema list_schema = SlideDTO.list_schema -slide_parser = reqparse.RequestParser() -slide_parser.add_argument("order", type=int, default=None, location="json") -slide_parser.add_argument("title", type=str, default=None, location="json") -slide_parser.add_argument("timer", type=int, default=None, location="json") -slide_parser.add_argument("background_image_id", default=None, type=int, location="json") +slide_parser_edit = reqparse.RequestParser() +slide_parser_edit.add_argument("order", type=int, default=sentinel, location="json") +slide_parser_edit.add_argument("title", type=str, default=sentinel, location="json") +slide_parser_edit.add_argument("timer", type=int, default=sentinel, location="json") +slide_parser_edit.add_argument("background_image_id", default=sentinel, type=int, location="json") @api.route("") @@ -40,10 +41,10 @@ class Slides(Resource): @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id): - args = slide_parser.parse_args(strict=True) + args = slide_parser_edit.parse_args(strict=True) item_slide = dbc.get.slide(competition_id, slide_id) - item_slide = dbc.edit.slide(item_slide, **args) + item_slide = dbc.edit.default(item_slide, **args) return item_response(schema.dump(item_slide)) @@ -60,7 +61,7 @@ class Slides(Resource): class SlideOrder(Resource): @protect_route(allowed_roles=["*"]) def put(self, competition_id, slide_id): - args = slide_parser.parse_args(strict=True) + args = slide_parser_edit.parse_args(strict=True) order = args.get("order") item_slide = dbc.get.slide(competition_id, slide_id) diff --git a/server/app/apis/teams.py b/server/app/apis/teams.py index c951ae53..71ca715d 100644 --- a/server/app/apis/teams.py +++ b/server/app/apis/teams.py @@ -3,14 +3,17 @@ import app.database.controller as dbc from app.apis import item_response, list_response, protect_route from app.core.dto import TeamDTO from flask_restx import Resource, reqparse -from flask_restx import reqparse +from app.core.parsers import sentinel api = TeamDTO.api schema = TeamDTO.schema list_schema = TeamDTO.list_schema -team_parser = reqparse.RequestParser() -team_parser.add_argument("name", type=str, location="json") +team_parser_add = reqparse.RequestParser() +team_parser_add.add_argument("name", type=str, required=True, location="json") + +team_parser_edit = reqparse.RequestParser() +team_parser_edit.add_argument("name", type=str, default=sentinel, location="json") @api.route("") @@ -23,7 +26,7 @@ class TeamsList(Resource): @protect_route(allowed_roles=["*"]) def post(self, competition_id): - args = team_parser.parse_args(strict=True) + args = team_parser_add.parse_args(strict=True) item_team = dbc.add.team(args["name"], competition_id) return item_response(schema.dump(item_team)) @@ -45,7 +48,7 @@ class Teams(Resource): @protect_route(allowed_roles=["*"]) def put(self, competition_id, team_id): - args = team_parser.parse_args(strict=True) + args = team_parser_edit.parse_args(strict=True) name = args.get("name") item_team = dbc.get.team(competition_id, team_id) diff --git a/server/app/apis/users.py b/server/app/apis/users.py index 50470db7..dc26ac5b 100644 --- a/server/app/apis/users.py +++ b/server/app/apis/users.py @@ -5,23 +5,23 @@ from app.core.dto import UserDTO from flask_jwt_extended import get_jwt_identity from flask_restx import Resource from flask_restx import inputs, reqparse -from app.core.parsers import search_parser +from app.core.parsers import search_parser, sentinel api = UserDTO.api schema = UserDTO.schema list_schema = UserDTO.list_schema -user_parser = reqparse.RequestParser() -user_parser.add_argument("email", type=inputs.email(), location="json") -user_parser.add_argument("name", type=str, location="json") -user_parser.add_argument("city_id", type=int, location="json") -user_parser.add_argument("role_id", type=int, location="json") +user_parser_edit = reqparse.RequestParser() +user_parser_edit.add_argument("email", type=inputs.email(), default=sentinel, location="json") +user_parser_edit.add_argument("name", type=str, default=sentinel, location="json") +user_parser_edit.add_argument("city_id", type=int, default=sentinel, location="json") +user_parser_edit.add_argument("role_id", type=int, default=sentinel, location="json") user_search_parser = search_parser.copy() -user_search_parser.add_argument("name", type=str, default=None, location="args") -user_search_parser.add_argument("email", type=str, default=None, location="args") -user_search_parser.add_argument("city_id", type=int, default=None, location="args") -user_search_parser.add_argument("role_id", type=int, default=None, location="args") +user_search_parser.add_argument("name", type=str, default=sentinel, location="args") +user_search_parser.add_argument("email", type=str, default=sentinel, location="args") +user_search_parser.add_argument("city_id", type=int, default=sentinel, location="args") +user_search_parser.add_argument("role_id", type=int, default=sentinel, location="args") def _edit_user(item_user, args): @@ -47,7 +47,7 @@ class UsersList(Resource): @protect_route(allowed_roles=["*"]) def put(self): - args = user_parser.parse_args(strict=True) + args = user_parser_edit.parse_args(strict=True) item = dbc.get.user(get_jwt_identity()) item = _edit_user(item, args) return item_response(schema.dump(item)) @@ -63,7 +63,7 @@ class Users(Resource): @protect_route(allowed_roles=["Admin"]) def put(self, ID): - args = user_parser.parse_args(strict=True) + args = user_parser_edit.parse_args(strict=True) item = dbc.get.user(ID) item = _edit_user(item, args) return item_response(schema.dump(item)) diff --git a/server/app/core/parsers.py b/server/app/core/parsers.py index c695dd23..160d67a0 100644 --- a/server/app/core/parsers.py +++ b/server/app/core/parsers.py @@ -1,5 +1,22 @@ from flask_restx import inputs, reqparse + +class Sentinel: + """ + Sentinel is used as default argument to parsers if it isn't necessary to + supply a value. This is used instead of None so that None can be supplied + as value. + """ + + def __repr__(self): + return "Sentinel" + + def __bool__(self): + return False + + +sentinel = Sentinel() + ###SEARCH#### search_parser = reqparse.RequestParser() search_parser.add_argument("page", type=int, default=0, location="args") diff --git a/server/app/database/controller/edit.py b/server/app/database/controller/edit.py index 94bc6008..efb49096 100644 --- a/server/app/database/controller/edit.py +++ b/server/app/database/controller/edit.py @@ -3,6 +3,7 @@ This file contains functionality to get data from the database. """ from app.core import db +from app.core.parsers import sentinel def switch_order(item1, item2): @@ -46,22 +47,8 @@ def default(item, **kwargs): for key, value in kwargs.items(): if not hasattr(item, key): raise AttributeError(f"Item of type {type(item)} has no attribute '{key}'") - if value is not None: + if value is not sentinel: setattr(item, key, value) db.session.commit() db.session.refresh(item) return item - - -def competition(item, **kwargs): - if kwargs["background_image_id"] == -1: - item.background_image_id = None - del kwargs["background_image_id"] - return default(item, **kwargs) - - -def slide(item, **kwargs): - if kwargs["background_image_id"] == -1: - item.background_image_id = None - del kwargs["background_image_id"] - return default(item, **kwargs) \ No newline at end of file -- GitLab