Skip to content

Commit

Permalink
[COST-3994] Rename field that contains the OpenShift project name (#4843
Browse files Browse the repository at this point in the history
)
  • Loading branch information
samdoran committed Dec 20, 2023
1 parent e01b5ce commit 4ec522a
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 43 deletions.
24 changes: 12 additions & 12 deletions koku/api/settings/cost_groups/query_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,10 @@ def _remove_default_projects(projects: list[dict[str, str]]) -> list[dict[str, s

scrubbed_projects = []
for request in projects:
if request["project_name"] in exact_matches:
if request["project"] in exact_matches:
continue

if any(request["project_name"].startswith(prefix.replace("%", "")) for prefix in prefix_matches):
if any(request["project"].startswith(prefix.replace("%", "")) for prefix in prefix_matches):
continue

scrubbed_projects.append(request)
Expand All @@ -60,7 +60,7 @@ def put_openshift_namespaces(projects: list[dict[str, str]]) -> list[dict[str, s

namespaces_to_create = [
OpenshiftCostCategoryNamespace(
namespace=new_project["project_name"],
namespace=new_project["project"],
system_default=False,
cost_category_id=cost_groups[new_project["group"]],
)
Expand All @@ -78,7 +78,7 @@ def put_openshift_namespaces(projects: list[dict[str, str]]) -> list[dict[str, s

def delete_openshift_namespaces(projects: list[dict[str, str]]) -> list[dict[str, str]]:
projects = _remove_default_projects(projects)
projects_to_delete = [item["project_name"] for item in projects]
projects_to_delete = [item["project"] for item in projects]
deleted_count, _ = (
OpenshiftCostCategoryNamespace.objects.filter(namespace__in=projects_to_delete)
.exclude(system_default=True)
Expand All @@ -97,7 +97,7 @@ class CostGroupsQueryHandler:
{
"group": MappingProxyType({"field": "group", "operation": "icontains"}),
"default": MappingProxyType({"field": "default", "operation": "exact"}),
"project_name": MappingProxyType({"field": "project_name", "operation": "icontains"}),
"project": MappingProxyType({"field": "project", "operation": "icontains"}),
}
)

Expand All @@ -110,7 +110,7 @@ def __init__(self, parameters: QueryParameters) -> None:
self.dh = DateHelper()
self.filters = QueryFilterCollection()
self.exclusion = QueryFilterCollection()
self._default_order_by = ["project_name"]
self._default_order_by = ["project"]

self._set_filters_or_exclusion()

Expand Down Expand Up @@ -167,34 +167,34 @@ def _add_worker_unallocated(self, field_name):
default_value = Value(None, output_field=CharField())
if field_name == "default":
default_value = Value(True)
return When(project_name="Worker unallocated", then=default_value)
return When(project="Worker unallocated", then=default_value)

def build_when_conditions(self, cost_group_projects, field_name):
"""Builds when conditions given a field name in the cost_group_projects."""
# __like is a custom django lookup we added to perform a postgresql LIKE
when_conditions = []
for project in cost_group_projects:
when_conditions.append(When(project_name__like=project["project_name"], then=Value(project[field_name])))
when_conditions.append(When(project__like=project["project"], then=Value(project[field_name])))
when_conditions.append(self._add_worker_unallocated(field_name))
return when_conditions

def execute_query(self):
"""Executes a query to grab the information we need for the api return."""
# This query builds the information we need for our when conditions
cost_group_projects = OpenshiftCostCategoryNamespace.objects.annotate(
project_name=F("namespace"),
project=F("namespace"),
default=F("system_default"),
group=F("cost_category__name"),
).values("project_name", "default", "group")
).values("project", "default", "group")

ocp_summary_query = (
OCPProject.objects.values(project_name=F("project"))
OCPProject.objects.values("project")
.annotate(
group=Case(*self.build_when_conditions(cost_group_projects, "group")),
default=Case(*self.build_when_conditions(cost_group_projects, "default")),
clusters=ArrayAgg(F("cluster__cluster_alias"), distinct=True),
)
.values("project_name", "group", "clusters", "default")
.values("project", "group", "clusters", "default")
.distinct()
)

Expand Down
12 changes: 6 additions & 6 deletions koku/api/settings/cost_groups/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@
class CostGroupFilterSerializer(FilterSerializer):
"""Serializer for Cost Group Settings."""

project_name = serializers.CharField(required=False)
project = serializers.CharField(required=False)
group = serializers.CharField(required=False)
default = serializers.BooleanField(required=False)


class CostGroupExcludeSerializer(ExcludeSerializer):
"""Serializer for Cost Group Settings."""

project_name = serializers.CharField(required=False)
project = serializers.CharField(required=False)
group = serializers.CharField(required=False)
default = serializers.BooleanField(required=False)

Expand All @@ -34,7 +34,7 @@ class CostGroupOrderSerializer(OrderSerializer):

ORDER_CHOICES = (("asc", "asc"), ("desc", "desc"))

project_name = serializers.ChoiceField(choices=ORDER_CHOICES, required=False)
project = serializers.ChoiceField(choices=ORDER_CHOICES, required=False)
group = serializers.ChoiceField(choices=ORDER_CHOICES, required=False)
default = serializers.ChoiceField(choices=ORDER_CHOICES, required=False)

Expand All @@ -46,11 +46,11 @@ class CostGroupQueryParamSerializer(ReportQueryParamSerializer):
EXCLUDE_SERIALIZER = CostGroupExcludeSerializer
ORDER_BY_SERIALIZER = CostGroupOrderSerializer

order_by_allowlist = frozenset(("project_name", "group", "default"))
order_by_allowlist = frozenset(("project", "group", "default"))


class CostGroupProjectSerializer(serializers.Serializer):
project_name = serializers.CharField()
project = serializers.CharField()
group = serializers.CharField()

def _is_valid_field_value(self, model, data: str, field_name: str) -> None:
Expand All @@ -70,7 +70,7 @@ def _is_valid_field_value(self, model, data: str, field_name: str) -> None:

raise serializers.ValidationError(msg)

def validate_project_name(self, data):
def validate_project(self, data):
self._is_valid_field_value(OCPProject, data, "project")

return data
Expand Down
2 changes: 1 addition & 1 deletion koku/api/settings/cost_groups/view.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ def delete(self, request: Request) -> Response:

def _summarize_current_month(self, schema_name: str, projects: list[dict[str, str]]) -> list[str]:
"""Resummarize OCP data for the current month."""
projects_to_summarize = [proj["project_name"] for proj in projects]
projects_to_summarize = [proj["project"] for proj in projects]
ocp_queue = OCP_QUEUE
if is_customer_large(schema_name):
ocp_queue = OCP_QUEUE_XL
Expand Down
22 changes: 11 additions & 11 deletions koku/api/settings/test/cost_groups/test_query_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ def setUp(self):
.first()
)

self.project_name = project_to_insert.get("project")
self.project = project_to_insert.get("project")
self.provider_uuid = project_to_insert.get("cluster__provider__uuid")
self.body_format = [{"project_name": self.project_name, "group": self.default_cost_group}]
self.body_format = [{"project": self.project, "group": self.default_cost_group}]

@property
def url(self):
Expand All @@ -63,7 +63,7 @@ def test_get_cost_groups_invalid(self):

def test_get_cost_groups_filters(self):
"""Basic test to exercise the API endpoint"""
parameters = [{"group": "Platform"}, {"default": True}, {"project_name": OCP_PLATFORM_NAMESPACE}]
parameters = [{"group": "Platform"}, {"default": True}, {"project": OCP_PLATFORM_NAMESPACE}]
for parameter in parameters:
with self.subTest(parameter=parameter):
for filter_option, filter_value in parameter.items():
Expand All @@ -87,7 +87,7 @@ def spotcheck_first_data_element(option, value):

return response.status_code, response.data.get("data")[0]

order_by_options = ["group", "default", "project_name"]
order_by_options = ["group", "default", "project"]
for order_by_option in order_by_options:
with self.subTest(order_by_option=order_by_option):
asc_status_code, spotcheck_asc = spotcheck_first_data_element(order_by_option, "asc")
Expand All @@ -98,7 +98,7 @@ def spotcheck_first_data_element(option, value):

def test_get_cost_groups_exclude_functionality(self):
"""Test that values can be excluded in the return."""
parameters = [{"group": "Platform"}, {"default": True}, {"project_name": "koku"}]
parameters = [{"group": "Platform"}, {"default": True}, {"project": "koku"}]
for parameter in parameters:
with self.subTest(parameter=parameter):
for exclude_option, exclude_value in parameter.items():
Expand Down Expand Up @@ -131,7 +131,7 @@ def _add_additional_projects(schema_name):
body = json.dumps(self.body_format)
with schema_context(self.schema_name):
response = self.client.delete(self.url, body, content_type="application/json", **self.headers)
current_count = OpenshiftCostCategoryNamespace.objects.filter(namespace=self.project_name).count()
current_count = OpenshiftCostCategoryNamespace.objects.filter(namespace=self.project).count()

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(current_count, 0)
Expand All @@ -142,19 +142,19 @@ def test_query_handler_remove_default_projects(self):
with schema_context(self.schema_name):
body_format = [
{
"project_name": OCP_PLATFORM_NAMESPACE,
"project": OCP_PLATFORM_NAMESPACE,
"group": self.default_cost_group,
},
{
"project_name": "openshift",
"project": "openshift",
"group": self.default_cost_group,
},
]
self.assertEqual(_remove_default_projects(body_format), [])

def test_put_catch_integrity_error(self):
"""Test that we catch integrity errors on put."""
self.body_format.append({"project_name": self.project_name, "group": self.default_cost_group})
self.body_format.append({"project": self.project, "group": self.default_cost_group})
with self.assertLogs(logger="api.settings.cost_groups.query_handler", level="WARNING") as log_warning:
with schema_context(self.schema_name):
put_openshift_namespaces(self.body_format)
Expand All @@ -168,7 +168,7 @@ def test_put_new_records(self, mock_is_customer_large, mock_update_schedule):
with schema_context(self.schema_name):
body = json.dumps(self.body_format)
response = self.client.put(self.url, body, content_type="application/json", **self.headers)
current_count = OpenshiftCostCategoryNamespace.objects.filter(namespace=self.project_name).count()
current_count = OpenshiftCostCategoryNamespace.objects.filter(namespace=self.project).count()

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(current_count, 1)
Expand All @@ -188,7 +188,7 @@ def test_put_new_records_large(self, mock_is_customer_large, mock_update_schedul
with schema_context(self.schema_name):
body = json.dumps(self.body_format)
response = self.client.put(self.url, body, content_type="application/json", **self.headers)
current_count = OpenshiftCostCategoryNamespace.objects.filter(namespace=self.project_name).count()
current_count = OpenshiftCostCategoryNamespace.objects.filter(namespace=self.project).count()

self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(current_count, 1)
Expand Down
26 changes: 13 additions & 13 deletions koku/api/settings/test/cost_groups/test_serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

class CostGroupFilterSerializerTest(TestCase):
def test_valid_serializer_data(self):
data = {"project_name": "example_project", "group": "example_group", "default": True}
data = {"project": "example_project", "group": "example_group", "default": True}
serializer = CostGroupFilterSerializer(data=data)
self.assertTrue(serializer.is_valid())

Expand All @@ -20,14 +20,14 @@ def test_invalid_serializer_data(self):
self.assertFalse(serializer.is_valid())

def test_serialization(self):
instance_data = {"project_name": "example_project", "group": "example_group", "default": True}
instance_data = {"project": "example_project", "group": "example_group", "default": True}
serializer = CostGroupFilterSerializer(instance_data)
self.assertEqual(serializer.data, instance_data)


class CostGroupExcludeSerializerTest(TestCase):
def test_valid_serializer_data(self):
data = {"project_name": "example_project", "group": "example_group", "default": True}
data = {"project": "example_project", "group": "example_group", "default": True}
serializer = CostGroupExcludeSerializer(data=data)
self.assertTrue(serializer.is_valid())

Expand All @@ -37,47 +37,47 @@ def test_invalid_serializer_data(self):
self.assertFalse(serializer.is_valid())

def test_serialization(self):
instance_data = {"project_name": "example_project", "group": "example_group", "default": True}
instance_data = {"project": "example_project", "group": "example_group", "default": True}
serializer = CostGroupExcludeSerializer(instance_data)
self.assertEqual(serializer.data, instance_data)


class CostGroupOrderSerializerTest(TestCase):
def test_valid_serializer_data(self):
data = {"project_name": "asc", "group": "desc", "default": "asc"}
data = {"project": "asc", "group": "desc", "default": "asc"}
serializer = CostGroupOrderSerializer(data=data)
self.assertTrue(serializer.is_valid())

def test_invalid_serializer_data(self):
data = {"project_name": "example_project", "group": "example_group"} # Invalid choices
data = {"project": "example_project", "group": "example_group"} # Invalid choices
serializer = CostGroupOrderSerializer(data=data)
self.assertFalse(serializer.is_valid())

def test_serialization(self):
instance_data = {"project_name": "asc", "group": "desc", "default": "asc"}
instance_data = {"project": "asc", "group": "desc", "default": "asc"}
serializer = CostGroupOrderSerializer(instance_data)
self.assertEqual(serializer.data, instance_data)


class CostGroupProjectSerializerTest(IamTestCase):
def test_validate_project_name_invalid(self):
"""Test when project_name is an invalid value"""
data = {"project_name": "invalid_project", "group": "Platform"}
def test_validate_project_invalid(self):
"""Test when project is an invalid value"""
data = {"project": "invalid_project", "group": "Platform"}
serializer = CostGroupProjectSerializer(data=data)
with schema_context(self.schema_name):
self.assertFalse(serializer.is_valid())
self.assertIn("project_name", serializer.errors)
self.assertIn("project", serializer.errors)

def test_validate_group_invalid(self):
data = {"project_name": "koku", "group": "Fake"}
data = {"project": "koku", "group": "Fake"}
serializer = CostGroupProjectSerializer(data=data)
with schema_context(self.schema_name):
self.assertFalse(serializer.is_valid())
self.assertIn("group", serializer.errors)

def test_valid(self):
"""Test we get a valid when a real project and group provided."""
data = {"project_name": "koku", "group": "Platform"}
data = {"project": "koku", "group": "Platform"}
serializer = CostGroupProjectSerializer(data=data)
with schema_context(self.schema_name):
self.assertTrue(serializer.is_valid())

0 comments on commit 4ec522a

Please sign in to comment.