Compare commits

...

703 Commits

Author SHA1 Message Date
fa03f7471b build: bump version 1.11.0 -> 1.12.0 2025-03-01 10:43:29 +00:00
Jon
a28a295d19 Merge pull request #652 from nofusscomputing/feature-next-release 2025-03-01 19:56:32 +09:30
Jon
f9abd0101e chore(api): add missing migration for authtoken.meta
ref: #652
2025-03-01 00:43:54 +09:30
Jon
053839af9a feat(api): Add delete column to AuthToken Table
ref: #652 nofusscomputing/centurion_erp_ui#88 closes #647
2025-02-28 23:52:01 +09:30
Jon
5abb4172a9 Merge pull request #651 from nofusscomputing/user-token-endpoint 2025-02-26 20:56:53 +09:30
Jon
d8cfff12ad build: update gitlab-ci to current head
ref: #651
2025-02-26 03:28:55 +09:30
Jon
526e9e876b docs(development): remove old history model pages and links
ref: #651
2025-02-26 03:05:02 +09:30
Jon
88163a954f feat(docker): Upgrade system packages on build
ref: #650
2025-02-26 02:52:51 +09:30
Jon
63184edc88 feat(api): AuthToken requires viewset get_back_url
ref: #650 #649
2025-02-26 01:59:27 +09:30
Jon
e3ec1a4da3 test(api): AuthToken ViewSet checks (unit)
ref: #650 #649
2025-02-26 01:24:52 +09:30
Jon
77316fc682 test(api): AuthToken API Field checks
ref: #650 #649
2025-02-26 01:01:48 +09:30
Jon
38208f3235 test(api): AuthToken Serializer checks
ref: #650 #649
2025-02-26 00:43:34 +09:30
Jon
04ffe056ae test(api): AuthToken ViewSet checks
ref: #650 #649
2025-02-26 00:06:06 +09:30
Jon
1372609e83 fix(api): correct usage of AuthToken.generate to a property
ref: #650 #649
2025-02-25 23:01:48 +09:30
Jon
c62f472546 feat(api): Add auth token api endpoint
ref: #650 #649
2025-02-25 22:33:54 +09:30
Jon
1b85ffca3e feat(settings): Add section title to auth tokens
ref: #650 #649
2025-02-25 22:33:32 +09:30
Jon
57cb5b4105 feat(settings): Add tokens url to user settings _urls
ref: #650 #649
2025-02-25 22:33:10 +09:30
Jon
8597c5e474 feat(api): Update Auth Token model for use with serializer
ref: #650 #649
2025-02-25 22:32:40 +09:30
Jon
eaabbb06c3 feat(api): Add user Auth Token viewset
ref: #650 #649
2025-02-25 22:31:07 +09:30
Jon
5bc7d585ad feat(api): Add user Auth Token serializer
ref: #650 #649
2025-02-25 22:30:32 +09:30
Jon
6c1690fccf feat(settings): Add page_layout attribute to User Settings
ref: #647 #648
2025-02-24 02:11:24 +09:30
f158bed904 build: bump version 1.10.1 -> 1.11.0 2025-02-21 11:36:36 +00:00
Jon
9503f88155 Merge pull request #570 from nofusscomputing/feature-next-release 2025-02-21 20:48:34 +09:30
Jon
79199e27c7 chore(core): Remove old history permissions from available permissions
ref: #605 #601
2025-02-21 02:31:00 +09:30
Jon
3058f7212a chore(core): Squash history commits
to reduce amount of migrations

ref: #605 closes #601
2025-02-21 02:30:48 +09:30
Jon
57d41d12a3 chore(core): Remove old history tests
ref: #605 #601 closes #46
2025-02-20 22:37:54 +09:30
Jon
fe997ee2f8 feat(core): Enable App settings History to save without specifying an organization
APP settings do not form part of an organization

ref: #605 closes #642
2025-02-20 22:37:17 +09:30
Jon
cecad1c368 fix(settings): App settings serializer fielad name does not exist
ref: #605 #642
2025-02-20 22:34:51 +09:30
Jon
df2120ab85 test(settings): History Entry checks for App Settings History
ref: #605 #642
2025-02-20 22:34:26 +09:30
Jon
63400c9ab6 test(settings): API Field Checks for App Settings History
ref: #605 #642
2025-02-20 22:34:02 +09:30
Jon
4ea29dedb9 feat(settings): save_history method added to App Settings
ref: #605 #642
2025-02-20 22:33:06 +09:30
Jon
701608b3ed feat(settings): History Model for App Settings Version added
ref: #605 #642
2025-02-20 22:32:46 +09:30
Jon
4bf54babb2 Merge pull request #605 from nofusscomputing/refactor-model-history 2025-02-20 06:58:06 +09:30
Jon
c1873e8c6d test: Model History not to save history on self
ref: #605 #602
2025-02-20 06:44:30 +09:30
Jon
31eded4f69 docs: remove api pages no longer required
ref: #605 #602
2025-02-20 06:38:12 +09:30
Jon
94737e7ae7 test(core): Correct lookup for model history test setup
ref: #605 #602
2025-02-20 06:28:50 +09:30
Jon
13d697e7de feat(core): Migration for history data to new history tables
ref: #605 closes #602
2025-02-20 06:07:36 +09:30
Jon
37aa9ff968 test(access): remove test cases for Team prarent_object
not required for model

ref: #605 #608
2025-02-20 05:41:00 +09:30
Jon
1663e98a1a fix(access): dont use organization property within organization model
ref: #605 #607
2025-02-20 05:40:01 +09:30
Jon
f765fe1fa0 test(access): History Entry checks for Team model
ref: #605 closes #608
2025-02-19 23:39:42 +09:30
Jon
1561b3a2e6 test(access): API Field Checks for Team History
ref: #605 #608
2025-02-19 23:39:28 +09:30
Jon
4174bacea1 feat(access): save_history method added to Team
ref: #605 #608
2025-02-19 23:39:00 +09:30
Jon
553f4d8579 feat(access): History Model for Team added
ref: #605 #608
2025-02-19 23:38:44 +09:30
Jon
72314ea2a4 test(access): History Entry checks for Organization model
ref: #605 closes #607
2025-02-19 23:38:17 +09:30
Jon
2c2ed724c3 test(access): API Field Checks for Organization History
ref: #605 #607
2025-02-19 23:37:28 +09:30
Jon
0275c7a211 feat(access): save_history method added to Organization
ref: #605 #607
2025-02-19 23:36:36 +09:30
Jon
b2f1fba86a feat(access): History Model for Organization added
ref: #605 #607
2025-02-19 23:36:22 +09:30
Jon
61207c55f9 Merge pull request #641 from nofusscomputing/refactor-access-models 2025-02-19 22:46:52 +09:30
Jon
cabb8d8c4e refactor(core): Update access imports to new path
ref: #641 closes #640
2025-02-19 22:34:55 +09:30
Jon
ef1f764b4b refactor(core): Update access imports to new path
ref: #640 #641
2025-02-19 22:20:41 +09:30
Jon
19ccc5af9b refactor: Update migrations imports to new path
ref: #640 #641
2025-02-19 22:17:49 +09:30
Jon
90dd1a3ded refactor(config_management): Update access imports to new path
ref: #640 #641
2025-02-19 22:02:34 +09:30
Jon
bc8af15d8b refactor(api): Update access imports to neew path
ref: #640 #641
2025-02-19 22:02:27 +09:30
Jon
3884253a2b refactor(settings): Update access imports to new path
ref: #640 #641
2025-02-19 21:55:05 +09:30
Jon
a9698eada1 refactor(project_management): Update access imports to new path
ref: #640 #641
2025-02-19 21:49:46 +09:30
Jon
e91ba06256 refactor(itim): Update access imports to new path
ref: #640 #641
2025-02-19 21:41:49 +09:30
Jon
c506925f6e refactor(itam): Update access imports to new path
ref: #640 #641
2025-02-19 21:30:37 +09:30
Jon
b007e05386 refactor(core): Update access imports to new path
ref: #640 #641
2025-02-19 21:11:01 +09:30
Jon
cadcb8b5d2 refactor(config_management): Update access imports to new path
ref: #640 #641
2025-02-19 20:55:30 +09:30
Jon
cc6d577978 refactor(assistance): Update access imports to new path
ref: #640 #641
2025-02-19 20:50:40 +09:30
Jon
7f2eaaca6a refactor(base): Update access imports to new path
ref: #640 #641
2025-02-19 20:42:44 +09:30
Jon
9b0d09e001 refactor(api): Update access imports to neew path
ref: #640 #641
2025-02-19 20:39:42 +09:30
Jon
77226f75fc refactor(access): Update access imports to neew path
ref: #640 #641
2025-02-19 20:34:28 +09:30
Jon
aeff3f610c refactor(access): Move models to their own file
ref: #640 #641
2025-02-19 20:33:41 +09:30
Jon
635159f364 test: Fix History API checks for kb
ref: #605 #602
2025-02-18 04:13:40 +09:30
Jon
c0e8769781 test: Fix History API checks for project milestone
ref: #605 #602
2025-02-18 04:13:40 +09:30
Jon
04491fbc34 test: Fix History Entry checks for models
ref: #605 #602
2025-02-18 04:13:39 +09:30
Jon
89e6bc362c fix(project_management): Project milestone is not a child model
ref: #605 #631
2025-02-18 04:13:39 +09:30
Jon
79511d0270 test(config_management): History Entry checks for Config_group_hosts model
ref: #605 closes #637
2025-02-18 04:13:39 +09:30
Jon
69bb6f149f test(settings): History Entry checks for External Link model
ref: #605 closes #635
2025-02-18 04:13:39 +09:30
Jon
381ceb723d test(project_management): History Entry checks for Project Type model
ref: #605 closes #634
2025-02-18 04:13:39 +09:30
Jon
67e010e001 test(project_management): History Entry checks for Project State model
ref: #605 closes #632
2025-02-18 04:13:39 +09:30
Jon
1a1317b584 test(project_management): History Entry checks for Project Milestone model
ref: #605 closes #631
2025-02-18 04:13:39 +09:30
Jon
2a3051c2ae test(project_management): History Entry checks for Project model
ref: #605 closes #630
2025-02-18 04:13:39 +09:30
Jon
dcc86a824a test(itim): History Entry checks for Service model
ref: #605 closes #629
2025-02-18 04:13:39 +09:30
Jon
44a6d1da2d test(itim): History Entry checks for Cluster Type model
ref: #605 closes #627
2025-02-18 04:13:39 +09:30
Jon
bd06be083b test(itim): History Entry checks for Port model
ref: #605 closes #628
2025-02-18 04:13:39 +09:30
Jon
2a14cd6119 test(itim): History Entry checks for Cluster model
ref: #605 closes #626
2025-02-18 04:13:39 +09:30
Jon
334dbfdf7f test(itam): History Entry checks for Software Version model
ref: #605 closes #625
2025-02-18 04:13:39 +09:30
Jon
0c961d2709 test(itam): History Entry checks for Software Category model
ref: #605 closes #624
2025-02-18 04:13:39 +09:30
Jon
99900a2e90 test(itam): History Entry checks for Software model
ref: #605 closes #623
2025-02-18 04:13:39 +09:30
Jon
ede4009933 test(itam): History Entry checks for Operating System Version model
ref: #605 closes #622
2025-02-18 04:13:39 +09:30
Jon
1323531254 test(itam): History Entry checks for Operating System model
ref: #605 closes #621
2025-02-18 04:13:39 +09:30
Jon
e9022edb1d test(itam): History Entry checks for Device Type model
ref: #605 closes #620
2025-02-18 04:13:39 +09:30
Jon
749fe57d4f test(itam): History Entry checks for Device OS model
ref: #605 closes #618
2025-02-18 04:13:39 +09:30
Jon
5e9f2a4763 test(itam): History Entry checks for Device Model model
ref: #605 closes #617
2025-02-18 04:13:39 +09:30
Jon
edb5148b7d test(itam): History Entry checks for Device model
ref: #605 closes #616
2025-02-18 04:13:39 +09:30
Jon
3d98f54b3c test(core): History Entry checks for Ticket Comment Category model
ref: #605 closes #615
2025-02-18 04:13:39 +09:30
Jon
126516a1ff test(core): History Entry checks forTicket Category model
ref: #605 closes #614
2025-02-18 04:13:39 +09:30
Jon
7169bb1618 test(config_management): History Entry checks for Config Groups Software model
ref: #605 closes #612
2025-02-18 04:13:39 +09:30
Jon
9cd72e604e test(config_management): History Entry checks for Config Groups model
ref: #605 closes #611
2025-02-18 04:13:39 +09:30
Jon
135fba1f50 test(assistance): History Entry checks for Knowledge base category model
ref: #605 closes #610
2025-02-18 04:13:39 +09:30
Jon
80fa12b747 test(assistance): History Entry checks for Knowledge base model
ref: #605 closes #609
2025-02-18 04:13:39 +09:30
Jon
77786d28e7 test(itam): Device Software History Entry checks
ref: #605 closes #619
2025-02-18 04:13:39 +09:30
Jon
2775e0a01b test(core): Manufacturer History Entry checks
ref: #605 closes #613
2025-02-18 04:13:39 +09:30
Jon
dd312c6351 fix(core): Child models on delete must make model field null
ref: #605 #602
2025-02-18 04:13:39 +09:30
Jon
91c964b581 fix(project_management): Project Milestone History is a primaryu model
ref: #605 #631
2025-02-18 04:13:39 +09:30
Jon
db5ce2352e test(core): Model History Entries Test Suite
ref: #605 #602
2025-02-18 04:13:39 +09:30
Jon
5c3c26c318 fix(core): When a child model is deleted ensure entry is still created on parent model history
ref: #605 #601
2025-02-18 04:13:39 +09:30
Jon
f77d778cb7 test(core): History Model Unit test cases for model and tenancy checks
ref: #605 #602
2025-02-18 04:13:39 +09:30
Jon
9731cb0eaa test(settings): API Field Checks for External Links History
ref: #605 #635
2025-02-18 04:13:38 +09:30
Jon
e31bc90869 test(project_management): API Field Checks for Project Type History
ref: #605 #634
2025-02-18 04:13:38 +09:30
Jon
a5156bd476 test(project_management): API Field Checks for Project State History
ref: #605 #632
2025-02-18 04:13:38 +09:30
Jon
675f46ce66 test(project_management): API Field Checks for Project Milestone History
ref: #605 #631
2025-02-18 04:13:38 +09:30
Jon
ccb85b16ae test(project_management): API Field Checks for Project History
ref: #605 #630
2025-02-18 04:13:38 +09:30
Jon
9d7750ce44 test(itim): API Field Checks for Service History
ref: #605 #629
2025-02-18 04:13:38 +09:30
Jon
d10faa4b3c test(itim): API Field Checks for Port History
ref: #605 #628
2025-02-18 04:13:38 +09:30
Jon
7028cbf02b test(itim): API Field Checks for Cluster Type History
ref: #605 #627
2025-02-18 04:13:38 +09:30
Jon
3a2d66a213 test(itim): API Field Checks for Cluster History
ref: #605 #626
2025-02-18 04:13:38 +09:30
Jon
9f03b52f58 test(core): API Field Checks for Ticket Comment Category History
ref: #605 #615
2025-02-18 04:13:38 +09:30
Jon
38ff4b9ee7 test(core): API Field Checks for Ticket Category History
ref: #605 #614
2025-02-18 04:13:38 +09:30
Jon
a70a2019b4 test(config_management): API Field Checks for Config Group Software History
ref: #605 #612
2025-02-18 04:13:38 +09:30
Jon
edef6aa2bc test(config_management): API Field Checks for Config Group Hosts History
ref: #605 #637
2025-02-18 04:13:38 +09:30
Jon
4d02276f55 test(config_management): API Field Checks for Config Group History
ref: #605 #611
2025-02-18 04:13:38 +09:30
Jon
2a2ce8e2e0 test(assistance): API Field Checks for Knowledge base category History
ref: #605 #610
2025-02-18 04:13:38 +09:30
Jon
5b0f1b9b82 test(assistance): API Field Checks for Knowledge base History
ref: #605 #609
2025-02-18 04:13:38 +09:30
Jon
58cca3e954 test(itam): API Field Checks for Software Version History
ref: #605 #625
2025-02-18 04:13:38 +09:30
Jon
1ddfd852d4 test(itam): API Field Checks for Software Category History
ref: #605 #624
2025-02-18 04:13:38 +09:30
Jon
22eec00f40 test(itam): API Field Checks for Software History
ref: #605 #623
2025-02-18 04:13:38 +09:30
Jon
23fe7e189e test(itam): API Field Checks for Operating System Version History
ref: #605 #622
2025-02-18 04:13:38 +09:30
Jon
f4edbe80d5 test(itam): API Field Checks for Operating System History
ref: #605 #621
2025-02-18 04:13:38 +09:30
Jon
076b35dedc test(itam): API Field Checks for Device Type History
ref: #605 #620
2025-02-18 04:13:38 +09:30
Jon
4432591168 test(itam): API Field Checks for Device OS History
ref: #605 #618
2025-02-18 04:13:38 +09:30
Jon
bb8a3ba716 test(itam): API Field Checks for Device Model History
ref: #605 #617
2025-02-18 04:13:38 +09:30
Jon
af71907264 test(itam): API Field Checks for Device History
ref: #605 #616
2025-02-18 04:13:38 +09:30
Jon
53d2da43ef test(core): Unit Test Suite for History Model API field checks urls can either be str or hyperlink
ref: #605 #602 #46
2025-02-18 04:13:38 +09:30
Jon
c226a0a910 test(itam): API Field Checks for Device Software History
ref: #605 #619
2025-02-18 04:13:38 +09:30
Jon
4d70925a3d test(core): API Field Checks for Manufacturer History
ref: #605 #613
2025-02-18 04:13:38 +09:30
Jon
6c61ff0121 test(core): API Field Checks for Model History
ref: #605 #601 #602 #46
2025-02-18 04:13:38 +09:30
Jon
48966b9009 test(core): Unit Test Suite for History Model API field checks
ref: #605 #602 #46
2025-02-18 04:13:38 +09:30
Jon
9a428c4e98 feat(core): add org field History Model api rendering
ref: #605 #602  #455
2025-02-18 04:13:38 +09:30
Jon
1ef90e96e0 test(core): Functional Test for History Model APIPermission updated to cater for tenancy obj
ref: #605 #602 closes #455
2025-02-18 04:13:37 +09:30
Jon
74f652aaf4 test(core): Functional Test for History Model API Permissions and Metadata
ref: #605 #602
2025-02-18 04:13:37 +09:30
Jon
c5999c2e04 fix(core): when fetching url_kwargs for model history, make it dynamic for related field name
ref: #605 #602
2025-02-18 04:13:37 +09:30
Jon
46e7f3fada fix(core): Xorrect logic for determining view_action
ref: #605 #602
2025-02-18 04:13:37 +09:30
Jon
afc1c6b6fc test(core): Unit Test for History Model Viewset
ref: #605 #602
2025-02-18 04:13:37 +09:30
Jon
1b5044cc42 feat(core): Show the model name within history
ref: #605 #601
2025-02-18 04:13:37 +09:30
Jon
413ddbc59b fix(core): dynamically search for history object name
ref: #605 #601
2025-02-18 04:13:37 +09:30
Jon
7e64e23d32 feat(project_management): Project Milestone added to modelhistory.child_history_models
ref: #605 #631
2025-02-18 04:13:37 +09:30
Jon
3aacc17f54 feat(settings): History Model migrations for External Link
ref: #605 #635
2025-02-18 04:13:37 +09:30
Jon
1485183aab feat(settings): save_history method added to External Link
ref: #605 #635
2025-02-18 04:13:37 +09:30
Jon
14e81e0bdb feat(settings): History Model for External Link added
ref: #605 #635
2025-02-18 04:13:37 +09:30
Jon
a66c441f5a feat(project_management): History Model migrations for Project Type
ref: #605 #634
2025-02-18 04:13:37 +09:30
Jon
1cf844d22d feat(project_management): save_history method added to Project Type
ref: #605 #634
2025-02-18 04:13:37 +09:30
Jon
aaba1c5ad6 feat(project_management): History Model for Project TYpe added
ref: #605 #634
2025-02-18 04:13:37 +09:30
Jon
2b1f8a6e05 feat(project_management): History Model migrations for Project State
ref: #605 #632
2025-02-18 04:13:37 +09:30
Jon
6cd5698f6e feat(project_management): save_history method added to Project State
ref: #605 #632
2025-02-18 04:13:37 +09:30
Jon
eb137c7ae3 feat(project_management): History Model for Project State added
ref: #605 #632
2025-02-18 04:13:37 +09:30
Jon
9ca5cdb88a feat(project_management): History Model migrations for Project Milestone
ref: #605 #631
2025-02-18 04:13:37 +09:30
Jon
0ec202d633 feat(project_management): save_history method added to Project Milestonr
ref: #605 #631
2025-02-18 04:13:37 +09:30
Jon
18ba0704ec feat(project_management): History Model for Project Milestone added
ref: #605 #631
2025-02-18 04:13:37 +09:30
Jon
70d24b1094 feat(project_management): History Model migrations for Project
ref: #605 #630
2025-02-18 04:13:37 +09:30
Jon
a9419fd742 feat(project_management): save_history method added to Project
ref: #605 #630
2025-02-18 04:13:37 +09:30
Jon
e17a0ac5e8 feat(project_management): History Model for Project added
ref: #605 #630
2025-02-18 04:13:37 +09:30
Jon
09bff87030 feat(itim): History Model migrations for Service
ref: #605 #628
2025-02-18 04:13:37 +09:30
Jon
1b1f9840e3 feat(itim): save_history method added to Service
ref: #605 #628
2025-02-18 04:13:37 +09:30
Jon
8802d396c5 feat(itim): History Model for Service added
ref: #605 #629
2025-02-18 04:13:37 +09:30
Jon
87f73ac5b0 feat(itim): History Model migrations for Port
ref: #605 #628
2025-02-18 04:13:37 +09:30
Jon
36de9817b5 feat(itim): save_history method added to Port
ref: #605 #628
2025-02-18 04:13:37 +09:30
Jon
a3a33085c8 feat(itim): History Model for Port added
ref: #605 #628
2025-02-18 04:13:37 +09:30
Jon
1c601f5ce1 feat(itim): History Model migrations for Cluster Type
ref: #605 #627
2025-02-18 04:13:37 +09:30
Jon
6b10795b2d feat(itim): save_history method added to Cluster TYpe
ref: #605 #627
2025-02-18 04:13:36 +09:30
Jon
cdf3068b50 feat(itim): History Model for Cluster Type added
ref: #605 #627
2025-02-18 04:13:36 +09:30
Jon
73d5ac6b10 feat(itim): History Model migrations for Cluster
ref: #605 #625
2025-02-18 04:13:36 +09:30
Jon
18cbd71d28 feat(itim): save_history method added to Cluster
ref: #605 #626
2025-02-18 04:13:36 +09:30
Jon
cf52db1434 feat(itim): History Model for Cluster added
ref: #605 #626
2025-02-18 04:13:36 +09:30
Jon
403b5226cb test(itam): remove test cases for os version model.parent_object as it's not required
ref: #605 #622
2025-02-18 04:13:36 +09:30
Jon
fcdb2fe057 feat(itam): History Model migrations for Software Version
ref: #605 #625
2025-02-18 04:13:36 +09:30
Jon
e04e3e4572 feat(itam): save_history method added to Software Version
ref: #605 #625
2025-02-18 04:13:36 +09:30
Jon
4c14e6db3f feat(itam): History Model for Software Version added
ref: #605 #625
2025-02-18 04:13:36 +09:30
Jon
e254ec6e78 feat(itam): History Model migrations for Software Category
ref: #605 #624
2025-02-18 04:13:36 +09:30
Jon
f815df41c4 feat(itam): save_history method added to Software Category
ref: #605 #624
2025-02-18 04:13:36 +09:30
Jon
13425c432b feat(itam): History Model for Software Category added
ref: #605 #624
2025-02-18 04:13:36 +09:30
Jon
d273c35baa feat(itam): History Model migrations for Software
ref: #605 #623
2025-02-18 04:13:36 +09:30
Jon
c587ae7102 feat(itam): save_history method added to Software
ref: #605 #623
2025-02-18 04:13:36 +09:30
Jon
cafaf3178a feat(itam): History Model for Software added
ref: #605 #623
2025-02-18 04:13:36 +09:30
Jon
947a9c88fd feat(itam): History Model migrations for Operating System Version
ref: #605 #622
2025-02-18 04:13:36 +09:30
Jon
ec06c417e1 feat(itam): save_history method added to Operating System Version
ref: #605 #622
2025-02-18 04:13:36 +09:30
Jon
f954b2507c feat(itam): History Model for Operating System Version added
ref: #605 #622
2025-02-18 04:13:36 +09:30
Jon
1173029981 feat(itam): History Model migrations for Device Type
ref: #605 #620
2025-02-18 04:13:36 +09:30
Jon
fa0a81e835 feat(itam): save_history method added to Device Type
ref: #605 #620
2025-02-18 04:13:36 +09:30
Jon
e8e3939de7 feat(itam): History Model for Device Type added
ref: #605 #620
2025-02-18 04:13:36 +09:30
Jon
2d7e621251 feat(itam): History Model migrations for Device Operating System
ref: #605 #618
2025-02-18 04:13:36 +09:30
Jon
9132a8f62c feat(itam): save_history method added to Device Operating System
ref: #605 #618
2025-02-18 04:13:36 +09:30
Jon
cda3730943 feat(itam): History Model for Device Operating System added
ref: #605 #618
2025-02-18 04:13:36 +09:30
Jon
70db017ac2 feat(itam): History Model migrations for Operating System
ref: #605 #621
2025-02-18 04:13:36 +09:30
Jon
8055bd79dd feat(itam): save_history method added to Operating System
ref: #605 #621
2025-02-18 04:13:35 +09:30
Jon
a73f5feccc feat(itam): History Model migrations for Operating System
ref: #605 #621
2025-02-18 04:13:35 +09:30
Jon
08a3042866 feat(itam): History Model migrations for Device Software
ref: #605 #619
2025-02-18 04:13:35 +09:30
Jon
86116f4563 feat(itam): History Model for Device Software added
ref: #605 #619
2025-02-18 04:13:35 +09:30
Jon
92fd22ae5c feat(itam): save_history method added to Device
ref: #605 #616
2025-02-18 04:13:35 +09:30
Jon
6ccb63c921 feat(itam): History Model migrations for Device Model
ref: #605 #617
2025-02-18 04:13:35 +09:30
Jon
4e5d2378d8 feat(itam): save_history method added to Device Model
ref: #605 #617
2025-02-18 04:13:35 +09:30
Jon
290a7de736 feat(itam): History Model for Device Model added
ref: #605 #617
2025-02-18 04:13:35 +09:30
Jon
8673862cd3 fix(config_management): Remove parent property from config groups
ref: #605 #611
2025-02-18 04:13:35 +09:30
Jon
1de5c634d0 feat(core): History Model migrations for Ticket Comment Category
ref: #605 #615
2025-02-18 04:13:35 +09:30
Jon
f5d59c703d feat(core): save_history method added to Ticket Comment Category
ref: #605 #615
2025-02-18 04:13:35 +09:30
Jon
0ee8369e24 feat(core): History Model for Ticket Comment Category added
ref: #605 #615
2025-02-18 04:13:35 +09:30
Jon
a67c3fb919 feat(config_management): Child History Models added to child model lists for config group hosts and software
ref: #605 #611 #637
2025-02-18 04:13:35 +09:30
Jon
4dd19ba22f fix(tests): Correct Permission Import due to removing from access.models
ref: #605 #601
2025-02-18 04:13:35 +09:30
Jon
d55ecabb2d feat(core): History Model migrations for Ticket Category
ref: #605 #614
2025-02-18 04:13:35 +09:30
Jon
a0ad877830 feat(core): save_history method added to Ticket Category
ref: #605 #614
2025-02-18 04:13:35 +09:30
Jon
9f192ef28d feat(core): History Model for Ticket Category added
ref: #605 #614
2025-02-18 04:13:35 +09:30
Jon
edf0fca794 feat(core): History Model migrations for Manufacturer
ref: #605 #613
2025-02-18 04:13:35 +09:30
Jon
3f4c8e93fc feat(core): save_history method added to Manufacturer
ref: #605 #613
2025-02-18 04:13:34 +09:30
Jon
1c7b63c4d0 feat(core): History Model for Manufacturer added
ref: #605 #613
2025-02-18 04:13:34 +09:30
Jon
aa43c2e46a feat(config_management): save_history method added to Config Group Software
ref: #605 #637
2025-02-18 04:13:34 +09:30
Jon
4d9e06c55b feat(config_management): save_history method added to Config Group Hosts
ref: #605 #637
2025-02-18 04:13:34 +09:30
Jon
2027c4fd0e feat(config_management): save_history method added to Config Groups
ref: #605 #611
2025-02-18 04:13:34 +09:30
Jon
d708f85883 feat(assistance): save_history method added to Knowledge base
ref: #605 #610
2025-02-18 04:13:34 +09:30
Jon
1d6ec4023a feat(assistance): save_history method added to Knowledge base category
ref: #605 #610
2025-02-18 04:13:34 +09:30
Jon
2059d014d7 feat(config_management): History Model migrations for Config Groupse + children
ref: #605 #611 #612 #637
2025-02-18 04:13:34 +09:30
Jon
9493543b92 feat(config_management): History Model for Config Group Software added
ref: #605 #612
2025-02-18 04:13:34 +09:30
Jon
589c7fab26 feat(config_management): History Model for Config Group Hosts added
ref: #605 #637
2025-02-18 04:13:34 +09:30
Jon
740d33ece1 feat(config_management): History Model for Config Groups added
ref: #605 #611
2025-02-18 04:13:34 +09:30
Jon
0a364f0f7f feat(assistance): History Model migrations for Knowledge base + children
ref: #605 #609 #610
2025-02-18 04:13:34 +09:30
Jon
f8a9e7dcad feat(assistance): History Model for Knowledge base category added
ref: #605 #610
2025-02-18 04:13:34 +09:30
Jon
b77bb3d10c feat(assistance): History Model for Knowledge base added
ref: #605 #609
2025-02-18 04:13:34 +09:30
Jon
d21fc512be test(core): disable hisotry viewset function test
ref: #602 #605
2025-02-18 04:13:34 +09:30
Jon
6852aa4748 feat(itam): Add device history model
ref: #605 #616
2025-02-18 04:13:34 +09:30
Jon
723bc7aed4 test(core): correct kwargs for history tests
ref: #602 #605
2025-02-18 04:13:34 +09:30
Jon
358c1720d5 fix(project_management): project Model serializer must inherit common serializer
ref: #601 #605
2025-02-18 04:13:34 +09:30
Jon
9b8f644261 chore(itam): device operating system+software get_url attribute "obj" renamed to match others "item"
ref: #601 #605
2025-02-18 04:13:34 +09:30
Jon
57060975b0 chore: remove old history model access from original UI and API V1
ref: #601 #605 closes #606
2025-02-18 04:13:34 +09:30
Jon
24e4c95928 fix(core): History audit objects must be a valid dict
ref: #601 #605 fixes #490
2025-02-18 04:13:34 +09:30
Jon
8f15b8bf20 chore(itam): device operating system get_url attribute "obj" renamed to match others "item"
ref: #601 #605
2025-02-18 04:13:34 +09:30
Jon
0d6bdad29b fix(api): history app names can contain an underscore
ref: #601 #605
2025-02-18 04:13:34 +09:30
Jon
4b2be4855b docs(development): History model update
ref: #601 #605
2025-02-18 04:13:34 +09:30
Jon
b738c4be20 feat(core): History view to only display objects from the model being requested
ref: #602 #605
2025-02-18 04:13:34 +09:30
Jon
8512bff4f1 fix(core): when saving history, use audit_model for content type
ref: #602 #605
2025-02-18 04:13:34 +09:30
Jon
507db1051c test(core): Remove old history model viewset tests
ref: #602 #605
2025-02-18 04:13:33 +09:30
Jon
7cd4cb965b refactor(core): move get_url to common serializer
reduces duplicated code

ref: #602 #605
2025-02-18 04:13:33 +09:30
Jon
460bf40175 refactor(api): Update history url kwargs to use vals from model._meta
ref: #602 #605
2025-02-18 04:13:33 +09:30
Jon
93e7a9097f docs(core): correct history_save code docs
ref: #602 #605
2025-02-18 04:13:33 +09:30
Jon
1541a89937 fix(core): add missing functions for fetching item url
ref: #602 #605
2025-02-18 04:13:33 +09:30
Jon
25373fc5b6 test: Disable Old History Model test suites
ref: #601 #602 #605
2025-02-18 04:13:33 +09:30
Jon
a9a5c189d2 feat(core): Add new history model to History Serializer
ref: #601 #602 #605
2025-02-18 04:13:33 +09:30
Jon
f2a995d277 feat(core): Add new history model
ref: #602 #605
2025-02-18 04:13:33 +09:30
Jon
75b5f48f9e docs(development): Add model history
ref: #601 #605
2025-02-18 04:13:33 +09:30
Jon
bb021a00d9 feat(development): lint for un-used imports
ref: #570
2025-02-18 04:12:59 +09:30
Jon
db07262623 feat(development): add pylit settings
ref: #570
2025-02-18 04:12:59 +09:30
Jon
6f94c95221 feat(core): added new history model
ref: #570 #602
2025-02-18 04:12:59 +09:30
Jon
103f25be1d fix(project_management): Opened by field set to read only for project task ticket
ref: #570
2025-02-18 04:12:59 +09:30
Jon
cc02976c3a fix(itim): Opened by field set to read only for problem ticket
ref: #570
2025-02-18 04:12:59 +09:30
Jon
af4a1d52c5 fix(itim): Opened by field set to read only for incident ticket
ref: #570
2025-02-18 04:12:59 +09:30
Jon
7ac56a35ae fix(itim): Opened by field set to read only for change ticket
ref: #570
2025-02-18 04:12:59 +09:30
Jon
030560d697 fix(assistance): Opened by field set to read only for request ticket
ref: #570
2025-02-18 04:12:59 +09:30
Jon
b7dd2dfaba refactor(core): superuser changed from import to triage access
ref: #570
2025-02-18 04:12:59 +09:30
Jon
008a879e6f docs(user): Use a table to show markdown model tags
ref: #570
2025-02-18 04:12:59 +09:30
Jon
71382a4689 docs: remove links to old notes
ref: #570
2025-02-18 04:12:59 +09:30
Jon
1963bf0817 feat(api): Device Software Viewset requires its own function to obtain the model view serializer
ref: #578 #477
2025-02-18 04:12:59 +09:30
Jon
31a45a1ab1 feat(api): Ticket Comment Viewset requires its own function to obtain the model view serializer
ref: #578 #477
2025-02-18 04:12:59 +09:30
Jon
f7818fe2e8 feat(api): Ticket Viewset requires its own function to obtain the model view serializer
ref: #578 #477
2025-02-18 04:12:59 +09:30
Jon
16388f0a10 feat(api): Always use a models View serializer for the response
ref: #477 #579 closes #578
2025-02-18 04:12:59 +09:30
Jon
2e0b835af9 test(core): Ensure that when parent_ticket changes on a ticket an action comment is created
ref: #495 #558 #577
2025-02-18 04:12:59 +09:30
Jon
81e856967b fix(core): Ensure that if the parent ticket changes, that the logic caters for none
ref: #558 #577
2025-02-18 04:12:59 +09:30
Jon
348b2f270e test(core): Confirm on category change to ticket that an action comment is created
ref: #577 closes #537
2025-02-18 04:12:59 +09:30
Jon
9a4521a604 fix(assistance): Category can be empty for Project Task Ticket
ref: #537 #577
2025-02-18 04:12:59 +09:30
Jon
83dab570a8 fix(assistance): Category can be empty for Problem Ticket
ref: #537 #577
2025-02-18 04:12:59 +09:30
Jon
a189a60e1b fix(assistance): Category can be empty for Incident Ticket
ref: #537 #577
2025-02-18 04:12:59 +09:30
Jon
91bea05496 fix(assistance): Category can be empty for Change Ticket
ref: #537 #577
2025-02-18 04:12:59 +09:30
Jon
ed41e0b8a9 fix(assistance): Category can be empty for Request Ticket
ref: #537 #577
2025-02-18 04:12:59 +09:30
Jon
64ca3415d4 fix(core): Ticket Action comment for category change must use category field
ref: #537 #577
2025-02-18 04:12:59 +09:30
Jon
ec6bc7d03a feat(core): Add logic to ensure when organization changes, an action comment is created
ref: #577 closes #558
2025-02-18 04:12:59 +09:30
Jon
0d1a0bff19 feat(core): Add logic to ensure when parnet ticket changes, an action comment is created
ref: #558 #577
2025-02-18 04:12:58 +09:30
Jon
cc46a1426d refactor(core): Ticket action comment logic only requires a single check
ref: #577 #558
2025-02-18 04:12:58 +09:30
Jon
30b24a1969 docs(development): remove old notes docs
ref: #570
2025-02-18 04:12:58 +09:30
Jon
ad27501334 chore: update new model tempplate
ref: #605
2025-02-18 04:12:30 +09:30
Jon
1109beb8e9 chore: issue template new model added
ref: #0
2025-02-16 08:43:04 +09:30
8b7550f573 build: bump version 1.10.0 -> 1.10.1 2025-02-14 12:24:21 +00:00
Jon
4f2a28bb52 fix(python): Dont use system TimeZone data, use python zoneinfo module zone data
ref: fixes #603
2025-02-14 21:41:21 +09:30
33c589a1a0 build: bump version 1.9.0 -> 1.10.0 2025-02-10 09:27:27 +00:00
Jon
0a21c37645 fix(core): Dont attempt to access parent_ticket field during ticket validation if it does not exist
ref: #559 #507
2025-02-10 18:42:31 +09:30
Jon
e4dac45d2e Merge pull request #522 from nofusscomputing/feature-next-release 2025-02-10 18:11:16 +09:30
Jon
c9eefc2eb3 Merge pull request #554 from nofusscomputing/ticket-circular-dependency-check 2025-02-10 17:45:07 +09:30
Jon
68d3c1ff26 test(settings): Test User Settings API render to ensure browser_model exists and is the correct type
ref: #554 closes #507
2025-02-10 17:32:59 +09:30
Jon
e5680cdbce test(settings): Test User Settings model to ensure browser_mode field exists
ref: #554 #507
2025-02-10 17:31:42 +09:30
Jon
067dd98eee feat(settings): Provide user with the ability to set browser mode
ref: #554 #481 #507 nofusscomputing/centurion_erp_ui#71
2025-02-10 17:24:52 +09:30
Jon
b153f648f9 feat(core): Parent Ticket validation added to ticket serializer
ref: #554 closes #517
2025-02-10 16:39:00 +09:30
Jon
062e846875 feat(core): Add to ticket endpoint the ability to filter using parent_ticket
ref: #517 #554
2025-02-10 16:19:47 +09:30
Jon
0e1f778b80 feat(core): Add to ticket model a function for circular dependecy check of parent ticket
ref: #517 #554
2025-02-10 16:19:18 +09:30
Jon
aca32b14be Merge pull request #525 from nofusscomputing/model_notes 2025-02-09 22:02:08 +09:30
Jon
16076f0ca3 chore(core): Remove old notes model test suites
ref: #525 #389
2025-02-09 21:48:23 +09:30
Jon
6d69b72368 feat(core): Migrate Notes data to new table
ref: #525 #389 #349 closes #547
2025-02-09 21:47:56 +09:30
Jon
d4c6c08710 chore(core): REmove modelnotes permission from permissions list
ref: #525 #389
2025-02-09 20:55:38 +09:30
Jon
05d4c4a94d refactor: Squash migrations so there is less of them for model notes
ref: #525 #389
2025-02-09 20:40:25 +09:30
Jon
4d5f029913 docs(core): add model notes
ref: #525
2025-02-09 20:04:58 +09:30
Jon
51e7f875a2 test(access): Team Note Model Check object requires org
ref: #525 #527
2025-02-09 20:01:14 +09:30
Jon
b17bc5af75 feat(project_management): Add notes tab to Project Milestone details page
ref: #525 #542
2025-02-09 19:45:11 +09:30
Jon
80db9022b7 feat(itam): Add notes tab to Software Version details page
ref: #525 #541
2025-02-09 19:44:42 +09:30
Jon
288abb5373 feat(itam): Add notes tab to Operating System details page
ref: #525 #531
2025-02-09 19:44:17 +09:30
Jon
f06c9f6091 docs: add model test suite
ref: #525
2025-02-09 19:36:21 +09:30
Jon
d55010d63f test(settings): External Links Note Model Checks
ref: #525 closes #545
2025-02-09 19:35:06 +09:30
Jon
eeb7075f83 test(project_management): Project Type Note Model Checks
ref: #525 closes #544
2025-02-09 19:34:42 +09:30
Jon
1c34fbad08 test(project_management): Project State Note Model Checks
ref: #525 closes #543
2025-02-09 19:34:29 +09:30
Jon
4549379e62 test(project_management): Project Note Model Checks
ref: #525 closes #335
2025-02-09 19:34:12 +09:30
Jon
64fd692d9c test(project_management): Project Milestone Note Model Checks
ref: #525 closes #542
2025-02-09 19:34:00 +09:30
Jon
795f029c75 test(itim): Service Note Model Checks
ref: #525 closes #534
2025-02-09 19:33:33 +09:30
Jon
a091be7b5e test(itim): Port Note Model Checks
ref: #525 closes #536
2025-02-09 19:33:12 +09:30
Jon
a1c9533555 test(itim): Cluster Type Note Model Checks
ref: #525 closes #535
2025-02-09 19:32:47 +09:30
Jon
20f52d856b test(itim): Cluster Note Model Checks
ref: #525 closes #533
2025-02-09 19:32:27 +09:30
Jon
6e296a3713 test(itam): Software Version Note Model Checks
ref: #525 closes #541
2025-02-09 19:31:57 +09:30
Jon
58b73b9b29 test(itam): Software Note Model Checks
ref: #525 closes #532
2025-02-09 19:31:43 +09:30
Jon
cd65d8e93d test(itam): Software Category Note Model Checks
ref: #525 closes #540
2025-02-09 19:31:17 +09:30
Jon
f26b0706ae test(itam): Operating System Version Note Model Checks
ref: #525 closes #539
2025-02-09 19:30:31 +09:30
Jon
1a0839a2c8 test(itam): Operating System Note Model Checks
ref: #525 closes #531
2025-02-09 19:30:14 +09:30
Jon
cc0f92e132 test(itam): Device Type Note Model Checks
ref: #525 closes #538
2025-02-09 19:29:40 +09:30
Jon
b14262fcad test(itam): Device Note Model Checks
ref: #525 closes #524
2025-02-09 19:28:59 +09:30
Jon
1474f47087 test(itam): Device Model Note Model Checks
ref: #525 closes #523
2025-02-09 19:28:46 +09:30
Jon
e56007d4b1 test(core): Manufacturer Note Model Checks
ref: #525 closes #530
2025-02-09 19:28:21 +09:30
Jon
0602070adb test(config_management): Config Group Note Model Checks
ref: #525 closes #529
2025-02-09 19:28:02 +09:30
Jon
760463705b test(assistance): KB Note Model Checks
ref: #525 closes #528
2025-02-09 19:27:37 +09:30
Jon
8eeb5780c6 test(assistance): KB Category Note Model Checks
ref: #525 closes #546
2025-02-09 19:27:25 +09:30
Jon
7b6d6935d4 test(access): Team Note Model Checks
ref: #525 closes #527
2025-02-09 19:27:05 +09:30
Jon
1cec44f7bd test(access): Organization Note Model Checks
ref: #525 closes #526
2025-02-09 19:26:38 +09:30
Jon
51ec6cb0e5 test(core): Model Notes Test Suite
ref: #389 #525
2025-02-09 19:25:36 +09:30
Jon
9b62ba6367 test(settings): Serializer Checks for External Links Notes
ref: #525 #545
2025-02-09 17:58:27 +09:30
Jon
96420f3d23 test(Project_management): Serializer Checks for Project Type Notes
ref: #525 #544
2025-02-09 17:57:59 +09:30
Jon
eb38e70c17 test(Project_management): Serializer Checks for Project State Notes
ref: #525 #543
2025-02-09 17:57:41 +09:30
Jon
dbafbe1a5c test(Project_management): Serializer Checks for Project Notes
ref: #525 #335
2025-02-09 17:57:22 +09:30
Jon
90bee291bd test(Project_management): Serializer Checks for Project Milestone Notes
ref: #525 #542
2025-02-09 17:57:06 +09:30
Jon
5eb120886d test(itim): Serializer Checks for Service Notes
ref: #525 #534
2025-02-09 17:56:31 +09:30
Jon
6e4e63a52e test(itim): Serializer Checks for Port Notes
ref: #525 #536
2025-02-09 17:56:09 +09:30
Jon
8d39fdb9e4 test(itim): Serializer Checks for Cluster Type Notes
ref: #525 #535
2025-02-09 17:55:52 +09:30
Jon
7aa7627faf test(itim): Serializer Checks for Cluster Notes
ref: #525 #533
2025-02-09 17:55:33 +09:30
Jon
29fc7e7e07 test(itam): Serializer Checks for Software Version Notes
ref: #525 #541
2025-02-09 17:55:02 +09:30
Jon
780ab35b26 test(itam): Serializer Checks for Software Notes
ref: #525 #532
2025-02-09 17:54:41 +09:30
Jon
b072ce9fbc test(itam): Serializer Checks for Software Category Notes
ref: #525 #540
2025-02-09 17:54:24 +09:30
Jon
fa757f2edf test(itam): Serializer Checks for Operating System Version Notes
ref: #525 #539
2025-02-09 17:53:43 +09:30
Jon
fccaacbf7c test(itam): Serializer Checks for Operating System Notes
ref: #525 #531
2025-02-09 17:53:27 +09:30
Jon
0f1ff924ed test(itam): Serializer Checks for Device Type Notes
ref: #525 #538
2025-02-09 17:53:02 +09:30
Jon
75598dff94 test(itam): Serializer Checks for Device Notes
ref: #525 #524
2025-02-09 17:52:47 +09:30
Jon
475205f95b test(itam): Serializer Checks for Device Model Notes
ref: #525 #523
2025-02-09 17:52:33 +09:30
Jon
bf74a4c4db test(core): Serializer Checks for Manufacturer Notes
ref: #525 #530
2025-02-09 17:52:07 +09:30
Jon
c15966c694 test(config_management): Serializer Checks for Config Groups Notes
ref: #525 #529
2025-02-09 17:51:18 +09:30
Jon
56ced08c4d test(assistance): Serializer Checks for KB Notes
ref: #525 #528
2025-02-09 17:50:53 +09:30
Jon
c8d6135b83 test(assistance): Serializer Checks for KB Category Notes
ref: #525 #546
2025-02-09 17:50:39 +09:30
Jon
98159c1840 test(access): Serializer Checks for Team Notes
ref: #525 #527
2025-02-09 17:50:03 +09:30
Jon
8140c1f945 test(access): Serializer Checks for Organization Notes
ref: #525 #526
2025-02-09 17:49:51 +09:30
Jon
eff1255419 feat(core): Ensure when editing a model note, the modified user is updated.
ref: #389
2025-02-09 16:47:13 +09:30
Jon
8f2bf3d71e test(core): Test Suite for Model Notes checks
ref: #49 #389 #525
2025-02-09 16:47:02 +09:30
Jon
1a7bade7f3 docs(development): Model Notes Creation
ref: #49 #389 #525
2025-02-09 00:26:55 +09:30
Jon
a7912531e3 test(api): API Fileds user to be super user for tests to run
ref: #525
2025-02-08 23:10:06 +09:30
Jon
32c6991730 test(settings): External Links Notes Function Viewset Tests
ref: #525 #545
2025-02-08 23:08:27 +09:30
Jon
98c02c4101 test(project_management): Project Type Notes Function Viewset Tests
ref: #525 #544
2025-02-08 23:08:01 +09:30
Jon
d8b12aeb7e test(project_management): Project State Notes Function Viewset Tests
ref: #525 #543
2025-02-08 23:07:44 +09:30
Jon
5b02f2c751 test(project_management): Project Notes Function Viewset Tests
ref: #525 #335
2025-02-08 23:07:27 +09:30
Jon
0d4ad05ac9 test(project_management): Project Milestone Notes Function Viewset Tests
ref: #525 #542
2025-02-08 23:07:13 +09:30
Jon
def43c1134 test(itim): Service Notes Function Viewset Tests
ref: #525 #534
2025-02-08 23:06:38 +09:30
Jon
3c08580c10 test(itim): Port Notes Function Viewset Tests
ref: #525 #536
2025-02-08 23:06:19 +09:30
Jon
bc329d2453 test(itim): Cluster Types Notes Function Viewset Tests
ref: #525 #535
2025-02-08 23:05:48 +09:30
Jon
72dfac9d22 test(itim): Cluster Notes Function Viewset Tests
ref: #525 #533
2025-02-08 23:05:27 +09:30
Jon
a74550e1f3 test(itam): Software Version Notes Function Viewset Tests
ref: #525 #541
2025-02-08 22:32:05 +09:30
Jon
382b1d2a30 test(itam): Software Notes Function Viewset Tests
ref: #525 #532
2025-02-08 22:31:48 +09:30
Jon
9c2ef50c3b test(itam): Software Category Notes Function Viewset Tests
ref: #525 #540
2025-02-08 22:31:34 +09:30
Jon
3536a59e5a test(itam): Operating System Version Notes Function Viewset Tests
ref: #525 #539
2025-02-08 22:31:07 +09:30
Jon
0213d45f8a test(itam): Operating System Notes Function Viewset Tests
ref: #525 #531
2025-02-08 22:30:47 +09:30
Jon
41cf9aae41 test(itam): Device Type Notes Function Viewset Tests
ref: #525 #538
2025-02-08 22:30:16 +09:30
Jon
0b25a85e26 test(itam): Device Notes Function Viewset Tests
ref: #525 #524
2025-02-08 22:30:01 +09:30
Jon
4caf26686c test(itam): Device Model Notes Function Viewset Tests
ref: #525 #523
2025-02-08 22:29:49 +09:30
Jon
58c19388c7 test(core): Manufacturer Notes Function Viewset Tests
ref: #525 #530
2025-02-08 22:29:27 +09:30
Jon
6a211677a8 test(config_management): Config Groups Notes Function Viewset Tests
ref: #525 #529
2025-02-08 22:29:07 +09:30
Jon
45d05a183d test(assistance): Knowledge Base Notes Function Viewset Tests
ref: #525 #528
2025-02-08 21:47:06 +09:30
Jon
f144129b4d test(assistance): Knowledge Base Category Notes Function Viewset Tests
ref: #525 #546
2025-02-08 21:46:50 +09:30
Jon
e038d08544 test(access): Team Notes Function Viewset Tests
ref: #525 #527
2025-02-08 21:46:24 +09:30
Jon
05c9e3f066 test(access): Organization Notes Function Viewset Tests
ref: #525 #526
2025-02-08 21:46:10 +09:30
Jon
a1851918e3 fix(core): Permissions require the parent model for model notes
ref: #389 #525
2025-02-08 21:45:09 +09:30
Jon
136f907cd5 docs(access): Add type to parent_model attribute within Organization Mixin
ref: #389 #525
2025-02-08 21:44:08 +09:30
Jon
969aaea884 test(core): Model Notes Test Cases
ref: #389 #525 closes #49
2025-02-08 20:45:41 +09:30
Jon
5a07892b25 test: Remove old notes model tests
ref: #49 #389 #525
2025-02-08 19:52:24 +09:30
Jon
5d17e1a49c test(settings): External Notes Test Cases for ViewSet
ref: #525 #545
2025-02-08 19:31:52 +09:30
Jon
185fb6c332 test(project_management): Project Type Notes Test Cases for ViewSet
ref: #525 #544
2025-02-08 19:30:06 +09:30
Jon
e690012da8 test(project_management): Project State Notes Test Cases for ViewSet
ref: #525 #543
2025-02-08 19:29:29 +09:30
Jon
76b6a57583 test(project_management): Project Notes Test Cases for ViewSet
ref: #525 #335
2025-02-08 19:29:11 +09:30
Jon
7bea5a6156 test(project_management): Project Milestone Notes Test Cases for ViewSet
ref: #525 #542
2025-02-08 19:28:59 +09:30
Jon
45b2f083de test(itim): Service Notes Test Cases for ViewSet
ref: #525 #534
2025-02-08 19:27:52 +09:30
Jon
643894cdfc test(itim): Port Notes Test Cases for ViewSet
ref: #525 #536
2025-02-08 19:27:36 +09:30
Jon
443cbce73b test(itim): Cluster Type Notes Test Cases for ViewSet
ref: #525 #535
2025-02-08 19:27:16 +09:30
Jon
9a5f4e90ff test(itim): Cluster Notes Test Cases for ViewSet
ref: #525 #533
2025-02-08 19:26:55 +09:30
Jon
9c7e572e00 test(itam): Software Version Notes Test Cases for ViewSet
ref: #525 #541
2025-02-08 19:26:34 +09:30
Jon
876a1622dd test(itam): Software Notes Test Cases for ViewSet
ref: #525 #532
2025-02-08 19:25:55 +09:30
Jon
161751f97f test(itam): Software Category Notes Test Cases for ViewSet
ref: #525 #540
2025-02-08 19:25:43 +09:30
Jon
ac0e756c77 test(itam): Operating System Version Notes Test Cases for ViewSet
ref: #525 #539
2025-02-08 19:25:14 +09:30
Jon
115159b8a6 test(itam): Operating_system Notes Test Cases for ViewSet
ref: #525 #531
2025-02-08 19:24:50 +09:30
Jon
10e2550621 test(itam): Device Type Notes Test Cases for ViewSet
ref: #525 #538
2025-02-08 19:24:29 +09:30
Jon
edb6aa6eb7 test(itam): Device Notes Test Cases for ViewSet
ref: #525 #524
2025-02-08 19:23:58 +09:30
Jon
2534557105 test(itam): Device Model Notes Test Cases for ViewSet
ref: #525 #523
2025-02-08 19:23:46 +09:30
Jon
2e6e73353e test(core): Manufacturer Notes Test Cases for ViewSet
ref: #525 #530
2025-02-08 19:23:16 +09:30
Jon
37d6ba202d test(config_management): Config Groups Notes Test Cases for ViewSet
ref: #525 #529
2025-02-08 19:22:55 +09:30
Jon
f3a4123fb6 test(assistance): Knowledge Base Notes Test Cases for ViewSet
ref: #525 #528
2025-02-08 19:22:23 +09:30
Jon
b20abd82e1 test(assistance): Knowledge Base Category Notes Test Cases for ViewSet
ref: #525 #546
2025-02-08 19:22:07 +09:30
Jon
d11e7fa71d test(access): Team Notes Test Cases for ViewSet
ref: #525 #527
2025-02-08 19:21:28 +09:30
Jon
96a8a6a158 test(access): Organization Notes Test Cases for ViewSet
ref: #525 #526
2025-02-08 19:21:15 +09:30
Jon
c0695c8c84 test(project_management): Correct kwargs for Project Milestone Notes Test Cases for API Field Checks
ref: #525 #542
2025-02-08 17:58:44 +09:30
Jon
64c2f1b0d9 test(assistance): Knowledge Base Category Notes Test Cases for API Field Checks
ref: #525 #546
2025-02-08 17:58:33 +09:30
Jon
37a8368a4e feat(assistance): Knowledge Base Category Notes viewset
ref: #525 #546
2025-02-08 17:43:51 +09:30
Jon
ba9c811e16 feat(assistance): Knowledge Base Category Notes Serializer
ref: #525 #546
2025-02-08 17:43:25 +09:30
Jon
ae47b06a72 feat(assistance): Knowledge Base Category Notes Model
ref: #525 #546
2025-02-08 17:43:09 +09:30
Jon
bc8815d607 test(Settings): External Link Notes Test Cases for API Field Checks
ref: #525 #545
2025-02-08 17:33:57 +09:30
Jon
8a06a0dd3a test(project_management): Project Type Notes Test Cases for API Field Checks
ref: #525 #544
2025-02-08 17:33:33 +09:30
Jon
46381fc138 test(project_management): Project State Notes Test Cases for API Field Checks
ref: #525 #543
2025-02-08 17:33:18 +09:30
Jon
be2e0ae837 test(project_management): Project Notes Test Cases for API Field Checks
ref: #335 #525
2025-02-08 17:32:54 +09:30
Jon
cc34e43262 test(project_management): Project Milestone Notes Test Cases for API Field Checks
ref: #525 #542
2025-02-08 17:32:27 +09:30
Jon
a3d0b10a75 test(itim): Service Notes Test Cases for API Field Checks
ref: #525 #534
2025-02-08 17:31:59 +09:30
Jon
eafc364ba5 test(itim): Port Notes Test Cases for API Field Checks
ref: #525 #536
2025-02-08 17:31:44 +09:30
Jon
0304010d0e test(itim): Cluster Types Notes Test Cases for API Field Checks
ref: #525 #535
2025-02-08 17:31:26 +09:30
Jon
5d3eadfd56 test(itim): Cluster Notes Test Cases for API Field Checks
ref: #525 #533
2025-02-08 17:31:09 +09:30
Jon
fe7e9169be test(itam): Software Version Notes Test Cases for API Field Checks
ref: #525 #541
2025-02-08 17:30:43 +09:30
Jon
9d109d2f9a test(itam): Software Notes Test Cases for API Field Checks
ref: #525 #532
2025-02-08 17:30:27 +09:30
Jon
f9b0b694f9 test(itam): Software Category Notes Test Cases for API Field Checks
ref: #525 #540
2025-02-08 17:28:57 +09:30
Jon
d19e742cc5 test(itam): Device Type Notes Test Cases for API Field Checks
ref: #525 #538
2025-02-08 17:27:13 +09:30
Jon
a077506eb0 test(itam): Device Notes Test Cases for API Field Checks
ref: #524 #525
2025-02-08 17:26:49 +09:30
Jon
585f1c01e8 test(itam): Device Model Notes Test Cases for API Field Checks
ref: #523 #525
2025-02-08 17:26:35 +09:30
Jon
f84759720c test(itam): Operating System Test Cases for API Field Checks
ref: #525 #531
2025-02-08 17:26:08 +09:30
Jon
91c5216120 test(itam): Operating System Version Test Cases for API Field Checks
ref: #525 #539
2025-02-08 17:25:47 +09:30
Jon
98868aab32 test(core): Manufacturer Test Cases for API Field Checks
ref: #525 #530
2025-02-08 17:25:00 +09:30
Jon
0a8c3cfffb test(config_management): Config Group Test Cases for API Field Checks
ref: #525 #529
2025-02-08 17:24:34 +09:30
Jon
bff5ea3fe8 test(assistance): KB Test Cases for API Field Checks
ref: #525 #528
2025-02-08 17:24:07 +09:30
Jon
e7729afc98 test(access): Team Test Cases for API Field Checks
ref: #525 #527
2025-02-08 17:23:14 +09:30
Jon
7358b0a90c test(access): Organization Test Cases for API Field Checks
ref: #525 #526
2025-02-08 17:22:59 +09:30
Jon
e67ede0e74 test(core): Model Notes Base Test Cases for API Field Checks
ref: #389 #525
2025-02-08 00:05:18 +09:30
Jon
1e914d1345 feat(project_management): Project Type Notes ViewSet
ref: #525 #544
2025-02-07 23:09:06 +09:30
Jon
ba7c0cd117 feat(project_management): Project Type Notes Serializer
ref: #525 #544
2025-02-07 23:08:34 +09:30
Jon
029e5a6d08 feat(project_management): Project Type Notes Model
ref: #525 #544
2025-02-07 23:08:17 +09:30
Jon
f71fe3d6d7 feat(project_management): Project State Notes ViewSet
ref: #525 #543
2025-02-07 23:00:34 +09:30
Jon
c97cdf276f feat(project_management): Project State Notes Serializer
ref: #525 #543
2025-02-07 22:59:56 +09:30
Jon
c2f7e25579 feat(project_management): Project State Notes Model
ref: #525 #543
2025-02-07 22:59:38 +09:30
Jon
9e7f731a0e feat(project_management): Project Milestone Notes ViewSet
ref: #525 #542
2025-02-07 22:52:59 +09:30
Jon
1f366ab8fd feat(project_management): Project Milestone Notes Serializer
ref: #525 #542
2025-02-07 22:52:20 +09:30
Jon
e59e808b7f feat(project_management): Project Milestone Notes Model
ref: #525 #542
2025-02-07 22:52:01 +09:30
Jon
39f48aadb1 feat(itam): Software Version Notes ViewSet
ref: #525 #541
2025-02-07 22:41:32 +09:30
Jon
e121741a6c feat(itam): Software Version Notes Serializer
ref: #525 #541
2025-02-07 22:40:57 +09:30
Jon
0145ee9349 feat(itam): Software Version Notes Model
ref: #525 #541
2025-02-07 22:40:41 +09:30
Jon
7a0be93752 feat(itam): Software Category Notes ViewSet
ref: #525 #540
2025-02-07 22:32:14 +09:30
Jon
89eecad448 feat(itam): Software Category Notes Serializer
ref: #525 #540
2025-02-07 22:31:45 +09:30
Jon
a5cbf108cb feat(itam): Software Category Notes Model
ref: #525 #540
2025-02-07 22:31:29 +09:30
Jon
67e892dbaa feat(itam): Operating System Version Notes ViewSet
ref: #525 #539
2025-02-07 22:08:12 +09:30
Jon
8477700125 feat(itam): Operating System Version Notes Serializer
ref: #525 #539
2025-02-07 22:07:39 +09:30
Jon
59603e0455 feat(itam): Operating System Version Notes Model
ref: #525 #539
2025-02-07 22:07:22 +09:30
Jon
0bc3f420f0 test: remove old notes model tests
ref: #389 #526
2025-02-07 21:47:01 +09:30
Jon
960646a68c feat(settings): External Link Notes ViewSet
ref: #526 #545
2025-02-07 21:42:09 +09:30
Jon
d1dbe965f9 feat(settings): External Link Notes Serializer
ref: #526 #545
2025-02-07 21:41:40 +09:30
Jon
9fa6ec46dd feat(settings): External Link Notes Model
ref: #526 #545
2025-02-07 21:41:19 +09:30
Jon
d03afa3017 feat(itam): Device Model Notes ViewSet
ref: #523 #526
2025-02-07 21:32:06 +09:30
Jon
f157545b01 feat(itam): Device Model Notes Serializer
ref: #523 #526
2025-02-07 21:31:24 +09:30
Jon
e18f70a20c feat(itam): Device Model Notes Model
ref: #523 #526
2025-02-07 21:31:09 +09:30
Jon
e8ce851c31 fix(access): field organization requires team related_model for org
ref: #526
2025-02-07 21:23:14 +09:30
Jon
4dd4215fd0 test: Update url_name to match new notes endpoint
ref: #526
2025-02-07 21:17:24 +09:30
Jon
dad843f1d4 test(config_Management): Update url_name to match new notes endpoint
ref: #526 #529
2025-02-07 21:11:43 +09:30
Jon
47b1dd7732 feat(itam): Device Type Notes ViewSet
ref: #526 #538
2025-02-07 21:08:55 +09:30
Jon
a599cc9f9c feat(itam): Device Type Notes Serializer
ref: #526 #538
2025-02-07 21:08:19 +09:30
Jon
893066942d feat(itam): Device Type Notes Model
ref: #526 #538
2025-02-07 21:08:05 +09:30
Jon
858538e3eb feat(core): Create an action comment on a ticket when the category changes
ref: #526 #537
2025-02-07 20:50:45 +09:30
Jon
b42a5a9abb feat(itim): Porte Notes ViewSet
ref: #526 #536
2025-02-07 20:35:27 +09:30
Jon
9ea8422007 feat(itim): Porte Notes Serializer
ref: #526 #536
2025-02-07 20:35:01 +09:30
Jon
39fdc9ef1d feat(itim): Porte Notes Model
ref: #526 #536
2025-02-07 20:34:48 +09:30
Jon
a161677f79 feat(itim): Cluster Type Notes ViewSet
ref: #526 #535
2025-02-07 20:28:56 +09:30
Jon
013c31272b feat(itim): Cluster Type Notes Serializer
ref: #526 #535
2025-02-07 20:28:17 +09:30
Jon
911a086963 feat(itim): Cluster Type Notes Model
ref: #526 #535
2025-02-07 20:28:06 +09:30
Jon
c1a47045e7 feat(project_management): Project Notes ViewSet
ref: #335 #526
2025-02-07 20:21:16 +09:30
Jon
644188f952 feat(project_management): Project Notes Serializer
ref: #335 #526
2025-02-07 20:20:46 +09:30
Jon
717bc5750b feat(project_management): Project Notes Model
ref: #335 #526
2025-02-07 20:20:30 +09:30
Jon
3ca1aa3756 feat(itim): Service Notes ViewSet
ref: #526 #534
2025-02-07 20:12:26 +09:30
Jon
afd20ac4e3 feat(itim): Service Notes Serializer
ref: #526 #534
2025-02-07 20:12:01 +09:30
Jon
79c96ff2b5 feat(itim): Service Notes Model
ref: #526 #534
2025-02-07 20:11:48 +09:30
Jon
b135b0690e feat(itim): Cluster Notes ViewSet
ref: #526 #533
2025-02-07 19:59:56 +09:30
Jon
041b837068 feat(itim): Cluster Notes Serializer
ref: #526 #533
2025-02-07 19:59:19 +09:30
Jon
ea8a56322f feat(itim): Cluster Notes Model
ref: #526 #533
2025-02-07 19:59:05 +09:30
Jon
0b418b6f2c feat(itam): Software Notes ViewSet
ref: #526 #532
2025-02-07 19:47:35 +09:30
Jon
a2abfc83ee feat(itam): Software Notes Serilaizer
ref: #526 #532
2025-02-07 19:46:54 +09:30
Jon
03ce6824ee feat(itam): Software Notes Model
ref: #526 #532
2025-02-07 19:46:39 +09:30
Jon
c12f9925ae feat(itam): Operating System Notes ViewSet
ref: #526 #531
2025-02-07 19:36:56 +09:30
Jon
3efa835295 feat(itam): Operating System Notes Serializer
ref: #526 #531
2025-02-07 19:36:10 +09:30
Jon
d8fdb17112 feat(itam): Operating System Notes Model
ref: #526 #531
2025-02-07 19:36:00 +09:30
Jon
5842e81455 feat(core): Manufacturer Notes viewset
ref: #526 #530
2025-02-07 19:18:16 +09:30
Jon
f057693a61 feat(core): Manufacturer Notes serializer
ref: #526 #530
2025-02-07 19:17:53 +09:30
Jon
c77d33e910 feat(core): Manufacturer Notes Model
ref: #526 #530
2025-02-07 19:17:37 +09:30
Jon
ecb7116f4a feat(config_management): Config Group Notes ViewSet
ref: #526 #529
2025-02-07 19:06:34 +09:30
Jon
3464fcf93b feat(config_management): Config Group Notes Serializer
ref: #526 #529
2025-02-07 19:06:11 +09:30
Jon
c8f0c54549 feat(config_management): Config Group Notes Model
ref: #526 #529
2025-02-07 19:05:57 +09:30
Jon
4367878396 feat(assistance): Knowledge Base Notes ViewSet
ref: #526 #528
2025-02-07 18:49:56 +09:30
Jon
c950daa011 feat(assistance): Knowledge Base Notes Serializer
ref: #526 #528
2025-02-07 18:49:34 +09:30
Jon
725b009ee6 feat(assistance): Knowledge Base Notes Model
ref: #526 #528
2025-02-07 18:49:22 +09:30
Jon
9fcb793bb2 feat(access): Team Notes ViewSet
ref: #526 #527
2025-02-07 18:35:38 +09:30
Jon
bd574ce952 feat(access): Team Notes Serializer
ref: #526 #527
2025-02-07 18:35:10 +09:30
Jon
77f0554893 feat(access): Team Notes Model
ref: #526 #527
2025-02-07 18:34:37 +09:30
Jon
e07af389ba feat(access): Organization Notes ViewSet
ref: #525 #526
2025-02-07 18:17:05 +09:30
Jon
3060fd0b86 feat(access): Organization Notes Serializer
ref: #525 #526
2025-02-07 18:16:27 +09:30
Jon
3184e5fb07 feat(access): Organization Notes Model
ref: #525 #526
2025-02-07 18:16:17 +09:30
Jon
02140ce731 feat(itam): Device Notes ViewSet
ref: #524 #525
2025-02-07 18:12:50 +09:30
Jon
dd06d9e1ff feat(itam): Device Notes Serializer
ref: #524 #525
2025-02-07 18:12:21 +09:30
Jon
ca4e9add88 feat(itam): Device Notes Model
ref: #524 #525
2025-02-07 18:12:09 +09:30
Jon
657bbfb25a test(core): Remove notes test cases for previous notes model
ref: #389 #525
2025-02-07 18:11:10 +09:30
Jon
64e344206c refactor(access): Dont add releationship from tenancyObject.organization to organization model
ref: #389 #525
2025-02-07 18:10:44 +09:30
Jon
a132baa838 feat(core): Base viewset for model notes
ref: #389 #525
2025-02-07 18:08:42 +09:30
Jon
f833b93074 feat(core): Base serializer for model notes
ref: #389 #525
2025-02-07 18:06:49 +09:30
Jon
06991853ff fix(core): Use generic APIError for ticket save when no action comment will be created
ref: #525
2025-02-07 16:32:37 +09:30
Jon
cff343989b feat(core): Base model for model notes
ref: #389 #525
2025-02-07 15:49:12 +09:30
Jon
ff8d422308 feat(core): Add failsafe to throw an exception if no action comment will be created
ref: #522 closes #492
2025-02-06 23:08:11 +09:30
Jon
aded110307 feat(core): Add field parent_ticket to base ticket view serializer
ref: #517 #522
2025-02-06 22:50:06 +09:30
Jon
fbc6a3c338 feat(project_management): Add field parent_ticket to project task ticket view serializer
ref: #517 #522
2025-02-06 22:49:42 +09:30
Jon
7629c6cf9b feat(itim): Add field parent_ticket to problem ticket view serializer
ref: #517 #522
2025-02-06 22:49:24 +09:30
Jon
96b2eec050 feat(itim): Add field parent_ticket to incident ticket view serializer
ref: #517 #522
2025-02-06 22:49:15 +09:30
Jon
30394644bf feat(itim): Add field parent_ticket to change ticket view serializer
ref: #517 #522
2025-02-06 22:49:02 +09:30
Jon
610df69e21 feat(assistance): Add field parent_ticket to request ticket view serializer
ref: #517 #522
2025-02-06 22:48:46 +09:30
Jon
0b0b527b55 feat(core): Add field parent to ticket model
ref: #517 #522
2025-02-06 22:40:20 +09:30
724e7e6fb0 build: bump version 1.8.0 -> 1.9.0 2025-02-06 12:29:10 +00:00
Jon
30160fb033 Merge pull request #485 from nofusscomputing/feature-next-release 2025-02-06 21:49:54 +09:30
Jon
317a66b714 Merge pull request #518 from nofusscomputing/viewset-caching-of-objects 2025-02-06 21:38:41 +09:30
Jon
aa9c0c7552 test(core): Add missing unit tests for notes ticket viewset
ref: #518 closes #512 closes #513
2025-02-06 21:24:23 +09:30
Jon
22f46160be fix(project_management): Add missing attribute view_description to project tasks viewset
ref: #518
2025-02-06 21:10:59 +09:30
Jon
745e1f6a66 fix(settings): Add missing attribute view_description to user settings viewset
ref: #518
2025-02-06 21:09:44 +09:30
Jon
d8fae15995 fix(settings): Add missing attribute view_description to app settings viewset
ref: #518
2025-02-06 21:09:35 +09:30
Jon
5d8332015b test(settings): Add missing unit tests for user settings ticket viewset
ref: #512 #513 #518
2025-02-06 21:08:59 +09:30
Jon
91e83b0ad9 test(settings): Add missing unit tests for app settings ticket viewset
ref: #512 #513 #518
2025-02-06 21:08:52 +09:30
Jon
f29a8ebc23 fix(itim): Add missing attribuite to problem ticket viewset
ref: #518
2025-02-06 21:01:47 +09:30
Jon
68a2ca73cc fix(itim): Add missing attribuite to incident ticket viewset
ref: #518
2025-02-06 21:01:38 +09:30
Jon
121ee7b861 fix(itim): Add missing attribuite to change ticket viewset
ref: #518
2025-02-06 21:01:30 +09:30
Jon
af0b05cdd5 chore(itim): correct var name within function get_serializer_class
ref: #512 #513 #518
2025-02-06 20:59:21 +09:30
Jon
73c3f0ac34 test(project_management): Add missing unit tests for project task ticket viewset
ref: #512 #513 #518
2025-02-06 20:50:56 +09:30
Jon
0db2c09be4 test(api): Add kwargs as arg to test cases
ref: #512 #513 #518
2025-02-06 20:47:58 +09:30
Jon
21a85555a7 fix(itimm): correct truthy check for service device ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 20:45:56 +09:30
Jon
cf35677464 fix(itam): correct truthy check for service cluster ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 20:45:37 +09:30
Jon
c508cfc8a0 fix(itam): correct truthy check for service cluster ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 20:45:26 +09:30
Jon
2e321d22bc fix(itam): correct truthy check for service device ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 20:45:13 +09:30
Jon
16199cbd30 test(itim): Add missing unit tests for problem ticket viewset
ref: #512 #513 #518
2025-02-06 20:44:27 +09:30
Jon
29443a51fc test(itim): Add missing unit tests for incident ticket viewset
ref: #512 #513 #518
2025-02-06 20:44:20 +09:30
Jon
bac22d6a9d test(itim): Add missing unit tests for change ticket viewset
ref: #512 #513 #518
2025-02-06 20:44:12 +09:30
Jon
f86038c169 test(core): add permisssion class override test case for celery results
ref: #518
2025-02-06 20:36:56 +09:30
Jon
38314ac977 fix(itam): add missing attribute view_name to celery log viewset
ref: #518
2025-02-06 20:36:17 +09:30
Jon
83fb6d005b test: Add empty kwargs to ViewSet index page test cases
ref: #512 #513 #518
2025-02-06 20:35:46 +09:30
Jon
c755feb9a7 fix(api): correct get_view_name to prioritize view_name over model.verbose_name
ref: #512 #513 #518
2025-02-06 20:34:46 +09:30
Jon
6f0a67a957 fix(itam): correct truthy check for software version ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 20:08:04 +09:30
Jon
66b6ba9ae9 fix(itam): correct truthy check for os version ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 20:07:51 +09:30
Jon
f1e1e76266 fix(itam): correct truthy check for device software ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 20:07:35 +09:30
Jon
22fcd42689 fix(itam): correct truthy check for device os ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 20:07:24 +09:30
Jon
efc95ef736 fix(itam): correct truthy check for software version ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 20:07:00 +09:30
Jon
184f775af4 fix(itam): correct truthy check for software category ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 20:06:39 +09:30
Jon
4eec08ffda fix(itam): correct truthy check for os version ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 20:06:15 +09:30
Jon
9c2b0cbe66 fix(itam): correct truthy check for device software ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 20:05:58 +09:30
Jon
84499bf5d1 fix(itam): correct truthy check for device os ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 20:05:44 +09:30
Jon
0338e5c8de test(itam): Add missing unit tests for software version viewset
ref: #512 #513 #518
2025-02-06 20:05:04 +09:30
Jon
b9c4228edc test(itam): Add missing unit tests for software categories viewset
ref: #512 #513 #518
2025-02-06 20:04:17 +09:30
Jon
c907efbadb test(itam): Add missing unit tests for os versions viewset
ref: #512 #513 #518
2025-02-06 20:04:02 +09:30
Jon
c54c54d821 test(itam): Add missing unit tests for software installs viewset
ref: #512 #513 #518
2025-02-06 20:03:49 +09:30
Jon
d608a3d138 test(itam): Add missing unit tests for os installs viewset
ref: #512 #513 #518
2025-02-06 20:03:38 +09:30
Jon
18ddd92509 test(itam): Add missing unit tests for device software viewset
ref: #512 #513 #518
2025-02-06 20:03:26 +09:30
Jon
02afaf29ff test(itam): Add missing unit tests for device operating system viewset
ref: #512 #513 #518
2025-02-06 20:03:02 +09:30
Jon
bb99b89a38 test(api): Add kwargs as arg to test cases
ref: #512 #513 #518
2025-02-06 19:40:51 +09:30
Jon
b988fe3084 fix(core): correct varname for queryset within notes queryset
ref: #518
2025-02-06 19:35:29 +09:30
Jon
697ebbb96b fix(core): add missing attribute view_description to ticket linked item viewset
ref: #518
2025-02-06 19:21:39 +09:30
Jon
241f18a055 fix(core): add missing attribute view_description to ticket comment viewset
ref: #518
2025-02-06 19:21:29 +09:30
Jon
a32713f6b7 fix(core): add missing attribute view_description to note viewset
ref: #518
2025-02-06 19:21:18 +09:30
Jon
f910503f3c fix(core): add missing attribute view_description to related ticket log viewset
ref: #518
2025-02-06 19:21:03 +09:30
Jon
9cb142fa4b fix(core): add missing attribute view_description to celery log viewset
ref: #518
2025-02-06 19:20:53 +09:30
Jon
991d30e1fd fix(core): add missing attribute view_description to history viewset
ref: #518
2025-02-06 19:20:44 +09:30
Jon
504437f8cc fix(core): correct truthy check for notes ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 19:20:15 +09:30
Jon
33fa7fd61c fix(core): correct truthy check for history ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 19:19:18 +09:30
Jon
16a827c73b fix(core): correct truthy check for notes ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 19:19:05 +09:30
Jon
805835721e fix(core): correct truthy check for related ticket ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 19:18:47 +09:30
Jon
82bd63b80d fix(core): correct truthy check for ticket comment ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 19:18:32 +09:30
Jon
caa6a6df99 fix(core): correct truthy check for ticket linked items ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 19:18:09 +09:30
Jon
34f7dd834d fix(core): correct truthy check for ticket linked items ViewSet property when evaluating queryset
ref: #513 #518
2025-02-06 19:17:27 +09:30
Jon
3e6206aaad fix(core): correct truthy check for ticket comment ViewSet property when evaluating queryset
ref: #513 #518
2025-02-06 19:17:02 +09:30
Jon
6e789e6966 fix(core): correct truthy check for related ticket ViewSet property when evaluating queryset
ref: #513 #518
2025-02-06 19:16:38 +09:30
Jon
d1364c6696 fix(core): correct truthy check for history ViewSet property when evaluating queryset
ref: #513 #518
2025-02-06 19:16:22 +09:30
Jon
1e71d0d39b fix(core): correct truthy check for celery log ViewSet property when evaluating queryset
ref: #513 #518
2025-02-06 19:16:06 +09:30
Jon
b8b534e499 test(core): Add missing unit tests for ticket linked items viewset
ref: #512 #513 #518
2025-02-06 19:15:29 +09:30
Jon
245962668f test(core): Add missing unit tests for ticket comment viewset
ref: #512 #513 #518
2025-02-06 19:15:18 +09:30
Jon
d5dd58f78e test(core): Add missing unit tests for celery log viewset
ref: #512 #513 #518
2025-02-06 19:14:30 +09:30
Jon
fcb212e66e test(core): Add missing unit tests for history viewset
ref: #512 #513 #518
2025-02-06 19:14:18 +09:30
Jon
038a3404d7 test(core): Add missing unit tests for related tickets viewset
ref: #512 #513 #518
2025-02-06 19:14:07 +09:30
Jon
b6e0a90848 fix(core): correct truthy check for Ticket Base ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 18:20:19 +09:30
Jon
29dd03557c fix(core): correct truthy check for ticket base ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 18:19:35 +09:30
Jon
fe73472aa4 fix(assistance): Add missing attribute view_description to request ticket ViewSet
ref: #518
2025-02-06 18:18:49 +09:30
Jon
6bab430466 test(assistance): Add missing unit tests for request ticket viewset
ref: #512 #513 #518
2025-02-06 18:18:26 +09:30
Jon
776449ba10 test(api): queryset and serializer_class test cases updated to use Fake request object
some viewsets require the request object for permission checking.

ref: #512 #513 #518
2025-02-06 18:10:54 +09:30
Jon
9ba2fd6779 fix(settinggs): Add missing attribute view_description to external links ViewSet
ref: #518
2025-02-06 17:43:52 +09:30
Jon
02595ca010 fix(core): Add missing attribute view_description to ticket comment category ViewSet
ref: #518
2025-02-06 17:36:48 +09:30
Jon
59c541486d fix(core): Add missing attribute view_description to ticekt category ViewSet
ref: #518
2025-02-06 17:36:35 +09:30
Jon
9b1d3384cd fix(core): Add missing attribute view_description to Manufacturer ViewSet
ref: #518
2025-02-06 17:36:12 +09:30
Jon
fe27218256 fix(project_management): correct truthy check for project milestone ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 17:29:50 +09:30
Jon
64c9ed7978 fix(settings): correct truthy check for user settings ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 17:26:30 +09:30
Jon
919bfd9d98 fix(settings): correct truthy check for external links ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 17:26:19 +09:30
Jon
1ba41d94fb fix(settings): correct truthy check for app settings ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 17:26:07 +09:30
Jon
0ed80217f2 test(settings): Add missing unit tests for external links viewset
ref: #512 #513 #518
2025-02-06 17:24:03 +09:30
Jon
346fc57ba7 fix(project_management): correct truthy check for project milestone ViewSet property when evaluating queryset
ref: #512 #518
2025-02-06 17:15:59 +09:30
Jon
ba443c4e64 fix(project_management): correct truthy check for project ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 17:15:34 +09:30
Jon
c753b799ee fix(project_management): correct truthy check for project type ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 17:15:26 +09:30
Jon
70baac6fb2 fix(project_management): correct truthy check for project state ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 17:15:14 +09:30
Jon
1ea5937881 fix(project_management): correct truthy check for project milestone ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 17:15:04 +09:30
Jon
33726b9114 test(project_management): Add missing unit tests for project type viewset
ref: #512 #513 #518
2025-02-06 17:14:32 +09:30
Jon
a89d13c2b1 test(project_management): Add missing unit tests for project state viewset
ref: #512 #513 #518
2025-02-06 17:14:05 +09:30
Jon
0d32fd3528 test(project_management): Add missing unit tests for project milestone viewset
ref: #512 #513 #518
2025-02-06 17:13:43 +09:30
Jon
3dce2dad05 test(project_management): Add missing unit tests for project viewset
ref: #512 #513 #518
2025-02-06 17:13:32 +09:30
Jon
e17164d336 fix(itam): correct truthy check for service ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 17:01:15 +09:30
Jon
c36e9a5293 fix(itam): correct truthy check for port ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 17:01:05 +09:30
Jon
dcf8136191 fix(itam): correct truthy check for cluster ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 17:00:55 +09:30
Jon
10e63b3135 fix(itam): correct truthy check for cluster type ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-06 17:00:49 +09:30
Jon
2baee0e4d0 test(itim): Add missing unit tests for service viewset
ref: #512 #513 #518
2025-02-06 17:00:29 +09:30
Jon
ae94285dc7 test(itim): Add missing unit tests for ports viewset
ref: #512 #513 #518
2025-02-06 17:00:13 +09:30
Jon
c3a0c563a6 test(itim): Add missing unit tests for cluster types viewset
ref: #512 #513 #518
2025-02-06 16:59:53 +09:30
Jon
d0590b8515 test(itim): Add missing unit tests for cluster viewset
ref: #512 #513 #518
2025-02-06 16:59:42 +09:30
Jon
f0c9c0cdc6 fix(itam): correct truthy check for software ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 08:02:23 +09:30
Jon
b57d68082b fix(itam): correct truthy check for os ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 08:02:13 +09:30
Jon
af494acd7f fix(itam): correct truthy check for device ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 08:02:05 +09:30
Jon
e205b0a7a6 fix(itam): correct truthy check for device type ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 08:01:59 +09:30
Jon
c7fc8055f6 fix(itam): correct truthy check for device model ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 08:01:49 +09:30
Jon
76cc4e1e8a fix(core): correct truthy check for ticket comment category ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 08:01:34 +09:30
Jon
4bbff26bd2 fix(core): correct truthy check for ticket category ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 08:01:23 +09:30
Jon
2f9b65a326 fix(core): correct truthy check for manufacturer ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 08:01:12 +09:30
Jon
27b7550114 fix(config_management): correct truthy check for config group ViewSet property when evaluating queryset
ref: #512 #518
2025-02-04 08:00:47 +09:30
Jon
06cefb9223 fix(config_management): correct truthy check for config group ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 08:00:36 +09:30
Jon
67a5e63b33 fix(config_management): correct truthy check for config group software ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 08:00:21 +09:30
Jon
4dae20b056 fix(config_management): correct truthy check for config group software ViewSet property when evaluating queryset
ref: #512 #518
2025-02-04 08:00:04 +09:30
Jon
4adb38fdd2 fix(assistance): correct truthy check for model kb article ViewSet property when evaluating queryset
ref: #512 #518
2025-02-04 07:59:32 +09:30
Jon
ab7c840c60 fix(assistance): correct truthy check for model kb article ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 07:59:19 +09:30
Jon
997d2df4b7 fix(assistance): correct truthy check for kb ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 07:58:58 +09:30
Jon
4160d974f5 fix(assistance): correct truthy check for kb ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 07:58:50 +09:30
Jon
baf7d0226e fix(access): correct truthy check for team ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 07:58:32 +09:30
Jon
36992f8469 fix(access): correct truthy check for team ViewSet property when evaluating queryset
ref: #512 #518
2025-02-04 07:58:13 +09:30
Jon
325509aa16 fix(access): correct truthy check for team user ViewSet property when evaluating queryset
ref: #512 #518
2025-02-04 07:58:01 +09:30
Jon
57cc0d2caa fix(access): correct truthy check for team user ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 07:57:44 +09:30
Jon
4833245e34 fix(access): correct truthy check for organization ViewSet property when evaluating serializer_class
ref: #513 #518
2025-02-04 07:57:27 +09:30
Jon
d57dfb809b test(itam): Add missing unit tests for Software viewset
ref: #512 #513 #518
2025-02-04 07:45:07 +09:30
Jon
98d47c69d2 test(itam): Add missing unit tests for Operating System viewset
ref: #512 #513 #518
2025-02-04 07:45:07 +09:30
Jon
ded0cc925c test(itam): Add missing unit tests for Device Type viewset
ref: #512 #513 #518
2025-02-04 07:45:07 +09:30
Jon
857bcc6652 test(itam): Add missing unit tests for Device Model viewset
ref: #512 #513 #518
2025-02-04 07:45:07 +09:30
Jon
f3be33431c test(itam): Add missing unit tests for Device viewset
ref: #512 #513 #518
2025-02-04 07:45:07 +09:30
Jon
8bd3a8fb45 fix(api): correct truthy check for set property when evaluating serializer_class
ref: #513 #518 closes #519
2025-02-04 07:45:07 +09:30
Jon
67db68c5ea fix(api): correct truthy check for set property when evaluating queryset
ref: #512 #518 closes #519
2025-02-04 07:45:07 +09:30
Jon
3baae2a6cd test(api): dont mock the qs bool
this was mocked as the class var was returning false when the var was set. appears python uses the variables object for a truthy check

ref: #512 #513 #518
2025-02-04 07:34:55 +09:30
Jon
4d3a8e768b test(core): Add missing unit tests for Ticket Comment Category viewset
ref: #512 #513 #518
2025-02-04 07:29:11 +09:30
Jon
169b8dbf4d test(core): Add missing unit tests for Ticket Category viewset
ref: #512 #513 #518
2025-02-04 07:29:11 +09:30
Jon
8d7d5a127f test(core): Add missing unit tests for manufacturer viewset
ref: #512 #513 #518
2025-02-04 07:29:11 +09:30
Jon
ad4d030a40 fix(api): correct variable name in common viewset for queryset
ref: #512 #518
2025-02-04 07:29:11 +09:30
Jon
ce0cc856a7 fix(config_management): config group software viewset must cache queryset
ref: #512 #518
2025-02-04 07:29:11 +09:30
Jon
fae11ca310 fix(config_management): config group viewset must cache queryset
ref: #512 #518
2025-02-04 07:29:11 +09:30
Jon
95af0d3848 test(config_management): Add missing unit tests for config groups software viewset
ref: #512 #513 #518
2025-02-04 07:29:11 +09:30
Jon
596c708212 test(config_management): Add missing unit tests for config groups viewset
ref: #512 #513 #518
2025-02-04 07:29:11 +09:30
Jon
2f9b6c6027 fix(assistance): Knowledge base category viewset must cache serializer_class
ref: #513 #518
2025-02-04 07:29:11 +09:30
Jon
84ac2d3562 test(assistance): Add missing unit tests for Model Knowledge Base Article viewset
ref: #512 #513 #518
2025-02-04 07:29:11 +09:30
Jon
f9837e0ec4 test(assistance): Add missing unit tests for Knowledge Base Category viewset
ref: #512 #513 #518
2025-02-04 07:29:11 +09:30
Jon
73a63f5af4 test(assistance): Add missing unit tests for Knowledge Base viewset
ref: #512 #513 #518
2025-02-04 07:29:11 +09:30
Jon
5066cf7536 fix(access): Team viewset must cache serializer_class
ref: #513 #518
2025-02-04 07:29:11 +09:30
Jon
6522d274a3 fix(access): Team viewset must cach queryset
ref: #512 #518
2025-02-04 07:29:11 +09:30
Jon
759e774928 fix(access): Team User viewset must cach queryset
ref: #512 #518
2025-02-04 07:29:11 +09:30
Jon
56aab474e9 test(access): Add missing unit tests for team user viewset
ref: #512 #513 #518
2025-02-04 07:29:11 +09:30
Jon
1bd1d546fc test(access): Add missing unit tests for team viewset
ref: #512 #513 #518
2025-02-04 07:29:11 +09:30
Jon
fe3688791f test(access): Add missing unit tests for organization viewset
ref: #512 #513 #518
2025-02-04 07:29:11 +09:30
Jon
a00db4e7f6 fix(api): Add missing property bacjk_url to Common viewset
ref: #518
2025-02-04 07:29:11 +09:30
Jon
f80ed4d366 fix(api): Common viewset to cache and use queryset Object
ref: #512 #518
2025-02-04 07:29:11 +09:30
Jon
cd47e643d5 test(base): Ensure viewsets are caching and using the serializer_class object
ref: #513 #518
2025-02-04 07:29:10 +09:30
Jon
db12750dc0 test(base): Ensure viewsets are caching and using the queryset object
ref: #512 #518
2025-02-04 07:29:10 +09:30
Jon
59a9a1ca75 Merge pull request #514 from nofusscomputing/ensure-user-always-added-to-ticket-comment 2025-02-01 20:24:45 +09:30
Jon
942104ebed test(core): Test case to ensure ticket comment always has user added
ref: #514 fixes #460
2025-02-01 20:10:28 +09:30
Jon
df7b40ba03 feat(core): Validate user field to ensure ticket comments always have user who added comment
ref: #460 #514
2025-02-01 20:10:10 +09:30
Jon
1488c6e96a Merge pull request #511 from nofusscomputing/db-queries-reduction 2025-02-01 18:07:02 +09:30
Jon
b419c03774 fix(access): When conduting permission check for user settings, if user not owner of settings, deny access
ref: #471 #511
2025-02-01 17:56:14 +09:30
Jon
1ad370e7cf test(settings): when checking if user can delete own settings, user must be owner of settings
ref: #471 #511
2025-02-01 17:55:37 +09:30
Jon
b7cdb21136 fix(access): when checking object permissions, dont cast obj to int untill checking it exists
ref: #471 #511
2025-02-01 17:36:29 +09:30
Jon
78afe7c86b test(settings): regardless of permissions a user can change their own settings
ref: #471 #511
2025-02-01 17:26:12 +09:30
Jon
5507c4a366 test(access): during permission check function has_permission ensure get_object not called
ref: #471 #511
2025-02-01 17:20:11 +09:30
Jon
a4788aba75 fix(access): org mixin get_obj_org not to call get_object
ref: #471 #511
2025-02-01 16:58:39 +09:30
Jon
b6593c6825 refactor(access): when checking obj permission use view cached obj organization
ref: #471 #511
2025-02-01 16:30:01 +09:30
Jon
f9393a59d2 chore(access): Remove extra calls to get_obj_permission from permission Mixin has_permission
ref: #471 #511
2025-02-01 16:27:26 +09:30
Jon
22b02dc044 refactor(access): When fetching obj org, if pk exist attempt to fetch object
ref: #471 #511
2025-02-01 16:26:02 +09:30
Jon
e5b0ed4c69 feat(core): Cache ticket linked item queryset
ref: #471 #511
2025-02-01 15:28:19 +09:30
Jon
96565aa2c5 fix(core): ensure item_type exists before trying to get queryset
ref: #471 #511
2025-02-01 15:27:47 +09:30
Jon
17a29405c5 refactor(core): When fetching a ticket, fetch related fields
ref: #471 #511
2025-02-01 15:26:40 +09:30
Jon
c5eb26e62b feat: Views to cache discovered serializer
ref: #471 #511
2025-02-01 14:46:05 +09:30
Jon
49088f1f68 Merge pull request #510 from nofusscomputing/test-ticket-description-action-comment 2025-01-31 22:12:16 +09:30
Jon
9140a886f2 test(core): Ensure that an action comment is created when ticket description is edited
ref: #510 closes #502
2025-01-30 18:20:56 +09:30
Jon
894ed14842 Merge pull request #509 from nofusscomputing/ticket-action-comment-description 2025-01-30 14:51:56 +09:30
Jon
d7a8840444 feat(core): When changing ticket description create an action comment with the details
ref: #502
2025-01-28 05:47:07 +09:30
Jon
624f892a73 Merge pull request #508 from nofusscomputing/add-missing-ticket-action-comments 2025-01-27 20:59:10 +09:30
Jon
26e475a42f test(core): Ticket Action comment test cases for real_finish_date actions
ref: #508 closes #501
2025-01-27 20:04:16 +09:30
Jon
de2722f6ca test(core): Ticket Action comment test cases for real_start_date actions
ref: #508 closes #499
2025-01-27 20:03:25 +09:30
Jon
32913895c9 test(core): Ticket Action comment test cases for planned_finish_date actions
ref: #508 closes #500
2025-01-27 20:02:37 +09:30
Jon
744d9a2ec4 test(core): Ticket Action comment test cases for planned_start_date actions
ref: #508 closes #498
2025-01-27 20:02:02 +09:30
Jon
98bc0fc1d7 fix(core): Ticket Action comment date fields must be checked if empty before use
ref: #492 #508
2025-01-27 20:01:31 +09:30
Jon
f7543ced8d test(core): Ticket Action comment test cases for milestone actions
ref: #508 closes #497
2025-01-27 19:27:18 +09:30
Jon
7190354d6a test(core): Ticket Action comment test cases for project actions
ref: #395 #508
2025-01-27 19:15:10 +09:30
Jon
467fc18bd1 test(core): Ticket Action comment tests moved to their own suite
ref: #497 #508
2025-01-27 18:59:22 +09:30
Jon
7572763129 refactor(core): Ticket action comment for changing milestone to use item tasg
ref: #497 #508
2025-01-27 17:58:02 +09:30
Jon
76e7d64341 refactor(core): Ticket action comment for changing project to use item tasg
ref: #492 #508
2025-01-27 17:57:01 +09:30
Jon
120380a95c docs(roadmap): update
ref: #508
2025-01-27 17:18:39 +09:30
Jon
4c5c460d88 feat(core): When changing a ticket real finish date create an action comment with the details
ref: #501 #508
2025-01-27 17:18:39 +09:30
Jon
2865c3d32a feat(core): When changing a ticket real start date create an action comment with the details
ref: #499 #508
2025-01-27 17:18:38 +09:30
Jon
efd205c244 feat(core): When changing a ticket planned finish date create an action comment with the details
ref: #500 #508
2025-01-27 17:18:38 +09:30
Jon
c91857850b feat(core): When changing a ticket planned start date create an action comment with the details
ref: #498 #508
2025-01-27 17:13:04 +09:30
Jon
a58cb1ee95 feat(core): When changing a ticket milestone create an action comment with the changed details
ref: #497 #508
2025-01-27 17:12:35 +09:30
Jon
995a102da1 Merge pull request #491 from nofusscomputing/ticket-badges 2025-01-24 17:50:52 +09:30
Jon
40a15ed4f0 test(core): Unit test cases for ticket urgency_badge field checks
ref: #491 closes #487
2025-01-24 17:26:35 +09:30
Jon
52e852c647 test(core): Unit test cases for ticket priority_badge field checks
ref: #491 closes #489
2025-01-24 17:26:35 +09:30
Jon
9a35d023d9 test(core): Unit test cases for ticket impact_badge field checks
ref: #491 closes #488
2025-01-24 17:26:34 +09:30
Jon
cbecdae5d1 feat(core): Add Priority badge field to ALL ticket types
ref: #489 #491
2025-01-24 17:06:56 +09:30
Jon
bcc71ee866 feat(core): Add Impact badge field to ALL ticket types
ref: #488 #491
2025-01-24 17:06:34 +09:30
Jon
824244f217 feat(core): Add urgency badge field to ALL ticket types
ref: #487 #491
2025-01-24 17:03:15 +09:30
Jon
06e0943e30 test(settings): Remove no-permission failure test as user settings require no permissions
ref: #485 fixes #486
2025-01-24 15:46:01 +09:30
Jon
b8cac94f9a fix(settings): grant the user access to their own settings object
ref: #485 #486
2025-01-24 15:17:07 +09:30
Jon
a1207bfb0f fix(settings): grant the user access to their own settings
ref: #485 #486
2025-01-23 22:47:55 +09:30
Jon
b0127584d6 docs: correct doc linting errors so they build
ref: #485  fixes #484
2025-01-23 19:04:38 +09:30
857 changed files with 31364 additions and 9246 deletions

View File

@ -17,5 +17,5 @@ commitizen:
prerelease_offset: 1
tag_format: $version
update_changelog_on_bump: false
version: 1.8.0
version: 1.12.0
version_scheme: semver

81
.github/ISSUE_TEMPLATE/new_model.md vendored Normal file
View File

@ -0,0 +1,81 @@
---
name: New Database Model
about: Use when creating a new database model.
title: "New Model - <model table name>"
type: Task
labels: task::feature, triage, type::task
---
<!-- Add an intro -->
<!-- describe a use case if not covered in intro -->
## 📝 Details
<!--
Describe in detail the following:
- New model field
- if foreign key field, what it's name will be or if it's not to be linked ensure specified and coded with `related_name = '+' to disable the link`.
- How the UI will work, be layed out, new ui features etc
- custom permissions if required
-->
### 🚧 Tasks
<!-- Don't remove tasks strike them out. use `~~` before and after the item. i.e. `- ~~[ ] Model Created~~` note: don't include the list dash-->
- [ ] 🆕 Model Created
- [ ] 🛠️ Migrations added
- [ ] 🏷️ Model tag added to `app/core/lib/slash_commands/linked_model.CommandLinkedModel.get_model()` function
- [ ] Tag updated in the [docs](https://nofusscomputing.com/projects/centurion_erp/user/core/markdown/#model-reference)
>[!note]
> Ensure that when creating the tag the following is adhered to:
> - Two words are not to contain a space char, `\s`. It is to be replaced with an underscore `_`
> - As much as practical, keep the tag as close to the model name as possible
- [ ] 📝 New [History model](https://nofusscomputing.com/projects/centurion_erp/development/core/model_history/) created
- [ ] 📓 New [Notes model](https://nofusscomputing.com/projects/centurion_erp/development/core/model_notes/) created
- [ ] Admin Documentation added/updated _if applicable_
- [ ] Developer Documentation added/updated _if applicable_
- [ ] User Documentation added/updated
---
<!-- Add additional tasks here and as a check box list -->
#### 🧪 Tests
- [ ] Unit Test Model
- [ ] Unit Test Serializer
- [ ] Unit Test Tenancy Object
- [ ] Unit Test ViewSet
- [ ] Function Test ViewSet
- [ ] Function Test API Metadata
- [ ] Function Test API Permissions
- [ ] Function Test API Render (fields)
- [ ] Function Test History Entries
### ✅ Requirements
A Requirement is a must have. In addition will also be tested.
- [ ] Must have a [model_tag](https://nofusscomputing.com/projects/centurion_erp/user/core/markdown/#model-reference)
---
<!-- Add additional requirement here and as a check box list -->

View File

@ -19,4 +19,6 @@
],
"cSpell.language": "en-AU",
"jest.enable": false,
"pylint.enabled": true,
"pylint.importStrategy": "fromEnvironment",
}

View File

@ -1,3 +1,715 @@
## 1.12.0 (2025-03-01)
### feat
- **api**: Add delete column to AuthToken Table
- **docker**: Upgrade system packages on build
- **api**: AuthToken requires viewset get_back_url
- **api**: Add auth token api endpoint
- **settings**: Add section title to auth tokens
- **settings**: Add tokens url to user settings `_urls`
- **api**: Update Auth Token model for use with serializer
- **api**: Add user Auth Token viewset
- **api**: Add user Auth Token serializer
- **settings**: Add `page_layout` attribute to User Settings
### Fixes
- **api**: correct usage of `AuthToken.generate` to a property
### Tests
- **api**: AuthToken ViewSet checks (unit)
- **api**: AuthToken API Field checks
- **api**: AuthToken Serializer checks
- **api**: AuthToken ViewSet checks
## 1.11.0 (2025-02-21)
### feat
- **core**: Enable App settings History to save without specifying an organization
- **settings**: save_history method added to App Settings
- **settings**: History Model for App Settings Version added
- **core**: Migration for history data to new history tables
- **access**: save_history method added to Team
- **access**: History Model for Team added
- **access**: save_history method added to Organization
- **access**: History Model for Organization added
- **core**: add org field History Model api rendering
- **core**: Show the model name within history
- **project_management**: Project Milestone added to modelhistory.child_history_models
- **settings**: History Model migrations for External Link
- **settings**: save_history method added to External Link
- **settings**: History Model for External Link added
- **project_management**: History Model migrations for Project Type
- **project_management**: save_history method added to Project Type
- **project_management**: History Model for Project TYpe added
- **project_management**: History Model migrations for Project State
- **project_management**: save_history method added to Project State
- **project_management**: History Model for Project State added
- **project_management**: History Model migrations for Project Milestone
- **project_management**: save_history method added to Project Milestonr
- **project_management**: History Model for Project Milestone added
- **project_management**: History Model migrations for Project
- **project_management**: save_history method added to Project
- **project_management**: History Model for Project added
- **itim**: History Model migrations for Service
- **itim**: save_history method added to Service
- **itim**: History Model for Service added
- **itim**: History Model migrations for Port
- **itim**: save_history method added to Port
- **itim**: History Model for Port added
- **itim**: History Model migrations for Cluster Type
- **itim**: save_history method added to Cluster TYpe
- **itim**: History Model for Cluster Type added
- **itim**: History Model migrations for Cluster
- **itim**: save_history method added to Cluster
- **itim**: History Model for Cluster added
- **itam**: History Model migrations for Software Version
- **itam**: save_history method added to Software Version
- **itam**: History Model for Software Version added
- **itam**: History Model migrations for Software Category
- **itam**: save_history method added to Software Category
- **itam**: History Model for Software Category added
- **itam**: History Model migrations for Software
- **itam**: save_history method added to Software
- **itam**: History Model for Software added
- **itam**: History Model migrations for Operating System Version
- **itam**: save_history method added to Operating System Version
- **itam**: History Model for Operating System Version added
- **itam**: History Model migrations for Device Type
- **itam**: save_history method added to Device Type
- **itam**: History Model for Device Type added
- **itam**: History Model migrations for Device Operating System
- **itam**: save_history method added to Device Operating System
- **itam**: History Model for Device Operating System added
- **itam**: History Model migrations for Operating System
- **itam**: save_history method added to Operating System
- **itam**: History Model migrations for Operating System
- **itam**: History Model migrations for Device Software
- **itam**: History Model for Device Software added
- **itam**: save_history method added to Device
- **itam**: History Model migrations for Device Model
- **itam**: save_history method added to Device Model
- **itam**: History Model for Device Model added
- **core**: History Model migrations for Ticket Comment Category
- **core**: save_history method added to Ticket Comment Category
- **core**: History Model for Ticket Comment Category added
- **config_management**: Child History Models added to child model lists for config group hosts and software
- **core**: History Model migrations for Ticket Category
- **core**: save_history method added to Ticket Category
- **core**: History Model for Ticket Category added
- **core**: History Model migrations for Manufacturer
- **core**: save_history method added to Manufacturer
- **core**: History Model for Manufacturer added
- **config_management**: save_history method added to Config Group Software
- **config_management**: save_history method added to Config Group Hosts
- **config_management**: save_history method added to Config Groups
- **assistance**: save_history method added to Knowledge base
- **assistance**: save_history method added to Knowledge base category
- **config_management**: History Model migrations for Config Groupse + children
- **config_management**: History Model for Config Group Software added
- **config_management**: History Model for Config Group Hosts added
- **config_management**: History Model for Config Groups added
- **assistance**: History Model migrations for Knowledge base + children
- **assistance**: History Model for Knowledge base category added
- **assistance**: History Model for Knowledge base added
- **itam**: Add device history model
- **core**: History view to only display objects from the model being requested
- **core**: Add new history model to History Serializer
- **core**: Add new history model
- **development**: lint for un-used imports
- **development**: add pylit settings
- **core**: added new history model
- **api**: Device Software Viewset requires its own function to obtain the model view serializer
- **api**: Ticket Comment Viewset requires its own function to obtain the model view serializer
- **api**: Ticket Viewset requires its own function to obtain the model view serializer
- **api**: Always use a models `View` serializer for the response
- **core**: Add logic to ensure when organization changes, an action comment is created
- **core**: Add logic to ensure when parnet ticket changes, an action comment is created
### Fixes
- **settings**: App settings serializer fielad name does not exist
- **access**: dont use organization property within organization model
- **project_management**: Project milestone is not a child model
- **core**: Child models on delete must make model field null
- **project_management**: Project Milestone History is a primaryu model
- **core**: When a child model is deleted ensure entry is still created on parent model history
- **core**: when fetching url_kwargs for model history, make it dynamic for related field name
- **core**: Xorrect logic for determining view_action
- **core**: dynamically search for history object name
- **config_management**: Remove parent property from config groups
- **tests**: Correct Permission Import due to removing from access.models
- **project_management**: project Model serializer must inherit common serializer
- **core**: History audit objects must be a valid dict
- **api**: history app names can contain an underscore
- **core**: when saving history, use audit_model for content type
- **core**: add missing functions for fetching item url
- **project_management**: Opened by field set to read only for project task ticket
- **itim**: Opened by field set to read only for problem ticket
- **itim**: Opened by field set to read only for incident ticket
- **itim**: Opened by field set to read only for change ticket
- **assistance**: Opened by field set to read only for request ticket
- **core**: Ensure that if the parent ticket changes, that the logic caters for none
- **assistance**: Category can be empty for Project Task Ticket
- **assistance**: Category can be empty for Problem Ticket
- **assistance**: Category can be empty for Incident Ticket
- **assistance**: Category can be empty for Change Ticket
- **assistance**: Category can be empty for Request Ticket
- **core**: Ticket Action comment for category change must use category field
### Refactoring
- **core**: Update access imports to new path
- **core**: Update access imports to new path
- Update migrations imports to new path
- **config_management**: Update access imports to new path
- **api**: Update access imports to neew path
- **settings**: Update access imports to new path
- **project_management**: Update access imports to new path
- **itim**: Update access imports to new path
- **itam**: Update access imports to new path
- **core**: Update access imports to new path
- **config_management**: Update access imports to new path
- **assistance**: Update access imports to new path
- **base**: Update access imports to new path
- **api**: Update access imports to neew path
- **access**: Update access imports to neew path
- **access**: Move models to their own file
- **core**: move get_url to common serializer
- **api**: Update history url kwargs to use vals from model._meta
- **core**: superuser changed from import to triage access
- **core**: Ticket action comment logic only requires a single check
### Tests
- **settings**: History Entry checks for App Settings History
- **settings**: API Field Checks for App Settings History
- Model History not to save history on self
- **core**: Correct lookup for model history test setup
- **access**: remove test cases for Team prarent_object
- **access**: History Entry checks for Team model
- **access**: API Field Checks for Team History
- **access**: History Entry checks for Organization model
- **access**: API Field Checks for Organization History
- Fix History API checks for kb
- Fix History API checks for project milestone
- Fix History Entry checks for models
- **config_management**: History Entry checks for Config_group_hosts model
- **settings**: History Entry checks for External Link model
- **project_management**: History Entry checks for Project Type model
- **project_management**: History Entry checks for Project State model
- **project_management**: History Entry checks for Project Milestone model
- **project_management**: History Entry checks for Project model
- **itim**: History Entry checks for Service model
- **itim**: History Entry checks for Cluster Type model
- **itim**: History Entry checks for Port model
- **itim**: History Entry checks for Cluster model
- **itam**: History Entry checks for Software Version model
- **itam**: History Entry checks for Software Category model
- **itam**: History Entry checks for Software model
- **itam**: History Entry checks for Operating System Version model
- **itam**: History Entry checks for Operating System model
- **itam**: History Entry checks for Device Type model
- **itam**: History Entry checks for Device OS model
- **itam**: History Entry checks for Device Model model
- **itam**: History Entry checks for Device model
- **core**: History Entry checks for Ticket Comment Category model
- **core**: History Entry checks forTicket Category model
- **config_management**: History Entry checks for Config Groups Software model
- **config_management**: History Entry checks for Config Groups model
- **assistance**: History Entry checks for Knowledge base category model
- **assistance**: History Entry checks for Knowledge base model
- **itam**: Device Software History Entry checks
- **core**: Manufacturer History Entry checks
- **core**: Model History Entries Test Suite
- **core**: History Model Unit test cases for model and tenancy checks
- **settings**: API Field Checks for External Links History
- **project_management**: API Field Checks for Project Type History
- **project_management**: API Field Checks for Project State History
- **project_management**: API Field Checks for Project Milestone History
- **project_management**: API Field Checks for Project History
- **itim**: API Field Checks for Service History
- **itim**: API Field Checks for Port History
- **itim**: API Field Checks for Cluster Type History
- **itim**: API Field Checks for Cluster History
- **core**: API Field Checks for Ticket Comment Category History
- **core**: API Field Checks for Ticket Category History
- **config_management**: API Field Checks for Config Group Software History
- **config_management**: API Field Checks for Config Group Hosts History
- **config_management**: API Field Checks for Config Group History
- **assistance**: API Field Checks for Knowledge base category History
- **assistance**: API Field Checks for Knowledge base History
- **itam**: API Field Checks for Software Version History
- **itam**: API Field Checks for Software Category History
- **itam**: API Field Checks for Software History
- **itam**: API Field Checks for Operating System Version History
- **itam**: API Field Checks for Operating System History
- **itam**: API Field Checks for Device Type History
- **itam**: API Field Checks for Device OS History
- **itam**: API Field Checks for Device Model History
- **itam**: API Field Checks for Device History
- **core**: Unit Test Suite for History Model API field checks urls can either be str or hyperlink
- **itam**: API Field Checks for Device Software History
- **core**: API Field Checks for Manufacturer History
- **core**: API Field Checks for Model History
- **core**: Unit Test Suite for History Model API field checks
- **core**: Functional Test for History Model APIPermission updated to cater for tenancy obj
- **core**: Functional Test for History Model API Permissions and Metadata
- **core**: Unit Test for History Model Viewset
- **itam**: remove test cases for os version model.parent_object as it's not required
- **core**: disable hisotry viewset function test
- **core**: correct kwargs for history tests
- **core**: Remove old history model viewset tests
- Disable Old History Model test suites
- **core**: Ensure that when parent_ticket changes on a ticket an action comment is created
- **core**: Confirm on category change to ticket that an action comment is created
## 1.10.1 (2025-02-14)
### Fixes
- **python**: Dont use system TimeZone data, use python zoneinfo module zone data
## 1.10.0 (2025-02-10)
### feat
- **settings**: Provide user with the ability to set browser mode
- **core**: Parent Ticket validation added to ticket serializer
- **core**: Add to ticket endpoint the ability to filter using `parent_ticket`
- **core**: Add to ticket model a function for circular dependecy check of parent ticket
- **core**: Migrate Notes data to new table
- **project_management**: Add notes tab to Project Milestone details page
- **itam**: Add notes tab to Software Version details page
- **itam**: Add notes tab to Operating System details page
- **core**: Ensure when editing a model note, the modified user is updated.
- **assistance**: Knowledge Base Category Notes viewset
- **assistance**: Knowledge Base Category Notes Serializer
- **assistance**: Knowledge Base Category Notes Model
- **project_management**: Project Type Notes ViewSet
- **project_management**: Project Type Notes Serializer
- **project_management**: Project Type Notes Model
- **project_management**: Project State Notes ViewSet
- **project_management**: Project State Notes Serializer
- **project_management**: Project State Notes Model
- **project_management**: Project Milestone Notes ViewSet
- **project_management**: Project Milestone Notes Serializer
- **project_management**: Project Milestone Notes Model
- **itam**: Software Version Notes ViewSet
- **itam**: Software Version Notes Serializer
- **itam**: Software Version Notes Model
- **itam**: Software Category Notes ViewSet
- **itam**: Software Category Notes Serializer
- **itam**: Software Category Notes Model
- **itam**: Operating System Version Notes ViewSet
- **itam**: Operating System Version Notes Serializer
- **itam**: Operating System Version Notes Model
- **settings**: External Link Notes ViewSet
- **settings**: External Link Notes Serializer
- **settings**: External Link Notes Model
- **itam**: Device Model Notes ViewSet
- **itam**: Device Model Notes Serializer
- **itam**: Device Model Notes Model
- **itam**: Device Type Notes ViewSet
- **itam**: Device Type Notes Serializer
- **itam**: Device Type Notes Model
- **core**: Create an action comment on a ticket when the category changes
- **itim**: Porte Notes ViewSet
- **itim**: Porte Notes Serializer
- **itim**: Porte Notes Model
- **itim**: Cluster Type Notes ViewSet
- **itim**: Cluster Type Notes Serializer
- **itim**: Cluster Type Notes Model
- **project_management**: Project Notes ViewSet
- **project_management**: Project Notes Serializer
- **project_management**: Project Notes Model
- **itim**: Service Notes ViewSet
- **itim**: Service Notes Serializer
- **itim**: Service Notes Model
- **itim**: Cluster Notes ViewSet
- **itim**: Cluster Notes Serializer
- **itim**: Cluster Notes Model
- **itam**: Software Notes ViewSet
- **itam**: Software Notes Serilaizer
- **itam**: Software Notes Model
- **itam**: Operating System Notes ViewSet
- **itam**: Operating System Notes Serializer
- **itam**: Operating System Notes Model
- **core**: Manufacturer Notes viewset
- **core**: Manufacturer Notes serializer
- **core**: Manufacturer Notes Model
- **config_management**: Config Group Notes ViewSet
- **config_management**: Config Group Notes Serializer
- **config_management**: Config Group Notes Model
- **assistance**: Knowledge Base Notes ViewSet
- **assistance**: Knowledge Base Notes Serializer
- **assistance**: Knowledge Base Notes Model
- **access**: Team Notes ViewSet
- **access**: Team Notes Serializer
- **access**: Team Notes Model
- **access**: Organization Notes ViewSet
- **access**: Organization Notes Serializer
- **access**: Organization Notes Model
- **itam**: Device Notes ViewSet
- **itam**: Device Notes Serializer
- **itam**: Device Notes Model
- **core**: Base viewset for model notes
- **core**: Base serializer for model notes
- **core**: Base model for model notes
- **core**: Add failsafe to throw an exception if no action comment will be created
- **core**: Add field parent_ticket to base ticket view serializer
- **project_management**: Add field parent_ticket to project task ticket view serializer
- **itim**: Add field parent_ticket to problem ticket view serializer
- **itim**: Add field parent_ticket to incident ticket view serializer
- **itim**: Add field parent_ticket to change ticket view serializer
- **assistance**: Add field parent_ticket to request ticket view serializer
- **core**: Add field parent to ticket model
### Fixes
- **core**: Dont attempt to access parent_ticket field during ticket validation if it does not exist
- **core**: Permissions require the parent model for model notes
- **access**: field organization requires team related_model for org
- **core**: Use generic APIError for ticket save when no action comment will be created
### Refactoring
- Squash migrations so there is less of them for model notes
- **access**: Dont add releationship from tenancyObject.organization to organization model
### Tests
- **settings**: Test User Settings API render to ensure browser_model exists and is the correct type
- **settings**: Test User Settings model to ensure `browser_mode` field exists
- **access**: Team Note Model Check object requires org
- **settings**: External Links Note Model Checks
- **project_management**: Project Type Note Model Checks
- **project_management**: Project State Note Model Checks
- **project_management**: Project Note Model Checks
- **project_management**: Project Milestone Note Model Checks
- **itim**: Service Note Model Checks
- **itim**: Port Note Model Checks
- **itim**: Cluster Type Note Model Checks
- **itim**: Cluster Note Model Checks
- **itam**: Software Version Note Model Checks
- **itam**: Software Note Model Checks
- **itam**: Software Category Note Model Checks
- **itam**: Operating System Version Note Model Checks
- **itam**: Operating System Note Model Checks
- **itam**: Device Type Note Model Checks
- **itam**: Device Note Model Checks
- **itam**: Device Model Note Model Checks
- **core**: Manufacturer Note Model Checks
- **config_management**: Config Group Note Model Checks
- **assistance**: KB Note Model Checks
- **assistance**: KB Category Note Model Checks
- **access**: Team Note Model Checks
- **access**: Organization Note Model Checks
- **core**: Model Notes Test Suite
- **settings**: Serializer Checks for External Links Notes
- **Project_management**: Serializer Checks for Project Type Notes
- **Project_management**: Serializer Checks for Project State Notes
- **Project_management**: Serializer Checks for Project Notes
- **Project_management**: Serializer Checks for Project Milestone Notes
- **itim**: Serializer Checks for Service Notes
- **itim**: Serializer Checks for Port Notes
- **itim**: Serializer Checks for Cluster Type Notes
- **itim**: Serializer Checks for Cluster Notes
- **itam**: Serializer Checks for Software Version Notes
- **itam**: Serializer Checks for Software Notes
- **itam**: Serializer Checks for Software Category Notes
- **itam**: Serializer Checks for Operating System Version Notes
- **itam**: Serializer Checks for Operating System Notes
- **itam**: Serializer Checks for Device Type Notes
- **itam**: Serializer Checks for Device Notes
- **itam**: Serializer Checks for Device Model Notes
- **core**: Serializer Checks for Manufacturer Notes
- **config_management**: Serializer Checks for Config Groups Notes
- **assistance**: Serializer Checks for KB Notes
- **assistance**: Serializer Checks for KB Category Notes
- **access**: Serializer Checks for Team Notes
- **access**: Serializer Checks for Organization Notes
- **core**: Test Suite for Model Notes checks
- **api**: API Fileds user to be super user for tests to run
- **settings**: External Links Notes Function Viewset Tests
- **project_management**: Project Type Notes Function Viewset Tests
- **project_management**: Project State Notes Function Viewset Tests
- **project_management**: Project Notes Function Viewset Tests
- **project_management**: Project Milestone Notes Function Viewset Tests
- **itim**: Service Notes Function Viewset Tests
- **itim**: Port Notes Function Viewset Tests
- **itim**: Cluster Types Notes Function Viewset Tests
- **itim**: Cluster Notes Function Viewset Tests
- **itam**: Software Version Notes Function Viewset Tests
- **itam**: Software Notes Function Viewset Tests
- **itam**: Software Category Notes Function Viewset Tests
- **itam**: Operating System Version Notes Function Viewset Tests
- **itam**: Operating System Notes Function Viewset Tests
- **itam**: Device Type Notes Function Viewset Tests
- **itam**: Device Notes Function Viewset Tests
- **itam**: Device Model Notes Function Viewset Tests
- **core**: Manufacturer Notes Function Viewset Tests
- **config_management**: Config Groups Notes Function Viewset Tests
- **assistance**: Knowledge Base Notes Function Viewset Tests
- **assistance**: Knowledge Base Category Notes Function Viewset Tests
- **access**: Team Notes Function Viewset Tests
- **access**: Organization Notes Function Viewset Tests
- **core**: Model Notes Test Cases
- Remove old notes model tests
- **settings**: External Notes Test Cases for ViewSet
- **project_management**: Project Type Notes Test Cases for ViewSet
- **project_management**: Project State Notes Test Cases for ViewSet
- **project_management**: Project Notes Test Cases for ViewSet
- **project_management**: Project Milestone Notes Test Cases for ViewSet
- **itim**: Service Notes Test Cases for ViewSet
- **itim**: Port Notes Test Cases for ViewSet
- **itim**: Cluster Type Notes Test Cases for ViewSet
- **itim**: Cluster Notes Test Cases for ViewSet
- **itam**: Software Version Notes Test Cases for ViewSet
- **itam**: Software Notes Test Cases for ViewSet
- **itam**: Software Category Notes Test Cases for ViewSet
- **itam**: Operating System Version Notes Test Cases for ViewSet
- **itam**: Operating_system Notes Test Cases for ViewSet
- **itam**: Device Type Notes Test Cases for ViewSet
- **itam**: Device Notes Test Cases for ViewSet
- **itam**: Device Model Notes Test Cases for ViewSet
- **core**: Manufacturer Notes Test Cases for ViewSet
- **config_management**: Config Groups Notes Test Cases for ViewSet
- **assistance**: Knowledge Base Notes Test Cases for ViewSet
- **assistance**: Knowledge Base Category Notes Test Cases for ViewSet
- **access**: Team Notes Test Cases for ViewSet
- **access**: Organization Notes Test Cases for ViewSet
- **project_management**: Correct kwargs for Project Milestone Notes Test Cases for API Field Checks
- **assistance**: Knowledge Base Category Notes Test Cases for API Field Checks
- **Settings**: External Link Notes Test Cases for API Field Checks
- **project_management**: Project Type Notes Test Cases for API Field Checks
- **project_management**: Project State Notes Test Cases for API Field Checks
- **project_management**: Project Notes Test Cases for API Field Checks
- **project_management**: Project Milestone Notes Test Cases for API Field Checks
- **itim**: Service Notes Test Cases for API Field Checks
- **itim**: Port Notes Test Cases for API Field Checks
- **itim**: Cluster Types Notes Test Cases for API Field Checks
- **itim**: Cluster Notes Test Cases for API Field Checks
- **itam**: Software Version Notes Test Cases for API Field Checks
- **itam**: Software Notes Test Cases for API Field Checks
- **itam**: Software Category Notes Test Cases for API Field Checks
- **itam**: Device Type Notes Test Cases for API Field Checks
- **itam**: Device Notes Test Cases for API Field Checks
- **itam**: Device Model Notes Test Cases for API Field Checks
- **itam**: Operating System Test Cases for API Field Checks
- **itam**: Operating System Version Test Cases for API Field Checks
- **core**: Manufacturer Test Cases for API Field Checks
- **config_management**: Config Group Test Cases for API Field Checks
- **assistance**: KB Test Cases for API Field Checks
- **access**: Team Test Cases for API Field Checks
- **access**: Organization Test Cases for API Field Checks
- **core**: Model Notes Base Test Cases for API Field Checks
- remove old notes model tests
- Update url_name to match new notes endpoint
- **config_Management**: Update url_name to match new notes endpoint
- **core**: Remove notes test cases for previous notes model
## 1.9.0 (2025-02-06)
### feat
- **core**: Validate user field to ensure ticket comments always have user who added comment
- **core**: Cache ticket linked item queryset
- Views to cache discovered serializer
- **core**: When changing ticket description create an action comment with the details
- **core**: When changing a ticket real finish date create an action comment with the details
- **core**: When changing a ticket real start date create an action comment with the details
- **core**: When changing a ticket planned finish date create an action comment with the details
- **core**: When changing a ticket planned start date create an action comment with the details
- **core**: When changing a ticket milestone create an action comment with the changed details
- **core**: Add Priority badge field to ALL ticket types
- **core**: Add Impact badge field to ALL ticket types
- **core**: Add urgency badge field to ALL ticket types
### Fixes
- **project_management**: Add missing attribute `view_description` to project tasks viewset
- **settings**: Add missing attribute `view_description` to user settings viewset
- **settings**: Add missing attribute `view_description` to app settings viewset
- **itim**: Add missing attribuite to problem ticket viewset
- **itim**: Add missing attribuite to incident ticket viewset
- **itim**: Add missing attribuite to change ticket viewset
- **itimm**: correct truthy check for service device ViewSet property when evaluating queryset
- **itam**: correct truthy check for service cluster ViewSet property when evaluating queryset
- **itam**: correct truthy check for service cluster ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for service device ViewSet property when evaluating serializer_class
- **itam**: add missing attribute view_name to celery log viewset
- **api**: correct get_view_name to prioritize view_name over model.verbose_name
- **itam**: correct truthy check for software version ViewSet property when evaluating queryset
- **itam**: correct truthy check for os version ViewSet property when evaluating queryset
- **itam**: correct truthy check for device software ViewSet property when evaluating queryset
- **itam**: correct truthy check for device os ViewSet property when evaluating queryset
- **itam**: correct truthy check for software version ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for software category ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for os version ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for device software ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for device os ViewSet property when evaluating serializer_class
- **core**: correct varname for queryset within notes queryset
- **core**: add missing attribute view_description to ticket linked item viewset
- **core**: add missing attribute view_description to ticket comment viewset
- **core**: add missing attribute view_description to note viewset
- **core**: add missing attribute view_description to related ticket log viewset
- **core**: add missing attribute view_description to celery log viewset
- **core**: add missing attribute view_description to history viewset
- **core**: correct truthy check for notes ViewSet property when evaluating serializer_class
- **core**: correct truthy check for history ViewSet property when evaluating queryset
- **core**: correct truthy check for notes ViewSet property when evaluating queryset
- **core**: correct truthy check for related ticket ViewSet property when evaluating queryset
- **core**: correct truthy check for ticket comment ViewSet property when evaluating queryset
- **core**: correct truthy check for ticket linked items ViewSet property when evaluating queryset
- **core**: correct truthy check for ticket linked items ViewSet property when evaluating queryset
- **core**: correct truthy check for ticket comment ViewSet property when evaluating queryset
- **core**: correct truthy check for related ticket ViewSet property when evaluating queryset
- **core**: correct truthy check for history ViewSet property when evaluating queryset
- **core**: correct truthy check for celery log ViewSet property when evaluating queryset
- **core**: correct truthy check for Ticket Base ViewSet property when evaluating queryset
- **core**: correct truthy check for ticket base ViewSet property when evaluating serializer_class
- **assistance**: Add missing attribute `view_description` to request ticket ViewSet
- **settinggs**: Add missing attribute `view_description` to external links ViewSet
- **core**: Add missing attribute `view_description` to ticket comment category ViewSet
- **core**: Add missing attribute `view_description` to ticekt category ViewSet
- **core**: Add missing attribute `view_description` to Manufacturer ViewSet
- **project_management**: correct truthy check for project milestone ViewSet property when evaluating queryset
- **settings**: correct truthy check for user settings ViewSet property when evaluating serializer_class
- **settings**: correct truthy check for external links ViewSet property when evaluating serializer_class
- **settings**: correct truthy check for app settings ViewSet property when evaluating serializer_class
- **project_management**: correct truthy check for project milestone ViewSet property when evaluating queryset
- **project_management**: correct truthy check for project ViewSet property when evaluating serializer_class
- **project_management**: correct truthy check for project type ViewSet property when evaluating serializer_class
- **project_management**: correct truthy check for project state ViewSet property when evaluating serializer_class
- **project_management**: correct truthy check for project milestone ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for service ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for port ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for cluster ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for cluster type ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for software ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for os ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for device ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for device type ViewSet property when evaluating serializer_class
- **itam**: correct truthy check for device model ViewSet property when evaluating serializer_class
- **core**: correct truthy check for ticket comment category ViewSet property when evaluating serializer_class
- **core**: correct truthy check for ticket category ViewSet property when evaluating serializer_class
- **core**: correct truthy check for manufacturer ViewSet property when evaluating serializer_class
- **config_management**: correct truthy check for config group ViewSet property when evaluating queryset
- **config_management**: correct truthy check for config group ViewSet property when evaluating serializer_class
- **config_management**: correct truthy check for config group software ViewSet property when evaluating serializer_class
- **config_management**: correct truthy check for config group software ViewSet property when evaluating queryset
- **assistance**: correct truthy check for model kb article ViewSet property when evaluating queryset
- **assistance**: correct truthy check for model kb article ViewSet property when evaluating serializer_class
- **assistance**: correct truthy check for kb ViewSet property when evaluating serializer_class
- **assistance**: correct truthy check for kb ViewSet property when evaluating serializer_class
- **access**: correct truthy check for team ViewSet property when evaluating serializer_class
- **access**: correct truthy check for team ViewSet property when evaluating queryset
- **access**: correct truthy check for team user ViewSet property when evaluating queryset
- **access**: correct truthy check for team user ViewSet property when evaluating serializer_class
- **access**: correct truthy check for organization ViewSet property when evaluating serializer_class
- **api**: correct truthy check for set property when evaluating serializer_class
- **api**: correct truthy check for set property when evaluating queryset
- **api**: correct variable name in common viewset for queryset
- **config_management**: config group software viewset must cache queryset
- **config_management**: config group viewset must cache queryset
- **assistance**: Knowledge base category viewset must cache serializer_class
- **access**: Team viewset must cache serializer_class
- **access**: Team viewset must cach queryset
- **access**: Team User viewset must cach queryset
- **api**: Add missing property `bacjk_url` to Common viewset
- **api**: Common viewset to cache and use queryset Object
- **access**: When conduting permission check for user settings, if user not owner of settings, deny access
- **access**: when checking object permissions, dont cast obj to int untill checking it exists
- **access**: org mixin get_obj_org not to call get_object
- **core**: ensure item_type exists before trying to get queryset
- **core**: Ticket Action comment date fields must be checked if empty before use
- **settings**: grant the user access to their own settings object
- **settings**: grant the user access to their own settings
### Refactoring
- **access**: when checking obj permission use view cached obj organization
- **access**: When fetching obj org, if pk exist attempt to fetch object
- **core**: When fetching a ticket, fetch related fields
- **core**: Ticket action comment for changing milestone to use item tasg
- **core**: Ticket action comment for changing project to use item tasg
### Tests
- **core**: Add missing unit tests for notes ticket viewset
- **settings**: Add missing unit tests for user settings ticket viewset
- **settings**: Add missing unit tests for app settings ticket viewset
- **project_management**: Add missing unit tests for project task ticket viewset
- **api**: Add kwargs as arg to test cases
- **itim**: Add missing unit tests for problem ticket viewset
- **itim**: Add missing unit tests for incident ticket viewset
- **itim**: Add missing unit tests for change ticket viewset
- **core**: add permisssion class override test case for celery results
- Add empty kwargs to ViewSet index page test cases
- **itam**: Add missing unit tests for software version viewset
- **itam**: Add missing unit tests for software categories viewset
- **itam**: Add missing unit tests for os versions viewset
- **itam**: Add missing unit tests for software installs viewset
- **itam**: Add missing unit tests for os installs viewset
- **itam**: Add missing unit tests for device software viewset
- **itam**: Add missing unit tests for device operating system viewset
- **api**: Add kwargs as arg to test cases
- **core**: Add missing unit tests for ticket linked items viewset
- **core**: Add missing unit tests for ticket comment viewset
- **core**: Add missing unit tests for celery log viewset
- **core**: Add missing unit tests for history viewset
- **core**: Add missing unit tests for related tickets viewset
- **assistance**: Add missing unit tests for request ticket viewset
- **api**: queryset and serializer_class test cases updated to use Fake request object
- **settings**: Add missing unit tests for external links viewset
- **project_management**: Add missing unit tests for project type viewset
- **project_management**: Add missing unit tests for project state viewset
- **project_management**: Add missing unit tests for project milestone viewset
- **project_management**: Add missing unit tests for project viewset
- **itim**: Add missing unit tests for service viewset
- **itim**: Add missing unit tests for ports viewset
- **itim**: Add missing unit tests for cluster types viewset
- **itim**: Add missing unit tests for cluster viewset
- **itam**: Add missing unit tests for Software viewset
- **itam**: Add missing unit tests for Operating System viewset
- **itam**: Add missing unit tests for Device Type viewset
- **itam**: Add missing unit tests for Device Model viewset
- **itam**: Add missing unit tests for Device viewset
- **api**: dont mock the qs bool
- **core**: Add missing unit tests for Ticket Comment Category viewset
- **core**: Add missing unit tests for Ticket Category viewset
- **core**: Add missing unit tests for manufacturer viewset
- **config_management**: Add missing unit tests for config groups software viewset
- **config_management**: Add missing unit tests for config groups viewset
- **assistance**: Add missing unit tests for Model Knowledge Base Article viewset
- **assistance**: Add missing unit tests for Knowledge Base Category viewset
- **assistance**: Add missing unit tests for Knowledge Base viewset
- **access**: Add missing unit tests for team user viewset
- **access**: Add missing unit tests for team viewset
- **access**: Add missing unit tests for organization viewset
- **base**: Ensure viewsets are caching and using the serializer_class object
- **base**: Ensure viewsets are caching and using the queryset object
- **core**: Test case to ensure ticket comment always has user added
- **settings**: when checking if user can delete own settings, user must be owner of settings
- **settings**: regardless of permissions a user can change their own settings
- **access**: during permission check function has_permission ensure `get_object` not called
- **core**: Ensure that an action comment is created when ticket description is edited
- **core**: Ticket Action comment test cases for real_finish_date actions
- **core**: Ticket Action comment test cases for real_start_date actions
- **core**: Ticket Action comment test cases for planned_finish_date actions
- **core**: Ticket Action comment test cases for planned_start_date actions
- **core**: Ticket Action comment test cases for milestone actions
- **core**: Ticket Action comment test cases for project actions
- **core**: Ticket Action comment tests moved to their own suite
- **core**: Unit test cases for ticket urgency_badge field checks
- **core**: Unit test cases for ticket priority_badge field checks
- **core**: Unit test cases for ticket impact_badge field checks
- **settings**: Remove no-permission failure test as user settings require no permissions
## 1.8.0 (2025-01-23)
### feat

View File

@ -95,6 +95,34 @@ clear; \
```
## Tips / Handy info
- To obtain a list of models _(in in the same order as the file system)_ using the db shell `python3 manage.py dbshell` run the following sql command:
``` sql
SELECT model FROM django_content_type ORDER BY app_label ASC, model ASC;
```
# Old working docs

View File

@ -1,3 +1,71 @@
## Version 1.11.0
**Note:** Migrations should be performed offline. **Failing to perform** an online migration, the option provided below will not be available if the migration crashes. Running the below commands to reset the database for the migrations to re-run will cause data loss if users are making changes to Centurion.
- History views removed from original Centurion interface.
- History views removed from API v1.
- A migration exists that will move the history from the old tables to the new ones.
if for some reason the migration crashes enter the following commands in the dbshell `python manage.py dbshell` and restart the migrations
``` sql
delete from access_organization_history;
delete from access_team_history;
delete from assistance_knowledge_base_history;
delete from assistance_knowledge_base_category_history;
delete from config_management_configgroups_history;
delete from config_management_configgroupsoftware_history;
delete from config_management_configgrouphosts_history;
delete from core_manufacturer_history;
delete from core_ticketcategory_history;
delete from core_ticketcommentcategory_history;
delete from itam_device_history;
delete from itam_devicemodel_history;
delete from itam_devicetype_history;
delete from itam_deviceoperatingsystem_history;
delete from itam_devicesoftware_history;
delete from itam_operatingsystem_history;
delete from itam_operatingsystemversion_history;
delete from itam_software_history;
delete from itam_softwareversion_history;
delete from itam_softwarecategory_history;
delete from itim_cluster_history;
delete from itim_clustertype_history;
delete from itim_port_history;
delete from itim_service_history;
delete from project_management_project_history;
delete from project_management_projectmilestone_history;
delete from project_management_projectstate_history;
delete from project_management_projecttype_history;
delete from settings_externallink_history;
delete from core_model_history;
```
The above commands truncate the data from the new history tables so the migration can run again.
## Version 1.10.0
- Nothing significant to report
## Version 1.9.0
- Nothing significant to report
## Version 1.8.0
- Prometheus exporter added. To enable metrics for the database you will have to update the database backend. see the [docs](https://nofusscomputing.com/projects/centurion_erp/administration/monitoring/#django-exporter-setup) for further information.

View File

@ -2,7 +2,10 @@ from django.contrib import admin
from django.contrib.auth.models import Group, User
from django.contrib.auth.admin import UserAdmin
from .models import *
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
admin.site.unregister(Group)

View File

@ -3,7 +3,7 @@ from django.db.models import Q
from app import settings
from access.models import Organization
from access.models.organization import Organization
from core.forms.common import CommonModelForm

View File

@ -4,7 +4,7 @@ from django.forms import inlineformset_factory
from .team_users import TeamUsersForm, TeamUsers
from access.models import Team
from access.models.team import Team
from access.functions import permissions
from app import settings

View File

@ -2,7 +2,7 @@ from django.db.models import Q
from app import settings
from access.models import TeamUsers
from access.models.team_user import TeamUsers
from core.forms.common import CommonModelForm

View File

@ -24,16 +24,22 @@ def permission_queryset():
'chordcounter',
'comment',
'groupresult',
'history',
'modelnotes',
'usersettings',
]
exclude_permissions = [
'add_history',
'add_organization',
'add_taskresult',
'change_history',
'change_organization',
'change_taskresult',
'delete_history',
'delete_organization',
'delete_taskresult',
'view_history',
]
return Permission.objects.filter(

View File

@ -6,7 +6,10 @@ from django.contrib.auth.middleware import (
from django.contrib.auth.models import User, Group
from django.utils.deprecation import MiddlewareMixin
from access.models import Organization, Team
from access.models.organization import Organization
from access.models.team import Team
from settings.models.app_settings import AppSettings

View File

@ -44,7 +44,7 @@ class Migration(migrations.Migration):
('team_name', models.CharField(default='', max_length=50, verbose_name='Name')),
('created', access.fields.AutoCreatedField(default=django.utils.timezone.now, editable=False)),
('modified', access.fields.AutoLastModifiedField(default=django.utils.timezone.now, editable=False)),
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.TenancyObject.validatate_organization_exists])),
('organization', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists])),
],
options={
'verbose_name_plural': 'Teams',

View File

@ -59,7 +59,7 @@ class Migration(migrations.Migration):
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'),
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.tenancy.TenancyObject.validatate_organization_exists], verbose_name='Organization'),
),
migrations.AlterField(
model_name='team',

View File

@ -0,0 +1,49 @@
# Generated by Django 5.1.5 on 2025-02-09 11:07
import access.models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0002_alter_organization_options_alter_team_options_and_more'),
('core', '0012_modelnotes'),
]
operations = [
migrations.AlterField(
model_name='team',
name='organization',
field=models.ForeignKey(help_text='Organization this belongs to', on_delete=django.db.models.deletion.CASCADE, to='access.organization', validators=[access.models.team.Team.validatate_organization_exists], verbose_name='Organization'),
),
migrations.CreateModel(
name='OrganizationNotes',
fields=[
('modelnotes_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelnotes')),
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='access.organization', verbose_name='Model')),
],
options={
'verbose_name': 'Organization Note',
'verbose_name_plural': 'Organization Notes',
'db_table': 'access_organization_notes',
'ordering': ['-created'],
},
bases=('core.modelnotes',),
),
migrations.CreateModel(
name='TeamNotes',
fields=[
('modelnotes_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelnotes')),
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='notes', to='access.team', verbose_name='Model')),
],
options={
'verbose_name': 'Team Note',
'verbose_name_plural': 'Team Notes',
'db_table': 'access_team_notes',
'ordering': ['-created'],
},
bases=('core.modelnotes',),
),
]

View File

@ -0,0 +1,43 @@
# Generated by Django 5.1.5 on 2025-02-20 13:25
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('access', '0003_alter_team_organization_organizationnotes_teamnotes'),
('core', '0015_modelhistory_manufacturerhistory_and_more'),
]
operations = [
migrations.CreateModel(
name='OrganizationHistory',
fields=[
('modelhistory_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelhistory')),
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='history', to='access.organization', verbose_name='Model')),
],
options={
'verbose_name': 'Organization History',
'verbose_name_plural': 'Organization History',
'db_table': 'access_organization_history',
'ordering': ['-created'],
},
bases=('core.modelhistory',),
),
migrations.CreateModel(
name='TeamHistory',
fields=[
('modelhistory_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='core.modelhistory')),
('model', models.ForeignKey(help_text='Model this note belongs to', on_delete=django.db.models.deletion.CASCADE, related_name='history', to='access.team', verbose_name='Model')),
],
options={
'verbose_name': 'Team History',
'verbose_name_plural': 'Team History',
'db_table': 'access_team_history',
'ordering': ['-created'],
},
bases=('core.modelhistory',),
),
]

View File

@ -4,7 +4,9 @@ from django.contrib.auth.models import Group
from django.core.exceptions import PermissionDenied
from django.utils.functional import cached_property
from .models import Organization, Team
from access.models.organization import Organization
from access.models.team import Team
class OrganizationMixin():

View File

@ -1,5 +1,9 @@
from django.contrib.auth.models import User, Group
from access.models import Organization, Team
from django.db import models
from access.models.organization import Organization
from access.models.team import Team
class OrganizationMixin:
@ -38,19 +42,19 @@ class OrganizationMixin:
return self._obj_organization
_obj_organization: Organization = None
if obj:
_obj_organization = getattr(obj, 'organization', None)
self._obj_organization = getattr(obj, 'organization', None)
if not _obj_organization:
if not self._obj_organization:
_obj_organization = getattr(obj, 'get_organization', lambda: None)()
self._obj_organization = getattr(obj, 'get_organization', lambda: None)()
elif request:
elif (
request
and not self.kwargs.get('pk', None)
):
if getattr(request.stream, 'method', '') != 'DELETE':
@ -72,23 +76,31 @@ class OrganizationMixin:
if data_organization:
_obj_organization = Organization.objects.get(
self._obj_organization = Organization.objects.get(
pk = int( data_organization )
)
elif self.kwargs.get('pk', None):
obj = self.model.objects.get( pk = self.kwargs.get('pk', None) )
if getattr(obj, 'organization', None):
self._obj_organization = obj.organization
elif str(self.model._meta.verbose_name).lower() == 'organization':
self._obj_organization = obj
if self.get_parent_model(): # if defined is to overwrite object organization
parent_obj = self.get_parent_obj()
_obj_organization = parent_obj.get_organization()
self._obj_organization = parent_obj.get_organization()
if _obj_organization:
self._obj_organization = _obj_organization
return self._obj_organization
@ -245,7 +257,7 @@ class OrganizationMixin:
parent_model: str = None
parent_model: models.Model = None
""" Parent Model
This attribute defines the parent model for the model in question. The parent model when defined

View File

@ -5,7 +5,7 @@ from django.core.exceptions import ObjectDoesNotExist
from rest_framework import exceptions
from rest_framework.permissions import DjangoObjectPermissions
from access.models import TenancyObject
from access.models.tenancy import Organization, TenancyObject
from core import exceptions as centurion_exceptions
@ -115,6 +115,33 @@ class OrganizationPermissionMixin(
try:
if (
(
view.model.__name__ == 'UserSettings'
and request._user.id == int(view.kwargs.get('pk', 0))
)
or (
view.model.__name__ == 'AuthToken'
and request._user.id == int(view.kwargs.get('model_id', 0))
)
):
return True
elif (
(
view.model.__name__ == 'UserSettings'
and request._user.id != int(view.kwargs.get('pk', 0))
)
or (
view.model.__name__ == 'AuthToken'
and request._user.id != int(view.kwargs.get('model_id', 0))
)
):
return False
has_permission_required: bool = False
@ -159,10 +186,6 @@ class OrganizationPermissionMixin(
view_action = 'delete'
obj_organization: Organization = view.get_obj_organization(
obj = view.get_object()
)
elif (
view.action == 'list'
):
@ -176,10 +199,6 @@ class OrganizationPermissionMixin(
view_action = 'change'
obj_organization: Organization = view.get_obj_organization(
obj = view.get_object()
)
elif (
view.action == 'update'
and request.method == 'PUT'
@ -187,10 +206,6 @@ class OrganizationPermissionMixin(
view_action = 'change'
obj_organization: Organization = view.get_obj_organization(
obj = view.get_object()
)
elif(
view.action == 'retrieve'
and request.method == 'GET'
@ -198,10 +213,6 @@ class OrganizationPermissionMixin(
view_action = 'view'
obj_organization: Organization = view.get_obj_organization(
obj = view.get_object()
)
elif(
view.action == 'metadata'
and request.method == 'OPTIONS'
@ -265,17 +276,32 @@ class OrganizationPermissionMixin(
try:
if request.user.is_anonymous:
return False
object_organization: int = getattr(view.get_obj_organization( obj = obj ), 'id', None)
if (
(
view.model.__name__ == 'UserSettings'
and request._user.id == int(view.kwargs.get('pk', 0))
)
or (
view.model.__name__ == 'AuthToken'
and request._user.id == int(view.kwargs.get('model_id', 0))
)
):
return True
object_organization = view._obj_organization
if object_organization:
if(
object_organization
int(object_organization)
in view.get_permission_organizations( view.get_permission_required() )
or request.user.is_superuser
or getattr(request.app_settings.global_organization, 'id', 0) == int(object_organization)

View File

@ -1,594 +0,0 @@
from django.conf import settings
from django.db import models
from django.contrib.auth.models import User, Group, Permission
from rest_framework.reverse import reverse
from .fields import *
from core import exceptions as centurion_exceptions
from core.middleware.get_request import get_request
from core.mixin.history_save import SaveHistory
class Organization(SaveHistory):
class Meta:
verbose_name = "Organization"
verbose_name_plural = "Organizations"
ordering = ['name']
def save(self, *args, **kwargs):
if self.slug == '_':
self.slug = self.name.lower().replace(' ', '_')
super().save(*args, **kwargs)
id = models.AutoField(
blank=False,
help_text = 'ID of this item',
primary_key=True,
unique=True,
verbose_name = 'ID'
)
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'
)
model_notes = models.TextField(
blank = True,
default = None,
help_text = 'Tid bits of information',
null= True,
verbose_name = 'Notes',
)
slug = AutoSlugField()
created = AutoCreatedField()
modified = AutoLastModifiedField()
def get_organization(self):
return self
def __int__(self):
return self.id
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):
"""Multi-Tennant Object Manager
This manager specifically caters for the multi-tenancy features of Centurion ERP.
"""
def get_queryset(self):
""" Fetch the data
This function filters the data fetched from the database to that which is from the organizations
the user is a part of.
!!! danger "Requirement"
This method may be overridden however must still be called from the overriding function. i.e. `super().get_queryset()`
## Workflow
This functions workflow is as follows:
- Fetch the user from the request
- Check if the user is authenticated
- Iterate over the users teams
- Store unique organizations from users teams
- return results
Returns:
(queryset): **super user**: return unfiltered data.
(queryset): **not super user**: return data from the stored unique organizations.
"""
request = get_request()
user_organizations: list(str()) = []
if request:
if request.app_settings.global_organization:
user_organizations += [ request.app_settings.global_organization.id ]
user = request.user
if user.is_authenticated:
for team in request.tenancy._user_teams:
if team.organization.id not in user_organizations:
if not user_organizations:
self.user_organizations = []
user_organizations += [ 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()
class TenancyObject(SaveHistory):
""" Tenancy Model Abstrct class.
This class is for inclusion wihtin **every** model within Centurion ERP.
Provides the required fields, functions and methods for multi tennant objects.
Unless otherwise stated, **no** object within this class may be overridden.
Raises:
ValidationError: User failed to supply organization
"""
objects = TenancyManager()
""" Multi-Tenanant Objects """
class Meta:
abstract = True
def validatate_organization_exists(self):
"""Ensure that the user did provide an organization
Raises:
ValidationError: User failed to supply organization.
"""
if not self:
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,
blank = False,
help_text = 'Organization this belongs to',
null = False,
on_delete = models.CASCADE,
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'
)
model_notes = models.TextField(
blank = True,
default = None,
help_text = 'Tid bits of information',
null = True,
verbose_name = 'Notes',
)
def get_organization(self) -> Organization:
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 not getattr(self, 'organization', None):
raise centurion_exceptions.ValidationError(
detail = {
'organization': 'Organization is required'
},
code = 'required'
)
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'
verbose_name_plural = "Teams"
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
self.name = self.organization.name.lower().replace(' ', '_') + '_' + self.team_name.lower().replace(' ', '_')
super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
team_name = models.CharField(
blank = False,
help_text = 'Name to give this team',
max_length = 50,
unique = False,
verbose_name = 'Name',
)
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):
""" Fetch the parent object """
return self.organization
def permission_list(self) -> list:
permission_list = []
for permission in self.permissions.all():
if str(permission.content_type.app_label + '.' + permission.codename) in permission_list:
continue
permission_list += [ str(permission.content_type.app_label + '.' + permission.codename) ]
return [permission_list, self.permissions.all()]
def __str__(self):
return self.organization.name + ', ' + self.team_name
class TeamUsers(SaveHistory):
class Meta:
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'
)
team = models.ForeignKey(
Team,
blank = False,
help_text = 'Team user belongs to',
null = False,
on_delete=models.CASCADE,
related_name="team",
verbose_name = 'Team'
)
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'
)
manager = models.BooleanField(
blank=True,
default=False,
help_text = 'Is this user to be a manager of this team',
verbose_name='manager',
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
page_layout: list = []
table_fields: list = [
'user',
'manager'
]
def delete(self, using=None, keep_parents=False):
""" Delete Team
Overrides, post-action
As teams are an extension of Groups, remove the user to the team.
"""
super().delete(using=using, keep_parents=keep_parents)
group = Group.objects.get(pk=self.team.id)
user = User.objects.get(pk=self.user_id)
user.groups.remove(group)
def get_organization(self) -> Organization:
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
Overrides, post-action
As teams are an extension of groups, add the user to the matching group.
"""
super().save(*args, **kwargs)
group = Group.objects.get(pk=self.team.id)
user = User.objects.get(pk=self.user_id)
user.groups.add(group)
@property
def parent_object(self):
""" Fetch the parent object """
return self.team
def __str__(self):
return self.user.username

View File

View File

@ -0,0 +1,155 @@
from django.db import models
from django.contrib.auth.models import User
from rest_framework.reverse import reverse
from access.fields import (
AutoCreatedField,
AutoLastModifiedField,
AutoSlugField
)
from core.mixin.history_save import SaveHistory
class Organization(SaveHistory):
class Meta:
verbose_name = "Organization"
verbose_name_plural = "Organizations"
ordering = ['name']
def save(self, *args, **kwargs):
if self.slug == '_':
self.slug = self.name.lower().replace(' ', '_')
super().save(*args, **kwargs)
id = models.AutoField(
blank=False,
help_text = 'ID of this item',
primary_key=True,
unique=True,
verbose_name = 'ID'
)
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'
)
model_notes = models.TextField(
blank = True,
default = None,
help_text = 'Tid bits of information',
null= True,
verbose_name = 'Notes',
)
slug = AutoSlugField()
created = AutoCreatedField()
modified = AutoLastModifiedField()
def get_organization(self):
return self
def __int__(self):
return self.id
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})
def save_history(self, before: dict, after: dict) -> bool:
from access.models.organization_history import OrganizationHistory
history = super().save_history(
before = before,
after = after,
history_model = OrganizationHistory
)
return history

View File

@ -0,0 +1,53 @@
from django.db import models
from core.models.model_history import ModelHistory
from access.models.organization import Organization
class OrganizationHistory(
ModelHistory
):
class Meta:
db_table = 'access_organization_history'
ordering = ModelHistory._meta.ordering
verbose_name = 'Organization History'
verbose_name_plural = 'Organization History'
model = models.ForeignKey(
Organization,
blank = False,
help_text = 'Model this note belongs to',
null = False,
on_delete = models.CASCADE,
related_name = 'history',
verbose_name = 'Model',
)
table_fields: list = []
page_layout: dict = []
def get_object(self):
return self
def get_serialized_model(self, serializer_context):
model = None
from access.serializers.organization import OrganizationBaseSerializer
model = OrganizationBaseSerializer(self.model, context = serializer_context)
return model

View File

@ -0,0 +1,45 @@
from django.db import models
from access.models.organization import Organization
from core.models.model_notes import ModelNotes
class OrganizationNotes(
ModelNotes
):
class Meta:
db_table = 'access_organization_notes'
ordering = ModelNotes._meta.ordering
verbose_name = 'Organization Note'
verbose_name_plural = 'Organization Notes'
model = models.ForeignKey(
Organization,
blank = False,
help_text = 'Model this note belongs to',
null = False,
on_delete = models.CASCADE,
related_name = 'notes',
verbose_name = 'Model',
)
table_fields: list = []
page_layout: dict = []
def get_url_kwargs(self) -> dict:
return {
'model_id': self.model.pk,
'pk': self.pk
}

175
app/access/models/team.py Normal file
View File

@ -0,0 +1,175 @@
from django.db import models
from django.contrib.auth.models import Group
from rest_framework.reverse import reverse
from access.fields import (
AutoCreatedField,
AutoLastModifiedField
)
from access.models.organization import Organization
from access.models.tenancy import TenancyObject
from core import exceptions as centurion_exceptions
class Team(Group, TenancyObject):
class Meta:
ordering = [ 'team_name' ]
verbose_name = 'Team'
verbose_name_plural = "Teams"
def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
self.name = self.organization.name.lower().replace(' ', '_') + '_' + self.team_name.lower().replace(' ', '_')
super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
def validatate_organization_exists(self):
"""Ensure that the user did provide an organization
Raises:
ValidationError: User failed to supply organization.
"""
if not self:
raise centurion_exceptions.ValidationError('You must provide an organization')
team_name = models.CharField(
blank = False,
help_text = 'Name to give this team',
max_length = 50,
unique = False,
verbose_name = 'Name',
)
organization = models.ForeignKey(
Organization,
blank = False,
help_text = 'Organization this belongs to',
null = False,
on_delete = models.CASCADE,
validators = [validatate_organization_exists],
verbose_name = 'Organization'
)
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:
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):
# """ Fetch the parent object """
# return self.organization
def permission_list(self) -> list:
permission_list = []
for permission in self.permissions.all():
if str(permission.content_type.app_label + '.' + permission.codename) in permission_list:
continue
permission_list += [ str(permission.content_type.app_label + '.' + permission.codename) ]
return [permission_list, self.permissions.all()]
def __str__(self):
return self.organization.name + ', ' + self.team_name
def save_history(self, before: dict, after: dict) -> bool:
from access.models.team_history import TeamHistory
history = super().save_history(
before = before,
after = after,
history_model = TeamHistory
)
return history

View File

@ -0,0 +1,53 @@
from django.db import models
from core.models.model_history import ModelHistory
from access.models.team import Team
class TeamHistory(
ModelHistory
):
class Meta:
db_table = 'access_team_history'
ordering = ModelHistory._meta.ordering
verbose_name = 'Team History'
verbose_name_plural = 'Team History'
model = models.ForeignKey(
Team,
blank = False,
help_text = 'Model this note belongs to',
null = False,
on_delete = models.CASCADE,
related_name = 'history',
verbose_name = 'Model',
)
table_fields: list = []
page_layout: dict = []
def get_object(self):
return self
def get_serialized_model(self, serializer_context):
model = None
from access.serializers.teams import TeamBaseSerializer
model = TeamBaseSerializer(self.model, context = serializer_context)
return model

View File

@ -0,0 +1,54 @@
from django.db import models
from rest_framework.reverse import reverse
from access.models.team import Team
from core.models.model_notes import ModelNotes
class TeamNotes(
ModelNotes
):
class Meta:
db_table = 'access_team_notes'
ordering = ModelNotes._meta.ordering
verbose_name = 'Team Note'
verbose_name_plural = 'Team Notes'
model = models.ForeignKey(
Team,
blank = False,
help_text = 'Model this note belongs to',
null = False,
on_delete = models.CASCADE,
related_name = 'notes',
verbose_name = 'Model',
)
table_fields: list = []
page_layout: dict = []
def get_url( self, request = None ) -> str:
kwargs = {
'organization_id': self.organization.pk,
'model_id': self.model.pk,
'pk': self.pk
}
if request:
return reverse("v2:_api_v2_organization_team_note-detail", request=request, kwargs = kwargs )
return reverse("v2:_api_v2_organization_team_note-detail", kwargs = kwargs )

View File

@ -0,0 +1,137 @@
from django.conf import settings
from django.db import models
from django.contrib.auth.models import User, Group
from rest_framework.reverse import reverse
from access.fields import (
AutoCreatedField,
AutoLastModifiedField
)
from access.models.organization import Organization
from access.models.team import Team
from core.mixin.history_save import SaveHistory
class TeamUsers(SaveHistory):
class Meta:
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'
)
team = models.ForeignKey(
Team,
blank = False,
help_text = 'Team user belongs to',
null = False,
on_delete=models.CASCADE,
related_name="team",
verbose_name = 'Team'
)
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'
)
manager = models.BooleanField(
blank=True,
default=False,
help_text = 'Is this user to be a manager of this team',
verbose_name='manager',
)
created = AutoCreatedField()
modified = AutoLastModifiedField()
page_layout: list = []
table_fields: list = [
'user',
'manager'
]
def delete(self, using=None, keep_parents=False):
""" Delete Team
Overrides, post-action
As teams are an extension of Groups, remove the user to the team.
"""
super().delete(using=using, keep_parents=keep_parents)
group = Group.objects.get(pk=self.team.id)
user = User.objects.get(pk=self.user_id)
user.groups.remove(group)
def get_organization(self) -> Organization:
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
Overrides, post-action
As teams are an extension of groups, add the user to the matching group.
"""
super().save(*args, **kwargs)
group = Group.objects.get(pk=self.team.id)
user = User.objects.get(pk=self.user_id)
user.groups.add(group)
@property
def parent_object(self):
""" Fetch the parent object """
return self.team
def __str__(self):
return self.user.username

View File

@ -0,0 +1,219 @@
# from django.conf import settings
from django.db import models
# from django.contrib.auth.models import User, Group
from rest_framework.reverse import reverse
# from .fields import *
from access.models.organization import Organization
from core import exceptions as centurion_exceptions
from core.middleware.get_request import get_request
from core.mixin.history_save import SaveHistory
class TenancyManager(models.Manager):
"""Multi-Tennant Object Manager
This manager specifically caters for the multi-tenancy features of Centurion ERP.
"""
def get_queryset(self):
""" Fetch the data
This function filters the data fetched from the database to that which is from the organizations
the user is a part of.
!!! danger "Requirement"
This method may be overridden however must still be called from the overriding function. i.e. `super().get_queryset()`
## Workflow
This functions workflow is as follows:
- Fetch the user from the request
- Check if the user is authenticated
- Iterate over the users teams
- Store unique organizations from users teams
- return results
Returns:
(queryset): **super user**: return unfiltered data.
(queryset): **not super user**: return data from the stored unique organizations.
"""
request = get_request()
user_organizations: list(str()) = []
if request:
if request.app_settings.global_organization:
user_organizations += [ request.app_settings.global_organization.id ]
user = request.user
if user.is_authenticated:
for team in request.tenancy._user_teams:
if team.organization.id not in user_organizations:
if not user_organizations:
self.user_organizations = []
user_organizations += [ 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()
class TenancyObject(SaveHistory):
""" Tenancy Model Abstrct class.
This class is for inclusion wihtin **every** model within Centurion ERP.
Provides the required fields, functions and methods for multi tennant objects.
Unless otherwise stated, **no** object within this class may be overridden.
Raises:
ValidationError: User failed to supply organization
"""
objects = TenancyManager()
""" Multi-Tenanant Objects """
class Meta:
abstract = True
def validatate_organization_exists(self):
"""Ensure that the user did provide an organization
Raises:
ValidationError: User failed to supply organization.
"""
if not self:
raise centurion_exceptions.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,
blank = False,
help_text = 'Organization this belongs to',
null = False,
on_delete = models.CASCADE,
related_name = '+',
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'
)
model_notes = models.TextField(
blank = True,
default = None,
help_text = 'Tid bits of information',
null = True,
verbose_name = 'Notes',
)
def get_organization(self) -> Organization:
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(
not getattr(self, 'organization', None)
and self._meta.model_name !='appsettingshistory' # App Settings for
):
raise centurion_exceptions.ValidationError(
detail = {
'organization': 'Organization is required'
},
code = 'required'
)
super().save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)

View File

@ -2,7 +2,7 @@ from rest_framework.reverse import reverse
from rest_framework import serializers
from access.models import Organization
from access.models.organization import Organization
from app.serializers.user import UserBaseSerializer
@ -60,6 +60,13 @@ class OrganizationModelSerializer(
'model_pk': item.pk
}
),
'notes': reverse(
"v2:_api_v2_organization_note-list",
request=self._context['view'].request,
kwargs={
'model_id': item.pk
}
),
'teams': reverse("v2:_api_v2_organization_team-list", request=self._context['view'].request, kwargs={'organization_id': item.pk}),
}

View File

@ -0,0 +1,48 @@
from rest_framework import serializers
from access.models.organization_notes import OrganizationNotes
from api.serializers import common
from app.serializers.user import UserBaseSerializer
from core.serializers.model_notes import (
ModelNotes,
ModelNoteBaseSerializer,
ModelNoteModelSerializer,
ModelNoteViewSerializer
)
class OrganizationNoteBaseSerializer(ModelNoteBaseSerializer):
pass
class OrganizationNoteModelSerializer(
ModelNoteModelSerializer
):
class Meta:
model = OrganizationNotes
fields = ModelNoteModelSerializer.Meta.fields + [
'model',
]
read_only_fields = ModelNoteModelSerializer.Meta.read_only_fields + [
'model',
'content_type',
]
class OrganizationNoteViewSerializer(
ModelNoteViewSerializer,
OrganizationNoteModelSerializer,
):
pass

View File

@ -0,0 +1,48 @@
from rest_framework import serializers
from access.models.team_notes import TeamNotes
from api.serializers import common
from app.serializers.user import UserBaseSerializer
from core.serializers.model_notes import (
ModelNotes,
ModelNoteBaseSerializer,
ModelNoteModelSerializer,
ModelNoteViewSerializer
)
class TeamNoteBaseSerializer(ModelNoteBaseSerializer):
pass
class TeamNoteModelSerializer(
ModelNoteModelSerializer
):
class Meta:
model = TeamNotes
fields = ModelNoteModelSerializer.Meta.fields + [
'model',
]
read_only_fields = ModelNoteModelSerializer.Meta.read_only_fields + [
'model',
'content_type',
]
class TeamNoteViewSerializer(
ModelNoteViewSerializer,
TeamNoteModelSerializer,
):
pass

View File

@ -2,7 +2,7 @@ from rest_framework.reverse import reverse
from rest_framework import serializers
from access.models import TeamUsers
from access.models.team_user import TeamUsers
from api.serializers import common
@ -53,9 +53,15 @@ class TeamUserModelSerializer(
def get_url(self, item) -> dict:
return {
'_self': item.get_url( request = self._context['view'].request )
}
get_url = super().get_url( item = item )
del get_url['history']
del get_url['knowledge_base']
del get_url['notes']
return get_url
class Meta:

View File

@ -2,7 +2,7 @@ from rest_framework.reverse import reverse
from rest_framework import serializers
from access.models import Team
from access.models.team import Team
from api.serializers import common
@ -61,16 +61,9 @@ class TeamModelSerializer(
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
}
),
get_url = super().get_url( item = item )
get_url.update({
'users': reverse(
'v2:_api_v2_organization_team_user-list',
request=self.context['view'].request,
@ -79,7 +72,10 @@ class TeamModelSerializer(
'team_id': item.pk
}
)
}
})
return get_url
team_name = centurion_field.CharField( autolink = True )

View File

@ -1,7 +1,7 @@
import pytest
import unittest
from access.models import TenancyManager
from access.models.tenancy import TenancyManager

View File

@ -0,0 +1,32 @@
from django.test import TestCase
from access.models.organization_history import Organization, OrganizationHistory
from core.tests.abstract.test_functional_history import HistoryEntriesCommon
class History(
HistoryEntriesCommon,
TestCase,
):
model = Organization
history_model = OrganizationHistory
@classmethod
def setUpTestData(self):
super().setUpTestData()
self.obj = self.model.objects.create(
name = self.field_value_original,
)
self.obj_delete = self.model.objects.create(
name = self.field_value_delete,
)
self.call_the_banners()

View File

@ -4,12 +4,14 @@ import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission, 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 access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.api_permissions_viewset import APIPermissions
from api.tests.abstract.api_serializer_viewset import SerializersTestCases

View File

@ -0,0 +1,116 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from access.viewsets.organization_notes import ViewSet
from core.tests.abstract.test_functional_notes_viewset import (
ModelNotesViewSetBase,
ModelNotesMetadata,
ModelNotesPermissionsAPI,
ModelNotesSerializer
)
class ViewSetBase(
ModelNotesViewSetBase
):
viewset = ViewSet
url_name = '_api_v2_organization_note'
@classmethod
def setUpTestData(self):
super().setUpTestData()
self.item = self.viewset.model.objects.create(
organization = self.organization,
content = 'a random comment',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.organization,
created_by = self.view_user,
modified_by = self.view_user,
)
self.other_org_item = self.viewset.model.objects.create(
organization = self.different_organization,
content = 'a random comment',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.different_organization,
created_by = self.view_user,
modified_by = self.view_user,
)
self.global_org_item = self.viewset.model.objects.create(
organization = self.global_organization,
content = 'a random comment global_organization',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.global_organization,
created_by = self.view_user,
modified_by = self.view_user,
)
self.url_kwargs = {
'model_id': self.item.model.pk,
}
self.url_view_kwargs = {
'model_id': self.item.model.pk,
'pk': self.item.id
}
class OrganizationModelNotesPermissionsAPI(
ViewSetBase,
ModelNotesPermissionsAPI,
TestCase,
):
def test_returned_data_from_user_and_global_organizations_only(self):
"""Check items returned
This test case is a over-ride of a test case with the same name.
This model is not a global model making this test not-applicable.
Items returned from the query Must be from the users organization and
global ONLY!
"""
pass
class OrganizationModelNotesSerializer(
ViewSetBase,
ModelNotesSerializer,
TestCase,
):
pass
class OrganizationModelNotesMetadata(
ViewSetBase,
ModelNotesMetadata,
TestCase,
):
pass

View File

@ -0,0 +1,53 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from core.tests.abstract.model_notes_api_fields import ModelNotesNotesAPIFields
from access.models.organization import Organization
from access.models.organization_notes import OrganizationNotes
class OrganizationNotesAPI(
ModelNotesNotesAPIFields,
TestCase,
):
model = OrganizationNotes
view_name: str = '_api_v2_organization_note'
@classmethod
def setUpTestData(self):
"""Setup Test
1. Call parent setup
2. Create a model note
3. add url kwargs
4. make the API request
"""
super().setUpTestData()
self.item = self.model.objects.create(
organization = self.organization,
content = 'a random comment',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = Organization.objects.create(
name = 'dev'
),
created_by = self.view_user,
modified_by = self.view_user,
)
self.url_view_kwargs = {
'model_id': self.item.model.pk,
'pk': self.item.pk
}
self.make_request()

View File

@ -0,0 +1,37 @@
from django.test import TestCase
from access.models.team_history import Team, TeamHistory
from core.tests.abstract.test_functional_history import HistoryEntriesCommon
class History(
HistoryEntriesCommon,
TestCase,
):
model = Team
history_model = TeamHistory
@classmethod
def setUpTestData(self):
super().setUpTestData()
self.field_name = 'team_name'
self.obj = self.model.objects.create(
organization = self.organization,
# name = self.field_value_original,
team_name = self.field_value_original
)
self.obj_delete = self.model.objects.create(
organization = self.organization,
name = self.field_value_delete,
)
self.call_the_banners()

View File

@ -4,13 +4,16 @@ import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission, 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 access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
from api.tests.abstract.api_permissions_viewset import APIPermissions

View File

@ -1,13 +1,14 @@
import pytest
from django.contrib.auth.models import User
from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from rest_framework.exceptions import ValidationError
from access.middleware.request import Tenancy
from access.models import Organization, Permission
from access.models.organization import Organization
from access.serializers.teams import (
Team,

View File

@ -0,0 +1,127 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from access.viewsets.team_notes import ViewSet
from core.tests.abstract.test_functional_notes_viewset import (
ModelNotesViewSetBase,
ModelNotesMetadata,
ModelNotesPermissionsAPI,
ModelNotesSerializer
)
class ViewSetBase(
ModelNotesViewSetBase
):
viewset = ViewSet
url_name = '_api_v2_organization_team_note'
@classmethod
def setUpTestData(self):
super().setUpTestData()
self.item = self.viewset.model.objects.create(
organization = self.organization,
content = 'a random comment',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.viewset.model.model.field.related_model.objects.create(
organization = self.organization,
name = 'note model'
),
created_by = self.view_user,
modified_by = self.view_user,
)
self.other_org_item = self.viewset.model.objects.create(
organization = self.different_organization,
content = 'a random comment',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.viewset.model.model.field.related_model.objects.create(
organization = self.different_organization,
name = 'note model'
),
created_by = self.view_user,
modified_by = self.view_user,
)
self.global_org_item = self.viewset.model.objects.create(
organization = self.global_organization,
content = 'a random comment global_organization',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.viewset.model.model.field.related_model.objects.create(
organization = self.global_organization,
name = 'note model global_organization'
),
created_by = self.view_user,
modified_by = self.view_user,
)
self.url_kwargs = {
'organization_id': self.organization.id,
'model_id': self.item.model.pk,
}
self.url_view_kwargs = {
'organization_id': self.organization.id,
'model_id': self.item.model.pk,
'pk': self.item.id
}
class TeamModelNotesPermissionsAPI(
ViewSetBase,
ModelNotesPermissionsAPI,
TestCase,
):
def test_returned_data_from_user_and_global_organizations_only(self):
"""Check items returned
This test case is a over-ride of a test case with the same name.
This model is not a global model making this test not-applicable.
Items returned from the query Must be from the users organization and
global ONLY!
"""
pass
class TeamModelNotesSerializer(
ViewSetBase,
ModelNotesSerializer,
TestCase,
):
pass
class TeamModelNotesMetadata(
ViewSetBase,
ModelNotesMetadata,
TestCase,
):
pass

View File

@ -0,0 +1,56 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from core.tests.abstract.model_notes_api_fields import ModelNotesNotesAPIFields
from access.models.team import Team
from access.models.team_notes import TeamNotes
class TeamNotesAPI(
ModelNotesNotesAPIFields,
TestCase,
):
model = TeamNotes
view_name: str = '_api_v2_organization_team_note'
@classmethod
def setUpTestData(self):
"""Setup Test
1. Call parent setup
2. Create a model note
3. add url kwargs
4. make the API request
"""
super().setUpTestData()
self.item = self.model.objects.create(
organization = self.organization,
content = 'a random comment',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = Team.objects.create(
organization = self.organization,
name = 'note model'
),
created_by = self.view_user,
modified_by = self.view_user,
)
self.url_view_kwargs = {
'organization_id': self.organization.pk,
'model_id': self.item.model.pk,
'pk': self.item.pk
}
self.make_request()

View File

@ -4,13 +4,15 @@ import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission, 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 access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.test_metadata_functional import MetadataAttributesFunctional
from api.tests.abstract.api_permissions_viewset import APIPermissions

View File

@ -1,12 +1,13 @@
import pytest
from django.contrib.auth.models import User
from django.contrib.auth.models import Permission, 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.models.organization import Organization
from access.models.team import Team
from access.serializers.team_user import (
TeamUsers,

View File

@ -1,7 +1,7 @@
import pytest
from unittest.mock import Mock, patch
from django.contrib.auth.models import User, AnonymousUser
from django.contrib.auth.models import User, Permission, AnonymousUser
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
@ -13,7 +13,10 @@ from api.viewsets.common import ModelViewSet
from access.mixins.organization import OrganizationMixin
from access.mixins.permissions import OrganizationPermissionMixin
from access.models import Organization, Team, TeamUsers, Permission
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from core import exceptions as centurion_exceptions
from core.models.manufacturer import Manufacturer
@ -429,7 +432,7 @@ class HasPermission(
@patch.object(GenericAPIView, 'get_object')
def test_action_has_permission_function_get_object_called_once(self, get_object):
def test_action_has_permission_function_get_object_called_none(self, get_object):
get_object.return_value = self.obj
@ -464,7 +467,7 @@ class HasPermission(
OrganizationPermissionMixin().has_permission(request = view.request, view = view)
assert get_object.call_count == 1
assert get_object.call_count == 0
@ -1331,7 +1334,7 @@ class HasPermissionDifferentOrganization(
):
@patch.object(GenericAPIView, 'get_object')
def test_action_has_permission_different_org_get_object_called_once(self, get_object):
def test_action_has_permission_different_org_get_object_called_none(self, get_object):
get_object.return_value = self.obj
@ -1365,7 +1368,7 @@ class HasPermissionDifferentOrganization(
OrganizationPermissionMixin().has_permission(request = view.request, view = view)
assert get_object.call_count == 1
assert get_object.call_count == 0

View File

@ -2,14 +2,17 @@ import pytest
import unittest
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission, 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 access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers

View File

@ -2,14 +2,17 @@ import pytest
import unittest
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission, 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 access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.api_fields import APICommonFields

View File

@ -1,6 +1,6 @@
# from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import TestCase, Client
@ -9,7 +9,9 @@ import pytest
import unittest
import requests
from access.models import Organization, Team, TeamUsers, Permission
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from access.tests.abstract.model_permissions_organization_manager import OrganizationManagerModelPermissionChange, OrganizationManagerModelPermissionView
from app.tests.abstract.model_permissions import ModelPermissionsView, ModelPermissionsChange

View File

@ -2,12 +2,14 @@ import pytest
import unittest
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission, 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 access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.api_permissions import APIPermissionChange, APIPermissionView

View File

@ -6,5 +6,8 @@ import unittest
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from access.models import Organization, Team
from access.models.organization import Organization
from access.models.team import Team

View File

@ -1,187 +0,0 @@
import pytest
import unittest
import requests
from django.test import TestCase, Client
from access.models import Organization
from core.models.history import History
from access.models import Organization
class OrganizationHistory(TestCase):
model = Organization
model_name = 'organization'
@classmethod
def setUpTestData(self):
""" Setup Test """
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.item_create = self.model.objects.create(
name = 'test_item_' + self.model_name,
)
self.history_create = History.objects.get(
action = int(History.Actions.ADD),
item_pk = self.item_create.pk,
item_class = self.model._meta.model_name,
)
self.item_change = self.item_create
self.item_change.name = 'test_item_' + self.model_name + '_changed'
self.item_change.save()
self.history_change = History.objects.get(
action = int(History.Actions.UPDATE),
item_pk = self.item_change.pk,
item_class = self.model._meta.model_name,
)
self.item_delete = self.model.objects.create(
name = 'test_item_delete_' + self.model_name,
)
self.item_delete.delete()
self.history_delete = History.objects.filter(
item_pk = self.item_delete.pk,
item_class = self.model._meta.model_name,
)
self.history_delete_children = History.objects.filter(
item_parent_pk = self.item_delete.pk,
item_parent_class = self.model._meta.model_name,
)
def test_history_entry_item_add_field_action(self):
""" Ensure action is "add" for item creation """
history = self.history_create.__dict__
assert history['action'] == int(History.Actions.ADD)
# assert type(history['action']) is int
@pytest.mark.skip(reason="to be written")
def test_history_entry_item_add_field_after(self):
""" Ensure after field contains correct value """
history = self.history_create.__dict__
assert history['after'] == str('{}')
# assert type(history['after']) is str
def test_history_entry_item_add_field_before(self):
""" Ensure before field is an empty JSON string for create """
history = self.history_create.__dict__
assert history['before'] == str('{}')
# assert type(history['before']) is str
def test_history_entry_item_add_field_item_pk(self):
""" Ensure history entry field item_pk is the created items pk """
history = self.history_create.__dict__
assert history['item_pk'] == self.item_create.pk
# assert type(history['item_pk']) is int
def test_history_entry_item_add_field_item_class(self):
""" Ensure history entry field item_class is the model name """
history = self.history_create.__dict__
assert history['item_class'] == self.model._meta.model_name
# assert type(history['item_class']) is str
################################## Change ##################################
def test_history_entry_item_change_field_action(self):
""" Ensure action is "add" for item creation """
history = self.history_change.__dict__
assert history['action'] == int(History.Actions.UPDATE)
# assert type(history['action']) is int
def test_history_entry_item_change_field_after(self):
""" Ensure after field contains correct value """
history = self.history_change.__dict__
assert history['after'] == str('{"name": "test_item_' + self.model_name + '_changed"}')
# assert type(history['after']) is str
@pytest.mark.skip(reason="to be written")
def test_history_entry_item_change_field_before(self):
""" Ensure before field is an empty JSON string for create """
history = self.history_change.__dict__
assert history['before'] == str('{}')
# assert type(history['before']) is str
def test_history_entry_item_change_field_item_pk(self):
""" Ensure history entry field item_pk is the created items pk """
history = self.history_change.__dict__
assert history['item_pk'] == self.item_create.pk
# assert type(history['item_pk']) is int
def test_history_entry_item_change_field_item_class(self):
""" Ensure history entry field item_class is the model name """
history = self.history_change.__dict__
assert history['item_class'] == self.model._meta.model_name
# assert type(history['item_class']) is str
################################## Delete ##################################
def test_device_history_entry_delete(self):
""" When an item is deleted, it's history entries must be removed """
assert self.history_delete.exists() is False
def test_device_history_entry_children_delete(self):
""" When an item is deleted, it's history entries must be removed """
assert self.history_delete_children.exists() is False

View File

@ -1,165 +0,0 @@
# from django.conf import settings
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 TestCase, Client
import pytest
import unittest
import requests
from access.models import Organization, Team, TeamUsers, Permission
from core.models.history import History
class OrganizationHistoryPermissions(TestCase):
item_model = Organization
model = History
model_name = 'history'
app_label = 'core'
namespace = ''
name_view = '_history'
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
2. create an organization that is different to item
3. Create a device
4. Add history device history entry as item
5. create a user
6. create user in different organization (with the required permission)
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
different_organization = Organization.objects.create(name='test_different_organization')
self.item = self.organization
self.history_model_name = self.item._meta.model_name
self.history = self.model.objects.get(
item_pk = self.item.id,
item_class = self.item._meta.model_name,
action = self.model.Actions.ADD,
)
view_permissions = Permission.objects.get(
codename = 'view_' + self.model_name,
content_type = ContentType.objects.get(
app_label = self.app_label,
model = self.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = organization,
)
view_team.permissions.set([view_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.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,
])
TeamUsers.objects.create(
team = different_organization_team,
user = self.different_organization_user
)
def test_auth_view_history_user_anon_denied(self):
""" Check correct permission for view
Attempt to view as anon user
"""
client = Client()
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
response = client.get(url)
assert response.status_code == 302 and response.url.startswith('/account/login')
def test_auth_view_history_no_permission_denied(self):
""" Check correct permission for view
Attempt to view with user missing permission
"""
client = Client()
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
client.force_login(self.no_permissions_user)
response = client.get(url)
assert response.status_code == 403
def test_auth_view_history_different_organizaiton_denied(self):
""" Check correct permission for view
Attempt to view with user from different organization
"""
client = Client()
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
client.force_login(self.different_organization_user)
response = client.get(url)
assert response.status_code == 403
def test_auth_view_history_has_permission(self):
""" Check correct permission for view
Attempt to view as user with view permission
"""
client = Client()
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
client.force_login(self.view_user)
response = client.get(url)
assert response.status_code == 200

View File

@ -0,0 +1,66 @@
import pytest
from django.contrib.auth.models import User
from django.test import Client, TestCase
from rest_framework.reverse import reverse
from access.models.organization import Organization
from access.viewsets.organization import ViewSet
from api.tests.abstract.viewsets import ViewSetModel
class ViewsetCommon(
ViewSetModel,
):
viewset = ViewSet
route_name = 'API:_api_v2_organization'
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization
3. create super user
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.view_user = User.objects.create_user(username="test_view_user", password="password", is_superuser=True)
self.kwargs = {}
class OrganizationViewsetList(
ViewsetCommon,
TestCase,
):
@classmethod
def setUpTestData(self):
"""Setup Test
1. make list request
"""
super().setUpTestData()
client = Client()
url = reverse(
self.route_name + '-list'
)
client.force_login(self.view_user)
self.http_options_response_list = client.options(url)

View File

@ -0,0 +1,40 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from core.tests.abstract.test_unit_model_history_api_v2 import PrimaryModelHistoryAPI
from access.models.organization_history import Organization, OrganizationHistory
class ModelHistoryAPI(
PrimaryModelHistoryAPI,
TestCase,
):
audit_model = Organization
model = OrganizationHistory
@classmethod
def setUpTestData(self):
super().setUpTestData()
self.audit_object = self.organization
self.history_entry = self.model.objects.create(
organization = self.audit_object,
action = self.model.Actions.ADD,
user = self.view_user,
before = {},
after = {},
content_type = ContentType.objects.get(
app_label = self.audit_object._meta.app_label,
model = self.audit_object._meta.model_name,
),
model = self.audit_object,
)
self.make_request()

View File

@ -0,0 +1,36 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from core.tests.abstract.test_unit_model_notes_model import ModelNotesModel
from access.models.organization_notes import OrganizationNotes
class OrganizationNotesModel(
ModelNotesModel,
TestCase,
):
model = OrganizationNotes
@classmethod
def setUpTestData(self):
"""Setup Test"""
super().setUpTestData()
self.item = self.model.objects.create(
organization = self.organization,
content = 'a random comment for an exiting item',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.model.model.field.related_model.objects.create(
name = 'note model existing item',
),
created_by = self.user,
)

View File

@ -0,0 +1,57 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from core.tests.abstract.test_unit_model_notes_serializer import ModelNotesSerializerTestCases
from access.serializers.organization_notes import OrganizationNotes, OrganizationNoteModelSerializer
class OrganizationNotesSerializer(
ModelNotesSerializerTestCases,
TestCase,
):
model = OrganizationNotes
model_serializer = OrganizationNoteModelSerializer
@classmethod
def setUpTestData(self):
"""Setup Test"""
super().setUpTestData()
self.note_model = self.model.model.field.related_model.objects.create(
name = 'note model',
)
self.note_model_two = self.model.model.field.related_model.objects.create(
name = 'note model two',
)
self.item = self.model.objects.create(
organization = self.organization,
content = 'a random comment for an exiting item',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.model.model.field.related_model.objects.create(
name = 'note model existing item',
),
created_by = self.user_two,
)
self.valid_data = {
'organization': self.organization_two.id,
'content': 'a random comment',
'content_type': self.content_type_two.id,
'model': self.note_model_two.id,
'created_by': self.user_two.id,
'modified_by': self.user_two.id,
}

View File

@ -0,0 +1,72 @@
import pytest
from django.contrib.auth.models import User
from django.test import Client, TestCase
from rest_framework.reverse import reverse
from access.models.organization import Organization
from access.viewsets.team_notes import ViewSet
from api.tests.abstract.viewsets import ViewSetModel
class ViewsetCommon(
ViewSetModel,
):
viewset = ViewSet
route_name = 'v2:_api_v2_organization_note'
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization
3. create super user
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.view_user = User.objects.create_user(username="test_view_user", password="password", is_superuser=True)
class OrganizationNotesViewsetList(
ViewsetCommon,
TestCase,
):
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create object that is to be tested against
2. add kwargs
3. make list request
"""
super().setUpTestData()
self.kwargs = {
'model_id': self.organization.id,
}
client = Client()
url = reverse(
self.route_name + '-list',
kwargs = self.kwargs
)
client.force_login(self.view_user)
self.http_options_response_list = client.options(url)

View File

@ -1,9 +1,12 @@
import pytest
import unittest
from django.contrib.auth.models import Permission
from django.test import TestCase, Client
from access.models import Organization, Team, TeamUsers, Permission
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from app.tests.abstract.models import TenancyModel
@ -34,22 +37,22 @@ class TeamModel(
)
def test_model_has_property_parent_object(self):
""" Check if model contains 'parent_object'
# def test_model_has_property_parent_object(self):
# """ Check if model contains 'parent_object'
This is a required property for all models that have a parent
"""
# This is a required property for all models that have a parent
# """
assert hasattr(self.model, 'parent_object')
# assert hasattr(self.model, 'parent_object')
def test_model_property_parent_object_returns_object(self):
""" Check if model contains 'parent_object'
# def test_model_property_parent_object_returns_object(self):
# """ Check if model contains 'parent_object'
This is a required property for all models that have a parent
"""
# This is a required property for all models that have a parent
# """
assert self.item.parent_object is self.parent_item
# assert self.item.parent_object is self.parent_item
@pytest.mark.skip(reason="to be written")

View File

@ -4,14 +4,16 @@ import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission, 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 access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
# from api.tests.abstract.api_permissions import APIPermissions

View File

@ -1,13 +1,15 @@
import pytest
from django.contrib.auth.models import User
from django.contrib.auth.models import Permission, 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 access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.api_fields import APITenancyObject

View File

@ -1,79 +0,0 @@
import pytest
import unittest
import requests
from django.test import TestCase, Client
from access.models import Organization
from core.models.history import History
from core.tests.abstract.history_entry import HistoryEntry
from core.tests.abstract.history_entry_child_model import HistoryEntryChildItem
from access.models import Team
from django.contrib.auth.models import Group
class TeamHistory(TestCase, HistoryEntry, HistoryEntryChildItem):
model = Team
@classmethod
def setUpTestData(self):
""" Setup Test """
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.item_parent = organization
self.item_create = self.model.objects.create(
team_name = 'test_item_' + self.model._meta.model_name,
organization = self.organization
)
self.history_create = History.objects.get(
action = int(History.Actions.ADD),
item_pk = self.item_create.pk,
item_class = self.model._meta.model_name,
)
self.item_change = self.item_create
self.item_change.team_name = 'test_item_' + self.model._meta.model_name + '_changed'
self.item_change.save()
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),
item_pk = self.item_change.pk,
item_class = self.model._meta.model_name,
)
debug = Group.objects.all()
self.item_delete = self.model.objects.create(
team_name = 'test_item_delete_' + self.model._meta.model_name,
organization = self.organization
)
self.deleted_pk = self.item_delete.pk
self.item_delete.delete()
self.history_delete = History.objects.get(
action = int(History.Actions.DELETE),
item_pk = self.deleted_pk,
item_class = self.model._meta.model_name,
)
self.history_delete_children = History.objects.filter(
item_parent_pk = self.deleted_pk,
item_parent_class = self.item_parent._meta.model_name,
)

View File

@ -1,168 +0,0 @@
# from django.conf import settings
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 TestCase, Client
import pytest
import unittest
import requests
from access.models import Organization, Team, TeamUsers, Permission
from core.models.history import History
class TeamHistoryPermissions(TestCase):
item_model = Team
model = History
model_name = 'history'
app_label = 'core'
namespace = ''
name_view = '_history'
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization for user and item
2. create an organization that is different to item
3. Create a device
4. Add history device history entry as item
5. create a user
6. create user in different organization (with the required permission)
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
different_organization = Organization.objects.create(name='test_different_organization')
self.item = self.item_model.objects.create(
organization=organization,
name = 'deviceone'
)
self.history_model_name = self.item._meta.model_name
self.history = self.model.objects.get(
item_pk = self.item.id,
item_class = self.item._meta.model_name,
action = self.model.Actions.ADD,
)
view_permissions = Permission.objects.get(
codename = 'view_' + self.model_name,
content_type = ContentType.objects.get(
app_label = self.app_label,
model = self.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = organization,
)
view_team.permissions.set([view_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.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,
])
TeamUsers.objects.create(
team = different_organization_team,
user = self.different_organization_user
)
def test_auth_view_history_user_anon_denied(self):
""" Check correct permission for view
Attempt to view as anon user
"""
client = Client()
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
response = client.get(url)
assert response.status_code == 302 and response.url.startswith('/account/login')
def test_auth_view_history_no_permission_denied(self):
""" Check correct permission for view
Attempt to view with user missing permission
"""
client = Client()
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
client.force_login(self.no_permissions_user)
response = client.get(url)
assert response.status_code == 403
def test_auth_view_history_different_organizaiton_denied(self):
""" Check correct permission for view
Attempt to view with user from different organization
"""
client = Client()
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
client.force_login(self.different_organization_user)
response = client.get(url)
assert response.status_code == 403
def test_auth_view_history_has_permission(self):
""" Check correct permission for view
Attempt to view as user with view permission
"""
client = Client()
url = reverse(self.namespace + self.name_view, kwargs={'model_name': self.history_model_name, 'model_pk': self.item.id})
client.force_login(self.view_user)
response = client.get(url)
assert response.status_code == 200

View File

@ -1,6 +1,6 @@
# from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import TestCase, Client
@ -9,7 +9,9 @@ import pytest
import unittest
import requests
from access.models import Organization, Team, TeamUsers, Permission
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from access.tests.abstract.model_permissions_organization_manager import OrganizationManagerModelPermissions
from app.tests.abstract.model_permissions import ModelPermissions

View File

@ -4,11 +4,13 @@ import requests
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from access.models import Organization, Team, TeamUsers, Permission
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.api_permissions import APIPermissions

View File

@ -0,0 +1,43 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from core.tests.abstract.test_unit_model_history_api_v2 import PrimaryModelHistoryAPI
from access.models.team_history import Team, TeamHistory
class ModelHistoryAPI(
PrimaryModelHistoryAPI,
TestCase,
):
audit_model = Team
model = TeamHistory
@classmethod
def setUpTestData(self):
super().setUpTestData()
self.audit_object = self.audit_model.objects.create(
organization = self.organization,
name = 'one',
)
self.history_entry = self.model.objects.create(
organization = self.audit_object.organization,
action = self.model.Actions.ADD,
user = self.view_user,
before = {},
after = {},
content_type = ContentType.objects.get(
app_label = self.audit_object._meta.app_label,
model = self.audit_object._meta.model_name,
),
model = self.audit_object,
)
self.make_request()

View File

@ -0,0 +1,67 @@
import pytest
from django.contrib.auth.models import User
from django.test import Client, TestCase
from rest_framework.reverse import reverse
from access.models.organization import Organization
from access.viewsets.team import ViewSet
from api.tests.abstract.viewsets import ViewSetModel
class ViewsetCommon(
ViewSetModel,
):
viewset = ViewSet
route_name = 'API:_api_v2_organization_team'
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization
3. create super user
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.view_user = User.objects.create_user(username="test_view_user", password="password", is_superuser=True)
self.kwargs = { 'organization_id': self.organization.id }
class TeamViewsetList(
ViewsetCommon,
TestCase,
):
@classmethod
def setUpTestData(self):
"""Setup Test
1. make list request
"""
super().setUpTestData()
client = Client()
url = reverse(
self.route_name + '-list',
kwargs = self.kwargs
)
client.force_login(self.view_user)
self.http_options_response_list = client.options(url)

View File

@ -0,0 +1,37 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from core.tests.abstract.test_unit_model_notes_model import ModelNotesModel
from access.models.team_notes import TeamNotes
class TeamNotesModel(
ModelNotesModel,
TestCase,
):
model = TeamNotes
@classmethod
def setUpTestData(self):
"""Setup Test"""
super().setUpTestData()
self.item = self.model.objects.create(
organization = self.organization,
content = 'a random comment for an exiting item',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.model.model.field.related_model.objects.create(
organization = self.organization,
name = 'note model existing item',
),
created_by = self.user,
)

View File

@ -0,0 +1,60 @@
from django.contrib.contenttypes.models import ContentType
from django.test import TestCase
from core.tests.abstract.test_unit_model_notes_serializer import ModelNotesSerializerTestCases
from access.serializers.team_notes import TeamNotes, TeamNoteModelSerializer
class TeamNotesSerializer(
ModelNotesSerializerTestCases,
TestCase,
):
model = TeamNotes
model_serializer = TeamNoteModelSerializer
@classmethod
def setUpTestData(self):
"""Setup Test"""
super().setUpTestData()
self.note_model = self.model.model.field.related_model.objects.create(
organization = self.organization,
team_name = 'note model',
)
self.note_model_two = self.model.model.field.related_model.objects.create(
organization = self.organization,
team_name = 'note model two',
)
self.item = self.model.objects.create(
organization = self.organization,
content = 'a random comment for an exiting item',
content_type = ContentType.objects.get(
app_label = str(self.model._meta.app_label).lower(),
model = str(self.model.model.field.related_model.__name__).replace(' ', '').lower(),
),
model = self.model.model.field.related_model.objects.create(
organization = self.organization,
team_name = 'note model existing item',
),
created_by = self.user_two,
)
self.valid_data = {
'organization': self.organization_two.id,
'content': 'a random comment',
'content_type': self.content_type_two.id,
'model': self.note_model_two.id,
'created_by': self.user_two.id,
'modified_by': self.user_two.id,
}

View File

@ -0,0 +1,78 @@
import pytest
from django.contrib.auth.models import User
from django.test import Client, TestCase
from rest_framework.reverse import reverse
from access.models.organization import Organization
from access.viewsets.team_notes import ViewSet
from api.tests.abstract.viewsets import ViewSetModel
class ViewsetCommon(
ViewSetModel,
):
viewset = ViewSet
route_name = 'v2:_api_v2_organization_team_note'
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization
3. create super user
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.view_user = User.objects.create_user(username="test_view_user", password="password", is_superuser=True)
class TeamNotesViewsetList(
ViewsetCommon,
TestCase,
):
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create object that is to be tested against
2. add kwargs
3. make list request
"""
super().setUpTestData()
self.note_model = self.viewset.model.model.field.related_model.objects.create(
organization = self.organization,
name = 'note model'
)
self.kwargs = {
'organization_id': self.organization.id,
'model_id': self.note_model.pk,
}
client = Client()
url = reverse(
self.route_name + '-list',
kwargs = self.kwargs
)
client.force_login(self.view_user)
self.http_options_response_list = client.options(url)

View File

@ -2,9 +2,11 @@ import pytest
import unittest
from django.test import TestCase, Client
from django.contrib.auth.models import User
from django.contrib.auth.models import Permission, User
from access.models import Organization, Team, TeamUsers, Permission
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from app.tests.abstract.models import BaseModel

View File

@ -1,13 +1,15 @@
import pytest
from django.contrib.auth.models import User
from django.contrib.auth.models import Permission, 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 access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.tests.abstract.api_fields import APICommonFields

View File

@ -1,92 +0,0 @@
import pytest
import unittest
import requests
from django.contrib.auth.models import User
from django.test import TestCase, Client
from access.models import Organization
from core.models.history import History
from core.tests.abstract.history_entry import HistoryEntry
from core.tests.abstract.history_entry_child_model import HistoryEntryChildItem
from access.models import Team, TeamUsers
class TeamUsersHistory(TestCase, HistoryEntry, HistoryEntryChildItem):
model = TeamUsers
model_name = 'teamusers'
@classmethod
def setUpTestData(self):
""" Setup Test """
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.item_parent = Team.objects.create(
team_name = 'test_item_' + self.model._meta.model_name,
organization = self.organization
)
self.user = User.objects.create(
username = 'test_item_' + self.model._meta.model_name,
password = 'a random password'
)
self.item_create = self.model.objects.create(
user = self.user,
team = self.item_parent
)
self.history_create = History.objects.get(
action = int(History.Actions.ADD),
item_pk = self.item_create.pk,
item_class = self.model._meta.model_name,
)
self.item_change = self.item_create
self.item_change.manager = True
self.item_change.save()
self.field_after_expected_value = '{"manager": true}'
self.history_change = History.objects.get(
action = int(History.Actions.UPDATE),
item_pk = self.item_change.pk,
item_class = self.model._meta.model_name,
)
self.user_delete = User.objects.create(
username = 'test_item_delete' + self.model._meta.model_name,
password = 'a random password'
)
self.item_delete = self.model.objects.create(
user = self.user_delete,
team = self.item_parent
)
self.deleted_pk = self.item_delete.pk
self.item_delete.delete()
self.history_delete = History.objects.get(
action = int(History.Actions.DELETE),
item_pk = self.deleted_pk,
item_class = self.model._meta.model_name,
)
self.history_delete_children = History.objects.filter(
item_parent_pk = self.deleted_pk,
item_parent_class = self.item_parent._meta.model_name,
)

View File

@ -6,7 +6,9 @@ import unittest
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from access.models import Organization, Team
from access.models.organization import Organization
from access.models.team import Team
# @pytest.fixture

View File

@ -1,6 +1,6 @@
# from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import TestCase, Client
@ -9,7 +9,10 @@ import pytest
import unittest
import requests
from access.models import Organization, Team, TeamUsers, Permission
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from access.tests.abstract.model_permissions_organization_manager import OrganizationManagerModelPermissionAdd, OrganizationManagerModelPermissionDelete
from app.tests.abstract.model_permissions import ModelPermissionsAdd, ModelPermissionsChange, ModelPermissionsDelete

View File

@ -0,0 +1,76 @@
import pytest
from django.contrib.auth.models import User
from django.test import Client, TestCase
from rest_framework.reverse import reverse
from access.models.organization import Organization
from access.models.team import Team
from access.viewsets.team_user import ViewSet
from api.tests.abstract.viewsets import ViewSetModel
class ViewsetCommon(
ViewSetModel,
):
viewset = ViewSet
route_name = 'API:_api_v2_organization_team_user'
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an organization
3. create super user
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.team = Team.objects.create(
organization = self.organization,
name = 'team'
)
self.view_user = User.objects.create_user(username="test_view_user", password="password", is_superuser=True)
self.kwargs = {
'organization_id': self.organization.id,
'team_id': self.team.id
}
class TeamUserViewsetList(
ViewsetCommon,
TestCase,
):
@classmethod
def setUpTestData(self):
"""Setup Test
1. make list request
"""
super().setUpTestData()
client = Client()
url = reverse(
self.route_name + '-list',
kwargs = self.kwargs
)
client.force_login(self.view_user)
self.http_options_response_list = client.options(url)

View File

@ -3,7 +3,8 @@ import unittest
from django.test import TestCase
from access.models import TenancyObject, TenancyManager
from access.models.tenancy import TenancyManager
from access.models.tenancy import TenancyObject
from core.mixin.history_save import SaveHistory

View File

@ -4,7 +4,7 @@ from django.test import Client, TestCase
from rest_framework.permissions import IsAuthenticated
from access.models import Organization
from access.models.organization import Organization
from api.tests.abstract.viewsets import ViewSetCommon
@ -43,6 +43,8 @@ class AccessViewset(
client.force_login(self.view_user)
self.http_options_response_list = client.options(url)
self.kwargs = {}
def test_view_attr_permission_classes_value(self):

View File

@ -4,7 +4,7 @@ from django.utils.decorators import method_decorator
from django.views import generic
from access.mixin import *
from access.models import *
from access.models.organization import Organization
from access.forms.organization import OrganizationForm

View File

@ -4,8 +4,10 @@ from django.utils.decorators import method_decorator
from django.urls import reverse
from access.forms.team import TeamForm, TeamFormAdd
from access.models import Team, TeamUsers, Organization
from access.mixin import *
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from core.views.common import AddView, ChangeView, DeleteView

View File

@ -2,11 +2,13 @@ from django.contrib.auth import decorators as auth_decorator
from django.urls import reverse
from access.forms.team_users import TeamUsersForm
from access.models import Team, TeamUsers
from access.models.team import Team
from access.models.team_user import TeamUsers
from core.views.common import AddView, DeleteView
class Add(AddView):
context_object_name = "teamuser"

View File

@ -1,5 +1,7 @@
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse
# THis import only exists so that the migrations can be created
from access.models.organization_history import OrganizationHistory # pylint: disable=W0611:unused-import
from access.serializers.organization import (
Organization,
OrganizationModelSerializer,
@ -75,13 +77,22 @@ class ViewSet( ModelViewSet ):
def get_serializer_class(self):
if self.serializer_class is not None:
return self.serializer_class
if (
self.action == 'list'
or self.action == 'retrieve'
):
return globals()[str( self.model._meta.verbose_name) + 'ViewSerializer']
self.serializer_class = globals()[str( self.model._meta.verbose_name) + 'ViewSerializer']
else:
self.serializer_class = globals()[str( self.model._meta.verbose_name) + 'ModelSerializer']
return globals()[str( self.model._meta.verbose_name) + 'ModelSerializer']
return self.serializer_class

View File

@ -0,0 +1,65 @@
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse
from access.serializers.organization_notes import (
OrganizationNotes,
OrganizationNoteModelSerializer,
OrganizationNoteViewSerializer,
)
from core.viewsets.model_notes import ModelNoteViewSet
@extend_schema_view(
create=extend_schema(
summary = 'Add a note to an organization',
description = '',
responses = {
201: OpenApiResponse(description='created', response=OrganizationNoteViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),
}
),
destroy = extend_schema(
summary = 'Delete an organization note',
description = ''
),
list = extend_schema(
summary = 'Fetch all organization notes',
description='',
),
retrieve = extend_schema(
summary = 'Fetch a single organization note',
description='',
),
update = extend_schema(exclude = True),
partial_update = extend_schema(
summary = 'Update an organization note',
description = ''
),
)
class ViewSet(ModelNoteViewSet):
model = OrganizationNotes
def get_serializer_class(self):
if self.serializer_class is not None:
return self.serializer_class
if (
self.action == 'list'
or self.action == 'retrieve'
):
self.serializer_class = OrganizationNoteViewSerializer
else:
self.serializer_class = OrganizationNoteModelSerializer
return self.serializer_class

View File

@ -1,7 +1,8 @@
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiResponse
from access.models import Organization
from access.models.organization import Organization
# THis import only exists so that the migrations can be created
from access.models.team_history import TeamHistory # pylint: disable=W0611:unused-import
from access.serializers.teams import (
Team,
TeamModelSerializer,
@ -144,6 +145,10 @@ class ViewSet( ModelViewSet ):
def get_queryset(self):
if self.queryset is not None:
return self.queryset
queryset = super().get_queryset()
queryset = queryset.filter(organization_id=self.kwargs['organization_id'])
@ -154,15 +159,22 @@ class ViewSet( ModelViewSet ):
def get_serializer_class(self):
if self.serializer_class is not None:
return self.serializer_class
if (
self.action == 'list'
or self.action == 'retrieve'
):
return globals()[str( self.model._meta.verbose_name) + 'ViewSerializer']
self.serializer_class = globals()[str( self.model._meta.verbose_name) + 'ViewSerializer']
else:
return globals()[str( self.model._meta.verbose_name) + 'ModelSerializer']
self.serializer_class = globals()[str( self.model._meta.verbose_name) + 'ModelSerializer']
return self.serializer_class
def get_return_url(self) -> str:

View File

@ -0,0 +1,65 @@
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiResponse
from access.serializers.team_notes import (
TeamNotes,
TeamNoteModelSerializer,
TeamNoteViewSerializer,
)
from core.viewsets.model_notes import ModelNoteViewSet
@extend_schema_view(
create=extend_schema(
summary = 'Add a note to a Team',
description = '',
responses = {
201: OpenApiResponse(description='created', response=TeamNoteViewSerializer),
400: OpenApiResponse(description='Validation failed.'),
403: OpenApiResponse(description='User is missing create permissions'),
}
),
destroy = extend_schema(
summary = 'Delete a team note',
description = ''
),
list = extend_schema(
summary = 'Fetch all team notes',
description='',
),
retrieve = extend_schema(
summary = 'Fetch a single team note',
description='',
),
update = extend_schema(exclude = True),
partial_update = extend_schema(
summary = 'Update a team note',
description = ''
),
)
class ViewSet(ModelNoteViewSet):
model = TeamNotes
def get_serializer_class(self):
if self.serializer_class is not None:
return self.serializer_class
if (
self.action == 'list'
or self.action == 'retrieve'
):
self.serializer_class = TeamNoteViewSerializer
else:
self.serializer_class = TeamNoteModelSerializer
return self.serializer_class

View File

@ -1,6 +1,6 @@
from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter, OpenApiResponse
from access.models import Team
from access.models.team import Team
from access.serializers.team_user import (
TeamUsers,
@ -171,28 +171,39 @@ class ViewSet( ModelViewSet ):
def get_queryset(self):
queryset = super().get_queryset()
if self.queryset is not None:
queryset = queryset.filter(
return self.queryset
self.queryset = super().get_queryset()
self.queryset = self.queryset.filter(
team_id = self.kwargs['team_id']
)
self.queryset = queryset
return self.queryset
def get_serializer_class(self):
if self.serializer_class is not None:
return self.serializer_class
if (
self.action == 'list'
or self.action == 'retrieve'
):
return globals()[str( self.model._meta.verbose_name).replace(' ', '') + 'ViewSerializer']
self.serializer_class = globals()[str( self.model._meta.verbose_name).replace(' ' , '') + 'ViewSerializer']
else:
self.serializer_class = globals()[str( self.model._meta.verbose_name).replace(' ' , '') + 'ModelSerializer']
return globals()[str( self.model._meta.verbose_name).replace(' ', '') + 'ModelSerializer']
return self.serializer_class
def get_return_url(self):

View File

@ -32,7 +32,7 @@ class AuthTokenForm(CommonModelForm):
if self.prefix + '-gen_token' not in self.data:
generated_token = self.instance.generate()
generated_token = self.instance.generate
else:

View File

@ -0,0 +1,17 @@
# Generated by Django 5.1.5 on 2025-02-28 15:12
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('api', '0002_alter_authtoken_expires_alter_authtoken_id_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='authtoken',
options={'ordering': ['expires'], 'verbose_name': 'Auth Token', 'verbose_name_plural': 'Auth Tokens'},
),
]

View File

@ -3,20 +3,34 @@ import random
import string
from django.conf import settings
from django.contrib.auth.models import User
from django.db import models
from django.db.models import Field
from django.forms import ValidationError
from access.fields import *
from access.models import TenancyObject
from rest_framework.reverse import reverse
from access.fields import (
AutoCreatedField,
AutoLastModifiedField
)
class AuthToken(models.Model):
def validate_note_no_token(self, note, token):
class Meta:
ordering = [
'expires'
]
verbose_name = 'Auth Token'
verbose_name_plural = 'Auth Tokens'
def validate_note_no_token(self, note, token, raise_exception = True) -> bool:
""" Ensure plaintext token cant be saved to notes field.
called from app.settings.views.user_settings.TokenAdd.form_valid()
@ -41,10 +55,12 @@ class AuthToken(models.Model):
validation = False
if not validation:
if not validation and raise_exception:
raise ValidationError('Token can not be placed in the notes field.')
return validation
id = models.AutoField(
@ -95,6 +111,7 @@ class AuthToken(models.Model):
modified = AutoLastModifiedField()
@property
def generate(self) -> str:
return str(hashlib.sha256(str(self.randomword()).encode('utf-8')).hexdigest())
@ -115,3 +132,33 @@ class AuthToken(models.Model):
def __str__(self):
return self.token
table_fields: list = [
'note',
'created',
'expires',
'-action_delete-',
]
def get_url( self, request = None ) -> str:
if request:
return reverse(f"v2:_api_v2_user_settings_token-detail", request=request, kwargs = self.get_url_kwargs() )
return reverse(f"v2:_api_v2_user_settings_token-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 {
'model_id': self.user.id,
'pk': self.id
}

View File

@ -13,7 +13,7 @@ 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 access.models.organization import Organization
from app.serializers.user import User, UserBaseSerializer

View File

@ -1,6 +1,8 @@
from rest_framework import serializers, request
from rest_framework.reverse import reverse
from access.models import Organization, Team
from access.models.organization import Organization
from access.models.team import Team
from django.contrib.auth.models import Permission

View File

@ -0,0 +1,136 @@
import datetime
import re
from rest_framework import serializers
from access.serializers.organization import OrganizationBaseSerializer
from api.models.tokens import AuthToken
from api.serializers import common
from core import exceptions as centurion_exception
class AuthTokenBaseSerializer(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_cluster-detail",
)
class Meta:
model = AuthToken
fields = [
'id',
'display_name',
'url',
]
read_only_fields = [
'id',
'display_name',
'url',
]
class AuthTokenModelSerializer(
common.CommonModelSerializer,
AuthTokenBaseSerializer
):
_urls = serializers.SerializerMethodField('get_url')
def get_url(self, item) -> dict:
get_url = super().get_url( item = item )
del get_url['history']
del get_url['knowledge_base']
del get_url['notes']
return get_url
expires = serializers.DateTimeField(
initial = (datetime.datetime.now(tz=datetime.timezone.utc) + datetime.timedelta(days=90)).replace(microsecond=0).isoformat(),
)
token = serializers.CharField(initial=AuthToken().generate, write_only = True )
class Meta:
model = AuthToken
fields = [
'id',
'display_name',
'note',
'token',
'user',
'expires',
'created',
'modified',
'_urls',
]
read_only_fields = [
'id',
'display_name',
'user',
'created',
'modified',
'_urls',
]
def validate(self, attrs):
if self.context['request'].user.id != int(self.context['view'].kwargs['model_id']):
raise centurion_exception.PermissionDenied()
if not self.Meta.model().validate_note_no_token(attrs['note'], attrs['token']):
raise centurion_exception.ValidationError(
detail = {
"note": "No more than nine chars of token can be contained within the notes field"
},
code = 'note_no_contain_token'
)
if not re.fullmatch(r'[0-9|a-f]{64}', str(attrs['token']).lower()):
raise centurion_exception.ValidationError(
detail = {
"token": "Token appears to have been edited."
},
code = 'token_not_sha256'
)
attrs['token'] = self.Meta.model().token_hash(attrs['token'])
attrs = super().validate(attrs)
attrs['user'] = self.context['request'].user
return attrs
class AuthTokenViewSerializer(AuthTokenModelSerializer):
organization = OrganizationBaseSerializer( many = False, read_only = True )

View File

@ -1,11 +1,12 @@
from rest_framework import serializers
from rest_framework.reverse import reverse
from access.serializers.organization import Organization, OrganizationBaseSerializer
from access.serializers.organization import Organization
from core import fields as centurion_field
from settings.models.app_settings import AppSettings
class OrganizationField(serializers.PrimaryKeyRelatedField):
@ -50,4 +51,36 @@ class CommonModelSerializer(CommonBaseSerializer):
model_notes = centurion_field.MarkdownField( required = False )
organization = OrganizationField(required = False)
organization = OrganizationField(required = False)
def get_url(self, item) -> dict:
return {
'_self': item.get_url( request = self._context['view'].request ),
'history': reverse(
"v2:_api_v2_model_history-list",
request = self._context['view'].request,
kwargs = {
'app_label': self.Meta.model._meta.app_label,
'model_name': self.Meta.model._meta.model_name,
'model_id': item.pk
}
),
'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
}
),
'notes': reverse(
"v2:_api_v2_operating_system_note-list",
request = self._context['view'].request,
kwargs = {
'model_id': item.pk
}
),
}

View File

@ -1,7 +1,72 @@
from django.contrib.auth.models import ContentType, Permission, User
from unittest.mock import patch, PropertyMock
from access.mixins.permissions import OrganizationPermissionMixin
from api.react_ui_metadata import ReactUIMetadata
from access.middleware.request import Tenancy
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from settings.models.app_settings import AppSettings
class MockRequest:
"""Fake Request
contains the user and tenancy object for permission checks
Some ViewSets rely upon the request object for obtaining the user and
fetching the tenacy object for permission checking.
"""
data = {}
kwargs = {}
tenancy: Tenancy = None
user: User = None
def __init__(self, user: User, organization: Organization, viewset):
self.user = user
view_permission = Permission.objects.get(
codename = 'view_' + viewset.model._meta.model_name,
content_type = ContentType.objects.get(
app_label = viewset.model._meta.app_label,
model = viewset.model._meta.model_name,
)
)
view_team = Team.objects.create(
team_name = 'view_team',
organization = organization,
)
view_team.permissions.set([view_permission])
teamuser = TeamUsers.objects.create(
team = view_team,
user = user
)
self.app_settings = AppSettings.objects.select_related('global_organization').get(
owner_organization = None
)
self.tenancy = Tenancy(
user = user,
app_settings = self.app_settings
)
class AllViewSet:
@ -34,6 +99,8 @@ class AllViewSet:
view_set = self.viewset()
view_set.kwargs = self.kwargs
assert view_set.allowed_methods is not None
@ -45,6 +112,8 @@ class AllViewSet:
view_set = self.viewset()
view_set.kwargs = self.kwargs
assert type(view_set.allowed_methods) is list
@ -367,7 +436,7 @@ class ModelViewSet(AllViewSet):
assert (
type(view_set.documentation) is str
or type(view_set.documentation) is None
or view_set.documentation is None
)
@ -425,6 +494,8 @@ class ModelViewSet(AllViewSet):
view_set = self.viewset()
view_set.kwargs = self.kwargs
for method in list(view_set.allowed_methods):
if method not in valid_values:
@ -584,4 +655,165 @@ class ViewSetModel(
APIRenderModelViewSet (class): Tests to check API rendering to ensure data is present, includes `APIRenderViewSet` tests.
"""
pass
def test_view_func_get_queryset_cache_result(self):
"""Viewset Test
Ensure that the `get_queryset` function caches the result under
attribute `<viewset>.queryset`
"""
view_set = self.viewset()
view_set.request = MockRequest(
user = self.view_user,
organization = self.organization,
viewset = self.viewset
)
view_set.kwargs = self.kwargs
view_set.action = 'list'
view_set.detail = False
assert view_set.queryset is None # Must be empty before init
q = view_set.get_queryset()
assert view_set.queryset is not None # Must not be empty after init
assert q == view_set.queryset
def test_view_func_get_queryset_cache_result_used(self):
"""Viewset Test
Ensure that the `get_queryset` function caches the result under
attribute `<viewset>.queryset`
"""
view_set = self.viewset()
view_set.request = MockRequest(
user = self.view_user,
organization = self.organization,
viewset = self.viewset
)
view_set.kwargs = self.kwargs
view_set.action = 'list'
view_set.detail = False
mock_return = view_set.get_queryset() # Real item to be used as mock return Some
# functions use `Queryset` for additional filtering
setter_not_called = True
with patch.object(self.viewset, 'queryset', new_callable=PropertyMock) as qs:
qs.return_value = mock_return
mocked_view_set = self.viewset()
mocked_view_set.kwargs = self.kwargs
mocked_view_set.action = 'list'
mocked_view_set.detail = False
qs.reset_mock() # Just in case
mocked_setup = mocked_view_set.get_queryset() # should only add two calls, if exists and the return
for mock_call in list(qs.mock_calls): # mock_calls with args means setter was called
if len(mock_call.args) > 0:
setter_not_called = False
assert setter_not_called
assert qs.call_count == 2
def test_view_func_get_serializer_class_cache_result(self):
"""Viewset Test
Ensure that the `get_serializer_class` function caches the result under
attribute `<viewset>.serializer_class`
"""
view_set = self.viewset()
view_set.request = MockRequest(
user = self.view_user,
organization = self.organization,
viewset = self.viewset
)
view_set.kwargs = self.kwargs
view_set.action = 'list'
view_set.detail = False
assert view_set.serializer_class is None # Must be empty before init
q = view_set.get_serializer_class()
assert view_set.serializer_class is not None # Must not be empty after init
assert q == view_set.serializer_class
def test_view_func_get_serializer_class_cache_result_used(self):
"""Viewset Test
Ensure that the `get_serializer_class` function caches the result under
attribute `<viewset>.serializer_class`
"""
view_set = self.viewset()
view_set.request = MockRequest(
user = self.view_user,
organization = self.organization,
viewset = self.viewset
)
view_set.kwargs = self.kwargs
view_set.action = 'list'
view_set.detail = False
mock_return = view_set.get_serializer_class() # Real item to be used as mock return Some
# functions use `Queryset` for additional filtering
setter_not_called = True
with patch.object(self.viewset, 'serializer_class', new_callable=PropertyMock) as qs:
qs.return_value = mock_return
mocked_view_set = self.viewset()
mocked_view_set.kwargs = self.kwargs
mocked_view_set.action = 'list'
mocked_view_set.detail = False
qs.reset_mock() # Just in case
mocked_setup = mocked_view_set.get_serializer_class() # should only add two calls, if exists and the return
for mock_call in list(qs.mock_calls): # mock_calls with args means setter was called
if len(mock_call.args) > 0:
setter_not_called = False
assert setter_not_called
assert qs.call_count == 2

View File

@ -0,0 +1,162 @@
import pytest
from django.test import TestCase
from rest_framework.exceptions import ValidationError, PermissionDenied
from access.models.organization import Organization
from api.serializers.auth_token import AuthToken, AuthTokenModelSerializer
from app.tests.abstract.mock_view import MockView, User
# from core.serializers.manufacturer import Manufacturer, ManufacturerModelSerializer
class ValidationAPI(
TestCase,
):
model = AuthToken
@classmethod
def setUpTestData(self):
"""Setup Test
1. Create an org
2. Create an item
"""
organization = Organization.objects.create(name='test_org')
self.organization = organization
self.user = User.objects.create_user(username="test_user_view", password="password")
self.valid_data = {
'note': 'a note',
'token': self.model().generate,
'user': self.user.id,
'expires': '2025-02-26T00:09Z'
}
self.mock_view = MockView( user = self.user )
self.mock_view.kwargs = {
'model_id': self.user.id
}
self.item = self.model.objects.create(
note = 'object note',
token = self.model().generate,
user = self.user,
expires = '2025-02-26T00:07Z'
)
def test_serializer_validation_valid_data(self):
"""Serializer Validation Check
Ensure that if creating with valid data, the object is created
"""
serializer = AuthTokenModelSerializer(
context = {
'request': self.mock_view.request,
'view': self.mock_view,
},
data = self.valid_data
)
assert serializer.is_valid(raise_exception = True)
def test_serializer_validation_valid_data_different_user(self):
"""Serializer Validation Check
Ensure that if adding the same manufacturer
it raises a validation error
"""
class MockUser:
id = 99
mock_view = MockView( user = self.user )
mock_view.request.user = MockUser()
mock_view.kwargs = {
'model_id': self.user.id
}
with pytest.raises(PermissionDenied) as err:
serializer = AuthTokenModelSerializer(
context = {
'request': mock_view.request,
'view': mock_view,
},
data = self.valid_data
)
serializer.is_valid(raise_exception = True)
assert err.value.get_codes() == 'permission_denied'
def test_serializer_validation_valid_data_token_not_sha256_same_length(self):
"""Serializer Validation Check
Ensure that if adding the same manufacturer
it raises a validation error
"""
valid_data = self.valid_data.copy()
valid_data['token'] = str(valid_data['token'])[:-5] + 'qwert'
with pytest.raises(ValidationError) as err:
serializer = AuthTokenModelSerializer(
context = {
'request': self.mock_view.request,
'view': self.mock_view,
},
data = valid_data
)
serializer.is_valid(raise_exception = True)
assert err.value.get_codes()['token'][0] == 'token_not_sha256'
def test_serializer_validation_valid_data_token_not_sha256_wrong_length(self):
"""Serializer Validation Check
Ensure that if adding the same manufacturer
it raises a validation error
"""
valid_data = self.valid_data.copy()
valid_data['token'] = str(valid_data['token'])[:-5] + 'qwer'
with pytest.raises(ValidationError) as err:
serializer = AuthTokenModelSerializer(
context = {
'request': self.mock_view.request,
'view': self.mock_view,
},
data = valid_data
)
serializer.is_valid(raise_exception = True)
assert err.value.get_codes()['token'][0] == 'token_not_sha256'

View File

@ -0,0 +1,382 @@
from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import reverse
from django.test import Client, TestCase
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.models.tokens import AuthToken
from api.tests.abstract.api_permissions_viewset import (
APIPermissionAdd,
APIPermissionDelete,
APIPermissionView,
)
from api.tests.abstract.api_serializer_viewset import (
SerializerAdd,
SerializerDelete,
SerializerView,
)
from api.tests.abstract.test_metadata_functional import (
MetadataAttributesFunctionalEndpoint,
MetadataAttributesFunctionalBase,
)
class ViewSetBase:
model = AuthToken
app_namespace = 'v2'
url_name = '_api_v2_user_settings_token'
change_data = {'device_model_is_global': True}
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
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.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.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.view_user = User.objects.create_user(username="test_user_view", password="password")
teamuser = TeamUsers.objects.create(
team = view_team,
user = self.view_user
)
self.item = self.model.objects.create(
note = 'a note',
token = self.model().generate,
user = self.view_user,
expires = '2025-02-25T23:14Z'
)
self.item_delete = self.model.objects.create(
note = 'a note',
token = self.model().generate,
user = self.delete_user,
expires = '2025-02-25T23:14Z'
)
# self.item.default_organization = self.organization
# self.item.save()
self.url_view_kwargs = {
'model_id': self.view_user.id,
'pk': self.item.id,
}
self.url_kwargs = {
'model_id': self.view_user.id,
}
self.add_data = {
'note': 'a note',
'token': self.model().generate,
'expires': '2025-02-26T23:14Z'
}
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 PermissionsAPI(
ViewSetBase,
APIPermissionAdd,
APIPermissionDelete,
APIPermissionView,
TestCase,
):
def test_returned_data_from_user_and_global_organizations_only(self):
"""Check items returned
This test case is a over-ride of a test case with the same name.
This model is not a tenancy model making this test not-applicable.
Items returned from the query Must be from the users organization and
global ONLY!
"""
pass
def test_add_has_permission(self):
""" Check correct permission for add
Attempt to add as user with permission
"""
url_kwargs = self.url_kwargs.copy()
url_kwargs['model_id'] = self.add_user.id
client = Client()
if self.url_kwargs:
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = 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
def test_add_permission_view_denied(self):
""" Check correct permission for add
Attempt to add a user with view permission
"""
url_kwargs = self.url_kwargs.copy()
url_kwargs['model_id'] = self.add_user.id
client = Client()
if self.url_kwargs:
url = reverse(self.app_namespace + ':' + self.url_name + '-list', kwargs = 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_delete_has_permission(self):
""" Check correct permission for delete
Delete item as user with delete permission
"""
url_view_kwargs = self.url_view_kwargs.copy()
url_view_kwargs['model_id'] = self.delete_user.id
url_view_kwargs['pk'] = self.item_delete.id
client = Client()
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=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_view_denied(self):
""" Check correct permission for delete
Attempt to delete as user with veiw permission only
"""
url_view_kwargs = self.url_view_kwargs.copy()
url_view_kwargs['model_id'] = self.delete_user.id
url_view_kwargs['pk'] = self.item_delete.id
client = Client()
url = reverse(self.app_namespace + ':' + self.url_name + '-detail', kwargs=url_view_kwargs)
client.force_login(self.view_user)
response = client.delete(url, data=self.delete_data)
assert response.status_code == 403
def test_returned_results_only_user_orgs(self):
"""Test not required
this test is not required as this model is not a tenancy model
"""
pass
def test_view_no_permission_denied(self):
""" Check correct permission for view
This test case is a duplicate of a test case with the same name.
This test is not required for this model as there are no permissions
assosiated with accessing this model.
Attempt to view with user missing permission
"""
pass
class ViewSet(
ViewSetBase,
SerializerAdd,
SerializerDelete,
SerializerView,
TestCase,
):
pass
class Metadata(
ViewSetBase,
MetadataAttributesFunctionalEndpoint,
MetadataAttributesFunctionalBase,
TestCase
):
viewset_type = 'detail'
@classmethod
def setUpTestData(self):
super().setUpTestData()
self.url_kwargs = self.url_view_kwargs

View File

@ -4,7 +4,7 @@ from django.test import Client, TestCase
from rest_framework.permissions import IsAuthenticated
from access.models import Organization
from access.models.organization import Organization
from api.tests.abstract.viewsets import ViewSetCommon
@ -43,6 +43,8 @@ class HomeViewset(
client.force_login(self.view_user)
self.http_options_response_list = client.options(url)
self.kwargs = {}
def test_view_attr_permission_classes_value(self):

View File

@ -1,8 +1,10 @@
from django.contrib.auth.models import User
from django.contrib.auth.models import Permission, User
from django.contrib.contenttypes.models import ContentType
from django.test import Client, TestCase
from access.models import Organization, Team, TeamUsers, Permission
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.react_ui_metadata import ReactUIMetadata

View File

@ -6,11 +6,13 @@ import unittest
from datetime import datetime, timedelta
from django.contrib.auth.models import AnonymousUser, User
from django.contrib.auth.models import AnonymousUser, Permission, User
from django.shortcuts import reverse
from django.test import TestCase, Client
from access.models import Organization, Team, TeamUsers, Permission
from access.models.organization import Organization
from access.models.team import Team
from access.models.team_user import TeamUsers
from api.models.tokens import AuthToken
@ -53,7 +55,7 @@ class APIAuthToken(TestCase):
expires=expires
)
self.api_token_valid = token.generate()
self.api_token_valid = token.generate
self.hashed_token = token.token_hash(self.api_token_valid)
token.token = self.hashed_token
@ -67,7 +69,7 @@ class APIAuthToken(TestCase):
expires = expires.strftime('%Y-%m-%d %H:%M:%S%z')
self.api_token_expired = token.generate()
self.api_token_expired = token.generate
self.hashed_token_expired = token.token_hash(self.api_token_expired)

Some files were not shown because too many files have changed in this diff Show More