nautobot 2.4.21__py3-none-any.whl → 3.0.0a3__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/apps/choices.py +2 -2
- nautobot/apps/filters.py +9 -9
- nautobot/apps/forms.py +2 -0
- nautobot/apps/models.py +7 -2
- nautobot/apps/ui.py +20 -1
- nautobot/apps/utils.py +2 -3
- nautobot/apps/views.py +7 -1
- nautobot/circuits/filters.py +8 -23
- nautobot/circuits/navigation.py +3 -1
- nautobot/circuits/templates/circuits/circuit_create.html +9 -9
- nautobot/circuits/templates/circuits/circuit_terminations_swap.html +2 -2
- nautobot/circuits/templates/circuits/circuittermination_create.html +24 -33
- nautobot/circuits/templates/circuits/inc/circuit_termination.html +10 -10
- nautobot/circuits/templates/circuits/inc/circuit_termination_cable_fragment.html +13 -13
- nautobot/circuits/templates/circuits/inc/circuit_termination_header_extra_content.html +6 -6
- nautobot/circuits/templates/circuits/inc/circuit_termination_speed_fragment.html +3 -3
- nautobot/circuits/templates/circuits/inc/speed_widget.html +13 -13
- nautobot/circuits/templates/circuits/provider_create.html +9 -9
- nautobot/circuits/tests/integration/test_circuit.py +19 -19
- nautobot/circuits/tests/integration/test_circuits_bulk_operations.py +3 -0
- nautobot/circuits/tests/integration/test_relationships.py +4 -12
- nautobot/circuits/views.py +0 -2
- nautobot/cloud/filters.py +1 -13
- nautobot/cloud/navigation.py +3 -1
- nautobot/cloud/templates/cloud/cloudnetwork_update.html +9 -9
- nautobot/cloud/templates/cloud/cloudservice_update.html +6 -6
- nautobot/core/api/fields.py +30 -2
- nautobot/core/api/schema.py +1 -1
- nautobot/core/api/serializers.py +9 -2
- nautobot/core/api/urls.py +2 -0
- nautobot/core/api/views.py +58 -37
- nautobot/core/apps/__init__.py +6 -12
- nautobot/core/branching.py +83 -0
- nautobot/core/celery/__init__.py +11 -6
- nautobot/core/celery/backends.py +2 -0
- nautobot/core/celery/encoders.py +7 -0
- nautobot/core/celery/task.py +44 -0
- nautobot/core/checks.py +60 -0
- nautobot/core/cli/bootstrap_v3_to_v5.py +776 -0
- nautobot/core/constants.py +9 -0
- nautobot/core/context_processors.py +84 -0
- nautobot/core/filters.py +131 -2
- nautobot/core/forms/__init__.py +4 -2
- nautobot/core/forms/fields.py +10 -8
- nautobot/core/forms/forms.py +21 -9
- nautobot/core/forms/search.py +0 -15
- nautobot/core/forms/widgets.py +3 -2
- nautobot/core/graphql/__init__.py +8 -26
- nautobot/core/graphql/generators.py +16 -6
- nautobot/core/graphql/schema.py +1 -1
- nautobot/core/graphql/schema_init.py +1 -2
- nautobot/core/graphql/utils.py +7 -9
- nautobot/core/jobs/__init__.py +158 -0
- nautobot/core/management/commands/generate_test_data.py +28 -9
- nautobot/core/models/__init__.py +17 -2
- nautobot/core/models/fields.py +3 -2
- nautobot/core/models/generics.py +9 -1
- nautobot/core/models/name_color_content_types.py +1 -1
- nautobot/core/models/ordering.py +7 -5
- nautobot/core/models/querysets.py +77 -2
- nautobot/core/models/tree_queries.py +6 -4
- nautobot/core/settings.py +30 -16
- nautobot/core/settings.yaml +13 -7
- nautobot/core/tables.py +114 -44
- nautobot/core/templates/403.html +1 -1
- nautobot/core/templates/403_csrf_failure.html +1 -1
- nautobot/core/templates/404.html +1 -1
- nautobot/core/templates/40x.html +8 -8
- nautobot/core/templates/500.html +10 -10
- nautobot/core/templates/about.html +13 -12
- nautobot/core/templates/admin/actions.html +1 -1
- nautobot/core/templates/admin/app_index.html +3 -3
- nautobot/core/templates/admin/base.html +45 -52
- nautobot/core/templates/admin/base_site.html +0 -9
- nautobot/core/templates/admin/change_form.html +5 -5
- nautobot/core/templates/admin/change_list.html +8 -12
- nautobot/core/templates/admin/change_list_results.html +3 -3
- nautobot/core/templates/admin/config/config.html +24 -24
- nautobot/core/templates/admin/delete_confirmation.html +5 -5
- nautobot/core/templates/admin/edit_inline/stacked.html +5 -5
- nautobot/core/templates/admin/edit_inline/tabular.html +3 -3
- nautobot/core/templates/admin/includes/fieldset.html +15 -15
- nautobot/core/templates/admin/index.html +8 -8
- nautobot/core/templates/admin/submit_line.html +5 -5
- nautobot/core/templates/base_django.html +36 -32
- nautobot/core/templates/buttons/add.html +1 -1
- nautobot/core/templates/buttons/consolidated_detail_view_action_buttons.html +2 -2
- nautobot/core/templates/buttons/export.html +17 -18
- nautobot/core/templates/buttons/job_import.html +2 -2
- nautobot/core/templates/components/breadcrumbs.html +19 -17
- nautobot/core/templates/components/button/dropdown.html +7 -5
- nautobot/core/templates/components/echarts.html +2 -0
- nautobot/core/templates/components/layout/one_over_two.html +3 -3
- nautobot/core/templates/components/layout/two_over_one.html +3 -3
- nautobot/core/templates/components/panel/body_content_data_table.html +2 -2
- nautobot/core/templates/components/panel/body_content_tags.html +1 -1
- nautobot/core/templates/components/panel/body_wrapper_generic.html +4 -2
- nautobot/core/templates/components/panel/body_wrapper_generic_table.html +1 -1
- nautobot/core/templates/components/panel/body_wrapper_key_value_table.html +5 -3
- nautobot/core/templates/components/panel/body_wrapper_table.html +4 -2
- nautobot/core/templates/components/panel/footer_contacts_table.html +4 -4
- nautobot/core/templates/components/panel/footer_content_table.html +3 -3
- nautobot/core/templates/components/panel/grouping_toggle.html +12 -11
- nautobot/core/templates/components/panel/header_extra_content_table.html +2 -11
- nautobot/core/templates/components/panel/panel.html +6 -3
- nautobot/core/templates/components/panel/stats_panel_body.html +9 -7
- nautobot/core/templates/components/tab/content_wrapper.html +29 -1
- nautobot/core/templates/components/tab/label_wrapper.html +10 -2
- nautobot/core/templates/components/tab/label_wrapper_distinct_view.html +11 -4
- nautobot/core/templates/echarts/echarts.html +20 -0
- nautobot/core/templates/exceptions/import_error.html +2 -2
- nautobot/core/templates/exceptions/permission_error.html +1 -1
- nautobot/core/templates/exceptions/programming_error.html +2 -2
- nautobot/core/templates/generic/object_bulk_add_component.html +29 -20
- nautobot/core/templates/generic/object_bulk_create.html +87 -75
- nautobot/core/templates/generic/object_bulk_destroy.html +35 -37
- nautobot/core/templates/generic/object_bulk_remove.html +30 -26
- nautobot/core/templates/generic/object_bulk_rename.html +53 -40
- nautobot/core/templates/generic/object_bulk_update.html +36 -29
- nautobot/core/templates/generic/object_create.html +40 -27
- nautobot/core/templates/generic/object_import.html +36 -24
- nautobot/core/templates/generic/object_list.html +279 -215
- nautobot/core/templates/generic/object_notes.html +21 -11
- nautobot/core/templates/generic/object_retrieve.html +161 -213
- nautobot/core/templates/graphene/graphiql.html +113 -60
- nautobot/core/templates/home.html +164 -87
- nautobot/core/templates/import_success.html +3 -2
- nautobot/core/templates/inc/ajax_loader.html +1 -1
- nautobot/core/templates/inc/computed_fields/panel_data.html +25 -13
- nautobot/core/templates/inc/created_updated.html +12 -7
- nautobot/core/templates/inc/custom_fields/panel_data.html +28 -16
- nautobot/core/templates/inc/custom_fields_panel.html +3 -3
- nautobot/core/templates/inc/dynamic_groups_panel.html +3 -3
- nautobot/core/templates/inc/extras_features_edit_form_fields.html +15 -15
- nautobot/core/templates/inc/footer.html +90 -40
- nautobot/core/templates/inc/form_static_field.html +6 -0
- nautobot/core/templates/inc/header.html +75 -0
- nautobot/core/templates/inc/header_banners.html +17 -0
- nautobot/core/templates/inc/header_messages.html +6 -0
- nautobot/core/templates/inc/image_attachments.html +9 -9
- nautobot/core/templates/inc/javascript.html +7 -24
- nautobot/core/templates/inc/media.html +4 -29
- nautobot/core/templates/inc/modal.html +2 -2
- nautobot/core/templates/inc/nav_favorites.html +27 -0
- nautobot/core/templates/inc/nav_menu.html +150 -108
- nautobot/core/templates/inc/object_details_advanced_panel.html +84 -71
- nautobot/core/templates/inc/page_title.html +23 -0
- nautobot/core/templates/inc/paginator.html +39 -28
- nautobot/core/templates/inc/relationships/panel_override.html +3 -3
- nautobot/core/templates/inc/relationships_panel.html +3 -3
- nautobot/core/templates/inc/relationships_table_rows.html +1 -1
- nautobot/core/templates/inc/search_panel.html +22 -16
- nautobot/core/templates/inc/table.html +61 -36
- nautobot/core/templates/inc/tenancy_form_panel.html +3 -3
- nautobot/core/templates/login.html +17 -59
- nautobot/core/templates/modals/modal_theme.html +12 -23
- nautobot/core/templates/nautobot_config.py.j2 +6 -5
- nautobot/core/templates/panel_table.html +8 -12
- nautobot/core/templates/redoc_ui.html +80 -0
- nautobot/core/templates/rest_framework/api.html +43 -21
- nautobot/core/templates/search.html +12 -13
- nautobot/core/templates/swagger_ui.html +19 -4
- nautobot/core/templates/system_jobs/import_objects.html +70 -58
- nautobot/core/templates/template.css +0 -6
- nautobot/core/templates/utilities/comment_form.html +34 -0
- nautobot/core/templates/utilities/confirmation_form.html +17 -9
- nautobot/core/templates/utilities/obj_table.html +19 -11
- nautobot/core/templates/utilities/render_field.html +27 -21
- nautobot/core/templates/utilities/render_jinja2.html +22 -25
- nautobot/core/templates/utilities/templatetags/advanced_filter_indicator.html +8 -0
- nautobot/core/templates/utilities/templatetags/badge.html +1 -1
- nautobot/core/templates/utilities/templatetags/dynamic_group_assignment_modal.html +2 -3
- nautobot/core/templates/utilities/templatetags/filter_form_drawer.html +482 -0
- nautobot/core/templates/utilities/templatetags/modal_form_as_dialog.html +14 -18
- nautobot/core/templates/utilities/templatetags/saved_view_modal.html +11 -11
- nautobot/core/templates/utilities/templatetags/table_config_form.html +51 -24
- nautobot/core/templates/utilities/templatetags/tag.html +1 -1
- nautobot/core/templates/utilities/templatetags/utilization_graph.html +3 -3
- nautobot/core/templates/utilities/theme_preview.html +829 -566
- nautobot/core/templates/utilities/worker_status.html +42 -41
- nautobot/core/templates/widgets/selectwithdisabled_option.html +3 -1
- nautobot/core/templates/widgets/sluginput.html +2 -2
- nautobot/core/templatetags/buttons.py +38 -40
- nautobot/core/templatetags/helpers.py +105 -28
- nautobot/core/templatetags/ui_framework.py +17 -0
- nautobot/core/testing/api.py +76 -12
- nautobot/core/testing/filters.py +11 -27
- nautobot/core/testing/integration.py +128 -10
- nautobot/core/testing/mixins.py +7 -4
- nautobot/core/testing/utils.py +28 -5
- nautobot/core/testing/views.py +125 -27
- nautobot/core/tests/integration/test_app_home.py +39 -35
- nautobot/core/tests/integration/test_app_navbar.py +60 -67
- nautobot/core/tests/integration/test_filters.py +123 -55
- nautobot/core/tests/integration/test_general_functionality.py +1 -1
- nautobot/core/tests/integration/test_home.py +10 -18
- nautobot/core/tests/integration/test_import_objects_ui.py +2 -9
- nautobot/core/tests/integration/test_navbar.py +41 -16
- nautobot/core/tests/integration/test_swagger.py +1 -7
- nautobot/core/tests/integration/test_theme.py +3 -0
- nautobot/core/tests/nautobot_config_without_example_apps.py +4 -0
- nautobot/core/tests/runner.py +6 -1
- nautobot/core/tests/test_api.py +5 -3
- nautobot/core/tests/test_branching.py +154 -0
- nautobot/core/tests/test_breadcrumbs.py +7 -8
- nautobot/core/tests/test_checks.py +28 -0
- nautobot/core/tests/test_commands.py +0 -41
- nautobot/core/tests/test_config.py +2 -1
- nautobot/core/tests/test_csv.py +4 -7
- nautobot/core/tests/test_filters.py +326 -318
- nautobot/core/tests/test_forms.py +19 -30
- nautobot/core/tests/test_graphql.py +67 -57
- nautobot/core/tests/test_models.py +1 -1
- nautobot/core/tests/test_nautobot_server.py +2 -0
- nautobot/core/tests/test_navigations.py +78 -10
- nautobot/core/tests/test_tables.py +3 -1
- nautobot/core/tests/test_templatetags_helpers.py +61 -21
- nautobot/core/tests/test_templatetags_ui_framework.py +36 -18
- nautobot/core/tests/test_ui.py +207 -2
- nautobot/core/tests/test_utils.py +147 -2
- nautobot/core/tests/test_views.py +201 -64
- nautobot/core/tests/test_views_utils.py +1 -1
- nautobot/core/ui/breadcrumbs.py +2 -12
- nautobot/core/ui/choices.py +190 -0
- nautobot/core/ui/constants.py +86 -0
- nautobot/core/ui/echarts.py +474 -0
- nautobot/core/ui/nav.py +5 -1
- nautobot/core/ui/object_detail.py +180 -16
- nautobot/core/urls.py +13 -1
- nautobot/core/utils/cache.py +71 -0
- nautobot/core/utils/data.py +8 -5
- nautobot/core/utils/filtering.py +8 -2
- nautobot/core/utils/git.py +3 -3
- nautobot/core/utils/lookup.py +87 -13
- nautobot/core/utils/migrations.py +22 -0
- nautobot/core/utils/module_loading.py +26 -0
- nautobot/core/utils/permissions.py +9 -5
- nautobot/core/views/__init__.py +114 -63
- nautobot/core/views/generic.py +34 -27
- nautobot/core/views/mixins.py +49 -27
- nautobot/core/views/renderers.py +3 -5
- nautobot/core/views/utils.py +10 -5
- nautobot/core/views/viewsets.py +2 -1
- nautobot/data_validation/__init__.py +0 -0
- nautobot/data_validation/api/__init__.py +1 -0
- nautobot/data_validation/api/serializers.py +80 -0
- nautobot/data_validation/api/urls.py +20 -0
- nautobot/data_validation/api/views.py +44 -0
- nautobot/data_validation/apps.py +18 -0
- nautobot/data_validation/custom_validators.py +330 -0
- nautobot/data_validation/filters.py +133 -0
- nautobot/data_validation/form_mixin.py +25 -0
- nautobot/data_validation/forms.py +342 -0
- nautobot/data_validation/migrations/0001_initial.py +224 -0
- nautobot/data_validation/migrations/0002_data_migration_from_app.py +324 -0
- nautobot/data_validation/migrations/__init__.py +0 -0
- nautobot/data_validation/models.py +361 -0
- nautobot/data_validation/navigation.py +74 -0
- nautobot/data_validation/signals.py +30 -0
- nautobot/data_validation/tables.py +259 -0
- nautobot/data_validation/templates/data_validation/datacompliance_retrieve.html +1 -0
- nautobot/data_validation/templates/data_validation/datacompliance_tab.html +11 -0
- nautobot/data_validation/templates/data_validation/device_constraints.html +61 -0
- nautobot/data_validation/tests/__init__.py +20 -0
- nautobot/data_validation/tests/migrations/__init__.py +0 -0
- nautobot/data_validation/tests/migrations/test_migrations.py +489 -0
- nautobot/data_validation/tests/test_api.py +238 -0
- nautobot/data_validation/tests/test_custom_validators.py +423 -0
- nautobot/data_validation/tests/test_data_compliance_rules.py +85 -0
- nautobot/data_validation/tests/test_filters.py +240 -0
- nautobot/data_validation/tests/test_form_mixin.py +115 -0
- nautobot/data_validation/tests/test_models.py +393 -0
- nautobot/data_validation/tests/test_views.py +435 -0
- nautobot/data_validation/urls.py +21 -0
- nautobot/data_validation/views.py +227 -0
- nautobot/dcim/api/serializers.py +10 -13
- nautobot/dcim/api/urls.py +2 -0
- nautobot/dcim/api/views.py +7 -0
- nautobot/dcim/apps.py +4 -0
- nautobot/dcim/choices.py +16 -0
- nautobot/dcim/custom_validators.py +84 -0
- nautobot/dcim/filter_mixins.py +353 -4
- nautobot/dcim/{filters/__init__.py → filters.py} +70 -157
- nautobot/dcim/forms.py +12 -6
- nautobot/dcim/graphql/types.py +1 -0
- nautobot/dcim/migrations/0075_add_deviceclusterassignment.py +52 -0
- nautobot/dcim/migrations/0076_device_cluster_to_clusters_data_migration.py +40 -0
- nautobot/dcim/migrations/0077_remove_device_cluster.py +14 -0
- nautobot/dcim/migrations/0078_remove_device_location_tenant_name_uniqueness.py +16 -0
- nautobot/dcim/migrations/0079_device_name_data_migration.py +59 -0
- nautobot/dcim/models/__init__.py +2 -0
- nautobot/dcim/models/device_components.py +3 -1
- nautobot/dcim/models/devices.py +115 -51
- nautobot/dcim/navigation.py +7 -3
- nautobot/dcim/querysets.py +6 -0
- nautobot/dcim/signals.py +19 -0
- nautobot/dcim/tables/devices.py +9 -5
- nautobot/dcim/tables/template_code.py +191 -102
- nautobot/dcim/templates/dcim/cable.html +1 -1
- nautobot/dcim/templates/dcim/cable_connect.html +62 -146
- nautobot/dcim/templates/dcim/cable_retrieve.html +10 -10
- nautobot/dcim/templates/dcim/cable_trace.html +15 -17
- nautobot/dcim/templates/dcim/console_port_connection_list.html +2 -2
- nautobot/dcim/templates/dcim/consoleport.html +18 -17
- nautobot/dcim/templates/dcim/consoleserverport.html +18 -17
- nautobot/dcim/templates/dcim/controller_create.html +12 -8
- nautobot/dcim/templates/dcim/controller_wirelessnetworks.html +1 -1
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_create.html +6 -6
- nautobot/dcim/templates/dcim/controllermanageddevicegroup_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/device/config.html +17 -19
- nautobot/dcim/templates/dcim/device/lldp_neighbors.html +4 -4
- nautobot/dcim/templates/dcim/device/status.html +20 -20
- nautobot/dcim/templates/dcim/device_component_add.html +24 -15
- nautobot/dcim/templates/dcim/device_create.html +120 -120
- nautobot/dcim/templates/dcim/device_list.html +75 -12
- nautobot/dcim/templates/dcim/devicebay.html +7 -7
- nautobot/dcim/templates/dcim/devicebay_populate.html +29 -23
- nautobot/dcim/templates/dcim/deviceredundancygroup_create.html +6 -6
- nautobot/dcim/templates/dcim/devicetype.html +1 -1
- nautobot/dcim/templates/dcim/devicetype_component_add.html +25 -19
- nautobot/dcim/templates/dcim/devicetype_list.html +4 -4
- nautobot/dcim/templates/dcim/devicetype_update.html +9 -9
- nautobot/dcim/templates/dcim/footer_convert_to_contact_or_team_record.html +3 -3
- nautobot/dcim/templates/dcim/frontport.html +21 -20
- nautobot/dcim/templates/dcim/inc/cable_form.html +7 -7
- nautobot/dcim/templates/dcim/inc/cable_termination.html +1 -1
- nautobot/dcim/templates/dcim/inc/cable_toggle_buttons.html +18 -9
- nautobot/dcim/templates/dcim/inc/detail_softwareversion_softwareimagefile_rows.html +1 -1
- nautobot/dcim/templates/dcim/inc/device_interface_filter.html +1 -1
- nautobot/dcim/templates/dcim/inc/devicetype_component_table.html +10 -10
- nautobot/dcim/templates/dcim/inc/edit_form_softwareversion_js.html +2 -2
- nautobot/dcim/templates/dcim/inc/homepage_connections.html +2 -2
- nautobot/dcim/templates/dcim/inc/moduletype_component_table.html +10 -10
- nautobot/dcim/templates/dcim/inc/rack_elevation.html +2 -2
- nautobot/dcim/templates/dcim/interface.html +42 -22
- nautobot/dcim/templates/dcim/interface_connection_list.html +2 -2
- nautobot/dcim/templates/dcim/interface_edit.html +26 -11
- nautobot/dcim/templates/dcim/interfaceredundancygroupassociation_create.html +3 -3
- nautobot/dcim/templates/dcim/inventoryitem.html +3 -3
- nautobot/dcim/templates/dcim/inventoryitem_add.html +21 -10
- nautobot/dcim/templates/dcim/inventoryitem_bulk_delete.html +1 -1
- nautobot/dcim/templates/dcim/inventoryitem_edit.html +6 -4
- nautobot/dcim/templates/dcim/location.html +1 -1
- nautobot/dcim/templates/dcim/location_migrate_data_to_contact.html +24 -18
- nautobot/dcim/templates/dcim/location_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/location_update.html +9 -9
- nautobot/dcim/templates/dcim/locationtype.html +0 -1
- nautobot/dcim/templates/dcim/module/base.html +67 -27
- nautobot/dcim/templates/dcim/module_consoleports.html +13 -15
- nautobot/dcim/templates/dcim/module_consoleserverports.html +13 -15
- nautobot/dcim/templates/dcim/module_frontports.html +13 -15
- nautobot/dcim/templates/dcim/module_interfaces.html +14 -16
- nautobot/dcim/templates/dcim/module_list.html +59 -10
- nautobot/dcim/templates/dcim/module_modulebays.html +12 -14
- nautobot/dcim/templates/dcim/module_poweroutlets.html +13 -15
- nautobot/dcim/templates/dcim/module_powerports.html +13 -15
- nautobot/dcim/templates/dcim/module_rearports.html +13 -15
- nautobot/dcim/templates/dcim/module_retrieve.html +3 -3
- nautobot/dcim/templates/dcim/module_update.html +15 -9
- nautobot/dcim/templates/dcim/modulebay_retrieve.html +0 -93
- nautobot/dcim/templates/dcim/modulefamily_retrieve.html +7 -7
- nautobot/dcim/templates/dcim/moduletype_list.html +2 -2
- nautobot/dcim/templates/dcim/moduletype_retrieve.html +74 -35
- nautobot/dcim/templates/dcim/platform_create.html +9 -9
- nautobot/dcim/templates/dcim/power_port_connection_list.html +3 -3
- nautobot/dcim/templates/dcim/powerfeed.html +1 -1
- nautobot/dcim/templates/dcim/powerfeed_edit.html +15 -15
- nautobot/dcim/templates/dcim/poweroutlet.html +13 -13
- nautobot/dcim/templates/dcim/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/powerport.html +17 -16
- nautobot/dcim/templates/dcim/rack.html +1 -1
- nautobot/dcim/templates/dcim/rack_elevation.html +2 -2
- nautobot/dcim/templates/dcim/rack_elevation_list.html +21 -9
- nautobot/dcim/templates/dcim/rack_retrieve.html +75 -57
- nautobot/dcim/templates/dcim/rack_update.html +14 -14
- nautobot/dcim/templates/dcim/rackreservation.html +1 -1
- nautobot/dcim/templates/dcim/rackreservation_edit.html +6 -6
- nautobot/dcim/templates/dcim/rearport.html +19 -18
- nautobot/dcim/templates/dcim/trace/cable.html +1 -1
- nautobot/dcim/templates/dcim/trace/circuit.html +1 -1
- nautobot/dcim/templates/dcim/trace/device.html +1 -1
- nautobot/dcim/templates/dcim/trace/powerpanel.html +1 -1
- nautobot/dcim/templates/dcim/trace/termination.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_add_member.html +25 -16
- nautobot/dcim/templates/dcim/virtualchassis_create.html +6 -6
- nautobot/dcim/templates/dcim/virtualchassis_edit.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_retrieve.html +1 -1
- nautobot/dcim/templates/dcim/virtualchassis_update.html +36 -22
- nautobot/dcim/templates/dcim/virtualdevicecontext_update.html +9 -9
- nautobot/dcim/tests/integration/test_controller.py +6 -6
- nautobot/dcim/tests/integration/test_controller_managed_device_group.py +7 -7
- nautobot/dcim/tests/integration/test_create_device.py +9 -9
- nautobot/dcim/tests/integration/test_device_bulk_operations.py +7 -2
- nautobot/dcim/tests/integration/test_fileinputpicker.py +5 -7
- nautobot/dcim/tests/integration/test_location_bulk_operations.py +2 -0
- nautobot/dcim/tests/integration/test_module_bay_position.py +4 -1
- nautobot/dcim/tests/test_api.py +86 -6
- nautobot/dcim/tests/test_custom_validators.py +229 -0
- nautobot/dcim/tests/test_filters.py +159 -110
- nautobot/dcim/tests/test_graphql.py +32 -36
- nautobot/dcim/tests/test_jobs.py +1 -1
- nautobot/dcim/tests/test_models.py +229 -1
- nautobot/dcim/tests/test_views.py +31 -20
- nautobot/dcim/utils.py +3 -3
- nautobot/dcim/views.py +77 -41
- nautobot/extras/api/serializers.py +83 -19
- nautobot/extras/api/urls.py +7 -0
- nautobot/extras/api/views.py +243 -140
- nautobot/extras/choices.py +34 -13
- nautobot/extras/constants.py +1 -1
- nautobot/extras/context_managers.py +26 -26
- nautobot/extras/datasources/git.py +22 -0
- nautobot/extras/datasources/registry.py +3 -0
- nautobot/extras/exceptions.py +5 -0
- nautobot/extras/factory.py +11 -1
- nautobot/extras/{filters/mixins.py → filter_mixins.py} +4 -3
- nautobot/extras/{filters/__init__.py → filters.py} +203 -58
- nautobot/extras/forms/base.py +2 -1
- nautobot/extras/forms/forms.py +225 -20
- nautobot/extras/forms/mixins.py +0 -41
- nautobot/extras/homepage.py +21 -2
- nautobot/extras/jobs.py +2 -8
- nautobot/extras/jobs_ui.py +2 -2
- nautobot/extras/management/__init__.py +9 -0
- nautobot/extras/managers.py +31 -22
- nautobot/extras/migrations/0126_approval_workflow_pre_check.py +58 -0
- nautobot/extras/migrations/0127_approval_workflow_models.py +266 -0
- nautobot/extras/migrations/0128_remove_job_approval_required_and_more.py +29 -0
- nautobot/extras/migrations/0129_jobresult_debug_log_count_jobresult_error_log_count_and_more.py +37 -0
- nautobot/extras/migrations/0130_jobresult_generate_log_entry_counts.py +42 -0
- nautobot/extras/models/__init__.py +14 -3
- nautobot/extras/models/approvals.py +556 -0
- nautobot/extras/models/change_logging.py +1 -0
- nautobot/extras/models/contacts.py +2 -0
- nautobot/extras/models/customfields.py +57 -22
- nautobot/extras/models/datasources.py +21 -0
- nautobot/extras/models/groups.py +2 -0
- nautobot/extras/models/jobs.py +122 -39
- nautobot/extras/models/metadata.py +2 -3
- nautobot/extras/models/mixins.py +129 -1
- nautobot/extras/models/models.py +22 -14
- nautobot/extras/models/relationships.py +47 -10
- nautobot/extras/models/secrets.py +1 -0
- nautobot/extras/models/statuses.py +0 -15
- nautobot/extras/models/tags.py +1 -1
- nautobot/extras/navigation.py +42 -15
- nautobot/extras/plugins/__init__.py +33 -55
- nautobot/extras/plugins/marketplace_manifest.yml +1 -23
- nautobot/extras/plugins/tables.py +8 -6
- nautobot/extras/plugins/urls.py +2 -21
- nautobot/extras/plugins/utils.py +1 -33
- nautobot/extras/plugins/validators.py +10 -10
- nautobot/extras/plugins/views.py +1 -5
- nautobot/extras/querysets.py +17 -21
- nautobot/extras/signals.py +23 -8
- nautobot/extras/tables.py +420 -99
- nautobot/extras/templates/extras/approval_dashboard.html +15 -0
- nautobot/extras/templates/extras/approval_workflow/approve.html +11 -0
- nautobot/extras/templates/extras/approval_workflow/comment.html +9 -0
- nautobot/extras/templates/extras/approval_workflow/deny.html +10 -0
- nautobot/extras/templates/extras/approvalworkflowdefinition_update.html +77 -0
- nautobot/extras/templates/extras/approvalworkflowstage_retrieve.html +29 -0
- nautobot/extras/templates/extras/configcontext_update.html +12 -12
- nautobot/extras/templates/extras/configcontextschema.html +1 -1
- nautobot/extras/templates/extras/configcontextschema_retrieve.html +9 -9
- nautobot/extras/templates/extras/configcontextschema_update.html +6 -6
- nautobot/extras/templates/extras/configcontextschema_validation.html +2 -2
- nautobot/extras/templates/extras/customfield_update.html +12 -12
- nautobot/extras/templates/extras/dynamicgroup.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup_edit.html +1 -1
- nautobot/extras/templates/extras/dynamicgroup_retrieve.html +17 -17
- nautobot/extras/templates/extras/dynamicgroup_update.html +24 -24
- nautobot/extras/templates/extras/externalintegration_update.html +6 -6
- nautobot/extras/templates/extras/gitrepository.html +1 -1
- nautobot/extras/templates/extras/gitrepository_object_edit.html +1 -1
- nautobot/extras/templates/extras/gitrepository_result.html +1 -1
- nautobot/extras/templates/extras/gitrepository_retrieve.html +12 -12
- nautobot/extras/templates/extras/gitrepository_update.html +25 -7
- nautobot/extras/templates/extras/graphqlquery_retrieve.html +1 -1
- nautobot/extras/templates/extras/inc/approval_buttons_column.html +38 -0
- nautobot/extras/templates/extras/inc/bulk_edit_overridable_field.html +14 -13
- nautobot/extras/templates/extras/inc/configcontext_format.html +11 -4
- nautobot/extras/templates/extras/inc/graphqlquery_execute.html +7 -7
- nautobot/extras/templates/extras/inc/job_label.html +5 -5
- nautobot/extras/templates/extras/inc/job_table.html +23 -10
- nautobot/extras/templates/extras/inc/job_tiles.html +33 -21
- nautobot/extras/templates/extras/inc/jobresult.html +6 -6
- nautobot/extras/templates/extras/inc/json_format.html +11 -4
- nautobot/extras/templates/extras/inc/object_contact_header.html +6 -6
- nautobot/extras/templates/extras/inc/overridable_field.html +16 -15
- nautobot/extras/templates/extras/inc/panel_approvalworkflowstage.html +34 -0
- nautobot/extras/templates/extras/inc/panel_changelog.html +9 -9
- nautobot/extras/templates/extras/inc/panel_jobhistory.html +8 -6
- nautobot/extras/templates/extras/inc/tags_panel.html +3 -3
- nautobot/extras/templates/extras/job.html +154 -155
- nautobot/extras/templates/extras/job_approval_confirmation.html +4 -27
- nautobot/extras/templates/extras/job_bulk_edit.html +18 -1
- nautobot/extras/templates/extras/job_detail.html +1 -1
- nautobot/extras/templates/extras/job_edit.html +69 -64
- nautobot/extras/templates/extras/job_list.html +37 -60
- nautobot/extras/templates/extras/jobresult.html +1 -1
- nautobot/extras/templates/extras/jobresult_retrieve.html +17 -17
- nautobot/extras/templates/extras/marketplace.html +62 -71
- nautobot/extras/templates/extras/metadatatype_create.html +9 -9
- nautobot/extras/templates/extras/note.html +1 -1
- nautobot/extras/templates/extras/object_approvalworkflow.html +36 -0
- nautobot/extras/templates/extras/object_assign_contact_or_team.html +16 -7
- nautobot/extras/templates/extras/object_configcontext.html +20 -20
- nautobot/extras/templates/extras/object_new_contact.html +6 -6
- nautobot/extras/templates/extras/object_new_team.html +6 -6
- nautobot/extras/templates/extras/objectchange.html +1 -1
- nautobot/extras/templates/extras/objectchange_retrieve.html +37 -56
- nautobot/extras/templates/extras/plugin_detail.html +40 -41
- nautobot/extras/templates/extras/plugins_list.html +23 -38
- nautobot/extras/templates/extras/plugins_tiles.html +28 -28
- nautobot/extras/templates/extras/role_retrieve.html +112 -48
- nautobot/extras/templates/extras/scheduledjob.html +25 -28
- nautobot/extras/templates/extras/secret_create.html +11 -11
- nautobot/extras/templates/extras/secretsgroup_update.html +6 -6
- nautobot/extras/templates/extras/staticgroupassociation_retrieve.html +3 -3
- nautobot/extras/templates/extras/status.html +1 -1
- nautobot/extras/templates/extras/tag.html +1 -1
- nautobot/extras/templates/extras/tag_update.html +3 -3
- nautobot/extras/templates/extras/templatetags/log_level.html +1 -1
- nautobot/extras/templates/extras/templatetags/plugin_object_detail_tabs.html +2 -2
- nautobot/extras/templates/extras/webhook.html +12 -12
- nautobot/extras/templatetags/approvals.py +19 -0
- nautobot/extras/templatetags/custom_links.py +12 -12
- nautobot/extras/templatetags/job_buttons.py +14 -12
- nautobot/extras/test_jobs/invalid_import.py +9 -0
- nautobot/extras/test_jobs/log_counts_by_level.py +23 -0
- nautobot/extras/test_jobs/missing_import.py +11 -0
- nautobot/extras/tests/integration/test_computedfields.py +5 -8
- nautobot/extras/tests/integration/test_configcontextschema.py +43 -48
- nautobot/extras/tests/integration/test_customfields.py +33 -33
- nautobot/extras/tests/integration/test_dynamicgroups.py +5 -10
- nautobot/extras/tests/integration/test_jobs.py +2 -4
- nautobot/extras/tests/integration/test_notes.py +3 -9
- nautobot/extras/tests/integration/test_plugin_banner.py +3 -0
- nautobot/extras/tests/integration/test_plugins.py +35 -27
- nautobot/extras/tests/integration/test_relationships.py +7 -11
- nautobot/extras/tests/integration/test_tagfilter.py +3 -11
- nautobot/extras/tests/test_api.py +786 -242
- nautobot/extras/tests/test_approvals.py +715 -0
- nautobot/extras/tests/test_changelog.py +18 -14
- nautobot/extras/tests/test_customfields.py +14 -13
- nautobot/extras/tests/test_datasources.py +1 -1
- nautobot/extras/tests/test_dynamicgroups.py +9 -4
- nautobot/extras/tests/test_filters.py +443 -13
- nautobot/extras/tests/test_forms.py +18 -57
- nautobot/extras/tests/test_jobs.py +25 -4
- nautobot/extras/tests/test_migrations.py +81 -1
- nautobot/extras/tests/test_models.py +378 -47
- nautobot/extras/tests/test_plugins.py +47 -13
- nautobot/extras/tests/test_relationships.py +7 -2
- nautobot/extras/tests/test_utils.py +2 -0
- nautobot/extras/tests/test_views.py +780 -493
- nautobot/extras/urls.py +36 -12
- nautobot/extras/utils.py +58 -12
- nautobot/extras/views.py +668 -209
- nautobot/ipam/factory.py +7 -0
- nautobot/ipam/filter_mixins.py +38 -0
- nautobot/ipam/filters.py +35 -71
- nautobot/ipam/formfields.py +1 -1
- nautobot/ipam/forms.py +6 -3
- nautobot/ipam/migrations/0030_ipam__namespaces.py +13 -0
- nautobot/ipam/migrations/0031_ipam___data_migrations.py +4 -1
- nautobot/ipam/migrations/0054_namespace_tenant.py +25 -0
- nautobot/ipam/models.py +29 -2
- nautobot/ipam/navigation.py +3 -1
- nautobot/ipam/querysets.py +1 -2
- nautobot/ipam/tables.py +26 -17
- nautobot/ipam/templates/ipam/inc/ipadress_edit_header.html +6 -6
- nautobot/ipam/templates/ipam/inc/service.html +8 -8
- nautobot/ipam/templates/ipam/inc/toggle_available.html +10 -10
- nautobot/ipam/templates/ipam/inc/vlangroup_header.html +3 -2
- nautobot/ipam/templates/ipam/ipaddress.html +27 -13
- nautobot/ipam/templates/ipam/ipaddress_assign.html +31 -24
- nautobot/ipam/templates/ipam/ipaddress_bulk_add.html +3 -3
- nautobot/ipam/templates/ipam/ipaddress_edit.html +9 -9
- nautobot/ipam/templates/ipam/ipaddress_interfaces.html +7 -9
- nautobot/ipam/templates/ipam/ipaddress_merge.html +195 -186
- nautobot/ipam/templates/ipam/ipaddress_vm_interfaces.html +7 -9
- nautobot/ipam/templates/ipam/ipaddresstointerface_retrieve.html +7 -5
- nautobot/ipam/templates/ipam/namespace_ip_addresses.html +1 -1
- nautobot/ipam/templates/ipam/namespace_prefixes.html +1 -1
- nautobot/ipam/templates/ipam/namespace_update.html +15 -0
- nautobot/ipam/templates/ipam/namespace_vrfs.html +1 -1
- nautobot/ipam/templates/ipam/prefix_create.html +9 -9
- nautobot/ipam/templates/ipam/prefix_list.html +15 -14
- nautobot/ipam/templates/ipam/prefix_retrieve.html +0 -1
- nautobot/ipam/templates/ipam/vlan.html +1 -1
- nautobot/ipam/templates/ipam/vlan_interfaces.html +1 -1
- nautobot/ipam/templates/ipam/vlan_update.html +6 -6
- nautobot/ipam/templates/ipam/vlan_vminterfaces.html +1 -1
- nautobot/ipam/templates/ipam/vrf_edit.html +15 -15
- nautobot/ipam/tests/integration/test_prefixes.py +5 -13
- nautobot/ipam/tests/migration/test_migrations.py +89 -0
- nautobot/ipam/tests/test_api.py +20 -7
- nautobot/ipam/tests/test_filters.py +10 -0
- nautobot/ipam/tests/test_forms.py +1 -1
- nautobot/ipam/tests/test_models.py +1 -1
- nautobot/ipam/tests/test_tables.py +1 -2
- nautobot/ipam/tests/test_utils.py +1 -1
- nautobot/ipam/tests/test_views.py +24 -21
- nautobot/ipam/ui.py +0 -17
- nautobot/ipam/utils/migrations.py +16 -2
- nautobot/ipam/utils/testing.py +9 -3
- nautobot/ipam/views.py +49 -7
- nautobot/project-static/dist/css/graphql-libraries.css +655 -0
- nautobot/project-static/dist/css/graphql-libraries.css.map +1 -0
- nautobot/project-static/dist/css/materialdesignicons.css +3 -0
- nautobot/project-static/dist/css/materialdesignicons.css.map +1 -0
- nautobot/project-static/dist/css/nautobot.css +13 -0
- nautobot/project-static/dist/css/nautobot.css.map +1 -0
- nautobot/project-static/dist/js/graphql-libraries.js +3 -0
- nautobot/project-static/dist/js/graphql-libraries.js.LICENSE.txt +62 -0
- nautobot/project-static/dist/js/graphql-libraries.js.map +1 -0
- nautobot/project-static/dist/js/libraries.js +3 -0
- nautobot/project-static/dist/js/libraries.js.LICENSE.txt +65 -0
- nautobot/project-static/dist/js/libraries.js.map +1 -0
- nautobot/project-static/dist/js/materialdesignicons.js +0 -0
- nautobot/project-static/dist/js/nautobot-graphiql.js +2 -0
- nautobot/project-static/dist/js/nautobot-graphiql.js.map +1 -0
- nautobot/project-static/dist/js/nautobot.js +2 -0
- nautobot/project-static/dist/js/nautobot.js.map +1 -0
- nautobot/project-static/fonts/Montserrat-v30-Bold.woff2 +0 -0
- nautobot/project-static/fonts/Montserrat-v30-Light.woff2 +0 -0
- nautobot/project-static/fonts/Montserrat-v30-Regular.woff2 +0 -0
- nautobot/project-static/fonts/Roboto-v48-Bold.woff2 +0 -0
- nautobot/project-static/fonts/Roboto-v48-Light.woff2 +0 -0
- nautobot/project-static/fonts/Roboto-v48-Regular.woff2 +0 -0
- nautobot/project-static/img/jinja_logo.svg +21 -92
- nautobot/project-static/js/cabletrace.js +1 -1
- nautobot/project-static/js/editor.js +4 -4
- nautobot/project-static/js/forms.js +67 -717
- nautobot/project-static/js/job_result.js +2 -2
- nautobot/project-static/nautobot-icons/360-degrees.svg +3 -0
- nautobot/project-static/nautobot-icons/arrow-decision.svg +3 -0
- nautobot/project-static/nautobot-icons/arrows-expand-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/arrows-move-2-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/arrows-move-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/atom.svg +3 -0
- nautobot/project-static/nautobot-icons/battery-3.svg +3 -0
- nautobot/project-static/nautobot-icons/branch.svg +3 -0
- nautobot/project-static/nautobot-icons/briefcase-2.svg +3 -0
- nautobot/project-static/nautobot-icons/cable-data-2.svg +3 -0
- nautobot/project-static/nautobot-icons/cable-data.svg +3 -0
- nautobot/project-static/nautobot-icons/cast.svg +3 -0
- nautobot/project-static/nautobot-icons/check-circle.svg +3 -0
- nautobot/project-static/nautobot-icons/checkbox-circle.svg +3 -0
- nautobot/project-static/nautobot-icons/checkbox-rec.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud-check.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud-lightning.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud-upload.svg +3 -0
- nautobot/project-static/nautobot-icons/cloud.svg +3 -0
- nautobot/project-static/nautobot-icons/compass.svg +3 -0
- nautobot/project-static/nautobot-icons/control-panel.svg +3 -0
- nautobot/project-static/nautobot-icons/credit-card.svg +3 -0
- nautobot/project-static/nautobot-icons/device-lifecycle.svg +3 -0
- nautobot/project-static/nautobot-icons/direction.svg +3 -0
- nautobot/project-static/nautobot-icons/elements.svg +3 -0
- nautobot/project-static/nautobot-icons/extensibility.svg +3 -0
- nautobot/project-static/nautobot-icons/globe-2.svg +3 -0
- nautobot/project-static/nautobot-icons/globe.svg +3 -0
- nautobot/project-static/nautobot-icons/hammer.svg +3 -0
- nautobot/project-static/nautobot-icons/history.svg +3 -0
- nautobot/project-static/nautobot-icons/ip.svg +3 -0
- nautobot/project-static/nautobot-icons/laptop.svg +3 -0
- nautobot/project-static/nautobot-icons/lightning.svg +3 -0
- nautobot/project-static/nautobot-icons/list-unordered.svg +3 -0
- nautobot/project-static/nautobot-icons/map-view.svg +3 -0
- nautobot/project-static/nautobot-icons/organization.svg +3 -0
- nautobot/project-static/nautobot-icons/pin-2.svg +3 -0
- nautobot/project-static/nautobot-icons/pin-3.svg +3 -0
- nautobot/project-static/nautobot-icons/plug.svg +3 -0
- nautobot/project-static/nautobot-icons/refresh-cw.svg +3 -0
- nautobot/project-static/nautobot-icons/rocket-2.svg +3 -0
- nautobot/project-static/nautobot-icons/rotate-cw.svg +3 -0
- nautobot/project-static/nautobot-icons/route.svg +3 -0
- nautobot/project-static/nautobot-icons/secrets.svg +3 -0
- nautobot/project-static/nautobot-icons/security.svg +3 -0
- nautobot/project-static/nautobot-icons/server-2.svg +3 -0
- nautobot/project-static/nautobot-icons/server.svg +3 -0
- nautobot/project-static/nautobot-icons/share.svg +3 -0
- nautobot/project-static/nautobot-icons/shield-check.svg +3 -0
- nautobot/project-static/nautobot-icons/sitemap-outline.svg +3 -0
- nautobot/project-static/nautobot-icons/sliders-vert-2.svg +3 -0
- nautobot/project-static/nautobot-icons/sliders-vert.svg +3 -0
- nautobot/project-static/nautobot-icons/star-filled.svg +3 -0
- nautobot/project-static/nautobot-icons/star.svg +3 -0
- nautobot/project-static/nautobot-icons/transform.svg +3 -0
- nautobot/project-static/nautobot-icons/wifi.svg +3 -0
- nautobot/tenancy/api/serializers.py +1 -0
- nautobot/tenancy/api/views.py +2 -1
- nautobot/tenancy/{filters/__init__.py → filters.py} +2 -10
- nautobot/tenancy/navigation.py +3 -1
- nautobot/tenancy/templates/tenancy/tenant_create.html +6 -6
- nautobot/tenancy/tests/test_filters.py +0 -2
- nautobot/tenancy/views.py +2 -1
- nautobot/ui/.gitignore +137 -0
- nautobot/ui/.node-version +1 -0
- nautobot/ui/.prettierignore +3 -0
- nautobot/ui/eslint.config.js +33 -0
- nautobot/ui/package-lock.json +6594 -0
- nautobot/ui/package.json +67 -0
- nautobot/ui/prettier.config.js +9 -0
- nautobot/ui/src/js/collapse.js +69 -0
- nautobot/ui/src/js/cookie.js +31 -0
- nautobot/ui/src/js/draggable.js +101 -0
- nautobot/ui/src/js/drawer.js +106 -0
- nautobot/ui/src/js/form.js +23 -0
- nautobot/ui/src/js/history.js +51 -0
- nautobot/ui/src/js/nautobot-graphiql.js +19 -0
- nautobot/ui/src/js/nautobot.js +128 -0
- nautobot/ui/src/js/search.js +274 -0
- nautobot/ui/src/js/select2.js +318 -0
- nautobot/ui/src/js/sidenav.js +87 -0
- nautobot/ui/src/js/tabs.js +139 -0
- nautobot/ui/src/js/theme.js +104 -0
- nautobot/ui/src/js/utils.js +54 -0
- nautobot/ui/src/scss/colors.scss +58 -0
- nautobot/ui/src/scss/nautobot.scss +2471 -0
- nautobot/ui/webpack.config.js +148 -0
- nautobot/users/apps.py +3 -0
- nautobot/users/filters.py +7 -11
- nautobot/users/forms.py +10 -0
- nautobot/users/models.py +8 -0
- nautobot/users/templates/users/advanced_settings_edit.html +31 -21
- nautobot/users/templates/users/api_tokens.html +61 -51
- nautobot/users/templates/users/base.html +23 -31
- nautobot/users/templates/users/change_password.html +29 -19
- nautobot/users/templates/users/preferences.html +55 -45
- nautobot/users/templates/users/profile.html +45 -14
- nautobot/users/tests/test_api.py +4 -0
- nautobot/users/urls.py +2 -0
- nautobot/users/views.py +70 -2
- nautobot/virtualization/api/views.py +1 -1
- nautobot/virtualization/filters.py +18 -32
- nautobot/virtualization/forms.py +22 -59
- nautobot/virtualization/models.py +1 -19
- nautobot/virtualization/navigation.py +3 -1
- nautobot/virtualization/tables.py +10 -6
- nautobot/virtualization/templates/virtualization/cluster.html +13 -13
- nautobot/virtualization/templates/virtualization/cluster_edit.html +6 -6
- nautobot/virtualization/templates/virtualization/inc/virtualmachine_vminterface_filter.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_component_add.html +24 -16
- nautobot/virtualization/templates/virtualization/virtualmachine_edit.html +1 -1
- nautobot/virtualization/templates/virtualization/virtualmachine_list.html +4 -4
- nautobot/virtualization/templates/virtualization/virtualmachine_update.html +27 -25
- nautobot/virtualization/templates/virtualization/vminterface.html +5 -5
- nautobot/virtualization/templates/virtualization/vminterface_edit.html +27 -11
- nautobot/virtualization/tests/test_api.py +3 -0
- nautobot/virtualization/tests/test_models.py +20 -5
- nautobot/virtualization/tests/test_views.py +3 -5
- nautobot/virtualization/urls.py +0 -11
- nautobot/virtualization/views.py +5 -122
- nautobot/vpn/__init__.py +0 -0
- nautobot/vpn/api/serializers.py +113 -0
- nautobot/vpn/api/urls.py +19 -0
- nautobot/vpn/api/views.py +70 -0
- nautobot/vpn/apps.py +8 -0
- nautobot/vpn/choices.py +171 -0
- nautobot/vpn/factory.py +209 -0
- nautobot/vpn/filters.py +233 -0
- nautobot/vpn/forms.py +486 -0
- nautobot/vpn/homepage.py +19 -0
- nautobot/vpn/migrations/0001_initial.py +541 -0
- nautobot/vpn/migrations/0002_populate_defaults.py +199 -0
- nautobot/vpn/migrations/__init__.py +0 -0
- nautobot/vpn/models.py +527 -0
- nautobot/vpn/navigation.py +98 -0
- nautobot/vpn/tables.py +380 -0
- nautobot/vpn/templates/vpn/vpnprofile.html +2 -0
- nautobot/vpn/templates/vpn/vpnprofile_create.html +150 -0
- nautobot/vpn/tests/__init__.py +0 -0
- nautobot/vpn/tests/test_api.py +341 -0
- nautobot/vpn/tests/test_filters.py +139 -0
- nautobot/vpn/tests/test_forms.py +294 -0
- nautobot/vpn/tests/test_models.py +97 -0
- nautobot/vpn/tests/test_views.py +281 -0
- nautobot/vpn/urls.py +16 -0
- nautobot/vpn/views.py +437 -0
- nautobot/wireless/filters.py +0 -8
- nautobot/wireless/navigation.py +3 -1
- nautobot/wireless/templates/wireless/wirelessnetwork_create.html +6 -6
- nautobot/wireless/tests/integration/test_radio_profile.py +3 -7
- nautobot/wireless/tests/test_api.py +1 -1
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/METADATA +5 -4
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/RECORD +802 -707
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/entry_points.txt +1 -0
- nautobot/core/management/commands/check_job_approval_status.py +0 -47
- nautobot/core/templates/search_form.html +0 -9
- nautobot/core/templates/utilities/templatetags/filter_form_modal.html +0 -87
- nautobot/dcim/filters/mixins.py +0 -354
- nautobot/extras/templates/extras/job_approval_request.html +0 -134
- nautobot/extras/templates/extras/scheduled_jobs_approval_queue_list.html +0 -28
- nautobot/ipam/mixins.py +0 -32
- nautobot/ipam/templates/ipam/inc/prefix_header_extra_content_table.html +0 -4
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css +0 -587
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css +0 -6
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap-theme.min.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css +0 -6865
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css +0 -6
- nautobot/project-static/bootstrap-3.4.1-dist/css/bootstrap.min.css.map +0 -1
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.eot +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.svg +0 -288
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.ttf +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/fonts/glyphicons-halflings-regular.woff2 +0 -0
- nautobot/project-static/bootstrap-3.4.1-dist/js/bootstrap.js +0 -2580
- nautobot/project-static/bootstrap-3.4.1-dist/js/bootstrap.min.js +0 -6
- nautobot/project-static/bootstrap-3.4.1-dist/js/npm.js +0 -13
- nautobot/project-static/clipboard.js-2.0.9/clipboard.min.js +0 -7
- nautobot/project-static/css/base.css +0 -1040
- nautobot/project-static/css/dark.css +0 -282
- nautobot/project-static/flatpickr-4.6.9/flatpickr.min.js +0 -2
- nautobot/project-static/flatpickr-4.6.9/themes/light.min.css +0 -1
- nautobot/project-static/graphiql-1.5.16/graphiql.min.css +0 -12
- nautobot/project-static/graphiql-1.5.16/graphiql.min.js +0 -11
- nautobot/project-static/highlight.js-11.9.0/github-dark.min.css +0 -10
- nautobot/project-static/highlight.js-11.9.0/github.min.css +0 -10
- nautobot/project-static/highlight.js-11.9.0/highlight.min.js +0 -378
- nautobot/project-static/jquery/jquery-3.7.1.min.js +0 -2
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_444444_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_555555_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_777620_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_777777_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_cc0000_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/images/ui-icons_ffffff_256x240.png +0 -0
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.css +0 -7
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.min.js +0 -6
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.structure.min.css +0 -5
- nautobot/project-static/jquery-ui-1.13.2/jquery-ui.theme.min.css +0 -5
- nautobot/project-static/js/homepage_layout.js +0 -182
- nautobot/project-static/js/nav_menu.js +0 -250
- nautobot/project-static/js/theme.js +0 -133
- nautobot/project-static/materialdesignicons-7.4.47/LICENSE +0 -20
- nautobot/project-static/materialdesignicons-7.4.47/css/materialdesignicons.min.css +0 -3
- nautobot/project-static/react-16.14.0/react.production.min.js +0 -32
- nautobot/project-static/react-dom-16.14.0/react-dom.production.min.js +0 -239
- nautobot/project-static/select2-4.0.13/i18n/af.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ar.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/az.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/bg.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/bn.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/bs.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ca.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/cs.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/da.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/de.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/dsb.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/el.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/en.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/es.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/et.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/eu.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/fa.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/fi.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/fr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/gl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/he.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hi.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hsb.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hu.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/hy.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/id.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/is.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/it.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ja.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ka.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/km.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ko.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/lt.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/lv.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/mk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ms.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/nb.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ne.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/nl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/pl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ps.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/pt-BR.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/pt.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ro.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/ru.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sq.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sr-Cyrl.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/sv.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/th.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/tk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/tr.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/uk.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/vi.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/zh-CN.js +0 -3
- nautobot/project-static/select2-4.0.13/i18n/zh-TW.js +0 -3
- nautobot/project-static/select2-4.0.13/select2.min.css +0 -1
- nautobot/project-static/select2-4.0.13/select2.min.js +0 -2
- nautobot/project-static/select2-bootstrap-0.1.0-beta.10/select2-bootstrap.min.css +0 -7
- nautobot/project-static/subscriptions-transport-ws-0.9.18/client.min.js +0 -8
- nautobot/project-static/whatwg-fetch-3.6.2/fetch.umd.min.js +0 -8
- nautobot/virtualization/templates/virtualization/cluster_add_devices.html +0 -37
- /nautobot/extras/{filters/customfields.py → filter_mixins_customfields.py} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.ttf → dist/1fcc36272ea3e53d0031.ttf} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.eot → dist/2146c3c82b553977abc7.eot} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff → dist/e55a20c80650829ec5fd.woff} +0 -0
- /nautobot/project-static/{materialdesignicons-7.4.47/fonts/materialdesignicons-webfont.woff2 → dist/ec024da790d2972da002.woff2} +0 -0
- /nautobot/tenancy/{filters/mixins.py → filter_mixins.py} +0 -0
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/LICENSE.txt +0 -0
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/NOTICE +0 -0
- {nautobot-2.4.21.dist-info → nautobot-3.0.0a3.dist-info}/WHEEL +0 -0
|
@@ -6,7 +6,7 @@ from django import forms as django_forms
|
|
|
6
6
|
from django.apps import apps as django_apps
|
|
7
7
|
from django.contrib.contenttypes.models import ContentType
|
|
8
8
|
from django.http import QueryDict
|
|
9
|
-
from django.test import
|
|
9
|
+
from django.test import tag
|
|
10
10
|
from django.urls import reverse
|
|
11
11
|
from django_filters.filterset import FilterSet
|
|
12
12
|
from netaddr import IPNetwork
|
|
@@ -20,19 +20,7 @@ from nautobot.extras import filters as extras_filters, models as extras_models
|
|
|
20
20
|
from nautobot.ipam import forms as ipam_forms, models as ipam_models
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
class
|
|
24
|
-
def test_q_placeholder(self):
|
|
25
|
-
from nautobot.core.forms import SearchForm
|
|
26
|
-
|
|
27
|
-
self.assertEqual(SearchForm().fields["q"].widget.attrs["placeholder"], "Search")
|
|
28
|
-
|
|
29
|
-
# Assert the q field placeholder is overridden
|
|
30
|
-
self.assertEqual(
|
|
31
|
-
SearchForm(q_placeholder="Search Locations").fields["q"].widget.attrs["placeholder"], "Search Locations"
|
|
32
|
-
)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class ExpandIPAddress(TestCase):
|
|
23
|
+
class ExpandIPAddress(testing.TestCase):
|
|
36
24
|
"""
|
|
37
25
|
Validate the operation of expand_ipaddress_pattern().
|
|
38
26
|
"""
|
|
@@ -215,7 +203,7 @@ class ExpandIPAddress(TestCase):
|
|
|
215
203
|
sorted(forms.expand_ipaddress_pattern("1.2.3.[4,,5]/32", 4))
|
|
216
204
|
|
|
217
205
|
|
|
218
|
-
class ExpandAlphanumeric(TestCase):
|
|
206
|
+
class ExpandAlphanumeric(testing.TestCase):
|
|
219
207
|
"""
|
|
220
208
|
Validate the operation of expand_alphanumeric_pattern().
|
|
221
209
|
"""
|
|
@@ -349,7 +337,7 @@ class ExpandAlphanumeric(TestCase):
|
|
|
349
337
|
sorted(forms.expand_alphanumeric_pattern("r[a,,b]a"))
|
|
350
338
|
|
|
351
339
|
|
|
352
|
-
class AddFieldToFormClassTest(TestCase):
|
|
340
|
+
class AddFieldToFormClassTest(testing.TestCase):
|
|
353
341
|
def test_field_added(self):
|
|
354
342
|
"""
|
|
355
343
|
Test adding of a new field to an existing form.
|
|
@@ -378,7 +366,7 @@ class AddFieldToFormClassTest(TestCase):
|
|
|
378
366
|
)
|
|
379
367
|
|
|
380
368
|
|
|
381
|
-
class DynamicModelChoiceFieldTest(TestCase):
|
|
369
|
+
class DynamicModelChoiceFieldTest(testing.TestCase):
|
|
382
370
|
"""Tests for DynamicModelChoiceField."""
|
|
383
371
|
|
|
384
372
|
def setUp(self):
|
|
@@ -414,7 +402,7 @@ class DynamicModelChoiceFieldTest(TestCase):
|
|
|
414
402
|
self.assertEqual(self.field_with_to_field_name.prepare_value(address), address.address)
|
|
415
403
|
|
|
416
404
|
|
|
417
|
-
class DynamicModelMultipleChoiceFieldTest(TestCase):
|
|
405
|
+
class DynamicModelMultipleChoiceFieldTest(testing.TestCase):
|
|
418
406
|
"""Tests for DynamicModelMultipleChoiceField."""
|
|
419
407
|
|
|
420
408
|
def setUp(self):
|
|
@@ -451,7 +439,7 @@ class DynamicModelMultipleChoiceFieldTest(TestCase):
|
|
|
451
439
|
)
|
|
452
440
|
|
|
453
441
|
|
|
454
|
-
class MultiValueCharFieldTest(TestCase):
|
|
442
|
+
class MultiValueCharFieldTest(testing.TestCase):
|
|
455
443
|
def setUp(self):
|
|
456
444
|
self.filter = filters.MultiValueCharFilter()
|
|
457
445
|
self.field = forms.MultiValueCharField()
|
|
@@ -484,7 +472,7 @@ class MultiValueCharFieldTest(TestCase):
|
|
|
484
472
|
)
|
|
485
473
|
|
|
486
474
|
|
|
487
|
-
class NumericArrayFieldTest(TestCase):
|
|
475
|
+
class NumericArrayFieldTest(testing.TestCase):
|
|
488
476
|
def setUp(self):
|
|
489
477
|
super().setUp()
|
|
490
478
|
# We need to use a field with required=False so we can test empty/None inputs
|
|
@@ -514,7 +502,7 @@ class NumericArrayFieldTest(TestCase):
|
|
|
514
502
|
self.field.clean(test)
|
|
515
503
|
|
|
516
504
|
|
|
517
|
-
class AddressFieldMixinTest(TestCase):
|
|
505
|
+
class AddressFieldMixinTest(testing.TestCase):
|
|
518
506
|
"""Test cases for the AddressFieldMixin."""
|
|
519
507
|
|
|
520
508
|
def setUp(self):
|
|
@@ -544,7 +532,7 @@ class AddressFieldMixinTest(TestCase):
|
|
|
544
532
|
mock_init.assert_called_with(initial=self.initial, instance=self.ip)
|
|
545
533
|
|
|
546
534
|
|
|
547
|
-
class PrefixFieldMixinTest(TestCase):
|
|
535
|
+
class PrefixFieldMixinTest(testing.TestCase):
|
|
548
536
|
"""Test cases for the PrefixFieldMixin."""
|
|
549
537
|
|
|
550
538
|
def setUp(self):
|
|
@@ -593,7 +581,7 @@ class JSONFieldTest(testing.TestCase):
|
|
|
593
581
|
self.assertEqual('"I am UTF-8! 😀"', forms.JSONField().prepare_value("I am UTF-8! 😀"))
|
|
594
582
|
|
|
595
583
|
|
|
596
|
-
class MultiMatchModelMultipleChoiceFieldTest(TestCase):
|
|
584
|
+
class MultiMatchModelMultipleChoiceFieldTest(testing.TestCase):
|
|
597
585
|
def test_clean(self):
|
|
598
586
|
field = forms.MultiMatchModelMultipleChoiceField(
|
|
599
587
|
queryset=ipam_models.VLANGroup.objects.all(), to_field_name="name"
|
|
@@ -621,14 +609,14 @@ class MultiMatchModelMultipleChoiceFieldTest(TestCase):
|
|
|
621
609
|
field.clean(value)
|
|
622
610
|
|
|
623
611
|
|
|
624
|
-
class WidgetsTest(TestCase):
|
|
612
|
+
class WidgetsTest(testing.TestCase):
|
|
625
613
|
def test_api_select_add_query_param_with_utf8(self):
|
|
626
614
|
widget = forms.APISelect()
|
|
627
615
|
widget.add_query_param("utf8", "I am UTF-8! 😀")
|
|
628
616
|
self.assertEqual('["I am UTF-8! 😀"]', widget.attrs["data-query-param-utf8"])
|
|
629
617
|
|
|
630
618
|
|
|
631
|
-
class DynamicFilterFormTest(TestCase):
|
|
619
|
+
class DynamicFilterFormTest(testing.TestCase):
|
|
632
620
|
def test_get_filterset_parameter_form_field_all_filters(self):
|
|
633
621
|
"""
|
|
634
622
|
Test every FilterSet to validate that Plural names are correctly mapped in get_filterset_parameter_form_field.
|
|
@@ -671,6 +659,7 @@ class DynamicFilterFormTest(TestCase):
|
|
|
671
659
|
# DynamicFilterForm()
|
|
672
660
|
# self.assertEqual("'DynamicFilterForm' object requires `filterset_class` attribute", str(err.exception))
|
|
673
661
|
|
|
662
|
+
@tag("example_app")
|
|
674
663
|
def test_dynamic_filter_form(self):
|
|
675
664
|
form = forms.DynamicFilterForm(filterset=extras_filters.StatusFilterSet())
|
|
676
665
|
location_form = forms.DynamicFilterForm(filterset=dcim_filters.LocationFilterSet())
|
|
@@ -678,7 +667,6 @@ class DynamicFilterFormTest(TestCase):
|
|
|
678
667
|
|
|
679
668
|
with self.subTest("Assert get_lookup_field_choices"):
|
|
680
669
|
self.assertEqual(
|
|
681
|
-
form._get_lookup_field_choices(),
|
|
682
670
|
[
|
|
683
671
|
("color", "Color"),
|
|
684
672
|
("contacts", "Contacts (name or ID)"),
|
|
@@ -690,13 +678,13 @@ class DynamicFilterFormTest(TestCase):
|
|
|
690
678
|
("name", "Name"),
|
|
691
679
|
("teams", "Teams (name or ID)"),
|
|
692
680
|
],
|
|
681
|
+
form._get_lookup_field_choices(),
|
|
693
682
|
)
|
|
694
683
|
self.assertEqual(
|
|
695
|
-
location_form._get_lookup_field_choices(),
|
|
696
684
|
[
|
|
697
685
|
("asn", "ASN"),
|
|
698
686
|
("child_location_type", "Child location type (name or ID)"),
|
|
699
|
-
("circuit_terminations", "Circuit terminations"),
|
|
687
|
+
("circuit_terminations", "Circuit terminations (ID)"),
|
|
700
688
|
("clusters", "Clusters (name or ID)"),
|
|
701
689
|
("comments", "Comments"),
|
|
702
690
|
("contact_email", "Contact E-mail"),
|
|
@@ -729,9 +717,9 @@ class DynamicFilterFormTest(TestCase):
|
|
|
729
717
|
("parent", "Parent location (name or ID)"),
|
|
730
718
|
("physical_address", "Physical address"),
|
|
731
719
|
("power_panels", "Power panels (name or ID)"),
|
|
732
|
-
("prefixes", "Prefixes"),
|
|
733
|
-
("racks", "Rack (name or ID)"),
|
|
720
|
+
("prefixes", "Prefixes (ID)"),
|
|
734
721
|
("rack_groups", "Rack groups (name or ID)"),
|
|
722
|
+
("racks", "Racks (name or ID)"),
|
|
735
723
|
("shipping_address", "Shipping address"),
|
|
736
724
|
("status", "Status (name or ID)"),
|
|
737
725
|
("vlans", "Tagged VLANs (VID or ID)"),
|
|
@@ -743,6 +731,7 @@ class DynamicFilterFormTest(TestCase):
|
|
|
743
731
|
("time_zone", "Time zone"),
|
|
744
732
|
("vlan_groups", "VLAN groups (name or ID)"),
|
|
745
733
|
],
|
|
734
|
+
location_form._get_lookup_field_choices(),
|
|
746
735
|
)
|
|
747
736
|
|
|
748
737
|
with self.subTest(
|
|
@@ -17,8 +17,7 @@ from django.urls import reverse
|
|
|
17
17
|
import graphene.types
|
|
18
18
|
from graphene_django.registry import get_global_registry
|
|
19
19
|
from graphene_django.settings import graphene_settings
|
|
20
|
-
from graphql import
|
|
21
|
-
from graphql.error.located_error import GraphQLLocatedError
|
|
20
|
+
from graphql import execute, get_introspection_query, graphql_sync, GraphQLError, parse
|
|
22
21
|
import redis.exceptions
|
|
23
22
|
from rest_framework import status
|
|
24
23
|
|
|
@@ -39,6 +38,7 @@ from nautobot.core.graphql.schema import (
|
|
|
39
38
|
from nautobot.core.graphql.types import DateType, OptimizedNautobotObjectType
|
|
40
39
|
from nautobot.core.graphql.utils import str_to_var_name
|
|
41
40
|
from nautobot.core.testing import create_test_user, NautobotTestClient, TestCase
|
|
41
|
+
from nautobot.core.utils.cache import construct_cache_key
|
|
42
42
|
from nautobot.dcim.choices import ConsolePortTypeChoices, InterfaceModeChoices, InterfaceTypeChoices, PortTypeChoices
|
|
43
43
|
from nautobot.dcim.filters import DeviceFilterSet, LocationFilterSet
|
|
44
44
|
from nautobot.dcim.graphql.types import DeviceType as DeviceTypeGraphQL
|
|
@@ -99,7 +99,9 @@ User = get_user_model()
|
|
|
99
99
|
|
|
100
100
|
class GraphQLTestCaseBase(TestCase):
|
|
101
101
|
def setUp(self):
|
|
102
|
-
self.SCHEMA =
|
|
102
|
+
self.SCHEMA = (
|
|
103
|
+
graphene_settings.SCHEMA.graphql_schema
|
|
104
|
+
) # not a no-op; this causes the schema to be built when first called
|
|
103
105
|
|
|
104
106
|
|
|
105
107
|
class GraphQLTestCase(GraphQLTestCaseBase):
|
|
@@ -109,7 +111,7 @@ class GraphQLTestCase(GraphQLTestCaseBase):
|
|
|
109
111
|
GraphQLQuery.objects.create(name="GQL 1", query="{ query: locations {name} }")
|
|
110
112
|
GraphQLQuery.objects.create(name="GQL 2", query="query ($name: [String!]) { locations(name:$name) {name} }")
|
|
111
113
|
self.location_type = LocationType.objects.get(name="Campus")
|
|
112
|
-
location_status = Status.objects.get_for_model(Location)
|
|
114
|
+
location_status = Status.objects.get_for_model(Location)[0]
|
|
113
115
|
self.locations = (
|
|
114
116
|
Location.objects.create(name="Location-1", location_type=self.location_type, status=location_status),
|
|
115
117
|
Location.objects.create(name="Location-2", location_type=self.location_type, status=location_status),
|
|
@@ -119,9 +121,9 @@ class GraphQLTestCase(GraphQLTestCaseBase):
|
|
|
119
121
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
120
122
|
def test_execute_query(self):
|
|
121
123
|
query = "{ query: locations {name} }"
|
|
122
|
-
resp = execute_query(query, user=self.user)
|
|
123
|
-
self.
|
|
124
|
-
self.assertEqual(len(resp
|
|
124
|
+
resp = execute_query(query, user=self.user)
|
|
125
|
+
self.assertIsNone(resp.errors)
|
|
126
|
+
self.assertEqual(len(resp.data["query"]), Location.objects.all().count())
|
|
125
127
|
|
|
126
128
|
@skip("Works in isolation, fails as part of the overall test suite due to issue #446")
|
|
127
129
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
@@ -136,8 +138,8 @@ class GraphQLTestCase(GraphQLTestCaseBase):
|
|
|
136
138
|
self.locations[0]._custom_field_data = custom_field_data
|
|
137
139
|
self.locations[0].save()
|
|
138
140
|
query = "query ($name: [String!]) { locations(name:$name) {name, _custom_field_data, cf_custom_date_field} }"
|
|
139
|
-
resp = execute_query(query, user=self.user, variables={"name": "Location-1"})
|
|
140
|
-
self.assertEqual(resp
|
|
141
|
+
resp = execute_query(query, user=self.user, variables={"name": "Location-1"})
|
|
142
|
+
self.assertEqual(resp.data["locations"]["cf_custom_date_field"], custom_field_data)
|
|
141
143
|
|
|
142
144
|
@skip("Works in isolation, fails as part of the overall test suite due to issue #446")
|
|
143
145
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
@@ -152,28 +154,28 @@ class GraphQLTestCase(GraphQLTestCaseBase):
|
|
|
152
154
|
self.locations[0]._custom_field_data = custom_field_data
|
|
153
155
|
self.locations[0].save()
|
|
154
156
|
query = "query ($name: [String!]) { locations(name:$name) {name, _custom_field_data, cf_custom_json_field} }"
|
|
155
|
-
resp = execute_query(query, user=self.user, variables={"name": "Location-1"})
|
|
156
|
-
self.assertEqual(resp
|
|
157
|
+
resp = execute_query(query, user=self.user, variables={"name": "Location-1"})
|
|
158
|
+
self.assertEqual(resp.data["locations"]["cf_custom_json_field"], custom_field_data)
|
|
157
159
|
|
|
158
160
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
159
161
|
def test_execute_query_with_variable(self):
|
|
160
162
|
query = "query ($name: [String!]) { locations(name:$name) {name} }"
|
|
161
|
-
resp = execute_query(query, user=self.user, variables={"name": "Location-1"})
|
|
162
|
-
self.
|
|
163
|
-
self.assertEqual(len(resp
|
|
163
|
+
resp = execute_query(query, user=self.user, variables={"name": "Location-1"})
|
|
164
|
+
self.assertIsNone(resp.errors)
|
|
165
|
+
self.assertEqual(len(resp.data["locations"]), 1)
|
|
164
166
|
|
|
165
167
|
def test_execute_query_with_error(self):
|
|
166
168
|
query = "THIS TEST WILL ERROR"
|
|
167
169
|
with self.assertRaises(GraphQLError):
|
|
168
|
-
execute_query(query, user=self.user)
|
|
170
|
+
execute_query(query, user=self.user)
|
|
169
171
|
|
|
170
172
|
def test_execute_saved_query(self):
|
|
171
|
-
resp = execute_saved_query("GQL 1", user=self.user)
|
|
172
|
-
self.
|
|
173
|
+
resp = execute_saved_query("GQL 1", user=self.user)
|
|
174
|
+
self.assertIsNone(resp.errors)
|
|
173
175
|
|
|
174
176
|
def test_execute_saved_query_with_variable(self):
|
|
175
|
-
resp = execute_saved_query("GQL 2", user=self.user, variables={"name": "location-1"})
|
|
176
|
-
self.
|
|
177
|
+
resp = execute_saved_query("GQL 2", user=self.user, variables={"name": "location-1"})
|
|
178
|
+
self.assertIsNone(resp.errors)
|
|
177
179
|
|
|
178
180
|
def test_graphql_types_registry(self):
|
|
179
181
|
"""Ensure models with graphql feature are registered in the graphene_django registry."""
|
|
@@ -186,8 +188,8 @@ class GraphQLTestCase(GraphQLTestCaseBase):
|
|
|
186
188
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
187
189
|
def test_graphql_url_field(self):
|
|
188
190
|
"""Test the url field for all graphql types."""
|
|
189
|
-
|
|
190
|
-
graphql_fields =
|
|
191
|
+
introspection_query_result = graphql_sync(self.SCHEMA, get_introspection_query(descriptions=False))
|
|
192
|
+
graphql_fields = introspection_query_result.data["__schema"]["types"][0]["fields"]
|
|
191
193
|
for graphql_field in graphql_fields:
|
|
192
194
|
if graphql_field["type"]["kind"] == "LIST" or graphql_field["name"] == "content_type":
|
|
193
195
|
continue
|
|
@@ -208,12 +210,12 @@ class GraphQLTestCase(GraphQLTestCaseBase):
|
|
|
208
210
|
query = f'{{ query: {graphql_field["name"]}(id:"{obj.pk}") {{ url }} }}'
|
|
209
211
|
request = RequestFactory(SERVER_NAME="nautobot.example.com").post("/graphql/")
|
|
210
212
|
request.user = self.user
|
|
211
|
-
resp = execute_query(query, request=request)
|
|
213
|
+
resp = execute_query(query, request=request)
|
|
212
214
|
self.assertIsNotNone(
|
|
213
|
-
resp
|
|
215
|
+
resp.data["query"]["url"], f"No url returned in graphql for {graphql_field['name']}"
|
|
214
216
|
)
|
|
215
217
|
self.assertTrue(
|
|
216
|
-
resp
|
|
218
|
+
resp.data["query"]["url"].endswith(f"/{obj.pk}/"),
|
|
217
219
|
f"Mismatched url returned in graphql for {graphql_field['name']}",
|
|
218
220
|
)
|
|
219
221
|
|
|
@@ -228,13 +230,13 @@ class GraphQLUtilsTestCase(GraphQLTestCaseBase):
|
|
|
228
230
|
class GraphQLGenerateSchemaTypeTestCase(GraphQLTestCaseBase):
|
|
229
231
|
def test_model_w_filterset(self):
|
|
230
232
|
schema = generate_schema_type(app_name="dcim", model=Device)
|
|
231
|
-
self.
|
|
233
|
+
self.assertIn(OptimizedNautobotObjectType, schema.__bases__)
|
|
232
234
|
self.assertEqual(schema._meta.model, Device)
|
|
233
235
|
self.assertEqual(schema._meta.filterset_class, DeviceFilterSet)
|
|
234
236
|
|
|
235
237
|
def test_model_wo_filterset(self):
|
|
236
238
|
schema = generate_schema_type(app_name="wrong_app", model=ChangeLoggedModel)
|
|
237
|
-
self.
|
|
239
|
+
self.assertIn(OptimizedNautobotObjectType, schema.__bases__)
|
|
238
240
|
self.assertEqual(schema._meta.model, ChangeLoggedModel)
|
|
239
241
|
self.assertIsNone(schema._meta.filterset_class)
|
|
240
242
|
|
|
@@ -402,8 +404,12 @@ class GraphQLExtendSchemaRelationship(GraphQLTestCaseBase):
|
|
|
402
404
|
def tearDown(self):
|
|
403
405
|
"""Ensure that relationship caches are cleared to avoid leakage into other tests."""
|
|
404
406
|
with contextlib.suppress(redis.exceptions.ConnectionError):
|
|
405
|
-
cache.delete_pattern(
|
|
406
|
-
|
|
407
|
+
cache.delete_pattern(
|
|
408
|
+
f"{construct_cache_key(Relationship.objects, method_name='get_for_model_source', branch_aware=True)}(*)"
|
|
409
|
+
)
|
|
410
|
+
cache.delete_pattern(
|
|
411
|
+
f"{construct_cache_key(Relationship.objects, method_name='get_for_model_destination', branch_aware=True)}(*)"
|
|
412
|
+
)
|
|
407
413
|
|
|
408
414
|
def test_extend_relationship_default_prefix(self):
|
|
409
415
|
"""Verify that relationships are correctly added to the schema."""
|
|
@@ -472,14 +478,15 @@ class GraphQLSearchParameters(GraphQLTestCaseBase):
|
|
|
472
478
|
def test_search_parameters(self):
|
|
473
479
|
fields = LocationFilterSet().filters.keys()
|
|
474
480
|
params = generate_list_search_parameters(self.schema)
|
|
475
|
-
|
|
481
|
+
args_filters = ["description"]
|
|
476
482
|
|
|
477
483
|
for field in fields:
|
|
478
484
|
field = str_to_var_name(field)
|
|
479
|
-
if field
|
|
480
|
-
self.assertIn(field, params.keys())
|
|
481
|
-
else:
|
|
485
|
+
if field in args_filters:
|
|
482
486
|
self.assertNotIn(field, params.keys())
|
|
487
|
+
self.assertIn(field, params["args"].keys())
|
|
488
|
+
else:
|
|
489
|
+
self.assertIn(field, params.keys())
|
|
483
490
|
|
|
484
491
|
|
|
485
492
|
class GraphQLAPIPermissionTest(GraphQLTestCaseBase):
|
|
@@ -514,7 +521,7 @@ class GraphQLAPIPermissionTest(GraphQLTestCaseBase):
|
|
|
514
521
|
cls.clients[3].credentials(HTTP_AUTHORIZATION=f"Token {cls.tokens[3].key}")
|
|
515
522
|
|
|
516
523
|
cls.location_type = LocationType.objects.get(name="Campus")
|
|
517
|
-
location_status = Status.objects.get_for_model(Location)
|
|
524
|
+
location_status = Status.objects.get_for_model(Location)[0]
|
|
518
525
|
cls.locations = (
|
|
519
526
|
Location.objects.create(name="Location 1", location_type=cls.location_type, status=location_status),
|
|
520
527
|
Location.objects.create(name="Location 2", location_type=cls.location_type, status=location_status),
|
|
@@ -544,7 +551,7 @@ class GraphQLAPIPermissionTest(GraphQLTestCaseBase):
|
|
|
544
551
|
location_obj_permission.groups.add(cls.groups[i])
|
|
545
552
|
location_obj_permission.users.add(cls.users[i])
|
|
546
553
|
|
|
547
|
-
rack_status = Status.objects.get_for_model(Rack)
|
|
554
|
+
rack_status = Status.objects.get_for_model(Rack)[0]
|
|
548
555
|
cls.rack_grp1 = (
|
|
549
556
|
Rack.objects.create(name="Rack 1-1", location=cls.locations[0], status=rack_status),
|
|
550
557
|
Rack.objects.create(name="Rack 1-2", location=cls.locations[0], status=rack_status),
|
|
@@ -603,14 +610,14 @@ class GraphQLAPIPermissionTest(GraphQLTestCaseBase):
|
|
|
603
610
|
"""Validate that users can query based on their permissions."""
|
|
604
611
|
# First user
|
|
605
612
|
response = self.clients[0].post(self.api_url, {"query": self.get_racks_query}, format="json")
|
|
606
|
-
self.
|
|
613
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
607
614
|
self.assertIsInstance(response.data["data"]["racks"], list)
|
|
608
615
|
names = [item["name"] for item in response.data["data"]["racks"]]
|
|
609
616
|
self.assertEqual(names, ["Rack 1-1", "Rack 1-2"])
|
|
610
617
|
|
|
611
618
|
# Second user
|
|
612
619
|
response = self.clients[1].post(self.api_url, {"query": self.get_racks_query}, format="json")
|
|
613
|
-
self.
|
|
620
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
614
621
|
self.assertIsInstance(response.data["data"]["racks"], list)
|
|
615
622
|
names = [item["name"] for item in response.data["data"]["racks"]]
|
|
616
623
|
self.assertEqual(names, ["Rack 2-1", "Rack 2-2"])
|
|
@@ -618,7 +625,7 @@ class GraphQLAPIPermissionTest(GraphQLTestCaseBase):
|
|
|
618
625
|
def test_graphql_api_token_super_user(self):
|
|
619
626
|
"""Validate a superuser can query everything."""
|
|
620
627
|
response = self.clients[2].post(self.api_url, {"query": self.get_racks_query}, format="json")
|
|
621
|
-
self.
|
|
628
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
622
629
|
self.assertIsInstance(response.data["data"]["racks"], list)
|
|
623
630
|
names = [item["name"] for item in response.data["data"]["racks"]]
|
|
624
631
|
self.assertEqual(names, ["Rack 1-1", "Rack 1-2", "Rack 2-1", "Rack 2-2"])
|
|
@@ -626,7 +633,7 @@ class GraphQLAPIPermissionTest(GraphQLTestCaseBase):
|
|
|
626
633
|
def test_graphql_api_token_no_group(self):
|
|
627
634
|
"""Validate users with no permission are not able to query anything by default."""
|
|
628
635
|
response = self.clients[3].post(self.api_url, {"query": self.get_racks_query}, format="json")
|
|
629
|
-
self.
|
|
636
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
630
637
|
self.assertIsInstance(response.data["data"]["racks"], list)
|
|
631
638
|
names = [item["name"] for item in response.data["data"]["racks"]]
|
|
632
639
|
self.assertEqual(names, [])
|
|
@@ -635,7 +642,7 @@ class GraphQLAPIPermissionTest(GraphQLTestCaseBase):
|
|
|
635
642
|
def test_graphql_api_token_no_group_exempt(self):
|
|
636
643
|
"""Validate users with no permission are able to query based on the exempt permissions."""
|
|
637
644
|
response = self.clients[3].post(self.api_url, {"query": self.get_racks_query}, format="json")
|
|
638
|
-
self.
|
|
645
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
639
646
|
self.assertIsInstance(response.data["data"]["racks"], list)
|
|
640
647
|
names = [item["name"] for item in response.data["data"]["racks"]]
|
|
641
648
|
self.assertEqual(names, ["Rack 1-1", "Rack 1-2", "Rack 2-1", "Rack 2-2"])
|
|
@@ -643,18 +650,18 @@ class GraphQLAPIPermissionTest(GraphQLTestCaseBase):
|
|
|
643
650
|
def test_graphql_api_no_token(self):
|
|
644
651
|
"""Validate unauthenticated users are not able to query anything."""
|
|
645
652
|
response = self.client.post(self.api_url, {"query": self.get_racks_query}, format="json")
|
|
646
|
-
self.
|
|
653
|
+
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
|
|
647
654
|
|
|
648
655
|
def test_graphql_api_wrong_token(self):
|
|
649
656
|
"""Validate a wrong token return 403."""
|
|
650
657
|
self.client.credentials(HTTP_AUTHORIZATION="Token zzzzzzzzzzabcdef0123456789abcdef01234567")
|
|
651
658
|
response = self.client.post(self.api_url, {"query": self.get_racks_query}, format="json")
|
|
652
|
-
self.
|
|
659
|
+
self.assertHttpStatus(response, status.HTTP_403_FORBIDDEN)
|
|
653
660
|
|
|
654
661
|
def test_graphql_query_params(self):
|
|
655
662
|
"""Validate query parameters are available for a model."""
|
|
656
663
|
response = self.clients[2].post(self.api_url, {"query": self.get_racks_params_query}, format="json")
|
|
657
|
-
self.
|
|
664
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
658
665
|
self.assertIsInstance(response.data["data"]["racks"], list)
|
|
659
666
|
names = [item["name"] for item in response.data["data"]["racks"]]
|
|
660
667
|
self.assertEqual(names, ["Rack 1-1", "Rack 1-2"])
|
|
@@ -663,14 +670,14 @@ class GraphQLAPIPermissionTest(GraphQLTestCaseBase):
|
|
|
663
670
|
"""Validate graphql variables are working as expected."""
|
|
664
671
|
payload = {"query": self.get_racks_var_query, "variables": {"location": "Location 1"}}
|
|
665
672
|
response = self.clients[2].post(self.api_url, payload, format="json")
|
|
666
|
-
self.
|
|
673
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
667
674
|
self.assertIsInstance(response.data["data"]["racks"], list)
|
|
668
675
|
names = [item["name"] for item in response.data["data"]["racks"]]
|
|
669
676
|
self.assertEqual(names, ["Rack 1-1", "Rack 1-2"])
|
|
670
677
|
|
|
671
678
|
payload = {"query": self.get_racks_var_query, "variables": {"location": "Location 2"}}
|
|
672
679
|
response = self.clients[2].post(self.api_url, payload, format="json")
|
|
673
|
-
self.
|
|
680
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
674
681
|
self.assertIsInstance(response.data["data"]["racks"], list)
|
|
675
682
|
names = [item["name"] for item in response.data["data"]["racks"]]
|
|
676
683
|
self.assertEqual(names, ["Rack 2-1", "Rack 2-2"])
|
|
@@ -679,14 +686,14 @@ class GraphQLAPIPermissionTest(GraphQLTestCaseBase):
|
|
|
679
686
|
"""Validate graphql query for a single object as opposed to a set of objects also works."""
|
|
680
687
|
payload = {"query": self.get_rack_query, "variables": {"id": Rack.objects.first().pk}}
|
|
681
688
|
response = self.clients[2].post(self.api_url, payload, format="json")
|
|
682
|
-
self.
|
|
689
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
683
690
|
self.assertIsInstance(response.data["data"]["rack"], dict)
|
|
684
691
|
self.assertEqual(response.data["data"]["rack"]["name"], Rack.objects.first().name)
|
|
685
692
|
|
|
686
693
|
def test_graphql_query_multi_level(self):
|
|
687
694
|
"""Validate request with multiple levels return the proper information, following the permissions."""
|
|
688
695
|
response = self.clients[0].post(self.api_url, {"query": self.get_locations_racks_query}, format="json")
|
|
689
|
-
self.
|
|
696
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
690
697
|
self.assertIsInstance(response.data["data"]["locations"], list)
|
|
691
698
|
self.assertGreater(len(response.data["data"]["locations"]), 0)
|
|
692
699
|
location_names = [item["name"] for item in response.data["data"]["locations"]]
|
|
@@ -702,7 +709,7 @@ class GraphQLAPIPermissionTest(GraphQLTestCaseBase):
|
|
|
702
709
|
data=self.get_locations_racks_query,
|
|
703
710
|
content_type="application/graphql",
|
|
704
711
|
)
|
|
705
|
-
self.
|
|
712
|
+
self.assertHttpStatus(response, status.HTTP_200_OK)
|
|
706
713
|
self.assertIsInstance(response.data["data"]["locations"], list)
|
|
707
714
|
location_names = [item["name"] for item in response.data["data"]["locations"]]
|
|
708
715
|
location_list = list(Location.objects.values_list("name", flat=True))
|
|
@@ -1114,14 +1121,12 @@ class GraphQLQueryTest(GraphQLTestCaseBase):
|
|
|
1114
1121
|
)
|
|
1115
1122
|
cls.rm2ms_assoc_3.validated_save()
|
|
1116
1123
|
|
|
1117
|
-
cls.backend = get_default_backend()
|
|
1118
|
-
|
|
1119
1124
|
def execute_query(self, query, variables=None):
|
|
1120
|
-
document =
|
|
1125
|
+
document = parse(query)
|
|
1121
1126
|
if variables:
|
|
1122
|
-
return
|
|
1127
|
+
return execute(schema=self.SCHEMA, document=document, context_value=self.request, variable_values=variables)
|
|
1123
1128
|
else:
|
|
1124
|
-
return
|
|
1129
|
+
return execute(schema=self.SCHEMA, document=document, context_value=self.request)
|
|
1125
1130
|
|
|
1126
1131
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
1127
1132
|
def test_query_circuit_terminations_cable_peer(self):
|
|
@@ -1522,7 +1527,7 @@ query {
|
|
|
1522
1527
|
|
|
1523
1528
|
response = self.execute_query(query)
|
|
1524
1529
|
self.assertEqual(len(response.errors), 1)
|
|
1525
|
-
self.assertIsInstance(response.errors[0],
|
|
1530
|
+
self.assertIsInstance(response.errors[0], GraphQLError)
|
|
1526
1531
|
|
|
1527
1532
|
@override_settings(EXEMPT_VIEW_PERMISSIONS=["*"])
|
|
1528
1533
|
def test_query_locations_filter(self):
|
|
@@ -1797,6 +1802,11 @@ query {
|
|
|
1797
1802
|
f'device: "{self.device1.name}"',
|
|
1798
1803
|
Q(device=self.device1),
|
|
1799
1804
|
),
|
|
1805
|
+
(
|
|
1806
|
+
f'type: "{PortTypeChoices.TYPE_8P8C}"',
|
|
1807
|
+
Q(type=PortTypeChoices.TYPE_8P8C),
|
|
1808
|
+
),
|
|
1809
|
+
# for backward compatibility:
|
|
1800
1810
|
(
|
|
1801
1811
|
f'_type: "{PortTypeChoices.TYPE_8P8C}"',
|
|
1802
1812
|
Q(type=PortTypeChoices.TYPE_8P8C),
|
|
@@ -1830,7 +1840,7 @@ query {
|
|
|
1830
1840
|
Q(device=self.device1),
|
|
1831
1841
|
),
|
|
1832
1842
|
(
|
|
1833
|
-
f'
|
|
1843
|
+
f'type: "{PortTypeChoices.TYPE_8P8C}"',
|
|
1834
1844
|
Q(type=PortTypeChoices.TYPE_8P8C),
|
|
1835
1845
|
),
|
|
1836
1846
|
)
|
|
@@ -2436,7 +2446,7 @@ query {
|
|
|
2436
2446
|
self.device1.primary_ip4 = self.ipaddr1
|
|
2437
2447
|
self.device1.save()
|
|
2438
2448
|
result = self.execute_query(query, variables={"device_id": str(self.device1.id)})
|
|
2439
|
-
self.
|
|
2449
|
+
self.assertIsNone(result.errors)
|
|
2440
2450
|
expected_interfaces_first = {"ip_addresses": [{"primary_ip4_for": [{"id": str(self.device1.id)}]}]}
|
|
2441
2451
|
self.assertEqual(result.data["device"]["interfaces"][0], expected_interfaces_first)
|
|
2442
2452
|
|
|
@@ -2450,12 +2460,12 @@ query {
|
|
|
2450
2460
|
query = """query { tenant_groups { name tenants { name } } }"""
|
|
2451
2461
|
# Prewarm caches of Relationships and such to reduce variation in the number of queries below
|
|
2452
2462
|
result = self.execute_query(query)
|
|
2453
|
-
self.
|
|
2463
|
+
self.assertIsNone(result.errors)
|
|
2454
2464
|
# Run it again and assert that the optimized query is run.
|
|
2455
2465
|
# Before the fix for #7651 this would result in N+1 queries where N is the number of tenant-groups!
|
|
2456
2466
|
with self.assertNumQueries(2):
|
|
2457
2467
|
result = self.execute_query(query)
|
|
2458
|
-
self.
|
|
2468
|
+
self.assertIsNone(result.errors)
|
|
2459
2469
|
|
|
2460
2470
|
|
|
2461
2471
|
class GraphQLTypeTestCase(UnitTestTestCase):
|
|
@@ -130,7 +130,7 @@ class NaturalKeyTestCase(BaseModelTest):
|
|
|
130
130
|
self.FakeBaseModel._content_type_cached
|
|
131
131
|
self.assertEqual(mock__content_type.call_count, 1)
|
|
132
132
|
|
|
133
|
-
time.sleep(
|
|
133
|
+
time.sleep(3) # Let the cache expire
|
|
134
134
|
|
|
135
135
|
self.FakeBaseModel._content_type_cached
|
|
136
136
|
self.assertEqual(mock__content_type.call_count, 2)
|
|
@@ -13,6 +13,7 @@ from unittest import mock, TestCase
|
|
|
13
13
|
|
|
14
14
|
from django import __version__ as django_version
|
|
15
15
|
from django.conf import settings
|
|
16
|
+
from django.test import tag
|
|
16
17
|
|
|
17
18
|
from nautobot import __version__ as nautobot_version
|
|
18
19
|
|
|
@@ -101,6 +102,7 @@ class NautobotServerTestCase(TestCase):
|
|
|
101
102
|
|
|
102
103
|
self.assertNotEqual(secret_key_1, secret_key_2)
|
|
103
104
|
|
|
105
|
+
@tag("example_app")
|
|
104
106
|
def test_settings_processing(self):
|
|
105
107
|
result = subprocess.run(
|
|
106
108
|
["nautobot-server", "--config", settings.SETTINGS_PATH, "print_settings"],
|