nautobot 2.0.5__py3-none-any.whl → 2.1.0b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of nautobot might be problematic. Click here for more details.
- nautobot/circuits/navigation.py +0 -25
- nautobot/circuits/templates/circuits/circuit_retrieve.html +0 -9
- nautobot/circuits/templates/circuits/providernetwork_retrieve.html +0 -2
- nautobot/circuits/tests/test_filters.py +1 -0
- nautobot/core/api/serializers.py +15 -5
- nautobot/core/api/views.py +18 -19
- nautobot/core/choices.py +1 -1
- nautobot/core/filters.py +12 -4
- nautobot/core/jobs/__init__.py +125 -3
- nautobot/core/management/commands/generate_test_data.py +4 -1
- nautobot/core/models/fields.py +12 -2
- nautobot/core/settings.py +8 -7
- nautobot/core/templates/base_django.html +2 -2
- nautobot/core/templates/buttons/export.html +57 -30
- nautobot/core/templates/generic/object_list.html +2 -2
- nautobot/core/templates/generic/object_retrieve.html +8 -1
- nautobot/core/templates/home.html +5 -5
- nautobot/core/templates/inc/created_updated.html +2 -2
- nautobot/core/templates/inc/footer.html +2 -2
- nautobot/core/templates/inc/javascript.html +0 -10
- nautobot/core/templates/inc/media.html +2 -0
- nautobot/core/templates/inc/nav_menu.html +66 -68
- nautobot/core/templates/inc/object_details_advanced_panel.html +19 -0
- nautobot/core/templates/nautobot_config.py.j2 +10 -4
- nautobot/core/templates/panel_table.html +1 -1
- nautobot/core/templates/template.css +89 -0
- nautobot/core/templates/utilities/templatetags/table_config_form.html +1 -0
- nautobot/core/templatetags/buttons.py +7 -2
- nautobot/core/testing/views.py +34 -4
- nautobot/core/tests/integration/test_home.py +1 -43
- nautobot/core/tests/integration/test_navbar.py +10 -64
- nautobot/core/tests/integration/test_plugin_home.py +4 -5
- nautobot/core/tests/integration/test_plugin_navbar.py +20 -16
- nautobot/core/tests/integration/test_theme.py +4 -0
- nautobot/core/tests/test_api.py +14 -66
- nautobot/core/tests/test_filters.py +127 -0
- nautobot/core/tests/test_forms.py +1 -1
- nautobot/core/tests/test_graphql.py +165 -2
- nautobot/core/tests/test_jobs.py +112 -0
- nautobot/core/tests/test_openapi.py +6 -0
- nautobot/core/tests/test_views.py +11 -85
- nautobot/core/urls.py +6 -1
- nautobot/core/utils/lookup.py +28 -0
- nautobot/core/utils/requests.py +2 -3
- nautobot/core/views/__init__.py +3 -4
- nautobot/core/views/generic.py +9 -4
- nautobot/core/views/mixins.py +4 -2
- nautobot/core/views/renderers.py +5 -0
- nautobot/dcim/models/device_components.py +1 -0
- nautobot/dcim/navigation.py +10 -165
- nautobot/dcim/templates/dcim/location.html +1 -1
- nautobot/dcim/tests/features/locations.feature +143 -0
- nautobot/dcim/tests/test_api.py +1 -1
- nautobot/dcim/tests/test_filters.py +11 -3
- nautobot/extras/admin.py +1 -1
- nautobot/extras/api/serializers.py +33 -0
- nautobot/extras/api/urls.py +6 -0
- nautobot/extras/api/views.py +45 -6
- nautobot/extras/factory.py +28 -2
- nautobot/extras/filters/__init__.py +52 -0
- nautobot/extras/filters/mixins.py +4 -29
- nautobot/extras/forms/forms.py +43 -0
- nautobot/extras/jobs.py +31 -9
- nautobot/extras/migrations/0100_fileproxy_job_result.py +32 -0
- nautobot/extras/migrations/0101_externalintegration.py +61 -0
- nautobot/extras/migrations/0102_set_null_objectchange_contenttype.py +32 -0
- nautobot/extras/models/__init__.py +2 -0
- nautobot/extras/models/change_logging.py +2 -2
- nautobot/extras/models/models.py +96 -16
- nautobot/extras/navigation.py +17 -29
- nautobot/extras/signals.py +15 -0
- nautobot/extras/tables.py +27 -0
- nautobot/extras/templates/extras/externalintegration_retrieve.html +37 -0
- nautobot/extras/templates/extras/inc/jobresult.html +24 -0
- nautobot/extras/templates/extras/jobresult.html +24 -0
- nautobot/extras/test_jobs/file_output.py +16 -0
- nautobot/extras/tests/test_api.py +92 -0
- nautobot/extras/tests/test_filters.py +64 -2
- nautobot/extras/tests/test_jobs.py +39 -0
- nautobot/extras/tests/test_models.py +34 -0
- nautobot/extras/tests/test_views.py +22 -2
- nautobot/extras/urls.py +1 -0
- nautobot/extras/views.py +15 -0
- nautobot/ipam/forms.py +16 -0
- nautobot/ipam/models.py +3 -0
- nautobot/ipam/navigation.py +2 -59
- nautobot/ipam/templates/ipam/ipaddress.html +0 -9
- nautobot/ipam/templates/ipam/prefix.html +0 -9
- nautobot/ipam/tests/features/prefixes.feature +134 -0
- nautobot/ipam/tests/test_filters.py +5 -10
- nautobot/ipam/tests/test_views.py +8 -1
- nautobot/ipam/views.py +3 -0
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css +191 -191
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +874 -881
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +1 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +1 -1
- nautobot/project-static/css/base.css +135 -99
- nautobot/project-static/css/dark.css +65 -6
- nautobot/project-static/docs/404.html +44 -16
- nautobot/project-static/docs/apps/index.html +44 -16
- nautobot/project-static/docs/apps/nautobot-apps.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/__init__.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/admin.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/api.html +1597 -1457
- nautobot/project-static/docs/code-reference/nautobot/apps/change_logging.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/choices.html +45 -17
- nautobot/project-static/docs/code-reference/nautobot/apps/config.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/datasources.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/exceptions.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/factory.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/filters.html +353 -432
- nautobot/project-static/docs/code-reference/nautobot/apps/forms.html +66 -38
- nautobot/project-static/docs/code-reference/nautobot/apps/graphql.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/jobs.html +2154 -2307
- nautobot/project-static/docs/code-reference/nautobot/apps/models.html +807 -691
- nautobot/project-static/docs/code-reference/nautobot/apps/querysets.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/secrets.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/tables.html +58 -30
- nautobot/project-static/docs/code-reference/nautobot/apps/testing.html +3600 -3456
- nautobot/project-static/docs/code-reference/nautobot/apps/ui.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/urls.html +44 -16
- nautobot/project-static/docs/code-reference/nautobot/apps/utils.html +101 -75
- nautobot/project-static/docs/code-reference/nautobot/apps/views.html +3083 -3019
- nautobot/project-static/docs/development/apps/api/configuration-view.html +44 -16
- nautobot/project-static/docs/development/apps/api/database-backend-config.html +44 -16
- nautobot/project-static/docs/development/apps/api/models/django-admin.html +44 -16
- nautobot/project-static/docs/development/apps/api/models/global-search.html +44 -16
- nautobot/project-static/docs/development/apps/api/models/graphql.html +44 -16
- nautobot/project-static/docs/development/apps/api/models/index.html +44 -16
- nautobot/project-static/docs/development/apps/api/nautobot-app-config.html +44 -16
- nautobot/project-static/docs/development/apps/api/platform-features/custom-validators.html +44 -16
- nautobot/project-static/docs/development/apps/api/platform-features/filter-extensions.html +44 -16
- nautobot/project-static/docs/development/apps/api/platform-features/git-repository-content.html +44 -16
- nautobot/project-static/docs/development/apps/api/platform-features/index.html +44 -16
- nautobot/project-static/docs/development/apps/api/platform-features/jinja2-filters.html +44 -16
- nautobot/project-static/docs/development/apps/api/platform-features/jobs.html +44 -16
- nautobot/project-static/docs/development/apps/api/platform-features/populating-extensibility-features.html +44 -16
- nautobot/project-static/docs/development/apps/api/platform-features/secrets-providers.html +44 -16
- nautobot/project-static/docs/development/apps/api/platform-features/uniquely-identify-objects.html +44 -16
- nautobot/project-static/docs/development/apps/api/prometheus.html +44 -16
- nautobot/project-static/docs/development/apps/api/setup.html +44 -16
- nautobot/project-static/docs/development/apps/api/testing.html +44 -16
- nautobot/project-static/docs/development/apps/api/ui-extensions/banners.html +44 -16
- nautobot/project-static/docs/development/apps/api/ui-extensions/home-page.html +44 -16
- nautobot/project-static/docs/development/apps/api/ui-extensions/index.html +44 -16
- nautobot/project-static/docs/development/apps/api/ui-extensions/navigation.html +44 -16
- nautobot/project-static/docs/development/apps/api/ui-extensions/object-detail-views.html +44 -16
- nautobot/project-static/docs/development/apps/api/ui-extensions/tabs.html +44 -16
- nautobot/project-static/docs/development/apps/api/views/base-template.html +44 -16
- nautobot/project-static/docs/development/apps/api/views/core-view-overrides.html +44 -16
- nautobot/project-static/docs/development/apps/api/views/django-generic-views.html +44 -16
- nautobot/project-static/docs/development/apps/api/views/help-documentation.html +44 -16
- nautobot/project-static/docs/development/apps/api/views/index.html +44 -16
- nautobot/project-static/docs/development/apps/api/views/nautobot-generic-views.html +44 -16
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewset.html +44 -16
- nautobot/project-static/docs/development/apps/api/views/nautobotuiviewsetrouter.html +44 -16
- nautobot/project-static/docs/development/apps/api/views/notes.html +44 -16
- nautobot/project-static/docs/development/apps/api/views/rest-api.html +44 -16
- nautobot/project-static/docs/development/apps/api/views/urls.html +44 -16
- nautobot/project-static/docs/development/apps/api/views/view-overrides.html +44 -16
- nautobot/project-static/docs/development/apps/index.html +44 -16
- nautobot/project-static/docs/development/apps/migration/code-updates.html +44 -16
- nautobot/project-static/docs/development/apps/migration/dependency-updates.html +44 -16
- nautobot/project-static/docs/development/apps/migration/from-v1.html +44 -16
- nautobot/project-static/docs/development/apps/migration/model-updates/dcim.html +44 -16
- nautobot/project-static/docs/development/apps/migration/model-updates/extras.html +44 -16
- nautobot/project-static/docs/development/apps/migration/model-updates/global.html +44 -16
- nautobot/project-static/docs/development/apps/migration/model-updates/ipam.html +44 -16
- nautobot/project-static/docs/development/apps/porting-from-netbox.html +44 -16
- nautobot/project-static/docs/development/core/application-registry.html +44 -16
- nautobot/project-static/docs/development/core/best-practices.html +44 -16
- nautobot/project-static/docs/development/core/docker-compose-advanced-use-cases.html +44 -16
- nautobot/project-static/docs/development/core/extending-models.html +44 -16
- nautobot/project-static/docs/development/core/generic-views.html +44 -16
- nautobot/project-static/docs/development/core/getting-started.html +44 -16
- nautobot/project-static/docs/development/core/homepage.html +44 -16
- nautobot/project-static/docs/development/core/index.html +44 -16
- nautobot/project-static/docs/development/core/model-features.html +44 -16
- nautobot/project-static/docs/development/core/natural-keys.html +44 -16
- nautobot/project-static/docs/development/core/navigation-menu.html +44 -21
- nautobot/project-static/docs/development/core/react-ui.html +44 -16
- nautobot/project-static/docs/development/core/release-checklist.html +44 -16
- nautobot/project-static/docs/development/core/role-internals.html +44 -16
- nautobot/project-static/docs/development/core/style-guide.html +44 -16
- nautobot/project-static/docs/development/core/templates.html +44 -16
- nautobot/project-static/docs/development/core/testing.html +44 -16
- nautobot/project-static/docs/development/core/user-preferences.html +44 -16
- nautobot/project-static/docs/development/index.html +44 -16
- nautobot/project-static/docs/development/jobs/index.html +280 -234
- nautobot/project-static/docs/development/jobs/migration/from-v1.html +44 -16
- nautobot/project-static/docs/index.html +44 -16
- nautobot/project-static/docs/objects.inv +0 -0
- nautobot/project-static/docs/release-notes/index.html +47 -19
- nautobot/project-static/docs/release-notes/version-1.0.html +44 -16
- nautobot/project-static/docs/release-notes/version-1.1.html +44 -16
- nautobot/project-static/docs/release-notes/version-1.2.html +44 -16
- nautobot/project-static/docs/release-notes/version-1.3.html +44 -16
- nautobot/project-static/docs/release-notes/version-1.4.html +44 -16
- nautobot/project-static/docs/release-notes/version-1.5.html +44 -16
- nautobot/project-static/docs/release-notes/version-1.6.html +44 -16
- nautobot/project-static/docs/release-notes/version-2.0.html +47 -19
- nautobot/project-static/docs/release-notes/version-2.1.html +5724 -0
- nautobot/project-static/docs/search/search_index.json +1 -1
- nautobot/project-static/docs/sitemap.xml +247 -237
- nautobot/project-static/docs/sitemap.xml.gz +0 -0
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/ldap.html +44 -16
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/remote.html +44 -16
- nautobot/project-static/docs/user-guide/administration/configuration/authentication/sso.html +44 -16
- nautobot/project-static/docs/user-guide/administration/configuration/index.html +44 -16
- nautobot/project-static/docs/user-guide/administration/configuration/node-configuration.html +44 -16
- nautobot/project-static/docs/user-guide/administration/configuration/optional-settings.html +109 -43
- nautobot/project-static/docs/user-guide/administration/configuration/required-settings.html +44 -16
- nautobot/project-static/docs/user-guide/administration/guides/caching.html +44 -16
- nautobot/project-static/docs/user-guide/administration/guides/celery-queues.html +44 -16
- nautobot/project-static/docs/user-guide/administration/guides/healthcheck.html +44 -16
- nautobot/project-static/docs/user-guide/administration/guides/permissions.html +44 -16
- nautobot/project-static/docs/user-guide/administration/guides/prometheus-metrics.html +44 -16
- nautobot/project-static/docs/user-guide/administration/guides/replicating-nautobot.html +44 -16
- nautobot/project-static/docs/user-guide/administration/guides/s3-django-storage.html +48 -19
- nautobot/project-static/docs/user-guide/administration/installation/app-install.html +44 -16
- nautobot/project-static/docs/user-guide/administration/installation/docker.html +44 -16
- nautobot/project-static/docs/user-guide/administration/installation/external-authentication.html +44 -16
- nautobot/project-static/docs/user-guide/administration/installation/http-server.html +44 -16
- nautobot/project-static/docs/user-guide/administration/installation/index.html +44 -16
- nautobot/project-static/docs/user-guide/administration/installation/install_system.html +44 -16
- nautobot/project-static/docs/user-guide/administration/installation/nautobot.html +44 -16
- nautobot/project-static/docs/user-guide/administration/installation/selinux-troubleshooting.html +44 -16
- nautobot/project-static/docs/user-guide/administration/installation/services.html +44 -16
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-netbox.html +44 -16
- nautobot/project-static/docs/user-guide/administration/migration/migrating-from-postgresql.html +44 -16
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-server.html +44 -16
- nautobot/project-static/docs/user-guide/administration/tools/nautobot-shell.html +44 -16
- nautobot/project-static/docs/user-guide/administration/upgrading/database-backup.html +44 -16
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/after-you-upgrade.html +44 -16
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/before-you-upgrade.html +44 -16
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/for-developers.html +44 -16
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/index.html +44 -16
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/ipam/whats-changed.html +44 -16
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/region-and-site-data-migration-guide.html +44 -16
- nautobot/project-static/docs/user-guide/administration/upgrading/from-v1/upgrading-from-nautobot-v1.html +44 -16
- nautobot/project-static/docs/user-guide/administration/upgrading/upgrading.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuit.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittermination.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/circuits/circuittype.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/circuits/provider.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/circuits/providernetwork.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/cable.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleport.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleporttemplate.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverport.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/consoleserverporttemplate.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/device.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebay.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicebaytemplate.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/deviceredundancygroup.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/devicetype.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontport.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/frontporttemplate.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interface.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfaceredundancygroup.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/interfacetemplate.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/inventoryitem.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/location.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/locationtype.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/manufacturer.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/platform.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerfeed.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlet.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/poweroutlettemplate.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerpanel.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerport.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/powerporttemplate.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rack.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackgroup.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rackreservation.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearport.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/rearporttemplate.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/dcim/virtualchassis.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontext.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/extras/configcontextschema.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/ipam/ipaddress.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/ipam/namespace.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/ipam/prefix.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/ipam/rir.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/ipam/routetarget.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/ipam/service.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlan.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vlangroup.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/ipam/vrf.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenant.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/tenancy/tenantgroup.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/cluster.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustergroup.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/clustertype.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/virtualmachine.html +44 -16
- nautobot/project-static/docs/user-guide/core-data-model/virtualization/vminterface.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/custom-fields.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-devices.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/creating-location-types-and-locations.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/index.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/interfaces.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/ipam.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/platforms.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/search-bar.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/tenants.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/getting-started/vlans-and-vlan-groups.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/git-data-source.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/graphql.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/ip-address-merge-tool.html +44 -16
- nautobot/project-static/docs/user-guide/feature-guides/relationships.html +44 -16
- nautobot/project-static/docs/user-guide/index.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/change-logging.html +110 -16
- nautobot/project-static/docs/user-guide/platform-functionality/computedfield.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/customfield.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/customlink.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/dynamicgroup.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/exporttemplate.html +47 -19
- nautobot/project-static/docs/user-guide/platform-functionality/externalintegration.html +5359 -0
- nautobot/project-static/docs/user-guide/platform-functionality/gitrepository.html +47 -19
- nautobot/project-static/docs/user-guide/platform-functionality/graphql.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/graphqlquery.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/imageattachment.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/index.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/job-scheduling-and-approvals.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobbutton.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/jobhook.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/jobs/models.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/napalm.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/note.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/relationship.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/authentication.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/filtering.html +113 -44
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/overview.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/rest-api/ui-related-endpoints.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/role.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/secret.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/status.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/tag.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/template-filters.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/users/objectpermission.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/users/token.html +44 -16
- nautobot/project-static/docs/user-guide/platform-functionality/webhook.html +44 -16
- nautobot/project-static/fonts/UFL.txt +96 -0
- nautobot/project-static/fonts/Ubuntu-Bold.woff2 +0 -0
- nautobot/project-static/fonts/Ubuntu-BoldItalic.woff2 +0 -0
- nautobot/project-static/fonts/Ubuntu-Italic.woff2 +0 -0
- nautobot/project-static/fonts/Ubuntu-Medium.woff2 +0 -0
- nautobot/project-static/fonts/Ubuntu-MediumItalic.woff2 +0 -0
- nautobot/project-static/fonts/Ubuntu-Regular.woff2 +0 -0
- nautobot/project-static/fonts/UbuntuMono-Bold.woff2 +0 -0
- nautobot/project-static/fonts/UbuntuMono-BoldItalic.woff2 +0 -0
- nautobot/project-static/fonts/UbuntuMono-Italic.woff2 +0 -0
- nautobot/project-static/fonts/UbuntuMono-Regular.woff2 +0 -0
- nautobot/project-static/img/dark-theme.png +0 -0
- nautobot/project-static/img/light-theme.png +0 -0
- nautobot/project-static/img/nautobot_chevron.svg +5 -0
- nautobot/project-static/img/nautobot_chevron_header.svg +5 -0
- nautobot/project-static/img/system-theme.png +0 -0
- nautobot/tenancy/navigation.py +0 -13
- nautobot/ui/package-lock.json +2 -2
- nautobot/ui/package.json +1 -1
- nautobot/users/admin.py +44 -0
- nautobot/users/migrations/0007_alter_objectpermission_object_types.py +33 -0
- nautobot/users/models.py +3 -2
- nautobot/virtualization/navigation.py +1 -33
- nautobot/virtualization/tests/test_api.py +1 -1
- nautobot/virtualization/tests/test_filters.py +1 -1
- {nautobot-2.0.5.dist-info → nautobot-2.1.0b1.dist-info}/METADATA +1 -1
- {nautobot-2.0.5.dist-info → nautobot-2.1.0b1.dist-info}/RECORD +376 -351
- {nautobot-2.0.5.dist-info → nautobot-2.1.0b1.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.0.5.dist-info → nautobot-2.1.0b1.dist-info}/WHEEL +0 -0
- {nautobot-2.0.5.dist-info → nautobot-2.1.0b1.dist-info}/entry_points.txt +0 -0
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from django.test.utils import override_settings
|
|
2
|
-
|
|
3
1
|
from nautobot.core.testing.integration import SeleniumTestCase
|
|
4
2
|
|
|
5
3
|
|
|
@@ -10,19 +8,19 @@ class NavBarTestCase(SeleniumTestCase):
|
|
|
10
8
|
navbar = {
|
|
11
9
|
"Organization": {
|
|
12
10
|
"Locations": {
|
|
13
|
-
"
|
|
11
|
+
"Locations": {
|
|
14
12
|
"permission": "dcim.view_location",
|
|
15
|
-
"buttons": ["Add"
|
|
13
|
+
"buttons": ["Add"],
|
|
16
14
|
},
|
|
17
15
|
"Location Types": {
|
|
18
16
|
"permission": "dcim.view_locationtype",
|
|
19
|
-
"buttons": ["Add"
|
|
17
|
+
"buttons": ["Add"],
|
|
20
18
|
},
|
|
21
19
|
},
|
|
22
20
|
"Tags": {
|
|
23
21
|
"Tags": {
|
|
24
22
|
"permission": "extras.view_tag",
|
|
25
|
-
"buttons": ["Add"
|
|
23
|
+
"buttons": ["Add"],
|
|
26
24
|
},
|
|
27
25
|
},
|
|
28
26
|
},
|
|
@@ -46,7 +44,6 @@ class NavBarTestCase(SeleniumTestCase):
|
|
|
46
44
|
self.logout()
|
|
47
45
|
super().tearDown()
|
|
48
46
|
|
|
49
|
-
@override_settings(HIDE_RESTRICTED_UI=False)
|
|
50
47
|
def test_navbar_render_superuser(self):
|
|
51
48
|
"""
|
|
52
49
|
Render navbar from home page with superuser.
|
|
@@ -60,75 +57,24 @@ class NavBarTestCase(SeleniumTestCase):
|
|
|
60
57
|
|
|
61
58
|
for tab_name, groups in self.navbar.items():
|
|
62
59
|
# XPath to find tabs using the tab name
|
|
63
|
-
tab_xpath = f"//*[@id='navbar']//*[
|
|
60
|
+
tab_xpath = f"//*[@id='navbar']//*[normalize-space()='{tab_name}']"
|
|
64
61
|
tab = self.browser.find_by_xpath(tab_xpath)
|
|
65
62
|
tab.click()
|
|
66
63
|
self.assertTrue(bool(tab["aria-expanded"]))
|
|
67
64
|
|
|
68
65
|
for group_name, items in groups.items():
|
|
69
66
|
# Append onto tab xpath with group name search
|
|
70
|
-
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[
|
|
67
|
+
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[normalize-space()='{group_name}']")
|
|
71
68
|
|
|
72
69
|
for item_name in items:
|
|
73
|
-
item_xpath = f"{tab_xpath}/following-sibling::ul//li[.//a[
|
|
70
|
+
item_xpath = f"{tab_xpath}/following-sibling::ul//li[.//a[normalize-space()='{item_name}']]"
|
|
74
71
|
group.find_by_xpath(item_xpath)
|
|
75
72
|
|
|
76
|
-
@override_settings(HIDE_RESTRICTED_UI=False)
|
|
77
|
-
def test_navbar_render_limit_permissions(self):
|
|
78
|
-
"""
|
|
79
|
-
Render navbar from home page with limited permissions.
|
|
80
|
-
"""
|
|
81
|
-
self.add_permissions("extras.view_relationship")
|
|
82
|
-
user_permissions = self.user.get_all_permissions()
|
|
83
|
-
|
|
84
|
-
self.browser.visit(self.live_server_url)
|
|
85
|
-
|
|
86
|
-
for tab_name, groups in self.navbar.items():
|
|
87
|
-
# XPath to find tabs using the tab name
|
|
88
|
-
tab_xpath = f"//*[@id='navbar']//*[contains(text(), '{tab_name}')]"
|
|
89
|
-
tab = self.browser.find_by_xpath(tab_xpath)
|
|
90
|
-
tab.click()
|
|
91
|
-
self.assertTrue(bool(tab["aria-expanded"]))
|
|
92
|
-
|
|
93
|
-
for group_name, items in groups.items():
|
|
94
|
-
# Append onto tab xpath with group name search
|
|
95
|
-
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[contains(text(), '{group_name}')]")
|
|
96
|
-
|
|
97
|
-
for item_name, item_details in items.items():
|
|
98
|
-
item_xpath = f"{tab_xpath}/following-sibling::ul//li[.//a[contains(text(), '{item_name}')]]"
|
|
99
|
-
item = group.find_by_xpath(item_xpath)
|
|
100
|
-
if item_details["permission"] in user_permissions:
|
|
101
|
-
self.assertNotEqual(item["class"], "disabled", f"Item `{item_name}` should not be disabled.")
|
|
102
|
-
else:
|
|
103
|
-
self.assertEqual(item["class"], "disabled", f"Item `{item_name}` should be disabled.")
|
|
104
|
-
|
|
105
|
-
@override_settings(HIDE_RESTRICTED_UI=False)
|
|
106
|
-
def test_navbar_render_no_permissions(self):
|
|
107
|
-
"""
|
|
108
|
-
Render navbar from home page with no permissions.
|
|
109
|
-
"""
|
|
110
|
-
self.browser.visit(self.live_server_url)
|
|
111
|
-
|
|
112
|
-
for tab_name, groups in self.navbar.items():
|
|
113
|
-
# XPath to find tabs using the tab name
|
|
114
|
-
tab_xpath = f"//*[@id='navbar']//*[contains(text(), '{tab_name}')]"
|
|
115
|
-
tab = self.browser.find_by_xpath(tab_xpath)
|
|
116
73
|
tab.click()
|
|
117
|
-
self.assertTrue(bool(tab["aria-expanded"]))
|
|
118
|
-
|
|
119
|
-
for group_name, items in groups.items():
|
|
120
|
-
# Append onto tab xpath with group name search
|
|
121
|
-
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[contains(text(), '{group_name}')]")
|
|
122
|
-
|
|
123
|
-
for item_name in items:
|
|
124
|
-
item_xpath = f"{tab_xpath}/following-sibling::ul//li[.//a[contains(text(), '{item_name}')]]"
|
|
125
|
-
item = group.find_by_xpath(item_xpath)
|
|
126
|
-
self.assertEqual(item["class"], "disabled", f"Item `{item_name}` should be disabled.")
|
|
127
74
|
|
|
128
|
-
|
|
129
|
-
def test_navbar_render_restricted_ui(self):
|
|
75
|
+
def test_navbar_render_with_limited_permissions(self):
|
|
130
76
|
"""
|
|
131
|
-
Render navbar from home page with
|
|
77
|
+
Render navbar from home page with limited permissions.
|
|
132
78
|
This restricts the user to be able to view ONLY relationships on the navbar.
|
|
133
79
|
It then checks the UI for these restrictions.
|
|
134
80
|
"""
|
|
@@ -146,7 +92,7 @@ class NavBarTestCase(SeleniumTestCase):
|
|
|
146
92
|
tab_flag = True
|
|
147
93
|
|
|
148
94
|
# XPath to find tabs using the tab name
|
|
149
|
-
tabs = self.browser.find_by_xpath(f"//*[@id='navbar']//*[
|
|
95
|
+
tabs = self.browser.find_by_xpath(f"//*[@id='navbar']//*[normalize-space()='{tab_name}']")
|
|
150
96
|
if tab_flag:
|
|
151
97
|
self.assertEqual(len(tabs), 1)
|
|
152
98
|
else:
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from django.test.utils import override_settings
|
|
2
1
|
from django.conf import settings
|
|
3
2
|
from unittest import skipIf
|
|
4
3
|
|
|
@@ -89,7 +88,6 @@ class PluginHomeTestCase(SeleniumTestCase):
|
|
|
89
88
|
counter_html = int(item_html.find_by_xpath("./../../span")["innerHTML"])
|
|
90
89
|
self.assertEqual(counter, counter_html)
|
|
91
90
|
|
|
92
|
-
@override_settings(HIDE_RESTRICTED_UI=False)
|
|
93
91
|
def test_homepage_render_no_permissions(self):
|
|
94
92
|
"""
|
|
95
93
|
Render homepage with no permissions.
|
|
@@ -101,7 +99,8 @@ class PluginHomeTestCase(SeleniumTestCase):
|
|
|
101
99
|
columns_html.first.find_by_xpath(f".//strong[text()='{panel_name}']")
|
|
102
100
|
for item_name, _ in panel_details.items():
|
|
103
101
|
item_html = columns_html.first.find_by_xpath(f".//h4[contains(text(), '{item_name}')]")
|
|
104
|
-
|
|
102
|
+
# Assert Panel items without permissions are not shown in DOM
|
|
103
|
+
self.assertFalse(item_html)
|
|
105
104
|
|
|
106
105
|
def test_examplemodel_custom_panel(self):
|
|
107
106
|
"""
|
|
@@ -118,7 +117,6 @@ class PluginHomeTestCase(SeleniumTestCase):
|
|
|
118
117
|
for item_name in self.custom_panel_examplemodel["items"]:
|
|
119
118
|
columns_html.first.find_by_xpath(f".//a[contains(text(), '{item_name}')]")
|
|
120
119
|
|
|
121
|
-
@override_settings(HIDE_RESTRICTED_UI=False)
|
|
122
120
|
def test_homepage_render_limit_permissions(self):
|
|
123
121
|
"""
|
|
124
122
|
Render homepage with limited permissions.
|
|
@@ -142,4 +140,5 @@ class PluginHomeTestCase(SeleniumTestCase):
|
|
|
142
140
|
self.assertEqual(counter, counter_html)
|
|
143
141
|
else:
|
|
144
142
|
item_html = columns_html.first.find_by_xpath(f".//h4[contains(text(), '{item_name}')]")
|
|
145
|
-
|
|
143
|
+
# Assert Layout items without permission is not visible in DOM
|
|
144
|
+
self.assertFalse(item_html)
|
|
@@ -19,7 +19,7 @@ class PluginNavBarTestCase(SeleniumTestCase):
|
|
|
19
19
|
"Example Group 1": {
|
|
20
20
|
"Example Model": {
|
|
21
21
|
"permission": "example_plugin.view_examplemodel",
|
|
22
|
-
"buttons": ["Add"
|
|
22
|
+
"buttons": ["Add"],
|
|
23
23
|
},
|
|
24
24
|
},
|
|
25
25
|
},
|
|
@@ -27,23 +27,23 @@ class PluginNavBarTestCase(SeleniumTestCase):
|
|
|
27
27
|
"Circuits": {
|
|
28
28
|
"Circuits": {
|
|
29
29
|
"permission": "circuits.view_circuit",
|
|
30
|
-
"buttons": ["Add"
|
|
30
|
+
"buttons": ["Add"],
|
|
31
31
|
},
|
|
32
|
-
"Circuit
|
|
32
|
+
"Circuit Types": {
|
|
33
33
|
"permission": "circuits.view_circuittype",
|
|
34
|
-
"buttons": ["Add"
|
|
34
|
+
"buttons": ["Add"],
|
|
35
35
|
},
|
|
36
36
|
},
|
|
37
37
|
"Example Circuit Group": {
|
|
38
38
|
"Example Model": {
|
|
39
39
|
"permission": "example_plugin.view_examplemodel",
|
|
40
|
-
"buttons": ["Add"
|
|
40
|
+
"buttons": ["Add"],
|
|
41
41
|
},
|
|
42
42
|
},
|
|
43
43
|
"Providers": {
|
|
44
44
|
"Providers": {
|
|
45
45
|
"permission": "circuits.view_provider",
|
|
46
|
-
"buttons": ["Add"
|
|
46
|
+
"buttons": ["Add"],
|
|
47
47
|
},
|
|
48
48
|
},
|
|
49
49
|
},
|
|
@@ -51,7 +51,7 @@ class PluginNavBarTestCase(SeleniumTestCase):
|
|
|
51
51
|
"Example Nautobot App": {
|
|
52
52
|
"Models": {
|
|
53
53
|
"permission": "example_plugin.view_examplemodel",
|
|
54
|
-
"buttons": ["Add a new example model"
|
|
54
|
+
"buttons": ["Add a new example model"],
|
|
55
55
|
},
|
|
56
56
|
"Other Models": {
|
|
57
57
|
"permission": "example_plugin.view_examplemodel",
|
|
@@ -80,14 +80,14 @@ class PluginNavBarTestCase(SeleniumTestCase):
|
|
|
80
80
|
# Retrieve home page
|
|
81
81
|
self.browser.visit(self.live_server_url)
|
|
82
82
|
|
|
83
|
-
tab_xpath = "//*[@id='navbar']//*[
|
|
83
|
+
tab_xpath = "//*[@id='navbar']//*[normalize-space()='Example Menu']"
|
|
84
84
|
tab = self.browser.find_by_xpath(tab_xpath)
|
|
85
85
|
tab.click()
|
|
86
86
|
self.assertTrue(bool(tab["aria-expanded"]))
|
|
87
87
|
|
|
88
|
-
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[
|
|
88
|
+
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[normalize-space()='Example Group 1']")
|
|
89
89
|
|
|
90
|
-
item_xpath = f"{tab_xpath}/following-sibling::ul//li[.//a[
|
|
90
|
+
item_xpath = f"{tab_xpath}/following-sibling::ul//li[.//a[normalize-space()='Example Model']]"
|
|
91
91
|
group.find_by_xpath(item_xpath)
|
|
92
92
|
|
|
93
93
|
def test_plugin_navbar_modify_circuits(self):
|
|
@@ -101,15 +101,15 @@ class PluginNavBarTestCase(SeleniumTestCase):
|
|
|
101
101
|
# Retrieve home page
|
|
102
102
|
self.browser.visit(self.live_server_url)
|
|
103
103
|
|
|
104
|
-
tab_xpath = "//*[@id='navbar']//*[
|
|
104
|
+
tab_xpath = "//*[@id='navbar']//*[normalize-space()='Circuits']"
|
|
105
105
|
tab = self.browser.find_by_xpath(tab_xpath)
|
|
106
106
|
tab.click()
|
|
107
107
|
self.assertTrue(bool(tab["aria-expanded"]))
|
|
108
108
|
|
|
109
109
|
for group_name, items in self.navbar["Circuits"].items():
|
|
110
|
-
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[
|
|
110
|
+
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[normalize-space()='{group_name}']")
|
|
111
111
|
for item_name, item_details in items.items():
|
|
112
|
-
item_xpath = f"{tab_xpath}/following-sibling::ul//li[.//a[
|
|
112
|
+
item_xpath = f"{tab_xpath}/following-sibling::ul//li[.//a[normalize-space()='{item_name}']]"
|
|
113
113
|
item = group.find_by_xpath(item_xpath)
|
|
114
114
|
|
|
115
115
|
for button_name in item_details["buttons"]:
|
|
@@ -120,6 +120,8 @@ class PluginNavBarTestCase(SeleniumTestCase):
|
|
|
120
120
|
icon = button.find_by_xpath(f"{item_xpath}/div//a[@title='{button_name}']/i")
|
|
121
121
|
self.assertIn(button_icon, icon["class"])
|
|
122
122
|
|
|
123
|
+
tab.click()
|
|
124
|
+
|
|
123
125
|
def test_plugin_navbar_plugin_tab(self):
|
|
124
126
|
"""
|
|
125
127
|
Test that old-style plugin menu definitions are correctly rendered to the Plugins menu tab.
|
|
@@ -131,15 +133,15 @@ class PluginNavBarTestCase(SeleniumTestCase):
|
|
|
131
133
|
# Retrieve home page
|
|
132
134
|
self.browser.visit(self.live_server_url)
|
|
133
135
|
|
|
134
|
-
tab_xpath = "//*[@id='navbar']//*[
|
|
136
|
+
tab_xpath = "//*[@id='navbar']//*[normalize-space()='Plugins']"
|
|
135
137
|
tab = self.browser.find_by_xpath(tab_xpath)
|
|
136
138
|
tab.click()
|
|
137
139
|
self.assertTrue(bool(tab["aria-expanded"]))
|
|
138
140
|
|
|
139
141
|
for group_name, items in self.navbar["Plugins"].items():
|
|
140
|
-
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[
|
|
142
|
+
group = tab.find_by_xpath(f"{tab_xpath}/following-sibling::ul//li[normalize-space()='{group_name}']")
|
|
141
143
|
for item_name, item_details in items.items():
|
|
142
|
-
item_xpath = f"{tab_xpath}/following-sibling::ul//li[.//a[
|
|
144
|
+
item_xpath = f"{tab_xpath}/following-sibling::ul//li[.//a[normalize-space()='{item_name}']]"
|
|
143
145
|
item = group.find_by_xpath(item_xpath)
|
|
144
146
|
|
|
145
147
|
for button_name in item_details["buttons"]:
|
|
@@ -149,3 +151,5 @@ class PluginNavBarTestCase(SeleniumTestCase):
|
|
|
149
151
|
if button_icon := getattr(ButtonActionIconChoices, button_name.upper(), None):
|
|
150
152
|
icon = button.find_by_xpath(f"{item_xpath}/div//a[@title='{button_name}']/i")
|
|
151
153
|
self.assertIn(button_icon, icon["class"])
|
|
154
|
+
|
|
155
|
+
tab.click()
|
|
@@ -4,6 +4,10 @@ from nautobot.core.testing.integration import SeleniumTestCase
|
|
|
4
4
|
class ThemeTestCase(SeleniumTestCase):
|
|
5
5
|
"""Integration test to check theme selection modal functionality."""
|
|
6
6
|
|
|
7
|
+
def setUp(self):
|
|
8
|
+
super().setUp()
|
|
9
|
+
self.login(self.user.username, self.password)
|
|
10
|
+
|
|
7
11
|
def test_modal_not_rendered(self):
|
|
8
12
|
"""As implemented, modal dialog box does not render until activated."""
|
|
9
13
|
|
nautobot/core/tests/test_api.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from copy import deepcopy
|
|
2
1
|
import csv
|
|
3
2
|
from io import BytesIO, StringIO
|
|
4
3
|
import json
|
|
@@ -6,6 +5,7 @@ from unittest import skip
|
|
|
6
5
|
|
|
7
6
|
from django.contrib.contenttypes.models import ContentType
|
|
8
7
|
from django.conf import settings
|
|
8
|
+
from django.contrib.auth import get_user_model
|
|
9
9
|
from django.test import RequestFactory, override_settings, TestCase
|
|
10
10
|
from django.urls import reverse
|
|
11
11
|
|
|
@@ -29,6 +29,9 @@ from nautobot.ipam import models as ipam_models
|
|
|
29
29
|
from nautobot.ipam.api import serializers as ipam_serializers
|
|
30
30
|
|
|
31
31
|
|
|
32
|
+
User = get_user_model()
|
|
33
|
+
|
|
34
|
+
|
|
32
35
|
class AppTest(testing.APITestCase):
|
|
33
36
|
def test_root(self):
|
|
34
37
|
url = reverse("api-root")
|
|
@@ -73,6 +76,9 @@ class APIDocsTestCase(TestCase):
|
|
|
73
76
|
self.cf_text.save()
|
|
74
77
|
|
|
75
78
|
def test_api_docs(self):
|
|
79
|
+
user = User.objects.create_user(username="nautobotuser")
|
|
80
|
+
self.client.force_login(user)
|
|
81
|
+
|
|
76
82
|
url = reverse("api_docs")
|
|
77
83
|
response = self.client.get(url)
|
|
78
84
|
self.assertEqual(response.status_code, 200)
|
|
@@ -183,17 +189,6 @@ class APIVersioningTestCase(testing.APITestCase):
|
|
|
183
189
|
Testing our custom API versioning, NautobotAPIVersioning.
|
|
184
190
|
"""
|
|
185
191
|
|
|
186
|
-
OVERRIDE_REST_FRAMEWORK = deepcopy(settings.REST_FRAMEWORK)
|
|
187
|
-
# For Nautobot 2.0, the default/only supported API version is 2.0, which limits our ability to test with multiple
|
|
188
|
-
# or min/max versions. For test purposes we force there to be some extra versions available.
|
|
189
|
-
EXTRA_ALLOWED_VERSIONS = [
|
|
190
|
-
f"{settings.VERSION_MAJOR - 1}.99",
|
|
191
|
-
*settings.REST_FRAMEWORK["ALLOWED_VERSIONS"],
|
|
192
|
-
f"{settings.VERSION_MAJOR}.{settings.VERSION_MINOR + 1}",
|
|
193
|
-
]
|
|
194
|
-
OVERRIDE_REST_FRAMEWORK["ALLOWED_VERSIONS"] = EXTRA_ALLOWED_VERSIONS
|
|
195
|
-
OVERRIDE_REST_FRAMEWORK["DEFAULT_VERSION"] = EXTRA_ALLOWED_VERSIONS[-1]
|
|
196
|
-
|
|
197
192
|
def test_default_version(self):
|
|
198
193
|
"""Test that a request with no specific API version gets the default version, which is the current version."""
|
|
199
194
|
url = reverse("api-root")
|
|
@@ -203,13 +198,6 @@ class APIVersioningTestCase(testing.APITestCase):
|
|
|
203
198
|
self.assertEqual(response["API-Version"], api_settings.DEFAULT_VERSION)
|
|
204
199
|
self.assertEqual(response["API-Version"], f"{settings.VERSION_MAJOR}.{settings.VERSION_MINOR}")
|
|
205
200
|
|
|
206
|
-
with override_settings(REST_FRAMEWORK=self.OVERRIDE_REST_FRAMEWORK):
|
|
207
|
-
response = self.client.get(url, **self.header)
|
|
208
|
-
self.assertHttpStatus(response, 200)
|
|
209
|
-
self.assertIn("API-Version", response)
|
|
210
|
-
self.assertEqual(response["API-Version"], self.OVERRIDE_REST_FRAMEWORK["DEFAULT_VERSION"])
|
|
211
|
-
self.assertEqual(response["API-Version"], f"{settings.VERSION_MAJOR}.{settings.VERSION_MINOR + 1}")
|
|
212
|
-
|
|
213
201
|
def test_allowed_versions(self):
|
|
214
202
|
"""Test that all expected versions are supported."""
|
|
215
203
|
for minor_version in range(0, settings.VERSION_MINOR + 1):
|
|
@@ -237,7 +225,7 @@ class APIVersioningTestCase(testing.APITestCase):
|
|
|
237
225
|
self.assertEqual(response["API-Version"], default_version)
|
|
238
226
|
|
|
239
227
|
max_version = api_settings.ALLOWED_VERSIONS[-1]
|
|
240
|
-
self.set_api_version(
|
|
228
|
+
self.set_api_version(max_version)
|
|
241
229
|
response = self.client.get(url, **self.header)
|
|
242
230
|
self.assertHttpStatus(response, 200)
|
|
243
231
|
self.assertIn("API-Version", response)
|
|
@@ -250,29 +238,6 @@ class APIVersioningTestCase(testing.APITestCase):
|
|
|
250
238
|
for version in api_settings.ALLOWED_VERSIONS:
|
|
251
239
|
self.assertIn(version, response.data["detail"])
|
|
252
240
|
|
|
253
|
-
# Also test with explicitly added additional allowed versions
|
|
254
|
-
with override_settings(REST_FRAMEWORK=self.OVERRIDE_REST_FRAMEWORK):
|
|
255
|
-
min_version = self.EXTRA_ALLOWED_VERSIONS[0]
|
|
256
|
-
self.set_api_version(min_version)
|
|
257
|
-
response = self.client.get(url, **self.header)
|
|
258
|
-
self.assertHttpStatus(response, 200)
|
|
259
|
-
self.assertIn("API-Version", response)
|
|
260
|
-
self.assertEqual(response["API-Version"], min_version)
|
|
261
|
-
|
|
262
|
-
max_version = self.EXTRA_ALLOWED_VERSIONS[-1]
|
|
263
|
-
self.set_api_version(max_version)
|
|
264
|
-
response = self.client.get(url, **self.header)
|
|
265
|
-
self.assertHttpStatus(response, 200)
|
|
266
|
-
self.assertIn("API-Version", response)
|
|
267
|
-
self.assertEqual(response["API-Version"], max_version)
|
|
268
|
-
|
|
269
|
-
invalid_version = "0.0"
|
|
270
|
-
self.set_api_version(invalid_version)
|
|
271
|
-
response = self.client.get(url, **self.header)
|
|
272
|
-
self.assertHttpStatus(response, 406) # Not Acceptable
|
|
273
|
-
for version in self.EXTRA_ALLOWED_VERSIONS:
|
|
274
|
-
self.assertIn(version, response.data["detail"])
|
|
275
|
-
|
|
276
241
|
def test_query_version(self):
|
|
277
242
|
"""Test that the API version can be specified via a query parameter."""
|
|
278
243
|
url = reverse("api-root")
|
|
@@ -301,33 +266,12 @@ class APIVersioningTestCase(testing.APITestCase):
|
|
|
301
266
|
for version in api_settings.ALLOWED_VERSIONS:
|
|
302
267
|
self.assertIn(version, response.data["detail"])
|
|
303
268
|
|
|
304
|
-
# Also test with explicitly added additional allowed versions
|
|
305
|
-
with override_settings(REST_FRAMEWORK=self.OVERRIDE_REST_FRAMEWORK):
|
|
306
|
-
min_version = self.EXTRA_ALLOWED_VERSIONS[0]
|
|
307
|
-
response = self.client.get(f"{url}?api_version={min_version}", **self.header)
|
|
308
|
-
self.assertHttpStatus(response, 200)
|
|
309
|
-
self.assertIn("API-Version", response)
|
|
310
|
-
self.assertEqual(response["API-Version"], min_version)
|
|
311
|
-
|
|
312
|
-
max_version = self.EXTRA_ALLOWED_VERSIONS[-1]
|
|
313
|
-
response = self.client.get(f"{url}?api_version={max_version}", **self.header)
|
|
314
|
-
self.assertHttpStatus(response, 200)
|
|
315
|
-
self.assertIn("API-Version", response)
|
|
316
|
-
self.assertEqual(response["API-Version"], max_version)
|
|
317
|
-
|
|
318
|
-
invalid_version = "0.0"
|
|
319
|
-
response = self.client.get(f"{url}?api_version={invalid_version}", **self.header)
|
|
320
|
-
self.assertHttpStatus(response, 404)
|
|
321
|
-
for version in self.EXTRA_ALLOWED_VERSIONS:
|
|
322
|
-
self.assertIn(version, response.data["detail"])
|
|
323
|
-
|
|
324
|
-
@override_settings(REST_FRAMEWORK=OVERRIDE_REST_FRAMEWORK)
|
|
325
269
|
def test_header_and_query_version(self):
|
|
326
270
|
"""Test the behavior when the API version is specified in both the Accept header *and* a query parameter."""
|
|
327
271
|
url = reverse("api-root")
|
|
328
272
|
|
|
329
|
-
min_version =
|
|
330
|
-
max_version =
|
|
273
|
+
min_version = api_settings.ALLOWED_VERSIONS[0]
|
|
274
|
+
max_version = api_settings.ALLOWED_VERSIONS[-1]
|
|
331
275
|
# Specify same version both as Accept header and as query parameter (valid)
|
|
332
276
|
self.set_api_version(max_version)
|
|
333
277
|
response = self.client.get(f"{url}?api_version={max_version}", **self.header)
|
|
@@ -873,6 +817,9 @@ class APIOrderingTestCase(testing.APITestCase):
|
|
|
873
817
|
class NewUIGetMenuAPIViewTestCase(testing.APITestCase):
|
|
874
818
|
def test_get_menu(self):
|
|
875
819
|
"""Asset response from new ui nav menu api returns a well formatted registry["new_ui_nav_menu"] expected by nautobot-ui."""
|
|
820
|
+
self.user.is_superuser = True
|
|
821
|
+
self.user.save()
|
|
822
|
+
|
|
876
823
|
url = reverse("ui-api:get-menu")
|
|
877
824
|
response = self.client.get(url, **self.header)
|
|
878
825
|
expected_response = {
|
|
@@ -975,6 +922,7 @@ class NewUIGetMenuAPIViewTestCase(testing.APITestCase):
|
|
|
975
922
|
},
|
|
976
923
|
},
|
|
977
924
|
}
|
|
925
|
+
|
|
978
926
|
self.assertEqual(response.status_code, 200)
|
|
979
927
|
self.assertEqual(response.data, expected_response)
|
|
980
928
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
|
|
1
3
|
from django.conf import settings
|
|
2
4
|
from django.contrib.auth import get_user_model
|
|
3
5
|
from django.contrib.contenttypes.models import ContentType
|
|
@@ -9,6 +11,8 @@ from django.test.utils import isolate_apps
|
|
|
9
11
|
import django_filters
|
|
10
12
|
from tree_queries.models import TreeNodeForeignKey
|
|
11
13
|
|
|
14
|
+
from nautobot.circuits.filters import CircuitFilterSet
|
|
15
|
+
from nautobot.circuits.models import Circuit, CircuitType, Provider
|
|
12
16
|
from nautobot.core import filters, testing
|
|
13
17
|
from nautobot.core.models import fields as core_fields
|
|
14
18
|
from nautobot.core.utils import lookup
|
|
@@ -1450,6 +1454,129 @@ class SearchFilterTest(TestCase, testing.NautobotTestCaseMixin):
|
|
|
1450
1454
|
q = filters.SearchFilter(filter_predicates={"asn": ["icontains"]})
|
|
1451
1455
|
|
|
1452
1456
|
|
|
1457
|
+
class LookupIsNullTest(TestCase, testing.NautobotTestCaseMixin):
|
|
1458
|
+
"""
|
|
1459
|
+
Validate isnull is properly applied and filtering.
|
|
1460
|
+
"""
|
|
1461
|
+
|
|
1462
|
+
platform_queryset = dcim_models.Platform.objects.all()
|
|
1463
|
+
platform_filterset = dcim_filters.PlatformFilterSet
|
|
1464
|
+
circuit_queryset = Circuit.objects.all()
|
|
1465
|
+
circuit_filterset = CircuitFilterSet
|
|
1466
|
+
device_queryset = dcim_models.Device.objects.all()
|
|
1467
|
+
device_filterset = dcim_filters.DeviceFilterSet
|
|
1468
|
+
|
|
1469
|
+
@classmethod
|
|
1470
|
+
def setUpTestData(cls):
|
|
1471
|
+
location_type = dcim_models.LocationType.objects.create(name="Location Type 1")
|
|
1472
|
+
location_status = extras_models.Status.objects.get_for_model(dcim_models.Location).first()
|
|
1473
|
+
location = dcim_models.Location.objects.create(
|
|
1474
|
+
name="Location 1",
|
|
1475
|
+
location_type=location_type,
|
|
1476
|
+
status=location_status,
|
|
1477
|
+
)
|
|
1478
|
+
manufacturer = dcim_models.Manufacturer.objects.create(name="Manufacturer 1")
|
|
1479
|
+
dcim_models.Platform.objects.create(name="Platform 1")
|
|
1480
|
+
platform = dcim_models.Platform.objects.create(name="Platform 2", manufacturer=manufacturer)
|
|
1481
|
+
|
|
1482
|
+
device_type = dcim_models.DeviceType.objects.create(
|
|
1483
|
+
manufacturer=manufacturer,
|
|
1484
|
+
model="Model 1",
|
|
1485
|
+
is_full_depth=True,
|
|
1486
|
+
)
|
|
1487
|
+
device_role = extras_models.Role.objects.create(name="Active Test")
|
|
1488
|
+
device_role.content_types.add(ContentType.objects.get_for_model(dcim_models.Device))
|
|
1489
|
+
|
|
1490
|
+
device_status = extras_models.Status.objects.get_for_model(dcim_models.Device).first()
|
|
1491
|
+
|
|
1492
|
+
dcim_models.Device.objects.create(
|
|
1493
|
+
name="Device 1",
|
|
1494
|
+
device_type=device_type,
|
|
1495
|
+
role=device_role,
|
|
1496
|
+
platform=platform,
|
|
1497
|
+
location=location,
|
|
1498
|
+
status=device_status,
|
|
1499
|
+
)
|
|
1500
|
+
dcim_models.Device.objects.create(
|
|
1501
|
+
device_type=device_type,
|
|
1502
|
+
role=device_role,
|
|
1503
|
+
platform=platform,
|
|
1504
|
+
location=location,
|
|
1505
|
+
status=device_status,
|
|
1506
|
+
)
|
|
1507
|
+
|
|
1508
|
+
provider = Provider.objects.create(name="provider 1", asn=1)
|
|
1509
|
+
circuit_type = CircuitType.objects.create(name="Circuit Type 1")
|
|
1510
|
+
circuit_status = extras_models.Status.objects.get_for_model(dcim_models.Location).first()
|
|
1511
|
+
|
|
1512
|
+
Circuit.objects.create(
|
|
1513
|
+
cid="Circuit 1",
|
|
1514
|
+
provider=provider,
|
|
1515
|
+
install_date=datetime.date(2020, 1, 1),
|
|
1516
|
+
commit_rate=1000,
|
|
1517
|
+
circuit_type=circuit_type,
|
|
1518
|
+
status=circuit_status,
|
|
1519
|
+
)
|
|
1520
|
+
Circuit.objects.create(
|
|
1521
|
+
cid="Circuit 2",
|
|
1522
|
+
provider=provider,
|
|
1523
|
+
circuit_type=circuit_type,
|
|
1524
|
+
status=circuit_status,
|
|
1525
|
+
)
|
|
1526
|
+
|
|
1527
|
+
def test_isnull_on_fk(self):
|
|
1528
|
+
"""Test that the `isnull` filter is applied for True and False queries on foreign key fields."""
|
|
1529
|
+
params = {"manufacturer__isnull": True}
|
|
1530
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
1531
|
+
dcim_filters.PlatformFilterSet(params, self.platform_queryset).qs,
|
|
1532
|
+
dcim_models.Platform.objects.filter(manufacturer__isnull=True),
|
|
1533
|
+
)
|
|
1534
|
+
params = {"manufacturer__isnull": False}
|
|
1535
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
1536
|
+
dcim_filters.PlatformFilterSet(params, self.platform_queryset).qs,
|
|
1537
|
+
dcim_models.Platform.objects.filter(manufacturer__isnull=False),
|
|
1538
|
+
)
|
|
1539
|
+
|
|
1540
|
+
def test_isnull_on_integer(self):
|
|
1541
|
+
"""Test that the `isnull` filter is applied for True and False queries on integer fields."""
|
|
1542
|
+
params = {"commit_rate__isnull": True}
|
|
1543
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
1544
|
+
CircuitFilterSet(params, self.circuit_queryset).qs,
|
|
1545
|
+
Circuit.objects.filter(commit_rate__isnull=True),
|
|
1546
|
+
)
|
|
1547
|
+
params = {"commit_rate__isnull": False}
|
|
1548
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
1549
|
+
CircuitFilterSet(params, self.circuit_queryset).qs,
|
|
1550
|
+
Circuit.objects.filter(commit_rate__isnull=False),
|
|
1551
|
+
)
|
|
1552
|
+
|
|
1553
|
+
def test_isnull_on_date(self):
|
|
1554
|
+
"""Test that the `isnull` filter is applied for True and False queries on datetime fields."""
|
|
1555
|
+
params = {"install_date__isnull": True}
|
|
1556
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
1557
|
+
CircuitFilterSet(params, self.circuit_queryset).qs,
|
|
1558
|
+
Circuit.objects.filter(install_date__isnull=True),
|
|
1559
|
+
)
|
|
1560
|
+
params = {"install_date__isnull": False}
|
|
1561
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
1562
|
+
CircuitFilterSet(params, self.circuit_queryset).qs,
|
|
1563
|
+
Circuit.objects.filter(install_date__isnull=False),
|
|
1564
|
+
)
|
|
1565
|
+
|
|
1566
|
+
def test_isnull_on_char(self):
|
|
1567
|
+
"""Test that the `isnull` filter is applied for True and False queries on char fields."""
|
|
1568
|
+
params = {"name__isnull": True}
|
|
1569
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
1570
|
+
dcim_filters.DeviceFilterSet(params, self.device_queryset).qs,
|
|
1571
|
+
dcim_models.Device.objects.filter(name__isnull=True),
|
|
1572
|
+
)
|
|
1573
|
+
params = {"name__isnull": False}
|
|
1574
|
+
self.assertQuerysetEqualAndNotEmpty(
|
|
1575
|
+
dcim_filters.DeviceFilterSet(params, self.device_queryset).qs,
|
|
1576
|
+
dcim_models.Device.objects.filter(name__isnull=False),
|
|
1577
|
+
)
|
|
1578
|
+
|
|
1579
|
+
|
|
1453
1580
|
class FilterTypeTest(TestCase):
|
|
1454
1581
|
client_class = testing.NautobotTestClient
|
|
1455
1582
|
|
|
@@ -687,7 +687,7 @@ class DynamicFilterFormTest(TestCase):
|
|
|
687
687
|
("racks", "Rack (name or ID)"),
|
|
688
688
|
("rack_groups", "Rack groups (name or ID)"),
|
|
689
689
|
("shipping_address", "Shipping address"),
|
|
690
|
-
("status", "Status"),
|
|
690
|
+
("status", "Status (name or ID)"),
|
|
691
691
|
("vlans", "Tagged VLANs (VID or ID)"),
|
|
692
692
|
("tags", "Tags"),
|
|
693
693
|
("tenant_id", 'Tenant (ID) (deprecated, use "tenant" filter instead)'),
|