Allow new shipcalls up to 1 day in the past for serverside validation

This commit is contained in:
Daniel Schick 2024-11-06 16:22:34 +01:00
parent b5a8a3d31c
commit d6e3ae20c1
3 changed files with 77 additions and 76 deletions

View File

@ -74,7 +74,7 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
# existance checks in content # existance checks in content
# datetime checks in loadedModel (datetime.datetime objects). Dates should be in the future. # datetime checks in loadedModel (datetime.datetime objects). Dates should be in the future.
time_now = datetime.datetime.now() time_now = datetime.datetime.now() - datetime.timedelta(days=1)
type_ = loadedModel.get("type", int(ShipcallType.undefined)) type_ = loadedModel.get("type", int(ShipcallType.undefined))
if int(type_)==int(ShipcallType.undefined): if int(type_)==int(ShipcallType.undefined):
raise ValidationError({"type":f"providing 'type' is mandatory. Missing key!"}) raise ValidationError({"type":f"providing 'type' is mandatory. Missing key!"})
@ -85,7 +85,7 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
if content.get("arrival_berth_id", None) is None: if content.get("arrival_berth_id", None) is None:
raise ValidationError({"arrival_berth_id":f"providing 'arrival_berth_id' is mandatory. Missing key!"}) raise ValidationError({"arrival_berth_id":f"providing 'arrival_berth_id' is mandatory. Missing key!"})
if not eta >= time_now: if not eta >= time_now:
raise ValidationError({"eta":f"'eta' must be in the future. Incorrect datetime provided."}) raise ValidationError({"eta":f"'eta' is too far in the past. Incorrect datetime provided."})
elif int(type_)==int(ShipcallType.departure): elif int(type_)==int(ShipcallType.departure):
etd = loadedModel.get("etd") etd = loadedModel.get("etd")
if (content.get("etd", None) is None): if (content.get("etd", None) is None):
@ -93,7 +93,7 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
if content.get("departure_berth_id", None) is None: if content.get("departure_berth_id", None) is None:
raise ValidationError({"departure_berth_id":f"providing 'departure_berth_id' is mandatory. Missing key!"}) raise ValidationError({"departure_berth_id":f"providing 'departure_berth_id' is mandatory. Missing key!"})
if not etd >= time_now: if not etd >= time_now:
raise ValidationError({"etd":f"'etd' must be in the future. Incorrect datetime provided."}) raise ValidationError({"etd":f"'etd' is too far in the past. Incorrect datetime provided."})
elif int(type_)==int(ShipcallType.shifting): elif int(type_)==int(ShipcallType.shifting):
eta = loadedModel.get("eta") eta = loadedModel.get("eta")
etd = loadedModel.get("etd") etd = loadedModel.get("etd")
@ -103,17 +103,17 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
if (content.get("arrival_berth_id", None) is None) or (content.get("departure_berth_id", None) is None): if (content.get("arrival_berth_id", None) is None) or (content.get("departure_berth_id", None) is None):
raise ValidationError({"arrival_berth_id_or_departure_berth_id":f"providing 'arrival_berth_id' & 'departure_berth_id' is mandatory. Missing key!"}) raise ValidationError({"arrival_berth_id_or_departure_berth_id":f"providing 'arrival_berth_id' & 'departure_berth_id' is mandatory. Missing key!"})
if (not eta >= time_now) or (not etd >= time_now) or (not eta >= etd): if (not eta >= time_now) or (not etd >= time_now) or (not eta >= etd):
raise ValidationError({"eta_or_etd":f"'eta' and 'etd' must be in the future. Incorrect datetime provided."}) raise ValidationError({"eta_or_etd":f"'eta' and 'etd' are too far in the past. Incorrect datetime provided."})
tidal_window_from = loadedModel.get("tidal_window_from", None) tidal_window_from = loadedModel.get("tidal_window_from", None)
tidal_window_to = loadedModel.get("tidal_window_to", None) tidal_window_to = loadedModel.get("tidal_window_to", None)
if tidal_window_to is not None: if tidal_window_to is not None:
if not tidal_window_to >= time_now: if not tidal_window_to >= time_now:
raise ValidationError({"tidal_window_to":f"'tidal_window_to' must be in the future. Incorrect datetime provided."}) raise ValidationError({"tidal_window_to":f"'tidal_window_to' is too far in the past. Incorrect datetime provided."})
if tidal_window_from is not None: if tidal_window_from is not None:
if not tidal_window_from >= time_now: if not tidal_window_from >= time_now:
raise ValidationError({"tidal_window_from":f"'tidal_window_from' must be in the future. Incorrect datetime provided."}) raise ValidationError({"tidal_window_from":f"'tidal_window_from' is too far in the past. Incorrect datetime provided."})
# #TODO: assert tidal_window_from > tidal_window_to # #TODO: assert tidal_window_from > tidal_window_to

View File

@ -335,8 +335,8 @@ class InputValidationShipcall():
Dates should be in the future. Depending on the ShipcallType, specific values should be checked Dates should be in the future. Depending on the ShipcallType, specific values should be checked
Perfornms datetime checks in the loadedModel (datetime.datetime objects). Perfornms datetime checks in the loadedModel (datetime.datetime objects).
""" """
# obtain the current datetime to check, whether the provided values are in the future # obtain the current datetime to check, whether the provided values are after ref time
time_now = datetime.datetime.now() time_ref = datetime.datetime.now() - datetime.timedelta(days=1)
type_ = loadedModel.get("type", ShipcallType.undefined.name) type_ = loadedModel.get("type", ShipcallType.undefined.name)
if isinstance(type_, str): # convert the name string to a ShipcallType data model if isinstance(type_, str): # convert the name string to a ShipcallType data model
@ -354,14 +354,14 @@ class InputValidationShipcall():
tidal_window_to = loadedModel.get("tidal_window_to", None) tidal_window_to = loadedModel.get("tidal_window_to", None)
# Estimated arrival or departure times # Estimated arrival or departure times
InputValidationShipcall.check_times_in_future_based_on_type(type_, time_now, eta, etd) InputValidationShipcall.check_times_in_future_based_on_type(type_, time_ref, eta, etd)
# Tidal Window # Tidal Window
InputValidationShipcall.check_tidal_window_in_future(type_, time_now, tidal_window_from, tidal_window_to) InputValidationShipcall.check_tidal_window_in_future(type_, time_ref, tidal_window_from, tidal_window_to)
return return
@staticmethod @staticmethod
def check_times_in_future_based_on_type(type_, time_now, eta, etd): def check_times_in_future_based_on_type(type_, time_ref, eta, etd):
""" """
checks, whether the ETA & ETD times are in the future. checks, whether the ETA & ETD times are in the future.
based on the type, this function checks: based on the type, this function checks:
@ -387,8 +387,8 @@ class InputValidationShipcall():
if eta is None: # null values -> no violation if eta is None: # null values -> no violation
return return
if not eta > time_now: if not eta > time_ref:
raise ValidationError({"eta":f"'eta' must be in the future. Incorrect datetime provided. Current Time: {time_now}. ETA: {eta}."}) raise ValidationError({"eta":f"'eta' is too far in the past. Incorrect datetime provided. Current Time: {time_ref}. ETA: {eta}."})
if etd is not None: if etd is not None:
raise ValidationError({"etd":f"'etd' should not be set when the shipcall type is 'arrival'."}) raise ValidationError({"etd":f"'etd' should not be set when the shipcall type is 'arrival'."})
if eta > time_in_a_year: if eta > time_in_a_year:
@ -398,8 +398,9 @@ class InputValidationShipcall():
if etd is None: # null values -> no violation if etd is None: # null values -> no violation
return return
if not etd > time_now: if not etd > time_ref:
raise ValidationError({"etd":f"'etd' must be in the future. Incorrect datetime provided. Current Time: {time_now}. ETD: {etd}."}) raise ValidationError({"etd":f"'etd' is too far in the past. Incorrect datetime provided. Current Time: {time_ref}. ETD: {etd}."})
if eta is not None: if eta is not None:
raise ValidationError({"eta":f"'eta' should not be set when the shipcall type is 'departure'."}) raise ValidationError({"eta":f"'eta' should not be set when the shipcall type is 'departure'."})
if etd > time_in_a_year: if etd > time_in_a_year:
@ -414,8 +415,8 @@ class InputValidationShipcall():
# rules, a user is only allowed to provide *both* values. # rules, a user is only allowed to provide *both* values.
raise ValidationError({"eta_or_etd":f"For shifting shipcalls one should always provide, both, eta and etd."}) raise ValidationError({"eta_or_etd":f"For shifting shipcalls one should always provide, both, eta and etd."})
if (not eta > time_now) or (not etd > time_now): if (not eta > time_ref) or (not etd > time_ref):
raise ValidationError({"eta_or_etd":f"'eta' and 'etd' must be in the future. Incorrect datetime provided. Current Time: {time_now}. ETA: {eta}. ETD: {etd}"}) raise ValidationError({"eta_or_etd":f"'eta' and 'etd' is too far in the past. Incorrect datetime provided. Current Time: {time_ref}. ETA: {eta}. ETD: {etd}"})
if (not etd < eta): if (not etd < eta):
raise ValidationError({"eta_or_etd":f"The estimated time of departure ('etd') must take place *before the estimated time of arrival ('eta'). The ship cannot arrive, before it has departed. Found: ETD: {etd}, ETA: {eta}"}) raise ValidationError({"eta_or_etd":f"The estimated time of departure ('etd') must take place *before the estimated time of arrival ('eta'). The ship cannot arrive, before it has departed. Found: ETD: {etd}, ETA: {eta}"})

View File

@ -226,17 +226,17 @@ def test_shipcall_post_request_fails_when_type_arrival_and_not_in_future(get_stu
# accept # accept
post_data = original_post_data.copy() post_data = original_post_data.copy()
post_data["type"] = ShipcallType.arrival.name post_data["type"] = ShipcallType.arrival.name
post_data["eta"] = (datetime.datetime.now() + datetime.timedelta(hours=3)).isoformat() post_data["eta"] = (datetime.datetime.now() + datetime.timedelta(days=2)).isoformat()
response = requests.post(f"{url}/shipcalls", headers={"Content-Type":"text", "Authorization":f"Bearer {token}"}, json=post_data) response = requests.post(f"{url}/shipcalls", headers={"Content-Type":"text", "Authorization":f"Bearer {token}"}, json=post_data)
assert response.status_code == 201 assert response.status_code == 201
# error # error
post_data = original_post_data.copy() post_data = original_post_data.copy()
post_data["type"] = ShipcallType.arrival.name post_data["type"] = ShipcallType.arrival.name
post_data["eta"] = (datetime.datetime.now() - datetime.timedelta(hours=3)).isoformat() post_data["eta"] = (datetime.datetime.now() - datetime.timedelta(days=2)).isoformat()
response = requests.post(f"{url}/shipcalls", headers={"Content-Type":"text", "Authorization":f"Bearer {token}"}, json=post_data) response = requests.post(f"{url}/shipcalls", headers={"Content-Type":"text", "Authorization":f"Bearer {token}"}, json=post_data)
with pytest.raises(ValidationError, match="must be in the future. Incorrect datetime provided"): with pytest.raises(ValidationError, match="is too far in the past. Incorrect datetime provided"):
assert response.status_code==400 assert response.status_code==400
raise ValidationError(response.json()) raise ValidationError(response.json())
return return
@ -256,10 +256,10 @@ def test_shipcall_post_request_fails_when_type_departure_and_not_in_future(get_s
# error # error
post_data = original_post_data.copy() post_data = original_post_data.copy()
post_data["type"] = ShipcallType.departure.name post_data["type"] = ShipcallType.departure.name
post_data["etd"] = (datetime.datetime.now() - datetime.timedelta(hours=3)).isoformat() post_data["etd"] = (datetime.datetime.now() - datetime.timedelta(days=3)).isoformat()
response = requests.post(f"{url}/shipcalls", headers={"Content-Type":"text", "Authorization":f"Bearer {token}"}, json=post_data) response = requests.post(f"{url}/shipcalls", headers={"Content-Type":"text", "Authorization":f"Bearer {token}"}, json=post_data)
with pytest.raises(ValidationError, match="must be in the future. Incorrect datetime provided"): with pytest.raises(ValidationError, match="is too far in the past. Incorrect datetime provided"):
assert response.status_code==400 assert response.status_code==400
raise ValidationError(response.json()) raise ValidationError(response.json())
return return
@ -280,11 +280,11 @@ def test_shipcall_post_request_fails_when_type_shifting_and_not_in_future(get_st
# error # error
post_data = original_post_data.copy() post_data = original_post_data.copy()
post_data["type"] = ShipcallType.shifting.name post_data["type"] = ShipcallType.shifting.name
post_data["etd"] = (datetime.datetime.now() - datetime.timedelta(hours=3)).isoformat() post_data["etd"] = (datetime.datetime.now() - datetime.timedelta(days=3)).isoformat()
post_data["eta"] = (datetime.datetime.now() + datetime.timedelta(hours=3,minutes=1)).isoformat() post_data["eta"] = (datetime.datetime.now() + datetime.timedelta(hours=3,minutes=1)).isoformat()
response = requests.post(f"{url}/shipcalls", headers={"Content-Type":"text", "Authorization":f"Bearer {token}"}, json=post_data) response = requests.post(f"{url}/shipcalls", headers={"Content-Type":"text", "Authorization":f"Bearer {token}"}, json=post_data)
with pytest.raises(ValidationError, match="must be in the future. Incorrect datetime provided"): with pytest.raises(ValidationError, match="is too far in the past. Incorrect datetime provided"):
assert response.status_code==400 assert response.status_code==400
raise ValidationError(response.json()) raise ValidationError(response.json())
return return