Skip to content

Commit

Permalink
Merge pull request #356 from nofusscomputing/itim-api-v2
Browse files Browse the repository at this point in the history
  • Loading branch information
jon-nfc authored Oct 21, 2024
2 parents 27c54c6 + 59fe9f1 commit 2d05cd8
Show file tree
Hide file tree
Showing 30 changed files with 3,589 additions and 29 deletions.
16 changes: 16 additions & 0 deletions app/api/react_ui_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,22 @@ def determine_metadata(self, request, view):
}
]
},
{
"display_name": "ITIM",
"name": "itim",
"pages": [
{
"display_name": "Clusters",
"name": "cluster",
"link": "/itim/cluster"
},
{
"display_name": "Services",
"name": "service",
"link": "/itim/service"
},
]
},
{
"display_name": "Config Management",
"name": "config_management",
Expand Down
15 changes: 14 additions & 1 deletion app/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@
)

from itim.viewsets import (
index as itim_v2
index as itim_v2,
cluster as cluster_v2,
cluster_type as cluster_type_v2,
port as port_v2,
service as service_v2,
service_device as service_device_v2
)

from project_management.viewsets import (
Expand Down Expand Up @@ -154,6 +159,7 @@
router.register('v2/itam', itam_index_v2.Index, basename='_api_v2_itam_home')
router.register('v2/itam/device', device_v2.ViewSet, basename='_api_v2_device')
router.register('v2/itam/device/(?P<device_id>[0-9]+)/software', device_software_v2.ViewSet, basename='_api_v2_device_software')
router.register('v2/itam/device/(?P<device_id>[0-9]+)/service', service_device_v2.ViewSet, basename='_api_v2_service_device')
router.register('v2/itam/device/(?P<device_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_device_notes')
router.register('v2/itam/operating_system', operating_system_v2.ViewSet, basename='_api_v2_operating_system')
router.register('v2/itam/operating_system/(?P<operating_system_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_operating_system_notes')
Expand All @@ -163,17 +169,24 @@
router.register('v2/itam/software/(?P<software_id>[0-9]+)/version', software_version_v2.ViewSet, basename='_api_v2_software_version')

router.register('v2/itim', itim_v2.Index, basename='_api_v2_itim_home')
router.register('v2/itim/cluster', cluster_v2.ViewSet, basename='_api_v2_cluster')
router.register('v2/itim/cluster/(?P<cluster_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_cluster_notes')
router.register('v2/itim/service', service_v2.ViewSet, basename='_api_v2_service')
router.register('v2/itim/service/(?P<service_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_service_notes')

router.register('v2/project_management', project_management_v2.Index, basename='_api_v2_project_management_home')

router.register('v2/settings', settings_index_v2.Index, basename='_api_v2_settings_home')
router.register('v2/settings/cluster_type', cluster_type_v2.ViewSet, basename='_api_v2_cluster_type')
router.register('v2/settings/cluster_type/(?P<cluster_type_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_cluster_type_notes')
router.register('v2/settings/device_model', device_model_v2.ViewSet, basename='_api_v2_device_model')
router.register('v2/settings/device_type', device_type_v2.ViewSet, basename='_api_v2_device_type')
router.register('v2/settings/external_link', external_link_v2.ViewSet, basename='_api_v2_external_link')
router.register('v2/settings/knowledge_base_category', knowledge_base_category_v2.ViewSet, basename='_api_v2_knowledge_base_category')
router.register('v2/settings/manufacturer', manufacturer_v2.ViewSet, basename='_api_v2_manufacturer')
router.register('v2/settings/manufacturer/(?P<manufacturer_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_manufacturer_notes')
router.register('v2/settings/port', port_v2.ViewSet, basename='_api_v2_port')
router.register('v2/settings/port/(?P<port_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_port_notes')
router.register('v2/settings/software_category', software_category_v2.ViewSet, basename='_api_v2_software_category')

urlpatterns = [
Expand Down
1 change: 1 addition & 0 deletions app/itam/serializers/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ def get_url(self, item):
}
),
'notes': reverse("API:_api_v2_device_notes-list", request=self._context['view'].request, kwargs={'device_id': item.pk}),
'service': reverse("API:_api_v2_service_device-list", request=self._context['view'].request, kwargs={'device_id': item.pk}),
'software': reverse("API:_api_v2_device_software-list", request=self._context['view'].request, kwargs={'device_id': item.pk}),
}

Expand Down
36 changes: 36 additions & 0 deletions app/itam/tests/unit/device/test_device_api_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,25 @@ def test_api_field_type_urls_notes(self):



def test_api_field_exists_urls_service(self):
""" Test for existance of API Field
_urls.service field must exist
"""

assert 'service' in self.api_data['_urls']


def test_api_field_type_urls_service(self):
""" Test for type for API Field
_urls.service field must be str
"""

assert type(self.api_data['_urls']['service']) is str



def test_api_field_exists_urls_software(self):
""" Test for existance of API Field
Expand All @@ -546,3 +565,20 @@ def test_api_field_type_urls_software(self):
assert type(self.api_data['_urls']['software']) is str



# def test_api_field_exists_urls_tickets(self):
# """ Test for existance of API Field

# _urls.tickets field must exist
# """

# assert 'tickets' in self.api_data['_urls']


# def test_api_field_type_urls_tickets(self):
# """ Test for type for API Field

# _urls.tickets field must be str
# """

# assert type(self.api_data['_urls']['tickets']) is str
22 changes: 22 additions & 0 deletions app/itim/migrations/0006_alter_port_options_alter_port_protocol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 5.1.2 on 2024-10-21 11:34

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('itim', '0005_alter_cluster_cluster_type_alter_cluster_id_and_more'),
]

operations = [
migrations.AlterModelOptions(
name='port',
options={'ordering': ['number', 'protocol'], 'verbose_name': 'Port', 'verbose_name_plural': 'Ports'},
),
migrations.AlterField(
model_name='port',
name='protocol',
field=models.CharField(choices=[('TCP', 'TCP'), ('UDP', 'UDP')], help_text='Layer 4 Network Protocol', max_length=3, verbose_name='Protocol'),
),
]
17 changes: 3 additions & 14 deletions app/itim/models/clusters.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class Meta:
"layout": "double",
"left": [
'organization',
'name'
'name',
'is_global',
],
"right": [
Expand All @@ -82,18 +82,6 @@ class Meta:
}
]
},
{
"name": "Rendered Config",
"slug": "config_management",
"sections": [
{
"layout": "single",
"fields": [
"rendered_config",
]
}
]
},
{
"name": "Tickets",
"slug": "ticket",
Expand Down Expand Up @@ -220,11 +208,12 @@ class Meta:
'organization',
'parent_cluster',
'cluster_type',
'name'
'name',
'is_global',
],
"right": [
'model_notes',
'resources',
'created',
'modified',
]
Expand Down
20 changes: 9 additions & 11 deletions app/itim/models/services.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ class Meta:
'protocol',
]

verbose_name = "Protocol"
verbose_name = "Port"

verbose_name_plural = "Protocols"
verbose_name_plural = "Ports"


class Protocol(models.TextChoices):
Expand Down Expand Up @@ -67,7 +67,6 @@ def validation_port_number(number: int):
protocol = models.CharField(
blank = False,
choices=Protocol.choices,
default = Protocol.TCP,
help_text = 'Layer 4 Network Protocol',
max_length = 3,
verbose_name = 'Protocol',
Expand Down Expand Up @@ -334,23 +333,22 @@ def validate_config_key_variable(value):
@property
def config_variables(self):

if self.is_template:
config: dict = {}

return self.config

if self.template:

template_config: dict = Service.objects.get(id=self.template.id).config
if self.template.config:

template_config.update(self.config)
config.update(self.template.config)

return template_config

else:
if self.config:

return self.config
config.update(self.config)

return config

return None


def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
Expand Down
143 changes: 143 additions & 0 deletions app/itim/serializers/cluster.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
from rest_framework.reverse import reverse
from rest_framework import serializers

from access.serializers.organization import OrganizationBaseSerializer

from itam.serializers.device import DeviceBaseSerializer

from itim.serializers.cluster_type import ClusterTypeBaseSerializer
from itim.models.clusters import Cluster



class ClusterBaseSerializer(serializers.ModelSerializer):

display_name = serializers.SerializerMethodField('get_display_name')

def get_display_name(self, item):

return str( item )

url = serializers.HyperlinkedIdentityField(
view_name="API:_api_v2_cluster-detail", format="html"
)

class Meta:

model = Cluster

fields = [
'id',
'display_name',
'name',
'url',
]

read_only_fields = [
'id',
'display_name',
'name',
'url',
]


class ClusterModelSerializer(ClusterBaseSerializer):

_urls = serializers.SerializerMethodField('get_url')

def get_url(self, item):

return {
'_self': reverse("API:_api_v2_cluster-detail", request=self._context['view'].request, kwargs={'pk': item.pk}),
'history': reverse(
"API:_api_v2_model_history-list",
request=self._context['view'].request,
kwargs={
'model_class': self.Meta.model._meta.model_name,
'model_id': item.pk
}
),
'notes': reverse("API:_api_v2_cluster_notes-list", request=self._context['view'].request, kwargs={'cluster_id': item.pk}),
'tickets': 'ToDo'
}


rendered_config = serializers.JSONField( read_only = True)

resources = serializers.CharField(
label = 'Available Resources',
read_only = True,
initial = 'xx/yy CPU, xx/yy RAM, xx/yy Storage',
default = 'xx/yy CPU, xx/yy RAM, xx/yy Storage',
)


class Meta:

model = Cluster

fields = [
'id',
'organization',
'display_name',
'name',
'model_notes',
'parent_cluster',
'cluster_type',
'resources',
'config',
'rendered_config',
'nodes',
'devices',
'is_global',
'created',
'modified',
'_urls',
]

read_only_fields = [
'id',
'display_name',
'rendered_config',
'resources',
'created',
'modified',
'_urls',
]


def is_valid(self, *, raise_exception=False):

is_valid = super().is_valid(raise_exception=raise_exception)


if 'parent_cluster' in self.validated_data:

if hasattr(self.instance, 'id') and self.validated_data['parent_cluster']:

if self.validated_data['parent_cluster'].id == self.instance.id:

is_valid = False

raise serializers.ValidationError(
detail = {
"parent_cluster": "Cluster can't have itself as its parent cluster"
},
code = 'parent_not_self'
)

return is_valid



class ClusterViewSerializer(ClusterModelSerializer):

cluster_type = ClusterTypeBaseSerializer( many = False, read_only = True )

devices = DeviceBaseSerializer( many = True, read_only = True )

nodes = DeviceBaseSerializer( many = True, read_only = True )

organization = OrganizationBaseSerializer( many = False, read_only = True )

parent_cluster = ClusterBaseSerializer( many = False, read_only = True )
Loading

0 comments on commit 2d05cd8

Please sign in to comment.