Compare commits
48 Commits
Author | SHA1 | Date | |
---|---|---|---|
4c41994068 | |||
6f7b3ffad6 | |||
cc97128e25 | |||
b6ba3d38dc | |||
18b788844a | |||
9d5464b5a9 | |||
7848397ae2 | |||
f298ce94bf | |||
3cace8943e | |||
aa40d68c88 | |||
c0f186db89 | |||
6b35e7808c | |||
467f6fca6b | |||
f86b2d5216 | |||
e29d8e1ec1 | |||
0fc5f41391 | |||
4b29448d84 | |||
e9fe4896df | |||
b9d32a2c16 | |||
d6bd99c5de | |||
7de5ab12bf | |||
3fe09fb8f9 | |||
eb6b03f731 | |||
40e3078a58 | |||
4ba79c6ae9 | |||
b5c31d81d3 | |||
c3b585d416 | |||
84d21f4af8 | |||
262e431834 | |||
cde2562048 | |||
67d853cf25 | |||
3ba6bb5b4b | |||
84d4f48c63 | |||
5e8bebbeb1 | |||
b66a8644a0 | |||
f0ae185fc5 | |||
43b7e413a6 | |||
04dc00d79d | |||
8e6fd58107 | |||
bfe9a95038 | |||
33687791ec | |||
57bc972b0f | |||
f437eeccb8 | |||
4e11ad67d0 | |||
40ba645a35 | |||
27e73e21d1 | |||
a6c0785de0 | |||
83328be22e |
17
.cz.yaml
17
.cz.yaml
@ -1,8 +1,21 @@
|
||||
---
|
||||
commitizen:
|
||||
name: cz_conventional_commits
|
||||
customize:
|
||||
change_type_map:
|
||||
feature: Features
|
||||
fix: Fixes
|
||||
refactor: Refactoring
|
||||
test: Tests
|
||||
change_type_order:
|
||||
- BREAKING CHANGE
|
||||
- feat
|
||||
- fix
|
||||
- test
|
||||
- refactor
|
||||
commit_parser: ^(?P<change_type>feat|fix|test|refactor|perf|BREAKING CHANGE)(?:\((?P<scope>[^()\r\n]*)\)|\()?(?P<breaking>!)?:\s(?P<message>.*)?
|
||||
name: cz_customize
|
||||
prerelease_offset: 1
|
||||
tag_format: $version
|
||||
update_changelog_on_bump: false
|
||||
version: 1.0.0-b6
|
||||
version: 1.0.0
|
||||
version_scheme: semver
|
||||
|
93
.github/workflows/ci.yaml
vendored
93
.github/workflows/ci.yaml
vendored
@ -10,20 +10,33 @@ on:
|
||||
tags:
|
||||
- '*'
|
||||
|
||||
env:
|
||||
GIT_SYNC_URL: "https://${{ secrets.GITLAB_USERNAME_ROBOT }}:${{ secrets.GITLAB_TOKEN_ROBOT }}@gitlab.com/nofusscomputing/projects/centurion_erp.git"
|
||||
|
||||
jobs:
|
||||
|
||||
|
||||
mkdocs:
|
||||
name: 'MKDocs'
|
||||
permissions:
|
||||
pull-requests: write
|
||||
contents: write
|
||||
statuses: write
|
||||
checks: write
|
||||
actions: write
|
||||
uses: nofusscomputing/action_mkdocs/.github/workflows/reusable_mkdocs.yaml@development
|
||||
|
||||
|
||||
docker:
|
||||
name: 'Docker'
|
||||
uses: nofusscomputing/action_docker/.github/workflows/docker.yaml@development
|
||||
with:
|
||||
DOCKER_BUILD_IMAGE_NAME: "nofusscomputing/centurion-erp"
|
||||
DOCKER_PUBLISH_REGISTRY: "ghcr.io"
|
||||
DOCKER_PUBLISH_REGISTRY: "docker.io"
|
||||
DOCKER_PUBLISH_IMAGE_NAME: "nofusscomputing/centurion-erp"
|
||||
secrets:
|
||||
DOCKER_PUBLISH_USERNAME: ${{ secrets.NFC_DOCKERHUB_TOKEN }}
|
||||
DOCKER_PUBLISH_PASSWORD: ${{ secrets.NFC_DOCKERHUB_USERNAME }}
|
||||
DOCKER_PUBLISH_USERNAME: ${{ secrets.NFC_DOCKERHUB_USERNAME }}
|
||||
DOCKER_PUBLISH_PASSWORD: ${{ secrets.NFC_DOCKERHUB_TOKEN }}
|
||||
|
||||
|
||||
python:
|
||||
@ -31,3 +44,77 @@ jobs:
|
||||
uses: nofusscomputing/action_python/.github/workflows/python.yaml@development
|
||||
secrets:
|
||||
WORKFLOW_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
|
||||
|
||||
|
||||
gitlab-mirror:
|
||||
if: ${{ github.repository == 'nofusscomputing/centurion_erp' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
||||
|
||||
- name: Checks
|
||||
shell: bash
|
||||
run: |
|
||||
if [ "0${{ env.GIT_SYNC_URL }}" == "0" ]; then
|
||||
|
||||
echo "[ERROR] you must define variable GIT_SYNC_URL for mirroring this repository.";
|
||||
|
||||
exit 1;
|
||||
|
||||
fi
|
||||
|
||||
|
||||
- name: clone
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
git clone --mirror https://github.com/${{ github.repository }} repo;
|
||||
|
||||
ls -la repo/
|
||||
|
||||
|
||||
- name: add remote
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
cd repo;
|
||||
|
||||
echo "**************************************** - git remote -v";
|
||||
|
||||
git remote -v;
|
||||
|
||||
echo "****************************************";
|
||||
|
||||
git remote add destination $GIT_SYNC_URL;
|
||||
|
||||
|
||||
- name: push branches
|
||||
shell: bash
|
||||
run: |
|
||||
|
||||
cd repo;
|
||||
|
||||
echo "**************************************** - git branch";
|
||||
|
||||
git branch;
|
||||
|
||||
echo "****************************************";
|
||||
|
||||
# git push destination --all --force;
|
||||
|
||||
git push destination --mirror || true;
|
||||
|
||||
|
||||
# - name: push tags
|
||||
# shell: bash
|
||||
# run: |
|
||||
|
||||
# cd repo;
|
||||
|
||||
# echo "**************************************** - git tag";
|
||||
|
||||
# git tag;
|
||||
|
||||
# echo "****************************************";
|
||||
|
||||
# git push destination --tags --force;
|
||||
|
37
.github/workflows/triage.yaml
vendored
Normal file
37
.github/workflows/triage.yaml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
---
|
||||
|
||||
name: Triage
|
||||
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- opened
|
||||
- reopened
|
||||
- transferred
|
||||
- milestoned
|
||||
- demilestoned
|
||||
- closed
|
||||
- assigned
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- edited
|
||||
- assigned
|
||||
- reopened
|
||||
- closed
|
||||
|
||||
|
||||
|
||||
jobs:
|
||||
|
||||
|
||||
project:
|
||||
name: Project
|
||||
uses: nofusscomputing/action_project/.github/workflows/project.yaml@development
|
||||
with:
|
||||
PROJECT_URL: https://github.com/orgs/nofusscomputing/projects/3
|
||||
secrets:
|
||||
WORKFLOW_TOKEN: ${{ secrets.WORKFLOW_TOKEN }}
|
||||
|
@ -32,9 +32,9 @@ include:
|
||||
file:
|
||||
- .gitlab-ci_common.yaml
|
||||
# - template/automagic.gitlab-ci.yaml
|
||||
- local: gitlab-ci/automation/.gitlab-ci-ansible.yaml
|
||||
- local: gitlab-ci/template/mkdocs-documentation.gitlab-ci.yaml
|
||||
- local: gitlab-ci/lint/ansible.gitlab-ci.yaml
|
||||
- automation/.gitlab-ci-ansible.yaml
|
||||
- template/mkdocs-documentation.gitlab-ci.yaml
|
||||
- lint/ansible.gitlab-ci.yaml
|
||||
|
||||
|
||||
|
||||
|
284
CHANGELOG.md
284
CHANGELOG.md
@ -1,63 +1,131 @@
|
||||
## 1.0.0 (2024-08-23)
|
||||
|
||||
## 1.0.0-b14 (2024-08-12)
|
||||
|
||||
### Fixes
|
||||
|
||||
- **api**: ensure model_notes is an available field
|
||||
|
||||
### Tests
|
||||
|
||||
- **access**: test field model_notes
|
||||
|
||||
## 1.0.0-b13 (2024-08-11)
|
||||
|
||||
### Fixes
|
||||
|
||||
- Audit models for validations
|
||||
- **itam**: Ensure device name is formatted according to RFC1035 2.3.1
|
||||
- **itam**: Ensure device UUID is correctly formatted
|
||||
- **config_management**: Ensure that config group can't set self as parent
|
||||
- **settings**: ensure that the api token cant be saved to notes field
|
||||
|
||||
### Tests
|
||||
|
||||
- api field checks
|
||||
- **software**: api field checks
|
||||
|
||||
## 1.0.0-b12 (2024-08-10)
|
||||
|
||||
### Fixes
|
||||
|
||||
- **api**: ensure org mixin is inherited by software view
|
||||
- **base**: correct project links to github
|
||||
|
||||
### Tests
|
||||
|
||||
- api field checks
|
||||
|
||||
#128 #162
|
||||
- **teams**: api field checks
|
||||
- **organization**: api field checks
|
||||
|
||||
## 1.0.0-b11 (2024-08-10)
|
||||
|
||||
## 1.0.0-b10 (2024-08-09)
|
||||
|
||||
## 1.0.0-b9 (2024-08-09)
|
||||
|
||||
## 1.0.0-b8 (2024-08-09)
|
||||
|
||||
## 1.0.0-b7 (2024-08-09)
|
||||
|
||||
## 1.0.0-b6 (2024-08-09)
|
||||
|
||||
## 1.0.0-b5 (2024-07-31)
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- add Config groups to API
|
||||
- **api**: Add device config groups to devices
|
||||
- **api**: Ability to fetch configgroups from api along with config
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **api**: Ensure device groups is read only
|
||||
|
||||
### Tests
|
||||
|
||||
- **api**: Field existence and type checks for device
|
||||
- **api**: test configgroups API fields
|
||||
|
||||
## 1.0.0-b4 (2024-07-29)
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- **swagger**: remove `{format}` suffixed doc entries
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- release-b3 fixes
|
||||
- **api**: cleanup team post/get
|
||||
- **api**: confirm HTTP method is allowed before permission check
|
||||
- **api**: Ensure that organizations can't be created via the API
|
||||
- **access**: Team model class inheritance order corrected
|
||||
|
||||
### Tests
|
||||
|
||||
- confirm that the tenancymanager is called
|
||||
|
||||
## 1.0.0-b3 (2024-07-21)
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **itam**: Limit os version count to devices user has access to
|
||||
|
||||
## 1.0.0-b2 (2024-07-19)
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **itam**: only show os version once
|
||||
|
||||
## 1.0.0-b1 (2024-07-19)
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **itam**: ensure installed operating system count is limited to users organizations
|
||||
- **itam**: ensure installed software count is limited to users organizations
|
||||
|
||||
## 1.0.0-a4 (2024-07-18)
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- **api**: When processing uploaded inventory and name does not match, update name to one within inventory file
|
||||
- **config_management**: Group name to be entire breadcrumb
|
||||
|
||||
### Tests
|
||||
|
||||
- ensure inventory upload matches by both serial number and uuid if device name different
|
||||
- placeholder for moving organization
|
||||
|
||||
## 1.0.0-a3 (2024-07-18)
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- **config_management**: Prevent a config group from being able to change organization
|
||||
- **itam**: On device organization change remove config groups
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **config_management**: dont attempt to do action during save if group being created
|
||||
- **itam**: remove org filter for device so that user can see installations
|
||||
@ -68,13 +136,13 @@
|
||||
|
||||
## 1.0.0-a2 (2024-07-17)
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- **api**: Inventory matching of device second by uuid
|
||||
- **api**: Inventory matching of device first by serial number
|
||||
- **base**: show warning bar if the user has not set a default organization
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **base**: dont show user warning bar for non-authenticated user
|
||||
- **api**: correct inventory operating system selection by name
|
||||
@ -87,25 +155,31 @@
|
||||
|
||||
- squashed DB migrations in preparation for v1.0 release.
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- Administratively set global items org/is_global field now read-only
|
||||
- **access**: Add multi-tennant manager
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **core**: migrate manufacturer to use new form/view logic
|
||||
- **settings**: correct the permission to view manufacturers
|
||||
- **access**: Correct team form fields
|
||||
- **config_management**: don't exclude parent from field, only self
|
||||
|
||||
### Refactor
|
||||
### Refactoring
|
||||
|
||||
- repo preperation for v1.0.0-Alpha-1
|
||||
- Squash database migrations
|
||||
|
||||
### Tests
|
||||
|
||||
- tenancy objects
|
||||
- refactor to single abstract model for inclusion.
|
||||
|
||||
## 0.7.0 (2024-07-14)
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- **core**: Filter every form field if associated with an organization to users organizations only
|
||||
- **core**: add var `template_name` to common view template for all views that require it
|
||||
@ -120,13 +194,14 @@
|
||||
- **ui**: add some navigation icons
|
||||
- **itam**: update inventory status icon
|
||||
- **itam**: ensure device software pagination links keep interface on software tab
|
||||
- "Migrate inventory processing to background worker"
|
||||
- **access**: enable non-organization django permission checks
|
||||
- **settings**: Add celery task results index and view page
|
||||
- **base**: Add background worker
|
||||
- **itam**: Update Serial Number from inventory if present and Serial Number not set
|
||||
- **itam**: Update UUID from inventory if present and UUID not set
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **config_management**: Don't allow a config group to assign itself as its parent
|
||||
- **config_management**: correct permission for deleting a host from config group
|
||||
@ -163,11 +238,14 @@
|
||||
- **itam**: show device model name instead of ID
|
||||
- **api**: Ensure if serial number from inventory is `null` that it's not used
|
||||
- **api**: ensure checked uuid and serial number is used for updating
|
||||
- inventory
|
||||
- **itam**: only remove device software when not found during inventory upload
|
||||
- **itam**: only update software version if different
|
||||
- existing device without uuid not updated when uploading an inventory
|
||||
- Device Software tab pagination does not work
|
||||
- **itam**: correct device software pagination
|
||||
|
||||
### Refactor
|
||||
### Refactoring
|
||||
|
||||
- adjust views missing add/change form to now use forms
|
||||
- add navigation menu expand arrows
|
||||
@ -182,31 +260,57 @@
|
||||
- **api**: migrate inventory processing to background worker
|
||||
- **itam**: only perform actions on device inventory if DB matches inventory item
|
||||
|
||||
### Tests
|
||||
|
||||
- add test test_view_*_attribute_not_exists_fields for add and change views
|
||||
- fix test_view_change_attribute_type_form_class to test if type class
|
||||
- **views**: add test cases for model views
|
||||
- Add Test case abstract classes to models
|
||||
- **inventory**: add mocks?? for calling background worker
|
||||
- **view**: view permission checks
|
||||
- **inventory**: update tests for background worker changes
|
||||
|
||||
## 0.6.0 (2024-06-30)
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- user api token
|
||||
- **api**: API token authentication
|
||||
- **api**: abilty for user to create/delete api token
|
||||
- **api**: create token model
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **user_token**: conduct user check on token view access
|
||||
- **itam**: use same form for edit and add
|
||||
- **itam**: dont add field inventorydate if adding new item
|
||||
- **api**: inventory upload requires sanitization
|
||||
|
||||
### Refactor
|
||||
### Refactoring
|
||||
|
||||
- **settings**: use seperate change/view views
|
||||
- **settings**: use form for user settings
|
||||
- **tests**: move unit tests to unit test sub-directory
|
||||
|
||||
### Tests
|
||||
|
||||
- **token_auth**: test authentication method token
|
||||
- more tests
|
||||
- add .coveragerc to remove non-code files from coverage report
|
||||
- Unit Tests TenancyObjects
|
||||
- Test Cases for TenancyObjects
|
||||
- tests for checking links from rendered templetes
|
||||
- **core**: test cases for notes permissions
|
||||
- **config_management**: config groups history permissions
|
||||
- **api**: Majority of Inventory upload tests
|
||||
- **access**: TenancyObject field tests
|
||||
- **access**: remove skipped api tests for team users
|
||||
|
||||
## 0.5.0 (2024-06-17)
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- Setup Organization Managers
|
||||
- **access**: add notes field to organization
|
||||
- **access**: add organization manger
|
||||
- **config_management**: Use breadcrumbs for child group name display
|
||||
@ -214,6 +318,7 @@
|
||||
- **itam**: add a status of "bad" for devices
|
||||
- **itam**: paginate device software tab
|
||||
- **itam**: status of device visible on device index page
|
||||
- API Browser
|
||||
- **core**: add skeleton http browser
|
||||
- **core**: Add a notes field to manufacturer/ publisher
|
||||
- **itam**: Add a notes field to software category
|
||||
@ -230,24 +335,28 @@
|
||||
- **itam**: add docs icon to devices page
|
||||
- **config_management**: add docs icon to config groups page
|
||||
- **base**: add dynamic docs icon
|
||||
- config group software
|
||||
- **models**: add property parent_object to models that have a parent
|
||||
- **config_management**: add config group software to group history
|
||||
- **itam**: render group software config within device rendered config
|
||||
- **config_management**: assign software action to config group
|
||||
- sso
|
||||
- add configuration value 'SESSION_COOKIE_AGE'
|
||||
- remove development SECRET_KEY and enforce checking for user configured one
|
||||
- **base**: build CSRF trusted origins from configuration
|
||||
- **base**: Enforceable SSO ONLY
|
||||
- **base**: configurable SSO
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **itam**: remove requirement that user needs change device to add notes
|
||||
- **core**: dont attempt to access parent_object if 'None' during history save
|
||||
- **config_management**: Add missing parent item getter to model
|
||||
- **core**: overridden save within SaveHistory to use default attributes
|
||||
- **access**: overridden save to use default attributes
|
||||
- History does not delete when item deleted
|
||||
- **core**: on object delete remove history entries
|
||||
- inventory upload cant determin object organization
|
||||
- **api**: ensure proper permission checking
|
||||
- dont throw an exception during settings load for an item django already checks
|
||||
- **core**: Add overrides for delete so delete history saved for items with parent model
|
||||
@ -255,7 +364,7 @@
|
||||
- **base**: remove social auth from nav menu
|
||||
- **access**: add a team user permissions to use team organization
|
||||
|
||||
### Refactor
|
||||
### Refactoring
|
||||
|
||||
- **access**: relocate permission check to own function
|
||||
- **itam**: move device os tab to details tab
|
||||
@ -269,14 +378,58 @@
|
||||
- login to use base template
|
||||
- adjust template block names
|
||||
|
||||
### Tests
|
||||
|
||||
- **access**: team user model permission check for organization manager
|
||||
- **access**: team model permission check for organization manager
|
||||
- **access**: organization model permission check for organization manager
|
||||
- **access**: add test cases for model delete as organization manager
|
||||
- **access**: add test cases for model addd as organization manager
|
||||
- **access**: add test cases for model change as organization manager
|
||||
- **access**: add test cases for model view as organization manager
|
||||
- write some more
|
||||
- **core**: skip invalid tests
|
||||
- **itam**: tests for device type history entries
|
||||
- **core**: tests for manufacturer history entries
|
||||
- move manufacturer to it's parent
|
||||
- refactor api model permission tests to use an abstract class of test cases
|
||||
- move tests to the module they belong to
|
||||
- refactor history permission tests to use an abstract class of test cases
|
||||
- refactor model permission tests to use an abstract class of test cases
|
||||
- refactor history entry to have test cases in abstract classes
|
||||
- **itam**: history entry tests for software category
|
||||
- **itam**: history entry tests for device operating system version
|
||||
- **itam**: history entry tests for device operating system
|
||||
- **itam**: history entry tests for device software
|
||||
- **itam**: ensure child history is removed on config group software delete
|
||||
- add placeholder tests
|
||||
- **itam**: ensure history is removed on software delete
|
||||
- **itam**: ensure history is removed on operating system delete
|
||||
- **itam**: ensure history is removed on device model delete
|
||||
- **config_management**: test history on delete for config groups
|
||||
- **itam**: ensure history is removed on device delete
|
||||
- **access**: test team history
|
||||
- **access**: ensure team user history is created and removed as required
|
||||
- **access**: ensure history is removed on team delete
|
||||
- **access**: ensure history is removed on item delete
|
||||
- **api**: Inventory upload permission checks
|
||||
- **config_management**: testing of config_groups rendered config
|
||||
- **config_management**: history save tests for config groups software
|
||||
- **config_management**: config group software permission for add, change and delete
|
||||
- **base**: placeholder tests for config groups software
|
||||
- **base**: basic test for merge_software helper
|
||||
- during unit tests add SECRET_KEY
|
||||
|
||||
## 0.4.0 (2024-06-05)
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- 2024 06 05
|
||||
- **database**: add mysql support
|
||||
- **api**: move invneotry api endpoint to '/api/device/inventory'
|
||||
- **core**: support more history types
|
||||
- **core**: function to fetch history entry item
|
||||
- 2024 06 02
|
||||
- **config_management**: Add button to groups ui for adding child group
|
||||
- **access**: throw error if no organization added
|
||||
- **itam**: add delete button to config group within ui
|
||||
@ -289,10 +442,12 @@
|
||||
- **api**: add swagger ui for documentation
|
||||
- **api**: filter software to users organizations
|
||||
- **api**: filter devices to users organizations
|
||||
- randomz
|
||||
- **api**: add org team view page
|
||||
- API configuration of permissions
|
||||
- **api**: configure team permissions
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **itam**: ensure device type saves history
|
||||
- **core**: correct history view permissions
|
||||
@ -309,7 +464,7 @@
|
||||
- **api**: correct reverse url lookup to use NS API
|
||||
- **api**: permissions for organization
|
||||
|
||||
### Refactor
|
||||
### Refactoring
|
||||
|
||||
- **access**: cache object so it doesnt have to be called multiple times
|
||||
- **config_management**: move groups to nav menu
|
||||
@ -317,20 +472,49 @@
|
||||
- **api**: move permission check to mixin
|
||||
- **access**: add team option to org permission check
|
||||
|
||||
### Tests
|
||||
|
||||
- **api**: placeholder test for inventory
|
||||
- **settings**: access permission check for app settings
|
||||
- **settings**: history view permission check for software category
|
||||
- **settings**: history view permission check for manufacturer
|
||||
- **settings**: history view permission check for device type
|
||||
- **settings**: user settings
|
||||
- **settings**: view permission check for user settings
|
||||
- refactor core test layout
|
||||
- **itam**: view permission check for software
|
||||
- **itam**: view permission check for operating system
|
||||
- **itam**: view permission check for device model
|
||||
- **itam**: view permission check for device
|
||||
- **config_management**: view permission check for config_groups
|
||||
- **access**: view permission check for team
|
||||
- **access**: view permission check for organization
|
||||
- add history entry creation tests for most models
|
||||
- **config_management**: when adding a host to config group filter out host that are already members of the group
|
||||
- **config_management**: unit test for config groups model to ensure permissions are working
|
||||
- **api**: remove tests for os and manufacturer as they are not used in api
|
||||
- **api**: check model permissions for software
|
||||
- **api**: check model permissions for devices
|
||||
- **api**: check model permissions for teams
|
||||
- **api**: check model permissions for organizations
|
||||
|
||||
## 0.3.0 (2024-05-29)
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- Randomz
|
||||
- **access**: during organization permission check, check to ensure user is logged on
|
||||
- **history**: always create an entry even if user=none
|
||||
- **itam**: device uuid must be unique
|
||||
- **itam**: device serial number must be unique
|
||||
- 2024 05 26
|
||||
- **setting**: Enable super admin to set ALL manufacturer/publishers as global
|
||||
- **setting**: Enable super admin to set ALL device types as global
|
||||
- **setting**: Enable super admin to set ALL device models as global
|
||||
- **setting**: Enable super admin to set ALL software categories as global
|
||||
- **UI**: show build details with page footer
|
||||
- **software**: Add output to stdout to show what is and has occurred
|
||||
- 2024 05 25
|
||||
- **base**: Add delete icon to content header
|
||||
- **itam**: Populate initial organization value from user default organization for software category creation
|
||||
- **itam**: Populate initial organization value from user default organization for device type creation
|
||||
@ -342,17 +526,20 @@
|
||||
- Add management command software
|
||||
- **setting**: Enable super admin to set ALL software as global
|
||||
- **user**: Add user settings panel
|
||||
- Manufacturer and Model Information
|
||||
- **itam**: Add publisher to software
|
||||
- **itam**: Add publisher to operating system
|
||||
- **itam**: Add device model
|
||||
- **core**: Add manufacturers
|
||||
- **settings**: add dummy model for permissions
|
||||
- **settings**: new module for whole of application settings/globals
|
||||
- 2024 05 21-23
|
||||
- **access**: Save changes to history for organization and teams
|
||||
- **software**: Save changes to history
|
||||
- **operating_system**: Save changes to history
|
||||
- **device**: Save changes to history
|
||||
- **core**: history model for saving model history
|
||||
- 2024 05 19/20
|
||||
- **itam**: Ability to add notes to software
|
||||
- **itam**: Ability to add notes to operating systems
|
||||
- **itam**: Ability to add notes on devices
|
||||
@ -361,7 +548,7 @@
|
||||
- **ui**: Show inventory details if they exist
|
||||
- **api**: API accept computer inventory
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **settings**: Add correct permissions for team user delete
|
||||
- **settings**: Add correct permissions for team user view/change
|
||||
@ -412,7 +599,7 @@
|
||||
- correct typo in notes templates
|
||||
- **ui**: Ensure navigation menu entry highlighted for sub items
|
||||
|
||||
### Refactor
|
||||
### Refactoring
|
||||
|
||||
- **access**: add to models a get_organization function
|
||||
- **access**: remove change view
|
||||
@ -424,13 +611,33 @@
|
||||
- **itam**: move device types to settings app
|
||||
- **template**: content_title can be rendered in base
|
||||
|
||||
### Tests
|
||||
|
||||
- cleanup duplicate tests and minor reshuffle
|
||||
- **access**: unit testing team user permissions
|
||||
- **access**: unit testing team permissions
|
||||
- **settings**: unit testing manufacturer permissions
|
||||
- **settings**: unit testing software category permissions
|
||||
- **device_model**: unit testing device type permissions
|
||||
- **device_model**: unit testing device model permissions
|
||||
- **organization**: unit testing organization permissions
|
||||
- **operating_system**: unit testing operating system permissions
|
||||
- **software**: unit testing software permissions
|
||||
- **device**: unit testing device permissions
|
||||
- adjust test layout and update contributing
|
||||
- **core**: placeholder tests for history component
|
||||
- **core**: place holder tests for notes model
|
||||
- **api**: add placeholder tests for inventory
|
||||
|
||||
## 0.2.0 (2024-05-18)
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- 2024 05 18
|
||||
- **itam**: Add Operating System to ITAM models
|
||||
- **api**: force content type to be JSON for req/resp
|
||||
- **software**: view software
|
||||
- 2024 05 17
|
||||
- **device**: Prevent devices from being set global
|
||||
- **software**: if no installations found, denote
|
||||
- **device**: configurable software version
|
||||
@ -442,21 +649,24 @@
|
||||
- **software**: add pagination for index
|
||||
- **device**: add pagination for index
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **device**: correct software link
|
||||
|
||||
## 0.1.0 (2024-05-17)
|
||||
|
||||
### Feat
|
||||
### feat
|
||||
|
||||
- API token auth
|
||||
- **api**: initial token authentication implementation
|
||||
- itam and API setup
|
||||
- **docker**: add settings to store data in separate volume
|
||||
- **django**: add split settings for specifying additional settings paths
|
||||
- **api**: Add device config to device
|
||||
- **itam**: add organization to device installs
|
||||
- **itam**: migrate app from own repo
|
||||
- Enable API by default
|
||||
- Genesis
|
||||
- **admin**: remove team management
|
||||
- **admin**: remove group management
|
||||
- **access**: adjustable team permissions
|
||||
@ -490,14 +700,14 @@
|
||||
- **template**: add base template
|
||||
- **django**: add organizations app
|
||||
|
||||
### Fix
|
||||
### Fixes
|
||||
|
||||
- **itam**: device software to come from device org or global not users orgs
|
||||
- **access**: correct team required permissions
|
||||
- **fields**: correct autoslug field so it works
|
||||
- **docker**: build wheels then install
|
||||
|
||||
### Refactor
|
||||
### Refactoring
|
||||
|
||||
- button to use same selection colour
|
||||
- **access**: remove inline form for org teams
|
||||
@ -506,4 +716,8 @@
|
||||
- **views**: move views to own directory
|
||||
- **access**: addjust org and teams to use different view per action
|
||||
|
||||
### Tests
|
||||
|
||||
- interim unit tests
|
||||
|
||||
## 0.0.1 (2024-05-06)
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<br>
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
[](https://hub.docker.com/r/nofusscomputing/centurion-erp) [](https://artifacthub.io/packages/container/centurion-erp/centurion-erp)
|
||||
@ -32,17 +32,18 @@ This project is hosted on [Github](https://github.com/NofussComputing/centurion_
|
||||
|
||||
**Stable Branch**
|
||||
|
||||
  
|
||||
  
|
||||

|
||||
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Development Branch**
|
||||
|
||||
|
||||
|
||||
  
|
||||
  
|
||||

|
||||
|
||||
|
||||
|
371
app/access/tests/unit/organization/test_organizaiton_api.py
Normal file
371
app/access/tests/unit/organization/test_organizaiton_api.py
Normal file
@ -0,0 +1,371 @@
|
||||
import pytest
|
||||
import unittest
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.relations import Hyperlink
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
|
||||
|
||||
class OrganizationAPI(TestCase):
|
||||
|
||||
model = Organization
|
||||
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_organization'
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user and item
|
||||
. create an organization that is different to item
|
||||
2. Create a device
|
||||
3. create teams with each permission: view, add, change, delete
|
||||
4. create a user per team
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
|
||||
self.item = organization
|
||||
|
||||
self.url_view_kwargs = {'pk': self.item.id}
|
||||
|
||||
self.url_kwargs = {'pk': self.item.id}
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name, kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
self.api_data = response.data
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
name field must exist
|
||||
"""
|
||||
|
||||
assert 'name' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['name']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_teams(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams field must exist
|
||||
"""
|
||||
|
||||
assert 'teams' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_teams(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams field must be list
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams']) is list
|
||||
|
||||
|
||||
def test_api_field_exists_url(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
url field must exist
|
||||
"""
|
||||
|
||||
assert 'url' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_url(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
url field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['url']) is Hyperlink
|
||||
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_teams_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams.id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data['teams'][0]
|
||||
|
||||
|
||||
def test_api_field_type_teams_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams.id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams'][0]['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_teams_team_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams.team_name field must exist
|
||||
"""
|
||||
|
||||
assert 'team_name' in self.api_data['teams'][0]
|
||||
|
||||
|
||||
def test_api_field_type_teams_team_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams.team_name field must be string
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams'][0]['team_name']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_teams_permissions(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams.permissions field must exist
|
||||
"""
|
||||
|
||||
assert 'permissions' in self.api_data['teams'][0]
|
||||
|
||||
|
||||
def test_api_field_type_teams_permissions(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams.permissions field must be list
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams'][0]['permissions']) is list
|
||||
|
||||
|
||||
def test_api_field_exists_teams_permissions_url(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams.permissions_url field must exist
|
||||
"""
|
||||
|
||||
assert 'permissions_url' in self.api_data['teams'][0]
|
||||
|
||||
|
||||
def test_api_field_type_teams_permissions_url(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams.permissions_url field must be url
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams'][0]['permissions_url']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_teams_url(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams.url field must exist
|
||||
"""
|
||||
|
||||
assert 'url' in self.api_data['teams'][0]
|
||||
|
||||
|
||||
def test_api_field_type_teams_url(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams.url field must be url
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams'][0]['url']) is str
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_teams_permissions_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams.permissions.id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data['teams'][0]['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_teams_permissions_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams.permissions.id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams'][0]['permissions'][0]['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_teams_permissions_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams.permissions.name field must exist
|
||||
"""
|
||||
|
||||
assert 'name' in self.api_data['teams'][0]['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_teams_permissions_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams.permissions.name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams'][0]['permissions'][0]['name']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_teams_permissions_codename(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams.permissions.codename field must exist
|
||||
"""
|
||||
|
||||
assert 'codename' in self.api_data['teams'][0]['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_teams_permissions_codename(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams.permissions.codename field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams'][0]['permissions'][0]['codename']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_teams_permissions_content_type(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams.permissions.content_type field must exist
|
||||
"""
|
||||
|
||||
assert 'content_type' in self.api_data['teams'][0]['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_teams_permissions_content_type(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams.permissions.content_type field must be dict
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams'][0]['permissions'][0]['content_type']) is dict
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_teams_permissions_content_type_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams.permissions.content_type.id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data['teams'][0]['permissions'][0]['content_type']
|
||||
|
||||
|
||||
def test_api_field_type_teams_permissions_content_type_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams.permissions.content_type.id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams'][0]['permissions'][0]['content_type']['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_teams_permissions_content_type_app_label(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams.permissions.content_type.app_label field must exist
|
||||
"""
|
||||
|
||||
assert 'app_label' in self.api_data['teams'][0]['permissions'][0]['content_type']
|
||||
|
||||
|
||||
def test_api_field_type_teams_permissions_content_type_app_label(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams.permissions.content_type.app_label field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams'][0]['permissions'][0]['content_type']['app_label']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_teams_permissions_content_type_model(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
teams.permissions.content_type.model field must exist
|
||||
"""
|
||||
|
||||
assert 'model' in self.api_data['teams'][0]['permissions'][0]['content_type']
|
||||
|
||||
|
||||
def test_api_field_type_teams_permissions_content_type_model(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
teams.permissions.content_type.model field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['teams'][0]['permissions'][0]['content_type']['model']) is str
|
313
app/access/tests/unit/team/test_team_api.py
Normal file
313
app/access/tests/unit/team/test_team_api.py
Normal file
@ -0,0 +1,313 @@
|
||||
import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.relations import Hyperlink
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
# from api.tests.abstract.api_permissions import APIPermissions
|
||||
|
||||
|
||||
|
||||
class TeamAPI(TestCase):
|
||||
|
||||
model = Team
|
||||
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = '_api_team'
|
||||
|
||||
# url_list = '_api_organization_teams'
|
||||
|
||||
# change_data = {'name': 'device'}
|
||||
|
||||
# delete_data = {'device': 'device'}
|
||||
|
||||
@classmethod
|
||||
def setUpTestData(self):
|
||||
"""Setup Test
|
||||
|
||||
1. Create an organization for user and item
|
||||
. create an organization that is different to item
|
||||
2. Create a team
|
||||
3. create teams with each permission: view, add, change, delete
|
||||
4. create a user per team
|
||||
"""
|
||||
|
||||
organization = Organization.objects.create(name='test_org')
|
||||
|
||||
self.organization = organization
|
||||
|
||||
different_organization = Organization.objects.create(name='test_different_organization')
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization=organization,
|
||||
team_name = 'teamone',
|
||||
model_notes = 'random note'
|
||||
)
|
||||
|
||||
|
||||
self.url_kwargs = {'organization_id': self.organization.id}
|
||||
|
||||
self.url_view_kwargs = {'organization_id': self.organization.id, 'group_ptr_id': self.item.id}
|
||||
|
||||
self.add_data = {'team_name': 'team_post'}
|
||||
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
# view_team = Team.objects.create(
|
||||
# team_name = 'view_team',
|
||||
# organization = organization,
|
||||
# )
|
||||
|
||||
self.item.permissions.set([view_permissions])
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = self.item,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name, kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
self.api_data = response.data
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_team_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
team_name field must exist
|
||||
"""
|
||||
|
||||
assert 'team_name' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
team_name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['team_name']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_model_notes(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
model_notes field must exist
|
||||
"""
|
||||
|
||||
assert 'model_notes' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_model_notes(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
model_notes field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['model_notes']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_url(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
url field must exist
|
||||
"""
|
||||
|
||||
assert 'url' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_url(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
url field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['url']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_permissions(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions field must exist
|
||||
"""
|
||||
|
||||
assert 'permissions' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_permissions(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
url field must be list
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions']) is list
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_permissions_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.name field must exist
|
||||
"""
|
||||
|
||||
assert 'name' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_permissions_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['name']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_codename(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.codename field must exist
|
||||
"""
|
||||
|
||||
assert 'codename' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_permissions_codename(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.codename field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['codename']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_content_type(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.content_type field must exist
|
||||
"""
|
||||
|
||||
assert 'content_type' in self.api_data['permissions'][0]
|
||||
|
||||
|
||||
def test_api_field_type_permissions_content_type(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.content_type field must be dict
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['content_type']) is dict
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_content_type_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.content_type.id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data['permissions'][0]['content_type']
|
||||
|
||||
|
||||
def test_api_field_type_permissions_content_type_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.content_type.id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['content_type']['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_content_type_app_label(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.content_type.app_label field must exist
|
||||
"""
|
||||
|
||||
assert 'app_label' in self.api_data['permissions'][0]['content_type']
|
||||
|
||||
|
||||
def test_api_field_type_permissions_content_type_app_label(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.content_type.app_label field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['content_type']['app_label']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_permissions_content_type_model(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
permissions.content_type.model field must exist
|
||||
"""
|
||||
|
||||
assert 'model' in self.api_data['permissions'][0]['content_type']
|
||||
|
||||
|
||||
def test_api_field_type_permissions_content_type_model(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
permissions.content_type.model field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['permissions'][0]['content_type']['model']) is str
|
@ -5,6 +5,8 @@ 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
|
||||
@ -14,6 +16,37 @@ from access.models import TenancyObject
|
||||
class AuthToken(models.Model):
|
||||
|
||||
|
||||
def validate_note_no_token(self, note, token):
|
||||
""" Ensure plaintext token cant be saved to notes field.
|
||||
|
||||
called from app.settings.views.user_settings.TokenAdd.form_valid()
|
||||
|
||||
Args:
|
||||
note (Field): _Note field_
|
||||
token (Field): _Token field_
|
||||
|
||||
Raises:
|
||||
ValidationError: _Validation failed_
|
||||
"""
|
||||
|
||||
validation: bool = True
|
||||
|
||||
|
||||
if str(note) == str(token):
|
||||
|
||||
validation = False
|
||||
|
||||
|
||||
if str(token)[:9] in str(note): # Allow user to use up to 8 chars so they can reference it.
|
||||
|
||||
validation = False
|
||||
|
||||
if not validation:
|
||||
|
||||
raise ValidationError('Token can not be placed in the notes field.')
|
||||
|
||||
|
||||
|
||||
id = models.AutoField(
|
||||
primary_key=True,
|
||||
unique=True,
|
||||
|
@ -15,6 +15,7 @@ class TeamSerializerBase(serializers.ModelSerializer):
|
||||
model = Team
|
||||
fields = (
|
||||
'team_name',
|
||||
'model_notes',
|
||||
'permissions',
|
||||
'url',
|
||||
)
|
||||
@ -75,6 +76,7 @@ class TeamSerializer(TeamSerializerBase):
|
||||
fields = (
|
||||
"id",
|
||||
"team_name",
|
||||
'model_notes',
|
||||
'permissions',
|
||||
'permissions_url',
|
||||
'url',
|
||||
|
@ -3,6 +3,8 @@ from django.shortcuts import get_object_or_404
|
||||
|
||||
from rest_framework import generics, viewsets
|
||||
|
||||
from access.mixin import OrganizationMixin
|
||||
|
||||
from api.serializers.itam.software import SoftwareSerializer
|
||||
from api.views.mixin import OrganizationPermissionAPI
|
||||
|
||||
@ -10,7 +12,7 @@ from itam.models.software import Software
|
||||
|
||||
|
||||
|
||||
class SoftwareViewSet(viewsets.ModelViewSet):
|
||||
class SoftwareViewSet(OrganizationMixin, viewsets.ModelViewSet):
|
||||
|
||||
permission_classes = [
|
||||
OrganizationPermissionAPI
|
||||
|
@ -195,6 +195,12 @@ class ConfigGroups(GroupsCommonFields, SaveHistory):
|
||||
# Prevent organization change. ToDo: add feature so that config can change organizations
|
||||
self.organization = obj.organization
|
||||
|
||||
if self.parent is not None:
|
||||
|
||||
if self.pk == self.parent.pk:
|
||||
|
||||
raise ValidationError('Can not set self as parent')
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
import json
|
||||
import re
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from django.db import models
|
||||
from django.forms import ValidationError
|
||||
|
||||
from access.fields import *
|
||||
from access.models import TenancyObject
|
||||
@ -18,6 +20,8 @@ from itam.models.operating_system import OperatingSystemVersion
|
||||
|
||||
from settings.models.app_settings import AppSettings
|
||||
|
||||
|
||||
|
||||
class DeviceType(DeviceCommonFieldsName, SaveHistory):
|
||||
|
||||
|
||||
@ -39,6 +43,35 @@ class DeviceType(DeviceCommonFieldsName, SaveHistory):
|
||||
|
||||
class Device(DeviceCommonFieldsName, SaveHistory):
|
||||
|
||||
|
||||
def validate_uuid_format(self):
|
||||
|
||||
pattern = r'[0-9|a-f]{8}\-[0-9|a-f]{4}\-[0-9|a-f]{4}\-[0-9|a-f]{4}\-[0-9|a-f]{12}'
|
||||
|
||||
if not re.match(pattern, str(self)):
|
||||
|
||||
raise ValidationError(f'UUID Must be in {str(pattern)}')
|
||||
|
||||
|
||||
def validate_hostname_format(self):
|
||||
|
||||
pattern = r'^[a-z]{1}[a-z|0-9|\-]+[a-z|0-9]{1}$'
|
||||
|
||||
if not re.match(pattern, str(self).lower()):
|
||||
|
||||
raise ValidationError(
|
||||
'''[RFC1035 2.3.1] A hostname must start with a letter, end with a letter or digit,
|
||||
and have as interior characters only letters, digits, and hyphen.'''
|
||||
)
|
||||
|
||||
|
||||
name = models.CharField(
|
||||
blank = False,
|
||||
max_length = 50,
|
||||
unique = True,
|
||||
validators = [ validate_hostname_format ]
|
||||
)
|
||||
|
||||
serial_number = models.CharField(
|
||||
verbose_name = 'Serial Number',
|
||||
max_length = 50,
|
||||
@ -58,6 +91,7 @@ class Device(DeviceCommonFieldsName, SaveHistory):
|
||||
blank = True,
|
||||
unique = True,
|
||||
help_text = 'System GUID/UUID.',
|
||||
validators = [ validate_uuid_format ]
|
||||
)
|
||||
|
||||
device_model = models.ForeignKey(
|
||||
|
294
app/itam/tests/unit/software/test_software_api.py
Normal file
294
app/itam/tests/unit/software/test_software_api.py
Normal file
@ -0,0 +1,294 @@
|
||||
import pytest
|
||||
import unittest
|
||||
import requests
|
||||
|
||||
from django.contrib.auth.models import AnonymousUser, User
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.shortcuts import reverse
|
||||
from django.test import Client, TestCase
|
||||
|
||||
from rest_framework.relations import Hyperlink
|
||||
|
||||
from access.models import Organization, Team, TeamUsers, Permission
|
||||
|
||||
from core.models.manufacturer import Manufacturer
|
||||
|
||||
from itam.models.software import Software, SoftwareCategory
|
||||
|
||||
|
||||
class SoftwareAPI(TestCase):
|
||||
|
||||
|
||||
model = Software
|
||||
|
||||
app_namespace = 'API'
|
||||
|
||||
url_name = 'software-detail'
|
||||
|
||||
|
||||
@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 software
|
||||
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')
|
||||
|
||||
category = SoftwareCategory.objects.create(
|
||||
name='a category'
|
||||
)
|
||||
|
||||
publisher = Manufacturer.objects.create(
|
||||
name='a manufacturer'
|
||||
)
|
||||
|
||||
|
||||
self.item = self.model.objects.create(
|
||||
organization=organization,
|
||||
name = 'softwareone',
|
||||
model_notes = 'random str',
|
||||
category = category,
|
||||
publisher = publisher
|
||||
)
|
||||
|
||||
self.url_view_kwargs = {'pk': self.item.id}
|
||||
|
||||
view_permissions = Permission.objects.get(
|
||||
codename = 'view_' + self.model._meta.model_name,
|
||||
content_type = ContentType.objects.get(
|
||||
app_label = self.model._meta.app_label,
|
||||
model = self.model._meta.model_name,
|
||||
)
|
||||
)
|
||||
|
||||
view_team = Team.objects.create(
|
||||
team_name = 'view_team',
|
||||
organization = organization,
|
||||
)
|
||||
|
||||
view_team.permissions.set([view_permissions])
|
||||
|
||||
|
||||
self.view_user = User.objects.create_user(username="test_user_view", password="password")
|
||||
teamuser = TeamUsers.objects.create(
|
||||
team = view_team,
|
||||
user = self.view_user
|
||||
)
|
||||
|
||||
client = Client()
|
||||
url = reverse(self.app_namespace + ':' + self.url_name, kwargs=self.url_view_kwargs)
|
||||
|
||||
|
||||
client.force_login(self.view_user)
|
||||
response = client.get(url)
|
||||
|
||||
self.api_data = response.data
|
||||
|
||||
|
||||
|
||||
def test_api_field_exists_id(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
id field must exist
|
||||
"""
|
||||
|
||||
assert 'id' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_id(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
id field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['id']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_url(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
url field must exist
|
||||
"""
|
||||
|
||||
assert 'url' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_url(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
url field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['url']) is Hyperlink
|
||||
|
||||
|
||||
def test_api_field_exists_is_global(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
is_global field must exist
|
||||
"""
|
||||
|
||||
assert 'is_global' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_is_global(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
is_global field must be boolean
|
||||
"""
|
||||
|
||||
assert type(self.api_data['is_global']) is bool
|
||||
|
||||
|
||||
def test_api_field_exists_model_notes(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
model_notes field must exist
|
||||
"""
|
||||
|
||||
assert 'model_notes' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_model_notes(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
model_notes field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['model_notes']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_name(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
name field must exist
|
||||
"""
|
||||
|
||||
assert 'name' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_name(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
name field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['name']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_slug(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
slug field must exist
|
||||
"""
|
||||
|
||||
assert 'slug' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_slug(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
slug field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['slug']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_created(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
created field must exist
|
||||
"""
|
||||
|
||||
assert 'created' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_created(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
created field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['created']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_modified(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
modified field must exist
|
||||
"""
|
||||
|
||||
assert 'modified' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_modified(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
modified field must be str
|
||||
"""
|
||||
|
||||
assert type(self.api_data['modified']) is str
|
||||
|
||||
|
||||
def test_api_field_exists_organization(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
organization field must exist
|
||||
"""
|
||||
|
||||
assert 'organization' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_organization(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
organization field must be intt
|
||||
"""
|
||||
|
||||
assert type(self.api_data['organization']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_publisher(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
publisher field must exist
|
||||
"""
|
||||
|
||||
assert 'publisher' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_publisher(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
publisher field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['publisher']) is int
|
||||
|
||||
|
||||
def test_api_field_exists_category(self):
|
||||
""" Test for existance of API Field
|
||||
|
||||
category field must exist
|
||||
"""
|
||||
|
||||
assert 'category' in self.api_data
|
||||
|
||||
|
||||
def test_api_field_type_category(self):
|
||||
""" Test for type for API Field
|
||||
|
||||
category field must be int
|
||||
"""
|
||||
|
||||
assert type(self.api_data['category']) is int
|
||||
|
@ -101,6 +101,8 @@ class TokenAdd(AddView):
|
||||
form.instance.user = self.request.user
|
||||
form.instance.token = form.instance.token_hash(form.fields['gen_token'].initial)
|
||||
|
||||
self.model.validate_note_no_token(self, note=form.instance.note, token=form.fields['gen_token'].initial)
|
||||
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
|
@ -133,7 +133,7 @@ section h2 span svg {
|
||||
<a title="Swagger API Documentation" href="/api/swagger/" target="_blank">
|
||||
{% include 'icons/swagger_docs.svg' %}
|
||||
</a>
|
||||
<a title="Code Home" href="https://gitlab.com/nofusscomputing/projects/centurion_erp" target="_blank">
|
||||
<a title="Code Home" href="{{ build_details.project_url }}" target="_blank">
|
||||
{% include 'icons/git.svg' %}
|
||||
</a>
|
||||
</span>
|
||||
@ -147,7 +147,7 @@ section h2 span svg {
|
||||
{% else %}
|
||||
development
|
||||
{% endif %}
|
||||
( {% if build_details.project_url %}<a href="{{ build_details.project_url }}/-/commit/{{ build_details.sha }}" target="_blank">{% endif %}
|
||||
( {% if build_details.project_url %}<a href="{{ build_details.project_url }}/commit/{{ build_details.sha }}" target="_blank">{% endif %}
|
||||
{{ build_details.sha }}
|
||||
{% if build_details.project_url %}</a>{% endif %} )
|
||||
</span>
|
||||
|
@ -2,5 +2,5 @@
|
||||
<span class="icon-issue">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="25px" viewBox="0 -960 960 960" width="25px"><path d="M480-144q-60 0-109-32.5T302-264h-74q-15.3 0-25.65-10.29Q192-284.58 192-299.79t10.35-25.71Q212.7-336 228-336h60v-60h-60q-15.3 0-25.65-10.29Q192-416.58 192-431.79t10.35-25.71Q212.7-468 228-468h60v-60h-60q-15.3 0-25.65-10.29Q192-548.58 192-563.79t10.35-25.71Q212.7-600 228-600h74q8-26 25.8-47.09Q345.6-668.18 369-684l-56-56q-11-11-10.5-25.5T314-791q11-11 25-11t25 11l76 75q19.86-5 40.43-5t40.57 5l75-75q11-11 25.67-11 14.66 0 25.33 11 11 11 11 25.5T647-740l-56 56q23 16 40 37t27 47h74q15.3 0 25.65 10.29Q768-579.42 768-564.21t-10.35 25.71Q747.3-528 732-528h-60v60h60q15.3 0 25.65 10.29Q768-447.42 768-432.21t-10.35 25.71Q747.3-396 732-396h-60v60h60q15.3 0 25.65 10.29Q768-315.42 768-300.21t-10.35 25.71Q747.3-264 732-264h-74q-20 55-69 87.5T480-144Zm0-72q48.67 0 83.34-35Q598-286 600-336v-192q2-50-33.5-85t-86-35q-50.5 0-85 35T360-528v192q-1 50 34 85t86 35Zm-36.09-120h71.83q15.26 0 25.76-10.29 10.5-10.29 10.5-25.5t-10.32-25.71Q531.35-408 516.09-408h-71.83q-15.26 0-25.76 10.29-10.5 10.29-10.5 25.5t10.32 25.71q10.33 10.5 25.59 10.5Zm0-120h71.83q15.26 0 25.76-10.29 10.5-10.29 10.5-25.5t-10.32-25.71Q531.35-528 516.09-528h-71.83q-15.26 0-25.76 10.29-10.5 10.29-10.5 25.5t10.32 25.71q10.33 10.5 25.59 10.5ZM480-430Z"/></svg>
|
||||
</span>
|
||||
<a href="https://gitlab.com/nofusscomputing/projects/django_template/-/issues/{{ issue }}" target="_blank"> see #{{ issue }}</a>
|
||||
<a href="https://github.com/nofusscomputing/centurion_erp/issues/{{ issue }}" target="_blank"> see #{{ issue }}</a>
|
||||
</span>
|
10
dockerfile
10
dockerfile
@ -5,6 +5,11 @@ ARG CI_COMMIT_TAG=''
|
||||
FROM python:3.11-alpine3.19 as build
|
||||
|
||||
|
||||
RUN pip --disable-pip-version-check list --outdated --format=json | \
|
||||
python -c "import json, sys; print('\n'.join([x['name'] for x in json.load(sys.stdin)]))" | \
|
||||
xargs -n1 pip install --no-cache-dir -U;
|
||||
|
||||
|
||||
RUN apk add --update \
|
||||
bash \
|
||||
git \
|
||||
@ -83,7 +88,10 @@ COPY --from=build /tmp/python_builds /tmp/python_builds
|
||||
|
||||
COPY includes/ /
|
||||
|
||||
RUN apk update --no-cache; \
|
||||
RUN pip --disable-pip-version-check list --outdated --format=json | \
|
||||
python -c "import json, sys; print('\n'.join([x['name'] for x in json.load(sys.stdin)]))" | \
|
||||
xargs -n1 pip install --no-cache-dir -U; \
|
||||
apk update --no-cache; \
|
||||
apk add --no-cache \
|
||||
mariadb-client \
|
||||
mariadb-dev \
|
||||
|
Binary file not shown.
After Width: | Height: | Size: 149 KiB |
@ -0,0 +1,94 @@
|
||||
<mxfile host="app.diagrams.net" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36" version="24.7.7">
|
||||
<diagram name="Page-1" id="0p1o9D85wg9-GzEIQXPA">
|
||||
<mxGraphModel dx="2049" dy="1094" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="1169" pageHeight="827" math="0" shadow="0">
|
||||
<root>
|
||||
<mxCell id="0" />
|
||||
<mxCell id="1" parent="0" />
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-24" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#E0E0E0;" vertex="1" parent="1">
|
||||
<mxGeometry x="170" y="140" width="990" height="890" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-2" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="5" width="1155" height="80" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-3" value="Navigation" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="5" y="80" width="165" height="950" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-4" value="<font style="font-size: 22px;">&lt;Type&gt; &lt;Title&gt;</font>" style="rounded=0;whiteSpace=wrap;html=1;align=center;" vertex="1" parent="1">
|
||||
<mxGeometry x="170" y="80" width="990" height="60" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-5" value="Metadata ??" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="1010" y="150" width="150" height="660" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-6" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
|
||||
<mxGeometry x="180" y="150" width="820" height="150" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-7" value="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<br>Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit, sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt. Neque porro quisquam est, qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit, sed quia non numquam eius modi tempora incidunt ut labore et dolore magnam aliquam quaerat&nbsp;" style="text;spacingTop=-5;whiteSpace=wrap;html=1;align=left;fontSize=12;fontFamily=Helvetica;fillColor=none;strokeColor=none;" vertex="1" parent="1">
|
||||
<mxGeometry x="190" y="170" width="800" height="160" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-8" value="Related" style="swimlane;strokeColor=#999999;swimlaneFillColor=#FFFFFF;fillColor=#ffffff;fontColor=#008CFF;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=0;marginBottom=0;whiteSpace=wrap;html=1;fontSize=17;align=left;" vertex="1" parent="1">
|
||||
<mxGeometry x="180" y="310" width="770" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-9" value="Item 1" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fillColor=none;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-8">
|
||||
<mxGeometry y="30" width="770" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-10" value="Item 2" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;rSize=5;fillColor=none;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-8">
|
||||
<mxGeometry y="50" width="770" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-11" value="Item 3" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;rSize=5;fillColor=#DDEEFF;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-8">
|
||||
<mxGeometry y="70" width="770" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-13" value="" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fillColor=none;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-8">
|
||||
<mxGeometry y="90" width="770" height="10" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-15" value="Linked Items" style="swimlane;strokeColor=#999999;swimlaneFillColor=#FFFFFF;fillColor=#ffffff;fontColor=#008CFF;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=0;marginBottom=0;whiteSpace=wrap;html=1;fontSize=17;align=left;" vertex="1" parent="1">
|
||||
<mxGeometry x="180" y="420" width="770" height="100" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-16" value="Item 1" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fillColor=none;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-15">
|
||||
<mxGeometry y="30" width="770" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-17" value="Item 2" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;rSize=5;fillColor=none;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-15">
|
||||
<mxGeometry y="50" width="770" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-18" value="Item 3" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;rSize=5;fillColor=#DDEEFF;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-15">
|
||||
<mxGeometry y="70" width="770" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-19" value="" style="text;spacing=0;strokeColor=none;align=left;verticalAlign=middle;spacingLeft=7;spacingRight=10;overflow=hidden;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;rotatable=0;whiteSpace=wrap;html=1;fillColor=none;fontColor=#666666;fontSize=17;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-15">
|
||||
<mxGeometry y="90" width="770" height="10" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-20" value="xx added ticket X as blocking" style="verticalLabelPosition=bottom;shadow=0;dashed=0;align=left;html=1;verticalAlign=top;strokeWidth=1;shape=mxgraph.mockup.graphics.simpleIcon;strokeColor=#999999;spacing=-20;spacingRight=0;spacingLeft=50;" vertex="1" parent="1">
|
||||
<mxGeometry x="200" y="540" width="20" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-21" value="xx added Device as related" style="verticalLabelPosition=bottom;shadow=0;dashed=0;align=left;html=1;verticalAlign=top;strokeWidth=1;shape=mxgraph.mockup.graphics.simpleIcon;strokeColor=#999999;spacing=-20;spacingRight=0;spacingLeft=50;" vertex="1" parent="1">
|
||||
<mxGeometry x="200" y="580" width="20" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-22" value="" style="verticalLabelPosition=bottom;shadow=0;dashed=0;align=center;html=1;verticalAlign=top;strokeWidth=1;shape=mxgraph.mockup.navigation.scrollBar;strokeColor=#999999;barPos=20;fillColor2=#99ddff;strokeColor2=none;direction=north;" vertex="1" parent="1">
|
||||
<mxGeometry x="980" y="310" width="20" height="720" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-32" value="" style="group" vertex="1" connectable="0" parent="1">
|
||||
<mxGeometry x="200" y="620" width="750" height="120" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-29" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-32">
|
||||
<mxGeometry width="750" height="120" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-30" value="XX replied on xx month year" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-32">
|
||||
<mxGeometry width="750" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-28" value="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<div><br/></div>" style="text;spacingTop=-5;whiteSpace=wrap;html=1;align=left;fontSize=12;fontFamily=Helvetica;fillColor=none;strokeColor=none;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-32">
|
||||
<mxGeometry x="10" y="30" width="730" height="77" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-33" value="" style="group" vertex="1" connectable="0" parent="1">
|
||||
<mxGeometry x="230" y="740" width="720" height="120" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-34" value="" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-33">
|
||||
<mxGeometry width="720" height="120" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-35" value="XX replied on xx month year" style="rounded=0;whiteSpace=wrap;html=1;align=left;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-33">
|
||||
<mxGeometry width="720" height="20" as="geometry" />
|
||||
</mxCell>
|
||||
<mxCell id="RO_ATfNDEhmNTfwGVFBN-36" value="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.<div><br/></div>" style="text;spacingTop=-5;whiteSpace=wrap;html=1;align=left;fontSize=12;fontFamily=Helvetica;fillColor=none;strokeColor=none;" vertex="1" parent="RO_ATfNDEhmNTfwGVFBN-33">
|
||||
<mxGeometry x="9.6" y="30" width="700.8" height="77" as="geometry" />
|
||||
</mxCell>
|
||||
</root>
|
||||
</mxGraphModel>
|
||||
</diagram>
|
||||
</mxfile>
|
@ -8,14 +8,18 @@ about: https://gitlab.com/nofusscomputing/infrastructure/configuration-managemen
|
||||
|
||||
Unit tests are written to aid in application stability and to assist in preventing regression bugs. As part of development the developer working on a Merge/Pull request is to ensure that tests are written. Failing to do so will more likely than not ensure that your Merge/Pull request is not merged.
|
||||
|
||||
User Interface (UI) test are written to test the user interface to ensure that it functions as it should. Changes to the UI will need to be tested.
|
||||
User Interface (UI) test are written _if applicable_ to test the user interface to ensure that it functions as it should. Changes to the UI will need to be tested.
|
||||
|
||||
In most cases functional tests will not need to be written, however you should confirm this with a maintainer.
|
||||
|
||||
Integration tests **will** be required if the development introduces code that interacts with an independent third-party application.
|
||||
|
||||
|
||||
## Writing Tests
|
||||
|
||||
We use class based tests. Each class will require a `setUpTestData` method for test setup. To furhter assist in the writing of tests, we have written the test cases for common items as an abstract class. You are advised to review the [test cases](./api/tests/index.md) and if it's applicable to the item you have added, than add the test case class to be inherited by your test class.
|
||||
|
||||
naming of test classes is in `CamelCase` in format `<Model Name><what's being tested>` for example the class name for device model history entry tests would be `DeviceHistory`.
|
||||
Naming of test classes is in `CamelCase` in format `<Model Name><what's being tested>` for example the class name for device model history entry tests would be `DeviceHistory`.
|
||||
|
||||
Test setup is written in a method called `setUpTestData` and is to contain the setup for all tests within the test class.
|
||||
|
||||
@ -51,22 +55,40 @@ class DeviceHistory(TestCase, HistoryEntry, HistoryEntryParentItem):
|
||||
|
||||
Each module is to contain a tests directory of the model being tested with a single file for grouping of what is being tested. for items that depend upon a parent model, the test file is to be within the child-models test directory named with format `test_<model>_<parent app>_<parent model name>`
|
||||
|
||||
_example file system structure for the device model that relies upon access app model organization, core app model history and model notes._
|
||||
_example file system structure showing the layout of the tests directory for a module_
|
||||
|
||||
``` text
|
||||
|
||||
.
|
||||
├── tests
|
||||
│ ├── <model name>
|
||||
│ │ ├── test_<model name>_access_organization.py
|
||||
│ │ ├── test_<model name>_api_permission.py
|
||||
│ │ ├── test_<model name>_core_history.py
|
||||
│ │ ├── test_<model name>_core_notes.py
|
||||
│ │ ├── test_<model name>_permission.py
|
||||
│ │ └── test_device.py
|
||||
|
||||
│ ├── functional
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── <model name>
|
||||
│ │ └── test_<model name>_a_tast_name.py
|
||||
│ ├── __init__.py
|
||||
│ ├── integration
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── <model name>
|
||||
│ │ └── test_<model name>_a_tast_name.py
|
||||
│ ├── ui
|
||||
│ │ ├── __init__.py
|
||||
│ │ └── <model name>
|
||||
│ │ └── test_<model name>_a_tast_name.py
|
||||
│ └── unit
|
||||
│ ├── __init__.py
|
||||
│ └── <model name>
|
||||
│ ├── test_<model name>_api.py
|
||||
│ ├── test_<model name>_permission_api.py
|
||||
│ ├── test_<model name>_permission.py
|
||||
│ ├── test_<model name>_core_history.py
|
||||
│ ├── test_<model name>_history_permission.py
|
||||
│ ├── test_<model name>.py
|
||||
│ └── test_<model name>_views.py
|
||||
|
||||
```
|
||||
|
||||
Tests are broken up into the type the test is (sub-directory to test), and they are `unit`, `functional`, `UI` and `integration`. These sub-directories each contain a sub-directory for each model they are testing.
|
||||
|
||||
|
||||
Items to test include, and are not limited to:
|
||||
|
||||
- CRUD permissions admin site
|
||||
@ -105,6 +127,10 @@ Items to test include, and are not limited to:
|
||||
|
||||
_applicable if notes are able to be added to an item._
|
||||
|
||||
- API Fields
|
||||
|
||||
_Field exists, Type is checked_
|
||||
|
||||
|
||||
## Running Tests
|
||||
|
||||
|
@ -8,15 +8,16 @@ about: https://gitlab.com/nofusscomputing/infrastructure/configuration-managemen
|
||||
|
||||
<span style="text-align: center;">
|
||||
|
||||

|
||||

|
||||
|
||||
 
|
||||
 
|
||||
|
||||
 [](https://gitlab.com/nofusscomputing/projects/centurion_erp/-/issues/?sort=created_date&state=opened&label_name%5B%5D=type%3A%3Abug)
|
||||
 
|
||||
|
||||

|
||||
 
|
||||
|
||||

|
||||
|
||||
 [](https://artifacthub.io/packages/container/centurion-erp/centurion-erp)
|
||||
|
||||
</span>
|
||||
|
||||
|
39
docs/pull_request_template.md
Normal file
39
docs/pull_request_template.md
Normal file
@ -0,0 +1,39 @@
|
||||
### :books: Summary
|
||||
<!-- your summary here emojis ref: https://github.com/yodamad/gitlab-emoji -->
|
||||
|
||||
|
||||
|
||||
### :link: Links / References
|
||||
<!--
|
||||
|
||||
using a list as any links to other references or links as required. if relevent, describe the link/reference
|
||||
|
||||
Include any issues or related merge requests. Note: dependent MR's also to be added to "Merge request dependencies"
|
||||
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### :construction_worker: Tasks
|
||||
|
||||
- [ ] Add your tasks here as required (delete task if n/a)
|
||||
|
||||
<!-- dont remove tasks below strike through including the checkbox by enclosing in double tidle '~~' -->
|
||||
|
||||
- [ ] :orange_circle: Related issue(s) closed via [commit message](https://www.conventionalcommits.org/en/v1.0.0) footer
|
||||
|
||||
- [ ] :yellow_circle: Contains `breaking-change` Any Breaking change(s)? [commit message](https://www.conventionalcommits.org/en/v1.0.0)
|
||||
|
||||
_Breaking Change must also be notated in the commit that introduces it and in [Conventional Commit Format](https://www.conventionalcommits.org/en/v1.0.0/)._
|
||||
|
||||
- [ ] :memo: Release notes updated
|
||||
|
||||
- [ ] :large_blue_circle: Documentation Documentation written
|
||||
|
||||
_All features to be documented within the correct section(s). Administration, Development and/or User_
|
||||
|
||||
- [ ] Milestone assigned
|
||||
|
||||
- [ ] :red_circle: [Unit Test(s) Written](https://nofusscomputing.com/projects/centurion_erp/development/testing/)
|
||||
|
||||
_ensure test coverage delta is not less than zero_
|
Submodule gitlab-ci updated: 58ffcabbfb...6f8dfcba0b
@ -3,8 +3,8 @@ INHERIT: website-template/mkdocs.yml
|
||||
docs_dir: 'docs'
|
||||
|
||||
repo_name: Centurion ERP
|
||||
repo_url: https://gitlab.com/nofusscomputing/projects/centurion_erp
|
||||
edit_uri: '/-/ide/project/nofusscomputing/projects/centurion_erp/edit/development/-/docs/'
|
||||
repo_url: https://github.com/nofusscomputing/centurion_erp
|
||||
edit_uri: '/edit/development/docs/'
|
||||
|
||||
plugins:
|
||||
mkdocstrings:
|
||||
|
@ -1,4 +1,4 @@
|
||||
Django==5.0.7
|
||||
Django==5.0.8
|
||||
django-debug-toolbar==4.3.0
|
||||
social-auth-app-django==5.4.1
|
||||
|
||||
|
Reference in New Issue
Block a user