diff --git a/apps/api/plane/app/views/analytic/base.py b/apps/api/plane/app/views/analytic/base.py index 2f3f8b5737d..a05712c4ecf 100644 --- a/apps/api/plane/app/views/analytic/base.py +++ b/apps/api/plane/app/views/analytic/base.py @@ -29,7 +29,7 @@ Module, ) -from plane.utils.analytics_plot import build_graph_plot +from plane.utils.analytics_plot import build_graph_plot, VALID_ANALYTICS_FIELDS, VALID_YAXIS from plane.utils.issue_filters import issue_filters from plane.app.permissions import allow_permission, ROLE @@ -41,32 +41,15 @@ def get(self, request, slug): y_axis = request.GET.get("y_axis", False) segment = request.GET.get("segment", False) - valid_xaxis_segment = [ - "state_id", - "state__group", - "labels__id", - "assignees__id", - "estimate_point__value", - "issue_cycle__cycle_id", - "issue_module__module_id", - "priority", - "start_date", - "target_date", - "created_at", - "completed_at", - ] - - valid_yaxis = ["issue_count", "estimate"] - # Check for x-axis and y-axis as thery are required parameters - if not x_axis or not y_axis or x_axis not in valid_xaxis_segment or y_axis not in valid_yaxis: + if not x_axis or not y_axis or x_axis not in VALID_ANALYTICS_FIELDS or y_axis not in VALID_YAXIS: return Response( {"error": "x-axis and y-axis dimensions are required and the values should be valid"}, status=status.HTTP_400_BAD_REQUEST, ) # If segment is present it cannot be same as x-axis - if segment and (segment not in valid_xaxis_segment or x_axis == segment): + if segment and (segment not in VALID_ANALYTICS_FIELDS or x_axis == segment): return Response( {"error": "Both segment and x axis cannot be same and segment should be valid"}, status=status.HTTP_400_BAD_REQUEST, @@ -214,13 +197,20 @@ def get(self, request, slug, analytic_id): x_axis = analytic_view.query_dict.get("x_axis", False) y_axis = analytic_view.query_dict.get("y_axis", False) - if not x_axis or not y_axis: + if not x_axis or not y_axis or x_axis not in VALID_ANALYTICS_FIELDS or y_axis not in VALID_YAXIS: return Response( - {"error": "x-axis and y-axis dimensions are required"}, + {"error": "x-axis and y-axis dimensions are required and the values should be valid"}, status=status.HTTP_400_BAD_REQUEST, ) segment = request.GET.get("segment", False) + + if segment and (segment not in VALID_ANALYTICS_FIELDS or x_axis == segment): + return Response( + {"error": "Both segment and x axis cannot be same and segment should be valid"}, + status=status.HTTP_400_BAD_REQUEST, + ) + distribution = build_graph_plot(queryset=queryset, x_axis=x_axis, y_axis=y_axis, segment=segment) total_issues = queryset.count() return Response( @@ -236,32 +226,15 @@ def post(self, request, slug): y_axis = request.data.get("y_axis", False) segment = request.data.get("segment", False) - valid_xaxis_segment = [ - "state_id", - "state__group", - "labels__id", - "assignees__id", - "estimate_point", - "issue_cycle__cycle_id", - "issue_module__module_id", - "priority", - "start_date", - "target_date", - "created_at", - "completed_at", - ] - - valid_yaxis = ["issue_count", "estimate"] - # Check for x-axis and y-axis as thery are required parameters - if not x_axis or not y_axis or x_axis not in valid_xaxis_segment or y_axis not in valid_yaxis: + if not x_axis or not y_axis or x_axis not in VALID_ANALYTICS_FIELDS or y_axis not in VALID_YAXIS: return Response( {"error": "x-axis and y-axis dimensions are required and the values should be valid"}, status=status.HTTP_400_BAD_REQUEST, ) # If segment is present it cannot be same as x-axis - if segment and (segment not in valid_xaxis_segment or x_axis == segment): + if segment and (segment not in VALID_ANALYTICS_FIELDS or x_axis == segment): return Response( {"error": "Both segment and x axis cannot be same and segment should be valid"}, status=status.HTTP_400_BAD_REQUEST, diff --git a/apps/api/plane/utils/analytics_plot.py b/apps/api/plane/utils/analytics_plot.py index acd86aca868..5a09f12fdb6 100644 --- a/apps/api/plane/utils/analytics_plot.py +++ b/apps/api/plane/utils/analytics_plot.py @@ -22,6 +22,23 @@ # Module imports from plane.db.models import Issue, Project +VALID_ANALYTICS_FIELDS = [ + "state_id", + "state__group", + "labels__id", + "assignees__id", + "estimate_point__value", + "issue_cycle__cycle_id", + "issue_module__module_id", + "priority", + "start_date", + "target_date", + "created_at", + "completed_at", +] + +VALID_YAXIS = ["issue_count", "estimate"] + def annotate_with_monthly_dimension(queryset, field_name, attribute): # Get the year and the months @@ -34,6 +51,8 @@ def annotate_with_monthly_dimension(queryset, field_name, attribute): def extract_axis(queryset, x_axis): + if x_axis not in VALID_ANALYTICS_FIELDS: + raise ValueError(f"Invalid x_axis value: {x_axis}") # Format the dimension when the axis is in date if x_axis in ["created_at", "start_date", "target_date", "completed_at"]: queryset = annotate_with_monthly_dimension(queryset, x_axis, "dimension") @@ -52,6 +71,13 @@ def sort_data(data, temp_axis): def build_graph_plot(queryset, x_axis, y_axis, segment=None): + if x_axis not in VALID_ANALYTICS_FIELDS: + raise ValueError(f"Invalid x_axis value: {x_axis}") + if y_axis not in VALID_YAXIS: + raise ValueError(f"Invalid y_axis value: {y_axis}") + if segment and segment not in VALID_ANALYTICS_FIELDS: + raise ValueError(f"Invalid segment value: {segment}") + # temp x_axis temp_axis = x_axis # Extract the x_axis and queryset