Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): ticket model #252

Merged
merged 54 commits into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
73ca0fe
chore(core): Add initial ticket model file
jon-nfc Aug 20, 2024
91d85d9
chore(core): Add initial comment model file
jon-nfc Aug 20, 2024
b8c4a54
fix(core): ensure is_global check does not process null value
jon-nfc Aug 24, 2024
cb9c782
chore(base): remove dev apps from debug added urls
jon-nfc Aug 24, 2024
52db44e
feat(development): add option for including additional stylesheets
jon-nfc Aug 25, 2024
c5a5c39
feat(core): add basic ticketing system
jon-nfc Aug 25, 2024
7b3a007
feat(assistance): Add Request ticket to navigation
jon-nfc Aug 25, 2024
31067aa
feat(itim): Add Change ticket to navigation
jon-nfc Aug 25, 2024
6ff3fe5
feat(itim): Add Incident ticket to navigation
jon-nfc Aug 25, 2024
81bd635
feat(itim): Add Problem ticket to navigation
jon-nfc Aug 25, 2024
8edb209
feat(api): Add Tickets endpoint
jon-nfc Aug 26, 2024
3c44561
chore(development): Add makefile
jon-nfc Aug 26, 2024
5d74ddf
fix(access): Don't query for `is_global=None` within `TenancyManager`
jon-nfc Aug 27, 2024
e63bec8
feat(access): add dynamic permissions to Tenancy Permissions
jon-nfc Aug 27, 2024
09afd7f
feat(core): Add permission checking to Tickets form
jon-nfc Aug 27, 2024
2a7857b
refactor(access): cache object_organization on lookup
jon-nfc Aug 28, 2024
e59a08b
refactor(access): cache user_organizations on lookup
jon-nfc Aug 28, 2024
5c4a802
feat(core): Add field level permission and validation checks
jon-nfc Aug 28, 2024
6a52730
feat(core): Add Title bar to ticket form
jon-nfc Aug 30, 2024
96ed198
fix(core): use from ticket title for "blocked by"
jon-nfc Aug 30, 2024
3f1f2fd
feat(core): Add ticket action comments on ticket update
jon-nfc Aug 30, 2024
8b00446
feat(core): Validate ticket status field for all ticket types
jon-nfc Aug 30, 2024
95979c6
feat(core): Validate ticket related and prevent duel related entries
jon-nfc Aug 30, 2024
1665e51
fix(core): return correct redirect path for related ticket form
jon-nfc Aug 30, 2024
6ec16cb
feat(core): colour code related ticket background to ticket type
jon-nfc Aug 30, 2024
5f3b12a
chore(core): clean up ticket css
jon-nfc Aug 31, 2024
0535674
docs(core): document get_dynamic_permissions function
jon-nfc Aug 31, 2024
31bc1e4
fix(access): correct permission check to cater for is_global=None
jon-nfc Aug 31, 2024
638ea46
docs(core): initial docs pages for v1.2
jon-nfc Aug 31, 2024
b709839
refactor(access): Add definable parameters to organization mixin
jon-nfc Aug 31, 2024
28fe89e
feat(core): Ticket comment orgaanization set to ticket organization
jon-nfc Aug 31, 2024
8242d9f
fix(core): Correct ticket comment permissions
jon-nfc Aug 31, 2024
6532d0e
fix(core): dont remove hidden fields on ticket comment form
jon-nfc Aug 31, 2024
7f138d4
feat(core): Ensure for tenancy objects that the organization is set
jon-nfc Aug 31, 2024
097b3fe
feat(core): Add api validation for ticket
jon-nfc Aug 31, 2024
8662feb
feat(api): Ensure device can add/edit organization
jon-nfc Aug 31, 2024
011a6c1
test: Ensure tests add organization to tenancy objects on creation
jon-nfc Aug 31, 2024
fe35390
test(itam): Refactor Device tests organization field to be editable.
jon-nfc Aug 31, 2024
d1b9283
test: Add view must have function `get_initial`
jon-nfc Aug 31, 2024
f0b604b
feat(core): Use common function for markdown rendering for ticket obj…
jon-nfc Aug 31, 2024
9132608
feat(core): render ticket number `#\d+` links within markdown
jon-nfc Aug 31, 2024
6f2d431
fix(core): Ensure that the organization field is available
jon-nfc Sep 1, 2024
1829395
fix(core): Add `ticket_type` field to import_permissions
jon-nfc Sep 1, 2024
b04b6fe
fix(core): Ensure new ticket can be created
jon-nfc Sep 1, 2024
3ba89a9
fix(core): Correct modified field to correct type
jon-nfc Sep 1, 2024
967b925
feat(api): Set default values for ticket comment form to match ticket
jon-nfc Sep 1, 2024
9732656
fix(api): Filter ticket comments to match ticket
jon-nfc Sep 1, 2024
c8d7b52
fix(core): Correct modified field to correct type for ticket comment
jon-nfc Sep 1, 2024
058e057
feat(core): Enable ticket comment created date can be set when an imp…
jon-nfc Sep 1, 2024
7829f4b
feat(core): Add ticket status icon
jon-nfc Sep 1, 2024
ee17095
chore(core): squash ticket migrations
jon-nfc Sep 1, 2024
523341c
docs(core): Add some ticketing docs
jon-nfc Sep 1, 2024
0b86ded
chore(core): Add Ticket Comment validation class
jon-nfc Sep 1, 2024
ba8b618
chore(core): update validate field permission docstring
jon-nfc Sep 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ artifacts/
volumes/
build/
pages/
node_modules/
.markdownlint-cli2.jsonc
.markdownlint.json
package-lock.json
package.json
**.junit.xml
70 changes: 70 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,76 @@
# 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
/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


# Old working docs


## Dev Environment

It's advised to setup a python virtual env for development. this can be done with the following commands.
Expand Down
77 changes: 69 additions & 8 deletions app/access/mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@
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 = []
Expand All @@ -26,20 +39,24 @@
parent_model (Model): with PK from kwargs['pk']
"""

return self.parent_model.objects.get(pk=self.kwargs['pk'])
return self.parent_model.objects.get(pk=self.kwargs[self.parent_model_pk_kwarg])


def object_organization(self) -> int:

id = None

if hasattr(self, '_object_organization'):

return self._object_organization

try:

if hasattr(self, 'get_queryset'):
self.get_queryset()


if hasattr(self, 'parent_model'):
if self.parent_model:
obj = self.get_parent_obj()

id = obj.get_organization().id
Expand All @@ -61,6 +78,10 @@

id = 0

if hasattr(self, 'instance') and id is None: # Form Instance

id = self.instance.get_organization()


except AttributeError:

Expand All @@ -84,6 +105,10 @@

pass

if id is not None:

self._object_organization = id


return id

Expand Down Expand Up @@ -147,6 +172,10 @@

user_organizations = []

if hasattr(self, '_user_organizations'):

return self._user_organizations

teams = Team.objects

for group in self.request.user.groups.all():
Expand All @@ -157,14 +186,32 @@

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) -> bool:
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.
"""

has_permission = False

if permissions_required is None:

permissions_required = self.get_permission_required()

if not organization:

organization = self.object_organization()
Expand All @@ -182,7 +229,7 @@

assembled_permission = str(permission["content_type__app_label"]) + '.' + str(permission["codename"])

if assembled_permission in self.get_permission_required() and (team['organization_id'] == organization or organization == 0):
if assembled_permission in permissions_required and (team['organization_id'] == organization or organization == 0):

return True

Expand Down Expand Up @@ -242,16 +289,24 @@

return True

perms = self.get_permission_required()
if permissions_required:

if self.has_organization_permission():
perms = permissions_required

return True
else:

if self.request.user.has_perms(perms) and len(self.kwargs) == 0 and str(self.request.method).lower() == 'get':
perms = self.get_permission_required()

if self.has_organization_permission(permissions_required = perms):

return True

if self.request.user.has_perms(perms) 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

for required_permission in self.permission_required:

if required_permission.replace(
Expand Down Expand Up @@ -327,6 +382,12 @@

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()
jon-nfc marked this conversation as resolved.
Show resolved Hide resolved

if len(self.permission_required) > 0:

Expand Down Expand Up @@ -357,6 +418,6 @@

if not self.permission_check(request):

raise PermissionDenied('You are not part of this organization')

Check failure on line 421 in app/access/mixin.py

View workflow job for this annotation

GitHub Actions / Python / Unit Test (3.10)

You are not part of this organization

Check failure on line 421 in app/access/mixin.py

View workflow job for this annotation

GitHub Actions / Python / Unit Test (3.10)

You are not part of this organization

Check failure on line 421 in app/access/mixin.py

View workflow job for this annotation

GitHub Actions / Python / Unit Test (3.11)

You are not part of this organization

Check failure on line 421 in app/access/mixin.py

View workflow job for this annotation

GitHub Actions / Python / Unit Test (3.11)

You are not part of this organization

Check failure on line 421 in app/access/mixin.py

View workflow job for this annotation

GitHub Actions / Python / Unit Test (3.12)

You are not part of this organization

Check failure on line 421 in app/access/mixin.py

View workflow job for this annotation

GitHub Actions / Python / Unit Test (3.12)

You are not part of this organization

return super().dispatch(self.request, *args, **kwargs)
29 changes: 23 additions & 6 deletions app/access/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,13 +124,21 @@ def get_queryset(self):
user_organizations += [ team_user.team.organization.id ]


if len(user_organizations) > 0 and not user.is_superuser:
if len(user_organizations) > 0 and not user.is_superuser and self.model.is_global is not None:

return super().get_queryset().filter(
models.Q(organization__in=user_organizations)
|
models.Q(is_global = True)
)
if self.model.is_global:
jon-nfc marked this conversation as resolved.
Show resolved Hide resolved

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()

Expand Down Expand Up @@ -188,6 +196,15 @@ def validatate_organization_exists(self):
def get_organization(self) -> Organization:
return self.organization


def save(self, force_insert=False, force_update=False, using=None, update_fields=None):

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):
Expand Down
10 changes: 10 additions & 0 deletions app/access/tests/unit/tenancy_object/test_tenancy_object.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,13 @@ def test_attribute_not_none_objects(self):
"""

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 change: 0 additions & 1 deletion app/api/serializers/itam/device.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ def get_device_config(self, device):

class Meta:
model = Device
depth = 1
fields = [
'id',
'is_global',
Expand Down
Loading
Loading