diff --git a/bmgt435_elp/apis.py b/bmgt435_elp/apis.py index 6e9ae69..1c273c0 100644 --- a/bmgt435_elp/apis.py +++ b/bmgt435_elp/apis.py @@ -4,13 +4,12 @@ from django.db import IntegrityError from django.db.models import Max - from .apps import bmgt435_file_system from .simulation.Cases import FoodCenter, SimulationException from .bmgtModels import * from .utils.statusCode import Status from .utils.jsonUtils import serialize_models, serialize_model_instance, serialize_simulation_result -from .utils.apiUtils import request_error_handler, password_valid, generic_paginated_query, pager_params_from_request, create_pager_params, logger +from .utils.apiUtils import request_error_handler, password_valid, generic_paginated_query, pager_params_from_request, create_pager_params, AppResponse import pandas as pd import json @@ -26,8 +25,12 @@ MAX_GROUP_SIZE = 4 def _get_session_user(request: HttpRequest) -> BMGTUser: + """ + raise key error if cookie not found + raise does not exist error if user not found + """ id = request.COOKIES.get('id', None) - user = BMGTUser.objects.get(id=id, activated=True, ) + user = BMGTUser.objects.get(id=id, activated=True,) return user @@ -35,8 +38,7 @@ class AuthApi: @staticmethod def __set_auth_cookie(response: HttpResponse, user: BMGTUser) -> None: - response.set_cookie('id', str(user.id), - samesite='strict', secure=True, httponly=True) + response.set_cookie('id', str(user.id), samesite='strict', secure=True, httponly=True) @staticmethod def __clear_auth_cookie(response: HttpResponse) -> None: @@ -45,27 +47,23 @@ def __clear_auth_cookie(response: HttpResponse) -> None: @request_error_handler @require_POST @staticmethod - def sign_in(request: HttpRequest) -> HttpResponse: + def sign_in(request: HttpRequest) -> AppResponse: try: - resp = HttpResponse() + resp = AppResponse() data = json.loads(request.body) did = data['did'] password = data['password'] user = BMGTUser.objects.get(did=did, activated=True,) if check_password(password, user.password): - resp.status_code = Status.OK AuthApi.__set_auth_cookie(resp, user) - resp.write(serialize_model_instance(user)) + resp.resolve(serialize_model_instance(user)) else: - resp.status_code = Status.UNAUTHORIZED - resp.write("Sign in failed. Please check your directory ID and password!") + resp.reject("Sign in failed. Please check your directory ID and password!") except BMGTUser.DoesNotExist: - resp.status_code = Status.NOT_FOUND - resp.write("Sign in failed. Please check your directory ID!") + resp.reject("Sign in failed. Please check your directory ID!") except KeyError: - resp.status_code = Status.BAD_REQUEST - resp.write("Sign in failed. Invalid data format!") - + resp.reject("Sign in failed. Invalid data format!") + return resp @staticmethod @@ -75,32 +73,27 @@ def set_session_cookie(response: HttpResponse, user: BMGTUser) -> None: @request_error_handler @require_POST @staticmethod - def sign_up(request: HttpRequest) -> HttpResponse: + def sign_up(request: HttpRequest) -> AppResponse: try: - resp = HttpResponse() + resp = AppResponse() data = json.loads(request.body) did = data['did'] password = data['password'] user = BMGTUser.objects.get(did=did,) if user.activated: - resp.status_code = Status.BAD_REQUEST - resp.write("Sign up failed. User already exists!") + resp.reject("Sign up failed. User already activated!") else: if password_valid(password): user.password = make_password(password) user.activated = True user.save() - resp.status_code = Status.OK - resp.write("Sign up success!") + resp.resolve("Sign up success!") else: - resp.status_code = Status.BAD_REQUEST - resp.write("Sign up failed! Password should contain at least 8 and up to 20 alphanumeric characters!") + resp.reject("Sign up failed! Password should contain at least 8 and up to 20 alphanumeric characters!") except BMGTUser.DoesNotExist: - resp.status_code = Status.NOT_FOUND - resp.write("Sign up failed. Please check your directory ID!") + resp.reject("Sign up failed. Please check your directory ID!") except KeyError: - resp.status_code = Status.BAD_REQUEST - resp.write("Sign up failed. Invalid data format!") + resp.reject("Sign up failed. Invalid data format!") return resp @@ -116,9 +109,14 @@ def password_reset(request: HttpRequest) -> HttpResponse: @require_POST @staticmethod def sign_out(request: HttpRequest) -> HttpResponse: - resp = HttpResponse() - AuthApi.__clear_auth_cookie(resp) - resp.status_code = Status.OK + try: + resp = AppResponse() + user = _get_session_user(request) + resp.resolve("Sign out success!") + AuthApi.__clear_auth_cookie(resp) + except (BMGTUser.DoesNotExist, KeyError) as e: + resp.reject("User not found!") + return resp @@ -129,16 +127,13 @@ class UserApi: @staticmethod def me(request: HttpRequest,) -> HttpResponse: try: - resp = HttpResponse() + resp = AppResponse() user = _get_session_user(request) - resp.write(serialize_model_instance(user)) - resp.status_code = Status.OK + resp.resolve(serialize_model_instance(user)) except BMGTUser.DoesNotExist: - resp.status_code = Status.NOT_FOUND - resp.write("User not found!") + resp.reject("User not found!") except KeyError: - resp.status_code = Status.BAD_REQUEST - resp.write("Invalid data format!") + resp.reject("Invalid data format!") return resp @@ -148,19 +143,16 @@ class GroupApi: @request_error_handler @require_GET @staticmethod - def get_group(request: HttpRequest) -> HttpResponse: + def get_group(request: HttpRequest) -> AppResponse: try: - resp = HttpResponse() + resp = AppResponse() group_id = int(request.GET.get('id')) group = BMGTGroup.objects.get(id=group_id) - resp.write(serialize_model_instance(group)) - resp.status_code = Status.OK + resp.resolve(serialize_model_instance(group)) except BMGTGroup.DoesNotExist: - resp.status_code = Status.NOT_FOUND - resp.write("Group not found!") + resp.reject("Group not found!") except KeyError: - resp.status_code = Status.BAD_REQUEST - resp.write("Invalid data format!") + resp.reject("Invalid data format!") return resp @@ -180,7 +172,8 @@ def groups_paginated(request: HttpRequest,) -> HttpResponse: @staticmethod def join_group(request: HttpRequest) -> HttpResponse: try: - resp = HttpResponse() + resp = AppResponse() + user: BMGTUser = _get_session_user(request) data = json.loads(request.body) group_id = data['group_id'] @@ -192,20 +185,15 @@ def join_group(request: HttpRequest) -> HttpResponse: if group.users.count() < MAX_GROUP_SIZE: user.group = group user.save() - resp.write(serialize_model_instance(group)) - resp.status_code = Status.OK + resp.resolve(serialize_model_instance(group)) else: - resp.write("Group already full!") - resp.status_code = Status.NOT_FOUND + resp.reject("Group already full!") else: - resp.write("Cannot join another group while you are alreay in a group!") - resp.status_code = Status.BAD_REQUEST + resp.reject("Cannot join another group while you are alreay in a group!") except BMGTGroup.DoesNotExist: - resp.status_code = Status.NOT_FOUND - resp.write("Group not found!") + resp.reject("Group not found!") except KeyError: - resp.status_code = Status.BAD_REQUEST - resp.write("Invalid data format!") + resp.reject("Invalid data format!") return resp @@ -213,16 +201,14 @@ def join_group(request: HttpRequest) -> HttpResponse: @require_POST @staticmethod def leave_group(request: HttpRequest) -> HttpResponse: - resp = HttpResponse() + resp = AppResponse() user: BMGTUser = _get_session_user(request) if user.group != None: user.group = None user.save() - resp.write("Group left!") - resp.status_code = Status.OK + resp.resolve("Group left!") else: - resp.write("You are not in a group!") - resp.status_code = Status.BAD_REQUEST + resp.resolve("You are not in a group!") return resp @@ -242,17 +228,14 @@ def __case_submittable(case:BMGTCase, group:BMGTGroup) -> bool: @staticmethod def get(request: HttpRequest) -> HttpResponse: try: - resp = HttpResponse() + resp = AppResponse() case_id = request.GET.get('case_id', None) case = BMGTCase.objects.get( id=case_id, visible=True) - resp.write(serialize_model_instance(case)) - resp.status_code = Status.OK + resp.resolve(serialize_model_instance(case)) except BMGTCase.DoesNotExist: - resp.status_code = Status.NOT_FOUND - resp.write("Case not found!") + resp.reject("Case not found!") except KeyError: - resp.status_code = Status.BAD_REQUEST - resp.write("Invalid data format!") + resp.reject("Invalid data format!") return resp @@ -269,7 +252,7 @@ def cases_paginated(request: HttpRequest) -> HttpResponse: @staticmethod def submit(request: HttpRequest) -> HttpResponse: try: - resp = HttpResponse() + resp = AppResponse() user: BMGTUser = _get_session_user(request) data = json.loads(request.body) case_id = int(data.get('case_id')) @@ -297,33 +280,26 @@ def submit(request: HttpRequest) -> HttpResponse: case_record.state = BMGTCaseRecord.State.SUCCESS case_record.score = res.score case_record.save() - resp.write(json.dumps({ + resp.resolve(json.dumps({ "case_record_id": case_record.id, "summary": case_record.summary_dict, "file_url": case_record.file_url, })) - resp.status_code = Status.OK case _: - resp.write("Case not found!") - resp.status_code = Status.NOT_FOUND + resp.reject("Case not found!") else: - resp.write( + resp.reject( "You have reached the maximum submission for this case!") - resp.status_code = Status.BAD_REQUEST else: - resp.write( + resp.reject( "You must join a group first to run the simulation!") - resp.status_code = Status.BAD_REQUEST except BMGTCase.DoesNotExist: - resp.status_code = Status.NOT_FOUND - resp.write("Case not found!") + resp.reject("Case not found!") except KeyError: - resp.status_code = Status.BAD_REQUEST - resp.write("Invalid data format!") + resp.reject("Invalid data format!") except SimulationException as e: - resp.status_code = Status.BAD_REQUEST - resp.write(f"Simulation failed!{e.args[0]}") + resp.reject(f"Simulation failed!{e.args[0]}") return resp @@ -333,17 +309,14 @@ class CaseRecordApi: @staticmethod def get_case_record(request: HttpRequest) -> HttpResponse: try: - resp = HttpResponse() + resp = AppResponse() case_record_id = request.GET.get('id', None) case_record = BMGTCaseRecord.objects.get(id=case_record_id, ) - resp.write(serialize_model_instance(case_record)) - resp.status_code = Status.OK + resp.resolve(serialize_model_instance(case_record)) except BMGTCaseRecord.DoesNotExist: - resp.status_code = Status.NOT_FOUND - resp.write("Case record not found!") + resp.reject("Case record not found!") except KeyError: - resp.status_code = Status.BAD_REQUEST - resp.write("Invalid data format!") + resp.reject("Invalid data format!") return resp @@ -366,18 +339,14 @@ def get_case_record_file(request: HttpRequest) -> HttpResponse: def case_records_paginated(request: HttpRequest) -> HttpResponse: user: BMGTUser = _get_session_user(request) group = user.group - if not group: - resp = HttpResponse() - resp.status_code = Status.NOT_FOUND - return resp - else: - return generic_paginated_query(BMGTCaseRecord, pager_params_from_request(request), state=BMGTCaseRecord.State.SUCCESS, group_id=group) + pagerParams = pager_params_from_request(request) + return generic_paginated_query(BMGTCaseRecord, pagerParams, state=BMGTCaseRecord.State.SUCCESS, group_id=group) @request_error_handler @require_GET def leader_board_paginated(request: HttpRequest) -> HttpResponse: try: - resp = HttpResponse() + resp = AppResponse() case_id = int(request.GET.get('case_id')) match case_id: case 1: @@ -390,11 +359,11 @@ def leader_board_paginated(request: HttpRequest) -> HttpResponse: case_id=case_id) case _: - resp.write("Case not found!") - resp.status_code = Status.NOT_FOUND + raise BMGTCase.DoesNotExist except KeyError: - resp.status_code = Status.BAD_REQUEST - resp.write("Invalid data format!") + resp.reject("Invalid data format!") + except BMGTCase.DoesNotExist: + resp.reject("Case not found!") return resp @@ -406,7 +375,7 @@ class ManageApi: @staticmethod def import_users(request: HttpRequest, semester_id) -> HttpResponse: try: - resp = HttpResponse() + resp = AppResponse() semester = BMGTSemester.objects.get(id=semester_id) with io.BytesIO(request.body) as file: raw_csv = pd.read_csv(file, encoding='utf-8') @@ -420,14 +389,11 @@ def import_users(request: HttpRequest, semester_id) -> HttpResponse: for row in user_csv.to_dict('records')] BMGTUser.objects.bulk_create(obj_set, batch_size=40) - resp.status_code = Status.OK - resp.write("Imported!") + resp.resolve("Imported!") else: - resp.status_code = Status.BAD_REQUEST - resp.write("Import failed! Please upload a CSV file that contains the following columns: user_first_name, user_last_name, directory_id") + resp.reject("Import failed! Please upload a CSV file that contains the following columns: user_first_name, user_last_name, directory_id") except IntegrityError: - resp.status_code = Status.BAD_REQUEST - resp.write("Import failed! Please remove the duplicated directory ID's from the CSV file!") + resp.reject("Import failed! Please remove the duplicated directory ID's from the CSV file!") return resp @@ -454,7 +420,7 @@ def view_users(request: HttpRequest) -> HttpResponse: @require_GET @staticmethod def system_status(request: HttpRequest) -> HttpResponse: - resp = HttpResponse() + resp = AppResponse() count_users = BMGTUser.objects.count() count_active_users = BMGTUser.objects.filter(activated=True).count() @@ -463,7 +429,7 @@ def system_status(request: HttpRequest) -> HttpResponse: count_case_records = BMGTCaseRecord.objects.count() count_case_records_success = BMGTCaseRecord.objects.filter(state=BMGTCaseRecord.State.SUCCESS).count() - resp.write(json.dumps({ + resp.resolve(json.dumps({ "count_users": count_users, "count_active_users": count_active_users, "count_groups": count_groups, @@ -472,7 +438,6 @@ def system_status(request: HttpRequest) -> HttpResponse: "count_case_records_success": count_case_records_success, })) - resp.status_code = Status.OK return resp @@ -480,18 +445,16 @@ def system_status(request: HttpRequest) -> HttpResponse: @require_POST @staticmethod def create_semester(request: HttpRequest) -> HttpResponse: - resp = HttpResponse() + resp = AppResponse() data = json.loads(request.body) year = data.get('year', None) season = data.get('season', None) if BMGTSemester.objects.filter(year=year, season=season).exists(): - resp.write("Semester already exists!") - resp.status_code = Status.BAD_REQUEST + resp.reject("Semester already exists!") else: semester = BMGTSemester.objects.create(year=year, season=season) semester.save() - resp.write(serialize_model_instance(semester)) - resp.status_code = Status.OK + resp.resolve(serialize_model_instance(semester)) return resp @@ -499,22 +462,30 @@ def create_semester(request: HttpRequest) -> HttpResponse: @require_POST @staticmethod def delete_semester(request: HttpRequest) -> HttpResponse: - resp = HttpResponse() - data = json.loads(request.body) - semester_id = data.get('semester_id', None) - semester = BMGTSemester.objects.get(id=semester_id) - semester.delete() - resp.status_code = Status.OK + try: + resp = AppResponse() + data = json.loads(request.body) + semester_id = data.get('semester_id', None) + semester = BMGTSemester.objects.get(id=semester_id) + semester.delete() + resp.resolve("Semester deleted!") + except BMGTSemester.DoesNotExist: + resp.reject("Semester not found!") + except KeyError: + resp.reject("Invalid data format!") return resp @request_error_handler @require_GET @staticmethod def get_semesters(request: HttpRequest) -> HttpResponse: - resp = HttpResponse() - semesters = BMGTSemester.objects.all() - resp.write(serialize_models(semesters)) - resp.status_code = Status.OK + try: + resp = AppResponse() + semesters = BMGTSemester.objects.all() + resp.resolve(serialize_models(semesters)) + except Exception as e: + resp.reject(e) + return resp @@ -522,16 +493,24 @@ def get_semesters(request: HttpRequest) -> HttpResponse: @require_POST @staticmethod def batch_create_group(request: HttpRequest) -> HttpResponse: - resp = HttpResponse() - data = json.loads(request.body) - semester_id = data.get('semester_id', None) - size = int(data.get('size')) - semester = BMGTSemester.objects.get(id=semester_id) - max_group_num = BMGTGroup.objects.aggregate(max_value=Max('number'))['max_value'] or 0 - BMGTGroup.objects.bulk_create( - [BMGTGroup(number=max_group_num + i + 1, semester=semester) for i in range(size)] - ) - resp.status_code = Status.OK + try: + resp = AppResponse() + data = json.loads(request.body) + semester_id = data.get('semester_id', None) + size = int(data.get('size')) + semester = BMGTSemester.objects.get(id=semester_id) + max_group_num = BMGTGroup.objects.aggregate(max_value=Max('number'))['max_value'] or 0 + BMGTGroup.objects.bulk_create( + [BMGTGroup(number=max_group_num + i + 1, semester=semester) for i in range(size)] + ) + resp.resolve("Groups created!") + except BMGTSemester.DoesNotExist: + resp.reject("Semester not found!") + except KeyError: + resp.reject("Invalid data format!") + except Exception as e: + resp.reject(e) + return resp @@ -549,21 +528,20 @@ class FeedbackApi: @staticmethod def post(request: HttpRequest) -> HttpResponse: try: - resp = HttpResponse() + resp = AppResponse() data = json.loads(request.body) user: BMGTUser = _get_session_user(request) content = data.get('content') if content: feedback = BMGTFeedback(user=user, content=content) feedback.save() - resp.status_code = Status.OK - resp.write("Feedback submitted!") + resp.resolve("Feedback submitted!") else: - resp.status_code = Status.BAD_REQUEST - resp.write("Feedback cannot be empty!") + resp.reject("Feedback cannot be empty!") except KeyError: - resp.status_code = Status.BAD_REQUEST - resp.write("Invalid data format!") + resp.reject("Invalid data format!") + except Exception as e: + resp.reject(e) return resp diff --git a/bmgt435_elp/test.py b/bmgt435_elp/test.py index 41590a7..c267b91 100644 --- a/bmgt435_elp/test.py +++ b/bmgt435_elp/test.py @@ -49,7 +49,27 @@ def _sendGet(url:str, method:Callable, cookies:dict, getParams:dict = None): return resp -class TestAuthApi(TestCase): +def _getResponeData(resp:HttpResponse): + return json.loads(resp.content) + + +class AppTestCaeBase(TestCase): + + def _deserialize_resp_data(self, resp:HttpResponse) -> dict: + return json.loads(resp.content) + + + def assertResolved(self, resp:HttpResponse, msg=None): + data = self._deserialize_resp_data(resp) + self.assertTrue(data.get('resolver') is not None, msg) + + + def assertRejected(self, resp:HttpResponse, msg=None): + data = self._deserialize_resp_data(resp) + self.assertTrue(data.get('error_msg') is not None, msg) + + +class TestAuthApi(AppTestCaeBase): def setUp(self): self.did = 'did' @@ -70,44 +90,81 @@ def testSignUpPositive(self): user = BMGTUser.objects.get(did=self.did) self.assertEqual(user.activated, True, 'user should be activated after sign up') self.assertEqual(resp.status_code, 200) + self.assertResolved(resp) def testSignUpNegative(self): - resp = _signUp('', '') - self.assertNotEqual(resp.status_code, 200) + resp = _signUp('23132', 'Grave2231312.') + self.assertEqual(resp.status_code, 200) assert not BMGTUser.objects.filter(did='').exists() + self.assertRejected(resp) def testSignUpNonExistentUser(self): resp = _signUp('did323', 'pass32132321.$') - self.assertNotEqual(resp.status_code, 200) + self.assertEqual(resp.status_code, 200) + self.assertRejected(resp) def testRepeatedSignUp(self): resp = _signUp(self.did, self.password) - self.assertEqual(resp.status_code, 200) + self.assertResolved(resp) resp2= _signUp(self.did, self.password) - self.assertNotEqual(resp2.status_code, 200) + self.assertRejected(resp2) def testSignInPositive(self): did = 'did232' pwd = 'pa3232..ssword' _signUp(did, pwd) - self.assertEqual(BMGTUser.objects.get(did=did).activated, True, 'user should be activated after sign up') + self.assertEqual(BMGTUser.objects.get(did=did).activated, True,) resp = _signIn(did, pwd) - self.assertEqual(resp.status_code, 200) + self.assertResolved(resp) def testSignInNegative(self): did = 'did' pwd = 'pa3232.ssword' - _signUp(did, pwd) - resp = _signIn(did, 'pa3232.ssword2') - self.assertNotEqual(resp.status_code, 200) + resp = _signUp(did, pwd) + self.assertResolved(resp) + resp2 = _signIn(did, 'pa3232.ssword2') + self.assertRejected(resp2) + + def testSignOutPositive(self): + did = 'did' + pwd = 'pa3232.ssword' + respSignUp = _signUp(did, pwd) + self.assertResolved(respSignUp) -class TestUserApi(TestCase): + respSignIn = _signIn(did, pwd) + self.assertResolved(respSignIn) + + req = RequestFactory().post( + '/bmgt435-service/api/auth/sign-out', + ) + req.COOKIES['id'] = 1 + respSignOut = AuthApi.sign_out(req) + self.assertResolved(respSignOut) + + + def testSignOutNegative(self): + req = RequestFactory().post( + '/bmgt435-service/api/auth/sign-out', + ) + req.COOKIES['id'] = -1 + resp = AuthApi.sign_out(req) + self.assertRejected(resp) + + req = RequestFactory().post( + '/bmgt435-service/api/auth/sign-out', + ) + req.COOKIES.clear() + resp = AuthApi.sign_out(req) + self.assertRejected(resp) + + +class TestUserApi(AppTestCaeBase): def setUp(self): BMGTUser(first_name='f', last_name='l', did='did', role='admin', activated=1, password='Grave11.').save() @@ -125,20 +182,20 @@ def testUserMeAfterSignIn(self): req.COOKIES['id'] = 1 resp = UserApi.me(req) - self.assertEqual(resp.status_code, 200) + self.assertResolved(resp) def testUserMeNegative(self): resp = _sendGet('/bmgt435-service/api/users/me', UserApi.me, {}) - self.assertNotEqual(resp.status_code, 200) + self.assertRejected(resp) def testUserMeNotActivated(self): resp = _sendGet('/bmgt435-service/api/users/me', UserApi.me, {'id':-1}) - self.assertNotEqual(resp.status_code, 200) + self.assertRejected(resp) -class TestGroupApi(TestCase): +class TestGroupApi(AppTestCaeBase): def setUp(self) -> None: @@ -154,41 +211,41 @@ def setUp(self) -> None: def testGroupIntegrity(self): try: BMGTGroup(number=1).save() - self.fail('should assign valid semester when creating groups') + self.fail() except IntegrityError: return def testGetGroupPositive(self): resp = _sendGet('/bmgt435-service/api/groups?id=1', GroupApi.get_group, self.cookies) - self.assertEqual(resp.status_code, 200) + self.assertResolved(resp) def testGetGroupNegative(self): resp = _sendGet('/bmgt435-service/api/groups?id=2', GroupApi.get_group, self.cookies) - self.assertEqual(resp.status_code, 404) + self.assertRejected(resp) def testGetGroupPaginated(self): resp = _sendGet('/bmgt435-service/api/groups/paginated', GroupApi.groups_paginated, self.cookies, self.paginatedParams) - self.assertEqual(resp.status_code, 200) + self.assertResolved(resp) def testGetGroupPaginatedNeg(self): params = self.paginatedParams params['page'] = -1 resp = _sendGet('/bmgt435-service/api/groups/paginated', GroupApi.groups_paginated, self.cookies, self.paginatedParams) - self.assertEqual(resp.status_code, 400) + self.assertRejected(resp) params['page'] = 100 resp = _sendGet('/bmgt435-service/api/groups/paginated', GroupApi.groups_paginated, self.cookies, self.paginatedParams) - self.assertEqual(resp.status_code, 404) + self.assertRejected(resp) def testGroupSuitePos(self): # join resp = _sendPost('/bmgt435-service/api/groups/join', GroupApi.join_group, {'group_id':1}, self.cookies) - self.assertEqual(resp.status_code, 200) + self.assertResolved(resp) self.assertEqual(BMGTGroup.objects.get(id=1).users.count(), 1, 'join group failed') self.assertEqual(BMGTUser.objects.get(id=1).group_id, 1, 'join group failed') @@ -201,7 +258,9 @@ def testGroupSuitePos(self): def testJoinGroupNeg(self): resp = _sendPost('/bmgt435-service/api/groups/join', GroupApi.join_group, {'group_id':-1}, self.cookies) - self.assertEqual(resp.status_code, 404) + self.assertEqual(resp.status_code, 200) + self.assertRejected(resp) resp = _sendPost('/bmgt435-service/api/groups/join', GroupApi.join_group, {'groupid':1}, self.cookies) - self.assertEqual(resp.status_code, 400) \ No newline at end of file + self.assertEqual(resp.status_code, 200) + self.assertRejected(resp) \ No newline at end of file diff --git a/bmgt435_elp/utils/apiUtils.py b/bmgt435_elp/utils/apiUtils.py index 6498da6..8f85967 100644 --- a/bmgt435_elp/utils/apiUtils.py +++ b/bmgt435_elp/utils/apiUtils.py @@ -2,9 +2,8 @@ from django.db import IntegrityError from django.core.paginator import Paginator, EmptyPage from django.http import HttpRequest, HttpResponse -from django.conf import settings from .statusCode import Status -from .jsonUtils import serialize_paginated_data, serialize_models +from .jsonUtils import serialize_paginated_data, serialize_models, CustomJSONEncoder from ..simulation.Cases import SimulationException from ..bmgtModels import BMGTTransaction @@ -133,32 +132,29 @@ def pager_params_from_request(request: HttpRequest) -> dict: return params -def generic_paginated_query(cls, pager_params, **kwargs) -> HttpResponse: +def generic_paginated_query(dbModel, pager_params, **kwargs) -> HttpResponse: """ generic paginated query on one table pass in a model class and a request object kwargs: filter conditions """ try: - resp = HttpResponse() + resp = AppResponse() - obj_set = cls.objects.filter(**kwargs) + obj_set = dbModel.objects.filter(**kwargs) obj_set = obj_set.order_by( pager_params['order'] if pager_params['asc'] else '-'+pager_params['order']) pager = Paginator(obj_set, pager_params['size']) - if pager_params['page'] > pager.num_pages: - resp.write("Page not found!") - resp.status_code = Status.NOT_FOUND + if pager_params['page'] > pager.num_pages or pager_params['page'] < 1: + resp.reject("Page not found!") else: - resp.write(serialize_paginated_data(pager, pager_params['page'])) - resp.status_code = Status.OK + resp.resolve(serialize_paginated_data(pager, pager_params['page'])) - return resp except EmptyPage: - resp.status_code = Status.BAD_REQUEST - return resp + resp.reject("Page empty!") + return resp def __log_event(request: HttpRequest, status_code: int): @@ -183,4 +179,25 @@ def wrapper(request: HttpRequest, **kwargs): return response except Exception as e: raise e - return wrapper \ No newline at end of file + return wrapper + + +class AppResponse(HttpResponse): + def __init__(self, status: int = Status.OK) -> None: + super().__init__(status=status) + self.resolver = None + self.rejector = None + + def reject(self, error_msg: str): + self.flush() + self.write(json.dumps({ + 'error_msg': error_msg + }, cls=CustomJSONEncoder + )) + + def resolve(self, data_resolver): + self.flush() + self.write(json.dumps({ + 'resolver': data_resolver + }, cls=CustomJSONEncoder + )) \ No newline at end of file diff --git a/bmgt435_elp/utils/jsonUtils.py b/bmgt435_elp/utils/jsonUtils.py index bab9ac4..fcce81b 100644 --- a/bmgt435_elp/utils/jsonUtils.py +++ b/bmgt435_elp/utils/jsonUtils.py @@ -21,7 +21,6 @@ def default(self, obj): return super().default(obj) - def serialize_models(querySet:QuerySet | list[BMGTModelBase]) -> str: return json.dumps([model.as_dictionary() for model in querySet], cls=CustomJSONEncoder)