Compare commits
34 Commits
1.6.0
...
8d071c68df
Author | SHA1 | Date | |
---|---|---|---|
8d071c68df | |||
3b1691ff62 | |||
a77c43d213 | |||
086959b431 | |||
3f117f9d83 | |||
6a23845a4f | |||
b9c6d04e04 | |||
32c0027ecf | |||
dae52e8646 | |||
890a5651a0 | |||
4cb37f8347 | |||
a2010b9517 | |||
c95736ce14 | |||
b46c61954c | |||
afe4266600 | |||
0c8d1c8da1 | |||
eac998b5cc | |||
5914782252 | |||
73d875c4ac | |||
8f439f0675 | |||
0f102c6aaf | |||
4852c6caeb | |||
3fffba2eba | |||
a1293984ea | |||
4876db50c1 | |||
425cc066af | |||
1086f517fa | |||
2fdbf87ddd | |||
86228836c7 | |||
a6e6c948a5 | |||
dcdfa8feb7 | |||
8388d2e695 | |||
29f269050f | |||
93c4fc2009 |
2
.cz.yaml
2
.cz.yaml
@ -17,5 +17,5 @@ commitizen:
|
||||
prerelease_offset: 1
|
||||
tag_format: $version
|
||||
update_changelog_on_bump: false
|
||||
version: 1.6.0
|
||||
version: 1.0.0-b14
|
||||
version_scheme: semver
|
||||
|
5
.github/pull_request_template.md
vendored
5
.github/pull_request_template.md
vendored
@ -20,9 +20,6 @@
|
||||
|
||||
<!-- 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/)._
|
||||
@ -35,8 +32,6 @@
|
||||
|
||||
- [ ] :checkered_flag: Milestone assigned
|
||||
|
||||
- [ ] :gear: :test_tube: [Functional Test(s) Written](https://nofusscomputing.com/projects/centurion_erp/development/testing/)
|
||||
|
||||
- [ ] :test_tube: [Unit Test(s) Written](https://nofusscomputing.com/projects/centurion_erp/development/testing/)
|
||||
|
||||
_ensure test coverage delta is not less than zero_
|
||||
|
11
.github/workflows/ci.yaml
vendored
11
.github/workflows/ci.yaml
vendored
@ -16,17 +16,6 @@ env:
|
||||
jobs:
|
||||
|
||||
|
||||
mkdocs:
|
||||
name: 'MKDocs'
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
statuses: write
|
||||
checks: write
|
||||
actions: write
|
||||
uses: nofusscomputing/action_mkdocs/.github/workflows/reusable_mkdocs.yaml@development
|
||||
|
||||
|
||||
docker:
|
||||
name: 'Docker'
|
||||
uses: nofusscomputing/action_docker/.github/workflows/docker.yaml@development
|
||||
|
6
.gitignore
vendored
6
.gitignore
vendored
@ -9,9 +9,3 @@ artifacts/
|
||||
volumes/
|
||||
build/
|
||||
pages/
|
||||
node_modules/
|
||||
.markdownlint-cli2.jsonc
|
||||
.markdownlint.json
|
||||
package-lock.json
|
||||
package.json
|
||||
**.junit.xml
|
||||
|
1
.vscode/extensions.json
vendored
1
.vscode/extensions.json
vendored
@ -7,6 +7,5 @@
|
||||
"streetsidesoftware.code-spell-checker",
|
||||
"qwtel.sqlite-viewer",
|
||||
"jebbs.markdown-extended",
|
||||
"william-voyek.vscode-nginx",
|
||||
]
|
||||
}
|
36
.vscode/launch.json
vendored
36
.vscode/launch.json
vendored
@ -5,7 +5,7 @@
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Centurion",
|
||||
"name": "Debug: Django",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
@ -16,41 +16,9 @@
|
||||
"autoStartBrowser": false,
|
||||
"program": "${workspaceFolder}/app/manage.py"
|
||||
},
|
||||
{
|
||||
|
||||
"name": "Debug: Gunicorn",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"module": "gunicorn",
|
||||
"args": [
|
||||
"--access-logfile",
|
||||
"-",
|
||||
"--workers",
|
||||
"3",
|
||||
"--bind",
|
||||
"0.0.0.0:8002",
|
||||
"app.wsgi:application",
|
||||
|
||||
],
|
||||
"django": true,
|
||||
"autoStartBrowser": false,
|
||||
"cwd": "${workspaceFolder}/app"
|
||||
},
|
||||
{
|
||||
"name": "Migrate",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"migrate"
|
||||
],
|
||||
"django": true,
|
||||
"autoStartBrowser": false,
|
||||
"program": "${workspaceFolder}/app/manage.py"
|
||||
|
||||
},
|
||||
{
|
||||
"name": "Debug: Celery",
|
||||
"type": "debugpy",
|
||||
"type": "python",
|
||||
"request": "launch",
|
||||
"module": "celery",
|
||||
"console": "integratedTerminal",
|
||||
|
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -17,5 +17,4 @@
|
||||
"ITSM"
|
||||
],
|
||||
"cSpell.language": "en-AU",
|
||||
"jest.enable": false,
|
||||
}
|
1243
CHANGELOG.md
1243
CHANGELOG.md
File diff suppressed because it is too large
Load Diff
@ -1,84 +1,6 @@
|
||||
# Contribution Guide
|
||||
|
||||
|
||||
Development of this project has been setup to be done from VSCodium. The following additional requirements need to be met:
|
||||
|
||||
- npm has been installed. _required for `markdown` linting_
|
||||
|
||||
`sudo apt install -y --no-install-recommends npm`
|
||||
|
||||
- setup of other requirements can be done with `make prepare`
|
||||
|
||||
- **ALL** Linting must pass for Merge to be conducted.
|
||||
|
||||
_`make lint`_
|
||||
|
||||
## TL;DR
|
||||
|
||||
|
||||
from the root of the project to start a test server use:
|
||||
|
||||
``` bash
|
||||
|
||||
# activate python venv
|
||||
source /tmp/centurion_erp/bin/activate
|
||||
|
||||
# enter app dir
|
||||
cd app
|
||||
|
||||
# Start dev server can be viewed at http://127.0.0.1:8002
|
||||
python manage.py runserver 8002
|
||||
|
||||
# Run any migrations, if required
|
||||
python manage.py migrate
|
||||
|
||||
# Create a super suer if required
|
||||
python manage.py createsuperuser
|
||||
|
||||
```
|
||||
|
||||
## Makefile
|
||||
|
||||
!!! tip "TL;DR"
|
||||
Common make commands are `make prepare` then `make docs` and `make lint`
|
||||
|
||||
Included within the root of the repository is a makefile that can be used during development to check/run different items as is required during development. The following make targets are available:
|
||||
|
||||
- `prepare`
|
||||
|
||||
_prepare the repository. init's all git submodules and sets up a python virtual env and other make targets_
|
||||
|
||||
- `docs`
|
||||
|
||||
_builds the docs and places them within a directory called build, which can be viewed within a web browser_
|
||||
|
||||
- `lint`
|
||||
|
||||
_conducts all required linting_
|
||||
|
||||
- `docs-lint`
|
||||
|
||||
_lints the markdown documents within the docs directory for formatting errors that MKDocs may/will have an issue with._
|
||||
|
||||
- `clean`
|
||||
|
||||
_cleans up build artifacts and removes the python virtual environment_
|
||||
|
||||
|
||||
> this doc is yet to receive a re-write
|
||||
|
||||
|
||||
## Docker Container
|
||||
|
||||
within the `deploy/` directory there is a docker compose file. running `docker compose up` from this directory will launch a full stack deployment locally containing Centurion API, User Interface, a worker and a RabbitMQ server. once launched you can navigate to `http://127.0.0.1/` to start browsing the site.
|
||||
|
||||
You may need to run migrations if your not mounting your own DB. to do this run `docker exec -ti centurion-erp python manage.py migrate`
|
||||
|
||||
|
||||
|
||||
# Old working docs
|
||||
|
||||
|
||||
## Dev Environment
|
||||
|
||||
It's advised to setup a python virtual env for development. this can be done with the following commands.
|
||||
|
13
README.md
13
README.md
@ -32,14 +32,9 @@ This project is hosted on [Github](https://github.com/NofussComputing/centurion_
|
||||
|
||||
**Stable Branch**
|
||||
|
||||
 
|
||||
|
||||

|
||||
  
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
|
||||
----
|
||||
@ -48,13 +43,9 @@ This project is hosted on [Github](https://github.com/NofussComputing/centurion_
|
||||
|
||||
|
||||
|
||||
 
|
||||
|
||||

|
||||
  
|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
----
|
||||
<br>
|
||||
|
@ -1,57 +1,7 @@
|
||||
## 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.
|
||||
API v2 is a beta release and is subject to change. On completion of the new UI, API v2 will more likely than not be set as stable.
|
||||
|
||||
- A large emphasis is being placed upon API stability. This is being achieved by ensuring the following:
|
||||
|
||||
- Actions can only be carried out by users whom have the correct permissions
|
||||
|
||||
- fields are of the correct type and visible when required as part of the API response
|
||||
|
||||
- Data validations work and notify the user of any issue
|
||||
|
||||
We are make the above possible by ensuring a more stringent test policy.
|
||||
|
||||
- New API will be at path `api/v2`.
|
||||
|
||||
- API v1 is now **Feature frozen** with only bug fixes being completed. It's recommended that you move to and start using API v2 as this has feature parity with API v1.
|
||||
|
||||
- API v1 is **depreciated**
|
||||
|
||||
- Depreciation of **ALL** API urls. API v1 Will be [removed in v2.0.0](https://github.com/nofusscomputing/centurion_erp/issues/343) release of Centurion.
|
||||
|
||||
|
||||
# Version 1.3.0
|
||||
|
||||
!!! danger "Security"
|
||||
As is currently the recommended method of deployment, the Centurion Container must be deployed behind a reverse proxy the conducts the SSL termination.
|
||||
|
||||
This release updates the docker container to be a production setup for deployment of Centurion. Prior to this version Centurion ERP was using a development setup for the webserver.
|
||||
|
||||
- Docker now uses SupervisorD for container
|
||||
|
||||
- Gunicorn WSGI setup for Centurion with NginX as the webserver.
|
||||
|
||||
- Container now has a health check.
|
||||
|
||||
- To setup container as "Worker", set `IS_WORKER='True'` environmental variable within container. _**Note:** You can still use command `celery -A app worker -l INFO`, although **not** recommended as the container health check will not be functioning_
|
||||
|
||||
|
||||
## Version 1.0.0
|
||||
|
||||
# Version 1.0.0
|
||||
|
||||
Initial Release of Centurion ERP.
|
||||
|
||||
|
||||
### Breaking changes
|
||||
## Breaking changes
|
||||
|
||||
- Nil
|
||||
|
@ -1,6 +1,5 @@
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
from .models import *
|
||||
|
||||
@ -26,40 +25,6 @@ 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)
|
||||
|
||||
|
@ -11,20 +11,12 @@ class AutoCreatedField(models.DateTimeField):
|
||||
|
||||
"""
|
||||
|
||||
help_text = 'Date and time of creation'
|
||||
|
||||
verbose_name = 'Created'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
kwargs.setdefault("editable", False)
|
||||
|
||||
kwargs.setdefault("default", now)
|
||||
|
||||
kwargs.setdefault("help_text", self.help_text)
|
||||
|
||||
kwargs.setdefault("verbose_name", self.verbose_name)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
@ -36,18 +28,6 @@ class AutoLastModifiedField(AutoCreatedField):
|
||||
|
||||
"""
|
||||
|
||||
help_text = 'Date and time of last modification'
|
||||
|
||||
verbose_name = 'Modified'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
kwargs.setdefault("help_text", self.help_text)
|
||||
|
||||
kwargs.setdefault("verbose_name", self.verbose_name)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def pre_save(self, model_instance, add):
|
||||
|
||||
value = now()
|
||||
@ -65,20 +45,6 @@ class AutoSlugField(models.SlugField):
|
||||
|
||||
"""
|
||||
|
||||
help_text = 'slug for this field'
|
||||
|
||||
verbose_name = 'Slug'
|
||||
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
|
||||
kwargs.setdefault("help_text", self.help_text)
|
||||
|
||||
kwargs.setdefault("verbose_name", self.verbose_name)
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
def pre_save(self, model_instance, add):
|
||||
|
||||
if not model_instance.slug or model_instance.slug == '_':
|
||||
|
@ -1,13 +1,13 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.db.models import Q
|
||||
from django.forms import inlineformset_factory
|
||||
|
||||
from app import settings
|
||||
|
||||
from .team_users import TeamUsersForm, TeamUsers
|
||||
|
||||
from access.models import Team
|
||||
from access.functions import permissions
|
||||
|
||||
from app import settings
|
||||
|
||||
from core.forms.common import CommonModelForm
|
||||
|
||||
@ -66,4 +66,38 @@ class TeamForm(CommonModelForm):
|
||||
|
||||
self.fields['permissions'].widget.attrs = {'style': "height: 200px;"}
|
||||
|
||||
self.fields['permissions'].queryset = permissions.permission_queryset()
|
||||
apps = [
|
||||
'access',
|
||||
'assistance',
|
||||
'config_management',
|
||||
'core',
|
||||
'django_celery_results',
|
||||
'itam',
|
||||
'settings',
|
||||
]
|
||||
|
||||
exclude_models = [
|
||||
'appsettings',
|
||||
'chordcounter',
|
||||
'groupresult',
|
||||
'organization'
|
||||
'settings',
|
||||
'usersettings',
|
||||
]
|
||||
|
||||
exclude_permissions = [
|
||||
'add_organization',
|
||||
'add_taskresult',
|
||||
'change_organization',
|
||||
'change_taskresult',
|
||||
'delete_organization',
|
||||
'delete_taskresult',
|
||||
]
|
||||
|
||||
self.fields['permissions'].queryset = Permission.objects.filter(
|
||||
content_type__app_label__in=apps,
|
||||
).exclude(
|
||||
content_type__model__in=exclude_models
|
||||
).exclude(
|
||||
codename__in = exclude_permissions
|
||||
)
|
||||
|
@ -1,45 +0,0 @@
|
||||
from django.contrib.auth.models import Permission
|
||||
|
||||
def permission_queryset():
|
||||
"""Filter Permissions to those used within the application
|
||||
|
||||
Returns:
|
||||
list: Filtered queryset that only contains the used permissions
|
||||
"""
|
||||
|
||||
apps = [
|
||||
'access',
|
||||
'assistance',
|
||||
'config_management',
|
||||
'core',
|
||||
'django_celery_results',
|
||||
'itam',
|
||||
'itim',
|
||||
'project_management',
|
||||
'settings',
|
||||
]
|
||||
|
||||
exclude_models = [
|
||||
'appsettings',
|
||||
'chordcounter',
|
||||
'comment',
|
||||
'groupresult',
|
||||
'usersettings',
|
||||
]
|
||||
|
||||
exclude_permissions = [
|
||||
'add_organization',
|
||||
'add_taskresult',
|
||||
'change_organization',
|
||||
'change_taskresult',
|
||||
'delete_organization',
|
||||
'delete_taskresult',
|
||||
]
|
||||
|
||||
return Permission.objects.filter(
|
||||
content_type__app_label__in=apps,
|
||||
).exclude(
|
||||
content_type__model__in=exclude_models
|
||||
).exclude(
|
||||
codename__in = exclude_permissions
|
||||
)
|
@ -1,89 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-12-06 06:47
|
||||
|
||||
import access.models
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('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',
|
||||
field=models.AutoField(help_text='ID of this item', primary_key=True, serialize=False, unique=True, verbose_name='ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='organization',
|
||||
name='manager',
|
||||
field=models.ForeignKey(help_text='Manager for this organization', null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL, verbose_name='Manager'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='organization',
|
||||
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='organization',
|
||||
name='name',
|
||||
field=models.CharField(help_text='Name of this Organization', max_length=50, unique=True, verbose_name='Name'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='is_global',
|
||||
field=models.BooleanField(default=False, help_text='Is this a global object?', verbose_name='Global Object'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
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='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'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='team',
|
||||
name='team_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'),
|
||||
),
|
||||
]
|
@ -10,19 +10,6 @@ from .models import Organization, Team
|
||||
class OrganizationMixin():
|
||||
"""Base Organization class"""
|
||||
|
||||
parent_model: str = None
|
||||
""" Parent Model
|
||||
|
||||
This attribute defines the parent model for the model in question. The parent model when defined
|
||||
will be used as the object to obtain the permissions from.
|
||||
"""
|
||||
|
||||
parent_model_pk_kwarg: str = 'pk'
|
||||
"""Parent Model kwarg
|
||||
|
||||
This value is used to define the kwarg that is used as the parent objects primary key (pk).
|
||||
"""
|
||||
|
||||
request = None
|
||||
|
||||
user_groups = []
|
||||
@ -39,24 +26,20 @@ class OrganizationMixin():
|
||||
parent_model (Model): with PK from kwargs['pk']
|
||||
"""
|
||||
|
||||
return self.parent_model.objects.get(pk=self.kwargs[self.parent_model_pk_kwarg])
|
||||
return self.parent_model.objects.get(pk=self.kwargs['pk'])
|
||||
|
||||
|
||||
def object_organization(self) -> int:
|
||||
|
||||
id = None
|
||||
|
||||
if hasattr(self, '_object_organization'):
|
||||
|
||||
return int(self._object_organization)
|
||||
|
||||
try:
|
||||
|
||||
if hasattr(self, 'get_queryset'):
|
||||
self.get_queryset()
|
||||
|
||||
|
||||
if self.parent_model:
|
||||
if hasattr(self, 'parent_model'):
|
||||
obj = self.get_parent_obj()
|
||||
|
||||
id = obj.get_organization().id
|
||||
@ -78,10 +61,6 @@ class OrganizationMixin():
|
||||
|
||||
id = 0
|
||||
|
||||
if hasattr(self, 'instance') and id is None: # Form Instance
|
||||
|
||||
id = self.instance.get_organization()
|
||||
|
||||
|
||||
except AttributeError:
|
||||
|
||||
@ -105,10 +84,6 @@ class OrganizationMixin():
|
||||
|
||||
pass
|
||||
|
||||
if id is not None:
|
||||
|
||||
self._object_organization = id
|
||||
|
||||
|
||||
return id
|
||||
|
||||
@ -124,13 +99,9 @@ class OrganizationMixin():
|
||||
|
||||
is_member = False
|
||||
|
||||
if organization is None:
|
||||
if organization in self.user_organizations():
|
||||
|
||||
return False
|
||||
|
||||
if int(organization) in self.user_organizations():
|
||||
|
||||
is_member = True
|
||||
return True
|
||||
|
||||
return is_member
|
||||
|
||||
@ -140,10 +111,6 @@ class OrganizationMixin():
|
||||
Override of 'PermissionRequiredMixin' method so that this mixin can obtain the required permission.
|
||||
"""
|
||||
|
||||
if not hasattr(self, 'permission_required'):
|
||||
|
||||
return []
|
||||
|
||||
if self.permission_required is None:
|
||||
raise ImproperlyConfigured(
|
||||
f"{self.__class__.__name__} is missing the "
|
||||
@ -180,10 +147,6 @@ class OrganizationMixin():
|
||||
|
||||
user_organizations = []
|
||||
|
||||
if hasattr(self, '_user_organizations'):
|
||||
|
||||
return self._user_organizations
|
||||
|
||||
teams = Team.objects
|
||||
|
||||
for group in self.request.user.groups.all():
|
||||
@ -194,41 +157,18 @@ class OrganizationMixin():
|
||||
|
||||
user_organizations = user_organizations + [team.organization.id]
|
||||
|
||||
if len(user_organizations) > 0:
|
||||
|
||||
self._user_organizations = user_organizations
|
||||
|
||||
|
||||
return user_organizations
|
||||
|
||||
|
||||
# ToDo: Ensure that the group has access to item
|
||||
def has_organization_permission(self, organization: int = None, permissions_required: list = None) -> bool:
|
||||
""" Check if user has permission within organization.
|
||||
|
||||
Args:
|
||||
organization (int, optional): Organization to check. Defaults to None.
|
||||
permissions_required (list, optional): if doing object level permissions, pass in required permission. Defaults to None.
|
||||
|
||||
Returns:
|
||||
bool: True for yes.
|
||||
"""
|
||||
def has_organization_permission(self, organization: int=None) -> bool:
|
||||
|
||||
has_permission = False
|
||||
|
||||
if permissions_required is None:
|
||||
|
||||
permissions_required = self.get_permission_required()
|
||||
|
||||
if not organization:
|
||||
|
||||
organization = self.object_organization()
|
||||
|
||||
else:
|
||||
|
||||
organization = int(organization)
|
||||
|
||||
|
||||
if self.is_member(organization) or organization == 0:
|
||||
|
||||
groups = Group.objects.filter(pk__in=self.user_groups)
|
||||
@ -242,7 +182,7 @@ class OrganizationMixin():
|
||||
|
||||
assembled_permission = str(permission["content_type__app_label"]) + '.' + str(permission["codename"])
|
||||
|
||||
if assembled_permission in permissions_required and (team['organization_id'] == organization or organization == 0):
|
||||
if assembled_permission in self.get_permission_required() and (team['organization_id'] == organization or organization == 0):
|
||||
|
||||
return True
|
||||
|
||||
@ -302,23 +242,15 @@ class OrganizationMixin():
|
||||
|
||||
return True
|
||||
|
||||
if permissions_required:
|
||||
perms = self.get_permission_required()
|
||||
|
||||
perms = permissions_required
|
||||
|
||||
else:
|
||||
|
||||
perms = self.get_permission_required()
|
||||
|
||||
if self.has_organization_permission(permissions_required = perms):
|
||||
if self.has_organization_permission():
|
||||
|
||||
return True
|
||||
|
||||
if self.request.user.has_perms(perms) and str(self.request.method).lower() == 'get':
|
||||
if self.request.user.has_perms(perms) and len(self.kwargs) == 0 and str(self.request.method).lower() == 'get':
|
||||
|
||||
if len(self.kwargs) == 0 or (len(self.kwargs) == 1 and 'ticket_type' in self.kwargs):
|
||||
|
||||
return True
|
||||
return True
|
||||
|
||||
for required_permission in self.permission_required:
|
||||
|
||||
@ -395,12 +327,6 @@ class OrganizationPermission(AccessMixin, OrganizationMixin):
|
||||
|
||||
if not request.user.is_authenticated:
|
||||
return self.handle_no_permission()
|
||||
|
||||
if len(self.permission_required) == 0:
|
||||
|
||||
if hasattr(self, 'get_dynamic_permissions'):
|
||||
|
||||
self.permission_required = self.get_dynamic_permissions()
|
||||
|
||||
if len(self.permission_required) > 0:
|
||||
|
||||
|
@ -3,8 +3,6 @@ from django.db import models
|
||||
from django.contrib.auth.models import User, Group, Permission
|
||||
from django.forms import ValidationError
|
||||
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from .fields import *
|
||||
|
||||
from core.middleware.get_request import get_request
|
||||
@ -14,7 +12,6 @@ from core.mixin.history_save import SaveHistory
|
||||
class Organization(SaveHistory):
|
||||
|
||||
class Meta:
|
||||
verbose_name = "Organization"
|
||||
verbose_name_plural = "Organizations"
|
||||
ordering = ['name']
|
||||
|
||||
@ -26,34 +23,28 @@ class Organization(SaveHistory):
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
id = models.AutoField(
|
||||
blank=False,
|
||||
help_text = 'ID of this item',
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
verbose_name = 'ID'
|
||||
blank=False
|
||||
)
|
||||
|
||||
name = models.CharField(
|
||||
blank = False,
|
||||
help_text = 'Name of this Organization',
|
||||
max_length = 50,
|
||||
unique = True,
|
||||
verbose_name = 'Name'
|
||||
)
|
||||
|
||||
manager = models.ForeignKey(
|
||||
User,
|
||||
blank = False,
|
||||
help_text = 'Manager for this organization',
|
||||
null = True,
|
||||
on_delete=models.SET_NULL,
|
||||
verbose_name = 'Manager'
|
||||
blank = False,
|
||||
null = True,
|
||||
help_text = 'Organization Manager'
|
||||
)
|
||||
|
||||
model_notes = models.TextField(
|
||||
blank = True,
|
||||
default = None,
|
||||
help_text = 'Tid bits of information',
|
||||
null= True,
|
||||
verbose_name = 'Notes',
|
||||
)
|
||||
@ -71,69 +62,6 @@ class Organization(SaveHistory):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
table_fields: list = [
|
||||
'nbsp',
|
||||
'name',
|
||||
'created',
|
||||
'modified',
|
||||
'nbsp'
|
||||
]
|
||||
|
||||
page_layout: list = [
|
||||
{
|
||||
"name": "Details",
|
||||
"slug": "details",
|
||||
"sections": [
|
||||
{
|
||||
"layout": "double",
|
||||
"left": [
|
||||
'name',
|
||||
'manager',
|
||||
'created',
|
||||
'modified',
|
||||
],
|
||||
"right": [
|
||||
'model_notes',
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Teams",
|
||||
"slug": "teams",
|
||||
"sections": [
|
||||
{
|
||||
"layout": "table",
|
||||
"field": "teams"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Knowledge Base",
|
||||
"slug": "kb_articles",
|
||||
"sections": [
|
||||
{
|
||||
"layout": "table",
|
||||
"field": "knowledge_base",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Notes",
|
||||
"slug": "notes",
|
||||
"sections": []
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def get_url( self, request = None ) -> str:
|
||||
|
||||
if request:
|
||||
|
||||
return reverse("v2:_api_v2_organization-detail", request=request, kwargs={'pk': self.id})
|
||||
|
||||
return reverse("v2:_api_v2_organization-detail", kwargs={'pk': self.id})
|
||||
|
||||
|
||||
|
||||
class TenancyManager(models.Manager):
|
||||
@ -178,9 +106,7 @@ class TenancyManager(models.Manager):
|
||||
|
||||
if request:
|
||||
|
||||
# user = request.user._wrapped if hasattr(request.user,'_wrapped') else request.user
|
||||
|
||||
user = request.user
|
||||
user = request.user._wrapped if hasattr(request.user,'_wrapped') else request.user
|
||||
|
||||
|
||||
if user.is_authenticated:
|
||||
@ -198,22 +124,13 @@ class TenancyManager(models.Manager):
|
||||
user_organizations += [ team_user.team.organization.id ]
|
||||
|
||||
|
||||
# if len(user_organizations) > 0 and not user.is_superuser and self.model.is_global is not None:
|
||||
if len(user_organizations) > 0 and not user.is_superuser:
|
||||
|
||||
if getattr(self.model, 'is_global', False) is True:
|
||||
|
||||
return super().get_queryset().filter(
|
||||
models.Q(organization__in=user_organizations)
|
||||
|
|
||||
models.Q(is_global = True)
|
||||
)
|
||||
|
||||
else:
|
||||
|
||||
return super().get_queryset().filter(
|
||||
models.Q(organization__in=user_organizations)
|
||||
)
|
||||
return super().get_queryset().filter(
|
||||
models.Q(organization__in=user_organizations)
|
||||
|
|
||||
models.Q(is_global = True)
|
||||
)
|
||||
|
||||
return super().get_queryset()
|
||||
|
||||
@ -248,36 +165,23 @@ class TenancyObject(SaveHistory):
|
||||
raise ValidationError('You must provide an organization')
|
||||
|
||||
|
||||
id = models.AutoField(
|
||||
blank=False,
|
||||
help_text = 'ID of the item',
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
verbose_name = 'ID'
|
||||
)
|
||||
|
||||
organization = models.ForeignKey(
|
||||
Organization,
|
||||
on_delete=models.CASCADE,
|
||||
blank = False,
|
||||
help_text = 'Organization this belongs to',
|
||||
null = False,
|
||||
on_delete = models.CASCADE,
|
||||
null = True,
|
||||
validators = [validatate_organization_exists],
|
||||
verbose_name = 'Organization'
|
||||
)
|
||||
|
||||
is_global = models.BooleanField(
|
||||
blank = False,
|
||||
default = False,
|
||||
help_text = 'Is this a global object?',
|
||||
verbose_name = 'Global Object'
|
||||
blank = False
|
||||
)
|
||||
|
||||
model_notes = models.TextField(
|
||||
blank = True,
|
||||
default = None,
|
||||
help_text = 'Tid bits of information',
|
||||
null = True,
|
||||
null= True,
|
||||
verbose_name = 'Notes',
|
||||
)
|
||||
|
||||
@ -285,61 +189,12 @@ class TenancyObject(SaveHistory):
|
||||
return self.organization
|
||||
|
||||
|
||||
def get_url( self, request = None ) -> str:
|
||||
"""Fetch the models URL
|
||||
|
||||
If URL kwargs are required to generate the URL, define a `get_url_kwargs` that returns them.
|
||||
|
||||
Args:
|
||||
request (object, optional): The request object that was made by the end user. Defaults to None.
|
||||
|
||||
Returns:
|
||||
str: Canonical URL of the model if the `request` object was provided. Otherwise the relative URL.
|
||||
"""
|
||||
|
||||
model_name = str(self._meta.verbose_name.lower()).replace(' ', '_')
|
||||
|
||||
|
||||
if request:
|
||||
|
||||
return reverse(f"v2:_api_v2_{model_name}-detail", request=request, kwargs = self.get_url_kwargs() )
|
||||
|
||||
return reverse(f"v2:_api_v2_{model_name}-detail", kwargs = self.get_url_kwargs() )
|
||||
|
||||
|
||||
def get_url_kwargs(self) -> dict:
|
||||
"""Fetch the URL kwargs
|
||||
|
||||
Returns:
|
||||
dict: kwargs required for generating the URL with `reverse`
|
||||
"""
|
||||
|
||||
return {
|
||||
'pk': self.id
|
||||
}
|
||||
|
||||
|
||||
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
|
||||
|
||||
self.clean()
|
||||
|
||||
if self.organization is None:
|
||||
|
||||
raise ValidationError('Organization not defined')
|
||||
|
||||
super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
|
||||
|
||||
|
||||
|
||||
class Team(Group, TenancyObject):
|
||||
|
||||
class Meta:
|
||||
|
||||
ordering = [ 'team_name' ]
|
||||
|
||||
verbose_name = 'Team'
|
||||
|
||||
# proxy = True
|
||||
verbose_name_plural = "Teams"
|
||||
ordering = ['team_name']
|
||||
|
||||
|
||||
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
|
||||
@ -350,89 +205,17 @@ class Team(Group, TenancyObject):
|
||||
|
||||
|
||||
team_name = models.CharField(
|
||||
verbose_name = 'Name',
|
||||
blank = False,
|
||||
help_text = 'Name to give this team',
|
||||
max_length = 50,
|
||||
unique = False,
|
||||
verbose_name = 'Name',
|
||||
default = ''
|
||||
)
|
||||
|
||||
created = AutoCreatedField()
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
page_layout: dict = [
|
||||
{
|
||||
"name": "Details",
|
||||
"slug": "details",
|
||||
"sections": [
|
||||
{
|
||||
"layout": "double",
|
||||
"left": [
|
||||
'organization',
|
||||
'team_name',
|
||||
'created',
|
||||
'modified',
|
||||
],
|
||||
"right": [
|
||||
'model_notes',
|
||||
]
|
||||
},
|
||||
{
|
||||
"layout": "table",
|
||||
"name": "Users",
|
||||
"field": "users",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Knowledge Base",
|
||||
"slug": "kb_articles",
|
||||
"sections": [
|
||||
{
|
||||
"layout": "table",
|
||||
"field": "knowledge_base",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Notes",
|
||||
"slug": "notes",
|
||||
"sections": []
|
||||
},
|
||||
]
|
||||
|
||||
table_fields: list = [
|
||||
'team_name',
|
||||
'modified',
|
||||
'created',
|
||||
]
|
||||
|
||||
|
||||
def get_url( self, request = None ) -> str:
|
||||
|
||||
model_name = str(self._meta.verbose_name.lower()).replace(' ', '_')
|
||||
|
||||
|
||||
if request:
|
||||
|
||||
return reverse(f"v2:_api_v2_organization_team-detail", request=request, kwargs = self.get_url_kwargs() )
|
||||
|
||||
return reverse(f"v2:_api_v2_organization_team-detail", kwargs = self.get_url_kwargs() )
|
||||
|
||||
|
||||
def get_url_kwargs(self) -> dict:
|
||||
"""Fetch the URL kwargs
|
||||
|
||||
Returns:
|
||||
dict: kwargs required for generating the URL with `reverse`
|
||||
"""
|
||||
|
||||
return {
|
||||
'organization_id': self.organization.id,
|
||||
'pk': self.id
|
||||
}
|
||||
|
||||
|
||||
@property
|
||||
def parent_object(self):
|
||||
@ -463,59 +246,36 @@ class Team(Group, TenancyObject):
|
||||
class TeamUsers(SaveHistory):
|
||||
|
||||
class Meta:
|
||||
|
||||
# proxy = True
|
||||
verbose_name_plural = "Team Users"
|
||||
ordering = ['user']
|
||||
|
||||
verbose_name = "Team User"
|
||||
|
||||
verbose_name_plural = "Team Users"
|
||||
|
||||
|
||||
id = models.AutoField(
|
||||
blank=False,
|
||||
help_text = 'ID of this Team User',
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
verbose_name = 'ID'
|
||||
blank=False
|
||||
)
|
||||
|
||||
team = models.ForeignKey(
|
||||
Team,
|
||||
blank = False,
|
||||
help_text = 'Team user belongs to',
|
||||
null = False,
|
||||
on_delete=models.CASCADE,
|
||||
related_name="team",
|
||||
verbose_name = 'Team'
|
||||
)
|
||||
on_delete=models.CASCADE)
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
blank = False,
|
||||
help_text = 'User who will be added to the team',
|
||||
null = False,
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name = 'User'
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
manager = models.BooleanField(
|
||||
blank=True,
|
||||
default=False,
|
||||
help_text = 'Is this user to be a manager of this team',
|
||||
verbose_name='manager',
|
||||
default=False,
|
||||
blank=True
|
||||
)
|
||||
|
||||
created = AutoCreatedField()
|
||||
|
||||
modified = AutoLastModifiedField()
|
||||
|
||||
page_layout: list = []
|
||||
|
||||
table_fields: list = [
|
||||
'user',
|
||||
'manager'
|
||||
]
|
||||
|
||||
|
||||
def delete(self, using=None, keep_parents=False):
|
||||
""" Delete Team
|
||||
@ -537,24 +297,6 @@ class TeamUsers(SaveHistory):
|
||||
return self.team.organization
|
||||
|
||||
|
||||
def get_url( self, request = None ) -> str:
|
||||
|
||||
url_kwargs: dict = {
|
||||
'organization_id': self.team.organization.id,
|
||||
'team_id': self.team.id,
|
||||
'pk': self.id
|
||||
}
|
||||
|
||||
print(f'url kwargs are: {url_kwargs}')
|
||||
|
||||
|
||||
if request:
|
||||
|
||||
return reverse(f"v2:_api_v2_organization_team_user-detail", request=request, kwargs = url_kwargs )
|
||||
|
||||
return reverse(f"v2:_api_v2_organization_team_user-detail", kwargs = url_kwargs )
|
||||
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
""" Save Team
|
||||
|
||||
|
@ -1,97 +0,0 @@
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from access.models import Organization
|
||||
|
||||
from app.serializers.user import UserBaseSerializer
|
||||
|
||||
from core import fields as centurion_field
|
||||
|
||||
|
||||
|
||||
class OrganizationBaseSerializer(serializers.ModelSerializer):
|
||||
|
||||
display_name = serializers.SerializerMethodField('get_display_name')
|
||||
|
||||
def get_display_name(self, item) -> str:
|
||||
|
||||
return str( item )
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="v2:_api_v2_organization-detail", format="html"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Organization
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'name',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'name',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
|
||||
class OrganizationModelSerializer(
|
||||
OrganizationBaseSerializer
|
||||
):
|
||||
|
||||
_urls = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> dict:
|
||||
|
||||
return {
|
||||
'_self': item.get_url( request = self._context['view'].request ),
|
||||
'knowledge_base': reverse(
|
||||
"v2:_api_v2_model_kb-list",
|
||||
request=self._context['view'].request,
|
||||
kwargs={
|
||||
'model': self.Meta.model._meta.model_name,
|
||||
'model_pk': item.pk
|
||||
}
|
||||
),
|
||||
'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:
|
||||
|
||||
model = Organization
|
||||
|
||||
fields = '__all__'
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'name',
|
||||
'model_notes',
|
||||
'manager',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
|
||||
class OrganizationViewSerializer(OrganizationModelSerializer):
|
||||
pass
|
||||
|
||||
manager = UserBaseSerializer(many=False, read_only = True)
|
@ -1,99 +0,0 @@
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from access.models import TeamUsers
|
||||
|
||||
from api.serializers import common
|
||||
|
||||
from app.serializers.user import UserBaseSerializer
|
||||
|
||||
|
||||
|
||||
class TeamUserBaseSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
display_name = serializers.SerializerMethodField('get_display_name')
|
||||
|
||||
def get_display_name(self, item) -> str:
|
||||
|
||||
return str( item )
|
||||
|
||||
url = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> str:
|
||||
|
||||
return item.get_url( request = self.context['view'].request )
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
|
||||
class TeamUserModelSerializer(
|
||||
common.CommonModelSerializer,
|
||||
TeamUserBaseSerializer
|
||||
):
|
||||
|
||||
_urls = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> dict:
|
||||
|
||||
return {
|
||||
'_self': item.get_url( request = self._context['view'].request )
|
||||
}
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'manager',
|
||||
'user',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
|
||||
|
||||
def is_valid(self, *, raise_exception=True) -> bool:
|
||||
|
||||
is_valid = False
|
||||
|
||||
is_valid = super().is_valid(raise_exception=raise_exception)
|
||||
|
||||
self.validated_data['team_id'] = int(self._context['view'].kwargs['team_id'])
|
||||
|
||||
return is_valid
|
||||
|
||||
|
||||
|
||||
class TeamUserViewSerializer(TeamUserModelSerializer):
|
||||
|
||||
user = UserBaseSerializer(read_only = True)
|
@ -1,136 +0,0 @@
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
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 Permission, PermissionBaseSerializer
|
||||
|
||||
from core import fields as centurion_field
|
||||
|
||||
|
||||
|
||||
class TeamBaseSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
display_name = serializers.SerializerMethodField('get_display_name')
|
||||
|
||||
def get_display_name(self, item) -> str:
|
||||
|
||||
return str( item )
|
||||
|
||||
url = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> str:
|
||||
|
||||
return item.get_url( request = self.context['view'].request )
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Team
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'team_name',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'team_name',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
|
||||
class TeamModelSerializer(
|
||||
common.CommonModelSerializer,
|
||||
TeamBaseSerializer
|
||||
):
|
||||
|
||||
|
||||
_urls = serializers.SerializerMethodField('get_url')
|
||||
|
||||
def get_url(self, item) -> dict:
|
||||
|
||||
return {
|
||||
'_self': item.get_url( request = self._context['view'].request ),
|
||||
'knowledge_base': reverse(
|
||||
"v2:_api_v2_model_kb-list",
|
||||
request=self._context['view'].request,
|
||||
kwargs={
|
||||
'model': self.Meta.model._meta.model_name,
|
||||
'model_pk': item.pk
|
||||
}
|
||||
),
|
||||
'users': reverse(
|
||||
'v2:_api_v2_organization_team_user-list',
|
||||
request=self.context['view'].request,
|
||||
kwargs={
|
||||
'organization_id': item.organization.id,
|
||||
'team_id': item.pk
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
team_name = centurion_field.CharField( autolink = True )
|
||||
|
||||
permissions = serializers.PrimaryKeyRelatedField(many = True, queryset=permission_queryset(), required = False)
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Team
|
||||
|
||||
fields = '__all__'
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'team_name',
|
||||
'model_notes',
|
||||
'permissions',
|
||||
'organization',
|
||||
'is_global',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'display_name',
|
||||
'name',
|
||||
'organization',
|
||||
'created',
|
||||
'modified',
|
||||
'_urls',
|
||||
]
|
||||
|
||||
|
||||
|
||||
def is_valid(self, *, raise_exception=True) -> bool:
|
||||
|
||||
is_valid = False
|
||||
|
||||
is_valid = super().is_valid(raise_exception=raise_exception)
|
||||
|
||||
self.validated_data['organization_id'] = int(self._context['view'].kwargs['organization_id'])
|
||||
|
||||
|
||||
return is_valid
|
||||
|
||||
|
||||
|
||||
class TeamViewSerializer(TeamModelSerializer):
|
||||
|
||||
organization = OrganizationBaseSerializer(many=False, read_only=True)
|
||||
|
||||
permissions = PermissionBaseSerializer(many = True)
|
@ -11,19 +11,6 @@ class TenancyObject:
|
||||
model = None
|
||||
""" Model to be tested """
|
||||
|
||||
should_model_history_be_saved: bool = True
|
||||
""" Should model history be saved.
|
||||
|
||||
By default this should always be 'True', however in special
|
||||
circumstances, this may not be desired.
|
||||
"""
|
||||
|
||||
|
||||
def test_history_save(self):
|
||||
"""Confirm the desired intent for saving model history."""
|
||||
|
||||
assert self.model.save_model_history == self.should_model_history_be_saved
|
||||
|
||||
|
||||
def test_has_attr_get_organization(self):
|
||||
""" TenancyObject attribute check
|
||||
|
@ -1,92 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.test import TestCase
|
||||
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from access.serializers.organization import (
|
||||
Organization,
|
||||
OrganizationModelSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
class OrganizationValidationAPI(
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = Organization
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an org
|
||||
2. Create an item
|
||||
"""
|
||||
|
||||
self.user = User.objects.create(username = 'org_user', password='random password')
|
||||
|
||||
self.valid_data = {
|
||||
'name': 'valid_org_data',
|
||||
'manager': self.user.id
|
||||
}
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
name = 'random title',
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_serializer_valid_data(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and no name is provided a validation error occurs
|
||||
"""
|
||||
|
||||
serializer = OrganizationModelSerializer(
|
||||
data = self.valid_data
|
||||
)
|
||||
|
||||
assert serializer.is_valid(raise_exception = True)
|
||||
|
||||
|
||||
|
||||
def test_serializer_validation_no_name(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and no name is provided a validation error occurs
|
||||
"""
|
||||
|
||||
data = self.valid_data.copy()
|
||||
|
||||
del data['name']
|
||||
|
||||
with pytest.raises(ValidationError) as err:
|
||||
|
||||
serializer = OrganizationModelSerializer(
|
||||
data = data
|
||||
)
|
||||
|
||||
serializer.is_valid(raise_exception = True)
|
||||
|
||||
assert err.value.get_codes()['name'][0] == 'required'
|
||||
|
||||
|
||||
|
||||
def test_serializer_validation_manager_optional(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and no name is provided a validation error occurs
|
||||
"""
|
||||
|
||||
data = self.valid_data.copy()
|
||||
|
||||
del data['manager']
|
||||
|
||||
serializer = OrganizationModelSerializer(
|
||||
data = data
|
||||
)
|
||||
|
||||
assert serializer.is_valid(raise_exception = True)
|
@ -1,294 +0,0 @@
|
||||
import pytest
|
||||
import unittest
|
||||
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.shortcuts import reverse
|
||||
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.api_serializer_viewset import SerializersTestCases
|
||||
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional, MetaDataNavigationEntriesFunctional
|
||||
|
||||
|
||||
|
||||
class ViewSetBase:
|
||||
|
||||
model = Organization
|
||||
|
||||
app_namespace = 'v2'
|
||||
|
||||
url_name = '_api_v2_organization'
|
||||
|
||||
change_data = {'name': 'device'}
|
||||
|
||||
delete_data = {}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user and item
|
||||
. create an organization that is different to item
|
||||
2. Create a team
|
||||
3. create teams with each permission: view, add, change, delete
|
||||
4. create a user per team
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.item = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
self.different_organization = different_organization
|
||||
|
||||
self.other_org_item = organization
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team_b = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = different_organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
view_team_b.permissions.set([view_permissions])
|
||||
|
||||
|
||||
|
||||
add_permissions = Permission.objects.get(
|
||||
codename = 'add_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
add_team = Team.objects.create(
|
||||
team_name = 'add_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
add_team.permissions.set([add_permissions])
|
||||
|
||||
|
||||
|
||||
change_permissions = Permission.objects.get(
|
||||
codename = 'change_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
change_team = Team.objects.create(
|
||||
team_name = 'change_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
change_team.permissions.set([change_permissions])
|
||||
|
||||
|
||||
|
||||
delete_permissions = Permission.objects.get(
|
||||
codename = 'delete_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
delete_team = Team.objects.create(
|
||||
team_name = 'delete_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
delete_team.permissions.set([delete_permissions])
|
||||
|
||||
|
||||
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
self.view_user_b = User.objects.create_user(username="test_user_view_b", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = view_team_b,
|
||||
user = self.view_user_b
|
||||
)
|
||||
|
||||
|
||||
self.url_view_kwargs = { 'pk': self.item.id }
|
||||
|
||||
self.add_data = {
|
||||
'name': 'team_post',
|
||||
}
|
||||
|
||||
|
||||
self.super_add_user = User.objects.create_user(username="test_user_add_super", password="password", is_superuser = True)
|
||||
|
||||
self.add_user = User.objects.create_user(username="test_user_add", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = add_team,
|
||||
user = self.add_user
|
||||
)
|
||||
|
||||
self.change_user = User.objects.create_user(username="test_user_change", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = change_team,
|
||||
user = self.change_user
|
||||
)
|
||||
|
||||
self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = delete_team,
|
||||
user = self.delete_user
|
||||
)
|
||||
|
||||
|
||||
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
|
||||
|
||||
|
||||
different_organization_team = Team.objects.create(
|
||||
team_name = 'different_organization_team',
|
||||
organization = different_organization,
|
||||
)
|
||||
|
||||
different_organization_team.permissions.set([
|
||||
view_permissions,
|
||||
add_permissions,
|
||||
change_permissions,
|
||||
delete_permissions,
|
||||
])
|
||||
|
||||
TeamUsers.objects.create(
|
||||
team = different_organization_team,
|
||||
user = self.different_organization_user
|
||||
)
|
||||
|
||||
|
||||
|
||||
class OrganizationPermissionsAPI(
|
||||
ViewSetBase,
|
||||
APIPermissions,
|
||||
TestCase
|
||||
):
|
||||
|
||||
|
||||
|
||||
def test_add_has_permission(self):
|
||||
""" Check correct permission for add
|
||||
|
||||
Attempt to add as user with permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
|
||||
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' )
|
||||
|
||||
|
||||
client.force_login( self.super_add_user )
|
||||
|
||||
response = client.post( url, data = self.add_data )
|
||||
|
||||
assert response.status_code == 201
|
||||
|
||||
|
||||
|
||||
def test_returned_results_only_user_orgs(self):
|
||||
"""Returned results check
|
||||
|
||||
This test case is an override of a test of the same name.
|
||||
organizations are not tenancy objects and therefor are supposed to
|
||||
return all items when a user queries them.
|
||||
|
||||
Ensure that a query to the viewset endpoint does not return
|
||||
items that are not part of the users organizations.
|
||||
"""
|
||||
|
||||
|
||||
# Ensure the other org item exists, without test not able to function
|
||||
print('Check that the different organization item has been defined')
|
||||
assert hasattr(self, 'other_org_item')
|
||||
|
||||
# ensure that the variables for the two orgs are different orgs
|
||||
print('checking that the different and user oganizations are different')
|
||||
assert self.different_organization.id != self.organization.id
|
||||
|
||||
|
||||
client = Client()
|
||||
|
||||
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')
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
contains_different_org: bool = False
|
||||
|
||||
# for item in response.data['results']:
|
||||
|
||||
# if int(item['id']) != self.organization.id:
|
||||
|
||||
# contains_different_org = True
|
||||
|
||||
assert len(response.data['results']) == 2
|
||||
|
||||
|
||||
|
||||
class OrganizationViewSet(
|
||||
ViewSetBase,
|
||||
SerializersTestCases,
|
||||
TestCase
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class OrganizationMetadata(
|
||||
ViewSetBase,
|
||||
MetadataAttributesFunctional,
|
||||
MetaDataNavigationEntriesFunctional,
|
||||
TestCase
|
||||
):
|
||||
|
||||
menu_id = 'access'
|
||||
|
||||
menu_entry_id = 'organization'
|
@ -1,301 +0,0 @@
|
||||
import pytest
|
||||
import unittest
|
||||
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 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
|
||||
|
||||
|
||||
|
||||
class ViewSetBase:
|
||||
|
||||
model = Team
|
||||
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_v2_organization_team'
|
||||
|
||||
change_data = {'name': 'device'}
|
||||
|
||||
delete_data = {'device': 'device'}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user and item
|
||||
. create an organization that is different to item
|
||||
2. Create a team
|
||||
3. create teams with each permission: view, add, change, delete
|
||||
4. create a user per team
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
self.different_organization = different_organization
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization=organization,
|
||||
name = 'teamone'
|
||||
)
|
||||
|
||||
self.other_org_item = self.model.objects.create(
|
||||
organization=different_organization,
|
||||
name = 'teamtwo'
|
||||
)
|
||||
|
||||
|
||||
self.url_kwargs = {'organization_id': self.organization.id}
|
||||
|
||||
self.url_view_kwargs = {'organization_id': self.organization.id, 'pk': self.item.id}
|
||||
|
||||
self.add_data = {'team_name': 'team_post'}
|
||||
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
|
||||
|
||||
add_permissions = Permission.objects.get(
|
||||
codename = 'add_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
add_team = Team.objects.create(
|
||||
team_name = 'add_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
add_team.permissions.set([add_permissions])
|
||||
|
||||
|
||||
|
||||
change_permissions = Permission.objects.get(
|
||||
codename = 'change_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
change_team = Team.objects.create(
|
||||
team_name = 'change_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
change_team.permissions.set([change_permissions])
|
||||
|
||||
|
||||
|
||||
delete_permissions = Permission.objects.get(
|
||||
codename = 'delete_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
delete_team = Team.objects.create(
|
||||
team_name = 'delete_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
delete_team.permissions.set([delete_permissions])
|
||||
|
||||
|
||||
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
self.add_user = User.objects.create_user(username="test_user_add", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = add_team,
|
||||
user = self.add_user
|
||||
)
|
||||
|
||||
self.change_user = User.objects.create_user(username="test_user_change", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = change_team,
|
||||
user = self.change_user
|
||||
)
|
||||
|
||||
self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = delete_team,
|
||||
user = self.delete_user
|
||||
)
|
||||
|
||||
|
||||
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
|
||||
|
||||
|
||||
different_organization_team = Team.objects.create(
|
||||
team_name = 'different_organization_team',
|
||||
organization = different_organization,
|
||||
)
|
||||
|
||||
different_organization_team.permissions.set([
|
||||
view_permissions,
|
||||
add_permissions,
|
||||
change_permissions,
|
||||
delete_permissions,
|
||||
])
|
||||
|
||||
TeamUsers.objects.create(
|
||||
team = different_organization_team,
|
||||
user = self.different_organization_user
|
||||
)
|
||||
|
||||
|
||||
|
||||
class TeamPermissionsAPI(
|
||||
ViewSetBase,
|
||||
APIPermissions,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TeamViewSet(
|
||||
ViewSetBase,
|
||||
SerializersTestCases,
|
||||
TestCase,
|
||||
):
|
||||
|
||||
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
|
||||
|
||||
|
@ -1,180 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from access.models import Organization, Permission
|
||||
|
||||
from access.serializers.teams import (
|
||||
Team,
|
||||
TeamModelSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
class MockView:
|
||||
|
||||
action: str = None
|
||||
|
||||
kwargs: dict = {}
|
||||
|
||||
|
||||
|
||||
class MockRequest:
|
||||
|
||||
user = None
|
||||
|
||||
|
||||
|
||||
class TeamValidationAPI(
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = Team
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an org
|
||||
2. Create an item
|
||||
"""
|
||||
|
||||
self.organization = Organization.objects.create(
|
||||
name = 'team org serializer test'
|
||||
)
|
||||
|
||||
self.user = User.objects.create(username = 'org_user', password='random password')
|
||||
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
self.valid_data = {
|
||||
'organization': self.organization.id,
|
||||
'team_name': 'valid_org_data',
|
||||
'permissions': [
|
||||
view_permissions.id,
|
||||
]
|
||||
}
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization = self.organization,
|
||||
name = 'random team title',
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_serializer_valid_data(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating an item supplied valid data
|
||||
creates an item.
|
||||
"""
|
||||
|
||||
|
||||
mock_view = MockView()
|
||||
mock_view.action = 'create'
|
||||
mock_view.kwargs: dict = {
|
||||
'organization_id': self.organization.id
|
||||
}
|
||||
|
||||
mock_request = MockRequest()
|
||||
mock_request.user = self.user
|
||||
|
||||
mock_view.request = mock_request
|
||||
|
||||
|
||||
serializer = TeamModelSerializer(
|
||||
context = {
|
||||
'request': mock_request,
|
||||
'view': mock_view,
|
||||
},
|
||||
data = self.valid_data
|
||||
)
|
||||
|
||||
assert serializer.is_valid(raise_exception = True)
|
||||
|
||||
|
||||
|
||||
def test_serializer_validation_no_name(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and no name is provided a validation error occurs
|
||||
"""
|
||||
|
||||
|
||||
mock_view = MockView()
|
||||
mock_view.action = 'create'
|
||||
mock_view.kwargs: dict = {
|
||||
'organization_id': self.organization.id
|
||||
}
|
||||
|
||||
mock_request = MockRequest()
|
||||
mock_request.user = self.user
|
||||
|
||||
mock_view.request = mock_request
|
||||
|
||||
|
||||
data = self.valid_data.copy()
|
||||
|
||||
del data['team_name']
|
||||
|
||||
with pytest.raises(ValidationError) as err:
|
||||
|
||||
serializer = TeamModelSerializer(
|
||||
context = {
|
||||
'request': mock_request,
|
||||
'view': mock_view,
|
||||
},
|
||||
data = data
|
||||
)
|
||||
|
||||
serializer.is_valid(raise_exception = True)
|
||||
|
||||
assert err.value.get_codes()['team_name'][0] == 'required'
|
||||
|
||||
|
||||
|
||||
def test_serializer_validation_permissions_optional(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and permissions are not supplied, the item is
|
||||
still created.
|
||||
"""
|
||||
|
||||
|
||||
mock_view = MockView()
|
||||
mock_view.action = 'create'
|
||||
mock_view.kwargs: dict = {
|
||||
'organization_id': self.organization.id
|
||||
}
|
||||
|
||||
mock_request = MockRequest()
|
||||
mock_request.user = self.user
|
||||
|
||||
mock_view.request = mock_request
|
||||
|
||||
|
||||
data = self.valid_data.copy()
|
||||
|
||||
del data['permissions']
|
||||
|
||||
serializer = TeamModelSerializer(
|
||||
context = {
|
||||
'request': mock_request,
|
||||
'view': mock_view,
|
||||
},
|
||||
data = data
|
||||
)
|
||||
|
||||
assert serializer.is_valid(raise_exception = True)
|
@ -1,309 +0,0 @@
|
||||
import pytest
|
||||
import unittest
|
||||
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 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
|
||||
|
||||
|
||||
|
||||
class ViewSetBase:
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_v2_organization_team_user'
|
||||
|
||||
change_data = {'name': 'device'}
|
||||
|
||||
delete_data = {'device': 'device'}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user and item
|
||||
2. Create a team
|
||||
3. create teams with each permission: view, add, change, delete
|
||||
4. create a user per team
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
self.different_organization = different_organization
|
||||
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team_b = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = different_organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
view_team_b.permissions.set([view_permissions])
|
||||
|
||||
|
||||
|
||||
add_permissions = Permission.objects.get(
|
||||
codename = 'add_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
add_team = Team.objects.create(
|
||||
team_name = 'add_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
add_team.permissions.set([add_permissions])
|
||||
|
||||
|
||||
|
||||
change_permissions = Permission.objects.get(
|
||||
codename = 'change_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
change_team = Team.objects.create(
|
||||
team_name = 'change_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
change_team.permissions.set([change_permissions])
|
||||
|
||||
|
||||
|
||||
delete_permissions = Permission.objects.get(
|
||||
codename = 'delete_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
delete_team = Team.objects.create(
|
||||
team_name = 'delete_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
delete_team.permissions.set([delete_permissions])
|
||||
|
||||
|
||||
self.no_permissions_user = User.objects.create_user(username="test_no_permissions", password="password")
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
|
||||
self.view_user_b = User.objects.create_user(username="test_user_view_b", password="password")
|
||||
|
||||
|
||||
self.item = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
self.other_org_item = TeamUsers.objects.create(
|
||||
team = view_team_b,
|
||||
user = self.view_user_b
|
||||
)
|
||||
|
||||
self.url_view_kwargs = {'organization_id': self.organization.id, 'team_id': view_team.id, 'pk': self.item.id}
|
||||
|
||||
self.url_kwargs = {'organization_id': self.organization.id, 'team_id': view_team.id}
|
||||
|
||||
|
||||
random_user = User.objects.create_user(username="random_user", password="password")
|
||||
self.add_data = {'user': random_user.id}
|
||||
|
||||
|
||||
self.add_user = User.objects.create_user(username="test_user_add", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = add_team,
|
||||
user = self.add_user
|
||||
)
|
||||
|
||||
self.change_user = User.objects.create_user(username="test_user_change", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = change_team,
|
||||
user = self.change_user
|
||||
)
|
||||
|
||||
self.delete_user = User.objects.create_user(username="test_user_delete", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = delete_team,
|
||||
user = self.delete_user
|
||||
)
|
||||
|
||||
|
||||
self.different_organization_user = User.objects.create_user(username="test_different_organization_user", password="password")
|
||||
|
||||
|
||||
different_organization_team = Team.objects.create(
|
||||
team_name = 'different_organization_team',
|
||||
organization = different_organization,
|
||||
)
|
||||
|
||||
different_organization_team.permissions.set([
|
||||
view_permissions,
|
||||
add_permissions,
|
||||
change_permissions,
|
||||
delete_permissions,
|
||||
])
|
||||
|
||||
TeamUsers.objects.create(
|
||||
team = different_organization_team,
|
||||
user = self.different_organization_user
|
||||
)
|
||||
|
||||
|
||||
|
||||
class TeamUserPermissionsAPI(
|
||||
ViewSetBase,
|
||||
APIPermissions,
|
||||
TestCase
|
||||
):
|
||||
|
||||
|
||||
def test_returned_results_only_user_orgs(self):
|
||||
"""This test is not applicable for team_user as users are not tenancy objects
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class TeamUserViewSet(
|
||||
ViewSetBase,
|
||||
SerializersTestCases,
|
||||
TestCase
|
||||
):
|
||||
|
||||
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,186 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.test import TestCase
|
||||
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from access.models import Organization, Permission, Team
|
||||
|
||||
from access.serializers.team_user import (
|
||||
TeamUsers,
|
||||
TeamUserModelSerializer
|
||||
)
|
||||
|
||||
|
||||
|
||||
class MockView:
|
||||
|
||||
action: str = None
|
||||
|
||||
kwargs: dict = {}
|
||||
|
||||
|
||||
|
||||
class MockRequest:
|
||||
|
||||
user = None
|
||||
|
||||
|
||||
|
||||
class TeamValidationAPI(
|
||||
TestCase,
|
||||
):
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an org
|
||||
2. Create an item
|
||||
"""
|
||||
|
||||
self.organization = Organization.objects.create(
|
||||
name = 'team org serializer test'
|
||||
)
|
||||
|
||||
self.user = User.objects.create(username = 'org_user', password='random password')
|
||||
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
self.team = Team.objects.create(
|
||||
organization = self.organization,
|
||||
name = 'random team title',
|
||||
)
|
||||
|
||||
|
||||
self.valid_data = {
|
||||
'team': self.team.id,
|
||||
'user': self.user.id
|
||||
}
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
team = self.team,
|
||||
user = self.user,
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_serializer_valid_data(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating an item supplied valid data
|
||||
creates an item.
|
||||
"""
|
||||
|
||||
|
||||
mock_view = MockView()
|
||||
mock_view.action = 'create'
|
||||
mock_view.kwargs: dict = {
|
||||
'organization_id': self.organization.id,
|
||||
'team_id': self.team.id
|
||||
}
|
||||
|
||||
mock_request = MockRequest()
|
||||
mock_request.user = self.user
|
||||
|
||||
mock_view.request = mock_request
|
||||
|
||||
|
||||
serializer = TeamUserModelSerializer(
|
||||
context = {
|
||||
'request': mock_request,
|
||||
'view': mock_view,
|
||||
},
|
||||
data = self.valid_data
|
||||
)
|
||||
|
||||
assert serializer.is_valid(raise_exception = True)
|
||||
|
||||
|
||||
|
||||
def test_serializer_validation_no_team_creates(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and no team is provided no validation
|
||||
error occurs as the team id is collected from the view
|
||||
"""
|
||||
|
||||
|
||||
mock_view = MockView()
|
||||
mock_view.action = 'create'
|
||||
mock_view.kwargs: dict = {
|
||||
'organization_id': self.organization.id,
|
||||
'team_id': self.team.id
|
||||
}
|
||||
|
||||
mock_request = MockRequest()
|
||||
mock_request.user = self.user
|
||||
|
||||
mock_view.request = mock_request
|
||||
|
||||
|
||||
data = self.valid_data.copy()
|
||||
|
||||
del data['team']
|
||||
|
||||
serializer = TeamUserModelSerializer(
|
||||
context = {
|
||||
'request': mock_request,
|
||||
'view': mock_view,
|
||||
},
|
||||
data = data
|
||||
)
|
||||
|
||||
assert serializer.is_valid(raise_exception = True)
|
||||
|
||||
|
||||
|
||||
def test_serializer_validation_no_user(self):
|
||||
"""Serializer Validation Check
|
||||
|
||||
Ensure that if creating and no user is provided a validation error occurs
|
||||
"""
|
||||
|
||||
|
||||
mock_view = MockView()
|
||||
mock_view.action = 'create'
|
||||
mock_view.kwargs: dict = {
|
||||
'organization_id': self.organization.id,
|
||||
'team_id': self.team.id
|
||||
}
|
||||
|
||||
mock_request = MockRequest()
|
||||
mock_request.user = self.user
|
||||
|
||||
mock_view.request = mock_request
|
||||
|
||||
|
||||
data = self.valid_data.copy()
|
||||
|
||||
del data['user']
|
||||
|
||||
with pytest.raises(ValidationError) as err:
|
||||
|
||||
serializer = TeamUserModelSerializer(
|
||||
context = {
|
||||
'request': mock_request,
|
||||
'view': mock_view,
|
||||
},
|
||||
data = data
|
||||
)
|
||||
|
||||
serializer.is_valid(raise_exception = True)
|
||||
|
||||
assert err.value.get_codes()['user'][0] == 'required'
|
@ -17,7 +17,7 @@ class OrganizationAPI(TestCase):
|
||||
|
||||
model = Organization
|
||||
|
||||
app_namespace = 'v1'
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_organization'
|
||||
|
||||
|
@ -1,192 +0,0 @@
|
||||
import pytest
|
||||
import unittest
|
||||
|
||||
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.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.relations import Hyperlink
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_fields import APICommonFields
|
||||
|
||||
|
||||
class OrganizationAPI(
|
||||
TestCase,
|
||||
APICommonFields
|
||||
):
|
||||
|
||||
model = Organization
|
||||
|
||||
app_namespace = 'v2'
|
||||
|
||||
url_name = '_api_v2_organization'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create the object
|
||||
2. create view user
|
||||
3. add user as org manager
|
||||
4. make api request
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org', model_notes='random text')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
|
||||
self.item = organization
|
||||
|
||||
self.url_view_kwargs = {'pk': self.item.id}
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
organization.manager = self.view_user
|
||||
|
||||
organization.save()
|
||||
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
self.api_data = response.data
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
name field must exist
|
||||
"""
|
||||
|
||||
assert 'name' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['name']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_manager(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
manager field must exist
|
||||
"""
|
||||
|
||||
assert 'manager' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_manager(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
manager field must be dict
|
||||
"""
|
||||
|
||||
assert type(self.api_data['manager']) is dict
|
||||
|
||||
|
||||
def test_api_field_exists_manager_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
manager.id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data['manager']
|
||||
|
||||
|
||||
def test_api_field_type_manager_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
manager.id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['manager']['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_manager_display_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
manager.display_name field must exist
|
||||
"""
|
||||
|
||||
assert 'display_name' in self.api_data['manager']
|
||||
|
||||
|
||||
def test_api_field_type_manager_display_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
manager.display_name field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['manager']['display_name']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_manager_url(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
manager.display_name field must exist
|
||||
"""
|
||||
|
||||
assert 'url' in self.api_data['manager']
|
||||
|
||||
|
||||
def test_api_field_type_manager_url(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
manager.url field must be Hyperlink
|
||||
"""
|
||||
|
||||
assert type(self.api_data['manager']['url']) is Hyperlink
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_url_teams(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
_urls.teams field must exist
|
||||
"""
|
||||
|
||||
assert 'teams' in self.api_data['_urls']
|
||||
|
||||
|
||||
def test_api_field_type_url_teams(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
_urls.teams field must be Hyperlink
|
||||
"""
|
||||
|
||||
assert type(self.api_data['_urls']['teams']) is str
|
@ -20,7 +20,7 @@ class OrganizationPermissionsAPI(TestCase, APIPermissionChange, APIPermissionVie
|
||||
model_name = 'organization'
|
||||
app_label = 'access'
|
||||
|
||||
app_namespace = 'v1'
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_organization'
|
||||
|
||||
|
@ -34,7 +34,7 @@ class OrganizationHistory(TestCase):
|
||||
|
||||
|
||||
self.history_create = History.objects.get(
|
||||
action = int(History.Actions.ADD),
|
||||
action = History.Actions.ADD[0],
|
||||
item_pk = self.item_create.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
@ -44,7 +44,7 @@ class OrganizationHistory(TestCase):
|
||||
self.item_change.save()
|
||||
|
||||
self.history_change = History.objects.get(
|
||||
action = int(History.Actions.UPDATE),
|
||||
action = History.Actions.UPDATE[0],
|
||||
item_pk = self.item_change.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
@ -72,7 +72,7 @@ class OrganizationHistory(TestCase):
|
||||
|
||||
history = self.history_create.__dict__
|
||||
|
||||
assert history['action'] == int(History.Actions.ADD)
|
||||
assert history['action'] == int(History.Actions.ADD[0])
|
||||
# assert type(history['action']) is int
|
||||
|
||||
|
||||
@ -125,7 +125,7 @@ class OrganizationHistory(TestCase):
|
||||
|
||||
history = self.history_change.__dict__
|
||||
|
||||
assert history['action'] == int(History.Actions.UPDATE)
|
||||
assert history['action'] == int(History.Actions.UPDATE[0])
|
||||
# assert type(history['action']) is int
|
||||
|
||||
|
||||
|
@ -67,74 +67,4 @@ class TeamModel(
|
||||
|
||||
@pytest.mark.skip(reason="uses Django group manager")
|
||||
def test_model_class_tenancy_manager_function_get_queryset_called(self):
|
||||
pass
|
||||
|
||||
|
||||
def test_model_fields_parameter_not_empty_help_text(self):
|
||||
"""Test Field called with Parameter
|
||||
|
||||
This is a custom test of a test derived of the samae name. It's required
|
||||
as the team model extends the Group model.
|
||||
|
||||
During field creation, paramater `help_text` must not be `None` or empty ('')
|
||||
"""
|
||||
|
||||
group_mode_fields_to_ignore: list = [
|
||||
'id',
|
||||
'name',
|
||||
'group_ptr_id'
|
||||
]
|
||||
|
||||
fields_have_test_value: bool = True
|
||||
|
||||
for field in self.model._meta.fields:
|
||||
|
||||
if field.attname in group_mode_fields_to_ignore:
|
||||
|
||||
continue
|
||||
|
||||
print(f'Checking field {field.attname} is not empty')
|
||||
|
||||
if (
|
||||
field.help_text is None
|
||||
or field.help_text == ''
|
||||
):
|
||||
|
||||
print(f' Failure on field {field.attname}')
|
||||
|
||||
fields_have_test_value = False
|
||||
|
||||
|
||||
assert fields_have_test_value
|
||||
|
||||
def test_model_fields_parameter_type_verbose_name(self):
|
||||
"""Test Field called with Parameter
|
||||
|
||||
This is a custom test of a test derived of the samae name. It's required
|
||||
as the team model extends the Group model.
|
||||
|
||||
During field creation, paramater `verbose_name` must be of type str
|
||||
"""
|
||||
|
||||
group_mode_fields_to_ignore: list = [
|
||||
'name',
|
||||
]
|
||||
|
||||
fields_have_test_value: bool = True
|
||||
|
||||
for field in self.model._meta.fields:
|
||||
|
||||
if field.attname in group_mode_fields_to_ignore:
|
||||
|
||||
continue
|
||||
|
||||
print(f'Checking field {field.attname} is of type str')
|
||||
|
||||
if not type(field.verbose_name) is str:
|
||||
|
||||
print(f' Failure on field {field.attname}')
|
||||
|
||||
fields_have_test_value = False
|
||||
|
||||
|
||||
assert fields_have_test_value
|
||||
pass
|
@ -21,7 +21,7 @@ class TeamAPI(TestCase):
|
||||
|
||||
model = Team
|
||||
|
||||
app_namespace = 'v1'
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_team'
|
||||
|
||||
|
@ -1,174 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.relations import Hyperlink
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_fields import APITenancyObject
|
||||
|
||||
|
||||
|
||||
class TeamAPI(
|
||||
TestCase,
|
||||
APITenancyObject
|
||||
):
|
||||
|
||||
model = Team
|
||||
|
||||
app_namespace = 'v2'
|
||||
|
||||
url_name = '_api_v2_organization_team'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create the object
|
||||
2. create view user
|
||||
3. add user as org manager
|
||||
4. make api request
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization=organization,
|
||||
team_name = 'teamone',
|
||||
model_notes = 'random note'
|
||||
)
|
||||
|
||||
self.url_view_kwargs = {'organization_id': self.organization.id, 'pk': self.item.id}
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
self.item.permissions.set([view_permissions])
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = self.item,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
organization.manager = self.view_user
|
||||
|
||||
organization.save()
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
self.api_data = response.data
|
||||
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_team_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
team_name field must exist
|
||||
"""
|
||||
|
||||
assert 'team_name' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_team_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
team_name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['team_name']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_permissions(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions field must exist
|
||||
"""
|
||||
|
||||
assert 'permissions' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_permissions(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
url field must be list
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions']) is list
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_permissions_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_display_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.display_name field must exist
|
||||
"""
|
||||
|
||||
assert 'display_name' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_permissions_display_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.display_name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['display_name']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_url(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.url field must exist
|
||||
"""
|
||||
|
||||
assert 'url' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_permissions_url(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.url field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['url']) is Hyperlink
|
@ -39,7 +39,7 @@ class TeamHistory(TestCase, HistoryEntry, HistoryEntryChildItem):
|
||||
|
||||
|
||||
self.history_create = History.objects.get(
|
||||
action = int(History.Actions.ADD),
|
||||
action = History.Actions.ADD[0],
|
||||
item_pk = self.item_create.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
@ -51,7 +51,7 @@ class TeamHistory(TestCase, HistoryEntry, HistoryEntryChildItem):
|
||||
self.field_after_expected_value = '{"name": "test_org_' + self.item_change.team_name + '", "team_name": "' + self.item_change.team_name + '"}'
|
||||
|
||||
self.history_change = History.objects.get(
|
||||
action = int(History.Actions.UPDATE),
|
||||
action = History.Actions.UPDATE[0],
|
||||
item_pk = self.item_change.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
@ -68,7 +68,7 @@ class TeamHistory(TestCase, HistoryEntry, HistoryEntryChildItem):
|
||||
self.item_delete.delete()
|
||||
|
||||
self.history_delete = History.objects.get(
|
||||
action = int(History.Actions.DELETE),
|
||||
action = History.Actions.DELETE[0],
|
||||
item_pk = self.deleted_pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
|
@ -18,7 +18,7 @@ class TeamPermissionsAPI(TestCase, APIPermissions):
|
||||
|
||||
model = Team
|
||||
|
||||
app_namespace = 'v1'
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_team'
|
||||
|
||||
|
@ -6,16 +6,9 @@ from django.contrib.auth.models import User
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from app.tests.abstract.models import BaseModel
|
||||
|
||||
from core.mixin.history_save import SaveHistory
|
||||
|
||||
|
||||
|
||||
class TeamUsersModel(
|
||||
TestCase,
|
||||
BaseModel
|
||||
):
|
||||
class TeamUsersModel(TestCase):
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
@ -61,14 +54,3 @@ class TeamUsersModel(
|
||||
"""
|
||||
|
||||
assert self.item.parent_object == self.parent_item
|
||||
|
||||
|
||||
|
||||
@pytest.mark.skip( reason = 'TeamUsers is not a tenancy object' )
|
||||
def test_class_inherits_tenancy_objecy(self):
|
||||
""" Confirm class inheritence
|
||||
|
||||
TenancyObject must inherit TenancyObject
|
||||
"""
|
||||
|
||||
pass
|
||||
|
@ -1,214 +0,0 @@
|
||||
import pytest
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.relations import Hyperlink
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from api.tests.abstract.api_fields import APICommonFields
|
||||
|
||||
|
||||
|
||||
class TeamUserAPI(
|
||||
TestCase,
|
||||
APICommonFields
|
||||
):
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
app_namespace = 'v2'
|
||||
|
||||
url_name = '_api_v2_organization_team_user'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create the object
|
||||
2. create view user
|
||||
3. add user as org manager
|
||||
4. make api request
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
|
||||
|
||||
self.item = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
self.url_view_kwargs = {'organization_id': self.organization.id, 'team_id': view_team.id, 'pk': self.item.id}
|
||||
|
||||
self.url_kwargs = {'organization_id': self.organization.id, 'team_id': view_team.id}
|
||||
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
self.api_data = response.data
|
||||
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_manager(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
manager field must exist
|
||||
"""
|
||||
|
||||
assert 'manager' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_manager(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
manager field must be bool
|
||||
"""
|
||||
|
||||
assert type(self.api_data['manager']) is bool
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_created(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
created field must exist
|
||||
"""
|
||||
|
||||
assert 'created' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_created(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
created field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['created']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_modified(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
modified field must exist
|
||||
"""
|
||||
|
||||
assert 'modified' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_modified(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
modified field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['modified']) is str
|
||||
|
||||
|
||||
|
||||
|
||||
# def test_api_field_exists_permissions(self):
|
||||
# """ Test for existance of API Field
|
||||
|
||||
# permissions field must exist
|
||||
# """
|
||||
|
||||
# assert 'permissions' in self.api_data
|
||||
|
||||
|
||||
# def test_api_field_type_permissions(self):
|
||||
# """ Test for type for API Field
|
||||
|
||||
# url field must be list
|
||||
# """
|
||||
|
||||
# assert type(self.api_data['permissions']) is list
|
||||
|
||||
|
||||
|
||||
# def test_api_field_exists_permissions_id(self):
|
||||
# """ Test for existance of API Field
|
||||
|
||||
# permissions.id field must exist
|
||||
# """
|
||||
|
||||
# assert 'id' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
# def test_api_field_type_permissions_id(self):
|
||||
# """ Test for type for API Field
|
||||
|
||||
# permissions.id field must be int
|
||||
# """
|
||||
|
||||
# assert type(self.api_data['permissions'][0]['id']) is int
|
||||
|
||||
|
||||
# def test_api_field_exists_permissions_display_name(self):
|
||||
# """ Test for existance of API Field
|
||||
|
||||
# permissions.display_name field must exist
|
||||
# """
|
||||
|
||||
# assert 'display_name' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
# def test_api_field_type_permissions_display_name(self):
|
||||
# """ Test for type for API Field
|
||||
|
||||
# permissions.display_name field must be str
|
||||
# """
|
||||
|
||||
# assert type(self.api_data['permissions'][0]['display_name']) is str
|
||||
|
||||
|
||||
|
||||
# def test_api_field_exists_permissions_url(self):
|
||||
# """ Test for existance of API Field
|
||||
|
||||
# permissions.url field must exist
|
||||
# """
|
||||
|
||||
# assert 'url' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
# def test_api_field_type_permissions_url(self):
|
||||
# """ Test for type for API Field
|
||||
|
||||
# permissions.url field must be str
|
||||
# """
|
||||
|
||||
# assert type(self.api_data['permissions'][0]['url']) is Hyperlink
|
@ -48,7 +48,7 @@ class TeamUsersHistory(TestCase, HistoryEntry, HistoryEntryChildItem):
|
||||
|
||||
|
||||
self.history_create = History.objects.get(
|
||||
action = int(History.Actions.ADD),
|
||||
action = History.Actions.ADD[0],
|
||||
item_pk = self.item_create.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
@ -60,7 +60,7 @@ class TeamUsersHistory(TestCase, HistoryEntry, HistoryEntryChildItem):
|
||||
self.field_after_expected_value = '{"manager": true}'
|
||||
|
||||
self.history_change = History.objects.get(
|
||||
action = int(History.Actions.UPDATE),
|
||||
action = History.Actions.UPDATE[0],
|
||||
item_pk = self.item_change.pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
@ -81,7 +81,7 @@ class TeamUsersHistory(TestCase, HistoryEntry, HistoryEntryChildItem):
|
||||
self.item_delete.delete()
|
||||
|
||||
self.history_delete = History.objects.get(
|
||||
action = int(History.Actions.DELETE),
|
||||
action = History.Actions.DELETE[0],
|
||||
item_pk = self.deleted_pk,
|
||||
item_class = self.model._meta.model_name,
|
||||
)
|
||||
|
@ -91,13 +91,3 @@ class TenancyObjectTests(TestCase):
|
||||
"""
|
||||
|
||||
assert self.item.objects is not None
|
||||
|
||||
|
||||
@pytest.mark.skip(reason="write test")
|
||||
def test_field_not_none_organzation(self):
|
||||
""" Ensure field is set
|
||||
|
||||
Field organization must be defined for all tenancy objects
|
||||
"""
|
||||
|
||||
assert self.item.objects is not None
|
||||
|
@ -1,42 +0,0 @@
|
||||
from django.contrib.auth.models import User
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from access.models import Organization
|
||||
|
||||
from api.tests.abstract.viewsets import ViewSetCommon
|
||||
|
||||
from access.viewsets.index import Index
|
||||
|
||||
|
||||
class AccessViewset(
|
||||
TestCase,
|
||||
ViewSetCommon
|
||||
):
|
||||
|
||||
viewset = Index
|
||||
|
||||
route_name = 'API:_api_v2_access_home'
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user
|
||||
3. create super user
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_add", password="password", is_superuser=True)
|
||||
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.route_name + '-list')
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
self.http_options_response_list = client.options(url)
|
@ -1,30 +0,0 @@
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
from api.viewsets.common import CommonViewSet
|
||||
|
||||
|
||||
|
||||
@extend_schema(exclude = True)
|
||||
class Index(CommonViewSet):
|
||||
|
||||
allowed_methods: list = [
|
||||
'GET',
|
||||
'HEAD',
|
||||
'OPTIONS'
|
||||
]
|
||||
|
||||
view_description = "Access Module"
|
||||
|
||||
view_name = "Access"
|
||||
|
||||
|
||||
def list(self, request, pk=None):
|
||||
|
||||
return Response(
|
||||
{
|
||||
"organization": reverse('v2:_api_v2_organization-list', request=request)
|
||||
}
|
||||
)
|
@ -1,89 +0,0 @@
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse
|
||||
|
||||
from access.serializers.organization import (
|
||||
Organization,
|
||||
OrganizationModelSerializer,
|
||||
OrganizationViewSerializer
|
||||
)
|
||||
|
||||
from api.viewsets.common import ModelViewSet
|
||||
|
||||
|
||||
|
||||
# @extend_schema(tags=['access'])
|
||||
@extend_schema_view(
|
||||
create=extend_schema(
|
||||
summary = 'Create an orgnaization',
|
||||
description='',
|
||||
responses = {
|
||||
# 200: OpenApiResponse(description='Allready exists', response=OrganizationViewSerializer),
|
||||
201: OpenApiResponse(description='Created', response=OrganizationViewSerializer),
|
||||
# 400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing add permissions'),
|
||||
}
|
||||
),
|
||||
destroy = extend_schema(
|
||||
summary = 'Delete an orgnaization',
|
||||
description = '',
|
||||
responses = {
|
||||
204: OpenApiResponse(description=''),
|
||||
403: OpenApiResponse(description='User is missing delete permissions'),
|
||||
}
|
||||
),
|
||||
list = extend_schema(
|
||||
summary = 'Fetch all orgnaizations',
|
||||
description='',
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=OrganizationViewSerializer),
|
||||
403: OpenApiResponse(description='User is missing view permissions'),
|
||||
}
|
||||
),
|
||||
retrieve = extend_schema(
|
||||
summary = 'Fetch a single orgnaization',
|
||||
description='',
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=OrganizationViewSerializer),
|
||||
403: OpenApiResponse(description='User is missing view permissions'),
|
||||
}
|
||||
),
|
||||
update = extend_schema(exclude = True),
|
||||
partial_update = extend_schema(
|
||||
summary = 'Update an orgnaization',
|
||||
description = '',
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=OrganizationViewSerializer),
|
||||
# 201: OpenApiResponse(description='Created', response=OrganizationViewSerializer),
|
||||
# # 400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing change permissions'),
|
||||
}
|
||||
),
|
||||
)
|
||||
class ViewSet( ModelViewSet ):
|
||||
|
||||
filterset_fields = [
|
||||
'name',
|
||||
'manager',
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
'name',
|
||||
]
|
||||
|
||||
model = Organization
|
||||
|
||||
documentation: str = ''
|
||||
|
||||
view_description = 'Centurion Organizations'
|
||||
|
||||
def get_serializer_class(self):
|
||||
|
||||
if (
|
||||
self.action == 'list'
|
||||
or self.action == 'retrieve'
|
||||
):
|
||||
|
||||
return globals()[str( self.model._meta.verbose_name) + 'ViewSerializer']
|
||||
|
||||
|
||||
return globals()[str( self.model._meta.verbose_name) + 'ModelSerializer']
|
||||
|
@ -1,186 +0,0 @@
|
||||
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiResponse
|
||||
|
||||
from access.models import Organization
|
||||
|
||||
from access.serializers.teams import (
|
||||
Team,
|
||||
TeamModelSerializer,
|
||||
TeamViewSerializer
|
||||
)
|
||||
|
||||
from api.viewsets.common import ModelViewSet
|
||||
|
||||
|
||||
|
||||
# @extend_schema(tags=['access'])
|
||||
@extend_schema_view(
|
||||
create=extend_schema(
|
||||
summary = 'Create a team within this organization',
|
||||
description='',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='Allready exists', response=TeamViewSerializer),
|
||||
201: OpenApiResponse(description='Created', response=TeamViewSerializer),
|
||||
# 400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing add permissions'),
|
||||
}
|
||||
),
|
||||
destroy = extend_schema(
|
||||
summary = 'Delete a team from this organization',
|
||||
description = '',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
204: OpenApiResponse(description=''),
|
||||
403: OpenApiResponse(description='User is missing delete permissions'),
|
||||
}
|
||||
),
|
||||
list = extend_schema(
|
||||
summary = 'Fetch all teams from this organization',
|
||||
description='',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=TeamViewSerializer),
|
||||
403: OpenApiResponse(description='User is missing view permissions'),
|
||||
}
|
||||
),
|
||||
retrieve = extend_schema(
|
||||
summary = 'Fetch a single team from this organization',
|
||||
description='',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=TeamViewSerializer),
|
||||
403: OpenApiResponse(description='User is missing view permissions'),
|
||||
}
|
||||
),
|
||||
update = extend_schema(exclude = True),
|
||||
partial_update = extend_schema(
|
||||
summary = 'Update a team within this organization',
|
||||
description = '',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=TeamViewSerializer),
|
||||
# 201: OpenApiResponse(description='Created', response=OrganizationViewSerializer),
|
||||
# # 400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing change permissions'),
|
||||
}
|
||||
),
|
||||
)
|
||||
class ViewSet( ModelViewSet ):
|
||||
|
||||
filterset_fields = [
|
||||
'team_name',
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
'team_name',
|
||||
]
|
||||
|
||||
model = Team
|
||||
|
||||
documentation: str = ''
|
||||
|
||||
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()
|
||||
|
||||
queryset = queryset.filter(organization_id=self.kwargs['organization_id'])
|
||||
|
||||
self.queryset = queryset
|
||||
|
||||
return self.queryset
|
||||
|
||||
def get_serializer_class(self):
|
||||
|
||||
if (
|
||||
self.action == 'list'
|
||||
or self.action == 'retrieve'
|
||||
):
|
||||
|
||||
return globals()[str( self.model._meta.verbose_name) + 'ViewSerializer']
|
||||
|
||||
|
||||
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,212 +0,0 @@
|
||||
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,
|
||||
TeamUserViewSerializer
|
||||
)
|
||||
|
||||
from api.viewsets.common import ModelViewSet
|
||||
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
create=extend_schema(
|
||||
summary = 'Create a user within this team',
|
||||
description='',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'team_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
# 200: OpenApiResponse(description='Allready exists', response=TeamUserViewSerializer),
|
||||
201: OpenApiResponse(description='Created', response=TeamUserViewSerializer),
|
||||
# 400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing add permissions'),
|
||||
}
|
||||
),
|
||||
destroy = extend_schema(
|
||||
summary = 'Delete a user from this team',
|
||||
description = '',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'team_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
204: OpenApiResponse(description=''),
|
||||
403: OpenApiResponse(description='User is missing delete permissions'),
|
||||
}
|
||||
),
|
||||
list = extend_schema(
|
||||
summary = 'Fetch all users from this team',
|
||||
description='',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'team_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=TeamUserViewSerializer),
|
||||
403: OpenApiResponse(description='User is missing view permissions'),
|
||||
}
|
||||
),
|
||||
retrieve = extend_schema(
|
||||
summary = 'Fetch a single user from this team',
|
||||
description='',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'team_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=TeamUserViewSerializer),
|
||||
403: OpenApiResponse(description='User is missing view permissions'),
|
||||
}
|
||||
),
|
||||
update = extend_schema(exclude = True),
|
||||
partial_update = extend_schema(
|
||||
summary = 'Update a user within this team',
|
||||
description = '',
|
||||
parameters = [
|
||||
OpenApiParameter(
|
||||
name = 'id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'organization_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
OpenApiParameter(
|
||||
name = 'team_id',
|
||||
location = 'path',
|
||||
type = int
|
||||
),
|
||||
],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='', response=TeamUserViewSerializer),
|
||||
# 201: OpenApiResponse(description='Created', response=OrganizationViewSerializer),
|
||||
# # 400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing change permissions'),
|
||||
}
|
||||
),
|
||||
)
|
||||
class ViewSet( ModelViewSet ):
|
||||
|
||||
filterset_fields = [
|
||||
'manager',
|
||||
'team__organization',
|
||||
]
|
||||
|
||||
search_fields = []
|
||||
|
||||
model = TeamUsers
|
||||
|
||||
documentation: str = ''
|
||||
|
||||
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()
|
||||
|
||||
queryset = queryset.filter(
|
||||
team_id = self.kwargs['team_id']
|
||||
)
|
||||
|
||||
self.queryset = queryset
|
||||
|
||||
return self.queryset
|
||||
|
||||
|
||||
def get_serializer_class(self):
|
||||
|
||||
if (
|
||||
self.action == 'list'
|
||||
or self.action == 'retrieve'
|
||||
):
|
||||
|
||||
return globals()[str( self.model._meta.verbose_name).replace(' ', '') + 'ViewSerializer']
|
||||
|
||||
|
||||
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
|
@ -5,20 +5,6 @@ from rest_framework.authentication import BaseAuthentication, get_authorization_
|
||||
|
||||
from api.models.tokens import AuthToken
|
||||
|
||||
# scheme.py
|
||||
from drf_spectacular.extensions import OpenApiAuthenticationExtension
|
||||
|
||||
class TokenScheme(OpenApiAuthenticationExtension):
|
||||
target_class = "api.auth.TokenAuthentication"
|
||||
name = "TokenAuthentication"
|
||||
|
||||
def get_security_definition(self, auto_schema):
|
||||
return {
|
||||
"type": "apiKey",
|
||||
"in": "header",
|
||||
"name": "Token Authorization",
|
||||
"description": "Token-based authentication with required prefix 'Token'",
|
||||
}
|
||||
|
||||
|
||||
class TokenAuthentication(BaseAuthentication):
|
||||
|
@ -1,8 +0,0 @@
|
||||
from rest_framework.exceptions import APIException
|
||||
from rest_framework import status
|
||||
|
||||
|
||||
class UnknownTicketType(APIException):
|
||||
status_code = status.HTTP_400_BAD_REQUEST
|
||||
default_detail = 'Unable to determin the ticket type.'
|
||||
default_code = 'unknown_ticket_type'
|
@ -1,41 +0,0 @@
|
||||
# Generated by Django 5.1.2 on 2024-12-06 06:47
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('api', '0001_initial'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='authtoken',
|
||||
name='expires',
|
||||
field=models.DateTimeField(help_text='When this token expires', verbose_name='Expiry Date'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='authtoken',
|
||||
name='id',
|
||||
field=models.AutoField(help_text='ID of this token', primary_key=True, serialize=False, unique=True, verbose_name='ID'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='authtoken',
|
||||
name='note',
|
||||
field=models.CharField(blank=True, default=None, help_text='A note about this token', max_length=50, null=True, verbose_name='Note'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='authtoken',
|
||||
name='token',
|
||||
field=models.CharField(db_index=True, help_text='The authorization token', max_length=64, unique=True, verbose_name='Auth Token'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='authtoken',
|
||||
name='user',
|
||||
field=models.ForeignKey(help_text='User this token belongs to', on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='Owner'),
|
||||
),
|
||||
]
|
@ -48,45 +48,37 @@ class AuthToken(models.Model):
|
||||
|
||||
|
||||
id = models.AutoField(
|
||||
blank=False,
|
||||
help_text = 'ID of this token',
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
verbose_name = 'ID'
|
||||
blank=False
|
||||
)
|
||||
|
||||
note = models.CharField(
|
||||
blank = True,
|
||||
default = None,
|
||||
help_text = 'A note about this token',
|
||||
max_length = 50,
|
||||
default = None,
|
||||
null= True,
|
||||
verbose_name = 'Note'
|
||||
)
|
||||
|
||||
token = models.CharField(
|
||||
blank = False,
|
||||
verbose_name = 'Auth Token',
|
||||
db_index=True,
|
||||
help_text = 'The authorization token',
|
||||
max_length = 64,
|
||||
null = False,
|
||||
blank = False,
|
||||
unique = True,
|
||||
verbose_name = 'Auth Token',
|
||||
)
|
||||
|
||||
|
||||
user = models.ForeignKey(
|
||||
settings.AUTH_USER_MODEL,
|
||||
help_text = 'User this token belongs to',
|
||||
on_delete=models.CASCADE,
|
||||
verbose_name = 'Owner'
|
||||
on_delete=models.CASCADE
|
||||
)
|
||||
|
||||
expires = models.DateTimeField(
|
||||
blank = False,
|
||||
help_text = 'When this token expires',
|
||||
null = False,
|
||||
verbose_name = 'Expiry Date',
|
||||
null = False,
|
||||
blank = False
|
||||
)
|
||||
|
||||
|
||||
|
@ -1,526 +0,0 @@
|
||||
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
|
||||
from rest_framework.reverse import reverse
|
||||
from rest_framework.utils.field_mapping import ClassLookupDict
|
||||
|
||||
from rest_framework_json_api.utils import get_related_resource_type
|
||||
|
||||
from access.models import Organization
|
||||
|
||||
from app.serializers.user import User, UserBaseSerializer
|
||||
|
||||
from core import fields as centurion_field
|
||||
from core.fields.badge import BadgeField
|
||||
from core.fields.icon import IconField
|
||||
|
||||
|
||||
|
||||
class OverRideJSONAPIMetadata(JSONAPIMetadata):
|
||||
|
||||
type_lookup = ClassLookupDict(
|
||||
{
|
||||
serializers.Field: "GenericField",
|
||||
serializers.RelatedField: "Relationship",
|
||||
serializers.BooleanField: "Boolean",
|
||||
serializers.CharField: "String",
|
||||
serializers.URLField: "URL",
|
||||
serializers.EmailField: "Email",
|
||||
serializers.RegexField: "Regex",
|
||||
serializers.SlugField: "Slug",
|
||||
serializers.IntegerField: "Integer",
|
||||
serializers.FloatField: "Float",
|
||||
serializers.DecimalField: "Decimal",
|
||||
serializers.DateField: "Date",
|
||||
serializers.DateTimeField: "DateTime",
|
||||
serializers.TimeField: "Time",
|
||||
serializers.ChoiceField: "Choice",
|
||||
serializers.MultipleChoiceField: "MultipleChoice",
|
||||
serializers.FileField: "File",
|
||||
serializers.ImageField: "Image",
|
||||
serializers.ListField: "List",
|
||||
serializers.DictField: "Dict",
|
||||
serializers.Serializer: "Serializer",
|
||||
serializers.JSONField: "JSON", # New. Does not exist in base class
|
||||
BadgeField: 'Badge',
|
||||
IconField: 'Icon',
|
||||
User: 'Relationship',
|
||||
UserBaseSerializer: 'Relationship',
|
||||
centurion_field.CharField: 'String',
|
||||
centurion_field.MarkdownField: 'Markdown'
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
class ReactUIMetadata(OverRideJSONAPIMetadata):
|
||||
|
||||
|
||||
def determine_metadata(self, request, view):
|
||||
|
||||
metadata = {}
|
||||
|
||||
metadata["name"] = view.get_view_name()
|
||||
|
||||
metadata["description"] = view.get_view_description()
|
||||
|
||||
metadata['urls']: dict = {}
|
||||
|
||||
url_self = None
|
||||
|
||||
|
||||
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 )
|
||||
|
||||
|
||||
elif view.kwargs:
|
||||
|
||||
url_self = reverse('v2:' + view.basename + '-list', request = view.request, kwargs = view.kwargs )
|
||||
|
||||
else:
|
||||
|
||||
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"] = [
|
||||
renderer.media_type for renderer in view.renderer_classes
|
||||
]
|
||||
|
||||
metadata["parses"] = [parser.media_type for parser in view.parser_classes]
|
||||
|
||||
metadata["allowed_methods"] = view.allowed_methods
|
||||
|
||||
if hasattr(view, 'get_serializer'):
|
||||
serializer = view.get_serializer()
|
||||
metadata['fields'] = self.get_serializer_info(serializer)
|
||||
|
||||
|
||||
if view.suffix == 'Instance':
|
||||
|
||||
metadata['layout'] = view.get_page_layout()
|
||||
|
||||
|
||||
if hasattr(view, 'get_model_documentation'):
|
||||
|
||||
if view.get_model_documentation():
|
||||
|
||||
metadata['documentation'] = view.get_model_documentation()
|
||||
|
||||
|
||||
elif view.suffix == 'List':
|
||||
|
||||
if hasattr(view, 'table_fields'):
|
||||
|
||||
metadata['table_fields'] = view.get_table_fields()
|
||||
|
||||
if view.documentation:
|
||||
|
||||
metadata['documentation'] = view.documentation
|
||||
|
||||
if hasattr(view, 'page_layout'):
|
||||
|
||||
metadata['layout'] = view.get_page_layout()
|
||||
|
||||
|
||||
build_repo: str = None
|
||||
|
||||
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
|
||||
|
||||
|
||||
|
||||
|
||||
def get_field_info(self, field):
|
||||
""" Custom from `rest_framewarok_json_api.metadata.py`
|
||||
|
||||
Require that read-only fields have their choices added to the
|
||||
metadata.
|
||||
|
||||
Given an instance of a serializer field, return a dictionary
|
||||
of metadata about it.
|
||||
"""
|
||||
field_info = {}
|
||||
serializer = field.parent
|
||||
|
||||
if hasattr(field, 'textarea'):
|
||||
|
||||
if field.textarea:
|
||||
|
||||
field_info["multi_line"] = True
|
||||
|
||||
if isinstance(field, serializers.ManyRelatedField):
|
||||
field_info["type"] = self.type_lookup[field.child_relation]
|
||||
else:
|
||||
field_info["type"] = self.type_lookup[field]
|
||||
|
||||
try:
|
||||
serializer_model = serializer.Meta.model
|
||||
field_info["relationship_type"] = self.relation_type_lookup[
|
||||
getattr(serializer_model, field.field_name)
|
||||
]
|
||||
except KeyError:
|
||||
pass
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
field_info["relationship_resource"] = get_related_resource_type(field)
|
||||
|
||||
if hasattr(field, 'autolink'):
|
||||
|
||||
if field.autolink:
|
||||
|
||||
field_info['autolink'] = field.autolink
|
||||
|
||||
|
||||
field_info["required"] = getattr(field, "required", False)
|
||||
|
||||
|
||||
if hasattr(field, 'style_class'):
|
||||
|
||||
field_info["style"]: dict = {
|
||||
'class': field.style_class
|
||||
}
|
||||
|
||||
|
||||
attrs = [
|
||||
"read_only",
|
||||
"write_only",
|
||||
"label",
|
||||
"help_text",
|
||||
"min_length",
|
||||
"max_length",
|
||||
"min_value",
|
||||
"max_value",
|
||||
"initial",
|
||||
]
|
||||
|
||||
for attr in attrs:
|
||||
value = getattr(field, attr, None)
|
||||
if value is not None and value != "":
|
||||
field_info[attr] = force_str(value, strings_only=True)
|
||||
|
||||
if getattr(field, "child", None):
|
||||
field_info["child"] = self.get_field_info(field.child)
|
||||
elif getattr(field, "fields", None):
|
||||
field_info["children"] = self.get_serializer_info(field)
|
||||
|
||||
if (
|
||||
hasattr(field, "choices")
|
||||
):
|
||||
field_info["choices"] = [
|
||||
{
|
||||
"value": choice_value,
|
||||
"display_name": force_str(choice_name, strings_only=True),
|
||||
}
|
||||
for choice_value, choice_name in field.choices.items()
|
||||
]
|
||||
|
||||
if (
|
||||
hasattr(serializer, "included_serializers")
|
||||
and "relationship_resource" in field_info
|
||||
):
|
||||
field_info["allows_include"] = (
|
||||
field.field_name in serializer.included_serializers
|
||||
)
|
||||
|
||||
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',
|
||||
]
|
||||
|
||||
|
||||
# user = view.request.user
|
||||
|
||||
user_orgainzations = Organization.objects.filter(
|
||||
manager = user
|
||||
)
|
||||
|
||||
|
||||
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(
|
||||
app == 'access'
|
||||
and permission == 'view_organization'
|
||||
and len(user_orgainzations) > 0
|
||||
):
|
||||
|
||||
if page not in new_pages:
|
||||
|
||||
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
|
@ -25,7 +25,7 @@ class TeamSerializerBase(serializers.ModelSerializer):
|
||||
|
||||
request = self.context.get('request')
|
||||
|
||||
return request.build_absolute_uri(reverse("v1:_api_team", args=[obj.organization.id,obj.pk]))
|
||||
return request.build_absolute_uri(reverse("API:_api_team", args=[obj.organization.id,obj.pk]))
|
||||
|
||||
|
||||
|
||||
@ -48,7 +48,7 @@ class TeamSerializer(TeamSerializerBase):
|
||||
|
||||
team = Team.objects.get(pk=obj.id)
|
||||
|
||||
return request.build_absolute_uri(reverse('v1:_api_team_permission', args=[team.organization_id,team.id]))
|
||||
return request.build_absolute_uri(reverse('API:_api_team_permission', args=[team.organization_id,team.id]))
|
||||
|
||||
|
||||
def validate(self, data):
|
||||
@ -67,7 +67,7 @@ class TeamSerializer(TeamSerializerBase):
|
||||
|
||||
request = self.context.get('request')
|
||||
|
||||
return request.build_absolute_uri(reverse('v1:_api_team', args=[obj.organization_id,obj.id]))
|
||||
return request.build_absolute_uri(reverse('API:_api_team', args=[obj.organization_id,obj.id]))
|
||||
|
||||
|
||||
class Meta:
|
||||
@ -93,7 +93,7 @@ class TeamSerializer(TeamSerializerBase):
|
||||
class OrganizationListSerializer(serializers.ModelSerializer):
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="v1:_api_organization", format="html"
|
||||
view_name="API:_api_organization", format="html"
|
||||
)
|
||||
|
||||
|
||||
@ -110,7 +110,7 @@ class OrganizationListSerializer(serializers.ModelSerializer):
|
||||
class OrganizationSerializer(serializers.ModelSerializer):
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="v1:_api_organization", format="html"
|
||||
view_name="API:_api_organization", format="html"
|
||||
)
|
||||
|
||||
team_url = serializers.SerializerMethodField('get_url')
|
||||
@ -121,11 +121,11 @@ class OrganizationSerializer(serializers.ModelSerializer):
|
||||
|
||||
team = Team.objects.filter(pk=obj.id)
|
||||
|
||||
return request.build_absolute_uri(reverse('v1:_api_organization_teams', args=[obj.id]))
|
||||
return request.build_absolute_uri(reverse('API:_api_organization_teams', args=[obj.id]))
|
||||
|
||||
teams = TeamSerializer(source='team_set', many=True, read_only=False)
|
||||
|
||||
view_name="v1:_api_organization"
|
||||
view_name="API:_api_organization"
|
||||
|
||||
|
||||
class Meta:
|
||||
|
@ -1,63 +0,0 @@
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from api.serializers.core.ticket import TicketSerializer
|
||||
|
||||
from core.models.ticket.ticket import Ticket
|
||||
|
||||
|
||||
|
||||
class RequestTicketSerializer(
|
||||
TicketSerializer,
|
||||
):
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Ticket
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'assigned_teams',
|
||||
'assigned_users',
|
||||
'category',
|
||||
'created',
|
||||
'modified',
|
||||
'status',
|
||||
'title',
|
||||
'description',
|
||||
'estimate',
|
||||
'urgency',
|
||||
'impact',
|
||||
'priority',
|
||||
'external_ref',
|
||||
'external_system',
|
||||
'ticket_type',
|
||||
'is_deleted',
|
||||
'date_closed',
|
||||
# 'planned_start_date',
|
||||
# 'planned_finish_date',
|
||||
# 'real_start_date',
|
||||
# 'real_finish_date',
|
||||
'opened_by',
|
||||
'organization',
|
||||
'project',
|
||||
'milestone',
|
||||
'subscribed_teams',
|
||||
'subscribed_users',
|
||||
'ticket_comments',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'ticket_type',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
|
||||
super().__init__(instance=instance, data=data, **kwargs)
|
||||
|
||||
self.fields.fields['category'].queryset = self.fields.fields['category'].queryset.filter(
|
||||
request = True
|
||||
)
|
@ -1,53 +0,0 @@
|
||||
|
||||
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):
|
||||
|
||||
pass
|
||||
|
||||
|
||||
|
||||
class CommonModelSerializer(CommonBaseSerializer):
|
||||
"""Common Model Serializer
|
||||
|
||||
_**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)
|
@ -28,7 +28,7 @@ class ParentGroupSerializer(serializers.ModelSerializer):
|
||||
|
||||
request = self.context.get('request')
|
||||
|
||||
return request.build_absolute_uri(reverse("v1:_api_config_group", args=[obj.pk]))
|
||||
return request.build_absolute_uri(reverse("API:_api_config_group", args=[obj.pk]))
|
||||
|
||||
|
||||
|
||||
@ -59,7 +59,7 @@ class ConfigGroupsSerializerBase(serializers.ModelSerializer):
|
||||
|
||||
request = self.context.get('request')
|
||||
|
||||
return request.build_absolute_uri(reverse("v1:_api_config_group", args=[obj.pk]))
|
||||
return request.build_absolute_uri(reverse("API:_api_config_group", args=[obj.pk]))
|
||||
|
||||
|
||||
|
||||
@ -74,7 +74,6 @@ class ConfigGroupsSerializer(ConfigGroupsSerializerBase):
|
||||
'parent',
|
||||
'name',
|
||||
'config',
|
||||
'hosts',
|
||||
'url',
|
||||
]
|
||||
read_only_fields = [
|
||||
|
@ -1,196 +0,0 @@
|
||||
from django.urls import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from api.serializers.core.ticket_comment import TicketCommentSerializer
|
||||
|
||||
from core import exceptions as centurion_exception
|
||||
from core.forms.validate_ticket import TicketValidation
|
||||
from core.models.ticket.ticket import Ticket
|
||||
|
||||
|
||||
|
||||
class TicketSerializer(
|
||||
serializers.ModelSerializer,
|
||||
TicketValidation,
|
||||
):
|
||||
|
||||
url = serializers.SerializerMethodField('get_url_ticket')
|
||||
|
||||
|
||||
def get_url_ticket(self, item):
|
||||
|
||||
request = self.context.get('request')
|
||||
|
||||
kwargs: dict = {
|
||||
'pk': item.id
|
||||
}
|
||||
|
||||
if item.ticket_type == self.Meta.model.TicketType.CHANGE.value:
|
||||
|
||||
view_name = '_api_itim_change'
|
||||
|
||||
elif item.ticket_type == self.Meta.model.TicketType.INCIDENT.value:
|
||||
|
||||
view_name = '_api_itim_incident'
|
||||
|
||||
elif item.ticket_type == self.Meta.model.TicketType.PROBLEM.value:
|
||||
|
||||
view_name = '_api_itim_problem'
|
||||
|
||||
elif item.ticket_type == self.Meta.model.TicketType.REQUEST.value:
|
||||
|
||||
view_name = '_api_assistance_request'
|
||||
|
||||
elif item.ticket_type == self.Meta.model.TicketType.PROJECT_TASK.value:
|
||||
|
||||
view_name = '_api_project_tasks'
|
||||
|
||||
kwargs.update({'project_id': item.project.id})
|
||||
else:
|
||||
|
||||
raise ValueError('Serializer unable to obtain ticket type')
|
||||
|
||||
|
||||
return request.build_absolute_uri(
|
||||
reverse(
|
||||
'v1:' + view_name + '-detail',
|
||||
kwargs = kwargs
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
ticket_comments = serializers.SerializerMethodField('get_url_ticket_comments')
|
||||
|
||||
|
||||
def get_url_ticket_comments(self, item):
|
||||
|
||||
request = self.context.get('request')
|
||||
|
||||
kwargs: dict = {
|
||||
'ticket_id': item.id
|
||||
}
|
||||
|
||||
if item.ticket_type == self.Meta.model.TicketType.CHANGE.value:
|
||||
|
||||
view_name = '_api_itim_change_ticket_comments'
|
||||
|
||||
elif item.ticket_type == self.Meta.model.TicketType.INCIDENT.value:
|
||||
|
||||
view_name = '_api_itim_incident_ticket_comments'
|
||||
|
||||
elif item.ticket_type == self.Meta.model.TicketType.PROBLEM.value:
|
||||
|
||||
view_name = '_api_itim_problem_ticket_comments'
|
||||
|
||||
elif item.ticket_type == self.Meta.model.TicketType.REQUEST.value:
|
||||
|
||||
view_name = '_api_assistance_request_ticket_comments'
|
||||
|
||||
elif item.ticket_type == self.Meta.model.TicketType.PROJECT_TASK.value:
|
||||
|
||||
view_name = '_api_project_tasks_comments'
|
||||
|
||||
kwargs.update({'project_id': item.project.id})
|
||||
|
||||
else:
|
||||
|
||||
raise ValueError('Serializer unable to obtain ticket type')
|
||||
|
||||
|
||||
return request.build_absolute_uri(
|
||||
reverse(
|
||||
'v1:' + view_name + '-list',
|
||||
kwargs = kwargs
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Ticket
|
||||
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'assigned_teams',
|
||||
'assigned_users',
|
||||
'category',
|
||||
'created',
|
||||
'modified',
|
||||
'status',
|
||||
'title',
|
||||
'description',
|
||||
'urgency',
|
||||
'impact',
|
||||
'priority',
|
||||
'external_ref',
|
||||
'external_system',
|
||||
'ticket_type',
|
||||
'is_deleted',
|
||||
'date_closed',
|
||||
'planned_start_date',
|
||||
'planned_finish_date',
|
||||
'real_start_date',
|
||||
'real_finish_date',
|
||||
'opened_by',
|
||||
'organization',
|
||||
'project',
|
||||
'subscribed_teams',
|
||||
'subscribed_users',
|
||||
'ticket_comments',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
|
||||
self.fields.fields['status'].initial = Ticket.TicketStatus.All.NEW
|
||||
self.fields.fields['status'].default = Ticket.TicketStatus.All.NEW
|
||||
|
||||
self.ticket_type_fields = self.Meta.fields
|
||||
|
||||
super().__init__(instance=instance, data=data, **kwargs)
|
||||
|
||||
self.fields['organization'].required = True
|
||||
|
||||
|
||||
def is_valid(self, *, raise_exception=True) -> bool:
|
||||
|
||||
is_valid = False
|
||||
|
||||
try:
|
||||
|
||||
self.request = self._context['request']
|
||||
|
||||
is_valid = super().is_valid(raise_exception=raise_exception)
|
||||
|
||||
self._ticket_type = str(self.fields['ticket_type'].choices[self._context['view']._ticket_type_value]).lower().replace(' ', '_')
|
||||
|
||||
self.validated_data['ticket_type'] = int(self._context['view']._ticket_type_value)
|
||||
|
||||
is_valid = self.validate_ticket()
|
||||
|
||||
if self.instance is None:
|
||||
|
||||
subscribed_users: list = []
|
||||
|
||||
if 'subscribed_users' in self.validated_data:
|
||||
|
||||
subscribed_users = self.validated_data['subscribed_users']
|
||||
|
||||
self.validated_data['subscribed_users'] = subscribed_users + [ self.validated_data['opened_by'] ]
|
||||
|
||||
except Exception as unhandled_exception:
|
||||
|
||||
centurion_exception.ParseError(
|
||||
detail=f"Server encountered an error during validation, Traceback: {unhandled_exception.with_traceback}"
|
||||
)
|
||||
|
||||
return is_valid
|
@ -1,44 +0,0 @@
|
||||
from django.urls import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from api.serializers.core.ticket_comment import TicketCommentSerializer
|
||||
|
||||
from core.forms.validate_ticket import TicketValidation
|
||||
from core.models.ticket.ticket_category import TicketCategory
|
||||
|
||||
|
||||
|
||||
class TicketCategorySerializer(
|
||||
serializers.ModelSerializer,
|
||||
):
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="v1:_api_ticket_category-detail", format="html"
|
||||
)
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = TicketCategory
|
||||
|
||||
fields = '__all__'
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
|
||||
if instance is not None:
|
||||
|
||||
if hasattr(instance, 'id'):
|
||||
|
||||
self.fields.fields['parent'].queryset = self.fields.fields['parent'].queryset.exclude(
|
||||
id=instance.id
|
||||
)
|
||||
|
||||
super().__init__(instance=instance, data=data, **kwargs)
|
@ -1,74 +0,0 @@
|
||||
from django.urls import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from core.models.ticket.ticket_comment import Ticket, TicketComment
|
||||
|
||||
|
||||
|
||||
class TicketCommentSerializer(serializers.ModelSerializer):
|
||||
|
||||
|
||||
url = serializers.SerializerMethodField('get_url_ticket_comment')
|
||||
|
||||
def get_url_ticket_comment(self, item):
|
||||
|
||||
request = self.context.get('request')
|
||||
|
||||
if item.ticket.ticket_type == item.ticket.__class__.TicketType.CHANGE:
|
||||
|
||||
view_name = '_api_itim_change_ticket_comments'
|
||||
|
||||
elif item.ticket.ticket_type == item.ticket.__class__.TicketType.INCIDENT:
|
||||
|
||||
view_name = '_api_itim_incident_ticket_comments'
|
||||
|
||||
elif item.ticket.ticket_type == item.ticket.__class__.TicketType.PROBLEM:
|
||||
|
||||
view_name = '_api_itim_problem_ticket_comments'
|
||||
|
||||
elif item.ticket.ticket_type == item.ticket.__class__.TicketType.REQUEST:
|
||||
|
||||
view_name = '_api_assistance_request_ticket_comments'
|
||||
|
||||
else:
|
||||
|
||||
raise ValueError('Serializer unable to obtain ticket type')
|
||||
|
||||
|
||||
return request.build_absolute_uri(
|
||||
reverse('v1:' + view_name + '-detail',
|
||||
kwargs={
|
||||
'ticket_id': item.ticket.id,
|
||||
'pk': item.id
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
|
||||
class Meta:
|
||||
model = TicketComment
|
||||
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
|
||||
if 'context' in self._kwargs:
|
||||
|
||||
if 'view' in self._kwargs['context']:
|
||||
|
||||
if 'ticket_id' in self._kwargs['context']['view'].kwargs:
|
||||
|
||||
ticket = Ticket.objects.get(pk=int(self._kwargs['context']['view'].kwargs['ticket_id']))
|
||||
self.fields.fields['organization'].initial = ticket.organization.id
|
||||
|
||||
self.fields.fields['ticket'].initial = int(self._kwargs['context']['view'].kwargs['ticket_id'])
|
||||
|
||||
self.fields.fields['comment_type'].initial = TicketComment.CommentType.COMMENT
|
||||
|
||||
self.fields.fields['user'].initial = kwargs['context']['request']._user.id
|
||||
|
||||
super().__init__(instance=instance, data=data, **kwargs)
|
@ -1,42 +0,0 @@
|
||||
from django.urls import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.fields import empty
|
||||
|
||||
|
||||
from core.models.ticket.ticket_comment_category import TicketCommentCategory
|
||||
|
||||
|
||||
|
||||
class TicketCommentCategorySerializer(
|
||||
serializers.ModelSerializer,
|
||||
):
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="v1:_api_ticket_comment_category-detail", format="html"
|
||||
)
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = TicketCommentCategory
|
||||
|
||||
fields = '__all__'
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
|
||||
if instance is not None:
|
||||
|
||||
if hasattr(instance, 'id'):
|
||||
|
||||
self.fields.fields['parent'].queryset = self.fields.fields['parent'].queryset.exclude(
|
||||
id=instance.id
|
||||
)
|
||||
|
||||
super().__init__(instance=instance, data=data, **kwargs)
|
@ -1,9 +1,6 @@
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.html import escape
|
||||
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
|
||||
|
||||
class Inventory:
|
||||
""" Inventory Object
|
||||
|
||||
|
@ -4,7 +4,7 @@ from rest_framework import serializers
|
||||
|
||||
from api.serializers.config import ParentGroupSerializer
|
||||
|
||||
from config_management.models.groups import ConfigGroups
|
||||
from config_management.models.groups import ConfigGroupHosts
|
||||
|
||||
from itam.models.device import Device
|
||||
|
||||
@ -12,13 +12,15 @@ from itam.models.device import Device
|
||||
|
||||
class DeviceConfigGroupsSerializer(serializers.ModelSerializer):
|
||||
|
||||
name = serializers.CharField(source='group.name', read_only=True)
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="v1:_api_config_group", format="html"
|
||||
view_name="API:_api_config_group", format="html"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
||||
model = ConfigGroups
|
||||
model = ConfigGroupHosts
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
@ -36,21 +38,22 @@ class DeviceConfigGroupsSerializer(serializers.ModelSerializer):
|
||||
class DeviceSerializer(serializers.ModelSerializer):
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="v1:device-detail", format="html"
|
||||
view_name="API:device-detail", format="html"
|
||||
)
|
||||
|
||||
config = serializers.SerializerMethodField('get_device_config')
|
||||
|
||||
groups = DeviceConfigGroupsSerializer(source='configgroups_set', many=True, read_only=True)
|
||||
groups = DeviceConfigGroupsSerializer(source='configgrouphosts_set', many=True, read_only=True)
|
||||
|
||||
def get_device_config(self, device):
|
||||
|
||||
request = self.context.get('request')
|
||||
return request.build_absolute_uri(reverse('v1:_api_device_config', args=[device.slug]))
|
||||
return request.build_absolute_uri(reverse('API:_api_device_config', args=[device.slug]))
|
||||
|
||||
|
||||
class Meta:
|
||||
model = Device
|
||||
depth = 1
|
||||
fields = [
|
||||
'id',
|
||||
'is_global',
|
||||
|
@ -7,7 +7,7 @@ from itam.models.device import Software
|
||||
class SoftwareSerializer(serializers.ModelSerializer):
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="v1:software-detail", format="html"
|
||||
view_name="API:software-detail", format="html"
|
||||
)
|
||||
|
||||
class Meta:
|
||||
|
@ -1,63 +0,0 @@
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from api.serializers.core.ticket import TicketSerializer
|
||||
|
||||
from core.models.ticket.ticket import Ticket
|
||||
|
||||
|
||||
|
||||
class ChangeTicketSerializer(
|
||||
TicketSerializer,
|
||||
):
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Ticket
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'assigned_teams',
|
||||
'assigned_users',
|
||||
'category',
|
||||
'created',
|
||||
'modified',
|
||||
'status',
|
||||
'title',
|
||||
'description',
|
||||
'estimate',
|
||||
'urgency',
|
||||
'impact',
|
||||
'priority',
|
||||
'external_ref',
|
||||
'external_system',
|
||||
'ticket_type',
|
||||
'is_deleted',
|
||||
'date_closed',
|
||||
# 'planned_start_date',
|
||||
# 'planned_finish_date',
|
||||
# 'real_start_date',
|
||||
# 'real_finish_date',
|
||||
'opened_by',
|
||||
'organization',
|
||||
'project',
|
||||
'milestone',
|
||||
'subscribed_teams',
|
||||
'subscribed_users',
|
||||
'ticket_comments',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'ticket_type',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
|
||||
super().__init__(instance=instance, data=data, **kwargs)
|
||||
|
||||
self.fields.fields['category'].queryset = self.fields.fields['category'].queryset.filter(
|
||||
project_task = True
|
||||
)
|
@ -1,63 +0,0 @@
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from api.serializers.core.ticket import TicketSerializer
|
||||
|
||||
from core.models.ticket.ticket import Ticket
|
||||
|
||||
|
||||
|
||||
class IncidentTicketSerializer(
|
||||
TicketSerializer,
|
||||
):
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Ticket
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'assigned_teams',
|
||||
'assigned_users',
|
||||
'category',
|
||||
'created',
|
||||
'modified',
|
||||
'status',
|
||||
'title',
|
||||
'description',
|
||||
'estimate',
|
||||
'urgency',
|
||||
'impact',
|
||||
'priority',
|
||||
'external_ref',
|
||||
'external_system',
|
||||
'ticket_type',
|
||||
'is_deleted',
|
||||
'date_closed',
|
||||
# 'planned_start_date',
|
||||
# 'planned_finish_date',
|
||||
# 'real_start_date',
|
||||
# 'real_finish_date',
|
||||
'opened_by',
|
||||
'organization',
|
||||
'project',
|
||||
'milestone',
|
||||
'subscribed_teams',
|
||||
'subscribed_users',
|
||||
'ticket_comments',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'ticket_type',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
|
||||
super().__init__(instance=instance, data=data, **kwargs)
|
||||
|
||||
self.fields.fields['category'].queryset = self.fields.fields['category'].queryset.filter(
|
||||
incident = True
|
||||
)
|
@ -1,63 +0,0 @@
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from api.serializers.core.ticket import TicketSerializer
|
||||
|
||||
from core.models.ticket.ticket import Ticket
|
||||
|
||||
|
||||
|
||||
class ProblemTicketSerializer(
|
||||
TicketSerializer,
|
||||
):
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Ticket
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'assigned_teams',
|
||||
'assigned_users',
|
||||
'category',
|
||||
'created',
|
||||
'modified',
|
||||
'status',
|
||||
'title',
|
||||
'description',
|
||||
'estimate',
|
||||
'urgency',
|
||||
'impact',
|
||||
'priority',
|
||||
'external_ref',
|
||||
'external_system',
|
||||
'ticket_type',
|
||||
'is_deleted',
|
||||
'date_closed',
|
||||
# 'planned_start_date',
|
||||
# 'planned_finish_date',
|
||||
# 'real_start_date',
|
||||
# 'real_finish_date',
|
||||
'opened_by',
|
||||
'organization',
|
||||
'project',
|
||||
'milestone',
|
||||
'subscribed_teams',
|
||||
'subscribed_users',
|
||||
'ticket_comments',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'ticket_type',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
|
||||
super().__init__(instance=instance, data=data, **kwargs)
|
||||
|
||||
self.fields.fields['category'].queryset = self.fields.fields['category'].queryset.filter(
|
||||
problem = True
|
||||
)
|
@ -1,74 +0,0 @@
|
||||
from django.urls import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from project_management.models.projects import Project
|
||||
from project_management.models.project_milestone import ProjectMilestone
|
||||
|
||||
|
||||
|
||||
class ProjectMilestoneSerializer(
|
||||
serializers.ModelSerializer,
|
||||
):
|
||||
|
||||
url = serializers.SerializerMethodField('get_url_project_milestone')
|
||||
|
||||
def get_url_project_milestone(self, item):
|
||||
|
||||
request = self.context.get('request')
|
||||
|
||||
return request.build_absolute_uri(
|
||||
reverse('v1:_api_project_milestone-detail',
|
||||
kwargs={
|
||||
'project_id': item.project.id,
|
||||
'pk': item.id
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = ProjectMilestone
|
||||
|
||||
fields = [
|
||||
'name',
|
||||
'description',
|
||||
'organization',
|
||||
'project',
|
||||
'start_date',
|
||||
'finish_date',
|
||||
'created',
|
||||
'modified',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
|
||||
self.fields.fields['organization'].read_only = True
|
||||
self.fields.fields['project'].read_only = True
|
||||
|
||||
super().__init__(instance=instance, data=data, **kwargs)
|
||||
|
||||
|
||||
def is_valid(self, *, raise_exception=False):
|
||||
|
||||
is_valid = super().is_valid(raise_exception=raise_exception)
|
||||
|
||||
project = Project.objects.get(
|
||||
pk = int(self._kwargs['context']['view'].kwargs['project_id'])
|
||||
)
|
||||
|
||||
self._validated_data.update({
|
||||
'organization': project.organization,
|
||||
'project': project
|
||||
})
|
||||
|
||||
return is_valid
|
@ -1,33 +0,0 @@
|
||||
from django.urls import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from project_management.models.project_states import ProjectState
|
||||
|
||||
|
||||
|
||||
class ProjectStateSerializer(
|
||||
serializers.ModelSerializer,
|
||||
):
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="v1:_api_project_state-detail", format="html"
|
||||
)
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = ProjectState
|
||||
|
||||
fields = '__all__'
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
|
||||
super().__init__(instance=instance, data=data, **kwargs)
|
@ -1,63 +0,0 @@
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from api.serializers.core.ticket import TicketSerializer
|
||||
|
||||
from core.models.ticket.ticket import Ticket
|
||||
|
||||
|
||||
|
||||
class ProjectTaskSerializer(
|
||||
TicketSerializer,
|
||||
):
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Ticket
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'assigned_teams',
|
||||
'assigned_users',
|
||||
'category',
|
||||
'created',
|
||||
'modified',
|
||||
'status',
|
||||
'title',
|
||||
'description',
|
||||
'estimate',
|
||||
'urgency',
|
||||
'impact',
|
||||
'priority',
|
||||
'external_ref',
|
||||
'external_system',
|
||||
'ticket_type',
|
||||
'is_deleted',
|
||||
'date_closed',
|
||||
'planned_start_date',
|
||||
'planned_finish_date',
|
||||
'real_start_date',
|
||||
'real_finish_date',
|
||||
'opened_by',
|
||||
'organization',
|
||||
'project',
|
||||
'milestone',
|
||||
'subscribed_teams',
|
||||
'subscribed_users',
|
||||
'ticket_comments',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'ticket_type',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
|
||||
super().__init__(instance=instance, data=data, **kwargs)
|
||||
|
||||
self.fields.fields['category'].queryset = self.fields.fields['category'].queryset.filter(
|
||||
project_task = True
|
||||
)
|
@ -1,33 +0,0 @@
|
||||
from django.urls import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from project_management.models.project_types import ProjectType
|
||||
|
||||
|
||||
|
||||
class ProjectTypeSerializer(
|
||||
serializers.ModelSerializer,
|
||||
):
|
||||
|
||||
url = serializers.HyperlinkedIdentityField(
|
||||
view_name="v1:_api_project_state-detail", format="html"
|
||||
)
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = ProjectType
|
||||
|
||||
fields = '__all__'
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'url',
|
||||
]
|
||||
|
||||
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
|
||||
super().__init__(instance=instance, data=data, **kwargs)
|
@ -1,134 +0,0 @@
|
||||
from django.urls import reverse
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.fields import empty
|
||||
|
||||
from project_management.models.projects import Project
|
||||
|
||||
|
||||
|
||||
class ProjectSerializer(
|
||||
serializers.ModelSerializer,
|
||||
):
|
||||
|
||||
percent_completed = serializers.CharField(
|
||||
read_only = True,
|
||||
)
|
||||
|
||||
url = serializers.SerializerMethodField('get_url')
|
||||
|
||||
|
||||
def get_url(self, item):
|
||||
|
||||
request = self.context.get('request')
|
||||
|
||||
return request.build_absolute_uri(reverse("v1:_api_projects-detail", args=[item.pk]))
|
||||
|
||||
|
||||
project_tasks_url = serializers.SerializerMethodField('get_url_project_tasks')
|
||||
|
||||
|
||||
def get_url_project_tasks(self, item):
|
||||
|
||||
request = self.context.get('request')
|
||||
|
||||
return request.build_absolute_uri(
|
||||
reverse(
|
||||
'v1:_api_project_tasks-list',
|
||||
kwargs={
|
||||
'project_id': item.id
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
project_milestone_url = serializers.SerializerMethodField('get_url_project_milestone')
|
||||
|
||||
def get_url_project_milestone(self, item):
|
||||
|
||||
request = self.context.get('request')
|
||||
|
||||
return request.build_absolute_uri(
|
||||
reverse(
|
||||
'v1:_api_project_milestone-list',
|
||||
kwargs={
|
||||
'project_id': item.id
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Project
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'organization',
|
||||
'state',
|
||||
'project_type',
|
||||
'priority',
|
||||
'name',
|
||||
'description',
|
||||
'code',
|
||||
'planned_start_date',
|
||||
'planned_finish_date',
|
||||
'real_start_date',
|
||||
'real_finish_date',
|
||||
'manager_user',
|
||||
'manager_team',
|
||||
'team_members',
|
||||
'project_tasks_url',
|
||||
'project_milestone_url',
|
||||
'percent_completed',
|
||||
'created',
|
||||
'modified',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'url',
|
||||
'created',
|
||||
'modified',
|
||||
]
|
||||
|
||||
|
||||
|
||||
class ProjectImportSerializer(ProjectSerializer):
|
||||
|
||||
class Meta:
|
||||
|
||||
model = Project
|
||||
|
||||
fields = [
|
||||
'id',
|
||||
'organization',
|
||||
'state',
|
||||
'project_type',
|
||||
'priority',
|
||||
'name',
|
||||
'description',
|
||||
'code',
|
||||
'planned_start_date',
|
||||
'planned_finish_date',
|
||||
'real_start_date',
|
||||
'real_finish_date',
|
||||
'manager_user',
|
||||
'manager_team',
|
||||
'team_members',
|
||||
'project_tasks_url',
|
||||
'project_milestone_url',
|
||||
'percent_completed',
|
||||
'created',
|
||||
'modified',
|
||||
'external_ref',
|
||||
'external_system',
|
||||
'is_deleted',
|
||||
'url',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'id',
|
||||
'url',
|
||||
]
|
@ -9,7 +9,7 @@ from celery import states
|
||||
|
||||
from access.models import Organization
|
||||
|
||||
from itam.serializers.inventory import InventorySerializer
|
||||
from api.serializers.inventory import Inventory
|
||||
|
||||
from itam.models.device import Device, DeviceType, DeviceOperatingSystem, DeviceSoftware
|
||||
from itam.models.operating_system import OperatingSystem, OperatingSystemVersion
|
||||
@ -32,15 +32,8 @@ def process_inventory(self, data, organization: int):
|
||||
|
||||
logger.info('Begin Processing Inventory')
|
||||
|
||||
if type(data) is str:
|
||||
|
||||
data = json.loads(data)
|
||||
|
||||
data = InventorySerializer(
|
||||
data = data
|
||||
)
|
||||
|
||||
data.is_valid()
|
||||
data = json.loads(data)
|
||||
data = Inventory(data)
|
||||
|
||||
organization = Organization.objects.get(id=organization)
|
||||
|
||||
@ -49,13 +42,13 @@ def process_inventory(self, data, organization: int):
|
||||
device_serial_number = None
|
||||
device_uuid = None
|
||||
|
||||
if data.validated_data['details']['serial_number'] and str(data.validated_data['details']['serial_number']).lower() != 'na':
|
||||
if data.details.serial_number and str(data.details.serial_number).lower() != 'na':
|
||||
|
||||
device_serial_number = str(data.validated_data['details']['serial_number'])
|
||||
device_serial_number = str(data.details.serial_number)
|
||||
|
||||
if data.validated_data['details']['uuid'] and str(data.validated_data['details']['uuid']).lower() != 'na':
|
||||
if data.details.uuid and str(data.details.uuid).lower() != 'na':
|
||||
|
||||
device_uuid = str(data.validated_data['details']['uuid'])
|
||||
device_uuid = str(data.details.uuid)
|
||||
|
||||
|
||||
if device_serial_number: # Search for device by serial number.
|
||||
@ -95,13 +88,13 @@ def process_inventory(self, data, organization: int):
|
||||
if not device: # Search for device by Name.
|
||||
|
||||
device = Device.objects.filter(
|
||||
name__iexact=str(data.validated_data['details']['name']).lower()
|
||||
name__iexact=str(data.details.name).lower()
|
||||
)
|
||||
|
||||
if device.exists():
|
||||
|
||||
device = Device.objects.get(
|
||||
name__iexact=str(data.validated_data['details']['name']).lower()
|
||||
name__iexact=str(data.details.name).lower()
|
||||
)
|
||||
|
||||
else:
|
||||
@ -114,7 +107,7 @@ def process_inventory(self, data, organization: int):
|
||||
if not device: # Create the device
|
||||
|
||||
device = Device.objects.create(
|
||||
name = data.validated_data['details']['name'],
|
||||
name = data.details.name,
|
||||
device_type = None,
|
||||
serial_number = device_serial_number,
|
||||
uuid = device_uuid,
|
||||
@ -138,14 +131,14 @@ def process_inventory(self, data, organization: int):
|
||||
|
||||
if not device.serial_number and device_serial_number:
|
||||
|
||||
device.serial_number = data.validated_data['details']['serial_number']
|
||||
device.serial_number = data.details.serial_number
|
||||
|
||||
device_edited = True
|
||||
|
||||
|
||||
if str(device.name).lower() != str(data.validated_data['details']['name']).lower(): # Update device Name
|
||||
if str(device.name).lower() != str(data.details.name).lower(): # Update device Name
|
||||
|
||||
device.name = data.validated_data['details']['name']
|
||||
device.name = data.details.name
|
||||
|
||||
device_edited = True
|
||||
|
||||
@ -156,14 +149,14 @@ def process_inventory(self, data, organization: int):
|
||||
|
||||
|
||||
operating_system = OperatingSystem.objects.filter(
|
||||
name = data.validated_data['os']['name'],
|
||||
name=data.operating_system.name,
|
||||
is_global = True
|
||||
)
|
||||
|
||||
if operating_system.exists():
|
||||
|
||||
operating_system = OperatingSystem.objects.get(
|
||||
name = data.validated_data['os']['name'],
|
||||
name=data.operating_system.name,
|
||||
is_global = True
|
||||
)
|
||||
|
||||
@ -177,7 +170,7 @@ def process_inventory(self, data, organization: int):
|
||||
if not operating_system:
|
||||
|
||||
operating_system = OperatingSystem.objects.filter(
|
||||
name = data.validated_data['os']['name'],
|
||||
name=data.operating_system.name,
|
||||
organization = organization
|
||||
)
|
||||
|
||||
@ -185,7 +178,7 @@ def process_inventory(self, data, organization: int):
|
||||
if operating_system.exists():
|
||||
|
||||
operating_system = OperatingSystem.objects.get(
|
||||
name = data.validated_data['os']['name'],
|
||||
name=data.operating_system.name,
|
||||
organization = organization
|
||||
)
|
||||
|
||||
@ -197,22 +190,22 @@ def process_inventory(self, data, organization: int):
|
||||
if not operating_system:
|
||||
|
||||
operating_system = OperatingSystem.objects.create(
|
||||
name = data.validated_data['os']['name'],
|
||||
name = data.operating_system.name,
|
||||
organization = organization,
|
||||
is_global = True
|
||||
)
|
||||
|
||||
|
||||
operating_system_version = OperatingSystemVersion.objects.filter(
|
||||
name = data.validated_data['os']['version_major'],
|
||||
operating_system = operating_system
|
||||
name=data.operating_system.version_major,
|
||||
operating_system=operating_system
|
||||
)
|
||||
|
||||
if operating_system_version.exists():
|
||||
|
||||
operating_system_version = OperatingSystemVersion.objects.get(
|
||||
name = data.validated_data['os']['version_major'],
|
||||
operating_system = operating_system
|
||||
name=data.operating_system.version_major,
|
||||
operating_system=operating_system
|
||||
)
|
||||
|
||||
else:
|
||||
@ -225,7 +218,7 @@ def process_inventory(self, data, organization: int):
|
||||
operating_system_version = OperatingSystemVersion.objects.create(
|
||||
organization = organization,
|
||||
is_global = True,
|
||||
name = data.validated_data['os']['version_major'],
|
||||
name = data.operating_system.version_major,
|
||||
operating_system = operating_system,
|
||||
)
|
||||
|
||||
@ -248,8 +241,8 @@ def process_inventory(self, data, organization: int):
|
||||
|
||||
device_operating_system = DeviceOperatingSystem.objects.create(
|
||||
organization = organization,
|
||||
device = device,
|
||||
version = data.validated_data['os']['version'],
|
||||
device=device,
|
||||
version = data.operating_system.version,
|
||||
operating_system_version = operating_system_version,
|
||||
installdate = timezone.now()
|
||||
)
|
||||
@ -268,9 +261,9 @@ def process_inventory(self, data, organization: int):
|
||||
device_operating_system.save()
|
||||
|
||||
|
||||
if device_operating_system.version != data.validated_data['os']['version']:
|
||||
if device_operating_system.version != data.operating_system.version:
|
||||
|
||||
device_operating_system.version = data.validated_data['os']['version']
|
||||
device_operating_system.version = data.operating_system.version
|
||||
|
||||
device_operating_system.save()
|
||||
|
||||
@ -294,7 +287,7 @@ def process_inventory(self, data, organization: int):
|
||||
|
||||
inventoried_software: list = []
|
||||
|
||||
for inventory in list(data.validated_data['software']):
|
||||
for inventory in list(data.software):
|
||||
|
||||
software = None
|
||||
software_category = None
|
||||
@ -302,13 +295,13 @@ def process_inventory(self, data, organization: int):
|
||||
|
||||
device_software = None
|
||||
|
||||
software_category = SoftwareCategory.objects.filter( name = inventory['category'] )
|
||||
software_category = SoftwareCategory.objects.filter( name = inventory.category )
|
||||
|
||||
|
||||
if software_category.exists():
|
||||
|
||||
software_category = SoftwareCategory.objects.get(
|
||||
name = inventory['category']
|
||||
name = inventory.category
|
||||
)
|
||||
|
||||
else: # Create Software Category
|
||||
@ -316,16 +309,16 @@ def process_inventory(self, data, organization: int):
|
||||
software_category = SoftwareCategory.objects.create(
|
||||
organization = software_category_organization,
|
||||
is_global = True,
|
||||
name = inventory['category'],
|
||||
name = inventory.category,
|
||||
)
|
||||
|
||||
|
||||
if software_category.name == inventory['category']:
|
||||
if software_category.name == inventory.category:
|
||||
|
||||
if Software.objects.filter( name = inventory['name'] ).exists():
|
||||
if Software.objects.filter( name = inventory.name ).exists():
|
||||
|
||||
software = Software.objects.get(
|
||||
name = inventory['name']
|
||||
name = inventory.name
|
||||
)
|
||||
|
||||
if not software.category:
|
||||
@ -338,16 +331,16 @@ def process_inventory(self, data, organization: int):
|
||||
software = Software.objects.create(
|
||||
organization = software_organization,
|
||||
is_global = True,
|
||||
name = inventory['name'],
|
||||
name = inventory.name,
|
||||
category = software_category,
|
||||
)
|
||||
|
||||
|
||||
if software.name == inventory['name']:
|
||||
if software.name == inventory.name:
|
||||
|
||||
pattern = r"^(\d+:)?(?P<semver>\d+\.\d+(\.\d+)?)"
|
||||
|
||||
semver = re.search(pattern, str(inventory['version']), re.DOTALL)
|
||||
semver = re.search(pattern, str(inventory.version), re.DOTALL)
|
||||
|
||||
|
||||
if semver:
|
||||
@ -355,7 +348,7 @@ def process_inventory(self, data, organization: int):
|
||||
semver = semver['semver']
|
||||
|
||||
else:
|
||||
semver = inventory['version']
|
||||
semver = inventory.version
|
||||
|
||||
|
||||
if SoftwareVersion.objects.filter( name = semver, software = software ).exists():
|
||||
|
@ -1,249 +0,0 @@
|
||||
from rest_framework.relations import Hyperlink
|
||||
|
||||
|
||||
|
||||
class APICommonFields:
|
||||
"""Test Cases for fields common to All API responses
|
||||
|
||||
Must contain:
|
||||
- id
|
||||
- display_name
|
||||
- _urls
|
||||
- _urls._self
|
||||
"""
|
||||
|
||||
|
||||
api_data: object
|
||||
""" API Response data """
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_display_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
display_name field must exist
|
||||
"""
|
||||
|
||||
assert 'display_name' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_display_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
display_name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['display_name']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_urls(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
_urls field must exist
|
||||
"""
|
||||
|
||||
assert '_urls' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_urls(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
_urls field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['_urls']) is dict
|
||||
|
||||
|
||||
def test_api_field_exists_urls_self(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
_urls._self field must exist
|
||||
"""
|
||||
|
||||
assert '_self' in self.api_data['_urls']
|
||||
|
||||
|
||||
def test_api_field_type_urls(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
_urls._self field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['_urls']['_self']) is str
|
||||
|
||||
|
||||
|
||||
class APIModelFields(
|
||||
APICommonFields
|
||||
):
|
||||
"""Test Cases for fields common to All API Model responses
|
||||
|
||||
Must contain:
|
||||
- id
|
||||
- display_name
|
||||
- _urls
|
||||
- _urls._self
|
||||
"""
|
||||
|
||||
|
||||
api_data: object
|
||||
""" API Response data """
|
||||
|
||||
|
||||
def test_api_field_exists_model_notes(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
model_notes field must exist
|
||||
"""
|
||||
|
||||
assert 'model_notes' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_model_notes(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
model_notes field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['model_notes']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_created(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
created field must exist
|
||||
"""
|
||||
|
||||
assert 'created' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_created(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
created field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['created']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_modified(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
modified field must exist
|
||||
"""
|
||||
|
||||
assert 'modified' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_modified(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
modified field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['modified']) is str
|
||||
|
||||
|
||||
|
||||
class APITenancyObject(
|
||||
APIModelFields
|
||||
):
|
||||
|
||||
|
||||
api_data: object
|
||||
""" API Response data """
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_organization(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
organization field must exist
|
||||
"""
|
||||
|
||||
assert 'organization' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_organization(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
organization field must be dict
|
||||
"""
|
||||
|
||||
assert type(self.api_data['organization']) is dict
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_organization_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
organization.id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data['organization']
|
||||
|
||||
|
||||
def test_api_field_type_organization_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
organization.id field must be dict
|
||||
"""
|
||||
|
||||
assert type(self.api_data['organization']['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_organization_display_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
organization.display_name field must exist
|
||||
"""
|
||||
|
||||
assert 'display_name' in self.api_data['organization']
|
||||
|
||||
|
||||
def test_api_field_type_organization_display_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
organization.display_name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['organization']['display_name']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_organization_url(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
organization.url field must exist
|
||||
"""
|
||||
|
||||
assert 'url' in self.api_data['organization']
|
||||
|
||||
|
||||
def test_api_field_type_organization_url(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
organization.url field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['organization']['url']) is Hyperlink
|
@ -194,7 +194,7 @@ class APIPermissionAdd:
|
||||
def test_add_has_permission(self):
|
||||
""" Check correct permission for add
|
||||
|
||||
Attempt to add as user with permission
|
||||
Attempt to add as user with no permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
|
@ -1,513 +0,0 @@
|
||||
import pytest
|
||||
import unittest
|
||||
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase, Client
|
||||
|
||||
|
||||
|
||||
class APIPermissionView:
|
||||
|
||||
|
||||
model: object
|
||||
""" Item Model to test """
|
||||
|
||||
app_namespace: str = None
|
||||
""" URL namespace """
|
||||
|
||||
url_name: str
|
||||
""" URL name of the view to test """
|
||||
|
||||
url_view_kwargs: dict = None
|
||||
""" URL kwargs of the item page """
|
||||
|
||||
|
||||
def test_view_user_anon_denied(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
Attempt to view as anon user
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
def test_view_no_permission_denied(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
Attempt to view with user missing permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.no_permissions_user)
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_view_different_organizaiton_denied(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
Attempt to view with user from different organization
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.different_organization_user)
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_view_has_permission(self):
|
||||
""" Check correct permission for view
|
||||
|
||||
Attempt to view as user with view permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
|
||||
def test_returned_results_only_user_orgs(self):
|
||||
"""Returned results check
|
||||
|
||||
Ensure that a query to the viewset endpoint does not return
|
||||
items that are not part of the users organizations.
|
||||
"""
|
||||
|
||||
|
||||
# Ensure the other org item exists, without test not able to function
|
||||
print('Check that the different organization item has been defined')
|
||||
assert hasattr(self, 'other_org_item')
|
||||
|
||||
# ensure that the variables for the two orgs are different orgs
|
||||
print('checking that the different and user oganizations are different')
|
||||
assert self.different_organization.id != self.organization.id
|
||||
|
||||
|
||||
client = Client()
|
||||
|
||||
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')
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
contains_different_org: bool = False
|
||||
|
||||
for item in response.data['results']:
|
||||
|
||||
if int(item['organization']['id']) != self.organization.id:
|
||||
|
||||
contains_different_org = True
|
||||
|
||||
assert not contains_different_org
|
||||
|
||||
|
||||
|
||||
|
||||
class APIPermissionAdd:
|
||||
|
||||
|
||||
model: object
|
||||
""" Item Model to test """
|
||||
|
||||
app_namespace: str = None
|
||||
""" URL namespace """
|
||||
|
||||
url_list: str
|
||||
""" URL view name of the item list page """
|
||||
|
||||
url_kwargs: dict = None
|
||||
""" URL view kwargs for the item list page """
|
||||
|
||||
add_data: dict = None
|
||||
|
||||
|
||||
def test_add_user_anon_denied(self):
|
||||
""" Check correct permission for add
|
||||
|
||||
Attempt to add as anon user
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
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.put(url, data=self.add_data)
|
||||
|
||||
assert response.status_code == 401
|
||||
|
||||
# @pytest.mark.skip(reason="ToDO: figure out why fails")
|
||||
def test_add_no_permission_denied(self):
|
||||
""" Check correct permission for add
|
||||
|
||||
Attempt to add as user with no permissions
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
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')
|
||||
|
||||
|
||||
client.force_login(self.no_permissions_user)
|
||||
response = client.post(url, data=self.add_data)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
# @pytest.mark.skip(reason="ToDO: figure out why fails")
|
||||
def test_add_different_organization_denied(self):
|
||||
""" Check correct permission for add
|
||||
|
||||
attempt to add as user from different organization
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
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')
|
||||
|
||||
|
||||
client.force_login(self.different_organization_user)
|
||||
response = client.post(url, data=self.add_data)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_add_permission_view_denied(self):
|
||||
""" Check correct permission for add
|
||||
|
||||
Attempt to add a user with view permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
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')
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.post(url, data=self.add_data)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_add_has_permission(self):
|
||||
""" Check correct permission for add
|
||||
|
||||
Attempt to add as user with permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
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')
|
||||
|
||||
|
||||
client.force_login(self.add_user)
|
||||
response = client.post(url, data=self.add_data)
|
||||
|
||||
assert response.status_code == 201
|
||||
|
||||
|
||||
class APIPermissionChange:
|
||||
|
||||
|
||||
model: object
|
||||
""" Item Model to test """
|
||||
|
||||
app_namespace: str = None
|
||||
""" URL namespace """
|
||||
|
||||
url_name: str
|
||||
""" URL name of the view to test """
|
||||
|
||||
url_view_kwargs: dict = None
|
||||
""" URL kwargs of the item page """
|
||||
|
||||
change_data: dict = None
|
||||
|
||||
|
||||
def test_change_user_anon_denied(self):
|
||||
""" Check correct permission for change
|
||||
|
||||
Attempt to change as anon
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
response = client.patch(url, data=self.change_data, content_type='application/json')
|
||||
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
def test_change_no_permission_denied(self):
|
||||
""" Ensure permission view cant make change
|
||||
|
||||
Attempt to make change as user without permissions
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.no_permissions_user)
|
||||
response = client.patch(url, data=self.change_data, content_type='application/json')
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_change_different_organization_denied(self):
|
||||
""" Ensure permission view cant make change
|
||||
|
||||
Attempt to make change as user from different organization
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.different_organization_user)
|
||||
response = client.patch(url, data=self.change_data, content_type='application/json')
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_change_permission_view_denied(self):
|
||||
""" Ensure permission view cant make change
|
||||
|
||||
Attempt to make change as user with view permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.patch(url, data=self.change_data, content_type='application/json')
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_change_permission_add_denied(self):
|
||||
""" Ensure permission view cant make change
|
||||
|
||||
Attempt to make change as user with add permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.add_user)
|
||||
response = client.patch(url, data=self.change_data, content_type='application/json')
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_change_has_permission(self):
|
||||
""" Check correct permission for change
|
||||
|
||||
Make change with user who has change permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.change_user)
|
||||
response = client.patch(url, data=self.change_data, content_type='application/json')
|
||||
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
|
||||
class APIPermissionDelete:
|
||||
|
||||
|
||||
model: object
|
||||
""" Item Model to test """
|
||||
|
||||
app_namespace: str = None
|
||||
""" URL namespace """
|
||||
|
||||
url_name: str
|
||||
""" URL name of the view to test """
|
||||
|
||||
url_view_kwargs: dict = None
|
||||
""" URL kwargs of the item page """
|
||||
|
||||
delete_data: dict = None
|
||||
|
||||
|
||||
def test_delete_user_anon_denied(self):
|
||||
""" Check correct permission for delete
|
||||
|
||||
Attempt to delete item as anon user
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
response = client.delete(url, data=self.delete_data)
|
||||
|
||||
assert response.status_code == 401
|
||||
|
||||
|
||||
def test_delete_no_permission_denied(self):
|
||||
""" Check correct permission for delete
|
||||
|
||||
Attempt to delete as user with no permissons
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.no_permissions_user)
|
||||
response = client.delete(url, data=self.delete_data)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_delete_different_organization_denied(self):
|
||||
""" Check correct permission for delete
|
||||
|
||||
Attempt to delete as user from different organization
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.different_organization_user)
|
||||
response = client.delete(url, data=self.delete_data)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_delete_permission_view_denied(self):
|
||||
""" Check correct permission for delete
|
||||
|
||||
Attempt to delete as user with veiw permission only
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.delete(url, data=self.delete_data)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_delete_permission_add_denied(self):
|
||||
""" Check correct permission for delete
|
||||
|
||||
Attempt to delete as user with add permission only
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.add_user)
|
||||
response = client.delete(url, data=self.delete_data)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
def test_delete_has_permission(self):
|
||||
""" Check correct permission for delete
|
||||
|
||||
Delete item as user with delete permission
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.delete_user)
|
||||
response = client.delete(url, data=self.delete_data)
|
||||
|
||||
assert response.status_code == 204
|
||||
|
||||
|
||||
def test_delete_permission_change_denied(self):
|
||||
""" Check correct permission for delete
|
||||
|
||||
Attempt to delete as user with change permission only
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.change_user)
|
||||
response = client.delete(url, data=self.delete_data)
|
||||
|
||||
assert response.status_code == 403
|
||||
|
||||
|
||||
|
||||
class APIPermissions(
|
||||
APIPermissionAdd,
|
||||
APIPermissionChange,
|
||||
APIPermissionDelete,
|
||||
APIPermissionView
|
||||
):
|
||||
""" Abstract class containing all API Permission test cases """
|
||||
|
||||
model: object
|
||||
""" Item Model to test """
|
@ -1,163 +0,0 @@
|
||||
import pytest
|
||||
import unittest
|
||||
|
||||
from django.shortcuts import reverse
|
||||
from django.test import TestCase, Client
|
||||
|
||||
|
||||
|
||||
class SerializerView:
|
||||
|
||||
|
||||
model: object
|
||||
""" Item Model to test """
|
||||
|
||||
app_namespace: str = None
|
||||
""" URL namespace """
|
||||
|
||||
url_name: str
|
||||
""" URL name of the view to test """
|
||||
|
||||
url_view_kwargs: dict = None
|
||||
""" URL kwargs of the item page """
|
||||
|
||||
|
||||
|
||||
def test_returned_serializer_user_view(self):
|
||||
""" Check correct Serializer is returned
|
||||
|
||||
View action for view user must return `ViewSerializer`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
assert str(response.renderer_context['view'].get_serializer().__class__.__name__).endswith('ViewSerializer')
|
||||
|
||||
|
||||
|
||||
class SerializerAdd:
|
||||
|
||||
|
||||
model: object
|
||||
""" Item Model to test """
|
||||
|
||||
app_namespace: str = None
|
||||
""" URL namespace """
|
||||
|
||||
url_list: str
|
||||
""" URL view name of the item list page """
|
||||
|
||||
url_kwargs: dict = None
|
||||
""" URL view kwargs for the item list page """
|
||||
|
||||
add_data: dict = None
|
||||
|
||||
|
||||
def test_returned_serializer_user_add(self):
|
||||
""" Check correct Serializer is returned
|
||||
|
||||
Add action for add user must return `ModelSerializer`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
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')
|
||||
|
||||
|
||||
client.force_login(self.add_user)
|
||||
response = client.post(url, data=self.add_data)
|
||||
|
||||
assert str(response.renderer_context['view'].get_serializer().__class__.__name__).endswith('ModelSerializer')
|
||||
|
||||
|
||||
|
||||
class SerializerChange:
|
||||
|
||||
|
||||
model: object
|
||||
""" Item Model to test """
|
||||
|
||||
app_namespace: str = None
|
||||
""" URL namespace """
|
||||
|
||||
url_name: str
|
||||
""" URL name of the view to test """
|
||||
|
||||
url_view_kwargs: dict = None
|
||||
""" URL kwargs of the item page """
|
||||
|
||||
change_data: dict = None
|
||||
|
||||
|
||||
def test_returned_serializer_user_change(self):
|
||||
""" Check correct Serializer is returned
|
||||
|
||||
Change action for change user must return `ModelSerializer`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.change_user)
|
||||
response = client.patch(url, data=self.change_data, content_type='application/json')
|
||||
|
||||
assert str(response.renderer_context['view'].get_serializer().__class__.__name__).endswith('ModelSerializer')
|
||||
|
||||
|
||||
|
||||
class SerializerDelete:
|
||||
|
||||
|
||||
model: object
|
||||
""" Item Model to test """
|
||||
|
||||
app_namespace: str = None
|
||||
""" URL namespace """
|
||||
|
||||
url_name: str
|
||||
""" URL name of the view to test """
|
||||
|
||||
url_view_kwargs: dict = None
|
||||
""" URL kwargs of the item page """
|
||||
|
||||
delete_data: dict = None
|
||||
|
||||
|
||||
def test_returned_serializer_user_delete(self):
|
||||
""" Check correct Serializer is returned
|
||||
|
||||
Delete action for delete user must return `ModelSerializer`
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.delete_user)
|
||||
response = client.delete(url)
|
||||
|
||||
assert str(response.renderer_context['view'].get_serializer().__class__.__name__).endswith('ModelSerializer')
|
||||
|
||||
|
||||
|
||||
class SerializersTestCases(
|
||||
SerializerAdd,
|
||||
SerializerChange,
|
||||
SerializerDelete,
|
||||
SerializerView
|
||||
):
|
||||
""" Abstract class containing all ViewSet test cases """
|
||||
|
||||
model: object
|
||||
""" Item Model to test """
|
@ -1,889 +0,0 @@
|
||||
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
|
||||
|
||||
|
@ -1,586 +0,0 @@
|
||||
from api.react_ui_metadata import ReactUIMetadata
|
||||
from api.views.mixin import OrganizationPermissionAPI
|
||||
|
||||
|
||||
|
||||
class AllViewSet:
|
||||
"""Tests specific to the Viewset
|
||||
|
||||
**Dont include these tests directly, see below for correct class**
|
||||
|
||||
Tests are for ALL viewsets.
|
||||
"""
|
||||
|
||||
viewset = None
|
||||
"""ViewSet to Test"""
|
||||
|
||||
|
||||
|
||||
def test_view_attr_allowed_methods_exists(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `allowed_methods` must exist
|
||||
"""
|
||||
|
||||
assert hasattr(self.viewset, 'allowed_methods')
|
||||
|
||||
|
||||
def test_view_attr_allowed_methods_not_empty(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `allowed_methods` must return a value
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert view_set.allowed_methods is not None
|
||||
|
||||
|
||||
def test_view_attr_allowed_methods_type(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `allowed_methods` must be of type list
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert type(view_set.allowed_methods) is list
|
||||
|
||||
|
||||
def test_view_attr_allowed_methods_values(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `allowed_methods` only contains valid values
|
||||
"""
|
||||
|
||||
# Values valid for index views
|
||||
valid_values: list = [
|
||||
'GET',
|
||||
'HEAD',
|
||||
'OPTIONS',
|
||||
]
|
||||
|
||||
all_valid: bool = True
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
for method in list(view_set.allowed_methods):
|
||||
|
||||
if method not in valid_values:
|
||||
|
||||
all_valid = False
|
||||
|
||||
assert all_valid
|
||||
|
||||
|
||||
|
||||
def test_view_attr_metadata_class_exists(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `metadata_class` must exist
|
||||
"""
|
||||
|
||||
assert hasattr(self.viewset, 'metadata_class')
|
||||
|
||||
|
||||
def test_view_attr_metadata_class_not_empty(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `metadata_class` must return a value
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert view_set.metadata_class is not None
|
||||
|
||||
|
||||
def test_view_attr_metadata_class_type(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `metadata_class` must be metadata class `ReactUIMetadata`
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert view_set.metadata_class is ReactUIMetadata
|
||||
|
||||
|
||||
|
||||
def test_view_attr_permission_classes_exists(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `permission_classes` must exist
|
||||
"""
|
||||
|
||||
assert hasattr(self.viewset, 'permission_classes')
|
||||
|
||||
|
||||
def test_view_attr_permission_classes_not_empty(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `permission_classes` must return a value
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert view_set.permission_classes is not None
|
||||
|
||||
|
||||
def test_view_attr_permission_classes_type(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `permission_classes` must be list
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert type(view_set.permission_classes) is list
|
||||
|
||||
|
||||
def test_view_attr_permission_classes_value(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `permission_classes` must be metadata class `ReactUIMetadata`
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert view_set.permission_classes[0] is OrganizationPermissionAPI
|
||||
|
||||
assert len(view_set.permission_classes) == 1
|
||||
|
||||
|
||||
|
||||
def test_view_attr_view_description_exists(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_description` must exist
|
||||
"""
|
||||
|
||||
assert hasattr(self.viewset, 'view_description')
|
||||
|
||||
|
||||
def test_view_attr_view_description_not_empty(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_description` must return a value
|
||||
"""
|
||||
|
||||
assert self.viewset.view_description is not None
|
||||
|
||||
|
||||
def test_view_attr_view_description_type(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_description` must be of type str
|
||||
"""
|
||||
|
||||
assert type(self.viewset.view_description) is str
|
||||
|
||||
|
||||
|
||||
def test_view_attr_view_name_exists(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_name` must exist
|
||||
"""
|
||||
|
||||
assert hasattr(self.viewset, 'view_name')
|
||||
|
||||
|
||||
def test_view_attr_view_name_not_empty(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_name` must return a value
|
||||
"""
|
||||
|
||||
assert self.viewset.view_name is not None
|
||||
|
||||
|
||||
def test_view_attr_view_name_type(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_name` must be of type str
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert (
|
||||
type(view_set.view_name) is str
|
||||
)
|
||||
|
||||
|
||||
|
||||
class APIRenderViewSet:
|
||||
|
||||
"""Function ViewSet test
|
||||
|
||||
**Dont include these tests directly, see below for correct class**
|
||||
|
||||
These tests ensure that the data from the ViewSet is present for a
|
||||
HTTP Request
|
||||
"""
|
||||
|
||||
http_options_response_list: dict = None
|
||||
"""The HTTP/Options Response for the ViewSet"""
|
||||
|
||||
|
||||
|
||||
def test_api_render_field_allowed_methods_exists(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `allowed_methods` must exist
|
||||
"""
|
||||
|
||||
assert 'allowed_methods' in self.http_options_response_list.data
|
||||
|
||||
|
||||
def test_api_render_field_allowed_methods_not_empty(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `allowed_methods` must return a value
|
||||
"""
|
||||
|
||||
assert len(self.http_options_response_list.data['allowed_methods']) > 0
|
||||
|
||||
|
||||
def test_api_render_field_allowed_methods_type(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `allowed_methods` must be of type list
|
||||
"""
|
||||
|
||||
assert type(self.http_options_response_list.data['allowed_methods']) is list
|
||||
|
||||
|
||||
def test_api_render_field_allowed_methods_values(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `allowed_methods` only contains valid values
|
||||
"""
|
||||
|
||||
# Values valid for index views
|
||||
valid_values: list = [
|
||||
'GET',
|
||||
'HEAD',
|
||||
'OPTIONS',
|
||||
]
|
||||
|
||||
all_valid: bool = True
|
||||
|
||||
for method in list(self.http_options_response_list.data['allowed_methods']):
|
||||
|
||||
if method not in valid_values:
|
||||
|
||||
all_valid = False
|
||||
|
||||
assert all_valid
|
||||
|
||||
|
||||
|
||||
def test_api_render_field_view_description_exists(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `description` must exist
|
||||
"""
|
||||
|
||||
assert 'description' in self.http_options_response_list.data
|
||||
|
||||
|
||||
def test_api_render_field_view_description_not_empty(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_description` must return a value
|
||||
"""
|
||||
|
||||
assert self.http_options_response_list.data['description'] is not None
|
||||
|
||||
|
||||
def test_api_render_field_view_description_type(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_description` must be of type str
|
||||
"""
|
||||
|
||||
assert type(self.http_options_response_list.data['description']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_render_field_view_name_exists(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_name` must exist
|
||||
"""
|
||||
|
||||
assert 'name' in self.http_options_response_list.data
|
||||
|
||||
|
||||
def test_api_render_field_view_name_not_empty(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_name` must return a value
|
||||
"""
|
||||
|
||||
assert self.http_options_response_list.data['name'] is not None
|
||||
|
||||
|
||||
def test_api_render_field_view_name_type(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_name` must be of type str
|
||||
"""
|
||||
|
||||
assert type(self.http_options_response_list.data['name']) is str
|
||||
|
||||
|
||||
|
||||
class ModelViewSet(AllViewSet):
|
||||
"""Tests for Model Viewsets
|
||||
|
||||
**Dont include these tests directly, see below for correct class**
|
||||
"""
|
||||
|
||||
viewset = None
|
||||
"""ViewSet to Test"""
|
||||
|
||||
|
||||
|
||||
def test_view_attr_documentation_exists(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `documentation` must exist
|
||||
"""
|
||||
|
||||
assert hasattr(self.viewset, 'documentation')
|
||||
|
||||
|
||||
def test_view_attr_documentation_type(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `documentation` must be of type str or None.
|
||||
|
||||
this attribute is optional.
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert (
|
||||
type(view_set.documentation) is str
|
||||
or type(view_set.documentation) is None
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_view_attr_filterset_fields_exists(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `filterset_fields` must exist
|
||||
"""
|
||||
|
||||
assert hasattr(self.viewset, 'filterset_fields')
|
||||
|
||||
|
||||
def test_view_attr_filterset_fields_not_empty(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `filterset_fields` must return a value
|
||||
"""
|
||||
|
||||
assert self.viewset.filterset_fields is not None
|
||||
|
||||
|
||||
def test_view_attr_filterset_fields_type(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `filterset_fields` must be of type list
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert (
|
||||
type(view_set.filterset_fields) is list
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_view_attr_allowed_methods_values(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `allowed_methods` only contains valid values
|
||||
"""
|
||||
|
||||
# Values valid for model views
|
||||
valid_values: list = [
|
||||
'DELETE',
|
||||
'GET',
|
||||
'HEAD',
|
||||
'OPTIONS',
|
||||
'PATCH',
|
||||
'POST',
|
||||
'PUT',
|
||||
]
|
||||
|
||||
all_valid: bool = True
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
for method in list(view_set.allowed_methods):
|
||||
|
||||
if method not in valid_values:
|
||||
|
||||
all_valid = False
|
||||
|
||||
assert all_valid
|
||||
|
||||
|
||||
|
||||
def test_view_attr_model_exists(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `model` must exist
|
||||
"""
|
||||
|
||||
assert hasattr(self.viewset, 'model')
|
||||
|
||||
|
||||
def test_view_attr_model_not_empty(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `model` must return a value
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert view_set.model is not None
|
||||
|
||||
|
||||
|
||||
def test_view_attr_search_fields_exists(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `search_fields` must exist
|
||||
"""
|
||||
|
||||
assert hasattr(self.viewset, 'search_fields')
|
||||
|
||||
|
||||
def test_view_attr_search_fields_not_empty(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `search_fields` must return a value
|
||||
"""
|
||||
|
||||
assert self.viewset.search_fields is not None
|
||||
|
||||
|
||||
def test_view_attr_search_fields_type(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `search_fields` must be of type list
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert (
|
||||
type(view_set.search_fields) is list
|
||||
)
|
||||
|
||||
|
||||
|
||||
def test_view_attr_view_name_not_empty(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_name` must return a value
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert (
|
||||
view_set.view_name is not None
|
||||
or view_set.get_view_name() is not None
|
||||
)
|
||||
|
||||
|
||||
def test_view_attr_view_name_type(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `view_name` must be of type str
|
||||
"""
|
||||
|
||||
view_set = self.viewset()
|
||||
|
||||
assert (
|
||||
type(view_set.view_name) is str
|
||||
or type(view_set.get_view_name()) is str
|
||||
)
|
||||
|
||||
|
||||
|
||||
class APIRenderModelViewSet(APIRenderViewSet):
|
||||
"""Tests for Model Viewsets
|
||||
|
||||
**Dont include these tests directly, see below for correct class**
|
||||
"""
|
||||
|
||||
viewset = None
|
||||
"""ViewSet to Test"""
|
||||
|
||||
|
||||
def test_api_render_field_allowed_methods_values(self):
|
||||
"""Attribute Test
|
||||
|
||||
Attribute `allowed_methods` only contains valid values
|
||||
"""
|
||||
|
||||
# Values valid for model views
|
||||
valid_values: list = [
|
||||
'DELETE',
|
||||
'GET',
|
||||
'HEAD',
|
||||
'OPTIONS',
|
||||
'PATCH',
|
||||
'POST',
|
||||
'PUT',
|
||||
]
|
||||
|
||||
all_valid: bool = True
|
||||
|
||||
for method in list(self.http_options_response_list.data['allowed_methods']):
|
||||
|
||||
if method not in valid_values:
|
||||
|
||||
all_valid = False
|
||||
|
||||
assert all_valid
|
||||
|
||||
|
||||
|
||||
class ViewSetCommon(
|
||||
AllViewSet,
|
||||
APIRenderViewSet
|
||||
):
|
||||
""" Tests for Non-Model Viewsets
|
||||
|
||||
**Include this class directly into Non-Model ViewSets**
|
||||
|
||||
Args:
|
||||
AllViewSet (class): Tests for all Viewsets.
|
||||
APIRenderViewSet (class): Tests to check API Rendering to ensure data present.
|
||||
"""
|
||||
pass
|
||||
|
||||
|
||||
class ViewSetModel(
|
||||
ModelViewSet,
|
||||
APIRenderModelViewSet
|
||||
):
|
||||
"""Tests for model ViewSets
|
||||
|
||||
**Include this class directly into Model ViewSets**
|
||||
|
||||
Args:
|
||||
ModelViewSet (class): Tests for Model Viewsets, includes `AllViewSet` tests.
|
||||
APIRenderModelViewSet (class): Tests to check API rendering to ensure data is present, includes `APIRenderViewSet` tests.
|
||||
"""
|
||||
|
||||
pass
|
@ -160,7 +160,7 @@ class InventoryAPI(TestCase):
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse('v1:_api_device_inventory')
|
||||
url = reverse('API:_api_device_inventory')
|
||||
|
||||
client.force_login(self.add_user)
|
||||
response = client.post(url, data=self.inventory, content_type='application/json')
|
||||
@ -182,7 +182,7 @@ class InventoryAPI(TestCase):
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse('v1:_api_device_inventory')
|
||||
url = reverse('API:_api_device_inventory')
|
||||
|
||||
client.force_login(self.add_user)
|
||||
response = client.post(url, data=self.inventory, content_type='application/json')
|
||||
@ -201,7 +201,7 @@ class InventoryAPI(TestCase):
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse('v1:_api_device_inventory')
|
||||
url = reverse('API:_api_device_inventory')
|
||||
|
||||
client.force_login(self.add_user)
|
||||
response = client.post(url, data=self.inventory, content_type='application/json')
|
||||
@ -220,7 +220,7 @@ class InventoryAPI(TestCase):
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse('v1:_api_device_inventory')
|
||||
url = reverse('API:_api_device_inventory')
|
||||
|
||||
client.force_login(self.add_user)
|
||||
response = client.post(url, data=self.inventory, content_type='application/json')
|
||||
@ -239,7 +239,7 @@ class InventoryAPI(TestCase):
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse('v1:_api_device_inventory')
|
||||
url = reverse('API:_api_device_inventory')
|
||||
|
||||
client.force_login(self.add_user)
|
||||
response = client.post(url, data=self.inventory, content_type='application/json')
|
||||
@ -395,7 +395,7 @@ class InventoryAPI(TestCase):
|
||||
""" Successful inventory upload returns 200 for existing device"""
|
||||
|
||||
client = Client()
|
||||
url = reverse('v1:_api_device_inventory')
|
||||
url = reverse('API:_api_device_inventory')
|
||||
|
||||
client.force_login(self.add_user)
|
||||
response = client.post(url, data=self.inventory, content_type='application/json')
|
||||
@ -409,7 +409,7 @@ class InventoryAPI(TestCase):
|
||||
""" Incorrectly formated inventory upload returns 400 """
|
||||
|
||||
client = Client()
|
||||
url = reverse('v1:_api_device_inventory')
|
||||
url = reverse('API:_api_device_inventory')
|
||||
|
||||
mod_inventory = self.inventory.copy()
|
||||
|
||||
@ -502,8 +502,7 @@ class InventoryAPIDifferentNameSerialNumberMatch(TestCase):
|
||||
|
||||
Device.objects.create(
|
||||
name='random device name',
|
||||
serial_number='serial_number_123',
|
||||
organization = organization,
|
||||
serial_number='serial_number_123'
|
||||
)
|
||||
|
||||
add_permissions = Permission.objects.get(
|
||||
@ -538,7 +537,7 @@ class InventoryAPIDifferentNameSerialNumberMatch(TestCase):
|
||||
process_inventory(json.dumps(self.inventory), organization.id)
|
||||
|
||||
|
||||
self.device = Device.objects.get(name=self.inventory['details']['name'], organization = organization)
|
||||
self.device = Device.objects.get(name=self.inventory['details']['name'])
|
||||
|
||||
self.operating_system = OperatingSystem.objects.get(name=self.inventory['os']['name'])
|
||||
|
||||
@ -779,8 +778,7 @@ class InventoryAPIDifferentNameUUIDMatch(TestCase):
|
||||
|
||||
Device.objects.create(
|
||||
name='random device name',
|
||||
uuid='123-456-789',
|
||||
organization = organization,
|
||||
uuid='123-456-789'
|
||||
)
|
||||
|
||||
add_permissions = Permission.objects.get(
|
||||
|
@ -201,7 +201,7 @@ class InventoryPermissionsAPI(TestCase):
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse('v1:_api_device_inventory')
|
||||
url = reverse('API:_api_device_inventory')
|
||||
|
||||
|
||||
response = client.put(url, data=self.inventory, content_type='application/json')
|
||||
@ -218,7 +218,7 @@ class InventoryPermissionsAPI(TestCase):
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse('v1:_api_device_inventory')
|
||||
url = reverse('API:_api_device_inventory')
|
||||
|
||||
|
||||
client.force_login(self.no_permissions_user)
|
||||
@ -236,7 +236,7 @@ class InventoryPermissionsAPI(TestCase):
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse('v1:_api_device_inventory')
|
||||
url = reverse('API:_api_device_inventory')
|
||||
|
||||
|
||||
client.force_login(self.different_organization_user)
|
||||
@ -254,7 +254,7 @@ class InventoryPermissionsAPI(TestCase):
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse('v1:_api_device_inventory')
|
||||
url = reverse('API:_api_device_inventory')
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
@ -272,7 +272,7 @@ class InventoryPermissionsAPI(TestCase):
|
||||
"""
|
||||
|
||||
client = Client()
|
||||
url = reverse('v1:_api_device_inventory')
|
||||
url = reverse('API:_api_device_inventory')
|
||||
|
||||
|
||||
client.force_login(self.add_user)
|
||||
|
@ -1,42 +0,0 @@
|
||||
from django.contrib.auth.models import User
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from access.models import Organization
|
||||
|
||||
from api.tests.abstract.viewsets import ViewSetCommon
|
||||
|
||||
from api.viewsets.index import Index
|
||||
|
||||
|
||||
class HomeViewset(
|
||||
TestCase,
|
||||
ViewSetCommon
|
||||
):
|
||||
|
||||
viewset = Index
|
||||
|
||||
route_name = 'API:_api_v2_home'
|
||||
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user
|
||||
3. create super user
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_add", password="password", is_superuser=True)
|
||||
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.route_name + '-list')
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
self.http_options_response_list = client.options(url)
|
File diff suppressed because it is too large
Load Diff
@ -5,25 +5,6 @@ from rest_framework.urlpatterns import format_suffix_patterns
|
||||
|
||||
from .views import access, config, index
|
||||
|
||||
from api.views.settings import permissions
|
||||
from api.views.settings import index as settings
|
||||
|
||||
from api.views import assistance, itim, project_management
|
||||
from api.views.assistance import request_ticket
|
||||
from api.views.core import (
|
||||
ticket_categories,
|
||||
ticket_comment_categories,
|
||||
ticket_comments as core_ticket_comments
|
||||
)
|
||||
from api.views.itim import change_ticket, incident_ticket, problem_ticket
|
||||
from api.views.project_management import (
|
||||
projects,
|
||||
project_milestone,
|
||||
project_state,
|
||||
project_type,
|
||||
project_task
|
||||
)
|
||||
|
||||
from .views.itam import software, config as itam_config
|
||||
from .views.itam.device import DeviceViewSet
|
||||
from .views.itam import inventory
|
||||
@ -32,42 +13,15 @@ from .views.itam import inventory
|
||||
app_name = "API"
|
||||
|
||||
|
||||
router = DefaultRouter(trailing_slash=False)
|
||||
router = DefaultRouter()
|
||||
|
||||
router.register('', index.Index, basename='_api_home')
|
||||
|
||||
router.register('assistance/request', request_ticket.View, basename='_api_assistance_request')
|
||||
router.register('assistance/request/(?P<ticket_id>[0-9]+)/comments', core_ticket_comments.View, basename='_api_assistance_request_ticket_comments')
|
||||
|
||||
router.register('device', DeviceViewSet, basename='device')
|
||||
|
||||
router.register('itim/change', change_ticket.View, basename='_api_itim_change')
|
||||
router.register('itim/change/(?P<ticket_id>[0-9]+)/comments', core_ticket_comments.View, basename='_api_itim_change_ticket_comments')
|
||||
|
||||
router.register('itim/incident', incident_ticket.View, basename='_api_itim_incident')
|
||||
router.register('itim/incident/(?P<ticket_id>[0-9]+)/comments', core_ticket_comments.View, basename='_api_itim_incident_ticket_comments')
|
||||
|
||||
router.register('itim/problem', problem_ticket.View, basename='_api_itim_problem')
|
||||
router.register('itim/problem/(?P<ticket_id>[0-9]+)/comments', core_ticket_comments.View, basename='_api_itim_problem_ticket_comments')
|
||||
|
||||
router.register('project_management/projects', projects.View, basename='_api_projects')
|
||||
router.register('project_management/projects/(?P<project_id>[0-9]+)/milestones', project_milestone.View, basename='_api_project_milestone')
|
||||
router.register('project_management/projects/(?P<project_id>[0-9]+)/tasks', project_task.View, basename='_api_project_tasks')
|
||||
router.register('project_management/projects/(?P<project_id>[0-9]+)/tasks/(?P<ticket_id>[0-9]+)/comments', core_ticket_comments.View, basename='_api_project_tasks_comments')
|
||||
|
||||
router.register('settings/ticket_categories', ticket_categories.View, basename='_api_ticket_category')
|
||||
|
||||
router.register('settings/project_state', project_state.View, basename='_api_project_state')
|
||||
router.register('settings/project_type', project_type.View, basename='_api_project_type')
|
||||
router.register('settings/ticket_comment_categories', ticket_comment_categories.View, basename='_api_ticket_comment_category')
|
||||
|
||||
router.register('software', software.SoftwareViewSet, basename='software')
|
||||
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
path("assistance", assistance.index.Index.as_view(), name="_api_assistance"),
|
||||
|
||||
path("config/<slug:slug>/", itam_config.View.as_view(), name="_api_device_config"),
|
||||
|
||||
path("configuration/", config.ConfigGroupsList.as_view(), name='_api_config_groups'),
|
||||
@ -75,8 +29,6 @@ urlpatterns = [
|
||||
|
||||
path("device/inventory", inventory.Collect.as_view(), name="_api_device_inventory"),
|
||||
|
||||
path("itim", itim.index.Index.as_view(), name="_api_itim"),
|
||||
|
||||
path("organization/", access.OrganizationList.as_view(), name='_api_orgs'),
|
||||
path("organization/<int:pk>/", access.OrganizationDetail.as_view(), name='_api_organization'),
|
||||
path("organization/<int:organization_id>/team", access.TeamList.as_view(), name='_api_organization_teams'),
|
||||
@ -84,11 +36,6 @@ urlpatterns = [
|
||||
path("organization/<int:organization_id>/team/<int:group_ptr_id>/permissions", access.TeamPermissionDetail.as_view(), name='_api_team_permission'),
|
||||
path("organization/team/", access.TeamList.as_view(), name='_api_teams'),
|
||||
|
||||
path("project_management", project_management.index.Index.as_view(), name="_api_project_management"),
|
||||
|
||||
path("settings", settings.View.as_view(), name='_settings'),
|
||||
path("settings/permissions", permissions.View.as_view(), name='_settings_permissions'),
|
||||
|
||||
]
|
||||
|
||||
urlpatterns = format_suffix_patterns(urlpatterns)
|
||||
|
@ -1,198 +0,0 @@
|
||||
from django.urls import path
|
||||
|
||||
from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView
|
||||
|
||||
from rest_framework.routers import DefaultRouter
|
||||
|
||||
from api.viewsets import (
|
||||
index as v2
|
||||
)
|
||||
|
||||
from app.viewsets.base import (
|
||||
index as base_index_v2,
|
||||
content_type as content_type_v2,
|
||||
permisson as permission_v2,
|
||||
user as user_v2
|
||||
)
|
||||
|
||||
from access.viewsets import (
|
||||
index as access_v2,
|
||||
organization as organization_v2,
|
||||
team as team_v2,
|
||||
team_user as team_user_v2
|
||||
)
|
||||
|
||||
from assistance.viewsets import (
|
||||
index as assistance_index_v2,
|
||||
knowledge_base as knowledge_base_v2,
|
||||
knowledge_base_category as knowledge_base_category_v2,
|
||||
model_knowledge_base_article,
|
||||
request as request_ticket_v2,
|
||||
)
|
||||
|
||||
from config_management.viewsets import (
|
||||
index as config_management_v2,
|
||||
config_group as config_group_v2,
|
||||
config_group_software as config_group_software_v2
|
||||
)
|
||||
|
||||
from core.viewsets import (
|
||||
celery_log as celery_log_v2,
|
||||
history as history_v2,
|
||||
manufacturer as manufacturer_v2,
|
||||
notes as notes_v2,
|
||||
ticket_category,
|
||||
ticket_comment,
|
||||
ticket_comment_category,
|
||||
ticket_linked_item,
|
||||
related_ticket,
|
||||
|
||||
)
|
||||
|
||||
from itam.viewsets import (
|
||||
index as itam_index_v2,
|
||||
device as device_v2,
|
||||
device_model as device_model_v2,
|
||||
device_type as device_type_v2,
|
||||
device_software as device_software_v2,
|
||||
device_operating_system,
|
||||
inventory,
|
||||
operating_system as operating_system_v2,
|
||||
operating_system_version as operating_system_version_v2,
|
||||
software as software_v2,
|
||||
software_category as software_category_v2,
|
||||
software_version as software_version_v2,
|
||||
)
|
||||
|
||||
from itim.viewsets import (
|
||||
index as itim_v2,
|
||||
change,
|
||||
cluster as cluster_v2,
|
||||
cluster_type as cluster_type_v2,
|
||||
incident,
|
||||
port as port_v2,
|
||||
problem,
|
||||
service as service_v2,
|
||||
service_device as service_device_v2
|
||||
)
|
||||
|
||||
from project_management.viewsets import (
|
||||
index as project_management_v2,
|
||||
project as project_v2,
|
||||
project_milestone as project_milestone_v2,
|
||||
project_state as project_state_v2,
|
||||
project_task,
|
||||
project_type as project_type_v2,
|
||||
)
|
||||
|
||||
from settings.viewsets import (
|
||||
app_settings as app_settings_v2,
|
||||
external_link as external_link_v2,
|
||||
index as settings_index_v2,
|
||||
user_settings as user_settings_v2
|
||||
)
|
||||
|
||||
app_name = "API"
|
||||
|
||||
|
||||
router = DefaultRouter(trailing_slash=False)
|
||||
|
||||
|
||||
router.register('', v2.Index, basename='_api_v2_home')
|
||||
|
||||
router.register('access', access_v2.Index, basename='_api_v2_access_home')
|
||||
router.register('access/organization', organization_v2.ViewSet, basename='_api_v2_organization')
|
||||
router.register('access/organization/(?P<organization_id>[0-9]+)/team', team_v2.ViewSet, basename='_api_v2_organization_team')
|
||||
router.register('access/organization/(?P<organization_id>[0-9]+)/team/(?P<team_id>[0-9]+)/user', team_user_v2.ViewSet, basename='_api_v2_organization_team_user')
|
||||
|
||||
|
||||
router.register('assistance', assistance_index_v2.Index, basename='_api_v2_assistance_home')
|
||||
router.register('assistance/knowledge_base', knowledge_base_v2.ViewSet, basename='_api_v2_knowledge_base')
|
||||
router.register('assistance/(?P<model>.+)/(?P<model_pk>[0-9]+)/knowledge_base', model_knowledge_base_article.ViewSet, basename='_api_v2_model_kb')
|
||||
router.register('assistance/ticket/request', request_ticket_v2.ViewSet, basename='_api_v2_ticket_request')
|
||||
|
||||
|
||||
router.register('base', base_index_v2.Index, basename='_api_v2_base_home')
|
||||
router.register('base/content_type', content_type_v2.ViewSet, basename='_api_v2_content_type')
|
||||
router.register('base/permission', permission_v2.ViewSet, basename='_api_v2_permission')
|
||||
router.register('base/user', user_v2.ViewSet, basename='_api_v2_user')
|
||||
|
||||
|
||||
router.register('config_management', config_management_v2.Index, basename='_api_v2_config_management_home')
|
||||
router.register('config_management/group', config_group_v2.ViewSet, basename='_api_v2_config_group')
|
||||
router.register('config_management/group/(?P<parent_group>[0-9]+)/child_group', config_group_v2.ViewSet, basename='_api_v2_config_group_child')
|
||||
router.register('config_management/group/(?P<config_group_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_config_group_notes')
|
||||
router.register('config_management/group/(?P<config_group_id>[0-9]+)/software', config_group_software_v2.ViewSet, basename='_api_v2_config_group_software')
|
||||
|
||||
|
||||
router.register('core/(?P<model_class>.+)/(?P<model_id>[0-9]+)/history', history_v2.ViewSet, basename='_api_v2_model_history')
|
||||
router.register('core/ticket/(?P<ticket_id>[0-9]+)/comments', ticket_comment.ViewSet, basename='_api_v2_ticket_comment')
|
||||
router.register('core/ticket/(?P<ticket_id>[0-9]+)/comments/(?P<parent_id>[0-9]+)/threads', ticket_comment.ViewSet, basename='_api_v2_ticket_comment_threads')
|
||||
router.register('core/ticket/(?P<ticket_id>[0-9]+)/linked_item', ticket_linked_item.ViewSet, basename='_api_v2_ticket_linked_item')
|
||||
router.register('core/ticket/(?P<ticket_id>[0-9]+)/related_ticket', related_ticket.ViewSet, basename='_api_v2_ticket_related')
|
||||
router.register('core/(?P<item_class>[a-z_]+)/(?P<item_id>[0-9]+)/item_ticket', ticket_linked_item.ViewSet, basename='_api_v2_item_tickets')
|
||||
|
||||
|
||||
router.register('itam', itam_index_v2.Index, basename='_api_v2_itam_home')
|
||||
router.register('itam/device', device_v2.ViewSet, basename='_api_v2_device')
|
||||
router.register('itam/device/(?P<device_id>[0-9]+)/operating_system', device_operating_system.ViewSet, basename='_api_v2_device_operating_system')
|
||||
router.register('itam/device/(?P<device_id>[0-9]+)/software', device_software_v2.ViewSet, basename='_api_v2_device_software')
|
||||
router.register('itam/device/(?P<device_id>[0-9]+)/service', service_device_v2.ViewSet, basename='_api_v2_service_device')
|
||||
router.register('itam/device/(?P<device_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_device_notes')
|
||||
router.register('itam/inventory', inventory.ViewSet, basename='_api_v2_inventory')
|
||||
router.register('itam/operating_system', operating_system_v2.ViewSet, basename='_api_v2_operating_system')
|
||||
router.register('itam/operating_system/(?P<operating_system_id>[0-9]+)/installs', device_operating_system.ViewSet, basename='_api_v2_operating_system_installs')
|
||||
router.register('itam/operating_system/(?P<operating_system_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_operating_system_notes')
|
||||
router.register('itam/operating_system/(?P<operating_system_id>[0-9]+)/version', operating_system_version_v2.ViewSet, basename='_api_v2_operating_system_version')
|
||||
router.register('itam/software', software_v2.ViewSet, basename='_api_v2_software')
|
||||
router.register('itam/software/(?P<software_id>[0-9]+)/installs', device_software_v2.ViewSet, basename='_api_v2_software_installs')
|
||||
router.register('itam/software/(?P<software_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_software_notes')
|
||||
router.register('itam/software/(?P<software_id>[0-9]+)/version', software_version_v2.ViewSet, basename='_api_v2_software_version')
|
||||
|
||||
|
||||
router.register('itim', itim_v2.Index, basename='_api_v2_itim_home')
|
||||
router.register('itim/ticket/change', change.ViewSet, basename='_api_v2_ticket_change')
|
||||
router.register('itim/cluster', cluster_v2.ViewSet, basename='_api_v2_cluster')
|
||||
router.register('itim/cluster/(?P<cluster_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_cluster_notes')
|
||||
router.register('itim/ticket/incident', incident.ViewSet, basename='_api_v2_ticket_incident')
|
||||
router.register('itim/ticket/problem', problem.ViewSet, basename='_api_v2_ticket_problem')
|
||||
router.register('itim/service', service_v2.ViewSet, basename='_api_v2_service')
|
||||
router.register('itim/service/(?P<service_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_service_notes')
|
||||
|
||||
|
||||
router.register('project_management', project_management_v2.Index, basename='_api_v2_project_management_home')
|
||||
router.register('project_management/project', project_v2.ViewSet, basename='_api_v2_project')
|
||||
router.register('project_management/project/(?P<project_id>[0-9]+)/milestone', project_milestone_v2.ViewSet, basename='_api_v2_project_milestone')
|
||||
router.register('project_management/project/(?P<project_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_project_notes')
|
||||
router.register('project_management/project/(?P<project_id>[0-9]+)/project_task', project_task.ViewSet, basename='_api_v2_ticket_project_task')
|
||||
|
||||
|
||||
router.register('settings', settings_index_v2.Index, basename='_api_v2_settings_home')
|
||||
router.register('settings/app_settings', app_settings_v2.ViewSet, basename='_api_v2_app_settings')
|
||||
router.register('settings/celery_log', celery_log_v2.ViewSet, basename='_api_v2_celery_log')
|
||||
router.register('settings/cluster_type', cluster_type_v2.ViewSet, basename='_api_v2_cluster_type')
|
||||
router.register('settings/cluster_type/(?P<cluster_type_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_cluster_type_notes')
|
||||
router.register('settings/device_model', device_model_v2.ViewSet, basename='_api_v2_device_model')
|
||||
router.register('settings/device_type', device_type_v2.ViewSet, basename='_api_v2_device_type')
|
||||
router.register('settings/external_link', external_link_v2.ViewSet, basename='_api_v2_external_link')
|
||||
router.register('settings/knowledge_base_category', knowledge_base_category_v2.ViewSet, basename='_api_v2_knowledge_base_category')
|
||||
router.register('settings/manufacturer', manufacturer_v2.ViewSet, basename='_api_v2_manufacturer')
|
||||
router.register('settings/manufacturer/(?P<manufacturer_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_manufacturer_notes')
|
||||
router.register('settings/port', port_v2.ViewSet, basename='_api_v2_port')
|
||||
router.register('settings/port/(?P<port_id>[0-9]+)/notes', notes_v2.ViewSet, basename='_api_v2_port_notes')
|
||||
router.register('settings/project_state', project_state_v2.ViewSet, basename='_api_v2_project_state')
|
||||
router.register('settings/project_type', project_type_v2.ViewSet, basename='_api_v2_project_type')
|
||||
router.register('settings/software_category', software_category_v2.ViewSet, basename='_api_v2_software_category')
|
||||
router.register('settings/ticket_category', ticket_category.ViewSet, basename='_api_v2_ticket_category')
|
||||
router.register('settings/ticket_comment_category', ticket_comment_category.ViewSet, basename='_api_v2_ticket_comment_category')
|
||||
router.register('settings/user_settings', user_settings_v2.ViewSet, basename='_api_v2_user_settings')
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
path('schema', SpectacularAPIView.as_view(api_version='v2'), name='schema-v2',),
|
||||
path('docs', SpectacularSwaggerView.as_view(url_name='schema-v2'), name='_api_v2_docs'),
|
||||
|
||||
]
|
||||
|
||||
urlpatterns += router.urls
|
@ -1,47 +0,0 @@
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
|
||||
class ViewSetAttributesUnit:
|
||||
""" Unit Tests For View Set attributes.
|
||||
|
||||
These tests ensure that View sets contian the required attributesthat are
|
||||
used by the API .
|
||||
"""
|
||||
|
||||
|
||||
def test_attribute_exists_page_layout(self):
|
||||
"""Attrribute Test, Exists
|
||||
|
||||
Ensure attribute `page_layout` exists
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def test_attribute_type_page_layout(self):
|
||||
"""Attrribute Test, Type
|
||||
|
||||
Ensure attribute `page_layout` is of type `list`
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
|
||||
def test_attribute_not_callable_page_layout(self):
|
||||
"""Attrribute Test, Not Callable
|
||||
|
||||
Attribute must be a property
|
||||
|
||||
Ensure attribute `page_layout` is not callable.
|
||||
"""
|
||||
|
||||
pass
|
||||
|
||||
# other tests required
|
||||
# - filterset_fields
|
||||
# - metadata_class
|
||||
# - search_fields
|
||||
# - documentation
|
||||
# - model_documentation or is in `model.documentation`
|
@ -12,7 +12,7 @@ from access.models import Organization, Team
|
||||
from api.serializers.access import OrganizationSerializer, OrganizationListSerializer, TeamSerializer, TeamPermissionSerializer
|
||||
from api.views.mixin import OrganizationPermissionAPI
|
||||
|
||||
@extend_schema(deprecated=True)
|
||||
|
||||
@extend_schema_view(
|
||||
get=extend_schema(
|
||||
summary = "Fetch Organizations",
|
||||
@ -34,7 +34,7 @@ class OrganizationList(generics.ListAPIView):
|
||||
return "Organizations"
|
||||
|
||||
|
||||
@extend_schema(deprecated=True)
|
||||
|
||||
@extend_schema_view(
|
||||
get=extend_schema(
|
||||
summary = "Get An Organization",
|
||||
@ -61,7 +61,7 @@ class OrganizationDetail(generics.RetrieveUpdateAPIView):
|
||||
return "Organization"
|
||||
|
||||
|
||||
@extend_schema(deprecated=True)
|
||||
|
||||
@extend_schema_view(
|
||||
post=extend_schema(
|
||||
summary = "Create a Team",
|
||||
@ -97,7 +97,7 @@ class TeamList(generics.ListCreateAPIView):
|
||||
return "Organization Teams"
|
||||
|
||||
|
||||
@extend_schema(deprecated=True)
|
||||
|
||||
@extend_schema_view(
|
||||
get=extend_schema(
|
||||
summary = "Fetch a Team",
|
||||
@ -149,7 +149,7 @@ class TeamDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
lookup_field = 'group_ptr_id'
|
||||
|
||||
|
||||
@extend_schema(deprecated=True)
|
||||
|
||||
@extend_schema_view(
|
||||
get=extend_schema(
|
||||
summary = "Fetch a teams permissions",
|
||||
|
@ -1 +0,0 @@
|
||||
from .index import *
|
@ -1,37 +0,0 @@
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
from rest_framework import generics, permissions, routers, views
|
||||
# from rest_framework.decorators import api_view
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
|
||||
@extend_schema(deprecated=True)
|
||||
class Index(views.APIView):
|
||||
|
||||
permission_classes = [
|
||||
IsAuthenticated,
|
||||
]
|
||||
|
||||
|
||||
def get_view_name(self):
|
||||
return "Assistance"
|
||||
|
||||
def get_view_description(self, html=False) -> str:
|
||||
text = "Assistance Module"
|
||||
if html:
|
||||
return mark_safe(f"<p>{text}</p>")
|
||||
else:
|
||||
return text
|
||||
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
body: dict = {
|
||||
'requests': reverse('v1:_api_assistance_request-list', request=request)
|
||||
}
|
||||
|
||||
return Response(body)
|
@ -1,78 +0,0 @@
|
||||
from drf_spectacular.utils import extend_schema, OpenApiResponse
|
||||
|
||||
from api.serializers.assistance.request import RequestTicketSerializer
|
||||
from api.views.core.tickets import View
|
||||
|
||||
|
||||
@extend_schema(deprecated=True)
|
||||
class View(View):
|
||||
|
||||
_ticket_type:str = 'request'
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Create a ticket',
|
||||
description = """This model includes all of the ticket types.
|
||||
Due to this not all fields will be available and what fields are available
|
||||
depends upon the comment type. see
|
||||
[administration docs](https://nofusscomputing.com/projects/centurion_erp/administration/core/ticketing/index.html) for more info.
|
||||
""",
|
||||
request = RequestTicketSerializer,
|
||||
responses = {
|
||||
201: OpenApiResponse(
|
||||
response = RequestTicketSerializer,
|
||||
),
|
||||
}
|
||||
)
|
||||
def create(self, request, *args, **kwargs):
|
||||
|
||||
return super().create(request, *args, **kwargs)
|
||||
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Fetch all tickets',
|
||||
description = """This model includes all of the ticket comment types.
|
||||
Due to this not all fields will be available and what fields are available
|
||||
depends upon the comment type. see
|
||||
[administration docs](https://nofusscomputing.com/projects/centurion_erp/administration/core/ticketing/index.html) for more info.
|
||||
""",
|
||||
methods=["GET"],
|
||||
responses = {
|
||||
200: OpenApiResponse(
|
||||
description='Success',
|
||||
response = RequestTicketSerializer
|
||||
)
|
||||
}
|
||||
)
|
||||
def list(self, request, *args, **kwargs):
|
||||
|
||||
return super().list(request, *args, **kwargs)
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Fetch the selected ticket',
|
||||
description = """This model includes all of the ticket comment types.
|
||||
Due to this not all fields will be available and what fields are available
|
||||
depends upon the comment type. see
|
||||
[administration docs](https://nofusscomputing.com/projects/centurion_erp/administration/core/ticketing/index.html) for more info.
|
||||
""",
|
||||
methods=["GET"],
|
||||
responses = {
|
||||
200: OpenApiResponse(
|
||||
description='Success',
|
||||
response = RequestTicketSerializer
|
||||
)
|
||||
}
|
||||
)
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
|
||||
return super().retrieve(request, *args, **kwargs)
|
||||
|
||||
|
||||
def get_view_name(self):
|
||||
|
||||
if self.detail:
|
||||
return "Request Ticket"
|
||||
|
||||
return 'Request Tickets'
|
@ -8,7 +8,7 @@ from api.views.mixin import OrganizationPermissionAPI
|
||||
from config_management.models.groups import ConfigGroups
|
||||
|
||||
|
||||
@extend_schema( deprecated = True )
|
||||
|
||||
@extend_schema_view(
|
||||
get=extend_schema(
|
||||
summary = "Fetch Config groups",
|
||||
@ -31,7 +31,6 @@ class ConfigGroupsList(generics.ListAPIView):
|
||||
|
||||
|
||||
|
||||
@extend_schema( deprecated = True )
|
||||
@extend_schema_view(
|
||||
get=extend_schema(
|
||||
summary = "Get A Config Group",
|
||||
|
@ -1,79 +0,0 @@
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from drf_spectacular.utils import extend_schema, OpenApiResponse
|
||||
|
||||
from rest_framework import generics, viewsets
|
||||
|
||||
from access.mixin import OrganizationMixin
|
||||
|
||||
from api.serializers.core.ticket_category import TicketCategory, TicketCategorySerializer
|
||||
from api.views.mixin import OrganizationPermissionAPI
|
||||
|
||||
|
||||
@extend_schema(deprecated=True)
|
||||
class View(OrganizationMixin, viewsets.ModelViewSet):
|
||||
|
||||
permission_classes = [
|
||||
OrganizationPermissionAPI
|
||||
]
|
||||
|
||||
queryset = TicketCategory.objects.all()
|
||||
|
||||
serializer_class = TicketCategorySerializer
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Create a ticket category',
|
||||
request = TicketCategorySerializer,
|
||||
responses = {
|
||||
201: OpenApiResponse(description='Ticket category created', response=TicketCategorySerializer),
|
||||
403: OpenApiResponse(description='User tried to edit field they dont have access to'),
|
||||
}
|
||||
)
|
||||
def create(self, request, *args, **kwargs):
|
||||
|
||||
return super().create(request, *args, **kwargs)
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Fetch all of a tickets category',
|
||||
methods=["GET"],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='Success', response=TicketCategorySerializer),
|
||||
}
|
||||
)
|
||||
def list(self, request, *args, **kwargs):
|
||||
|
||||
return super().list(request, *args, **kwargs)
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Fetch the selected ticket category',
|
||||
methods=["GET"],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='Success', response=TicketCategorySerializer),
|
||||
}
|
||||
)
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
|
||||
return super().retrieve(request, *args, **kwargs)
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Update a ticket category',
|
||||
methods=["PUT"],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='Ticket comment updated', response=TicketCategorySerializer),
|
||||
403: OpenApiResponse(description='User tried to edit field they dont have access to'),
|
||||
}
|
||||
)
|
||||
def update(self, request, *args, **kwargs):
|
||||
|
||||
return super().update(request, *args, **kwargs)
|
||||
|
||||
|
||||
def get_view_name(self):
|
||||
if self.detail:
|
||||
return "Ticket Category"
|
||||
|
||||
return 'Ticket Categories'
|
@ -1,79 +0,0 @@
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from drf_spectacular.utils import extend_schema, OpenApiResponse
|
||||
|
||||
from rest_framework import generics, viewsets
|
||||
|
||||
from access.mixin import OrganizationMixin
|
||||
|
||||
from api.serializers.core.ticket_comment_category import TicketCommentCategory, TicketCommentCategorySerializer
|
||||
from api.views.mixin import OrganizationPermissionAPI
|
||||
|
||||
|
||||
@extend_schema(deprecated=True)
|
||||
class View(OrganizationMixin, viewsets.ModelViewSet):
|
||||
|
||||
permission_classes = [
|
||||
OrganizationPermissionAPI
|
||||
]
|
||||
|
||||
queryset = TicketCommentCategory.objects.all()
|
||||
|
||||
serializer_class = TicketCommentCategorySerializer
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Create a ticket comment category',
|
||||
request = TicketCommentCategorySerializer,
|
||||
responses = {
|
||||
201: OpenApiResponse(description='Ticket category created', response=TicketCommentCategorySerializer),
|
||||
403: OpenApiResponse(description='User tried to edit field they dont have access to'),
|
||||
}
|
||||
)
|
||||
def create(self, request, *args, **kwargs):
|
||||
|
||||
return super().create(request, *args, **kwargs)
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Fetch all of the ticket comment categories',
|
||||
methods=["GET"],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='Success', response=TicketCommentCategorySerializer),
|
||||
}
|
||||
)
|
||||
def list(self, request, *args, **kwargs):
|
||||
|
||||
return super().list(request, *args, **kwargs)
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Fetch the selected ticket comment category',
|
||||
methods=["GET"],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='Success', response=TicketCommentCategorySerializer),
|
||||
}
|
||||
)
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
|
||||
return super().retrieve(request, *args, **kwargs)
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Update a ticket comment category',
|
||||
methods=["PUT"],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='Ticket comment updated', response=TicketCommentCategorySerializer),
|
||||
403: OpenApiResponse(description='User tried to edit field they dont have access to'),
|
||||
}
|
||||
)
|
||||
def update(self, request, *args, **kwargs):
|
||||
|
||||
return super().update(request, *args, **kwargs)
|
||||
|
||||
|
||||
def get_view_name(self):
|
||||
if self.detail:
|
||||
return "Ticket Comment Category"
|
||||
|
||||
return 'Ticket Comment Categories'
|
@ -1,102 +0,0 @@
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from drf_spectacular.utils import extend_schema, OpenApiResponse
|
||||
|
||||
from rest_framework import generics, viewsets
|
||||
|
||||
from access.mixin import OrganizationMixin
|
||||
|
||||
from api.serializers.core.ticket_comment import TicketCommentSerializer
|
||||
from api.views.mixin import OrganizationPermissionAPI
|
||||
|
||||
from core.models.ticket.ticket_comment import TicketComment
|
||||
|
||||
|
||||
@extend_schema(deprecated=True)
|
||||
class View(OrganizationMixin, viewsets.ModelViewSet):
|
||||
|
||||
permission_classes = [
|
||||
OrganizationPermissionAPI
|
||||
]
|
||||
|
||||
queryset = TicketComment.objects.all()
|
||||
|
||||
serializer_class = TicketCommentSerializer
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Create a ticket comment',
|
||||
description = """This model includes all of the ticket comment types.
|
||||
Due to this not all fields will be available and what fields are available
|
||||
depends upon the comment type.
|
||||
""",
|
||||
request = TicketCommentSerializer,
|
||||
responses = {
|
||||
201: OpenApiResponse(description='Ticket comment created', response=TicketCommentSerializer),
|
||||
403: OpenApiResponse(description='User tried to edit field they dont have access to'),
|
||||
}
|
||||
)
|
||||
def create(self, request, *args, **kwargs):
|
||||
|
||||
return super().create(request, *args, **kwargs)
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Fetch all of a tickets comments',
|
||||
methods=["GET"],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='Success', response=TicketCommentSerializer),
|
||||
}
|
||||
)
|
||||
def list(self, request, *args, **kwargs):
|
||||
|
||||
return super().list(request, *args, **kwargs)
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Fetch the selected ticket Comment',
|
||||
methods=["GET"],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='Success', response=TicketCommentSerializer),
|
||||
}
|
||||
)
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
|
||||
return super().retrieve(request, *args, **kwargs)
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Update a ticket Comment',
|
||||
description = """This model includes all of the ticket comment types.
|
||||
Due to this not all fields will be available and what fields are available
|
||||
depends upon the comment type.
|
||||
""",
|
||||
methods=["PUT"],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='Ticket comment updated', response=TicketCommentSerializer),
|
||||
403: OpenApiResponse(description='User tried to edit field they dont have access to'),
|
||||
}
|
||||
)
|
||||
def update(self, request, *args, **kwargs):
|
||||
|
||||
return super().update(request, *args, **kwargs)
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
if 'ticket_id' in self.kwargs:
|
||||
|
||||
self.queryset = self.queryset.filter(ticket=self.kwargs['ticket_id']).order_by('created')
|
||||
|
||||
if 'pk' in self.kwargs:
|
||||
|
||||
self.queryset = self.queryset.filter(pk = self.kwargs['pk'])
|
||||
|
||||
return self.queryset
|
||||
|
||||
|
||||
def get_view_name(self):
|
||||
if self.detail:
|
||||
return "Ticket Comment"
|
||||
|
||||
return 'Ticket Comments'
|
@ -1,164 +0,0 @@
|
||||
from django.db.models import Q
|
||||
|
||||
from rest_framework import generics, viewsets
|
||||
|
||||
from access.mixin import OrganizationMixin
|
||||
|
||||
from api.serializers.assistance.request import RequestTicketSerializer
|
||||
from api.serializers.itim.change import ChangeTicketSerializer
|
||||
from api.serializers.itim.incident import IncidentTicketSerializer
|
||||
from api.serializers.itim.problem import ProblemTicketSerializer
|
||||
from api.serializers.project_management.project_task import ProjectTaskSerializer
|
||||
from api.views.mixin import OrganizationPermissionAPI
|
||||
|
||||
from core.models.ticket.ticket import Ticket
|
||||
|
||||
|
||||
|
||||
class View(OrganizationMixin, viewsets.ModelViewSet):
|
||||
|
||||
filterset_fields = [
|
||||
'external_system',
|
||||
'external_ref',
|
||||
]
|
||||
|
||||
search_fields = [
|
||||
'title',
|
||||
'description',
|
||||
]
|
||||
|
||||
permission_classes = [
|
||||
OrganizationPermissionAPI
|
||||
]
|
||||
|
||||
def get_dynamic_permissions(self):
|
||||
|
||||
if self.action == 'create':
|
||||
|
||||
action_keyword = 'add'
|
||||
|
||||
elif self.action == 'destroy':
|
||||
|
||||
action_keyword = 'delete'
|
||||
|
||||
elif self.action == 'list':
|
||||
|
||||
action_keyword = 'view'
|
||||
|
||||
elif self.action == 'partial_update':
|
||||
|
||||
action_keyword = 'change'
|
||||
|
||||
elif self.action == 'retrieve':
|
||||
|
||||
action_keyword = 'view'
|
||||
|
||||
elif self.action == 'update':
|
||||
|
||||
action_keyword = 'change'
|
||||
|
||||
elif self.action is None:
|
||||
|
||||
action_keyword = 'view'
|
||||
|
||||
else:
|
||||
|
||||
raise ValueError('unable to determin the action_keyword')
|
||||
|
||||
self.permission_required = [
|
||||
'core.' + action_keyword + '_ticket_' + self._ticket_type,
|
||||
]
|
||||
|
||||
return super().get_permission_required()
|
||||
|
||||
|
||||
# queryset = Ticket.objects.all()
|
||||
queryset = None
|
||||
|
||||
model = Ticket
|
||||
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
|
||||
if self._ticket_type == 'change':
|
||||
|
||||
self.serializer_class = ChangeTicketSerializer
|
||||
|
||||
self._ticket_type_value = Ticket.TicketType.CHANGE.value
|
||||
|
||||
elif self._ticket_type == 'incident':
|
||||
|
||||
self.serializer_class = IncidentTicketSerializer
|
||||
self._ticket_type_value = Ticket.TicketType.INCIDENT.value
|
||||
|
||||
elif self._ticket_type == 'problem':
|
||||
|
||||
self.serializer_class = ProblemTicketSerializer
|
||||
self._ticket_type_value = Ticket.TicketType.PROBLEM.value
|
||||
|
||||
elif self._ticket_type == 'request':
|
||||
|
||||
self.serializer_class = RequestTicketSerializer
|
||||
self._ticket_type_value = Ticket.TicketType.REQUEST.value
|
||||
|
||||
elif self._ticket_type == 'project_task':
|
||||
|
||||
self.serializer_class = ProjectTaskSerializer
|
||||
self._ticket_type_value = Ticket.TicketType.PROJECT_TASK.value
|
||||
|
||||
else:
|
||||
|
||||
raise ValueError('unable to determin the serializer_class')
|
||||
|
||||
return super().get_serializer(*args, **kwargs)
|
||||
|
||||
|
||||
def get_queryset(self):
|
||||
|
||||
if self._ticket_type == 'change':
|
||||
|
||||
ticket_type = self.model.TicketType.CHANGE.value
|
||||
|
||||
elif self._ticket_type == 'incident':
|
||||
|
||||
ticket_type = self.model.TicketType.INCIDENT.value
|
||||
|
||||
elif self._ticket_type == 'problem':
|
||||
|
||||
ticket_type = self.model.TicketType.PROBLEM.value
|
||||
|
||||
elif self._ticket_type == 'request':
|
||||
|
||||
ticket_type = self.model.TicketType.REQUEST.value
|
||||
|
||||
elif self._ticket_type == 'project_task':
|
||||
|
||||
ticket_type = self.model.TicketType.REQUEST.value
|
||||
|
||||
# return self.queryset.filter(
|
||||
# project = self.kwargs['project_id']
|
||||
# )
|
||||
|
||||
else:
|
||||
|
||||
raise ValueError('Unknown ticket type. kwarg `ticket_type` must be set')
|
||||
|
||||
|
||||
if not self.queryset:
|
||||
|
||||
queryset = Ticket.objects.all()
|
||||
|
||||
queryset = queryset.filter(
|
||||
ticket_type = ticket_type
|
||||
)
|
||||
|
||||
if self._ticket_type == 'project_task':
|
||||
|
||||
queryset = queryset.filter(
|
||||
project = self.kwargs['project_id']
|
||||
)
|
||||
|
||||
self.queryset = queryset
|
||||
|
||||
|
||||
return self.queryset
|
@ -1,27 +1,21 @@
|
||||
from django.conf import settings as django_settings
|
||||
|
||||
# from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
|
||||
from django.contrib.auth.models import User
|
||||
from django.utils.safestring import mark_safe
|
||||
|
||||
from rest_framework import generics, permissions, routers, viewsets
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.reverse import reverse
|
||||
|
||||
|
||||
|
||||
class Index(viewsets.ViewSet):
|
||||
|
||||
permission_classes = [
|
||||
IsAuthenticated,
|
||||
]
|
||||
|
||||
# permission_required = 'access.view_organization'
|
||||
|
||||
def get_view_name(self):
|
||||
return "API"
|
||||
return "API Index"
|
||||
|
||||
def get_view_description(self, html=False) -> str:
|
||||
text = "Centurion ERP Rest API"
|
||||
text = "My REST API"
|
||||
if html:
|
||||
return mark_safe(f"<p>{text}</p>")
|
||||
else:
|
||||
@ -29,18 +23,12 @@ class Index(viewsets.ViewSet):
|
||||
|
||||
|
||||
def list(self, request, pk=None):
|
||||
|
||||
API: dict = {
|
||||
return Response(
|
||||
{
|
||||
# "teams": reverse("_api_teams", request=request),
|
||||
'assistance': reverse("v1:_api_assistance", request=request),
|
||||
"devices": reverse("v1:device-list", request=request),
|
||||
"config_groups": reverse("v1:_api_config_groups", request=request),
|
||||
'itim': reverse("v1:_api_itim", request=request),
|
||||
"organizations": reverse("v1:_api_orgs", request=request),
|
||||
'project_management': reverse("v1:_api_project_management", request=request),
|
||||
"settings": reverse('v1:_settings', request=request),
|
||||
"software": reverse("v1:software-list", request=request),
|
||||
'v2': reverse("v2:_api_v2_home-list", request=request)
|
||||
"devices": reverse("API:device-list", request=request),
|
||||
"config_groups": reverse("API:_api_config_groups", request=request),
|
||||
"organizations": reverse("API:_api_orgs", request=request),
|
||||
"software": reverse("API:software-list", request=request),
|
||||
}
|
||||
|
||||
return Response( API )
|
||||
)
|
||||
|
@ -1,14 +1,11 @@
|
||||
from django.contrib.auth.mixins import PermissionRequiredMixin, LoginRequiredMixin
|
||||
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
from itam.models.device import Device
|
||||
|
||||
from rest_framework import views
|
||||
from rest_framework.response import Response
|
||||
|
||||
|
||||
@extend_schema( deprecated = True )
|
||||
class View(views.APIView):
|
||||
|
||||
def get(self, request, slug):
|
||||
|
@ -1,10 +1,9 @@
|
||||
from django.db.models import Q
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from drf_spectacular.utils import extend_schema, OpenApiResponse
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
from rest_framework import generics, viewsets
|
||||
from rest_framework.response import Response
|
||||
|
||||
from access.mixin import OrganizationMixin
|
||||
|
||||
@ -14,7 +13,7 @@ from api.views.mixin import OrganizationPermissionAPI
|
||||
from itam.models.device import Device
|
||||
|
||||
|
||||
@extend_schema( deprecated = True )
|
||||
|
||||
class DeviceViewSet(OrganizationMixin, viewsets.ModelViewSet):
|
||||
|
||||
permission_classes = [
|
||||
@ -25,46 +24,6 @@ class DeviceViewSet(OrganizationMixin, viewsets.ModelViewSet):
|
||||
|
||||
serializer_class = DeviceSerializer
|
||||
|
||||
@extend_schema(
|
||||
summary = 'Create a device',
|
||||
description="""Add a new device to the ITAM database.
|
||||
If you attempt to create a device and a device with a matching name and uuid or name and serial number
|
||||
is found within the database, it will not re-create it. The device will be returned within the message body.
|
||||
""",
|
||||
methods=["POST"],
|
||||
responses = {
|
||||
200: OpenApiResponse(description='Device allready exists', response=DeviceSerializer),
|
||||
201: OpenApiResponse(description='Device created', response=DeviceSerializer),
|
||||
400: OpenApiResponse(description='Validation failed.'),
|
||||
403: OpenApiResponse(description='User is missing create permissions'),
|
||||
}
|
||||
)
|
||||
def create(self, request, *args, **kwargs):
|
||||
|
||||
current_device = []
|
||||
|
||||
if 'uuid' in self.request.POST:
|
||||
|
||||
current_device = self.serializer_class.Meta.model.objects.filter(
|
||||
organization = int(self.request.POST['organization']),
|
||||
uuid = str(self.request.POST['uuid'])
|
||||
)
|
||||
|
||||
if 'serial_number' in self.request.POST and len(current_device) == 0:
|
||||
|
||||
current_device = self.serializer_class.Meta.model.objects.filter(
|
||||
organization = int(self.request.POST['organization']),
|
||||
serial_number = str(self.request.POST['serial_number'])
|
||||
)
|
||||
|
||||
if len(current_device) == 1:
|
||||
|
||||
instance = current_device.get()
|
||||
serializer = self.get_serializer(instance)
|
||||
return Response(serializer.data)
|
||||
|
||||
return super().create(request, *args, **kwargs)
|
||||
|
||||
|
||||
@extend_schema( description='Fetch devices that are from the users assigned organization(s)', methods=["GET"])
|
||||
def list(self, request):
|
||||
|
@ -1,10 +1,11 @@
|
||||
import json
|
||||
import re
|
||||
|
||||
from django.core.exceptions import ValidationError, PermissionDenied
|
||||
|
||||
from drf_spectacular.utils import extend_schema, OpenApiResponse
|
||||
|
||||
from rest_framework import generics, views
|
||||
from rest_framework.exceptions import PermissionDenied, ValidationError
|
||||
from rest_framework.response import Response
|
||||
|
||||
from api.views.mixin import OrganizationPermissionAPI
|
||||
@ -33,7 +34,6 @@ class InventoryPermissions(OrganizationPermissionAPI):
|
||||
|
||||
|
||||
|
||||
@extend_schema( deprecated = True )
|
||||
class Collect(OrganizationPermissionAPI, views.APIView):
|
||||
|
||||
queryset = Device.objects.all()
|
||||
@ -91,13 +91,12 @@ this setting populated, no device will be created and the endpoint will return H
|
||||
|
||||
if not self.permission_check(request=request, view=self, obj=device):
|
||||
|
||||
raise PermissionDenied()
|
||||
raise Http404
|
||||
|
||||
task = process_inventory.delay(request.body, self.default_organization.id)
|
||||
|
||||
response_data: dict = {"task_id": f"{task.id}"}
|
||||
|
||||
|
||||
except PermissionDenied as e:
|
||||
|
||||
status = Http.Status.FORBIDDEN
|
||||
@ -106,7 +105,7 @@ this setting populated, no device will be created and the endpoint will return H
|
||||
except ValidationError as e:
|
||||
|
||||
status = Http.Status.BAD_REQUEST
|
||||
response_data = e.detail
|
||||
response_data = e.message
|
||||
|
||||
except Exception as e:
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
from django.db.models import Q
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
from drf_spectacular.utils import extend_schema
|
||||
|
||||
from rest_framework import generics, viewsets
|
||||
|
||||
from access.mixin import OrganizationMixin
|
||||
@ -13,7 +11,7 @@ from api.views.mixin import OrganizationPermissionAPI
|
||||
from itam.models.software import Software
|
||||
|
||||
|
||||
@extend_schema(deprecated = True)
|
||||
|
||||
class SoftwareViewSet(OrganizationMixin, viewsets.ModelViewSet):
|
||||
|
||||
permission_classes = [
|
||||
|
@ -1 +0,0 @@
|
||||
from .index import *
|
@ -1,82 +0,0 @@
|
||||
from drf_spectacular.utils import extend_schema, OpenApiResponse
|
||||
|
||||
from api.serializers.itim.change import ChangeTicketSerializer
|
||||
|
||||
from api.views.core.tickets import View
|
||||
|
||||
|
||||
@extend_schema(deprecated=True)
|
||||
class View(View):
|
||||
|
||||
_ticket_type:str = 'change'
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Create a ticket',
|
||||
description = """This model includes all of the ticket types.
|
||||
Due to this not all fields will be available and what fields are available
|
||||
depends upon the comment type. see
|
||||
[administration docs](https://nofusscomputing.com/projects/centurion_erp/administration/core/ticketing/index.html) for more info.
|
||||
""",
|
||||
request = ChangeTicketSerializer,
|
||||
responses = {
|
||||
201: OpenApiResponse(
|
||||
response = ChangeTicketSerializer,
|
||||
),
|
||||
}
|
||||
)
|
||||
def create(self, request, *args, **kwargs):
|
||||
|
||||
return super().create(request, *args, **kwargs)
|
||||
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Fetch all tickets',
|
||||
description = """This model includes all of the ticket comment types.
|
||||
Due to this not all fields will be available and what fields are available
|
||||
depends upon the comment type. see
|
||||
[administration docs](https://nofusscomputing.com/projects/centurion_erp/administration/core/ticketing/index.html) for more info.
|
||||
""",
|
||||
methods=["GET"],
|
||||
responses = {
|
||||
200: OpenApiResponse(
|
||||
description='Success',
|
||||
response = ChangeTicketSerializer
|
||||
)
|
||||
}
|
||||
)
|
||||
def list(self, request, *args, **kwargs):
|
||||
|
||||
return super().list(request, *args, **kwargs)
|
||||
|
||||
|
||||
@extend_schema(
|
||||
summary='Fetch the selected ticket',
|
||||
description = """This model includes all of the ticket comment types.
|
||||
Due to this not all fields will be available and what fields are available
|
||||
depends upon the comment type. see
|
||||
[administration docs](https://nofusscomputing.com/projects/centurion_erp/administration/core/ticketing/index.html) for more info.
|
||||
""",
|
||||
methods=["GET"],
|
||||
responses = {
|
||||
200: OpenApiResponse(
|
||||
description='Success',
|
||||
response = ChangeTicketSerializer
|
||||
)
|
||||
}
|
||||
)
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
|
||||
return super().retrieve(request, *args, **kwargs)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_view_name(self):
|
||||
|
||||
if self.detail:
|
||||
return "Change Ticket"
|
||||
|
||||
return 'Change Tickets'
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user