diff --git a/api/bugsnag_error.py b/api/bugsnag_error.py new file mode 100644 index 000000000..30a547cbf --- /dev/null +++ b/api/bugsnag_error.py @@ -0,0 +1,14 @@ +import bugsnag +import os +from graphql import GraphQLError + + +class Errors: + + def report_errors_bugsnag_and_graphQL(error_message): + if os.getenv("APP_SETTINGS") != "testing": + bugsnag.notify(Exception(error_message), severity="error") + raise GraphQLError(error_message) + + +return_error = Errors diff --git a/api/devices/schema.py b/api/devices/schema.py index c8aaf6ec7..92d300038 100644 --- a/api/devices/schema.py +++ b/api/devices/schema.py @@ -1,5 +1,4 @@ import graphene -from graphql import GraphQLError from datetime import datetime from graphene_sqlalchemy import SQLAlchemyObjectType from sqlalchemy import func, String, cast @@ -14,6 +13,7 @@ from helpers.room_filter.room_filter import location_join_room from helpers.auth.user_details import get_user_from_db from helpers.auth.admin_roles import admin_roles +from api.bugsnag_error import return_error class Devices(SQLAlchemyObjectType): @@ -43,7 +43,7 @@ def mutate(self, info, **kwargs): RoomModel.state == 'active' ).first() if not room_location: - raise GraphQLError("Room not found") + return_error.report_errors_bugsnag_and_graphQL("Room not found") user = get_user_from_db() device = DevicesModel( **kwargs, @@ -77,7 +77,8 @@ def mutate(self, info, device_id, **kwargs): DevicesModel.id == device_id ).first() if not exact_device: - raise GraphQLError("Device ID not found") + return_error.report_errors_bugsnag_and_graphQL( + "Device ID not found") update_entity_fields(exact_device, **kwargs) exact_device.save() @@ -102,7 +103,7 @@ def mutate(self, info, device_id, **kwargs): DevicesModel.id == device_id ).first() if not exact_device: - raise GraphQLError("Device not found") + return_error.report_errors_bugsnag_and_graphQL("Device not found") update_entity_fields(exact_device, state="archived", **kwargs) exact_device.save() return DeleteDevice(device=exact_device) @@ -160,7 +161,7 @@ def resolve_specific_device(self, info, device_id): device = query.filter(DevicesModel.id == device_id).first() if not device: - raise GraphQLError("Device not found") + return_error.report_errors_bugsnag_and_graphQL("Device not found") return device @@ -169,7 +170,8 @@ def resolve_device_by_name(self, info, device_name): devices = Devices.get_query(info) device_name = ''.join(device_name.split()).lower() if not device_name: - raise GraphQLError("Please provide the device name") + return_error.report_errors_bugsnag_and_graphQL( + "Please provide the device name") found_devices = [] for device in devices: exact_name = ''.join(device.name.split()).lower() diff --git a/api/events/models.py b/api/events/models.py index 9021c0a9e..ab8b2bc99 100644 --- a/api/events/models.py +++ b/api/events/models.py @@ -1,7 +1,6 @@ from sqlalchemy import (Column, String, Integer, Boolean, ForeignKey, Enum) from sqlalchemy.orm import relationship from sqlalchemy.schema import Sequence -from graphql import GraphQLError from helpers.database import Base from utilities.utility import Utility, StateType @@ -9,6 +8,7 @@ validate_date_input, format_range_dates, ) +from api.bugsnag_error import return_error class Events(Base, Utility): @@ -38,7 +38,7 @@ def filter_event(start_date, end_date, room_id=None): or returns all events otherwise. """ def error_message(error): - raise GraphQLError(error) + return_error.report_errors_bugsnag_and_graphQL(error) validate_date_input(start_date, end_date) diff --git a/api/events/schema.py b/api/events/schema.py index 03955c433..3cb46ccb6 100644 --- a/api/events/schema.py +++ b/api/events/schema.py @@ -1,6 +1,5 @@ import graphene from graphene_sqlalchemy import SQLAlchemyObjectType -from graphql import GraphQLError import pytz from dateutil import parser from datetime import timedelta @@ -29,6 +28,7 @@ calendar_dates_format, empty_string_checker ) +from api.bugsnag_error import return_error utc = pytz.utc @@ -175,7 +175,7 @@ def mutate(self, info, **kwargs): device_last_seen = parser.parse( kwargs['start_time']) + timedelta(minutes=10) except ValueError: - raise GraphQLError("Invalid start time") + return_error.report_errors_bugsnag_and_graphQL("Invalid start time") update_device_last_activity( info, room_id, device_last_seen, 'cancel meeting') if not event: @@ -200,7 +200,8 @@ def mutate(self, info, **kwargs): room_id, event_reject_reason ): - raise GraphQLError("Event cancelled but email not sent") + return_error.report_errors_bugsnag_and_graphQL( + "Event cancelled but email not sent") return CancelEvent(event=event) @@ -281,7 +282,8 @@ def check_event_in_db(instance, info, event_check, **kwargs): return room_id, event elif event and event_check == 'ended': if event.meeting_end_time: - raise GraphQLError("Event has already ended") + return_error.report_errors_bugsnag_and_graphQL( + "Event has already ended") event.meeting_end_time = kwargs['meeting_end_time'] event.save() return room_id, event @@ -389,7 +391,7 @@ def resolve_all_events(self, info, **kwargs): per_page = kwargs.get('per_page') page, per_page = validate_page_and_per_page(page, per_page) response = filter_event( - start_date, end_date + start_date, end_date ) sort_events_by_date(response) @@ -422,7 +424,8 @@ def resolve_all_events_by_room(self, info, **kwargs): calendar_id=calendar_id ).first() if not room: - raise GraphQLError("No rooms with the given CalendarId") + return_error.report_errors_bugsnag_and_graphQL( + "No rooms with the given CalendarId") response = filter_event( start_date, end_date, room.id ) diff --git a/api/location/schema.py b/api/location/schema.py index 1ab822537..d8a7aaf70 100644 --- a/api/location/schema.py +++ b/api/location/schema.py @@ -1,6 +1,5 @@ import graphene from sqlalchemy import func -from graphql import GraphQLError from graphene_sqlalchemy import SQLAlchemyObjectType from api.location.models import Location as LocationModel @@ -20,6 +19,7 @@ from helpers.room_filter.room_filter import room_join_location from helpers.auth.authentication import Auth from helpers.auth.admin_roles import admin_roles +from api.bugsnag_error import return_error class Location(SQLAlchemyObjectType): @@ -60,15 +60,15 @@ def mutate(self, info, **kwargs): template = 'location_success.html' payload = { 'model': LocationModel, 'field': 'name', 'value': kwargs['name'] - } + } with SaveContextManager(location, 'Location', payload): if not notification.send_email_notification( email=email, subject=subject, template=template, location_name=location.name, user_name=admin_name ): - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "Location created but email not sent" - ) + ) return CreateLocation(location=location) @@ -93,7 +93,7 @@ def mutate(self, info, location_id, **kwargs): location_object = result.filter( LocationModel.id == location_id).first() if not location_object: - raise GraphQLError("Location not found") + return_error.report_errors_bugsnag_and_graphQL("Location not found") admin_roles.verify_admin_location(location_id) if "time_zone" in kwargs: validate_timezone_field(**kwargs) @@ -130,7 +130,7 @@ def mutate(self, info, location_id, **kwargs): location = result.filter( LocationModel.id == location_id).first() # noqa: E501 if not location: - raise GraphQLError("location not found") + return_error.report_errors_bugsnag_and_graphQL("location not found") admin_roles.verify_admin_location(location_id) update_entity_fields(location, state="archived", **kwargs) location.save() diff --git a/api/office/schema.py b/api/office/schema.py index ba113fcc4..774baf32e 100644 --- a/api/office/schema.py +++ b/api/office/schema.py @@ -1,6 +1,5 @@ import graphene from graphene_sqlalchemy import SQLAlchemyObjectType -from graphql import GraphQLError from sqlalchemy import exc, func from api.office.models import Office as OfficeModel @@ -16,6 +15,7 @@ from helpers.pagination.paginate import Paginate, validate_page from helpers.email.email import notification from helpers.auth.user_details import get_user_from_db +from api.bugsnag_error import return_error class Office(SQLAlchemyObjectType): @@ -41,7 +41,7 @@ class Arguments: def mutate(self, info, **kwargs): location = Location.query.filter_by(id=kwargs['location_id']).first() if not location: - raise GraphQLError("Location not found") + return_error.report_errors_bugsnag_and_graphQL("Location not found") admin_roles.verify_admin_location(location_id=kwargs['location_id']) office = OfficeModel(**kwargs) admin = get_user_from_db() @@ -50,15 +50,16 @@ def mutate(self, info, **kwargs): admin_name = username.split(".")[0] payload = { 'model': OfficeModel, 'field': 'name', 'value': kwargs['name'] - } + } with SaveContextManager( - office, 'Office', payload + office, 'Office', payload ): new_office = kwargs['name'] if not notification.send_email_notification( email, new_office, location.name, admin_name ): - raise GraphQLError("Office created but Emails not Sent") + return_error.report_errors_bugsnag_and_graphQL( + "Office created but Emails not Sent") return CreateOffice(office=office) @@ -79,7 +80,7 @@ def mutate(self, info, office_id, **kwargs): exact_office = result.filter( OfficeModel.id == office_id).first() # noqa: E501 if not exact_office: - raise GraphQLError("Office not found") + return_error.report_errors_bugsnag_and_graphQL("Office not found") admin_roles.create_rooms_update_delete_office(office_id) update_entity_fields(exact_office, state="archived", **kwargs) @@ -104,13 +105,13 @@ def mutate(self, info, office_id, **kwargs): result = get_office.filter(OfficeModel.state == "active") exact_office = result.filter(OfficeModel.id == office_id).first() if not exact_office: - raise GraphQLError("Office not found") + return_error.report_errors_bugsnag_and_graphQL("Office not found") admin_roles.create_rooms_update_delete_office(office_id) try: update_entity_fields(exact_office, **kwargs) exact_office.save() except exc.SQLAlchemyError: - raise GraphQLError("Action Failed") + return_error.report_errors_bugsnag_and_graphQL("Action Failed") return UpdateOffice(office=exact_office) @@ -134,7 +135,7 @@ def resolve_offices(self, info, **kwargs): func.lower(OfficeModel.name)).limit( per_page).offset(page * per_page) if result.count() == 0: - return GraphQLError("No more offices") + return_error.report_errors_bugsnag_and_graphQL("No more offices") return result @@ -166,7 +167,7 @@ def resolve_get_office_by_name(self, info, name): active_offices = query.filter(OfficeModel.state == "active") check_office = active_offices.filter(OfficeModel.name == name).first() if not check_office: - raise GraphQLError("Office Not found") + return_error.report_errors_bugsnag_and_graphQL("Office Not found") if name == "Epic tower": exact_query = lagos_office_join_location(active_offices) result = exact_query.filter(OfficeModel.name == name) diff --git a/api/office_structure/schema.py b/api/office_structure/schema.py index 543416f1a..36513ef3b 100644 --- a/api/office_structure/schema.py +++ b/api/office_structure/schema.py @@ -1,6 +1,5 @@ import graphene from graphene_sqlalchemy import SQLAlchemyObjectType -from graphql import GraphQLError from enum import Enum from sqlalchemy import asc, desc from api.office_structure.models import OfficeStructure as StructureModel @@ -12,6 +11,7 @@ from helpers.auth.admin_roles import admin_roles from helpers.database import db_session from utilities.utility import update_entity_fields +from api.bugsnag_error import return_error class StructureNode(SQLAlchemyObjectType): @@ -43,7 +43,7 @@ def mutate(self, info, **kwargs): StructureModel.id == kwargs['node_id']).first() if not node: - raise GraphQLError("node not found") + return_error.report_errors_bugsnag_and_graphQL("node not found") validate_empty_fields(**kwargs) @@ -78,7 +78,7 @@ def mutate(self, info, node_id): exact_node = db_session.query(StructureModel).filter( StructureModel.id == node_id).first() if not exact_node: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "The specified node does not exist" ) db_session.delete(exact_node) @@ -111,7 +111,7 @@ def mutate(self, info, node_list): node['name'] = node.name.strip() node['tag'] = node.tag.strip() nodes.append(StructureModel( - **node, location_id=admin_location_id)) + **node, location_id=admin_location_id)) db_session.add_all(nodes) db_session.commit() return CreateStructure(structure=nodes) @@ -146,7 +146,7 @@ class Query(graphene.ObjectType): node_name=graphene.String(required=True), order=graphene.Argument(graphene.Enum.from_enum(Order)), description="Returns the path from root to the node within structure" - ) + ) @Auth.user_roles('Admin', 'Default User', 'Super Admin') def resolve_node_path_by_name(self, info, **kwargs): @@ -155,7 +155,7 @@ def resolve_node_path_by_name(self, info, **kwargs): query = StructureNode.get_query(info) order = asc if order == 'asc' else desc node = query.filter( - StructureModel.name.ilike(node_name.strip())).first() + StructureModel.name.ilike(node_name.strip())).first() if not node: - raise GraphQLError('node not found') + return_error.report_errors_bugsnag_and_graphQL('node not found') return node.path_to_root(order=order) diff --git a/api/question/schema.py b/api/question/schema.py index 3058c87ef..18239e236 100644 --- a/api/question/schema.py +++ b/api/question/schema.py @@ -1,13 +1,12 @@ import graphene from graphene_sqlalchemy import SQLAlchemyObjectType -from graphql import GraphQLError from api.question.models import Question as QuestionModel from utilities.validations import ( validate_empty_fields, validate_date_time_range, validate_question_type - ) +) from utilities.utility import update_entity_fields from helpers.auth.authentication import Auth from helpers.auth.error_handler import SaveContextManager @@ -15,6 +14,7 @@ from helpers.questions_filter.questions_filter import ( filter_questions_by_date_range ) +from api.bugsnag_error import return_error class Question(SQLAlchemyObjectType): @@ -52,7 +52,7 @@ def mutate(self, info, **kwargs): question_type = kwargs['question_type'] fields = kwargs if question_type == "check" and not kwargs.get('check_options'): - return GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "No check options supplied for question type check" ) if question_type != "check": @@ -60,7 +60,7 @@ def mutate(self, info, **kwargs): payload = { 'model': QuestionModel, 'field': 'question', 'value': kwargs['question'] - } + } question = QuestionModel(**fields) with SaveContextManager(question, 'Question', payload): return CreateQuestion(question=question) @@ -85,7 +85,7 @@ def resolve_questions(self, info): self.query_total = active_questions.count() result = active_questions.limit(per_page).offset(page * per_page) if result.count() == 0: - return GraphQLError("No questions found") + return_error.report_errors_bugsnag_and_graphQL("No questions found") return result @@ -117,7 +117,7 @@ def mutate(self, info, question_id, **kwargs): exact_question = active_questions.filter( QuestionModel.id == question_id).first() if not exact_question: - raise GraphQLError("Question not found") + return_error.report_errors_bugsnag_and_graphQL("Question not found") validate_date_time_range(**kwargs) update_entity_fields(exact_question, **kwargs) exact_question.save() @@ -144,7 +144,7 @@ def mutate(self, info, question_id): exact_question = active_questions.filter( QuestionModel.id == question_id).first() if not exact_question: - raise GraphQLError("Question not found") + return_error.report_errors_bugsnag_and_graphQL("Question not found") update_entity_fields(exact_question, state="archived") exact_question.save() return DeleteQuestion(question=exact_question) @@ -203,9 +203,9 @@ def resolve_all_questions(self, info, start_date=None, end_date=None): query = Question.get_query(info) questions = query.filter(QuestionModel.state == "active").all() questions_by_range = filter_questions_by_date_range( - questions, - start_date, end_date - ) + questions, + start_date, end_date + ) return questions_by_range def resolve_questions(self, info, **kwargs): @@ -218,7 +218,8 @@ def resolve_question(self, info, id): active_questions = query.filter(QuestionModel.state == "active") response = active_questions.filter(QuestionModel.id == id).first() if not response: - raise GraphQLError('Question does not exist') + return_error.report_errors_bugsnag_and_graphQL( + 'Question does not exist') return response diff --git a/api/response/schema.py b/api/response/schema.py index c6c72bbb1..d21d22260 100644 --- a/api/response/schema.py +++ b/api/response/schema.py @@ -1,20 +1,20 @@ from datetime import datetime import graphene from graphene_sqlalchemy import SQLAlchemyObjectType -from graphql import GraphQLError from api.question.models import Question as QuestionModel from api.response.models import Response as ResponseModel from api.room.schema import Room from helpers.auth.authentication import Auth from helpers.pagination.paginate import ListPaginate from helpers.response.create_response import ( - create_response, - create_response_details, - map_response_type, - ResponseDetail, - ResponseData) + create_response, + create_response_details, + map_response_type, + ResponseDetail, + ResponseData) from utilities.utility import update_entity_fields from utilities.validations import validate_empty_fields +from api.bugsnag_error import return_error class Response(SQLAlchemyObjectType): @@ -79,7 +79,8 @@ def mutate(self, info, **kwargs): present_date = datetime.now().strftime('%Y/%m/%d %H:%M:%S') room = query.filter_by(id=kwargs['room_id']).first() if not room: - raise GraphQLError("Non-existent room id") + return_error.report_errors_bugsnag_and_graphQL( + "Non-existent room id") for each_response in kwargs['responses']: question = QuestionModel.query.filter_by( id=each_response.question_id).first() @@ -98,7 +99,7 @@ def mutate(self, info, **kwargs): **each_response ) if errors: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( ('The following errors occured: {}').format( str(errors).strip('[]')) ) @@ -148,7 +149,7 @@ def resolve_get_room_response(self, info, **kwargs): room_feedback = query.filter_by(room_id=kwargs['room_id']) active_feedback = room_feedback.filter(ResponseModel.state == "active") if active_feedback.count() < 1: - raise GraphQLError("This room\ + return_error.report_errors_bugsnag_and_graphQL("This room\ doesn't exist or doesn't have feedback.") if page and per_page: @@ -204,7 +205,8 @@ def mutate(self, info, response_id, **kwargs): room_response = query_responses.filter( ResponseModel.id == response_id).first() if not room_response: - raise GraphQLError("Response does not exist") + return_error.report_errors_bugsnag_and_graphQL( + "Response does not exist") if room_response.resolved: room_response.resolved = False room_response.save() @@ -238,7 +240,7 @@ def mutate(self, info, responses, resolved): room_response = query_responses.filter( ResponseModel.id == response).first() if not room_response: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "Response " + str(response) + " does not exist" ) for response in responses: @@ -276,7 +278,7 @@ def mutate(self, info, response_id): ResponseModel.id == response_id, ResponseModel.state == "active", ResponseModel.resolved).first() if not exact_response: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "The specified response does not exist\ or hasn't been resolved yet." ) diff --git a/api/response/schema_query.py b/api/response/schema_query.py index a6c815784..6f352e4d5 100644 --- a/api/response/schema_query.py +++ b/api/response/schema_query.py @@ -1,5 +1,4 @@ import graphene -from graphql import GraphQLError from api.response.schema import Response from api.room.schema import Room from api.room.models import Room as RoomModel @@ -18,6 +17,7 @@ map_response_type, ResponseDetail ) +from api.bugsnag_error import return_error class RoomResponse(graphene.ObjectType): @@ -95,7 +95,8 @@ def resolve_room_response(self, info, room_id, resolved=False): query_response = Response.get_query(info) room = query.filter_by(id=room_id).first() if not room: - raise GraphQLError("Non-existent room id") + return_error.report_errors_bugsnag_and_graphQL( + "Non-existent room id") room_response = query_response.filter_by(room_id=room_id) if resolved: room_response = room_response.filter_by(resolved=True) @@ -131,14 +132,15 @@ def search_response_by_room(self, info, **kwargs): RoomModel.name.ilike('%' + kwargs.get('room') + '%'), RoomModel.state == "active").first() if not exact_room: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "No response for this room, enter a valid room name") room_response = Response.get_query(info).filter_by( room_id=exact_room.id) if kwargs.get('resolved'): room_response = room_response.filter_by(resolved=True) if not room_response: - raise GraphQLError("No response for this room at the moment") + return_error.report_errors_bugsnag_and_graphQL( + "No response for this room at the moment") total_response = room_response.count() all_responses = Query.get_room_response( self, room_response, exact_room.id) diff --git a/api/room/models.py b/api/room/models.py index dc7b58f23..1f46038b7 100644 --- a/api/room/models.py +++ b/api/room/models.py @@ -3,7 +3,6 @@ ) from sqlalchemy.dialects import postgresql from sqlalchemy.orm import relationship -from graphql import GraphQLError from sqlalchemy.schema import Sequence from helpers.database import Base, db_session @@ -12,7 +11,9 @@ from api.response.models import Response # noqa: F401 from api.tag.models import Tag # noqa: F401 from utilities.validator import verify_calendar_id -from api.devices.models import Devices # noqa F4 +from api.devices.models import Devices # noqa F4 +from api.bugsnag_error import return_error + tags = Table( 'room_tags', @@ -66,13 +67,13 @@ class Room(Base, Utility): resources = relationship("RoomResource", back_populates='room') __table_args__ = ( - Index( - 'ix_unique_room_in_location_content', - 'name', - 'location_id', - unique=True, - postgresql_where=(state == 'active')), - ) + Index( + 'ix_unique_room_in_location_content', + 'name', + 'location_id', + unique=True, + postgresql_where=(state == 'active')), + ) @event.listens_for(Room, 'before_insert') @@ -80,7 +81,8 @@ def receive_before_insert(mapper, connection, target): @event.listens_for(db_session, "after_flush", once=True) def receive_after_flush(session, context): if not verify_calendar_id(target.calendar_id): - raise GraphQLError("Room calendar Id is invalid") + return_error.report_errors_bugsnag_and_graphQL( + "Room calendar Id is invalid") pass diff --git a/api/room/schema.py b/api/room/schema.py index 16c12bed9..030b4f3bd 100644 --- a/api/room/schema.py +++ b/api/room/schema.py @@ -1,7 +1,6 @@ import graphene from sqlalchemy import func from graphene_sqlalchemy import (SQLAlchemyObjectType) -from graphql import GraphQLError from api.room.models import Room as RoomModel from api.tag.models import Tag as TagModel from utilities.validations import ( @@ -18,6 +17,7 @@ ErrorHandler) from helpers.room_filter.room_filter import room_filter, room_join_location from helpers.pagination.paginate import Paginate, validate_page +from api.bugsnag_error import return_error class Room(SQLAlchemyObjectType): @@ -37,7 +37,8 @@ def save_room_tags(room, room_tags): missing_tags += str(tag)+" " room.room_tags.append(room_tag) if missing_tags: - raise GraphQLError("Tag id {}not found".format(missing_tags)) + return_error.report_errors_bugsnag_and_graphQL( + "Tag id {}not found".format(missing_tags)) room.save() @@ -164,7 +165,7 @@ def resolve_rooms(self, info, **kwargs): result = active_rooms.order_by(func.lower( RoomModel.name)).limit(per_page).offset(page*per_page) if result.count() == 0: - return GraphQLError("No more resources") + return_error.report_errors_bugsnag_and_graphQL("No more resources") return result @@ -195,7 +196,7 @@ def mutate(self, info, room_id, **kwargs): active_rooms = query_room.filter(RoomModel.state == "active") room = active_rooms.filter(RoomModel.id == room_id).first() if not room: - raise GraphQLError("Room not found") + return_error.report_errors_bugsnag_and_graphQL("Room not found") admin_roles.update_delete_rooms_create_resource(room_id) room_tags = [] if kwargs.get('room_tags'): @@ -224,7 +225,7 @@ def mutate(self, info, room_id, **kwargs): exact_room = active_rooms.filter( RoomModel.id == room_id).first() if not exact_room: - raise GraphQLError("Room not found") + return_error.report_errors_bugsnag_and_graphQL("Room not found") exact_room.room_tags.clear() admin_roles.update_delete_rooms_create_resource(room_id) update_entity_fields(exact_room, state="archived", **kwargs) @@ -249,7 +250,7 @@ def mutate(self, info, room_id, **kwargs): active_rooms = query_room.filter(RoomModel.state == "active") room = active_rooms.filter(RoomModel.id == room_id).first() if not room: - raise GraphQLError("Room not found") + return_error.report_errors_bugsnag_and_graphQL("Room not found") update_entity_fields(room, **kwargs) room.save() subscriber.update_room_token.delay( diff --git a/api/room/schema_query.py b/api/room/schema_query.py index 82612f962..66cf372d7 100644 --- a/api/room/schema_query.py +++ b/api/room/schema_query.py @@ -1,7 +1,6 @@ import graphene import os import re -from graphql import GraphQLError from helpers.calendar.analytics import RoomAnalytics # noqa: E501 from helpers.auth.authentication import Auth from helpers.auth.admin_roles import admin_roles @@ -26,6 +25,7 @@ ) from api.room.schema import (RatioOfCheckinsAndCancellations, BookingsAnalyticsCount) +from api.bugsnag_error import return_error def resolve_booked_rooms_analytics(*args): @@ -278,7 +278,7 @@ def check_valid_calendar_id(self, query, calendar_id): RoomModel.calendar_id == calendar_id ).first() if not check_calendar_id: - raise GraphQLError("CalendarId given not assigned to any room on converge") # noqa: E501 + return_error.report_errors_bugsnag_and_graphQL("CalendarId given not assigned to any room on converge") # noqa: E501 def room_occupants_room_schedule(self, info, calender_id, days): query = Room.get_query(info) @@ -387,7 +387,7 @@ def resolve_all_available_rooms(self, info, **kwargs): } res = service.freebusy().query( body=free_busy_rooms_request_object - ).execute() + ).execute() all_remote_rooms.append(res[u'calendars']) # all busy remote rooms in a given period @@ -397,13 +397,14 @@ def resolve_all_available_rooms(self, info, **kwargs): # all available rooms in a user's location in a given period available_rooms = [room for room in all_rooms if room[ 'id' - ] not in busy_rooms] + ] not in busy_rooms] all_available_rooms = [AvailableRooms(room['id'], room['name'] ) for room in available_rooms] def raise_no_available_rooms(): - raise GraphQLError("No available rooms at the moment") + return_error.report_errors_bugsnag_and_graphQL( + "No available rooms at the moment") return AllAvailableRooms( availableRoom=all_available_rooms @@ -414,7 +415,8 @@ def resolve_filter_rooms_by_tag(self, info, tagId): rooms = Room.get_query(info).join(tags).join( Tag).filter(tags.c.tag_id == tagId).all() if not rooms: - raise GraphQLError('No rooms found with this tag') + return_error.report_errors_bugsnag_and_graphQL( + 'No rooms found with this tag') return rooms @@ -424,17 +426,18 @@ def resolve_get_room_by_id(self, info, room_id): RoomModel.id == room_id, RoomModel.state == "active").first() if not check_room: - raise GraphQLError("Room not found") + return_error.report_errors_bugsnag_and_graphQL("Room not found") return check_room def resolve_get_room_by_name(self, info, name): query = Room.get_query(info) if name == "": - raise GraphQLError("Please input Room Name") + return_error.report_errors_bugsnag_and_graphQL( + "Please input Room Name") check_room_name = list(query.filter( RoomModel.name.ilike("%" + name + "%"), RoomModel.state == "active").all()) # noqa: E501 if not check_room_name: - raise GraphQLError("Room not found") + return_error.report_errors_bugsnag_and_graphQL("Room not found") return check_room_name def resolve_room_occupants(self, info, calendar_id, days): diff --git a/api/room_resource/schema.py b/api/room_resource/schema.py index 3db9a93af..a24d3aa27 100644 --- a/api/room_resource/schema.py +++ b/api/room_resource/schema.py @@ -1,7 +1,6 @@ import graphene from graphene_sqlalchemy import SQLAlchemyObjectType from sqlalchemy import func, and_, String, cast -from graphql import GraphQLError from api.room_resource.models import Resource as ResourceModel from api.room.models import RoomResource as RoomResourceModel @@ -13,6 +12,7 @@ from helpers.auth.error_handler import SaveContextManager from helpers.pagination.paginate import Paginate, validate_page from helpers.room_filter.room_filter import room_resources_join_room +from api.bugsnag_error import return_error class Resource(SQLAlchemyObjectType): @@ -51,15 +51,16 @@ def mutate(self, info, **kwargs): exact_room = active_rooms.filter( RoomModel.id == kwargs['room_id']).first() if not exact_room: - raise GraphQLError("Room not found") + return_error.report_errors_bugsnag_and_graphQL("Room not found") exact_resource = ResourceModel.query.filter_by( id=kwargs['resource_id'], state="active").first() if not exact_resource: - raise GraphQLError('Resource with such id does not exist.') + return_error.report_errors_bugsnag_and_graphQL( + 'Resource with such id does not exist.') resource_name = exact_resource.name assigned_quantity = kwargs['quantity'] if assigned_quantity < 1: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "Assigned quantity cannot be less than 1." ) resource_query = RoomResource.get_query(info) @@ -67,7 +68,7 @@ def mutate(self, info, **kwargs): RoomResourceModel.resource_id == kwargs['resource_id'], RoomResourceModel.room_id == kwargs['room_id']).first() if exact_resource_id: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( 'Resource already exists in the room.' ) room_resources = RoomResourceModel(**kwargs, name=resource_name) @@ -94,9 +95,10 @@ def mutate(self, info, **kwargs): room_id=room_id, resource_id=resource_id).first() if not actual_resource: - raise GraphQLError('Resource does not exist in the room') + return_error.report_errors_bugsnag_and_graphQL( + 'Resource does not exist in the room') if kwargs['quantity'] < 0: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( 'Assigned quantity cannot be less than zero' ) update_entity_fields(actual_resource, **kwargs) @@ -118,16 +120,18 @@ def mutate(self, info, resource_id, room_id): query = RoomResource.get_query(info) exact_room = RoomModel.query.filter_by(id=room_id).first() if not exact_room: - raise GraphQLError('Room does not exist') + return_error.report_errors_bugsnag_and_graphQL( + 'Room does not exist') exact_resource = ResourceModel.query.filter_by( id=resource_id).first() if not exact_resource: - raise GraphQLError('Resource does not exist') + return_error.report_errors_bugsnag_and_graphQL( + 'Resource does not exist') assigned_resource = query.filter( RoomResourceModel.room_id == room_id, RoomResourceModel.resource_id == resource_id).first() if not assigned_resource: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( 'The resource has not been assigned to the specified room') assigned_resource.delete() return DeleteAssignedResource(room_resource=assigned_resource) @@ -164,7 +168,7 @@ def resolve_resources(self, info): result = active_resources.order_by(func.lower( ResourceModel.name)).limit(per_page).offset(page*per_page) if result.count() == 0: - return GraphQLError("No more resources") + return_error.report_errors_bugsnag_and_graphQL("No more resources") return result @@ -206,7 +210,7 @@ def mutate(self, info, resource_id, **kwargs): exact_resource = active_resources.filter( ResourceModel.id == resource_id).first() if not exact_resource: - raise GraphQLError("Resource not found") + return_error.report_errors_bugsnag_and_graphQL("Resource not found") update_entity_fields(exact_resource, **kwargs) exact_resource.save() return UpdateRoomResource(resource=exact_resource) @@ -229,7 +233,7 @@ def mutate(self, info, resource_id): ResourceModel.state == "active", ResourceModel.id == resource_id)).first() if not exact_resource: - raise GraphQLError("Resource not found") + return_error.report_errors_bugsnag_and_graphQL("Resource not found") update_entity_fields(exact_resource, state="archived") query_room_resource = RoomResource.get_query(info) room_resources = query_room_resource.filter( @@ -287,13 +291,14 @@ def resolve_get_resources_by_room_id(self, info, room_id): RoomModel.state == "active" ).first() if not exact_room: - raise GraphQLError("Room not found") + return_error.report_errors_bugsnag_and_graphQL("Room not found") resource_query = Resource.get_query(info) room_resources = RoomResource.get_query(info).filter( RoomResourceModel.room_id == room_id ) if not room_resources.first(): - raise GraphQLError("Room has no resource yet") + return_error.report_errors_bugsnag_and_graphQL( + "Room has no resource yet") resources = [] for resource in room_resources: room_resource = resource_query.filter( @@ -309,14 +314,16 @@ def resolve_resource_by_name(self, info, search_name): matching_resources = [] resource_name = search_name.lower().replace(" ", "") if not resource_name: - raise GraphQLError("Please input Resource Name") + return_error.report_errors_bugsnag_and_graphQL( + "Please input Resource Name") all_resources = Resource.get_query(info).filter_by(state="active") for each_resource in all_resources: striped_resource = each_resource.name.lower().replace(" ", "") if resource_name in striped_resource: matching_resources.append(each_resource) if not matching_resources: - raise GraphQLError('No Matching Resource') + return_error.report_errors_bugsnag_and_graphQL( + 'No Matching Resource') return matching_resources @Auth.user_roles('Admin', 'Super Admin') @@ -324,7 +331,8 @@ def resolve_rooms_containing_resource(self, info, resource_id): resource_exists = Resource.get_query(info).filter_by( id=resource_id).first() if not resource_exists: - raise GraphQLError('Resource does not exist') + return_error.report_errors_bugsnag_and_graphQL( + 'Resource does not exist') query = RoomResource.get_query(info).join(ResourceModel).join( RoomModel) rooms_with_resource = query.filter( diff --git a/api/structure/schema.py b/api/structure/schema.py index c91a7b556..e616fffe5 100644 --- a/api/structure/schema.py +++ b/api/structure/schema.py @@ -3,7 +3,6 @@ from api.structure.models import Structure as StructureModel from helpers.auth.authentication import Auth from helpers.structure.create_structure import create_structure -from graphql import GraphQLError from helpers.auth.admin_roles import admin_roles from api.room.schema import Room from api.room.models import Room as RoomModel @@ -13,6 +12,7 @@ validate_unique_structure_id ) from utilities.utility import update_entity_fields +from api.bugsnag_error import return_error class Structure(SQLAlchemyObjectType): @@ -116,7 +116,8 @@ def mutate(self, info, structure_id, **kwargs): StructureModel.structure_id == structure_id, StructureModel.state == "active").first() if not active_structure: - raise GraphQLError('Structure not found') + return_error.report_errors_bugsnag_and_graphQL( + 'Structure not found') update_entity_fields(active_structure, **kwargs) active_structure.save() return UpdateOfficeStructure(structure=active_structure) @@ -164,11 +165,13 @@ def resolve_all_structures(self, info): @Auth.user_roles('Admin', 'Default User', 'Super Admin') def resolve_structure_by_structure_id(self, info, structure_id): if not structure_id.strip(): - raise GraphQLError("Please input a valid structureId") + return_error.report_errors_bugsnag_and_graphQL( + "Please input a valid structureId") query = Structure.get_query(info) location_id = admin_roles.user_location_for_analytics_view() structure = query.filter( StructureModel.structure_id == structure_id).first() if not structure or location_id != structure.location_id: - raise GraphQLError("Structure not found") + return_error.report_errors_bugsnag_and_graphQL( + "Structure not found") return structure diff --git a/api/tag/schema.py b/api/tag/schema.py index e16fb7656..e2212b6aa 100644 --- a/api/tag/schema.py +++ b/api/tag/schema.py @@ -1,11 +1,11 @@ import graphene -from graphql import GraphQLError from graphene_sqlalchemy import SQLAlchemyObjectType from api.tag.models import Tag as TagModel from utilities.validations import validate_empty_fields from utilities.utility import update_entity_fields from helpers.auth.authentication import Auth from helpers.auth.error_handler import SaveContextManager +from api.bugsnag_error import return_error class Tag(SQLAlchemyObjectType): @@ -32,9 +32,9 @@ def mutate(self, info, **kwargs): tag = TagModel(**kwargs) payload = { 'model': TagModel, 'field': 'name', 'value': kwargs['name'] - } + } with SaveContextManager( - tag, 'Tag', payload + tag, 'Tag', payload ): return CreateTag(tag=tag) @@ -58,7 +58,7 @@ def mutate(self, info, tag_id, **kwargs): TagModel.id == tag_id, TagModel.state == "active").first() if not tag: - raise GraphQLError("Tag not found") + return_error.report_errors_bugsnag_and_graphQL("Tag not found") update_entity_fields(tag, **kwargs) tag.save() return UpdateTag(tag=tag) @@ -80,7 +80,7 @@ def mutate(self, info, tag_id, **kwargs): tag = result.filter( TagModel.id == tag_id).first() if not tag: - raise GraphQLError("Tag not found") + return_error.report_errors_bugsnag_and_graphQL("Tag not found") update_entity_fields(tag, state="archived", **kwargs) tag.save() return DeleteTag(tag=tag) diff --git a/api/user/schema.py b/api/user/schema.py index 9149ec46a..bf081d6fc 100644 --- a/api/user/schema.py +++ b/api/user/schema.py @@ -1,7 +1,7 @@ import graphene + from graphene_sqlalchemy import (SQLAlchemyObjectType) from sqlalchemy import exc -from graphql import GraphQLError from api.user.models import User as UserModel from api.notification.models import Notification as NotificationModel @@ -15,6 +15,7 @@ from api.role.models import Role as RoleModel from api.location.models import Location as LocationModel from helpers.user_role.restrict_admin import check_admin_restriction +from api.bugsnag_error import return_error class User(SQLAlchemyObjectType): @@ -40,7 +41,8 @@ class Arguments: def mutate(self, info, **kwargs): user = UserModel(**kwargs) if not verify_email(user.email): - raise GraphQLError("This email is not allowed") + return_error.report_errors_bugsnag_and_graphQL( + "This email is not allowed") payload = { 'model': UserModel, 'field': 'email', 'value': kwargs['email'] } @@ -65,13 +67,15 @@ class Arguments: @Auth.user_roles('Admin', 'Super Admin') def mutate(self, info, email, **kwargs): if not verify_email(email): - raise GraphQLError("Invalid email format") + return_error.report_errors_bugsnag_and_graphQL( + "Invalid email format") user_to_be_deleted = User.get_query(info).filter_by(email=email).first() if not user_to_be_deleted: - raise GraphQLError("User not found") + return_error.report_errors_bugsnag_and_graphQL("User not found") current_user = get_user_from_db() if current_user.email == user_to_be_deleted.email: - raise GraphQLError("You cannot delete yourself") + return_error.report_errors_bugsnag_and_graphQL( + "You cannot delete yourself") should_remove_user = kwargs.get('remove') if should_remove_user: user_to_be_deleted.delete() @@ -98,15 +102,16 @@ def mutate(self, info, email, **kwargs): active_user = query_user.filter(UserModel.state == "active") exact_user = active_user.filter(UserModel.email == email).first() if not exact_user: - raise GraphQLError("User not found") + return_error.report_errors_bugsnag_and_graphQL("User not found") new_role = RoleModel.query.filter_by(id=kwargs['role_id']).first() if not new_role: - raise GraphQLError('invalid role id') + return_error.report_errors_bugsnag_and_graphQL('invalid role id') current_user_role = exact_user.roles[0].role if new_role.role == current_user_role: - raise GraphQLError('This role is already assigned to this user') + return_error.report_errors_bugsnag_and_graphQL( + 'This role is already assigned to this user') check_admin_restriction(new_role.role) exact_user.roles[0] = new_role @@ -114,7 +119,8 @@ def mutate(self, info, email, **kwargs): if not notification.send_changed_role_email( email, exact_user.name, new_role.role): - raise GraphQLError("Role changed but email not sent") + return_error.report_errors_bugsnag_and_graphQL( + "Role changed but email not sent") return ChangeUserRole(user=exact_user) @@ -136,13 +142,15 @@ def mutate(self, info, **kwargs): user = query_user.filter( UserModel.state == "active", UserModel.email == email).first() if not user: - raise GraphQLError("User not found") + return_error.report_errors_bugsnag_and_graphQL("User not found") new_location = LocationModel.query.filter_by( id=location_id, state="active").first() if not new_location: - raise GraphQLError('the location supplied does not exist') + return_error.report_errors_bugsnag_and_graphQL( + 'the location supplied does not exist') if user.location == new_location.name: - raise GraphQLError('user already in this location') + return_error.report_errors_bugsnag_and_graphQL( + 'user already in this location') user.location = new_location.name user.save() return ChangeUserLocation(user=user) @@ -163,7 +171,8 @@ def mutate(self, info, **kwargs): query_user = User.get_query(info) user = query_user.filter(UserModel.id == logged_in_user.id).first() if user.location: - raise GraphQLError('This user already has a location set.') + return_error.report_errors_bugsnag_and_graphQL( + 'This user already has a location set.') class CreateUserRole(graphene.Mutation): @@ -182,23 +191,26 @@ def mutate(self, info, **kwargs): exact_user = user.filter_by(id=kwargs['user_id']).first() if not exact_user: - raise GraphQLError('User not found') + return_error.report_errors_bugsnag_and_graphQL('User not found') role = Role.get_query(info) exact_role = role.filter_by(id=kwargs['role_id']).first() if not exact_role: - raise GraphQLError('Role id does not exist') + return_error.report_errors_bugsnag_and_graphQL( + 'Role id does not exist') if len(exact_user.roles) > 0: - raise GraphQLError('This user is already assigned a role') + return_error.report_errors_bugsnag_and_graphQL( + 'This user is already assigned a role') exact_user.roles.append(exact_role) exact_user.save() return CreateUserRole(user_role=exact_user) except exc.ProgrammingError: - raise GraphQLError("The database cannot be reached") + return_error.report_errors_bugsnag_and_graphQL( + "The database cannot be reached") class InviteToConverge(graphene.Mutation): @@ -214,12 +226,14 @@ class Arguments: @Auth.user_roles('Admin', 'Super Admin') def mutate(self, info, email): if not verify_email(email): - raise GraphQLError("Use a valid andela email") + return_error.report_errors_bugsnag_and_graphQL( + "Use a valid andela email") query_user = User.get_query(info) active_user = query_user.filter(UserModel.state == "active") user = active_user.filter(UserModel.email == email).first() if user: - raise GraphQLError("User already joined Converge") + return_error.report_errors_bugsnag_and_graphQL( + "User already joined Converge") admin = get_user_from_db() notification.email_invite(email, admin.__dict__["name"]) return InviteToConverge(email=email) diff --git a/api/user/schema_query.py b/api/user/schema_query.py index 3f2447fb5..30b874253 100644 --- a/api/user/schema_query.py +++ b/api/user/schema_query.py @@ -1,11 +1,11 @@ import graphene from sqlalchemy import func -from graphql import GraphQLError from api.user.schema import User from api.user.models import User as UserModel from helpers.auth.authentication import Auth from helpers.user_filter.user_filter import user_filter from helpers.pagination.paginate import Paginate, validate_page +from api.bugsnag_error import return_error class PaginatedUsers(Paginate): @@ -27,7 +27,7 @@ def resolve_users(self, info): users = exact_query.order_by( func.lower(UserModel.name)).limit(per_page).offset(page * per_page) if users.count() == 0: - return GraphQLError("No users found") + return_error.report_errors_bugsnag_and_graphQL("No users found") return users @@ -74,13 +74,14 @@ def resolve_user_by_name(self, info, user_name): user_list = [] user_name = ''.join(user_name.split()).lower() if not user_name: - raise GraphQLError("Please provide the user name") + return_error.report_errors_bugsnag_and_graphQL( + "Please provide the user name") active_users = User.get_query(info).filter_by(state="active") for user in active_users: exact_user_name = user.name.lower().replace(" ", "") if user_name in exact_user_name: user_list.append(user) if not user_list: - raise GraphQLError("User not found") + return_error.report_errors_bugsnag_and_graphQL("User not found") return user_list diff --git a/app.py b/app.py index f5d2c5a0a..1ccbd850e 100644 --- a/app.py +++ b/app.py @@ -2,6 +2,7 @@ from flask_graphql import GraphQLView from flask_cors import CORS from flask_json import FlaskJSON +from bugsnag.flask import handle_exceptions from flask_mail import Mail from config import config @@ -21,6 +22,7 @@ def create_app(config_name): app.config.from_object(config[config_name]) config[config_name].init_app(app) mail.init_app(app) + handle_exceptions(app) @app.route("/", methods=['GET']) def index(): diff --git a/fixtures/user/user_fixture.py b/fixtures/user/user_fixture.py index ba617d307..c1448c50e 100644 --- a/fixtures/user/user_fixture.py +++ b/fixtures/user/user_fixture.py @@ -280,7 +280,7 @@ filter_user_by_location = ''' query { - users (locationId:5) { + users (locationId:10) { users { email name diff --git a/helpers/auth/admin_roles.py b/helpers/auth/admin_roles.py index fff971008..9d02102f9 100644 --- a/helpers/auth/admin_roles.py +++ b/helpers/auth/admin_roles.py @@ -1,11 +1,10 @@ -from graphql import GraphQLError - from api.location.models import Location from api.room.models import Room as RoomModel from helpers.auth.user_details import get_user_from_db from helpers.room_filter.room_filter import ( location_join_room) from utilities.utility import StateType +from api.bugsnag_error import return_error class Admin_roles(): @@ -14,13 +13,13 @@ def create_rooms_update_delete_location(self, kwargs): admin_details = get_user_from_db() location = Location.query.filter_by(id=kwargs['location_id']).first() if admin_details.location != location.name: - raise GraphQLError("You are not authorized to make changes in " + location.name) # noqa: E501 + return_error.report_errors_bugsnag_and_graphQL("You are not authorized to make changes in " + location.name) # noqa: E501 def verify_admin_location(self, location_id): admin_details = get_user_from_db() location = Location.query.filter_by(id=location_id).first() if admin_details.location != location.name: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "You are not authorized to make changes in " + location.name ) @@ -30,7 +29,7 @@ def update_delete_rooms_create_resource(self, room_id): room_location = location_query.filter( RoomModel.id == room_id, RoomModel.state == "active").first() if admin_details.location != room_location.name: - raise GraphQLError("You are not authorized to make changes in " + room_location.name) # noqa: E501 + return_error.report_errors_bugsnag_and_graphQL("You are not authorized to make changes in " + room_location.name) # noqa: E501 def user_location_for_analytics_view(self, location_name=False): """ @@ -38,12 +37,14 @@ def user_location_for_analytics_view(self, location_name=False): """ admin_details = get_user_from_db() location = Location.query.filter_by( - name=admin_details.location + name=admin_details.location ).first() if not location: - raise GraphQLError('Your location does not exist') + return_error.report_errors_bugsnag_and_graphQL( + 'Your location does not exist') if location.state != StateType.active: - raise GraphQLError('Location is not active') + return_error.report_errors_bugsnag_and_graphQL( + 'Location is not active') if location_name: return location.name return location.id diff --git a/helpers/auth/authentication.py b/helpers/auth/authentication.py index 8c51e2db9..57f0c2ae1 100644 --- a/helpers/auth/authentication.py +++ b/helpers/auth/authentication.py @@ -6,7 +6,6 @@ from functools import wraps import requests -from graphql import GraphQLError from sqlalchemy.exc import SQLAlchemyError from api.user.models import User @@ -17,6 +16,7 @@ from utilities.utility import StateType from helpers.database import db_session +from api.bugsnag_error import return_error api_url = "https://api-prod.andela.com/api/v1/" @@ -146,10 +146,11 @@ def wrapper(*args, **kwargs): try: user = User.query.filter_by(email=email).first() except Exception: - raise GraphQLError("The database cannot be reached") + return_error.report_errors_bugsnag_and_graphQL( + "The database cannot be reached") if user and user.state != StateType.active: # pragma: no cover # noqa - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "Your account is not active, please contact an admin") # noqa if not user: @@ -164,7 +165,8 @@ def wrapper(*args, **kwargs): handle_http_error(message, status, expected_args) else: - raise GraphQLError(user_data[0].data) + return_error.report_errors_bugsnag_and_graphQL( + user_data[0].data) return wrapper diff --git a/helpers/auth/error_handler.py b/helpers/auth/error_handler.py index 4f6f8f311..6f747c77a 100644 --- a/helpers/auth/error_handler.py +++ b/helpers/auth/error_handler.py @@ -1,7 +1,7 @@ import os from sqlalchemy import exc -from graphql import GraphQLError from utilities.validator import ErrorHandler +from api.bugsnag_error import return_error class SaveContextManager(): @@ -21,9 +21,9 @@ def __enter__(self): long_error_message = str(error.orig) index = long_error_message.find("\n") Specific_error = long_error_message[:index] - raise GraphQLError(Specific_error) + return_error.report_errors_bugsnag_and_graphQL(Specific_error) else: # pragma: no cover - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "There seems to be a database connection error \ contact your admin for assistance") except exc.IntegrityError as err: diff --git a/helpers/calendar/analytics_helper.py b/helpers/calendar/analytics_helper.py index 2f3f898c0..52e253f23 100644 --- a/helpers/calendar/analytics_helper.py +++ b/helpers/calendar/analytics_helper.py @@ -3,7 +3,6 @@ from datetime import datetime, timedelta import dateutil.parser from dateutil.relativedelta import relativedelta -from graphql import GraphQLError from sqlalchemy.sql import text from helpers.database import db_session from helpers.calendar.events_sql import room_events_query @@ -14,6 +13,7 @@ from flask import request from flask_json import JsonError from api.events.models import Events as EventsModel +from api.bugsnag_error import return_error class EventsDuration(graphene.ObjectType): @@ -61,7 +61,8 @@ def all_analytics_date_validation(self, start_date, end_date): self, start_date, end_date ) if start_date > end_date: - raise GraphQLError('Earlier date should be lower than later date') + return_error.report_errors_bugsnag_and_graphQL( + 'Earlier date should be lower than later date') return (start_date, end_date) def validate_current_date(self, start_date, end_date): @@ -78,7 +79,7 @@ def validate_current_date(self, start_date, end_date): user_end_date = dateutil.parser.parse(end_date) today = dateutil.parser.parse(date_now) if user_start_date > today or user_end_date > today: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "Invalid date. You can not retrieve data beyond today") return(start_date, end_date) @@ -117,7 +118,8 @@ def get_room_details(self, query): if 'analytics' in request.url: raise JsonError(Message='No rooms in this location') else: - raise GraphQLError("No rooms in this location") + return_error.report_errors_bugsnag_and_graphQL( + "No rooms in this location") result = [{ 'name': room.name, 'room_id': room.id, @@ -136,16 +138,16 @@ def get_all_events_in_a_room(self, """ user_time_zone = CommonAnalytics.get_user_time_zone() hour_offset = str(event_start_time.astimezone(pytz.timezone( - user_time_zone)).utcoffset().total_seconds()/60/60) + 'h' + user_time_zone)).utcoffset().total_seconds()/60/60) + 'h' events = db_session.query(EventsModel).from_statement( - text(room_events_query)).params( - state="active", - room_id=room_id, - event_end_time=event_end_time.isoformat(), - event_start_time=event_start_time.isoformat(), - hour_offset=hour_offset - ).all() + text(room_events_query)).params( + state="active", + room_id=room_id, + event_end_time=event_end_time.isoformat(), + event_start_time=event_start_time.isoformat(), + hour_offset=hour_offset + ).all() return events def get_event_details(self, query, event, room_id): @@ -172,7 +174,8 @@ def get_total_bookings(instance, query, start_date, end_date, room_id=None): exact_room = active_rooms.filter( RoomModel.id == room_id).first() if not exact_room: - raise GraphQLError("Room Id does not exist") + return_error.report_errors_bugsnag_and_graphQL( + "Room Id does not exist") bookings = CommonAnalytics.get_bookings_count(instance, room, start_date, diff --git a/helpers/calendar/credentials.py b/helpers/calendar/credentials.py index 8ab5c8ad9..a8ad91e36 100644 --- a/helpers/calendar/credentials.py +++ b/helpers/calendar/credentials.py @@ -2,9 +2,9 @@ from apiclient.discovery import build from httplib2 import Http -from graphql import GraphQLError from oauth2client import file, client, tools # noqa from oauth2client.client import OAuth2WebServerFlow # noqa +from api.bugsnag_error import return_error class Credentials(): @@ -67,7 +67,7 @@ def get_google_api_calendar_list(pageToken=None): calendars_list = service.calendarList().list( pageToken=pageToken).execute() except Exception as exception: - raise GraphQLError(exception) + return_error.report_errors_bugsnag_and_graphQL(exception) return calendars_list diff --git a/helpers/calendar/events.py b/helpers/calendar/events.py index 022264f6e..adbbe570a 100644 --- a/helpers/calendar/events.py +++ b/helpers/calendar/events.py @@ -10,6 +10,7 @@ from api.room.models import Room as RoomModel from .analytics_helper import CommonAnalytics from .credentials import Credentials, get_google_calendar_events +from api.bugsnag_error import return_error class RoomSchedules(Credentials): @@ -119,14 +120,17 @@ def check_event_status(self, info, **kwargs): start_time=kwargs['start_time'], cancelled=True).count() if checked_in_events > 0 and 'meeting_end_time' not in kwargs: - raise GraphQLError("Event already checked in") + return_error.report_errors_bugsnag_and_graphQL( + "Event already checked in") elif checked_in_events < 1 and 'meeting_end_time' in kwargs: - raise GraphQLError("Event yet to be checked in") + return_error.report_errors_bugsnag_and_graphQL( + "Event yet to be checked in") elif cancelled_events > 0: - raise GraphQLError("Event already cancelled") + return_error.report_errors_bugsnag_and_graphQL( + "Event already cancelled") return room_id except AttributeError: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "This Calendar ID is invalid") diff --git a/helpers/connection/connection_error_handler.py b/helpers/connection/connection_error_handler.py index a824c5c3b..3a980e84e 100644 --- a/helpers/connection/connection_error_handler.py +++ b/helpers/connection/connection_error_handler.py @@ -1,5 +1,5 @@ -from graphql import GraphQLError from flask_json import JsonError +from api.bugsnag_error import return_error def handle_http_error(*args): @@ -9,4 +9,4 @@ def handle_http_error(*args): message, status, expected_args = args if 'REST' in expected_args: raise JsonError(message=message, status=status) - raise GraphQLError(message) + return_error.report_errors_bugsnag_and_graphQL(message) diff --git a/helpers/devices/devices.py b/helpers/devices/devices.py index fceead7e1..0b929618d 100644 --- a/helpers/devices/devices.py +++ b/helpers/devices/devices.py @@ -1,15 +1,15 @@ -from graphql import GraphQLError from api.devices.models import Devices as DeviceModel from api.devices.schema import Devices as DeviceSchema +from api.bugsnag_error import return_error def update_device_last_activity(info, room_id, activity_time, activity): device_query = DeviceSchema.get_query(info) device = device_query.filter( - DeviceModel.room_id == room_id - ).first() + DeviceModel.room_id == room_id + ).first() if not device: - raise GraphQLError("Room device not found") + return_error.report_errors_bugsnag_and_graphQL("Room device not found") device.last_seen = activity_time device.last_activity = activity device.save() diff --git a/helpers/events_filter/events_filter.py b/helpers/events_filter/events_filter.py index 4784d36b6..57037c473 100644 --- a/helpers/events_filter/events_filter.py +++ b/helpers/events_filter/events_filter.py @@ -1,8 +1,8 @@ from datetime import datetime, timedelta -from graphql import GraphQLError import pytz import inspect from dateutil import parser +from api.bugsnag_error import return_error utc = pytz.utc @@ -12,9 +12,11 @@ def validate_date_input(start_date, end_date): Ensures either both or none of start_date and end_date is supplied """ if start_date and not end_date: - raise GraphQLError("endDate argument missing") + return_error.report_errors_bugsnag_and_graphQL( + "endDate argument missing") if end_date and not start_date: - raise GraphQLError("startDate argument missing") + return_error.report_errors_bugsnag_and_graphQL( + "startDate argument missing") def validate_calendar_id_input(calendar_id): @@ -22,7 +24,7 @@ def validate_calendar_id_input(calendar_id): Ensures that the calendar id is supplied """ if not calendar_id: - raise GraphQLError("Calendar Id missing") + return_error.report_errors_bugsnag_and_graphQL("Calendar Id missing") def format_range_dates(start_date, end_date): @@ -35,7 +37,8 @@ def format_range_dates(start_date, end_date): end_date = datetime.strptime(end_date, '%b %d %Y') if start_date > end_date: - raise GraphQLError("Start date must be lower than end date") + return_error.report_errors_bugsnag_and_graphQL( + "Start date must be lower than end date") start_date = start_date end_date = end_date + timedelta(days=1) @@ -48,13 +51,16 @@ def format_range_dates(start_date, end_date): def validate_page_and_per_page(page, per_page): if page is not None and page < 1: - raise GraphQLError("page must be at least 1") + return_error.report_errors_bugsnag_and_graphQL( + "page must be at least 1") if per_page is not None and per_page < 1: - raise GraphQLError("perPage must be at least 1") + return_error.report_errors_bugsnag_and_graphQL( + "perPage must be at least 1") if page and not per_page: - raise GraphQLError("perPage argument missing") + return_error.report_errors_bugsnag_and_graphQL( + "perPage argument missing") if per_page and not page: - raise GraphQLError("page argument missing") + return_error.report_errors_bugsnag_and_graphQL("page argument missing") else: return (page, per_page) @@ -76,7 +82,8 @@ def empty_string_checker(string_to_check): context = info_data[1].code_context[0].strip() error_title = context[context.index("(") + 1:context.rindex(")")] if not string_to_check: - raise GraphQLError('{} can not be empty'.format(error_title)) + return_error.report_errors_bugsnag_and_graphQL( + '{} can not be empty'.format(error_title)) def date_time_format_validator(date_text, time_text): @@ -116,7 +123,8 @@ def calendar_dates_format(start_date, start_time, duration): current_date = datetime.now() if current_date > start_date: - raise GraphQLError("Sorry time travel hasn't been invented yet") + return_error.report_errors_bugsnag_and_graphQL( + "Sorry time travel hasn't been invented yet") end_date = start_date + timedelta(minutes=duration) @@ -135,7 +143,8 @@ def format_range_time(start_time, end_time): end_time = datetime.strptime(end_time, '%H:%M:%S') if start_time > end_time: - raise GraphQLError("Start time must be lower than end time") + return_error.report_errors_bugsnag_and_graphQL( + "Start time must be lower than end time") return start_time, end_time diff --git a/helpers/pagination/paginate.py b/helpers/pagination/paginate.py index 82c3d679b..3eb9ad225 100644 --- a/helpers/pagination/paginate.py +++ b/helpers/pagination/paginate.py @@ -2,7 +2,7 @@ import graphene from math import ceil -from graphql import GraphQLError +from api.bugsnag_error import return_error class Paginate(graphene.ObjectType): @@ -52,13 +52,14 @@ def resolve_has_previous(self, has_previous): def resolve_current_page(self, pages): pages = self.resolve_pages(pages) if self.page > pages: - raise GraphQLError("Page does not exist") + return_error.report_errors_bugsnag_and_graphQL( + "Page does not exist") return self.page def validate_page(page): if page < 1: - raise GraphQLError("No page requested") + return_error.report_errors_bugsnag_and_graphQL("No page requested") else: return page - 1 @@ -67,6 +68,7 @@ class ListPaginate: """Handles pagination for data(list) which is queried from google calendar API rather than from our database """ + def __init__(self, iterable, per_page, page): self.iterable = iterable self.per_page = per_page @@ -112,7 +114,8 @@ def get_paginated_result(iterable, per_page): # get page_number that can be used in list indexing def get_page_index(self): if self.page < 1: - raise GraphQLError("Invalid page requested") + return_error.report_errors_bugsnag_and_graphQL( + "Invalid page requested") return self.page - 1 def get_paginated(self): @@ -124,5 +127,6 @@ def get_current_page(self): try: self.current_page = self.paginated[self.get_page_index()] except IndexError: - raise GraphQLError("Page does not exist") + return_error.report_errors_bugsnag_and_graphQL( + "Page does not exist") return self.current_page diff --git a/helpers/questions_filter/questions_filter.py b/helpers/questions_filter/questions_filter.py index b1a14e912..f610da1e6 100644 --- a/helpers/questions_filter/questions_filter.py +++ b/helpers/questions_filter/questions_filter.py @@ -1,6 +1,6 @@ from dateutil import parser from dateutil.relativedelta import relativedelta -from graphql import GraphQLError +from api.bugsnag_error import return_error def filter_questions_by_date_range(questions, start_date, end_date): @@ -27,7 +27,8 @@ def format_range_dates(start_date, end_date): start_date = parser.parse(start_date).strftime('%Y-%m-%d') end_date = parser.parse(end_date).strftime('%Y-%m-%d') if start_date > end_date: - raise GraphQLError("Start date must be lower than end date") + return_error.report_errors_bugsnag_and_graphQL( + "Start date must be lower than end date") start_date = parser.parse(start_date) end_date = parser.parse(end_date) + relativedelta(days=1) return(start_date, end_date) diff --git a/helpers/response/query_response.py b/helpers/response/query_response.py index cf2b5ea49..ea0f0ceff 100644 --- a/helpers/response/query_response.py +++ b/helpers/response/query_response.py @@ -1,6 +1,6 @@ from datetime import datetime, timedelta -from graphql import GraphQLError from utilities.validations import validate_date_range +from api.bugsnag_error import return_error def filter_rooms_by_responses( @@ -49,7 +49,7 @@ def check_limits_are_provided(lower_limit, upper_limit, data_type): and not isinstance(upper_limit, data_type)) or (isinstance(upper_limit, data_type) and not isinstance(lower_limit, data_type))): - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "Provide upper and lower limits to filter") @@ -66,7 +66,7 @@ def validate_responses_by_room(data_type, function, **kwargs): if kwargs['filtered_search']: return kwargs['filtered_search'] else: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( "No response for this room at this range") diff --git a/helpers/user_filter/user_filter.py b/helpers/user_filter/user_filter.py index 3c7a20df2..07725238a 100644 --- a/helpers/user_filter/user_filter.py +++ b/helpers/user_filter/user_filter.py @@ -1,7 +1,6 @@ -from graphql import GraphQLError - from api.location.models import Location as LocationModel from api.user.models import users_roles +from api.bugsnag_error import return_error def user_filter(query, filter_data): @@ -31,7 +30,8 @@ def user_filter(query, filter_data): def filter_by_location(query, location): get_location = LocationModel.query.filter_by(id=location).first() if not get_location: - raise GraphQLError("Location id does not exist") + return_error.report_errors_bugsnag_and_graphQL( + "Location id does not exist") return query.filter_by(location=get_location.name) diff --git a/helpers/user_role/restrict_admin.py b/helpers/user_role/restrict_admin.py index a2be7f28d..440e48d56 100644 --- a/helpers/user_role/restrict_admin.py +++ b/helpers/user_role/restrict_admin.py @@ -1,6 +1,6 @@ -from graphql import GraphQLError from helpers.auth.user_details import get_user_from_db from api.role.models import Role as RoleModel +from api.bugsnag_error import return_error def check_admin_restriction(new_role): @@ -12,4 +12,5 @@ def check_admin_restriction(new_role): admin_role = RoleModel.query.filter_by( id=admin_details.roles[0].id).first() if admin_role.role != 'Super Admin' and new_role == 'Super Admin': - raise GraphQLError('You are not authorized to assign this role') + return_error.report_errors_bugsnag_and_graphQL( + 'You are not authorized to assign this role') diff --git a/manage.py b/manage.py index a9e7943d6..427431a80 100644 --- a/manage.py +++ b/manage.py @@ -1,16 +1,28 @@ import os import bugsnag +import logging from flask_script import Manager, Shell from bugsnag.flask import handle_exceptions +from bugsnag.handlers import BugsnagHandler +from bugsnag.celery import connect_failure_handler +connect_failure_handler() # Configure bugnsag bugsnag.configure( - api_key=os.getenv('BUGSNAG_API_TOKEN'), - release_stage="development", - project_root="app" + api_key=os.getenv('BUGSNAG_API_TOKEN'), + release_stage="development", + asynchronous=False, + auto_capture_sessions=True, + notify_release_stages=['development', 'production'] ) +logger = logging.getLogger("test.logger") +handler = BugsnagHandler() + +# send only ERROR-level logs and above +handler.setLevel(logging.ERROR) +logger.addHandler(handler) # local imports from app import create_app # noqa: E402 diff --git a/services/room_cancelation/auto_cancel_event.py b/services/room_cancelation/auto_cancel_event.py index a74402a7b..65418fa97 100644 --- a/services/room_cancelation/auto_cancel_event.py +++ b/services/room_cancelation/auto_cancel_event.py @@ -1,10 +1,10 @@ from dateutil.relativedelta import relativedelta -from graphql import GraphQLError from api.room.models import Room as RoomModel from api.events.models import Events as EventsModel from datetime import datetime from helpers.calendar.credentials import Credentials from helpers.email.email import event_cancellation_notification +from api.bugsnag_error import return_error class UpdateRecurringEvent(): @@ -136,7 +136,8 @@ def update_recurring_event_status(self): if not event_cancellation_notification( event, room_id, event_reject_reason ): - raise GraphQLError("Event cancelled but email not sent") + return_error.report_errors_bugsnag_and_graphQL( + "Event cancelled but email not sent") for missed_checkin in missed_checkins: missed_checkin.state = "archived" missed_checkin.save() diff --git a/utilities/validations.py b/utilities/validations.py index 677a83923..2bb2456b9 100644 --- a/utilities/validations.py +++ b/utilities/validations.py @@ -3,9 +3,9 @@ import re from api.location.models import CountryType, TimeZoneType -from graphql import GraphQLError from api.structure.models import Structure as StructureModel from api.office_structure.models import OfficeStructure as OfficeStructureModel +from api.bugsnag_error import return_error def validate_url(**kwargs): @@ -113,7 +113,8 @@ def validate_room_labels(**kwargs): for label in room_labels: structure = StructureModel.query.filter_by(name=label).first() if structure is None: - raise GraphQLError("Structure does not exist") + return_error.report_errors_bugsnag_and_graphQL( + "Structure does not exist") break @@ -127,7 +128,7 @@ def validate_structure_id(**kwargs): structure_id=structure_id).first() if not structure: error_message = 'The structure {} does not exist'.format(structure_id) - raise GraphQLError(error_message) + return_error.report_errors_bugsnag_and_graphQL(error_message) def validate_unique_structure_id(**kwargs): @@ -138,32 +139,35 @@ def validate_unique_structure_id(**kwargs): [structure['structure_id'] for structure in kwargs['data']] for structure_id in structure_id_list: if structure_id_list.count(structure_id) > 1: - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( 'The office stuctures does not contain unique ids') structure_in_db = StructureModel.query.filter_by( structure_id=structure_id).first() if structure_in_db: - raise GraphQLError('{} already exists'.format(structure_in_db.name)) + return_error.report_errors_bugsnag_and_graphQL( + '{} already exists'.format(structure_in_db.name)) def ensure_unique_id(node_list): node_id_list = [node.id for node in node_list] if len(node_list) != len(set(node_id_list)): - raise GraphQLError('nodes must have unique id') + return_error.report_errors_bugsnag_and_graphQL( + 'nodes must have unique id') node_with_id = OfficeStructureModel.query.filter( OfficeStructureModel.id.in_(node_id_list)).first() if node_with_id: - raise GraphQLError('node id "{}" in use'.format(node_with_id.id)) + return_error.report_errors_bugsnag_and_graphQL( + 'node id "{}" in use'.format(node_with_id.id)) def ensure_single_root_node(node_list): total_root_nodes = len( - [node.parent_id for node in node_list if node.parent_id is None]) + [node.parent_id for node in node_list if node.parent_id is None]) if total_root_nodes != 1: - raise GraphQLError( - 'there must be exactly 1 root node. {} were supplied'.format( - total_root_nodes)) + return_error.report_errors_bugsnag_and_graphQL( + 'there must be exactly 1 root node. {} were supplied'.format( + total_root_nodes)) def ensure_valid_parent_id(node_list): @@ -173,9 +177,9 @@ def ensure_valid_parent_id(node_list): available_parents = set() for node in node_list: if node.parent_id and node.parent_id not in available_parents: - raise GraphQLError( - 'node "{}" appears before its parent'.format( - node.name)) + return_error.report_errors_bugsnag_and_graphQL( + 'node "{}" appears before its parent'.format( + node.name)) available_parents.add(node.id) @@ -185,7 +189,8 @@ def validate_structure_nodes(node_list): a valid structure """ if not len(node_list): - raise GraphQLError('node_list cannot be empty') + return_error.report_errors_bugsnag_and_graphQL( + 'node_list cannot be empty') ensure_unique_id(node_list) ensure_single_root_node(node_list) ensure_valid_parent_id(node_list) diff --git a/utilities/validator.py b/utilities/validator.py index 3bf7e213a..d62e89a8b 100644 --- a/utilities/validator.py +++ b/utilities/validator.py @@ -1,10 +1,10 @@ import re -from graphql import GraphQLError from helpers.calendar.credentials import ( get_google_calendar_events) from api.location.models import Location from api.tag.models import Tag as TagModel +from api.bugsnag_error import return_error def verify_email(email): @@ -23,7 +23,8 @@ def verify_location_id(kwargs): location_id = kwargs.get('location_id') if location_id and not Location.query.filter_by(id=location_id, state="active").first(): - raise AttributeError("Location Id does not exist") + return_error.report_errors_bugsnag_and_graphQL( + "Location Id does not exist") def verify_tag_id(tag_id): @@ -33,7 +34,8 @@ def verify_tag_id(tag_id): """ tag_id = TagModel.query.filter_by(id=tag_id).first() if not tag_id: - raise GraphQLError("Tag ID Provided does not exist") + return_error.report_errors_bugsnag_and_graphQL( + "Tag ID Provided does not exist") class ErrorHandler(): @@ -41,14 +43,15 @@ class ErrorHandler(): def check_conflict(self, entity_name, entity): # Database integrity error - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( '{} {} already exists'.format(entity_name, entity)) def foreign_key_conflict(self, entity_name, entity): # Database foreign key error - raise GraphQLError( + return_error.report_errors_bugsnag_and_graphQL( '{} {} does not exists'.format(entity_name, entity)) def db_connection(self): # Database connection error - raise GraphQLError('Error: Could not connect to Db') + return_error.report_errors_bugsnag_and_graphQL( + 'Error: Could not connect to Db')