Compare commits
9 Commits
Author | SHA1 | Date | |
---|---|---|---|
c496d10c1a | |||
3993cc96a5 | |||
a4b37b34a9 | |||
2f55024f0b | |||
213644a51a | |||
281d839801 | |||
4fd157a785 | |||
968b3a0f92 | |||
f5ba608ed1 |
2
.cz.yaml
2
.cz.yaml
@ -4,5 +4,5 @@ commitizen:
|
|||||||
prerelease_offset: 1
|
prerelease_offset: 1
|
||||||
tag_format: $version
|
tag_format: $version
|
||||||
update_changelog_on_bump: false
|
update_changelog_on_bump: false
|
||||||
version: 1.0.0-b4
|
version: 1.0.0-b5
|
||||||
version_scheme: semver
|
version_scheme: semver
|
||||||
|
@ -151,7 +151,7 @@ Docker Container:
|
|||||||
script:
|
script:
|
||||||
- if [ "$CI_COMMIT_BRANCH" == "development" ] ; then RELEASE_CHANGELOG=$(cz bump --changelog --changelog-to-stdout --prerelease beta); else RELEASE_CHANGELOG=$(cz bump --changelog --changelog-to-stdout); fi
|
- if [ "$CI_COMMIT_BRANCH" == "development" ] ; then RELEASE_CHANGELOG=$(cz bump --changelog --changelog-to-stdout --prerelease beta); else RELEASE_CHANGELOG=$(cz bump --changelog --changelog-to-stdout); fi
|
||||||
- RELEASE_VERSION_NEW=$(cz version --project)
|
- RELEASE_VERSION_NEW=$(cz version --project)
|
||||||
- RELEASE_TAG=$RELEASE_VERSION_NEW
|
- export RELEASE_TAG=$RELEASE_VERSION_NEW
|
||||||
- echo "[DEBUG] RELEASE_VERSION_CURRENT[$RELEASE_VERSION_CURRENT]"
|
- echo "[DEBUG] RELEASE_VERSION_CURRENT[$RELEASE_VERSION_CURRENT]"
|
||||||
- echo "[DEBUG] RELEASE_CHANGELOG[$RELEASE_CHANGELOG]"
|
- echo "[DEBUG] RELEASE_CHANGELOG[$RELEASE_CHANGELOG]"
|
||||||
- echo "[DEBUG] RELEASE_VERSION_NEW[$RELEASE_VERSION_NEW]"
|
- echo "[DEBUG] RELEASE_VERSION_NEW[$RELEASE_VERSION_NEW]"
|
||||||
|
11
CHANGELOG.md
11
CHANGELOG.md
@ -1,3 +1,14 @@
|
|||||||
|
## 1.0.0-b5 (2024-07-31)
|
||||||
|
|
||||||
|
### Feat
|
||||||
|
|
||||||
|
- **api**: Add device config groups to devices
|
||||||
|
- **api**: Ability to fetch configgroups from api along with config
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
- **api**: Ensure device groups is read only
|
||||||
|
|
||||||
## 1.0.0-b4 (2024-07-29)
|
## 1.0.0-b4 (2024-07-29)
|
||||||
|
|
||||||
### Feat
|
### Feat
|
||||||
|
86
app/api/serializers/config.py
Normal file
86
app/api/serializers/config.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
from rest_framework import serializers
|
||||||
|
from rest_framework.reverse import reverse
|
||||||
|
|
||||||
|
from config_management.models.groups import ConfigGroups
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ParentGroupSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
url = serializers.SerializerMethodField('get_url')
|
||||||
|
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ConfigGroups
|
||||||
|
fields = [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'url',
|
||||||
|
]
|
||||||
|
read_only_fields = [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'url',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_url(self, obj):
|
||||||
|
|
||||||
|
request = self.context.get('request')
|
||||||
|
|
||||||
|
return request.build_absolute_uri(reverse("API:_api_config_group", args=[obj.pk]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigGroupsSerializerBase(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
parent = ParentGroupSerializer(read_only=True)
|
||||||
|
url = serializers.SerializerMethodField('get_url')
|
||||||
|
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ConfigGroups
|
||||||
|
fields = [
|
||||||
|
'id',
|
||||||
|
'parent',
|
||||||
|
'name',
|
||||||
|
'config',
|
||||||
|
'url',
|
||||||
|
]
|
||||||
|
read_only_fields = [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'config',
|
||||||
|
'url',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def get_url(self, obj):
|
||||||
|
|
||||||
|
request = self.context.get('request')
|
||||||
|
|
||||||
|
return request.build_absolute_uri(reverse("API:_api_config_group", args=[obj.pk]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ConfigGroupsSerializer(ConfigGroupsSerializerBase):
|
||||||
|
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = ConfigGroups
|
||||||
|
depth = 1
|
||||||
|
fields = [
|
||||||
|
'id',
|
||||||
|
'parent',
|
||||||
|
'name',
|
||||||
|
'config',
|
||||||
|
'url',
|
||||||
|
]
|
||||||
|
read_only_fields = [
|
||||||
|
'id',
|
||||||
|
'parent',
|
||||||
|
'name',
|
||||||
|
'config',
|
||||||
|
'url',
|
||||||
|
]
|
||||||
|
|
@ -1,9 +1,38 @@
|
|||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from itam.models.device import Device
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from api.serializers.config import ParentGroupSerializer
|
||||||
|
|
||||||
|
from config_management.models.groups import ConfigGroupHosts
|
||||||
|
|
||||||
|
from itam.models.device import Device
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceConfigGroupsSerializer(serializers.ModelSerializer):
|
||||||
|
|
||||||
|
name = serializers.CharField(source='group.name', read_only=True)
|
||||||
|
|
||||||
|
url = serializers.HyperlinkedIdentityField(
|
||||||
|
view_name="API:_api_config_group", format="html"
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
|
||||||
|
model = ConfigGroupHosts
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'url',
|
||||||
|
|
||||||
|
]
|
||||||
|
read_only_fields = [
|
||||||
|
'id',
|
||||||
|
'name',
|
||||||
|
'url',
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class DeviceSerializer(serializers.ModelSerializer):
|
class DeviceSerializer(serializers.ModelSerializer):
|
||||||
@ -14,6 +43,8 @@ class DeviceSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
config = serializers.SerializerMethodField('get_device_config')
|
config = serializers.SerializerMethodField('get_device_config')
|
||||||
|
|
||||||
|
groups = DeviceConfigGroupsSerializer(source='configgrouphosts_set', many=True, read_only=True)
|
||||||
|
|
||||||
def get_device_config(self, device):
|
def get_device_config(self, device):
|
||||||
|
|
||||||
request = self.context.get('request')
|
request = self.context.get('request')
|
||||||
@ -22,11 +53,29 @@ class DeviceSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Device
|
model = Device
|
||||||
fields = '__all__'
|
depth = 1
|
||||||
|
fields = [
|
||||||
read_only_fields = [
|
'id',
|
||||||
'inventorydate',
|
|
||||||
'is_global',
|
'is_global',
|
||||||
'slug',
|
'name',
|
||||||
|
'config',
|
||||||
|
'serial_number',
|
||||||
|
'uuid',
|
||||||
|
'inventorydate',
|
||||||
|
'created',
|
||||||
|
'modified',
|
||||||
|
'groups',
|
||||||
|
'organization',
|
||||||
|
'url',
|
||||||
|
]
|
||||||
|
|
||||||
|
read_only_fields = [
|
||||||
|
'id',
|
||||||
|
'config',
|
||||||
|
'inventorydate',
|
||||||
|
'created',
|
||||||
|
'modified',
|
||||||
|
'groups',
|
||||||
|
'url',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ from django.urls import path
|
|||||||
from rest_framework.routers import DefaultRouter
|
from rest_framework.routers import DefaultRouter
|
||||||
from rest_framework.urlpatterns import format_suffix_patterns
|
from rest_framework.urlpatterns import format_suffix_patterns
|
||||||
|
|
||||||
from .views import access, index
|
from .views import access, config, index
|
||||||
|
|
||||||
from .views.itam import software, config as itam_config
|
from .views.itam import software, config as itam_config
|
||||||
from .views.itam.device import DeviceViewSet
|
from .views.itam.device import DeviceViewSet
|
||||||
@ -24,6 +24,9 @@ router.register('software', software.SoftwareViewSet, basename='software')
|
|||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("config/<slug:slug>/", itam_config.View.as_view(), name="_api_device_config"),
|
path("config/<slug:slug>/", itam_config.View.as_view(), name="_api_device_config"),
|
||||||
|
|
||||||
|
path("configuration/", config.ConfigGroupsList.as_view(), name='_api_config_groups'),
|
||||||
|
path("configuration/<int:pk>", config.ConfigGroupsDetail.as_view(), name='_api_config_group'),
|
||||||
|
|
||||||
path("device/inventory", inventory.Collect.as_view(), name="_api_device_inventory"),
|
path("device/inventory", inventory.Collect.as_view(), name="_api_device_inventory"),
|
||||||
|
|
||||||
path("organization/", access.OrganizationList.as_view(), name='_api_orgs'),
|
path("organization/", access.OrganizationList.as_view(), name='_api_orgs'),
|
||||||
|
54
app/api/views/config.py
Normal file
54
app/api/views/config.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
from drf_spectacular.utils import extend_schema, extend_schema_view
|
||||||
|
|
||||||
|
from rest_framework import generics
|
||||||
|
|
||||||
|
from api.serializers.config import ConfigGroupsSerializer
|
||||||
|
from api.views.mixin import OrganizationPermissionAPI
|
||||||
|
|
||||||
|
from config_management.models.groups import ConfigGroups
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_view(
|
||||||
|
get=extend_schema(
|
||||||
|
summary = "Fetch Config groups",
|
||||||
|
description="Returns a list of Config Groups."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
class ConfigGroupsList(generics.ListAPIView):
|
||||||
|
|
||||||
|
permission_classes = [
|
||||||
|
OrganizationPermissionAPI
|
||||||
|
]
|
||||||
|
|
||||||
|
queryset = ConfigGroups.objects.all()
|
||||||
|
lookup_field = 'pk'
|
||||||
|
serializer_class = ConfigGroupsSerializer
|
||||||
|
|
||||||
|
|
||||||
|
def get_view_name(self):
|
||||||
|
return "Config Groups"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@extend_schema_view(
|
||||||
|
get=extend_schema(
|
||||||
|
summary = "Get A Config Group",
|
||||||
|
# responses = {}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
class ConfigGroupsDetail(generics.RetrieveAPIView):
|
||||||
|
|
||||||
|
permission_classes = [
|
||||||
|
OrganizationPermissionAPI
|
||||||
|
]
|
||||||
|
|
||||||
|
queryset = ConfigGroups.objects.all()
|
||||||
|
lookup_field = 'pk'
|
||||||
|
serializer_class = ConfigGroupsSerializer
|
||||||
|
|
||||||
|
|
||||||
|
def get_view_name(self):
|
||||||
|
return "Config Group"
|
||||||
|
|
||||||
|
|
@ -27,6 +27,7 @@ class Index(viewsets.ViewSet):
|
|||||||
{
|
{
|
||||||
# "teams": reverse("_api_teams", request=request),
|
# "teams": reverse("_api_teams", request=request),
|
||||||
"devices": reverse("API:device-list", request=request),
|
"devices": reverse("API:device-list", request=request),
|
||||||
|
"config_groups": reverse("API:_api_config_groups", request=request),
|
||||||
"organizations": reverse("API:_api_orgs", request=request),
|
"organizations": reverse("API:_api_orgs", request=request),
|
||||||
"software": reverse("API:software-list", request=request),
|
"software": reverse("API:software-list", request=request),
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,224 @@
|
|||||||
|
import pytest
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.shortcuts import reverse
|
||||||
|
from django.test import Client, TestCase
|
||||||
|
|
||||||
|
from access.models import Organization, Team, TeamUsers, Permission
|
||||||
|
|
||||||
|
from app.tests.abstract.models import TenancyModel
|
||||||
|
|
||||||
|
from config_management.models.groups import ConfigGroups
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.django_db
|
||||||
|
class ConfigGroupsAPI(
|
||||||
|
TestCase,
|
||||||
|
):
|
||||||
|
|
||||||
|
model = ConfigGroups
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(self):
|
||||||
|
"""Setup Test
|
||||||
|
|
||||||
|
1. Create an organization for user and item
|
||||||
|
2. Create an item
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
self.organization = Organization.objects.create(name='test_org')
|
||||||
|
|
||||||
|
|
||||||
|
self.item = self.model.objects.create(
|
||||||
|
organization = self.organization,
|
||||||
|
name = 'one',
|
||||||
|
config = dict({"key": "one", "existing": "dont_over_write"})
|
||||||
|
)
|
||||||
|
|
||||||
|
self.second_item = self.model.objects.create(
|
||||||
|
organization = self.organization,
|
||||||
|
name = 'one_two',
|
||||||
|
config = dict({"key": "two"}),
|
||||||
|
parent = self.item
|
||||||
|
)
|
||||||
|
|
||||||
|
self.url_view_kwargs = {'pk': self.second_item.id}
|
||||||
|
|
||||||
|
view_permissions = Permission.objects.get(
|
||||||
|
codename = 'view_' + self.model._meta.model_name,
|
||||||
|
content_type = ContentType.objects.get(
|
||||||
|
app_label = self.model._meta.app_label,
|
||||||
|
model = self.model._meta.model_name,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
view_team = Team.objects.create(
|
||||||
|
team_name = 'view_team',
|
||||||
|
organization = self.organization,
|
||||||
|
)
|
||||||
|
|
||||||
|
view_team.permissions.set([view_permissions])
|
||||||
|
|
||||||
|
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||||
|
teamuser = TeamUsers.objects.create(
|
||||||
|
team = view_team,
|
||||||
|
user = self.view_user
|
||||||
|
)
|
||||||
|
|
||||||
|
client = Client()
|
||||||
|
url = reverse('API:_api_config_group', kwargs=self.url_view_kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
client.force_login(self.view_user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
self.api_data = response.data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_id(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
id field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'id' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_id(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
id field must be int
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['id']) is int
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_parent(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
parent field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'parent' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_parent(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
parent field must be dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['parent']) is dict
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_name(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
name field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'name' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_name(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
name field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['name']) is str
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_config(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
config field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'config' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_config(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
config field must be dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['config']) is dict
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_url(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
url field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'url' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_url(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
url field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['url']) is str
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_parent_id(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
parent.id field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'id' in self.api_data['parent']
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_parent_id(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
parent.id field must be int
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['parent']['id']) is int
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_parent_name(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
parent.name field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'name' in self.api_data['parent']
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_parent_name(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
parent.name field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['parent']['name']) is str
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_parent_url(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
parent.url field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'url' in self.api_data['parent']
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_parent_url(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
parent.url field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['parent']['url']) is str
|
522
app/itam/tests/unit/device/test_device_api.py
Normal file
522
app/itam/tests/unit/device/test_device_api.py
Normal file
@ -0,0 +1,522 @@
|
|||||||
|
import pytest
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
from django.contrib.auth.models import AnonymousUser, User
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django.shortcuts import reverse
|
||||||
|
from django.test import Client, TestCase
|
||||||
|
|
||||||
|
from rest_framework.relations import Hyperlink
|
||||||
|
|
||||||
|
from access.models import Organization, Team, TeamUsers, Permission
|
||||||
|
|
||||||
|
from api.tests.abstract.api_permissions import APIPermissions
|
||||||
|
|
||||||
|
from config_management.models.groups import ConfigGroups, ConfigGroupHosts
|
||||||
|
|
||||||
|
from itam.models.device import Device
|
||||||
|
|
||||||
|
|
||||||
|
class DeviceAPI(TestCase):
|
||||||
|
|
||||||
|
|
||||||
|
model = Device
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def setUpTestData(self):
|
||||||
|
"""Setup Test
|
||||||
|
|
||||||
|
1. Create an organization for user and item
|
||||||
|
. create an organization that is different to item
|
||||||
|
2. Create a device
|
||||||
|
3. create teams with each permission: view, add, change, delete
|
||||||
|
4. create a user per team
|
||||||
|
"""
|
||||||
|
|
||||||
|
organization = Organization.objects.create(name='test_org')
|
||||||
|
|
||||||
|
self.organization = organization
|
||||||
|
|
||||||
|
different_organization = Organization.objects.create(name='test_different_organization')
|
||||||
|
|
||||||
|
|
||||||
|
self.item = self.model.objects.create(
|
||||||
|
organization=organization,
|
||||||
|
name = 'deviceone',
|
||||||
|
uuid = 'val',
|
||||||
|
serial_number = 'another val'
|
||||||
|
)
|
||||||
|
|
||||||
|
config_group = ConfigGroups.objects.create(
|
||||||
|
organization = self.organization,
|
||||||
|
name = 'one',
|
||||||
|
config = dict({"key": "one", "existing": "dont_over_write"})
|
||||||
|
)
|
||||||
|
|
||||||
|
config_group_second_item = ConfigGroups.objects.create(
|
||||||
|
organization = self.organization,
|
||||||
|
name = 'one_two',
|
||||||
|
config = dict({"key": "two"}),
|
||||||
|
parent = config_group
|
||||||
|
)
|
||||||
|
|
||||||
|
config_group_hosts = ConfigGroupHosts.objects.create(
|
||||||
|
organization = organization,
|
||||||
|
host = self.item,
|
||||||
|
group = config_group,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
config_group_hosts_two = ConfigGroupHosts.objects.create(
|
||||||
|
organization = organization,
|
||||||
|
host = self.item,
|
||||||
|
group = config_group_second_item,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# self.url_kwargs = {'pk': self.item.id}
|
||||||
|
|
||||||
|
self.url_view_kwargs = {'pk': self.item.id}
|
||||||
|
|
||||||
|
# self.add_data = {'name': 'device', 'organization': self.organization.id}
|
||||||
|
|
||||||
|
|
||||||
|
view_permissions = Permission.objects.get(
|
||||||
|
codename = 'view_' + self.model._meta.model_name,
|
||||||
|
content_type = ContentType.objects.get(
|
||||||
|
app_label = self.model._meta.app_label,
|
||||||
|
model = self.model._meta.model_name,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
view_team = Team.objects.create(
|
||||||
|
team_name = 'view_team',
|
||||||
|
organization = organization,
|
||||||
|
)
|
||||||
|
|
||||||
|
view_team.permissions.set([view_permissions])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# add_permissions = Permission.objects.get(
|
||||||
|
# codename = 'add_' + self.model._meta.model_name,
|
||||||
|
# content_type = ContentType.objects.get(
|
||||||
|
# app_label = self.model._meta.app_label,
|
||||||
|
# model = self.model._meta.model_name,
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
|
||||||
|
# add_team = Team.objects.create(
|
||||||
|
# team_name = 'add_team',
|
||||||
|
# organization = organization,
|
||||||
|
# )
|
||||||
|
|
||||||
|
# add_team.permissions.set([add_permissions])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# change_permissions = Permission.objects.get(
|
||||||
|
# codename = 'change_' + self.model._meta.model_name,
|
||||||
|
# content_type = ContentType.objects.get(
|
||||||
|
# app_label = self.model._meta.app_label,
|
||||||
|
# model = self.model._meta.model_name,
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
|
||||||
|
# change_team = Team.objects.create(
|
||||||
|
# team_name = 'change_team',
|
||||||
|
# organization = organization,
|
||||||
|
# )
|
||||||
|
|
||||||
|
# change_team.permissions.set([change_permissions])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# delete_permissions = Permission.objects.get(
|
||||||
|
# codename = 'delete_' + self.model._meta.model_name,
|
||||||
|
# content_type = ContentType.objects.get(
|
||||||
|
# app_label = self.model._meta.app_label,
|
||||||
|
# model = self.model._meta.model_name,
|
||||||
|
# )
|
||||||
|
# )
|
||||||
|
|
||||||
|
# delete_team = Team.objects.create(
|
||||||
|
# team_name = 'delete_team',
|
||||||
|
# organization = organization,
|
||||||
|
# )
|
||||||
|
|
||||||
|
# delete_team.permissions.set([delete_permissions])
|
||||||
|
|
||||||
|
|
||||||
|
# self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
|
||||||
|
|
||||||
|
|
||||||
|
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||||
|
teamuser = TeamUsers.objects.create(
|
||||||
|
team = view_team,
|
||||||
|
user = self.view_user
|
||||||
|
)
|
||||||
|
|
||||||
|
# self.add_user = User.objects.create_user(username="test_user_add", password="password")
|
||||||
|
# teamuser = TeamUsers.objects.create(
|
||||||
|
# team = add_team,
|
||||||
|
# user = self.add_user
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.change_user = User.objects.create_user(username="test_user_change", password="password")
|
||||||
|
# teamuser = TeamUsers.objects.create(
|
||||||
|
# team = change_team,
|
||||||
|
# user = self.change_user
|
||||||
|
# )
|
||||||
|
|
||||||
|
# self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
|
||||||
|
# teamuser = TeamUsers.objects.create(
|
||||||
|
# team = delete_team,
|
||||||
|
# user = self.delete_user
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
# self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
|
||||||
|
|
||||||
|
|
||||||
|
# different_organization_team = Team.objects.create(
|
||||||
|
# team_name = 'different_organization_team',
|
||||||
|
# organization = different_organization,
|
||||||
|
# )
|
||||||
|
|
||||||
|
# different_organization_team.permissions.set([
|
||||||
|
# view_permissions,
|
||||||
|
# add_permissions,
|
||||||
|
# change_permissions,
|
||||||
|
# delete_permissions,
|
||||||
|
# ])
|
||||||
|
|
||||||
|
# TeamUsers.objects.create(
|
||||||
|
# team = different_organization_team,
|
||||||
|
# user = self.different_organization_user
|
||||||
|
# )
|
||||||
|
|
||||||
|
|
||||||
|
client = Client()
|
||||||
|
url = reverse('API:device-detail', kwargs=self.url_view_kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
client.force_login(self.view_user)
|
||||||
|
response = client.get(url)
|
||||||
|
|
||||||
|
self.api_data = response.data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_id(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
id field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'id' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_id(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
id field must be int
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['id']) is int
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_is_global(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
is_global field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'is_global' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_is_global(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
is_global field must be boolean
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['is_global']) is bool
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_name(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
name field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'name' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_name(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
name field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['name']) is str
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_config(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
config field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'config' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_config(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
config field must be dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['config']) is str
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_serial_number(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
serial_number field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'serial_number' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_serial_number(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
serial_number field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['serial_number']) is str
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_uuid(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
uuid field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'uuid' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_uuid(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
uuid field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['uuid']) is str
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_inventorydate(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
inventorydate field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'inventorydate' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_inventorydate(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
inventorydate field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert (
|
||||||
|
type(self.api_data['inventorydate']) is str
|
||||||
|
or
|
||||||
|
self.api_data['inventorydate'] is None
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_created(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
created field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'created' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_created(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
created field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['created']) is str
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_modified(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
modified field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'modified' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_modified(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
modified field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['modified']) is str
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_groups(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
groups field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'groups' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_groups(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
groups field must be list
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['groups']) is list
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_organization(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
organization field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'organization' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_organization(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
organization field must be dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['organization']) is dict
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_url(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
url field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'url' in self.api_data
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_url(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
url field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['url']) is Hyperlink
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_organization_id(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
organization.id field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'id' in self.api_data['organization']
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_organization_id(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
organization.id field must be int
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['organization']['id']) is int
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_organization_name(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
organization.name field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'name' in self.api_data['organization']
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_organization_name(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
organization.name field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['organization']['name']) is str
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_groups_id(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
groups.id field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'id' in self.api_data['groups'][0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_groups_id(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
groups.id field must be int
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['groups'][0]['id']) is int
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_groups_name(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
groups.name field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'name' in self.api_data['groups'][0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_groups_name(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
groups.name field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['groups'][0]['name']) is str
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_exists_groups_url(self):
|
||||||
|
""" Test for existance of API Field
|
||||||
|
|
||||||
|
groups.url field must exist
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert 'url' in self.api_data['groups'][0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_api_field_type_groups_url(self):
|
||||||
|
""" Test for type for API Field
|
||||||
|
|
||||||
|
groups.url field must be str
|
||||||
|
"""
|
||||||
|
|
||||||
|
assert type(self.api_data['groups'][0]['url']) is Hyperlink
|
@ -16,3 +16,8 @@ This documentation is targeted towards those whom administer the applications de
|
|||||||
- [Backup](./backup.md)
|
- [Backup](./backup.md)
|
||||||
|
|
||||||
- [Installation](./installation.md)
|
- [Installation](./installation.md)
|
||||||
|
|
||||||
|
|
||||||
|
## Ansible Automation Platform / AWX
|
||||||
|
|
||||||
|
We have built an [Ansible Collection](../../ansible/collections/centurion/index.md) for Centurion ERP that you could consider the bridge between the config within Centurion and the end device. This collection can be directly added to AAP / AWX as a project which enables accessing the features the collection has to offer. Please refer to the [collections documentation](../../ansible/collections/centurion/index.md) for further information.
|
||||||
|
@ -27,6 +27,8 @@ Whilst there are many Enterprise Rescource Planning (ERP) applications, Centurio
|
|||||||
|
|
||||||
Centurion ERP contains the following modules:
|
Centurion ERP contains the following modules:
|
||||||
|
|
||||||
|
- [Companion Ansible Collection](../ansible/collections/centurion/index.md)
|
||||||
|
|
||||||
- [Configuration Management](./user/config_management/index.md)
|
- [Configuration Management](./user/config_management/index.md)
|
||||||
|
|
||||||
- [IT Asset Management (ITAM)](./user/itam/index.md)
|
- [IT Asset Management (ITAM)](./user/itam/index.md)
|
||||||
|
Reference in New Issue
Block a user