unified return structure to use error_field instead of message key to correspond to API specification

This commit is contained in:
Daniel Schick 2024-09-16 14:17:30 +02:00
parent a68a768277
commit 5ce866936d
13 changed files with 64 additions and 63 deletions

View File

@ -22,15 +22,15 @@ def get_request_code(code_id):
class RequestStatusCode(ABC): class RequestStatusCode(ABC):
def __init__(self): def __init__(self):
return return
@abstractmethod @abstractmethod
def __call__(self, data): def __call__(self, data):
raise NotImplementedError("any default status code object must be callable") raise NotImplementedError("any default status code object must be callable")
@abstractmethod @abstractmethod
def status_code(self): def status_code(self):
raise NotImplementedError("any default status code object should return an integer") raise NotImplementedError("any default status code object should return an integer")
@abstractmethod @abstractmethod
def response(self, data): def response(self, data):
raise NotImplementedError("the response method should return a binary json object. typically, json.dumps is used") raise NotImplementedError("the response method should return a binary json object. typically, json.dumps is used")
@ -38,7 +38,7 @@ class RequestStatusCode(ABC):
def headers(self): def headers(self):
return {'Content-Type': 'application/json; charset=utf-8'} return {'Content-Type': 'application/json; charset=utf-8'}
class RequestCode_HTTP_200_OK(RequestStatusCode): class RequestCode_HTTP_200_OK(RequestStatusCode):
def __init__(self) -> None: def __init__(self) -> None:
@ -46,13 +46,13 @@ class RequestCode_HTTP_200_OK(RequestStatusCode):
def __call__(self, data): def __call__(self, data):
return (self.response(data), self.status_code(), self.headers()) return (self.response(data), self.status_code(), self.headers())
def status_code(self): def status_code(self):
return 200 return 200
def response(self, data): def response(self, data):
return json.dumps(data, default=obj_dict) return json.dumps(data, default=obj_dict)
class RequestCode_HTTP_201_CREATED(RequestStatusCode): class RequestCode_HTTP_201_CREATED(RequestStatusCode):
def __init__(self) -> None: def __init__(self) -> None:
@ -60,10 +60,10 @@ class RequestCode_HTTP_201_CREATED(RequestStatusCode):
def __call__(self, data): def __call__(self, data):
return (self.response(data), self.status_code(), self.headers()) return (self.response(data), self.status_code(), self.headers())
def status_code(self): def status_code(self):
return 201 return 201
def response(self, new_id): def response(self, new_id):
return json.dumps({"id":new_id}) return json.dumps({"id":new_id})
@ -74,10 +74,10 @@ class RequestCode_HTTP_400_BAD_REQUEST(RequestStatusCode):
def __call__(self, data): def __call__(self, data):
return (self.response(data), self.status_code(), self.headers()) return (self.response(data), self.status_code(), self.headers())
def status_code(self): def status_code(self):
return 400 return 400
def response(self, data): def response(self, data):
return json.dumps(data) return json.dumps(data)
@ -88,15 +88,15 @@ class RequestCode_HTTP_403_FORBIDDEN(RequestStatusCode):
def __call__(self, data): def __call__(self, data):
return (self.response(data), self.status_code(), self.headers()) return (self.response(data), self.status_code(), self.headers())
def status_code(self): def status_code(self):
return 403 return 403
def response(self, message="invalid credentials"): def response(self, message="invalid credentials"):
result = {} result = {}
result["message"] = message result["error_field"] = message
return json.dumps(result) return json.dumps(result)
class RequestCode_HTTP_404_NOT_FOUND(RequestStatusCode): class RequestCode_HTTP_404_NOT_FOUND(RequestStatusCode):
def __init__(self) -> None: def __init__(self) -> None:
@ -104,13 +104,13 @@ class RequestCode_HTTP_404_NOT_FOUND(RequestStatusCode):
def __call__(self, data): def __call__(self, data):
return (self.response(data), self.status_code(), self.headers()) return (self.response(data), self.status_code(), self.headers())
def status_code(self): def status_code(self):
return 404 return 404
def response(self, message="no such record"): def response(self, message="no such record"):
result = {} result = {}
result["message"] = message result["error_field"] = message
return json.dumps(result) return json.dumps(result)
@ -120,12 +120,12 @@ class RequestCode_HTTP_500_INTERNAL_SERVER_ERROR(RequestStatusCode):
def __call__(self, data): def __call__(self, data):
return (self.response(data), self.status_code(), self.headers()) return (self.response(data), self.status_code(), self.headers())
def status_code(self): def status_code(self):
return 500 return 500
def response(self, message="credential lookup mismatch"): def response(self, message="credential lookup mismatch"):
result = {} result = {}
result["message"] = message result["error_field"] = message
return json.dumps(result) return json.dumps(result)

View File

@ -13,7 +13,7 @@ def GetBerths(options):
try: try:
pooledConnection = local_db.getPoolConnection() pooledConnection = local_db.getPoolConnection()
commands = pydapper.using(pooledConnection) commands = pydapper.using(pooledConnection)
# only load berths to ports that the participant is assigned to # only load berths to ports that the participant is assigned to
query = ("SELECT id, name, `lock`, owner_id, port_id, authority_id, created, modified, deleted FROM berth WHERE " + query = ("SELECT id, name, `lock`, owner_id, port_id, authority_id, created, modified, deleted FROM berth WHERE " +
"deleted = 0 AND + " "deleted = 0 AND + "
@ -26,7 +26,7 @@ def GetBerths(options):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500 return json.dumps(result), 500
finally: finally:

View File

@ -35,7 +35,7 @@ def GetHistory(options):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps("call failed"), 500 return json.dumps("call failed"), 500
return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'}

View File

@ -21,7 +21,7 @@ def GetUser(options):
"api_key, notify_email, notify_whatsapp, notify_signal, notify_popup, created, modified FROM user " + "api_key, notify_email, notify_whatsapp, notify_signal, notify_popup, created, modified FROM user " +
"WHERE user_name = ?username? OR user_email = ?username?", "WHERE user_name = ?username? OR user_email = ?username?",
model=model.User, param={"username" : options["username"]}) model=model.User, param={"username" : options["username"]})
if len(data) == 1: if len(data) == 1:
if bcrypt.checkpw(options["password"].encode("utf-8"), bytes(data[0].password_hash, "utf-8")): if bcrypt.checkpw(options["password"].encode("utf-8"), bytes(data[0].password_hash, "utf-8")):
result = { result = {
@ -39,18 +39,19 @@ def GetUser(options):
if len(data) > 1: if len(data) > 1:
result = {} result = {}
result["message"] = "credential lookup mismatch" result["error_field"] = "credential lookup mismatch"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
result = {} result = {}
result["message"] = "invalid credentials" result["error_field"] = "invalid credentials"
return json.dumps(result), 403, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 403, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex: except Exception as ex:
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed: " + str(ex) result["error_field"] = "call failed"
result["error_description"] = str(ex)
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
finally: finally:

View File

@ -27,7 +27,7 @@ def GetNotifications(options):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'}

View File

@ -10,7 +10,7 @@ def GetParticipant(options):
""" """
:param options: A dictionary containing all the paramters for the Operations :param options: A dictionary containing all the paramters for the Operations
options["user_id"]: **Id of user**. *Example: 2*. User id returned by login call. options["user_id"]: **Id of user**. *Example: 2*. User id returned by login call.
""" """
try: try:
pooledConnection = local_db.getPoolConnection() pooledConnection = local_db.getPoolConnection()
@ -22,19 +22,19 @@ def GetParticipant(options):
"INNER JOIN user u WHERE u.participant_id = p.id and u.id = %d") % options["user_id"] "INNER JOIN user u WHERE u.participant_id = p.id and u.id = %d") % options["user_id"]
data = commands.query(query, model=model.Participant) data = commands.query(query, model=model.Participant)
else: else:
# query = SQLQuery.get_participants() # query = SQLQuery.get_participants()
# list only participants that are assigned to the same ports than participant of caller # list only participants that are assigned to the same ports than participant of caller
query = ("SELECT p.id as id, name, street, postal_code, city, type, flags, p.created, p.modified, p.deleted " + query = ("SELECT p.id as id, name, street, postal_code, city, type, flags, p.created, p.modified, p.deleted " +
"FROM participant p " + "FROM participant p " +
"JOIN participant_port_map ON p.id = participant_port_map.participant_id " + "JOIN participant_port_map ON p.id = participant_port_map.participant_id " +
"WHERE participant_port_map.port_id IN " + "WHERE participant_port_map.port_id IN " +
"(SELECT port_id FROM participant_port_map where participant_id = %d) " + "(SELECT port_id FROM participant_port_map where participant_id = %d) " +
"GROUP BY id " + "GROUP BY id " +
"ORDER BY p.name") % options["participant_id"] "ORDER BY p.name") % options["participant_id"]
data = commands.query(query, model=model.Participant) data = commands.query(query, model=model.Participant)
for participant in data: for participant in data:
port_query = "SELECT port_id FROM participant_port_map WHERE participant_id=?id?"; port_query = "SELECT port_id FROM participant_port_map WHERE participant_id=?id?";
for record in commands.query(port_query, model=model.Port_Assignment, param={"id" : participant.id}, buffered=False): for record in commands.query(port_query, model=model.Port_Assignment, param={"id" : participant.id}, buffered=False):
pa = model.Port_Assignment(record.port_id) pa = model.Port_Assignment(record.port_id)
@ -46,7 +46,7 @@ def GetParticipant(options):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps("call failed"), 500 return json.dumps("call failed"), 500
finally: finally:

View File

@ -21,7 +21,7 @@ def GetPorts(token):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500 return json.dumps(result), 500
finally: finally:

View File

@ -42,7 +42,7 @@ def GetShipcalls(options):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
finally: finally:
@ -52,8 +52,8 @@ def GetShipcalls(options):
def PostShipcalls(schemaModel): def PostShipcalls(schemaModel):
""" """
This function *executes* a post-request for shipcalls. The function is accessible as part of an API route. This function *executes* a post-request for shipcalls. The function is accessible as part of an API route.
The common sequence is: The common sequence is:
a) issue a request to the Flask API a) issue a request to the Flask API
b) BreCal.api.shipcalls.PostShipcalls, to verify the incoming request (which includes an authentification guard) b) BreCal.api.shipcalls.PostShipcalls, to verify the incoming request (which includes an authentification guard)
@ -62,8 +62,8 @@ def PostShipcalls(schemaModel):
:param schemaModel: The deserialized dict of the request :param schemaModel: The deserialized dict of the request
e.g., e.g.,
{ {
'ship_id': 1, 'type': 1, 'eta': datetime.datetime(2023, 7, 23, 7, 18, 19), 'ship_id': 1, 'type': 1, 'eta': datetime.datetime(2023, 7, 23, 7, 18, 19),
'voyage': '43B', 'tug_required': False, 'pilot_required': True, 'flags': 0, 'voyage': '43B', 'tug_required': False, 'pilot_required': True, 'flags': 0,
'pier_side': False, 'bunkering': True, 'recommended_tugs': 2, 'type_value': 1, 'evaluation_value': 0} 'pier_side': False, 'bunkering': True, 'recommended_tugs': 2, 'type_value': 1, 'evaluation_value': 0}
} }
""" """
@ -89,9 +89,9 @@ def PostShipcalls(schemaModel):
if key == "evaluation": if key == "evaluation":
continue continue
if key == "evaluation_message": if key == "evaluation_message":
continue continue
if key == "type_value": if key == "type_value":
continue continue
if key == "evaluation_value": if key == "evaluation_value":
continue continue
if isNotFirst: if isNotFirst:
@ -151,7 +151,7 @@ def PostShipcalls(schemaModel):
commands.execute(query, {"scid" : new_id, "pid" : user_data["participant_id"], "uid" : user_data["id"]}) commands.execute(query, {"scid" : new_id, "pid" : user_data["participant_id"], "uid" : user_data["id"]})
return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps({"id" : new_id}), 201, {'Content-Type': 'application/json; charset=utf-8'}
except ValidationError as ex: except ValidationError as ex:
return create_validation_error_response(ex, status_code=400, create_log=True) return create_validation_error_response(ex, status_code=400, create_log=True)
@ -160,7 +160,7 @@ def PostShipcalls(schemaModel):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
finally: finally:
@ -262,7 +262,7 @@ def PutShipcalls(schemaModel):
commands.execute(query, {"scid" : schemaModel["id"], "pid" : user_data["participant_id"], "uid" : user_data["id"]}) commands.execute(query, {"scid" : schemaModel["id"], "pid" : user_data["participant_id"], "uid" : user_data["id"]})
return json.dumps({"id" : schemaModel["id"]}), 200 return json.dumps({"id" : schemaModel["id"]}), 200
except ValidationError as ex: except ValidationError as ex:
return create_validation_error_response(ex, status_code=400, create_log=True) return create_validation_error_response(ex, status_code=400, create_log=True)
@ -271,7 +271,7 @@ def PutShipcalls(schemaModel):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
finally: finally:

View File

@ -25,7 +25,7 @@ def GetShips(token):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
finally: finally:
@ -91,7 +91,7 @@ def PostShip(schemaModel):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
@ -133,7 +133,7 @@ def PutShip(schemaModel):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
@ -157,12 +157,12 @@ def DeleteShip(options):
return json.dumps({"id" : options["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps({"id" : options["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'}
result = {} result = {}
result["message"] = "no such record" result["error_field"] = "no such record"
return json.dumps(result), 404, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 404, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex: except Exception as ex:
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}

View File

@ -35,7 +35,7 @@ def GetTimes(options):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(data, default=model.obj_dict), 200, {'Content-Type': 'application/json; charset=utf-8'}
@ -104,7 +104,7 @@ def PostTimes(schemaModel):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
finally: finally:
@ -164,7 +164,7 @@ def PutTimes(schemaModel):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
finally: finally:
@ -195,14 +195,14 @@ def DeleteTimes(options):
return json.dumps({"id" : options["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps({"id" : options["id"]}), 200, {'Content-Type': 'application/json; charset=utf-8'}
result = {} result = {}
result["message"] = "no such record" result["error_field"] = "no such record"
return json.dumps(result), 404, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 404, {'Content-Type': 'application/json; charset=utf-8'}
except Exception as ex: except Exception as ex:
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
finally: finally:

View File

@ -63,7 +63,7 @@ def PutUser(schemaModel):
commands.execute(query, param={"password_hash" : password_hash, "id" : schemaModel["id"]}) commands.execute(query, param={"password_hash" : password_hash, "id" : schemaModel["id"]})
else: else:
result = {} result = {}
result["message"] = "old password invalid" result["error_field"] = "old password invalid"
return json.dumps(result), 400, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 400, {'Content-Type': 'application/json; charset=utf-8'}
return json.dumps({"id" : schemaModel["id"]}), 200 return json.dumps({"id" : schemaModel["id"]}), 200
@ -72,7 +72,7 @@ def PutUser(schemaModel):
logging.error(ex) logging.error(ex)
print(ex) print(ex)
result = {} result = {}
result["message"] = "call failed" result["error_field"] = "call failed"
return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'} return json.dumps(result), 500, {'Content-Type': 'application/json; charset=utf-8'}
finally: finally:

View File

@ -25,9 +25,9 @@ def auth_guard(role=None):
try: try:
user_data = check_jwt() user_data = check_jwt()
except Exception as e: except Exception as e:
return json.dumps({"message" : f'{e}', "status": 401}), 401 return json.dumps({"error_field" : f'{e}', "status": 401}), 401
if role and role not in user_data['roles']: if role and role not in user_data['roles']:
return json.dumps({"message": 'Authorization required.', "status" : 403}), 403 return json.dumps({"error_field": 'Authorization required.', "status" : 403}), 403
# get on to original route # get on to original route
return route_function(*args, **kwargs) return route_function(*args, **kwargs)
decorated_function.__name__ = route_function.__name__ decorated_function.__name__ = route_function.__name__

View File

@ -77,7 +77,7 @@ def create_werkzeug_error_response(ex:Forbidden, status_code:int=403, create_log
def create_dynamic_exception_response(ex, status_code:int=400, message:typing.Optional[str]=None, create_log:bool=True): def create_dynamic_exception_response(ex, status_code:int=400, message:typing.Optional[str]=None, create_log:bool=True):
message = repr(ex) if message is None else message message = repr(ex) if message is None else message
json_response = create_default_json_response_format(error_field="Exception", error_description=message) json_response = create_default_json_response_format(error_field="Exception", error_description=message)
json_response["message"] = "call failed" json_response["error_field"] = "call failed"
serialized_response = json.dumps(json_response, default=str) serialized_response = json.dumps(json_response, default=str)