Skip to content

Commit

Permalink
Merge pull request #541 from FreeTAKTeam/539-fix-emergency-end-point
Browse files Browse the repository at this point in the history
539 fix emergency end point
  • Loading branch information
brothercorvo authored Mar 1, 2023
2 parents 8568366 + 8515b4c commit af3f6dc
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,36 @@
??BroadcastEmergency = FreeTAKServer.components.extended.emergency.emergency_facade.Emergency.emergency_broadcast
??SendEmergenciesToClient = FreeTAKServer.components.extended.emergency.emergency_facade.Emergency.send_emergencies_to_client

; this action is responsible for saving an emergency node to persistence
; Request
; action: SaveEmergency
; values:
; None
; Response values:
; values:
; model_object (Node): an emergency node to be persisted
??SaveEmergency = FreeTAKServer.components.extended.emergency.emergency_facade.Emergency.save_emergency

; this action is responsible for getting all persisted emergencies
; Request
; action: GetAllEmergencies
; values:
; None
; Response values:
; values:
; emergencies (List[Node]): a list of node objects representing persisted emergency objects
??GetAllEmergencies = FreeTAKServer.components.extended.emergency.emergency_facade.Emergency.get_all_emergencies

; this action is responsible for deleting a persisted emergency
; Request
; action: DeleteEmergency
; values:
; uid
; Response values:
; values:
; model_object (Node): the uid of an emergency to be deleted
??DeleteEmergency = FreeTAKServer.components.extended.emergency.emergency_facade.Emergency.delete_emergency

[Emergency]
__class = FreeTAKServer.components.extended.emergency.emergency_facade.Emergency

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ Emergency??EmergencyCancelled = FreeTAKServer.components.extended.emergency.cont
??BroadcastEmergency = FreeTAKServer.components.extended.emergency.controllers.emergency_sender_controller.EmergencySenderController.broadcast_emergency

??SaveEmergency = FreeTAKServer.components.extended.emergency.controllers.emergency_persistence.EmergencyPersistence.save_emergency
??DeleteEmergency = FreeTAKServer.components.extended.emergency.controllers.emergency_persistence.EmergencyPersistence.delete_emergency
??GetAllEmergencies = FreeTAKServer.components.extended.emergency.controllers.emergency_persistence.EmergencyPersistence.get_all_emergencies

??ParseEmergencyOn = FreeTAKServer.components.extended.emergency.controllers.emergency_on_controller.EmergencyOnController.parse_emergency_on
Expand All @@ -21,6 +20,17 @@ Emergency??EmergencyCancelled = FreeTAKServer.components.extended.emergency.cont
??SerializeEmergency = FreeTAKServer.components.extended.emergency.controllers.emergency_general_controller.EmergencyGeneralController.serialize_emergency

??ConvertType = FreeTAKServer.components.extended.emergency.controllers.emergency_general_controller.EmergencyGeneralController.convert_type

; this action is responsible for deleting a persisted emergency
; Request
; action: DeleteEmergency
; values:
; uid
; Response values:
; values:
; model_object (Node): the uid of an emergency to be deleted
??DeleteEmergency = FreeTAKServer.components.extended.emergency.controllers.emergency_persistence.EmergencyPersistence.delete_emergency

[Request]
__class = digitalpy.core.zmanager.impl.default_request.DefaultRequest

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from ..configuration.emergency_constants import (
DEST_SCHEMA,
DEST_CLASS,
MAXIMUM_EMERGENCY_DISTANCE
)

from FreeTAKServer.components.core.domain.domain._event import Event
Expand Down Expand Up @@ -74,10 +73,10 @@ def validate_user_distance(self, emergency: Event, connection):
# check that the distance between the user and the emergency is less than 10km
# TODO: this hardcoded distance should be added to the business rules
return (
MAXIMUM_EMERGENCY_DISTANCE==0 or
config.EmergencyRadius==0 or
distance.geodesic(
(connection_location.lat, connection_location.lon),
(emergency.point.lat, emergency.point.lon),
).km
< MAXIMUM_EMERGENCY_DISTANCE
< config.EmergencyRadius
)
5 changes: 5 additions & 0 deletions FreeTAKServer/core/configuration/MainConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ class MainConfig:
"IntegrationManagerPublisherAddress": {"default": "127.0.0.1", "type": str},
"yaml_path": {"default": r"/opt/FTSConfig.yaml", "type": str},
"ip": {"default": _ip, "type": str},
# radius of emergency within-which users will receive it
"EmergencyRadius": {"default": 10, "type": int}
}

# This structure maps environmental vars to config vars
Expand Down Expand Up @@ -240,6 +242,8 @@ class MainConfig:
"FTS_INTEGRATION_MANAGER_PUBLISHER_PORT": "IntegrationManagerPublisherPort",
# address from which to publish messages by the integration manager
"FTS_INTEGRATION_MANAGER_PUBLISHER_ADDRESS": "IntegrationManagerPublisherAddress",
# radius of emergency within-which users will receive it
"FTS_EMERGENCY_RADIUS": "EmergencyRadius"
}

# This is a simple representation of the YAML config schema with
Expand All @@ -254,6 +258,7 @@ class MainConfig:
"FTS_SECRET_KEY": "SecretKey",
"FTS_DATA_RECEPTION_BUFFER": "DataReceptionBuffer",
"FTS_MAX_RECEPTION_TIME": "MaxReceptionTime",
"FTS_EMERGENCY_RADIUS": "EmergencyRadius"
},
"Addresses": {
"FTS_COT_PORT": "CoTServicePort",
Expand Down
176 changes: 96 additions & 80 deletions FreeTAKServer/services/rest_api_service/rest_api_service_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import qrcode
import io
from typing import Dict, List
from geopy import Point, distance
import time

from digitalpy.core.service_management.digitalpy_service import DigitalPyService
Expand Down Expand Up @@ -1128,47 +1127,6 @@ def postChatToAll():
logger.error(str(e))
return "An error occurred sending chat.", 500


@app.route("/ManageEmergency/getEmergency", methods=[restMethods.GET])
@auth.login_required
def getEmergency():
try:
output = getemergencys()
return jsonify(json_list=output), 200
except Exception as e:
logger.error(str(e))
return "An error occurred retreiving emergency details.", 500


@app.route("/ManageEmergency/postEmergency", methods=[restMethods.POST])
@auth.login_required
def postEmergency():
try:
jsondata = request.get_json(force=True)
jsonobj = JsonController().serialize_emergency_post(jsondata)
EmergencyObject = SendEmergencyController(jsonobj).getCoTObject()
APIPipe.put(EmergencyObject)
return EmergencyObject.modelObject.getuid(), 200
except Exception as e:
logger.error(str(e))
return "An error occurred adding emergency.", 500


@app.route("/ManageEmergency/deleteEmergency", methods=[restMethods.DELETE])
@auth.login_required
def deleteEmergency():
try:
from json import dumps
jsondata = request.get_json(force=True)
jsonobj = JsonController().serialize_emergency_delete(jsondata)
EmergencyObject = SendEmergencyController(jsonobj).getCoTObject()
APIPipe.put(EmergencyObject)
return 'success', 200
except Exception as e:
logger.error(str(e))
return "An error occurred deleting emergency.", 500


@app.route("/Sensor")
@auth.login_required
def sensor():
Expand Down Expand Up @@ -1268,13 +1226,6 @@ def authenticate_user():
logger.error(str(e))
return "An error occurred authenticating user.", 500


@app.route("/ManageEmergency")
@auth.login_required
def Emergency():
pass


# @app.route("/ConnectionMessage", methods=[restMethods.POST])
def ConnectionMessage():
"""this endpoint is responsible for generating the CoT equivalen to the content of the JSON sent
Expand Down Expand Up @@ -1834,43 +1785,36 @@ def emitUpdates(Updates):
socketio.emit('up', json.dumps(returnValue), broadcast=True)
return 1

APPLICATION_PROTOCOL = "xml"
# API Request timeout in ms
API_REQUEST_TIMEOUT = 5000
# TODO: move this out of the rest_api_service and into it's own file in views
# this will require changing it from using the API Pipe to use the ZManager instead
import json
import datetime as dt
import datetime

from flask import request
from typing import Dict, List
from digitalpy.core.main.object_factory import ObjectFactory
from digitalpy.core.zmanager.request import Request
from geopy import Point, distance

from FreeTAKServer.core.RestMessageControllers.SendSimpleCoTController import SendSimpleCoTController
from FreeTAKServer.core.parsers.JsonController import JsonController

from FreeTAKServer.services.rest_api_service.views.base_view import BaseView

class ManageGeoObjects(views.View):
#decorators = [auth.login_required]
def dispatch_request(self, method):
class ManageGeoObjects(BaseView):
"""this class is responsible for creating the flask views required for managing
geo objects in FTS
"""
decorators = [auth.login_required]

def __init__(self) -> None:
endpoints: Dict[str, callable] = {
"GetRepeatedMessages": self.get_repeated_messages,
"postGeoObject": self.post_geo_object,
"DeleteRepeatedMessages": self.delete_repeated_messages,
}
return endpoints[method]()

def make_request(self, action: str, values: Dict = {}, synchronous:bool=True) -> Response:
"""make a request to the zmanager
Args:
action (str): the action key to be sent
values (Dict, optional): the values to be sent in the reques. Defaults to {}.
synchronous (bool, optional): whether or not to wait for a response from the zmanager. Defaults to True.
Returns:
Response: the response coming from the the zmanager
"""
rest_api_service = ObjectFactory.get_instance("RestAPIService")

# request to get repeated messages
request: Request = ObjectFactory.get_new_instance("request")
request.set_action(action)
request.set_sender(rest_api_service.__class__.__name__.lower())
request.set_format("pickled")
request.set_values(values)
rest_api_service.subject_send_request(request, APPLICATION_PROTOCOL)
if synchronous:
response = rest_api_service.retrieve_response(request.get_id())
return response
super().__init__(endpoints)

def get_repeated_messages(self):
"""method to retrieve a repeated messages
Expand Down Expand Up @@ -2002,8 +1946,80 @@ def delete_repeated_messages(self):
except Exception as e:
return str(e), 500

# TODO: move this out of the rest_api_service and into it's own file in views
# this will require changing it from using the API Pipe to use the ZManager instead

from FreeTAKServer.services.rest_api_service.views.base_view import BaseView
from typing import Dict
from flask import request

from FreeTAKServer.core.configuration.LoggingConstants import LoggingConstants
from FreeTAKServer.core.configuration.CreateLoggerController import CreateLoggerController
from FreeTAKServer.core.parsers.JsonController import JsonController
from FreeTAKServer.core.RestMessageControllers.SendEmergencyController import SendEmergencyController

loggingConstants = LoggingConstants(log_name="FTS-ManageEmergencyView")
logger = CreateLoggerController("FTS-ManageEmergencyView", logging_constants=loggingConstants).getLogger()

class ManageEmergency(BaseView):
decorators = [auth.login_required]

def __init__(self) -> None:
endpoints = {
"getEmergency": self.get_emergency,
"postEmergency": self.post_emergency,
"deleteEmergency": self.delete_emergency,
}
super().__init__(endpoints)

def get_emergency(self):
"""method to retrieve all emergency's
Returns:
str: returns a json string containing dictionary of emergency's
"""
response = self.make_request("GetAllEmergencies") # retrieve emergencies from the emergency component
emergencies = response.get_value("emergencies")
output = {"json_list": []}
for emergency in emergencies:
try:
serialized_emergency = {
"lat": emergency.point.lat,
"lon": emergency.point.lon,
"type": emergency.detail.emergency.type,
"name": emergency.detail.contact.callsign,
"uid": emergency.uid
}
output["json_list"].append(serialized_emergency)
except AttributeError as ex:
logger.error("emergency model object missing attribute %s", ex)
return output

def post_emergency(self):
jsondata = request.get_json(force=True)
jsonobj = JsonController().serialize_emergency_post(jsondata)
emergency_object = SendEmergencyController(jsonobj).getCoTObject()
self.make_request("SaveEmergency", {"model_object": emergency_object.modelObject})
APIPipe.put(emergency_object)
return emergency_object.modelObject.getuid(), 200

def delete_emergency(self) -> str:
"""delete an emergency from the emergency persistence
"""
jsondata = request.get_json(force=True)
jsonobj = JsonController().serialize_emergency_delete(jsondata)
emergency_object = SendEmergencyController(jsonobj).getCoTObject()
APIPipe.put(emergency_object)
self.make_request("DeleteEmergency", {"model_object": emergency_object.modelObject})
return 'success', 200

app.add_url_rule('/ManageEmergency/<method>', view_func=ManageEmergency.as_view('/ManageEmergency/<method>'), methods=["POST", "GET", "DELETE"])
app.add_url_rule('/ManageGeoObject/<method>', view_func=ManageGeoObjects.as_view('/ManageGeoObject/<method>'), methods=["POST", "GET","DELETE"])

APPLICATION_PROTOCOL = "xml"
API_REQUEST_TIMEOUT = 5000

class RestAPI(DigitalPyService):

# a dictionary containing the request_id and response objects for all received requests
Expand Down
Empty file.
60 changes: 60 additions & 0 deletions FreeTAKServer/services/rest_api_service/views/base_view.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from flask import request, views
from typing import Dict, List
from digitalpy.core.main.object_factory import ObjectFactory
from digitalpy.core.zmanager.request import Request
from digitalpy.core.zmanager.response import Response

from FreeTAKServer.core.configuration.LoggingConstants import LoggingConstants
from FreeTAKServer.core.configuration.CreateLoggerController import CreateLoggerController

loggingConstants = LoggingConstants(log_name="FTS-ManageEmergencyView")
logger = CreateLoggerController("FTS-ManageEmergencyView", logging_constants=loggingConstants).getLogger()

APPLICATION_PROTOCOL = "xml"
# API Request timeout in ms
API_REQUEST_TIMEOUT = 5000

class BaseView(views.View):
"""base class meant to provide the base functionality for
an FTS API views
"""
def __init__(self, endpoints: Dict[str, callable]) -> None:
"""the BaseView constructor. Meant to be called by inheriting class'
Args:
endpoints (Dict[str, callable]): dictionary containing endpoint names and their associated functions
"""
self.endpoints = endpoints

def dispatch_request(self, method: str):
try:
return self.endpoints[method]()
except Exception as ex:
error_msg = f"error {str(ex)} thrown while executing {method} in {self.__class__.__name__}"
logger.error(error_msg)
return error_msg, 500


def make_request(self, action: str, values: Dict = {}, synchronous:bool=True) -> Response:
"""make a request to the zmanager
Args:
action (str): the action key to be sent
values (Dict, optional): the values to be sent in the reques. Defaults to {}.
synchronous (bool, optional): whether or not to wait for a response from the zmanager. Defaults to True.
Returns:
Response: the response coming from the the zmanager
"""
rest_api_service = ObjectFactory.get_instance("RestAPIService")

# request to get repeated messages
request: Request = ObjectFactory.get_new_instance("request")
request.set_action(action)
request.set_sender(rest_api_service.__class__.__name__.lower())
request.set_format("pickled")
request.set_values(values)
rest_api_service.subject_send_request(request, APPLICATION_PROTOCOL)
if synchronous:
response = rest_api_service.retrieve_response(request.get_id())
return response

0 comments on commit af3f6dc

Please sign in to comment.