Allow new shipcalls up to 1 day in the past for serverside validation
This commit is contained in:
parent
f1e1355986
commit
529872b590
@ -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
|
||||||
|
|
||||||
|
|||||||
@ -327,10 +327,10 @@ class InputValidationShipcall():
|
|||||||
def check_times_are_in_future(loadedModel:dict, content:dict):
|
def check_times_are_in_future(loadedModel:dict, content:dict):
|
||||||
"""
|
"""
|
||||||
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).
|
Performs 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
|
||||||
@ -348,14 +348,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:
|
||||||
@ -366,7 +366,7 @@ class InputValidationShipcall():
|
|||||||
if (eta is None) and (etd is None):
|
if (eta is None) and (etd is None):
|
||||||
return
|
return
|
||||||
|
|
||||||
time_in_a_year = time_now.replace(time_now.year + 1)
|
time_in_a_year = datetime.datetime.now().replace(datetime.datetime.now().year + 1)
|
||||||
|
|
||||||
if type_ is None:
|
if type_ is None:
|
||||||
raise ValidationError({"type":f"when providing 'eta' or 'etd', one must provide the type of the shipcall, so the datetimes can be verified."})
|
raise ValidationError({"type":f"when providing 'eta' or 'etd', one must provide the type of the shipcall, so the datetimes can be verified."})
|
||||||
@ -381,8 +381,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:
|
||||||
@ -392,8 +392,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:
|
||||||
@ -408,8 +409,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}"})
|
||||||
|
|
||||||
@ -423,19 +424,18 @@ class InputValidationShipcall():
|
|||||||
return
|
return
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def check_tidal_window_in_future(type_, time_now, tidal_window_from, tidal_window_to):
|
def check_tidal_window_in_future(type_, time_ref, tidal_window_from, tidal_window_to):
|
||||||
|
|
||||||
time_in_a_year = time_now.replace(time_now.year + 1)
|
|
||||||
|
|
||||||
|
time_in_a_year = datetime.datetime.now().replace(datetime.datetime.now().year + 1)
|
||||||
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_ref:
|
||||||
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_to > time_in_a_year:
|
if tidal_window_to > time_in_a_year:
|
||||||
raise ValidationError({"tidal_window_to":f"'tidal_window_to' is more than a year in the future. Found: {tidal_window_to}."})
|
raise ValidationError({"tidal_window_to":f"'tidal_window_to' is more than a year in the future. Found: {tidal_window_to}."})
|
||||||
|
|
||||||
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_ref:
|
||||||
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."})
|
||||||
if tidal_window_from > time_in_a_year:
|
if tidal_window_from > time_in_a_year:
|
||||||
raise ValidationError({"tidal_window_from":f"'tidal_window_from' is more than a year in the future. Found: {tidal_window_from}."})
|
raise ValidationError({"tidal_window_from":f"'tidal_window_from' is more than a year in the future. Found: {tidal_window_from}."})
|
||||||
|
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user