Compare commits
89 Commits
Author | SHA1 | Date | |
---|---|---|---|
39e06a43a3 | |||
0ceb5a512e | |||
51a2fe1141 | |||
8332b31a1a | |||
c03b7e7d49 | |||
ed6cdaef8b | |||
5335758c70 | |||
c14ee4c4be | |||
b51ce7d513 | |||
03c39d2e2f | |||
10285bedef | |||
b9e8caecc1 | |||
afc0c66602 | |||
41ffe5b3bc | |||
e158f49a21 | |||
18bb2909a5 | |||
ca2da06d2c | |||
17f47040d6 | |||
c5faf3115d | |||
3cdd8adf38 | |||
39539a4bae | |||
5de8893330 | |||
d9b9e32019 | |||
3bce54b495 | |||
34c327b7b6 | |||
8b790b451e | |||
00bebbd8f4 | |||
cebb02de99 | |||
ebeea4f526 | |||
c91bc6ee10 | |||
625cb52dd2 | |||
497adc68f4 | |||
a10f1b3694 | |||
11abf177ab | |||
becf55e22f | |||
c57d465c5b | |||
741a149f33 | |||
f85cc0fd3d | |||
80a82605aa | |||
d9b5ec7d41 | |||
fcf3d568e4 | |||
eb3071c93d | |||
873f241d14 | |||
da5c4d76e9 | |||
8f1e61a7a6 | |||
7f46daeb54 | |||
6291510ba4 | |||
871cdc6b5d | |||
9e78aa5940 | |||
4df7e57ca7 | |||
b80d8a5c64 | |||
f3ef3be883 | |||
250ba96c84 | |||
df5234e8d3 | |||
c4459bd21f | |||
702644adc5 | |||
c9caa96f98 | |||
eb4a132f39 | |||
cb636fff01 | |||
f8338ff6f0 | |||
47898d7e1d | |||
2be5819839 | |||
c0d5bfad45 | |||
cadb3bcac0 | |||
089f8beef2 | |||
d5771401c8 | |||
a32d942db6 | |||
2ef5124ccc | |||
b54d710c50 | |||
fd3bd7f04e | |||
61f6996f5e | |||
2b2c719e69 | |||
f13bdf5a05 | |||
6010973c3b | |||
85face7cc6 | |||
38ba86b8b5 | |||
d0118e1f6f | |||
827fe14369 | |||
6ed4db0502 | |||
deb93378b0 | |||
1904c2e28c | |||
aaf2d23c53 | |||
7c9320a84b | |||
c8b6a31cd4 | |||
22615e46ef | |||
4ae965603c | |||
e4ce1b539e | |||
cee396da3f | |||
0786977633 |
2
.cz.yaml
2
.cz.yaml
@ -17,5 +17,5 @@ commitizen:
|
||||
prerelease_offset: 1
|
||||
tag_format: $version
|
||||
update_changelog_on_bump: false
|
||||
version: 1.4.1
|
||||
version: 1.5.0
|
||||
version_scheme: semver
|
||||
|
3
.github/pull_request_template.md
vendored
3
.github/pull_request_template.md
vendored
@ -20,6 +20,9 @@
|
||||
|
||||
<!-- dont remove tasks below strike through including the checkbox by enclosing in double tidle '~~' -->
|
||||
|
||||
- [ ] **Feature Release ONLY** :red_square: Squash migration files :red_square:
|
||||
_Multiple migration files created as part of this release are to be sqauashed into a few files as possible so as to limit the number of migrations_
|
||||
|
||||
- [ ] :firecracker: Contains breaking-change Any Breaking change(s)?
|
||||
|
||||
_Breaking Change must also be notated in the commit that introduces it and in [Conventional Commit Format](https://www.conventionalcommits.org/en/v1.0.0/)._
|
||||
|
92
CHANGELOG.md
92
CHANGELOG.md
@ -1,3 +1,95 @@
|
||||
## 1.5.0 (2024-12-09)
|
||||
|
||||
### feat
|
||||
|
||||
- **python**: update django 5.1.2 -> 5.1.4
|
||||
- **api**: If global organization defined, filter from ALL organization fields
|
||||
- **api**: Add nav menu permission checks for settings
|
||||
- **api**: When fething an items url dueing metadata creation, used named parameters
|
||||
- **access**: Modify Admin User panel by removing perms and adding teams
|
||||
- **access**: filter permissions available
|
||||
- **api**: Filter navigation menu by user permissions
|
||||
- **api**: Add API version details to the metadata
|
||||
- **access**: add `back` and `return_url` urls to team user metadata
|
||||
- **access**: add `back` and `return_url` urls to team metadata
|
||||
- **api**: Add `back` url to metadata
|
||||
- **api**: Add `return_url` to metadata
|
||||
|
||||
### Fixes
|
||||
|
||||
- **settings**: Add missing `get_url` function to user_settings model
|
||||
- **settings**: Add missing `get_url` function to app_settings model
|
||||
- **core**: correctr the required parameters for related ticket serializer when fetching own url
|
||||
- **core**: Remove requirement that ticket be specified for related tickets `get_url`
|
||||
- **access**: Add missing `table_fields` attribute to team users model
|
||||
- **api**: during metadata navigation permission checks, cater for non-existant keys
|
||||
- **core**: Remove superfluous check from ticket viewset
|
||||
- **access**: Team permissions is not a required field
|
||||
- **core**: History query must also be for self, not just children
|
||||
- **access**: correct team users table to correct data key
|
||||
|
||||
### Refactoring
|
||||
|
||||
- **access**: Settings must be an available permissions when setting team permissions
|
||||
- **itam**: set deviceoperatingsystem model, device field to be type `onetoone`
|
||||
- **assistance**: make content the first tab for kb articles
|
||||
- **api**: move metadata url_return -> urls.self
|
||||
|
||||
### Tests
|
||||
|
||||
- **api**: Nav menu permission checks for settings
|
||||
- **api**: Nav menu permission checks
|
||||
- **core**: Correct `url.self`checks to use list view
|
||||
- **core**: Dont test History for table view
|
||||
- **settings**: Dont test user settings for table view
|
||||
- **steeings**: Dont test app settings for table view
|
||||
- **core**: Dont test related ticket for table or detail view
|
||||
- **api**: Refactor test so that endpoints not expected to have an endpoint or be rendered in a table wont be tested for it.
|
||||
- **settings**: API Metadata checks for user settings
|
||||
- **settings**: API Metadata checks for external links
|
||||
- **settings**: API Metadata checks for app settings
|
||||
- **project_management**: API Metadata checks for project type
|
||||
- **project_management**: API Metadata checks for project task
|
||||
- **project_management**: API Metadata checks for project state
|
||||
- **project_management**: API Metadata checks for project milestone
|
||||
- **project_management**: API Metadata checks for project
|
||||
- **itim**: API Metadata checks for problem ticket
|
||||
- **itim**: API Metadata checks for incident ticket
|
||||
- **itim**: API Metadata checks for change ticket
|
||||
- **itim**: API Metadata checks for service
|
||||
- **itim**: API Metadata checks for port
|
||||
- **itim**: API Metadata checks for cluster type
|
||||
- **itim**: API Metadata checks for cluster
|
||||
- **itam**: API Metadata checks for software version
|
||||
- **itam**: API Metadata checks for software category
|
||||
- **itam**: API Metadata checks for software
|
||||
- **itam**: API Metadata checks for operating system version
|
||||
- **itam**: API Metadata checks for operating system
|
||||
- **itam**: API Metadata checks for software
|
||||
- **itam**: API Metadata checks for operating system
|
||||
- **itam**: API Metadata checks for device type
|
||||
- **itam**: API Metadata checks for device OS
|
||||
- **itam**: API Metadata checks for device model
|
||||
- **itam**: API Metadata checks for device
|
||||
- **core**: API Metadata checks for ticket comment category
|
||||
- **core**: API Metadata checks for ticket comment
|
||||
- **core**: API Metadata checks for ticket category
|
||||
- **core**: API Metadata checks for history
|
||||
- **core**: API Metadata checks for related tickets
|
||||
- **core**: API Metadata checks for manufacturers
|
||||
- **config_management**: API Metadata checks for config group software
|
||||
- **config_management**: API Metadata checks for config groups
|
||||
- **access**: API Metadata checks for request ticket
|
||||
- **access**: API Metadata checks for kb category
|
||||
- **access**: API Metadata checks for kb
|
||||
- **api**: correct metadata testcases
|
||||
- **access**: API Metadata checks for organization
|
||||
- **api**: API Metadata test cases for navigation menu rendering
|
||||
- **api**: correct logic for test class attribute fetching
|
||||
- **access**: API Metadata checks for Team User model
|
||||
- **access**: API Metadata checks for Team model
|
||||
- **api**: API Metadata functional Test Cases
|
||||
|
||||
## 1.4.1 (2024-11-30)
|
||||
|
||||
### Fixes
|
||||
|
@ -1,3 +1,11 @@
|
||||
## Version 1.5.0
|
||||
|
||||
- When v1.4.0 was release the migrations were not merged. As part of the work conducted on this release the v1.4 migrations have been squashed. This should not have any effect on any system that when they updated to v1.4, they ran the migrations and they **completed successfully**. Upgrading from <1.4.0 to this release should also have no difficulties as the migrations required still exist. There are less of them, however with more work per migration.
|
||||
|
||||
!!! Note
|
||||
If you require the previously squashed migrations for what ever reason. Clone the repo and go to commit 17f47040d6737905a1769eee5c45d9d15339fdbf, which is the commit prior to the squashing which is commit ca2da06d2cd393cabb7e172ad47dfb2dd922d952.
|
||||
|
||||
|
||||
## Version 1.4.0
|
||||
|
||||
API redesign in preparation for moving the UI out of centurion to it's [own project](https://github.com/nofusscomputing/centurion_erp_ui). This release introduces a **Feature freeze** to the current UI. Only bug fixes will be done for the current UI.
|
||||
|
@ -1,5 +1,6 @@
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
|
||||
from .models import *
|
||||
|
||||
@ -25,6 +26,40 @@ class OrganizationAdmin(admin.ModelAdmin):
|
||||
list_filter = ["created"]
|
||||
search_fields = ["team_name"]
|
||||
|
||||
|
||||
admin.site.register(Organization,OrganizationAdmin)
|
||||
|
||||
|
||||
class TeamUserInline(admin.TabularInline):
|
||||
model = TeamUsers
|
||||
extra = 0
|
||||
|
||||
readonly_fields = ['created', 'modified']
|
||||
fields = ['team']
|
||||
|
||||
fk_name = 'user'
|
||||
|
||||
|
||||
admin.site.unregister(User)
|
||||
class UsrAdmin(UserAdmin):
|
||||
|
||||
fieldsets = (
|
||||
(None, {"fields": ("username", "password")}),
|
||||
("Personal info", {"fields": ("first_name", "last_name", "email")}),
|
||||
(
|
||||
"Permissions",
|
||||
{
|
||||
"fields": (
|
||||
"is_active",
|
||||
"is_staff",
|
||||
"is_superuser",
|
||||
),
|
||||
|
||||
},
|
||||
),
|
||||
("Important dates", {"fields": ("last_login", "date_joined")}),
|
||||
)
|
||||
|
||||
inlines = [TeamUserInline]
|
||||
|
||||
admin.site.register(User,UsrAdmin)
|
||||
|
||||
|
@ -23,8 +23,6 @@ def permission_queryset():
|
||||
'chordcounter',
|
||||
'comment',
|
||||
'groupresult',
|
||||
'organization'
|
||||
'settings',
|
||||
'usersettings',
|
||||
]
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 15:27
|
||||
# Generated by Django 5.1.2 on 2024-12-06 06:47
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
@ -9,11 +9,23 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0002_alter_team_options'),
|
||||
('access', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='organization',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Organization', 'verbose_name_plural': 'Organizations'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='team',
|
||||
options={'ordering': ['team_name'], 'verbose_name': 'Team', 'verbose_name_plural': 'Teams'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='teamusers',
|
||||
options={'ordering': ['user'], 'verbose_name': 'Team User', 'verbose_name_plural': 'Team Users'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='organization',
|
||||
name='id',
|
||||
@ -47,11 +59,31 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='team_name',
|
||||
field=models.CharField(default='', help_text='Name to give this team', max_length=50, verbose_name='Name'),
|
||||
field=models.CharField(help_text='Name to give this team', max_length=50, verbose_name='Name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='teamusers',
|
||||
name='id',
|
||||
field=models.AutoField(help_text='ID of this Team User', primary_key=True, serialize=False, unique=True, verbose_name='ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='teamusers',
|
||||
name='manager',
|
||||
field=models.BooleanField(blank=True, default=False, help_text='Is this user to be a manager of this team', verbose_name='manager'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='teamusers',
|
||||
name='team',
|
||||
field=models.ForeignKey(help_text='Team user belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='team', to='access.team', verbose_name='Team'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='teamusers',
|
||||
name='user',
|
||||
field=models.ForeignKey(help_text='User who will be added to the team', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 06:42
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='team',
|
||||
options={'ordering': ['team_name'], 'verbose_name': 'Team', 'verbose_name_plural': 'Teams'},
|
||||
),
|
||||
]
|
@ -1,44 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-16 06:54
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0003_alter_organization_id_alter_organization_manager_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='organization',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Organization', 'verbose_name_plural': 'Organizations'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='teamusers',
|
||||
options={'ordering': ['user'], 'verbose_name': 'Team User', 'verbose_name_plural': 'Team Users'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='teamusers',
|
||||
name='id',
|
||||
field=models.AutoField(help_text='ID of this Team User', primary_key=True, serialize=False, unique=True, verbose_name='ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='teamusers',
|
||||
name='manager',
|
||||
field=models.BooleanField(blank=True, default=False, help_text='Is this user to be a manager of this team', verbose_name='manager'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='teamusers',
|
||||
name='team',
|
||||
field=models.ForeignKey(help_text='Team user belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='team', to='access.team', verbose_name='Team'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='teamusers',
|
||||
name='user',
|
||||
field=models.ForeignKey(help_text='User who will be added to the team', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='User'),
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-07 06:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0004_alter_organization_options_alter_teamusers_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='team_name',
|
||||
field=models.CharField(help_text='Name to give this team', max_length=50, verbose_name='Name'),
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-20 02:41
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0005_alter_team_team_name'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
]
|
@ -369,7 +369,7 @@ class Team(Group, TenancyObject):
|
||||
{
|
||||
"layout": "table",
|
||||
"name": "Users",
|
||||
"field": "user",
|
||||
"field": "users",
|
||||
},
|
||||
]
|
||||
},
|
||||
@ -489,7 +489,10 @@ class TeamUsers(SaveHistory):
|
||||
|
||||
page_layout: list = []
|
||||
|
||||
table_fields: list = []
|
||||
table_fields: list = [
|
||||
'user',
|
||||
'manager'
|
||||
]
|
||||
|
||||
|
||||
def delete(self, using=None, keep_parents=False):
|
||||
|
@ -4,10 +4,10 @@ from rest_framework import serializers
|
||||
|
||||
from access.models import Organization
|
||||
|
||||
from api.serializers import common
|
||||
|
||||
from app.serializers.user import UserBaseSerializer
|
||||
|
||||
from core import fields as centurion_field
|
||||
|
||||
|
||||
|
||||
class OrganizationBaseSerializer(serializers.ModelSerializer):
|
||||
@ -43,7 +43,6 @@ class OrganizationBaseSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
class OrganizationModelSerializer(
|
||||
common.CommonModelSerializer,
|
||||
OrganizationBaseSerializer
|
||||
):
|
||||
|
||||
@ -56,6 +55,7 @@ class OrganizationModelSerializer(
|
||||
'teams': reverse("v2:_api_v2_organization_team-list", request=self._context['view'].request, kwargs={'organization_id': item.pk}),
|
||||
}
|
||||
|
||||
model_notes = centurion_field.MarkdownField( required = False )
|
||||
|
||||
class Meta:
|
||||
|
||||
|
@ -6,9 +6,10 @@ from access.models import Team
|
||||
|
||||
from api.serializers import common
|
||||
|
||||
from access.functions.permissions import permission_queryset
|
||||
from access.serializers.organization import OrganizationBaseSerializer
|
||||
|
||||
from app.serializers.permission import PermissionBaseSerializer
|
||||
from app.serializers.permission import Permission, PermissionBaseSerializer
|
||||
|
||||
from core import fields as centurion_field
|
||||
|
||||
@ -74,6 +75,7 @@ class TeamModelSerializer(
|
||||
|
||||
team_name = centurion_field.CharField( autolink = True )
|
||||
|
||||
permissions = serializers.PrimaryKeyRelatedField(many = True, queryset=permission_queryset(), required = False)
|
||||
|
||||
class Meta:
|
||||
|
||||
|
@ -13,6 +13,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
|
||||
|
||||
@ -278,3 +279,16 @@ class OrganizationViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class OrganizationMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'access'
|
||||
|
||||
menu_entry_id = 'organization'
|
@ -6,10 +6,13 @@ import requests
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
|
||||
@ -199,3 +202,100 @@ class TeamViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TeamMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase,
|
||||
|
||||
):
|
||||
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_has_key_urls_back(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned has key `urls.back`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert 'back' in response.data['urls']
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_urls_back_is_str(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data key `urls.back` is str
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert type(response.data['urls']['back']) is str
|
||||
|
||||
|
||||
|
||||
def test_method_options_request_list_data_has_key_urls_return_url(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned has key `urls.return_url`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if self.url_kwargs:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert 'return_url' in response.data['urls']
|
||||
|
||||
|
||||
def test_method_options_request_list_data_key_urls_return_url_is_str(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data key `urls.return_url` is str
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if self.url_kwargs:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert type(response.data['urls']['return_url']) is str
|
||||
|
||||
|
||||
|
@ -6,10 +6,13 @@ import requests
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
|
||||
@ -208,4 +211,99 @@ class TeamUserViewSet(
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TeamUserMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase,
|
||||
|
||||
):
|
||||
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_has_key_urls_back(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned has key `urls.back`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert 'back' in response.data['urls']
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_urls_back_is_str(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data key `urls.back` is str
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert type(response.data['urls']['back']) is str
|
||||
|
||||
|
||||
|
||||
def test_method_options_request_list_data_has_key_urls_return_url(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned has key `urls.return_url`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if self.url_kwargs:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert 'return_url' in response.data['urls']
|
||||
|
||||
|
||||
def test_method_options_request_list_data_key_urls_return_url_is_str(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data key `urls.return_url` is str
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if self.url_kwargs:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert type(response.data['urls']['return_url']) is str
|
||||
|
@ -1,5 +1,7 @@
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiResponse
|
||||
|
||||
from access.models import Organization
|
||||
|
||||
from access.serializers.teams import (
|
||||
Team,
|
||||
TeamModelSerializer,
|
||||
@ -124,6 +126,24 @@ class ViewSet( ModelViewSet ):
|
||||
|
||||
view_description = 'Teams belonging to a single organization'
|
||||
|
||||
|
||||
def get_back_url(self) -> str:
|
||||
|
||||
if(
|
||||
getattr(self, '_back_url', None) is None
|
||||
):
|
||||
|
||||
return_model = Organization.objects.get(
|
||||
pk = self.kwargs['organization_id']
|
||||
)
|
||||
|
||||
self._back_url = str(
|
||||
return_model.get_url( self.request )
|
||||
)
|
||||
|
||||
return self._back_url
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
queryset = super().get_queryset()
|
||||
@ -146,3 +166,21 @@ class ViewSet( ModelViewSet ):
|
||||
|
||||
return globals()[str( self.model._meta.verbose_name) + 'ModelSerializer']
|
||||
|
||||
|
||||
def get_return_url(self) -> str:
|
||||
|
||||
if getattr(self, '_get_return_url', None):
|
||||
|
||||
return self._get_return_url
|
||||
|
||||
if self.kwargs.get('pk', None) is None:
|
||||
|
||||
return_model = Organization.objects.get(
|
||||
pk = self.kwargs['organization_id']
|
||||
)
|
||||
|
||||
self._get_return_url = return_model.get_url( self.request )
|
||||
|
||||
return self._get_return_url
|
||||
|
||||
return None
|
||||
|
@ -1,5 +1,7 @@
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiResponse
|
||||
|
||||
from access.models import Team
|
||||
|
||||
from access.serializers.team_user import (
|
||||
TeamUsers,
|
||||
TeamUserModelSerializer,
|
||||
@ -147,6 +149,24 @@ class ViewSet( ModelViewSet ):
|
||||
|
||||
view_description = 'Users belonging to a single team'
|
||||
|
||||
|
||||
def get_back_url(self) -> str:
|
||||
|
||||
if(
|
||||
getattr(self, '_back_url', None) is None
|
||||
):
|
||||
|
||||
return_model =Team.objects.get(
|
||||
pk = self.kwargs['team_id']
|
||||
)
|
||||
|
||||
self._back_url = str(
|
||||
return_model.get_url( self.request )
|
||||
)
|
||||
|
||||
return self._back_url
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
queryset = super().get_queryset()
|
||||
@ -172,3 +192,21 @@ class ViewSet( ModelViewSet ):
|
||||
|
||||
return globals()[str( self.model._meta.verbose_name).replace(' ', '') + 'ModelSerializer']
|
||||
|
||||
|
||||
def get_return_url(self):
|
||||
|
||||
if getattr(self, '_get_return_url', None):
|
||||
|
||||
return self._get_return_url
|
||||
|
||||
if self.kwargs.get('pk', None) is None:
|
||||
|
||||
return_model = Team.objects.get(
|
||||
pk = self.kwargs['team_id']
|
||||
)
|
||||
|
||||
self._get_return_url = return_model.get_url( self.request )
|
||||
|
||||
return self._get_return_url
|
||||
|
||||
return None
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 15:27
|
||||
# Generated by Django 5.1.2 on 2024-12-06 06:47
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
|
@ -1,5 +1,8 @@
|
||||
from django.conf import settings
|
||||
from django.utils.encoding import force_str
|
||||
|
||||
from django.contrib.auth.models import ContentType, Permission
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework_json_api.metadata import JSONAPIMetadata
|
||||
from rest_framework.request import clone_request
|
||||
@ -64,23 +67,39 @@ class ReactUIMetadata(OverRideJSONAPIMetadata):
|
||||
|
||||
metadata["description"] = view.get_view_description()
|
||||
|
||||
if 'pk' in view.kwargs:
|
||||
metadata['urls']: dict = {}
|
||||
|
||||
if view.kwargs['pk']:
|
||||
url_self = None
|
||||
|
||||
qs = view.get_queryset()[0]
|
||||
|
||||
if hasattr(qs, 'get_url'):
|
||||
if view.kwargs.get('pk', None) is not None:
|
||||
|
||||
qs = view.get_queryset()[0]
|
||||
|
||||
if hasattr(qs, 'get_url'):
|
||||
|
||||
url_self = qs.get_url( request=request )
|
||||
|
||||
metadata['return_url'] = qs.get_url( request )
|
||||
|
||||
elif view.kwargs:
|
||||
|
||||
metadata['return_url'] = reverse('v2:' + view.basename + '-list', request = view.request, kwargs = view.kwargs )
|
||||
url_self = reverse('v2:' + view.basename + '-list', request = view.request, kwargs = view.kwargs )
|
||||
|
||||
else:
|
||||
|
||||
metadata['return_url'] = reverse('v2:' + view.basename + '-list', request = view.request )
|
||||
url_self = reverse('v2:' + view.basename + '-list', request = view.request )
|
||||
|
||||
if url_self:
|
||||
|
||||
metadata['urls'].update({'self': url_self})
|
||||
|
||||
if view.get_back_url():
|
||||
|
||||
metadata['urls'].update({'back': view.get_back_url()})
|
||||
|
||||
if view.get_return_url():
|
||||
|
||||
metadata['urls'].update({'return_url': view.get_return_url()})
|
||||
|
||||
|
||||
metadata["renders"] = [
|
||||
@ -123,136 +142,33 @@ class ReactUIMetadata(OverRideJSONAPIMetadata):
|
||||
metadata['layout'] = view.get_page_layout()
|
||||
|
||||
|
||||
metadata['navigation'] = [
|
||||
{
|
||||
"display_name": "Access",
|
||||
"name": "access",
|
||||
"pages": [
|
||||
{
|
||||
"display_name": "Organization",
|
||||
"name": "organization",
|
||||
"link": "/access/organization"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"display_name": "Assistance",
|
||||
"name": "assistance",
|
||||
"pages": [
|
||||
{
|
||||
"display_name": "Requests",
|
||||
"name": "request",
|
||||
"icon": "ticket_request",
|
||||
"link": "/assistance/ticket/request"
|
||||
},
|
||||
{
|
||||
"display_name": "Knowledge Base",
|
||||
"name": "knowledge_base",
|
||||
"icon": "information",
|
||||
"link": "/assistance/knowledge_base"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"display_name": "ITAM",
|
||||
"name": "itam",
|
||||
"pages": [
|
||||
{
|
||||
"display_name": "Devices",
|
||||
"name": "device",
|
||||
"icon": "device",
|
||||
"link": "/itam/device"
|
||||
},
|
||||
{
|
||||
"display_name": "Operating System",
|
||||
"name": "operating_system",
|
||||
"link": "/itam/operating_system"
|
||||
},
|
||||
{
|
||||
"display_name": "Software",
|
||||
"name": "software",
|
||||
"link": "/itam/software"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"display_name": "ITIM",
|
||||
"name": "itim",
|
||||
"pages": [
|
||||
{
|
||||
"display_name": "Changes",
|
||||
"name": "ticket_change",
|
||||
"link": "/itim/ticket/change"
|
||||
},
|
||||
{
|
||||
"display_name": "Clusters",
|
||||
"name": "cluster",
|
||||
"link": "/itim/cluster"
|
||||
},
|
||||
{
|
||||
"display_name": "Incidents",
|
||||
"name": "ticket_incident",
|
||||
"link": "/itim/ticket/incident"
|
||||
},
|
||||
{
|
||||
"display_name": "Problems",
|
||||
"name": "ticket_problem",
|
||||
"link": "/itim/ticket/problem"
|
||||
},
|
||||
{
|
||||
"display_name": "Services",
|
||||
"name": "service",
|
||||
"link": "/itim/service"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"display_name": "Config Management",
|
||||
"name": "config_management",
|
||||
"icon": "ansible",
|
||||
"pages": [
|
||||
{
|
||||
"display_name": "Groups",
|
||||
"name": "group",
|
||||
"icon": 'config_management',
|
||||
"link": "/config_management/group"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"display_name": "Project Management",
|
||||
"name": "project_management",
|
||||
"icon": 'project',
|
||||
"pages": [
|
||||
{
|
||||
"display_name": "Projects",
|
||||
"name": "project",
|
||||
"icon": 'kanban',
|
||||
"link": "/project_management/project"
|
||||
}
|
||||
]
|
||||
},
|
||||
build_repo: str = None
|
||||
|
||||
{
|
||||
"display_name": "Settings",
|
||||
"name": "settings",
|
||||
"pages": [
|
||||
{
|
||||
"display_name": "System",
|
||||
"name": "setting",
|
||||
"icon": "system",
|
||||
"link": "/settings"
|
||||
},
|
||||
{
|
||||
"display_name": "Task Log",
|
||||
"name": "celery_log",
|
||||
# "icon": "settings",
|
||||
"link": "/settings/celery_log"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
if settings.BUILD_REPO:
|
||||
|
||||
build_repo = settings.BUILD_REPO
|
||||
|
||||
build_sha: str = None
|
||||
|
||||
if settings.BUILD_SHA:
|
||||
|
||||
build_sha = settings.BUILD_SHA
|
||||
|
||||
build_version: str = 'development'
|
||||
|
||||
if settings.BUILD_VERSION:
|
||||
|
||||
build_version = settings.BUILD_VERSION
|
||||
|
||||
|
||||
metadata['version']: dict = {
|
||||
'project_url': build_repo,
|
||||
'sha': build_sha,
|
||||
'version': build_version,
|
||||
}
|
||||
|
||||
|
||||
metadata['navigation'] = self.get_navigation(request.user)
|
||||
|
||||
return metadata
|
||||
|
||||
@ -334,7 +250,6 @@ class ReactUIMetadata(OverRideJSONAPIMetadata):
|
||||
field_info["children"] = self.get_serializer_info(field)
|
||||
|
||||
if (
|
||||
# not field_info.get("read_only")
|
||||
hasattr(field, "choices")
|
||||
):
|
||||
field_info["choices"] = [
|
||||
@ -353,4 +268,238 @@ class ReactUIMetadata(OverRideJSONAPIMetadata):
|
||||
field.field_name in serializer.included_serializers
|
||||
)
|
||||
|
||||
return field_info
|
||||
return field_info
|
||||
|
||||
|
||||
_nav = {
|
||||
'access': {
|
||||
"display_name": "Access",
|
||||
"name": "access",
|
||||
"pages": {
|
||||
'view_organization': {
|
||||
"display_name": "Organization",
|
||||
"name": "organization",
|
||||
"link": "/access/organization"
|
||||
}
|
||||
}
|
||||
},
|
||||
'assistance': {
|
||||
"display_name": "Assistance",
|
||||
"name": "assistance",
|
||||
"pages": {
|
||||
'core.view_ticket_request': {
|
||||
"display_name": "Requests",
|
||||
"name": "request",
|
||||
"icon": "ticket_request",
|
||||
"link": "/assistance/ticket/request"
|
||||
},
|
||||
'view_knowledgebase': {
|
||||
"display_name": "Knowledge Base",
|
||||
"name": "knowledge_base",
|
||||
"icon": "information",
|
||||
"link": "/assistance/knowledge_base"
|
||||
}
|
||||
}
|
||||
},
|
||||
'itam': {
|
||||
"display_name": "ITAM",
|
||||
"name": "itam",
|
||||
"pages": {
|
||||
'view_device': {
|
||||
"display_name": "Devices",
|
||||
"name": "device",
|
||||
"icon": "device",
|
||||
"link": "/itam/device"
|
||||
},
|
||||
'view_operatingsystem': {
|
||||
"display_name": "Operating System",
|
||||
"name": "operating_system",
|
||||
"link": "/itam/operating_system"
|
||||
},
|
||||
'view_software': {
|
||||
"display_name": "Software",
|
||||
"name": "software",
|
||||
"link": "/itam/software"
|
||||
}
|
||||
}
|
||||
},
|
||||
'itim': {
|
||||
"display_name": "ITIM",
|
||||
"name": "itim",
|
||||
"pages": {
|
||||
'core.view_ticket_change': {
|
||||
"display_name": "Changes",
|
||||
"name": "ticket_change",
|
||||
"link": "/itim/ticket/change"
|
||||
},
|
||||
'view_cluster': {
|
||||
"display_name": "Clusters",
|
||||
"name": "cluster",
|
||||
"link": "/itim/cluster"
|
||||
},
|
||||
'core.view_ticket_incident': {
|
||||
"display_name": "Incidents",
|
||||
"name": "ticket_incident",
|
||||
"link": "/itim/ticket/incident"
|
||||
},
|
||||
'core.view_ticket_problem': {
|
||||
"display_name": "Problems",
|
||||
"name": "ticket_problem",
|
||||
"link": "/itim/ticket/problem"
|
||||
},
|
||||
'view_service': {
|
||||
"display_name": "Services",
|
||||
"name": "service",
|
||||
"link": "/itim/service"
|
||||
},
|
||||
}
|
||||
},
|
||||
'config_management': {
|
||||
"display_name": "Config Management",
|
||||
"name": "config_management",
|
||||
"icon": "ansible",
|
||||
"pages": {
|
||||
'view_configgroups': {
|
||||
"display_name": "Groups",
|
||||
"name": "group",
|
||||
"icon": 'config_management',
|
||||
"link": "/config_management/group"
|
||||
}
|
||||
}
|
||||
},
|
||||
'project_management': {
|
||||
"display_name": "Project Management",
|
||||
"name": "project_management",
|
||||
"icon": 'project',
|
||||
"pages": {
|
||||
'view_project': {
|
||||
"display_name": "Projects",
|
||||
"name": "project",
|
||||
"icon": 'kanban',
|
||||
"link": "/project_management/project"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
'settings': {
|
||||
"display_name": "Settings",
|
||||
"name": "settings",
|
||||
"pages": {
|
||||
'all_settings': {
|
||||
"display_name": "System",
|
||||
"name": "setting",
|
||||
"icon": "system",
|
||||
"link": "/settings"
|
||||
},
|
||||
'django_celery_results.view_taskresult': {
|
||||
"display_name": "Task Log",
|
||||
"name": "celery_log",
|
||||
# "icon": "settings",
|
||||
"link": "/settings/celery_log"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def get_navigation(self, user) -> list(dict()):
|
||||
"""Render the navigation menu
|
||||
|
||||
Check the users permissions agains `_nav`. if they have the permission, add the
|
||||
menu entry to the navigation to be rendered,
|
||||
|
||||
**No** Menu is to be rendered that contains no menu entries.
|
||||
|
||||
Args:
|
||||
user (User): User object from the request.
|
||||
|
||||
Returns:
|
||||
list(dict()): Rendered navigation menu in the format the UI requires it to be.
|
||||
"""
|
||||
|
||||
nav: list = []
|
||||
|
||||
processed_permissions: dict = {}
|
||||
|
||||
for group in user.groups.all():
|
||||
|
||||
for permission in group.permissions.all():
|
||||
|
||||
if str(permission.codename).startswith('view_'):
|
||||
|
||||
|
||||
if not processed_permissions.get(permission.content_type.app_label, None):
|
||||
|
||||
processed_permissions.update({permission.content_type.app_label: {}})
|
||||
|
||||
if permission.codename not in processed_permissions[permission.content_type.app_label]:
|
||||
|
||||
processed_permissions[permission.content_type.app_label].update({str(permission.codename): '_'})
|
||||
|
||||
view_settings: list = [
|
||||
'assistance.view_knowledgebasecategory',
|
||||
'core.view_manufacturer',
|
||||
'core.view_ticketcategory',
|
||||
'core.view_ticketcommentcategory',
|
||||
'itam.view_devicemodel',
|
||||
'itam.view_devicetype',
|
||||
'itam.view_softwarecategory',
|
||||
'itim.view_clustertype',
|
||||
'project_management.view_projectstate',
|
||||
'project_management.view_projecttype',
|
||||
'settings.view_appsettings',
|
||||
]
|
||||
|
||||
for app, entry in self._nav.items():
|
||||
|
||||
new_menu_entry: dict = {}
|
||||
|
||||
new_pages: list = []
|
||||
|
||||
# if processed_permissions.get(app, None): # doesn't cater for `.` in perm
|
||||
|
||||
for permission, page in entry['pages'].items():
|
||||
|
||||
if permission == 'all_settings':
|
||||
|
||||
for setting_permission in view_settings:
|
||||
|
||||
app_permission = str(setting_permission).split('.')
|
||||
|
||||
if processed_permissions.get(app_permission[0], None):
|
||||
|
||||
if processed_permissions[app_permission[0]].get(app_permission[1], None):
|
||||
|
||||
new_pages += [ page ]
|
||||
|
||||
break
|
||||
|
||||
|
||||
elif '.' in permission:
|
||||
|
||||
app_permission = str(permission).split('.')
|
||||
|
||||
if processed_permissions.get(app_permission[0], None):
|
||||
|
||||
if processed_permissions[app_permission[0]].get(app_permission[1], None):
|
||||
|
||||
new_pages += [ page ]
|
||||
|
||||
else:
|
||||
|
||||
if processed_permissions.get(app, None):
|
||||
|
||||
if processed_permissions[app].get(permission, None):
|
||||
|
||||
new_pages += [ page ]
|
||||
|
||||
|
||||
if len(new_pages) > 0:
|
||||
|
||||
new_menu_entry = entry.copy()
|
||||
|
||||
new_menu_entry.update({ 'pages': new_pages })
|
||||
|
||||
nav += [ new_menu_entry ]
|
||||
|
||||
return nav
|
||||
|
@ -1,8 +1,32 @@
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from access.serializers.organization import Organization, OrganizationBaseSerializer
|
||||
|
||||
from core import fields as centurion_field
|
||||
|
||||
from settings.models.app_settings import AppSettings
|
||||
|
||||
|
||||
class OrganizationField(serializers.PrimaryKeyRelatedField):
|
||||
|
||||
def get_queryset(self):
|
||||
""" Queryset Override
|
||||
|
||||
Override the base serializer and filter out the `global_organization`
|
||||
if defined.
|
||||
"""
|
||||
|
||||
app_settings = AppSettings.objects.all()
|
||||
|
||||
queryset = Organization.objects.all()
|
||||
|
||||
if getattr(app_settings[0], 'global_organization', None):
|
||||
|
||||
queryset = queryset.exclude(id=app_settings[0].global_organization.id)
|
||||
|
||||
return queryset
|
||||
|
||||
|
||||
|
||||
class CommonBaseSerializer(serializers.ModelSerializer):
|
||||
@ -12,5 +36,18 @@ class CommonBaseSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
class CommonModelSerializer(CommonBaseSerializer):
|
||||
"""Common Model Serializer
|
||||
|
||||
model_notes = centurion_field.MarkdownField( required = False )
|
||||
_**Note:** This serializer is not inherited by the organization Serializer_
|
||||
_`access.serializers.organization`, this is by design_
|
||||
|
||||
This serializer is included within ALL model (Tenancy Model) serilaizers and is intended to be used
|
||||
to add objects that ALL model serializers will require.
|
||||
|
||||
Args:
|
||||
CommonBaseSerializer (Class): Common base serializer
|
||||
"""
|
||||
|
||||
model_notes = centurion_field.MarkdownField( required = False )
|
||||
|
||||
organization = OrganizationField(required = False)
|
889
app/api/tests/abstract/test_metadata_functional.py
Normal file
889
app/api/tests/abstract/test_metadata_functional.py
Normal file
@ -0,0 +1,889 @@
|
||||
import pytest
|
||||
from django.test import Client
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
|
||||
|
||||
class MetadataAttributesFunctionalBase:
|
||||
""" Functional Tests for API, HTTP/Options Method
|
||||
|
||||
These tests ensure that **ALL** serializers include the metaclass that adds the required
|
||||
data to the HTTP Options method.
|
||||
|
||||
Metaclass adds data required for the UI to function correctly.
|
||||
"""
|
||||
|
||||
app_namespace: str = None
|
||||
|
||||
url_name: str = None
|
||||
|
||||
viewset_type: str = 'list'
|
||||
|
||||
|
||||
def test_method_options_request_list_ok(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request returns `OK`.
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type, kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type)
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_method_options_request_list_data_returned(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request returns data.
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type, kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type)
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert response.data is not None
|
||||
|
||||
|
||||
def test_method_options_request_list_data_type(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned is of type `dict`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type, kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type)
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert type(response.data) is dict
|
||||
|
||||
|
||||
def test_method_options_request_detail_ok(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request returns `OK`.
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type, kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type)
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_returned(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request returns data.
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert response.data is not None
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_type(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned is of type `dict`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert type(response.data) is dict
|
||||
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_has_key_urls(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned has key `urls`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert 'urls' in response.data
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_urls_is_dict(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data key `urls` is dict
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert type(response.data['urls']) is dict
|
||||
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_has_key_urls_self(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned has key `urls.self`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert 'urls' in response.data
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_urls_self_is_str(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data key `urls.self` is a string
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert type(response.data['urls']['self']) is str
|
||||
|
||||
|
||||
@pytest.mark.skip(reason='to be written')
|
||||
def test_method_options_no_field_is_generic(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Fields are used for the UI to setup inputs correctly.
|
||||
|
||||
Ensure all fields at path `.actions.<METHOD>.<name>.type` do not have `GenericField` as the value.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class MetadataAttributesFunctionalTable:
|
||||
"""Test cases for Metadata
|
||||
|
||||
These test cases are for models that are expected to
|
||||
be rendered in a table.
|
||||
"""
|
||||
|
||||
|
||||
def test_method_options_request_list_data_has_key_table_fields(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned has key `table_fields`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type, kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type)
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert 'table_fields' in response.data
|
||||
|
||||
|
||||
def test_method_options_request_list_data_key_table_fields_is_list(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data['table_fields'] is of type `list`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type, kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type)
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
assert type(response.data['table_fields']) is list
|
||||
|
||||
|
||||
def test_method_options_request_list_data_key_table_fields_is_list_of_str(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data['table_fields'] list is of `str`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type, kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-' + self.viewset_type)
|
||||
|
||||
response = client.options( url, content_type='application/json' )
|
||||
|
||||
all_string = True
|
||||
|
||||
for item in response.data['table_fields']:
|
||||
|
||||
if type(item) is not str:
|
||||
|
||||
all_string = False
|
||||
|
||||
|
||||
assert all_string
|
||||
|
||||
|
||||
|
||||
class MetadataAttributesFunctionalEndpoint:
|
||||
"""Test cases for Metadata
|
||||
|
||||
These test cases are for models that will have an
|
||||
endpoint. i.e. A Detail view
|
||||
"""
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_has_key_page_layout(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data returned has key `layout`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert 'layout' in response.data
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_page_layout_is_list(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data['layout'] is of type `list`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert type(response.data['layout']) is list
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_page_layout_is_list_of_dict(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data['layout'] list is of `dict`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
all_dict = True
|
||||
|
||||
for item in response.data['layout']:
|
||||
|
||||
if type(item) is not dict:
|
||||
|
||||
all_dict = False
|
||||
|
||||
|
||||
assert all_dict
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_page_layout_dicts_key_exists_name(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data['layout'].x has key `name`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
has_key = True
|
||||
|
||||
for item in response.data['layout']:
|
||||
|
||||
if 'name' not in item:
|
||||
|
||||
has_key = False
|
||||
|
||||
|
||||
assert has_key
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_page_layout_dicts_key_type_name(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data['layout'].x.[name] is of type `str`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
all_are_str = True
|
||||
|
||||
for item in response.data['layout']:
|
||||
|
||||
if type(item['name']) is not str:
|
||||
|
||||
all_are_str = False
|
||||
|
||||
|
||||
assert all_are_str
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_page_layout_dicts_key_exists_sections(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data['layout'].x has key `sections`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
has_key = True
|
||||
|
||||
for item in response.data['layout']:
|
||||
|
||||
if 'sections' not in item:
|
||||
|
||||
has_key = False
|
||||
|
||||
|
||||
assert has_key
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_page_layout_dicts_key_type_sections(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
Ensure the request data['layout'].x.[sections] is of type `list`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-detail',
|
||||
kwargs=self.url_view_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
all_are_str = True
|
||||
|
||||
for item in response.data['layout']:
|
||||
|
||||
if type(item['sections']) is not list:
|
||||
|
||||
all_are_str = False
|
||||
|
||||
|
||||
assert all_are_str
|
||||
|
||||
|
||||
|
||||
class MetadataAttributesFunctional(
|
||||
MetadataAttributesFunctionalEndpoint,
|
||||
MetadataAttributesFunctionalTable,
|
||||
MetadataAttributesFunctionalBase,
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class MetaDataNavigationEntriesFunctional:
|
||||
""" Test cases for the Navigation menu
|
||||
|
||||
Navigation menu is rendered as part of the API when a HTTP/OPTIONS
|
||||
request has been made. Each menu entry requires that a user has View
|
||||
permissions for that entry to be visible.
|
||||
|
||||
**No** menu entry is to be returned for **any** user whom does not
|
||||
have the corresponding view permission.
|
||||
|
||||
These test cases are for any model that has a navigation menu entry.
|
||||
|
||||
## Tests
|
||||
|
||||
- Ensure add user does not have menu entry
|
||||
- Ensure change user does not have menu entry
|
||||
- Ensure delete user does not have menu entry
|
||||
- Ensure the view user has menu entry
|
||||
- No menu to return without pages for add user
|
||||
- No menu to return without pages for change user
|
||||
- No menu to return without pages for delete user
|
||||
- No menu to return without pages for view user
|
||||
"""
|
||||
|
||||
menu_id: str = None
|
||||
""" Name of the Menu entry
|
||||
|
||||
Match for .navigation[i][name]
|
||||
"""
|
||||
|
||||
menu_entry_id: str = None
|
||||
"""Name of the menu entry
|
||||
|
||||
Match for .navigation[i][pages][i][name]
|
||||
"""
|
||||
|
||||
app_namespace:str = None
|
||||
"""application namespace"""
|
||||
|
||||
url_name: str = None
|
||||
"""url name"""
|
||||
|
||||
url_kwargs: dict = None
|
||||
"""View URL kwargs"""
|
||||
|
||||
add_user = None
|
||||
""" User with add permission"""
|
||||
|
||||
change_user = None
|
||||
""" User with change permission"""
|
||||
|
||||
delete_user = None
|
||||
""" User with delete permission"""
|
||||
|
||||
view_user = None
|
||||
""" User with view permission"""
|
||||
|
||||
|
||||
|
||||
def test_navigation_entry_add_user(self):
|
||||
"""Test HTTP/Options Method Navigation Entry
|
||||
|
||||
Ensure that a user with add permission, does not
|
||||
have the menu entry within navigation
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.add_user)
|
||||
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options(
|
||||
url,
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
no_menu_entry_found: bool = True
|
||||
|
||||
for nav_menu in response.data['navigation']:
|
||||
|
||||
if nav_menu['name'] == self.menu_id:
|
||||
|
||||
for menu_entry in nav_menu['pages']:
|
||||
|
||||
if menu_entry['name'] == self.menu_entry_id:
|
||||
|
||||
no_menu_entry_found = False
|
||||
|
||||
assert no_menu_entry_found
|
||||
|
||||
|
||||
|
||||
def test_navigation_no_empty_menu_add_user(self):
|
||||
"""Test HTTP/Options Method Navigation Entry
|
||||
|
||||
Ensure that a user with add permission, does not
|
||||
have any nave menu without pages
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.add_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options(
|
||||
url,
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
no_empty_menu_found: bool = True
|
||||
|
||||
for nav_menu in response.data['navigation']:
|
||||
|
||||
if len(nav_menu['pages']) == 0:
|
||||
|
||||
no_empty_menu_found = False
|
||||
|
||||
assert no_empty_menu_found
|
||||
|
||||
|
||||
|
||||
def test_navigation_entry_change_user(self):
|
||||
"""Test HTTP/Options Method Navigation Entry
|
||||
|
||||
Ensure that a user with change permission, does not
|
||||
have the menu entry within navigation
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.change_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options(
|
||||
url,
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
no_menu_entry_found: bool = True
|
||||
|
||||
for nav_menu in response.data['navigation']:
|
||||
|
||||
if nav_menu['name'] == self.menu_id:
|
||||
|
||||
for menu_entry in nav_menu['pages']:
|
||||
|
||||
if menu_entry['name'] == self.menu_entry_id:
|
||||
|
||||
no_menu_entry_found = False
|
||||
|
||||
assert no_menu_entry_found
|
||||
|
||||
|
||||
|
||||
def test_navigation_no_empty_menu_change_user(self):
|
||||
"""Test HTTP/Options Method Navigation Entry
|
||||
|
||||
Ensure that a user with change permission, does not
|
||||
have any nave menu without pages
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.change_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options(
|
||||
url,
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
no_empty_menu_found: bool = True
|
||||
|
||||
for nav_menu in response.data['navigation']:
|
||||
|
||||
if len(nav_menu['pages']) == 0:
|
||||
|
||||
no_empty_menu_found = False
|
||||
|
||||
assert no_empty_menu_found
|
||||
|
||||
|
||||
|
||||
def test_navigation_entry_delete_user(self):
|
||||
"""Test HTTP/Options Method Navigation Entry
|
||||
|
||||
Ensure that a user with delete permission, does not
|
||||
have the menu entry within navigation
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.delete_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options(
|
||||
url,
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
no_menu_entry_found: bool = True
|
||||
|
||||
for nav_menu in response.data['navigation']:
|
||||
|
||||
if nav_menu['name'] == self.menu_id:
|
||||
|
||||
for menu_entry in nav_menu['pages']:
|
||||
|
||||
if menu_entry['name'] == self.menu_entry_id:
|
||||
|
||||
no_menu_entry_found = False
|
||||
|
||||
assert no_menu_entry_found
|
||||
|
||||
|
||||
|
||||
def test_navigation_no_empty_menu_delete_user(self):
|
||||
"""Test HTTP/Options Method Navigation Entry
|
||||
|
||||
Ensure that a user with delete permission, does not
|
||||
have any nave menu without pages
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.delete_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options(
|
||||
url,
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
no_empty_menu_found: bool = True
|
||||
|
||||
for nav_menu in response.data['navigation']:
|
||||
|
||||
if len(nav_menu['pages']) == 0:
|
||||
|
||||
no_empty_menu_found = False
|
||||
|
||||
assert no_empty_menu_found
|
||||
|
||||
|
||||
|
||||
def test_navigation_entry_view_user(self):
|
||||
"""Test HTTP/Options Method Navigation Entry
|
||||
|
||||
Ensure that a user with view permission,
|
||||
has the menu entry within navigation
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options(
|
||||
url,
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
menu_entry_found: bool = False
|
||||
|
||||
for nav_menu in response.data['navigation']:
|
||||
|
||||
if nav_menu['name'] == self.menu_id:
|
||||
|
||||
for menu_entry in nav_menu['pages']:
|
||||
|
||||
if menu_entry['name'] == self.menu_entry_id:
|
||||
|
||||
menu_entry_found = True
|
||||
|
||||
assert menu_entry_found
|
||||
|
||||
|
||||
|
||||
def test_navigation_no_empty_menu_view_user(self):
|
||||
"""Test HTTP/Options Method Navigation Entry
|
||||
|
||||
Ensure that a user with view permission, does not
|
||||
have any nave menu without pages
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
if getattr(self, 'url_kwargs', None):
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = self.url_kwargs)
|
||||
|
||||
else:
|
||||
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-list')
|
||||
|
||||
response = client.options(
|
||||
url,
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
no_empty_menu_found: bool = True
|
||||
|
||||
for nav_menu in response.data['navigation']:
|
||||
|
||||
if len(nav_menu['pages']) == 0:
|
||||
|
||||
no_empty_menu_found = False
|
||||
|
||||
assert no_empty_menu_found
|
||||
|
||||
|
1557
app/api/tests/unit/test_navigation_menu.py
Normal file
1557
app/api/tests/unit/test_navigation_menu.py
Normal file
File diff suppressed because it is too large
Load Diff
@ -82,6 +82,25 @@ class CommonViewSet(
|
||||
|
||||
view_name: str = None
|
||||
|
||||
def get_back_url(self) -> str:
|
||||
"""Metadata Back URL
|
||||
|
||||
This URL is an optional URL that if required the view must
|
||||
override this method. If the URL for a back operation
|
||||
is not the models URL, then this method is used to return
|
||||
the URL that will be used.
|
||||
|
||||
Defining this URL will predominatly be for sub-models. It's
|
||||
recommended that the `reverse` function
|
||||
(rest_framework.reverse.reverse) be used with a `request`
|
||||
object.
|
||||
|
||||
Returns:
|
||||
str: Full url in format `<protocol>://<doman name>.<tld>/api/<API version>/<model url>`
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_model_documentation(self):
|
||||
|
||||
@ -115,6 +134,26 @@ class CommonViewSet(
|
||||
return self.page_layout
|
||||
|
||||
|
||||
def get_return_url(self) -> str:
|
||||
"""Metadata return URL
|
||||
|
||||
This URL is an optional URL that if required the view must
|
||||
override this method. If the URL for a cancel operation
|
||||
is not the models URL, then this method is used to return
|
||||
the URL that will be used.
|
||||
|
||||
Defining this URL will predominatly be for sub-models. It's
|
||||
recommended that the `reverse` function
|
||||
(rest_framework.reverse.reverse) be used with a `request`
|
||||
object.
|
||||
|
||||
Returns:
|
||||
str: Full url in format `<protocol>://<doman name>.<tld>/api/<API version>/<model url>`
|
||||
"""
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def get_table_fields(self):
|
||||
|
||||
if len(self.table_fields) < 1:
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 15:27
|
||||
# Generated by Django 5.1.2 on 2024-12-06 06:47
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
@ -8,11 +8,19 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0003_alter_organization_id_alter_organization_manager_and_more'),
|
||||
('access', '0002_alter_organization_options_alter_team_options_and_more'),
|
||||
('assistance', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='knowledgebase',
|
||||
options={'ordering': ['title'], 'verbose_name': 'Knowledge Base', 'verbose_name_plural': 'Knowledge Base Articles'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='knowledgebasecategory',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Knowledge Base Category', 'verbose_name_plural': 'Knowledge Base Categories'},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='knowledgebasecategory',
|
||||
name='slug',
|
||||
@ -30,7 +38,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='knowledgebase',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='knowledgebasecategory',
|
||||
@ -50,6 +58,6 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='knowledgebasecategory',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
]
|
@ -1,21 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-16 06:54
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('assistance', '0002_remove_knowledgebasecategory_slug_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='knowledgebase',
|
||||
options={'ordering': ['title'], 'verbose_name': 'Knowledge Base', 'verbose_name_plural': 'Knowledge Base Articles'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='knowledgebasecategory',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Knowledge Base Category', 'verbose_name_plural': 'Knowledge Base Categories'},
|
||||
),
|
||||
]
|
@ -1,27 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-20 02:41
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0006_alter_team_organization'),
|
||||
('assistance', '0003_alter_knowledgebase_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='knowledgebase',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='knowledgebasecategory',
|
||||
name='organization',
|
||||
field=models.ForeignKey(default=None, help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -250,6 +250,24 @@ class KnowledgeBase(TenancyObject):
|
||||
|
||||
|
||||
page_layout: dict = [
|
||||
{
|
||||
"name": "Content",
|
||||
"slug": "content",
|
||||
"sections": [
|
||||
{
|
||||
"layout": "single",
|
||||
"fields": [
|
||||
'summary',
|
||||
]
|
||||
},
|
||||
{
|
||||
"layout": "single",
|
||||
"fields": [
|
||||
'content',
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Details",
|
||||
"slug": "details",
|
||||
|
@ -12,6 +12,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
from assistance.models.knowledge_base import KnowledgeBase
|
||||
|
||||
@ -211,4 +212,17 @@ class KnowledgeBaseViewSet(
|
||||
TestCase,
|
||||
):
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class KnowledgeBaseMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'assistance'
|
||||
|
||||
menu_entry_id = 'knowledge_base'
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from assistance.models.knowledge_base import KnowledgeBaseCategory
|
||||
|
||||
@ -206,3 +207,13 @@ class KnowledgeBaseCategoryViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class KnowledgeBaseCategoryMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -1,5 +1,7 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
from core.tests.abstract.test_ticket_viewset import Ticket, TicketViewSetBase, TicketViewSetPermissionsAPI, TicketViewSet
|
||||
|
||||
|
||||
@ -29,3 +31,16 @@ class TicketRequestViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TicketRequestMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'assistance'
|
||||
|
||||
menu_entry_id = 'request'
|
||||
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 06:42
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('config_management', '0003_alter_configgroups_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='configgroups',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Config Group', 'verbose_name_plural': 'Config Groups'},
|
||||
),
|
||||
]
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 15:27
|
||||
# Generated by Django 5.1.2 on 2024-12-06 06:47
|
||||
|
||||
import access.models
|
||||
import config_management.models.groups
|
||||
@ -9,12 +9,25 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0003_alter_organization_id_alter_organization_manager_and_more'),
|
||||
('config_management', '0005_alter_configgroupsoftware_options'),
|
||||
('itam', '0014_alter_softwarecategory_options'),
|
||||
('access', '0002_alter_organization_options_alter_team_options_and_more'),
|
||||
('config_management', '0003_alter_configgroups_options_and_more'),
|
||||
('itam', '0004_alter_deviceoperatingsystem_device_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='configgroups',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Config Group', 'verbose_name_plural': 'Config Groups'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='configgroupsoftware',
|
||||
options={'ordering': ['-action', 'software'], 'verbose_name': 'Config Group Software', 'verbose_name_plural': 'Config Group Softwares'},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='configgroups',
|
||||
name='hosts',
|
||||
field=models.ManyToManyField(blank=True, help_text='Hosts that are part of this group', to='itam.device', verbose_name='Hosts'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='configgrouphosts',
|
||||
name='group',
|
||||
@ -43,7 +56,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='configgrouphosts',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='configgroups',
|
||||
@ -73,7 +86,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='configgroups',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='configgroups',
|
||||
@ -83,7 +96,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='configgroupsoftware',
|
||||
name='action',
|
||||
field=models.CharField(blank=True, choices=[('1', 'Install'), ('0', 'Remove')], default=None, help_text='ACtion to perform with this software', max_length=1, null=True, verbose_name='Action'),
|
||||
field=models.IntegerField(blank=True, choices=[(1, 'Install'), (0, 'Remove')], default=None, help_text='ACtion to perform with this software', null=True, verbose_name='Action'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='configgroupsoftware',
|
||||
@ -108,7 +121,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='configgroupsoftware',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='configgroupsoftware',
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 06:51
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('config_management', '0004_alter_configgroups_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='configgroupsoftware',
|
||||
options={'ordering': ['-action', 'software'], 'verbose_name': 'Config Group Software', 'verbose_name_plural': 'Config Group Softwares'},
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-16 06:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('config_management', '0006_alter_configgrouphosts_group_and_more'),
|
||||
('itam', '0015_alter_device_device_model_alter_device_device_type_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='configgroups',
|
||||
name='hosts',
|
||||
field=models.ManyToManyField(blank=True, help_text='Hosts that are part of this group', to='itam.device', verbose_name='Hosts'),
|
||||
),
|
||||
]
|
@ -1,75 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-16 06:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def migrate_to_configgroups_hosts(apps, schema_editor):
|
||||
|
||||
if schema_editor.connection.alias != "default":
|
||||
|
||||
return
|
||||
|
||||
print('')
|
||||
|
||||
ConfigGroups = apps.get_model('config_management', 'ConfigGroups')
|
||||
ConfigGroupHosts = apps.get_model('config_management', 'ConfigGroupHosts')
|
||||
|
||||
current_data = ConfigGroupHosts.objects.all()
|
||||
|
||||
for host in current_data:
|
||||
|
||||
print(f'Begin migrating host {host.host} in group {host.group}:')
|
||||
|
||||
config_group = ConfigGroups.objects.get(pk = host.group.id)
|
||||
|
||||
print(f' migrate {host.host} in group {config_group}')
|
||||
|
||||
config_group.hosts.add( host.host )
|
||||
|
||||
try:
|
||||
|
||||
was_migrated = ConfigGroups.objects.get(pk = host.group.id)
|
||||
|
||||
if host.host in was_migrated.hosts.all():
|
||||
|
||||
print(f' successfully migrated {host.id} {host.host} to new table')
|
||||
|
||||
ConfigGroupHosts.objects.get(pk = host.id).delete()
|
||||
|
||||
try:
|
||||
|
||||
ConfigGroupHosts.objects.get(pk = host.id)
|
||||
|
||||
print(f' Error Failed to remove old data for host {host.host}')
|
||||
|
||||
except ConfigGroupHosts.DoesNotExist:
|
||||
|
||||
print(f' Old data removed')
|
||||
|
||||
except ConfigGroupHosts.DoesNotExist:
|
||||
|
||||
print(f' Error, {host.host} was not migrated to new table')
|
||||
|
||||
|
||||
old_data = ConfigGroupHosts.objects.all()
|
||||
|
||||
if len(old_data) == 0:
|
||||
|
||||
print(f'Successfully migrated data to new table, removing old table')
|
||||
|
||||
migrations.DeleteModel("ConfigGroupHosts")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('config_management', '0007_configgroups_hosts'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_to_configgroups_hosts),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-17 03:37
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('config_management', '0008_move_data_configgroup_hosts'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='configgroupsoftware',
|
||||
name='action',
|
||||
field=models.IntegerField(blank=True, choices=[(1, 'Install'), (0, 'Remove')], default=None, help_text='ACtion to perform with this software', null=True, verbose_name='Action'),
|
||||
),
|
||||
]
|
@ -1,31 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-20 02:41
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0006_alter_team_organization'),
|
||||
('config_management', '0009_alter_configgroupsoftware_action'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='configgrouphosts',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='configgroups',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='configgroupsoftware',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
]
|
@ -12,6 +12,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
from config_management.models.groups import ConfigGroups
|
||||
|
||||
@ -207,3 +208,16 @@ class ConfigGroupsViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class ConfigGroupsMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'config_management'
|
||||
|
||||
menu_entry_id = 'group'
|
||||
|
@ -12,6 +12,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from config_management.models.groups import ConfigGroups, ConfigGroupSoftware, Software, SoftwareVersion
|
||||
|
||||
@ -247,3 +248,13 @@ class ConfigGroupSoftwareViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class ConfigGroupSoftwareMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -1,23 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-11 16:03
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0006_ticket_milestone_ticket_opened_by_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='history',
|
||||
name='after',
|
||||
field=models.JSONField(blank=True, default=None, help_text='JSON Object After Change', null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='history',
|
||||
name='before',
|
||||
field=models.JSONField(blank=True, default=None, help_text='JSON Object before Change', null=True),
|
||||
),
|
||||
]
|
@ -1,7 +1,9 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 15:27
|
||||
# Generated by Django 5.1.2 on 2024-12-06 06:47
|
||||
|
||||
import access.fields
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
@ -9,19 +11,39 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0003_alter_organization_id_alter_organization_manager_and_more'),
|
||||
('config_management', '0006_alter_configgrouphosts_group_and_more'),
|
||||
('core', '0009_alter_notes_options'),
|
||||
('itam', '0014_alter_softwarecategory_options'),
|
||||
('itim', '0005_alter_cluster_cluster_type_alter_cluster_id_and_more'),
|
||||
('access', '0002_alter_organization_options_alter_team_options_and_more'),
|
||||
('config_management', '0004_alter_configgroups_options_and_more'),
|
||||
('core', '0006_ticket_milestone_ticket_opened_by_and_more'),
|
||||
('itam', '0004_alter_deviceoperatingsystem_device_and_more'),
|
||||
('itim', '0005_alter_port_options_alter_cluster_cluster_type_and_more'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='history',
|
||||
options={'ordering': ['-created'], 'verbose_name': 'History', 'verbose_name_plural': 'History'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='manufacturer',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Manufacturer', 'verbose_name_plural': 'Manufacturers'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='notes',
|
||||
options={'ordering': ['-created'], 'verbose_name': 'Note', 'verbose_name_plural': 'Notes'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='relatedtickets',
|
||||
options={'ordering': ['id'], 'verbose_name': 'Related Ticket', 'verbose_name_plural': 'Related Tickets'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='ticketcomment',
|
||||
options={'ordering': ['created', 'ticket', 'parent_id'], 'verbose_name': 'Ticket Comment', 'verbose_name_plural': 'Ticket Comments'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='history',
|
||||
name='action',
|
||||
field=models.IntegerField(choices=[('1', 'Create'), ('2', 'Update'), ('3', 'Delete')], default=None, help_text='History action performed', null=True, verbose_name='Action'),
|
||||
field=models.IntegerField(choices=[(1, 'Create'), (2, 'Update'), (3, 'Delete')], default=None, help_text='History action performed', null=True, verbose_name='Action'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='history',
|
||||
@ -78,6 +100,11 @@ class Migration(migrations.Migration):
|
||||
name='model_notes',
|
||||
field=models.TextField(blank=True, default=None, help_text='Tid bits of information', null=True, verbose_name='Notes'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='manufacturer',
|
||||
name='modified',
|
||||
field=access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, help_text='Date and time of last modification', verbose_name='Modified'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='manufacturer',
|
||||
name='name',
|
||||
@ -86,7 +113,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='manufacturer',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notes',
|
||||
@ -116,7 +143,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='notes',
|
||||
name='note',
|
||||
field=models.TextField(blank=True, default=None, help_text='The tid bit you wish to add', null=True, verbose_name='Note'),
|
||||
field=models.TextField(help_text='The tid bit you wish to add', verbose_name='Note'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notes',
|
||||
@ -126,7 +153,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='notes',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notes',
|
||||
@ -151,7 +178,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='relatedtickets',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
@ -161,7 +188,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticketcategory',
|
||||
@ -176,12 +203,12 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='ticketcategory',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticketcomment',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticketcommentcategory',
|
||||
@ -196,11 +223,11 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='ticketcommentcategory',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticketlinkeditem',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 06:58
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0007_alter_history_after_alter_history_before'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='manufacturer',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Manufacturer', 'verbose_name_plural': 'Manufacturers'},
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 07:02
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0008_alter_manufacturer_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='notes',
|
||||
options={'ordering': ['-created'], 'verbose_name': 'Note', 'verbose_name_plural': 'Notes'},
|
||||
),
|
||||
]
|
@ -1,22 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-18 03:34
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0010_alter_history_action_alter_history_after_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='history',
|
||||
options={'ordering': ['-created'], 'verbose_name': 'History', 'verbose_name_plural': 'History'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='history',
|
||||
name='action',
|
||||
field=models.IntegerField(choices=[(1, 'Create'), (2, 'Update'), (3, 'Delete')], default=None, help_text='History action performed', null=True, verbose_name='Action'),
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-19 02:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0011_alter_history_options_alter_history_action'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='notes',
|
||||
name='note',
|
||||
field=models.TextField(default='-', help_text='The tid bit you wish to add', verbose_name='Note'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
@ -1,20 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-19 03:58
|
||||
|
||||
import access.fields
|
||||
import django.utils.timezone
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0012_alter_notes_note'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='manufacturer',
|
||||
name='modified',
|
||||
field=access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False, help_text='Date and time of last modification', verbose_name='Modified'),
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-26 13:00
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0013_alter_manufacturer_modified'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='ticketcomment',
|
||||
options={'ordering': ['created', 'ticket', 'parent_id'], 'verbose_name': 'Comment', 'verbose_name_plural': 'Comments'},
|
||||
),
|
||||
]
|
@ -1,21 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-03 14:21
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('core', '0014_alter_ticketcomment_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='relatedtickets',
|
||||
options={'ordering': ['id'], 'verbose_name': 'Related Ticket', 'verbose_name_plural': 'Related Tickets'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='ticketcomment',
|
||||
options={'ordering': ['created', 'ticket', 'parent_id'], 'verbose_name': 'Ticket Comment', 'verbose_name_plural': 'Ticket Comments'},
|
||||
),
|
||||
]
|
@ -1,56 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-20 02:41
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0006_alter_team_organization'),
|
||||
('core', '0015_alter_relatedtickets_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='manufacturer',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='notes',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='relatedtickets',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticket',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticketcategory',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticketcomment',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticketcommentcategory',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ticketlinkeditem',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
]
|
@ -1186,11 +1186,7 @@ class RelatedTickets(TenancyObject):
|
||||
]
|
||||
|
||||
|
||||
def get_url( self, ticket_id, request = None ) -> str:
|
||||
|
||||
if not ticket_id:
|
||||
|
||||
return ''
|
||||
def get_url( self, request = None ) -> str:
|
||||
|
||||
if request:
|
||||
|
||||
@ -1198,7 +1194,7 @@ class RelatedTickets(TenancyObject):
|
||||
"v2:_api_v2_ticket_related-detail",
|
||||
request = request,
|
||||
kwargs={
|
||||
'ticket_id': ticket_id,
|
||||
'ticket_id': self.from_ticket_id.id,
|
||||
'pk': self.id
|
||||
}
|
||||
)
|
||||
@ -1206,7 +1202,7 @@ class RelatedTickets(TenancyObject):
|
||||
return reverse(
|
||||
"v2:_api_v2_ticket_related-detail",
|
||||
kwargs={
|
||||
'ticket_id': ticket_id,
|
||||
'ticket_id': self.from_ticket_id.id,
|
||||
'pk': self.id
|
||||
}
|
||||
)
|
||||
|
@ -81,7 +81,7 @@ class RelatedTicketModelSerializer(RelatedTicketBaseSerializer):
|
||||
ticket_id = int(self._kwargs['context']['view'].kwargs['ticket_id'])
|
||||
|
||||
return {
|
||||
'_self': item.get_url( ticket_id = ticket_id, request = request ),
|
||||
'_self': item.get_url( request = request ),
|
||||
}
|
||||
|
||||
|
||||
|
@ -12,6 +12,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from core.models.manufacturer import Manufacturer
|
||||
|
||||
@ -207,3 +208,13 @@ class ManufacturerViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class ManufacturerMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -15,16 +15,15 @@ from api.tests.abstract.api_permissions_viewset import (
|
||||
APIPermissionDelete,
|
||||
APIPermissionView,
|
||||
)
|
||||
from api.tests.abstract.test_metadata_functional import (
|
||||
MetadataAttributesFunctionalBase
|
||||
)
|
||||
|
||||
from core.models.ticket.ticket import Ticket, RelatedTickets
|
||||
|
||||
|
||||
|
||||
class RelatedTicketsPermissionsAPI(
|
||||
APIPermissionDelete,
|
||||
APIPermissionView,
|
||||
TestCase,
|
||||
):
|
||||
class ViewSetBase:
|
||||
|
||||
model = RelatedTickets
|
||||
|
||||
@ -209,6 +208,13 @@ class RelatedTicketsPermissionsAPI(
|
||||
|
||||
|
||||
|
||||
class RelatedTicketsPermissionsAPI(
|
||||
ViewSetBase,
|
||||
APIPermissionDelete,
|
||||
APIPermissionView,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
def test_add_has_permission_post_not_allowed(self):
|
||||
""" Check correct permission for add
|
||||
|
||||
@ -273,3 +279,13 @@ class RelatedTicketsPermissionsAPI(
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class RelatedTicketsMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctionalBase,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -12,6 +12,10 @@ from django.test import Client, TestCase
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissionView
|
||||
from api.tests.abstract.test_metadata_functional import (
|
||||
MetadataAttributesFunctionalBase,
|
||||
MetadataAttributesFunctionalEndpoint,
|
||||
)
|
||||
|
||||
from core.models.history import History
|
||||
|
||||
@ -19,7 +23,7 @@ from itam.models.device import Device
|
||||
|
||||
|
||||
|
||||
class HistoryPermissionsAPI(APIPermissionView, TestCase):
|
||||
class ViewSetBase:
|
||||
|
||||
model = History
|
||||
|
||||
@ -218,6 +222,15 @@ class HistoryPermissionsAPI(APIPermissionView, TestCase):
|
||||
user = self.different_organization_user
|
||||
)
|
||||
|
||||
|
||||
|
||||
class HistoryPermissionsAPI(
|
||||
ViewSetBase,
|
||||
APIPermissionView,
|
||||
TestCase
|
||||
):
|
||||
|
||||
|
||||
def test_view_list_has_permission(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
@ -326,3 +339,61 @@ class HistoryPermissionsAPI(APIPermissionView, TestCase):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class HistoryMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctionalEndpoint,
|
||||
MetadataAttributesFunctionalBase,
|
||||
TestCase
|
||||
):
|
||||
|
||||
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_has_key_urls_self(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
This is a custom test of a test with the same name.
|
||||
history view has no detail view, due to using a custom
|
||||
view "history",
|
||||
|
||||
Ensure the request data returned has key `urls.self`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-list',
|
||||
kwargs=self.url_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert 'urls' in response.data
|
||||
|
||||
|
||||
def test_method_options_request_detail_data_key_urls_self_is_str(self):
|
||||
"""Test HTTP/Options Method
|
||||
|
||||
This is a custom test of a test with the same name.
|
||||
history view has no detail view, due to using a custom
|
||||
view "history",
|
||||
|
||||
Ensure the request data key `urls.self` is a string
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
client.force_login(self.view_user)
|
||||
|
||||
response = client.options(
|
||||
reverse(
|
||||
self.app_namespace + ':' + self.url_name + '-list',
|
||||
kwargs=self.url_kwargs
|
||||
),
|
||||
content_type='application/json'
|
||||
)
|
||||
|
||||
assert type(response.data['urls']['self']) is str
|
||||
|
@ -12,6 +12,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from core.models.ticket.ticket_category import TicketCategory
|
||||
|
||||
@ -203,3 +204,13 @@ class TicketCategoryViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TicketCategoryMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -6,6 +6,7 @@ from django.test import Client, TestCase
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from core.models.ticket.ticket_comment import Ticket, TicketComment
|
||||
|
||||
@ -13,10 +14,7 @@ from settings.models.user_settings import UserSettings
|
||||
|
||||
|
||||
|
||||
class TicketCommentPermissionsAPI(
|
||||
APIPermissions,
|
||||
TestCase
|
||||
):
|
||||
class ViewSetBase:
|
||||
""" Test Cases common to ALL ticket types """
|
||||
|
||||
model = TicketComment
|
||||
@ -230,6 +228,11 @@ class TicketCommentPermissionsAPI(
|
||||
)
|
||||
|
||||
|
||||
class TicketCommentPermissionsAPI(
|
||||
ViewSetBase,
|
||||
APIPermissions,
|
||||
TestCase
|
||||
):
|
||||
def test_returned_results_only_user_orgs(self):
|
||||
"""Test not required
|
||||
|
||||
@ -238,3 +241,13 @@ class TicketCommentPermissionsAPI(
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TicketCommentMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -12,6 +12,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from core.models.ticket.ticket_comment_category import TicketCommentCategory
|
||||
|
||||
@ -203,3 +204,13 @@ class TicketCommentCategoryViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TicketCommentCategoryMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -1,3 +1,5 @@
|
||||
from django.db.models import Q
|
||||
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse
|
||||
|
||||
from api.viewsets.common import ModelViewSet
|
||||
@ -46,9 +48,10 @@ class ViewSet(ModelViewSet):
|
||||
queryset = super().get_queryset()
|
||||
|
||||
self.queryset = queryset.filter(
|
||||
item_parent_class = self.kwargs['model_class'],
|
||||
item_parent_pk = self.kwargs['model_id']
|
||||
).order_by('-created')
|
||||
Q(item_pk = self.kwargs['model_id'], item_class = self.kwargs['model_class'])
|
||||
|
|
||||
Q(item_parent_pk = self.kwargs['model_id'], item_parent_class = self.kwargs['model_class'])
|
||||
)
|
||||
|
||||
return self.queryset
|
||||
|
||||
|
@ -251,17 +251,6 @@ class TicketViewSet(ModelViewSet):
|
||||
|
||||
|
||||
if (
|
||||
self.action == 'list'
|
||||
):
|
||||
|
||||
user_settings = UserSettings.objects.get(
|
||||
user = self.request.user
|
||||
)
|
||||
|
||||
organization = user_settings.default_organization.id
|
||||
|
||||
|
||||
elif (
|
||||
self.action == 'create'
|
||||
):
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 15:27
|
||||
# Generated by Django 5.1.2 on 2024-12-06 06:47
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
@ -9,11 +9,52 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0003_alter_organization_id_alter_organization_manager_and_more'),
|
||||
('itam', '0014_alter_softwarecategory_options'),
|
||||
('access', '0002_alter_organization_options_alter_team_options_and_more'),
|
||||
('core', '0007_alter_history_options_alter_manufacturer_options_and_more'),
|
||||
('itam', '0004_alter_deviceoperatingsystem_device_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='device',
|
||||
options={'ordering': ['name', 'organization'], 'verbose_name': 'Device', 'verbose_name_plural': 'Devices'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='devicemodel',
|
||||
options={'ordering': ['manufacturer', 'name'], 'verbose_name': 'Device Model', 'verbose_name_plural': 'Device Models'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='deviceoperatingsystem',
|
||||
options={'ordering': ['device'], 'verbose_name': 'Device Operating System', 'verbose_name_plural': 'Device Operating Systems'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='devicesoftware',
|
||||
options={'ordering': ['-action', 'software'], 'verbose_name': 'Device Software', 'verbose_name_plural': 'Device Softwares'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='devicetype',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Device Type', 'verbose_name_plural': 'Device Types'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='operatingsystem',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Operating System', 'verbose_name_plural': 'Operating Systems'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='operatingsystemversion',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Operating System Version', 'verbose_name_plural': 'Operating System Versions'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='software',
|
||||
options={'ordering': ['name', 'publisher__name'], 'verbose_name': 'Software', 'verbose_name_plural': 'Softwares'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='softwarecategory',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Software Category', 'verbose_name_plural': 'Software Categories'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='softwareversion',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Software Version', 'verbose_name_plural': 'Software Versions'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='device_model',
|
||||
@ -52,7 +93,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicemodel',
|
||||
@ -82,12 +123,12 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='devicemodel',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='deviceoperatingsystem',
|
||||
name='device',
|
||||
field=models.ForeignKey(help_text='Device for the Operating System', on_delete=django.db.models.deletion.CASCADE, to='itam.device', verbose_name='Device'),
|
||||
field=models.ForeignKey(help_text='Device for the Operating System', on_delete=django.db.models.deletion.CASCADE, to='itam.device', unique=True, verbose_name='Device'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='deviceoperatingsystem',
|
||||
@ -117,13 +158,18 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='deviceoperatingsystem',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='deviceoperatingsystem',
|
||||
name='version',
|
||||
field=models.CharField(help_text='Version detected as installed', max_length=15, verbose_name='Installed Version'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='action',
|
||||
field=models.IntegerField(blank=True, choices=[(1, 'Install'), (0, 'Remove')], default=None, help_text='Action to perform', null=True, verbose_name='Action'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='device',
|
||||
@ -134,6 +180,11 @@ class Migration(migrations.Migration):
|
||||
name='id',
|
||||
field=models.AutoField(help_text='ID of this item', primary_key=True, serialize=False, unique=True, verbose_name='ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='installed',
|
||||
field=models.DateTimeField(blank=True, help_text='Date detected as installed', null=True, verbose_name='Date Installed'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='installedversion',
|
||||
@ -152,13 +203,18 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='software',
|
||||
field=models.ForeignKey(help_text='Software Name', on_delete=django.db.models.deletion.CASCADE, to='itam.software', verbose_name='Software'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='version',
|
||||
field=models.ForeignKey(blank=True, default=None, help_text='Version to install', null=True, on_delete=django.db.models.deletion.CASCADE, to='itam.softwareversion', verbose_name='Desired Version'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicetype',
|
||||
name='id',
|
||||
@ -182,7 +238,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='devicetype',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='operatingsystem',
|
||||
@ -207,7 +263,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='operatingsystem',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='operatingsystem',
|
||||
@ -237,12 +293,12 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='operatingsystemversion',
|
||||
name='operating_system',
|
||||
field=models.ForeignKey(help_text='Operating system this version applies to', on_delete=django.db.models.deletion.CASCADE, to='itam.operatingsystem', verbose_name='Operaating System'),
|
||||
field=models.ForeignKey(help_text='Operating system this version applies to', on_delete=django.db.models.deletion.CASCADE, to='itam.operatingsystem', verbose_name='Operating System'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='operatingsystemversion',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='software',
|
||||
@ -272,7 +328,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='software',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='software',
|
||||
@ -302,7 +358,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='softwarecategory',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='softwareversion',
|
||||
@ -327,7 +383,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='softwareversion',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='softwareversion',
|
@ -1,34 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-11 16:03
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0004_alter_deviceoperatingsystem_device_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='action',
|
||||
field=models.CharField(blank=True, choices=[('1', 'Install'), ('0', 'Remove')], default=None, help_text='Action to perform', max_length=1, null=True, verbose_name='Action'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='installed',
|
||||
field=models.DateTimeField(blank=True, help_text='Date detected as installed', null=True, verbose_name='Date Installed'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='installedversion',
|
||||
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='installedversion', to='itam.softwareversion', verbose_name='Installed Version'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='version',
|
||||
field=models.ForeignKey(blank=True, default=None, help_text='Version to install', null=True, on_delete=django.db.models.deletion.CASCADE, to='itam.softwareversion', verbose_name='Desired Version'),
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 06:42
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0005_alter_devicesoftware_action_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='device',
|
||||
options={'ordering': ['name', 'organization'], 'verbose_name': 'Device', 'verbose_name_plural': 'Devices'},
|
||||
),
|
||||
]
|
@ -0,0 +1,19 @@
|
||||
# Generated by Django 5.1.2 on 2024-12-06 07:09
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0005_alter_device_options_alter_devicemodel_options_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='deviceoperatingsystem',
|
||||
name='device',
|
||||
field=models.OneToOneField(help_text='Device for the Operating System', on_delete=django.db.models.deletion.CASCADE, to='itam.device', verbose_name='Device'),
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 07:26
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0006_alter_device_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='devicemodel',
|
||||
options={'ordering': ['manufacturer', 'name'], 'verbose_name': 'Device Model', 'verbose_name_plural': 'Device Models'},
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 07:33
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0007_alter_devicemodel_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='deviceoperatingsystem',
|
||||
options={'ordering': ['device'], 'verbose_name': 'Device Operating System', 'verbose_name_plural': 'Device Operating Systems'},
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 07:39
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0008_alter_deviceoperatingsystem_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='devicesoftware',
|
||||
options={'ordering': ['-action', 'software'], 'verbose_name': 'Device Software', 'verbose_name_plural': 'Device Softwares'},
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 07:42
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0009_alter_devicesoftware_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='devicetype',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Device Type', 'verbose_name_plural': 'Device Types'},
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 07:51
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0010_alter_devicetype_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='operatingsystem',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Operating System', 'verbose_name_plural': 'Operating Systems'},
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 07:57
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0011_alter_operatingsystem_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='operatingsystemversion',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Operating System Version', 'verbose_name_plural': 'Operating System Versions'},
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 08:06
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0012_alter_operatingsystemversion_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='software',
|
||||
options={'ordering': ['name', 'publisher__name'], 'verbose_name': 'Software', 'verbose_name_plural': 'Softwares'},
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 08:15
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0013_alter_software_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='softwarecategory',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Software Category', 'verbose_name_plural': 'Software Categories'},
|
||||
),
|
||||
]
|
@ -1,18 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-17 03:16
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0015_alter_device_device_model_alter_device_device_type_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='action',
|
||||
field=models.IntegerField(blank=True, choices=[(1, 'Install'), (0, 'Remove')], default=None, help_text='Action to perform', null=True, verbose_name='Action'),
|
||||
),
|
||||
]
|
@ -1,17 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-20 07:26
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0016_alter_devicesoftware_action'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='softwareversion',
|
||||
options={'ordering': ['name'], 'verbose_name': 'Software Version', 'verbose_name_plural': 'Software Versions'},
|
||||
),
|
||||
]
|
@ -1,71 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-20 02:43
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0006_alter_team_organization'),
|
||||
('itam', '0017_alter_softwareversion_options'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='device',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicemodel',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='deviceoperatingsystem',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicesoftware',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='devicetype',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='operatingsystem',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='operatingsystemversion',
|
||||
name='operating_system',
|
||||
field=models.ForeignKey(help_text='Operating system this version applies to', on_delete=django.db.models.deletion.CASCADE, to='itam.operatingsystem', verbose_name='Operating System'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='operatingsystemversion',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='software',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='softwarecategory',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='softwareversion',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
]
|
@ -1,19 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-20 02:43
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('itam', '0018_alter_device_organization_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='deviceoperatingsystem',
|
||||
name='device',
|
||||
field=models.ForeignKey(help_text='Device for the Operating System', on_delete=django.db.models.deletion.CASCADE, to='itam.device', unique=True, verbose_name='Device'),
|
||||
),
|
||||
]
|
@ -662,7 +662,7 @@ class DeviceOperatingSystem(DeviceCommonFields, SaveHistory):
|
||||
verbose_name_plural = 'Device Operating Systems'
|
||||
|
||||
|
||||
device = models.ForeignKey(
|
||||
device = models.OneToOneField(
|
||||
Device,
|
||||
blank = False,
|
||||
help_text = 'Device for the Operating System',
|
||||
|
@ -6,9 +6,10 @@ from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
from itam.models.device import Device
|
||||
|
||||
@ -198,4 +199,17 @@ class DeviceViewSet(
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class DeviceMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'itam'
|
||||
|
||||
menu_entry_id = 'device'
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from itam.models.device import DeviceModel
|
||||
|
||||
@ -199,3 +200,13 @@ class DeviceModelViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class DeviceModelMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from itam.serializers.device_operating_system import Device, DeviceOperatingSystem, DeviceOperatingSystemModelSerializer
|
||||
from itam.models.operating_system import OperatingSystem, OperatingSystemVersion
|
||||
@ -257,3 +258,13 @@ class DeviceOperatingViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class DeviceOperatingMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from itam.models.device import DeviceType
|
||||
|
||||
@ -198,4 +199,14 @@ class DeviceTypeViewSet(
|
||||
TestCase,
|
||||
):
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class DeviceTypeMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissionView
|
||||
from api.tests.abstract.api_serializer_viewset import SerializerView
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from itam.serializers.device_operating_system import Device, DeviceOperatingSystem, DeviceOperatingSystemModelSerializer
|
||||
from itam.models.operating_system import OperatingSystem, OperatingSystemVersion
|
||||
@ -180,3 +181,13 @@ class OperatingSystemInstallsViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class OperatingSystemInstallsMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from itam.models.device import Device, DeviceSoftware
|
||||
from itam.models.software import Software
|
||||
@ -228,4 +229,14 @@ class SoftwareInstallsViewSet(
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class SoftwareInstallsMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
from itam.models.operating_system import OperatingSystem
|
||||
|
||||
@ -198,4 +199,17 @@ class OperatingSystemViewSet(
|
||||
TestCase,
|
||||
):
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class OperatingSystemMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'itam'
|
||||
|
||||
menu_entry_id = 'operating_system'
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from itam.models.operating_system import OperatingSystem, OperatingSystemVersion
|
||||
|
||||
@ -205,3 +206,13 @@ class OperatingSystemVersionPermissionsAPI(ViewSetBase, APIPermissions, TestCase
|
||||
class OperatingSystemVersionViewSetBase(ViewSetBase, SerializersTestCases, TestCase):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class OperatingSystemVersionMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
from itam.models.software import Software
|
||||
|
||||
@ -191,3 +192,16 @@ class SoftwarePermissionsAPI(ViewSetBase, APIPermissions, TestCase):
|
||||
class SoftwareViewSet(ViewSetBase, SerializersTestCases, TestCase):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class SoftwareMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'itam'
|
||||
|
||||
menu_entry_id = 'software'
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from itam.models.software import SoftwareCategory
|
||||
|
||||
@ -190,4 +191,14 @@ class SoftwareCategoryPermissionsAPI(ViewSetBase, APIPermissions, TestCase):
|
||||
|
||||
class SoftwareCategoryViewSet(ViewSetBase, SerializersTestCases, TestCase):
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class SoftwareCategoryMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from itam.models.software import Software, SoftwareVersion
|
||||
|
||||
@ -203,4 +204,14 @@ class SoftwareVersionPermissionsAPI(ViewSetBase, APIPermissions, TestCase):
|
||||
|
||||
class SoftwareVersionViewSet(ViewSetBase, SerializersTestCases, TestCase):
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class SoftwareVersionMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 15:27
|
||||
# Generated by Django 5.1.2 on 2024-12-06 06:47
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
@ -8,11 +8,15 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0003_alter_organization_id_alter_organization_manager_and_more'),
|
||||
('access', '0002_alter_organization_options_alter_team_options_and_more'),
|
||||
('itim', '0004_alter_service_config_key_variable'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='port',
|
||||
options={'ordering': ['number', 'protocol'], 'verbose_name': 'Port', 'verbose_name_plural': 'Ports'},
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cluster',
|
||||
name='cluster_type',
|
||||
@ -36,7 +40,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='cluster',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='cluster',
|
||||
@ -61,7 +65,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='clustertype',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='port',
|
||||
@ -81,7 +85,12 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='port',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
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'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='service',
|
||||
@ -101,6 +110,6 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='service',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
]
|
@ -1,22 +0,0 @@
|
||||
# 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'),
|
||||
),
|
||||
]
|
@ -1,36 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-11-20 02:41
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0006_alter_team_organization'),
|
||||
('itim', '0006_alter_port_options_alter_port_protocol'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='cluster',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='clustertype',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='port',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='service',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
]
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
from itim.models.clusters import Cluster
|
||||
|
||||
@ -190,4 +191,17 @@ class ClusterPermissionsAPI(ViewSetBase, APIPermissions, TestCase):
|
||||
|
||||
class ClusterViewSet(ViewSetBase, SerializersTestCases, TestCase):
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class ClusterMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'itim'
|
||||
|
||||
menu_entry_id = 'cluster'
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from itim.models.clusters import ClusterType
|
||||
|
||||
@ -191,4 +192,14 @@ class ClusterTypePermissionsAPI(ViewSetBase, APIPermissions, TestCase):
|
||||
|
||||
class ClusterTypeViewSet(ViewSetBase, SerializersTestCases, TestCase):
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class ClusterTypeMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
|
||||
|
||||
from itim.models.services import Port
|
||||
|
||||
@ -193,4 +194,14 @@ class PortPermissionsAPI(ViewSetBase, APIPermissions, TestCase):
|
||||
|
||||
class PortViewSet(ViewSetBase, SerializersTestCases, TestCase):
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class PortMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
@ -8,6 +8,7 @@ from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_permissions_viewset import APIPermissions
|
||||
from api.tests.abstract.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
from itam.models.device import Device
|
||||
|
||||
@ -212,4 +213,17 @@ class ServicePermissionsAPI(ViewSetBase, APIPermissions, TestCase):
|
||||
|
||||
class ServiceViewSet(ViewSetBase, SerializersTestCases, TestCase):
|
||||
|
||||
pass
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class ServiceMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'itim'
|
||||
|
||||
menu_entry_id = 'service'
|
||||
|
@ -1,6 +1,7 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from core.tests.abstract.test_ticket_viewset import Ticket, TicketViewSetBase, TicketViewSetPermissionsAPI, TicketViewSet
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
|
||||
class ViewSetBase( TicketViewSetBase ):
|
||||
@ -28,3 +29,16 @@ class TicketChangeViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TicketChangeMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'itim'
|
||||
|
||||
menu_entry_id = 'ticket_change'
|
||||
|
@ -1,6 +1,7 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from core.tests.abstract.test_ticket_viewset import Ticket, TicketViewSetBase, TicketViewSetPermissionsAPI, TicketViewSet
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
|
||||
class ViewSetBase( TicketViewSetBase ):
|
||||
@ -28,3 +29,16 @@ class TicketIncidentViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TicketIncidentMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'itim'
|
||||
|
||||
menu_entry_id = 'ticket_incident'
|
||||
|
@ -1,6 +1,7 @@
|
||||
from django.test import TestCase
|
||||
|
||||
from core.tests.abstract.test_ticket_viewset import Ticket, TicketViewSetBase, TicketViewSetPermissionsAPI, TicketViewSet
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
|
||||
class ViewSetBase( TicketViewSetBase ):
|
||||
@ -28,3 +29,16 @@ class TicketProblemViewSet(
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TicketProblemMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'itim'
|
||||
|
||||
menu_entry_id = 'ticket_problem'
|
||||
|
@ -1,4 +1,4 @@
|
||||
# Generated by Django 5.1.2 on 2024-10-13 15:27
|
||||
# Generated by Django 5.1.2 on 2024-12-06 06:47
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
@ -9,7 +9,6 @@ from django.db import migrations, models
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('access', '0003_alter_organization_id_alter_organization_manager_and_more'),
|
||||
('project_management', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
@ -48,7 +47,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='project',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='projectmilestone',
|
||||
@ -68,7 +67,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='projectmilestone',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='projectstate',
|
||||
@ -83,7 +82,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='projectstate',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='projecttype',
|
||||
@ -98,6 +97,6 @@ class Migration(migrations.Migration):
|
||||
migrations.AlterField(
|
||||
model_name='projecttype',
|
||||
name='organization',
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
|
||||
),
|
||||
]
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user