Compare commits

..

9 Commits

Author SHA1 Message Date
ee2c859a9e Merge pull request '# Improve exception reporting for API' (#515) from improve_exception_reporting into master
Reviewed-on: #515
2025-12-19 14:17:37 +01:00
328f672ec0 # Improve exception reporting for API
* fixes typo in exception_reporter.py
* properly catches error on geometry cast into multipolygon if input are no valid polygons
* extends error response on malicious api calls
* specifies different exceptions on try-catch while initializing api data
2025-12-19 14:17:15 +01:00
047c9489fe Merge pull request '# ExceptionReporter adjustment' (#513) from improve_exception_reporting into master
Reviewed-on: #513
2025-12-17 14:03:23 +01:00
38b81996ed # ExceptionReporter adjustment
* extends the KonovaExceptionReporter to hold POST body content (practical for debugging broken content on API)
2025-12-17 14:02:08 +01:00
4c4d64cc3d Merge pull request '# HOTFIX: empty geometry save' (#510) from hotfix_empty_geometry_save into master
Reviewed-on: #510
2025-12-03 13:49:55 +01:00
fbde03caec # Optimization
* optimizes logic in case of empty geometry by dropping redundant pre-check on emptiness
2025-12-03 13:48:58 +01:00
43eb598d3f # HOTFIX: empty geometry save
* fixes a bug where the saving of an empty geometry could lead into a json decode error
2025-12-03 13:38:13 +01:00
b7fac0ae03 Merge pull request '# Fix fpr #507' (#508) from 507_Improper_deduction-recording_rendering_on_unrecorded_eco_account into master
Reviewed-on: #508
2025-11-30 12:33:39 +01:00
447ba942b5 # Fix fpr #507
* fixes incorrect rendering of recording-info for deductions on unrecorded eco accounts
2025-11-30 12:32:05 +01:00
7 changed files with 48 additions and 22 deletions

View File

@@ -71,7 +71,7 @@ class APIV1CreateTestCase(BaseAPIV1TestCase):
# Expect this first request to fail, since user has no shared access on the intervention, we want to create # Expect this first request to fail, since user has no shared access on the intervention, we want to create
# a compensation for # a compensation for
response = self._run_create_request(url, post_body) response = self._run_create_request(url, post_body)
self.assertEqual(response.status_code, 500, msg=response.content) self.assertEqual(response.status_code, 400, msg=response.content)
content = json.loads(response.content) content = json.loads(response.content)
self.assertGreater(len(content.get("errors", [])), 0, msg=response.content) self.assertGreater(len(content.get("errors", [])), 0, msg=response.content)

View File

@@ -6,7 +6,9 @@ Created on: 21.01.22
""" """
import json import json
from json import JSONDecodeError
from django.core.exceptions import ObjectDoesNotExist
from django.http import JsonResponse, HttpRequest from django.http import JsonResponse, HttpRequest
from api.utils.serializer.v1.compensation import CompensationAPISerializerV1 from api.utils.serializer.v1.compensation import CompensationAPISerializerV1
@@ -66,8 +68,12 @@ class AbstractAPIViewV1(AbstractAPIView):
body = request.body.decode("utf-8") body = request.body.decode("utf-8")
body = json.loads(body) body = json.loads(body)
created_id = self.serializer.create_model_from_json(body, self.user) created_id = self.serializer.create_model_from_json(body, self.user)
except Exception as e: except (JSONDecodeError,
return self._return_error_response(e, 500) AssertionError,
ValueError,
PermissionError,
ObjectDoesNotExist) as e:
return self._return_error_response(e, 400)
return JsonResponse({"id": created_id}) return JsonResponse({"id": created_id})
def put(self, request: HttpRequest, id=None): def put(self, request: HttpRequest, id=None):

View File

@@ -81,9 +81,7 @@ class AbstractAPIView(View):
Returns: Returns:
""" """
content = [error.__str__()] content = [f"{error.__class__.__name__}: {str(error)}"]
if hasattr(error, "messages"):
content = error.messages
return JsonResponse( return JsonResponse(
{ {
"errors": content "errors": content

View File

@@ -53,7 +53,7 @@
</td> </td>
<td class="align-middle"> <td class="align-middle">
{% if deduction.intervention.recorded %} {% if deduction.intervention.recorded %}
<em title="{% trans 'Recorded on' %} {{obj.recorded.timestamp}} {% trans 'by' %} {{obj.recorded.user}}" class='fas fa-bookmark registered-bookmark'></em> <em title="{% trans 'Recorded on' %} {{deduction.intervention.recorded.timestamp}} {% trans 'by' %} {{deduction.intervention.recorded.user}}" class='fas fa-bookmark registered-bookmark'></em>
{% else %} {% else %}
<em title="{% trans 'Not recorded yet' %}" class='far fa-bookmark'></em> <em title="{% trans 'Not recorded yet' %}" class='far fa-bookmark'></em>
{% endif %} {% endif %}

View File

@@ -54,7 +54,7 @@ class SimpleGeomForm(BaseForm):
geom = json.dumps(geojson) geom = json.dumps(geojson)
except AttributeError: except AttributeError:
# If no geometry exists for this form, we simply set the value to None and zoom to the maximum level # If no geometry exists for this form, we simply set the value to None and zoom to the maximum level
geom = "" geom = json.dumps({})
self.empty = True self.empty = True
self.initialize_form_field("output", geom) self.initialize_form_field("output", geom)
@@ -72,20 +72,12 @@ class SimpleGeomForm(BaseForm):
self.initialize_form_field("output", json.dumps(submitted_data)) self.initialize_form_field("output", json.dumps(submitted_data))
# Get geojson from form for validity checking # Get geojson from form for validity checking
geom = self.data.get("output", None) geom = self.data.get("output", json.dumps({}))
geom_is_empty = geom is None or len(geom) == 0 geom = json.loads(geom)
if geom_is_empty:
# If no geometry has been submitted, we create an empty geometry object since
# an empty geometry is a valid geometry
self.cleaned_data["output"] = MultiPolygon(srid=DEFAULT_SRID_RLP).ewkt
return is_valid
# Initialize features list with empty MultiPolygon, so that an empty input will result in a # Initialize features list with empty MultiPolygon, so that an empty input will result in a
# proper empty MultiPolygon object # proper empty MultiPolygon object
features = [] features = []
# Prepare geometry for validity checks (create iterable dict)
geom = json.loads(geom)
features_json = geom.get("features", []) features_json = geom.get("features", [])
accepted_ogr_types = [ accepted_ogr_types = [
"Polygon", "Polygon",
@@ -141,6 +133,7 @@ class SimpleGeomForm(BaseForm):
if features: if features:
form_geom = MultiPolygon(*features, srid=DEFAULT_SRID_RLP).unary_union form_geom = MultiPolygon(*features, srid=DEFAULT_SRID_RLP).unary_union
else: else:
# If no features have been processed, this indicates an empty geometry - so we store an empty geometry
form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP) form_geom = MultiPolygon(srid=DEFAULT_SRID_RLP)
# Make sure to convert into a MultiPolygon. Relevant if a single Polygon is provided. # Make sure to convert into a MultiPolygon. Relevant if a single Polygon is provided.

View File

@@ -407,7 +407,10 @@ class Geometry(BaseResource):
""" """
output_geom = input_geom output_geom = input_geom
if not isinstance(input_geom, MultiPolygon): if not isinstance(input_geom, MultiPolygon):
output_geom = MultiPolygon(input_geom, srid=DEFAULT_SRID_RLP) try:
output_geom = MultiPolygon(input_geom, srid=DEFAULT_SRID_RLP)
except TypeError as e:
raise AssertionError(f"Only (Multi)Polygon allowed! Could not convert {input_geom.geom_type} to MultiPolygon")
return output_geom return output_geom
@staticmethod @staticmethod

View File

@@ -5,6 +5,9 @@ Contact: ksp-servicestelle@sgdnord.rlp.de
Created on: 11.12.23 Created on: 11.12.23
""" """
import json
from json import JSONDecodeError
from django.views.debug import ExceptionReporter from django.views.debug import ExceptionReporter
@@ -30,7 +33,7 @@ class KonovaExceptionReporter(ExceptionReporter):
""" """
whitelist = [ whitelist = [
"is_email", "is_email",
"unicdoe_hint", "unicode_hint",
"frames", "frames",
"request", "request",
"user_str", "user_str",
@@ -39,6 +42,8 @@ class KonovaExceptionReporter(ExceptionReporter):
"raising_view_name", "raising_view_name",
"exception_type", "exception_type",
"exception_value", "exception_value",
"filtered_GET_items",
"filtered_POST_items",
] ]
clean_data = dict() clean_data = dict()
for entry in whitelist: for entry in whitelist:
@@ -56,7 +61,28 @@ class KonovaExceptionReporter(ExceptionReporter):
""" """
tb_data = super().get_traceback_data() tb_data = super().get_traceback_data()
return_data = tb_data
if self.is_email: if self.is_email:
tb_data = self._filter_traceback_data(tb_data) filtered_data = dict()
filtered_data.update(self._filter_traceback_data(tb_data))
filtered_data.update(self._filter_POST_body(tb_data))
return_data = filtered_data
return return_data
return tb_data def _filter_POST_body(self, tb_data: dict):
""" Filters POST body from traceback data
"""
post_data = tb_data.get("request", None)
if post_data:
post_data = post_data.body
try:
post_data = json.loads(post_data)
except JSONDecodeError:
pass
post_data = {
"filtered_POST_items": [
("body", post_data),
]
}
return post_data