netbox-data-import 2.2.0__tar.gz → 2.3.0__tar.gz
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.
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/PKG-INFO +1 -1
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/__init__.py +1 -1
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/api/serializers.py +60 -12
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/api/urls.py +4 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/api/views.py +107 -50
- netbox_data_import-2.3.0/netbox_data_import/graphql/schema.py +30 -0
- netbox_data_import-2.3.0/netbox_data_import/graphql/types.py +42 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/models.py +2 -0
- netbox_data_import-2.3.0/netbox_data_import/profile_yaml.py +350 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/helpers.py +17 -5
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_atomic_exits.py +3 -0
- netbox_data_import-2.3.0/netbox_data_import/tests/test_configuration_surfaces.py +933 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_inference_secret_containment.py +1 -1
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_legacy_profile_yaml.py +1 -1
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_standard_views.py +1 -1
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_target_field_catalog.py +4 -4
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_views.py +54 -55
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_views_coverage2.py +1 -13
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/views.py +5 -364
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import.egg-info/PKG-INFO +1 -1
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import.egg-info/SOURCES.txt +2 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/pyproject.toml +1 -1
- netbox_data_import-2.2.0/netbox_data_import/graphql/schema.py +0 -22
- netbox_data_import-2.2.0/netbox_data_import/graphql/types.py +0 -23
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/LICENSE +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/README.md +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/adapter_config.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/adapter_forms.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/adapters.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/api/__init__.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/cable_target.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/catalog.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/contact_resolution.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/device_field_review.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/device_identity.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/field_keys.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/filters.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/flat_workbook.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/forms.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/graphql/__init__.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/graphql/filters.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/import_engine.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/inference_adapter.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/inference_backend.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/inference_connection_test.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/inference_credentials.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/inference_settings.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/inference_transport.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/inference_trust.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/ip_assignment.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/jobs.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0001_initial.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0002_importjob.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0003_remove_classrolemapping_unique_class_role_mapping_per_profile_and_more.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0004_features.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0005_deviceexistingmatch.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0006_deviceexistingmatch_asset_tag.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0007_manufacturermapping.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0008_unique_constraints.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0009_add_rack_type_to_classrolemapping.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0010_alter_manufacturermapping_id.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0011_importprofile_capture_extra_data.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0012_classrolemapping_rack_type_related_name.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0013_remove_columnmapping_unique_target.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0014_alter_columnmapping_target_field_and_more.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0015_cleanup_duplicate_device_matches.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0016_deviceexistingmatch_ndi_devicematch_profile_device.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0017_native_contact_sync.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0018_ignoredfielddifference.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0019_deviceimportsource.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0020_migrate_import_source_custom_field.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0021_importprofile_adapter_config.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0022_migrate_profile_adapter_config.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0023_drop_moved_profile_columns.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0024_rename_importjob_importexecution.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0025_remove_importexecution_dry_run_and_more.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0026_alter_sourcedocument_profile.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0027_cableclassmapping_terminationresolution.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0028_cableimportsource.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0029_alter_cableimportsource_from_text_and_more.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0030_remove_device_type_creation_config.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0031_inferencebackend.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0032_inference_timeout_floor.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/0033_resolutionproposal.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/migrations/__init__.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/navigation.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/netbox_reader.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/object_permissions.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/plan.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/preview_row_actions.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/proposal_decisions.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/proposal_jobs.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/proposal_presentation.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/proposal_response.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/proposal_tasks.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/resolution_proposals.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/review_workspace.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/signals.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/source_resolution.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/static/netbox_data_import/js/conflict_modal.js +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/static/netbox_data_import/js/contact_candidate_modal.js +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/static/netbox_data_import/js/preview_row_actions.js +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/static/netbox_data_import/js/preview_row_controls.js +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/static/netbox_data_import/js/split_name_modal.js +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/static/netbox_data_import/js/sync_row_modal.js +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/static/netbox_data_import/js/trace_proposals.js +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/static/netbox_data_import/js/trace_termination_picker.js +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tables.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/target_modules.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/target_runtime.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/template_content.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/_import_progress.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/_proposal_action.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/_proposal_card.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/analysis.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/bulk_yaml_import.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/cableclassmapping_edit.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/classrolemapping_edit.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/columnmapping_edit.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/columntransformrule_edit.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/device_import_data.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/devicetypemapping_edit.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/import_preview.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/import_profile_yaml.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/import_progress.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/import_results.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/import_setup.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/importexecution_list.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/importprofile.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/importprofile_list.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/inferencebackend.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/source_resolution_list.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/templates/netbox_data_import/trace_workspace.html +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/termination_proposal.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/__init__.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/conftest.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/fixtures/combined_name.xlsx +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/fixtures/sample_cans.xlsx +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/fixtures/trace_copper.xlsx +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/fixtures/trace_fiber.xlsx +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/inference_http.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/isolated_settings.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/mixins.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/mock_discipline.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/netbox_configuration.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/parallel.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_adapter_config.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_adapter_config_migration.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_api.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_cable_module.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_ci_configuration.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_comment_style.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_contact_resolution_ajax.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_contact_role_suggestion.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_contact_sync.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_cutover_edge_contracts.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_devcontainer_e2e.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_device_module.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_digest_indexed_models.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_execution_audit.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_graphql_filters.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_import_cutover.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_import_engine.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_import_execution.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_import_plan.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_import_source_migration.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_inference_adapter.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_inference_backend.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_inference_connection_test.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_inference_credentials.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_inference_settings.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_inference_trust.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_installation_docs.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_ip_target.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_migrations.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_misc_coverage.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_mock_discipline.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_model_base_conventions.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_models.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_module_boundaries.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_navigation.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_netbox_reader.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_object_permission_writer.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_parallel_test_setup.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_placement_reference_locks.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_preview_assets.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_preview_state_ownership.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_pristine_preview_rows.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_profile_policy_crud_scoping.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_proposal_decisions.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_proposal_jobs.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_proposal_response.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_proposal_tasks.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_proposal_workspace.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_quick_action_input_bounds.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_quick_action_object_permissions.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_quick_action_profile_id.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_rack_module.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_release_metadata.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_remove_device_type_creation_config_migration.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_resolution_proposal_api.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_resolution_proposals.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_review_actions.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_review_resolutions.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_review_workspace.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_serial_display.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_source_adapter.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_target_module_edge_contracts.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_trace_decisions.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_trace_workbook.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_trace_workspace.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/tests/test_views_extra.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/trace_schema.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/trace_workbook.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/transform_regex.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/urls.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/utils.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import/values.py +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import.egg-info/dependency_links.txt +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import.egg-info/requires.txt +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/netbox_data_import.egg-info/top_level.txt +0 -0
- {netbox_data_import-2.2.0 → netbox_data_import-2.3.0}/setup.cfg +0 -0
|
@@ -10,8 +10,11 @@ from rest_framework import serializers
|
|
|
10
10
|
|
|
11
11
|
from ..adapters import DEFAULT_ADAPTER_KEY, get_adapter, selectable_adapter_choices
|
|
12
12
|
from ..catalog import CATALOG
|
|
13
|
+
from ..forms import InferenceBackendForm
|
|
13
14
|
from ..models import (
|
|
14
15
|
ImportProfile,
|
|
16
|
+
CableClassMapping,
|
|
17
|
+
CableImportSource,
|
|
15
18
|
ColumnMapping,
|
|
16
19
|
ClassRoleMapping,
|
|
17
20
|
DeviceTypeMapping,
|
|
@@ -63,6 +66,17 @@ class PolicySectionSerializer(PolicySectionApplicabilityMixin, ValidatedModelSer
|
|
|
63
66
|
def validate_policy_row(self, attrs):
|
|
64
67
|
"""Check the fields this model resolves through the catalog. Subclasses override."""
|
|
65
68
|
|
|
69
|
+
def model_cleaned_values(self):
|
|
70
|
+
"""Return request values after applying the model's canonical normalization."""
|
|
71
|
+
values = dict(self.validated_data)
|
|
72
|
+
if self.instance is None:
|
|
73
|
+
instance = self.Meta.model(**values)
|
|
74
|
+
instance.full_clean(validate_unique=False)
|
|
75
|
+
else:
|
|
76
|
+
# ValidatedModelSerializer.validate() applies request values and full_clean() to this instance.
|
|
77
|
+
instance = self.instance
|
|
78
|
+
return {name: getattr(instance, name) for name in values}
|
|
79
|
+
|
|
66
80
|
|
|
67
81
|
def _validate_target_keys(instance, attrs, names, *, allow_candidates=True, required=False):
|
|
68
82
|
"""Reject a target key the profile's Source Adapter cannot supply."""
|
|
@@ -145,6 +159,24 @@ class ColumnMappingSerializer(PolicySectionSerializer):
|
|
|
145
159
|
_validate_target_keys(self.instance, attrs, ("target_field",), required=True)
|
|
146
160
|
|
|
147
161
|
|
|
162
|
+
class CableClassMappingSerializer(PolicySectionSerializer):
|
|
163
|
+
"""Serializer for CableClassMapping policy decisions."""
|
|
164
|
+
|
|
165
|
+
class Meta:
|
|
166
|
+
model = CableClassMapping
|
|
167
|
+
fields = [
|
|
168
|
+
"id",
|
|
169
|
+
"url",
|
|
170
|
+
"display",
|
|
171
|
+
"profile",
|
|
172
|
+
"cable_class",
|
|
173
|
+
"cable_type_resolved",
|
|
174
|
+
"cable_type",
|
|
175
|
+
"cable_profile_resolved",
|
|
176
|
+
"cable_profile",
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
|
|
148
180
|
class _RackTypeSlugField(serializers.SlugRelatedField):
|
|
149
181
|
"""SlugRelatedField for RackType that defers the queryset import."""
|
|
150
182
|
|
|
@@ -282,6 +314,30 @@ class ImportExecutionSerializer(serializers.ModelSerializer):
|
|
|
282
314
|
read_only_fields = fields
|
|
283
315
|
|
|
284
316
|
|
|
317
|
+
class CableImportSourceSerializer(serializers.ModelSerializer):
|
|
318
|
+
"""Read-only serializer for one Cable provenance record."""
|
|
319
|
+
|
|
320
|
+
class Meta:
|
|
321
|
+
model = CableImportSource
|
|
322
|
+
fields = [
|
|
323
|
+
"id",
|
|
324
|
+
"cable",
|
|
325
|
+
"profile",
|
|
326
|
+
"trace_identity",
|
|
327
|
+
"segment_index",
|
|
328
|
+
"from_text",
|
|
329
|
+
"to_text",
|
|
330
|
+
"direction",
|
|
331
|
+
"workbook_fingerprint",
|
|
332
|
+
"sheet",
|
|
333
|
+
"block_ordinal",
|
|
334
|
+
"row_start",
|
|
335
|
+
"row_end",
|
|
336
|
+
"export_timestamp",
|
|
337
|
+
]
|
|
338
|
+
read_only_fields = fields
|
|
339
|
+
|
|
340
|
+
|
|
285
341
|
class ResolutionProposalSerializer(serializers.ModelSerializer):
|
|
286
342
|
"""Read-only serializer for the Resolution Proposal audit record."""
|
|
287
343
|
|
|
@@ -328,28 +384,20 @@ class ResolutionProposalHistorySerializer(serializers.ModelSerializer):
|
|
|
328
384
|
|
|
329
385
|
|
|
330
386
|
class InferenceBackendSerializer(NetBoxModelSerializer):
|
|
331
|
-
"""Serialize one Inference Backend row
|
|
387
|
+
"""Serialize one Inference Backend row with a write-only credential reference.
|
|
332
388
|
|
|
333
389
|
NetBox resolves this class by model name to freeze a delete event payload.
|
|
334
390
|
"""
|
|
335
391
|
|
|
392
|
+
credential_reference = serializers.JSONField(write_only=True)
|
|
393
|
+
|
|
336
394
|
class Meta:
|
|
337
395
|
model = InferenceBackend
|
|
338
396
|
fields = [
|
|
339
397
|
"id",
|
|
340
398
|
"url",
|
|
341
399
|
"display",
|
|
342
|
-
|
|
343
|
-
"display_name",
|
|
344
|
-
"adapter_type",
|
|
345
|
-
"api_root",
|
|
346
|
-
"model",
|
|
347
|
-
"authentication",
|
|
348
|
-
"response_mode",
|
|
349
|
-
"connect_timeout",
|
|
350
|
-
"read_timeout",
|
|
351
|
-
"enabled",
|
|
352
|
-
"tags",
|
|
400
|
+
*InferenceBackendForm.Meta.fields,
|
|
353
401
|
"custom_fields",
|
|
354
402
|
"created",
|
|
355
403
|
"last_updated",
|
|
@@ -7,6 +7,8 @@ from netbox.api.routers import NetBoxRouter
|
|
|
7
7
|
|
|
8
8
|
from .views import (
|
|
9
9
|
ImportProfileViewSet,
|
|
10
|
+
CableClassMappingViewSet,
|
|
11
|
+
CableImportSourceViewSet,
|
|
10
12
|
ColumnMappingViewSet,
|
|
11
13
|
ClassRoleMappingViewSet,
|
|
12
14
|
DeviceTypeMappingViewSet,
|
|
@@ -21,6 +23,8 @@ from .views import (
|
|
|
21
23
|
|
|
22
24
|
router = NetBoxRouter()
|
|
23
25
|
router.register("profiles", ImportProfileViewSet)
|
|
26
|
+
router.register("cable-class-mappings", CableClassMappingViewSet)
|
|
27
|
+
router.register("cable-provenance", CableImportSourceViewSet)
|
|
24
28
|
router.register("column-mappings", ColumnMappingViewSet)
|
|
25
29
|
router.register("class-role-mappings", ClassRoleMappingViewSet)
|
|
26
30
|
router.register("device-type-mappings", DeviceTypeMappingViewSet)
|
|
@@ -3,16 +3,17 @@
|
|
|
3
3
|
"""DRF viewsets for the data-import plugin API."""
|
|
4
4
|
|
|
5
5
|
from django.http import Http404
|
|
6
|
-
from netbox.api.viewsets import NetBoxModelViewSet
|
|
6
|
+
from netbox.api.viewsets import NetBoxModelViewSet
|
|
7
7
|
from rest_framework import mixins, permissions, viewsets
|
|
8
|
-
from rest_framework.exceptions import ValidationError
|
|
8
|
+
from rest_framework.exceptions import PermissionDenied, ValidationError
|
|
9
9
|
from rest_framework.permissions import DjangoModelPermissions
|
|
10
10
|
|
|
11
11
|
from ..field_keys import SELECT_TERMINATION_TASK, parse_termination_field_key
|
|
12
12
|
from ..models import (
|
|
13
13
|
locked_profile_policy,
|
|
14
|
-
locked_resolution_policy,
|
|
15
14
|
ImportProfile,
|
|
15
|
+
CableClassMapping,
|
|
16
|
+
CableImportSource,
|
|
16
17
|
ColumnMapping,
|
|
17
18
|
ClassRoleMapping,
|
|
18
19
|
DeviceTypeMapping,
|
|
@@ -23,8 +24,15 @@ from ..models import (
|
|
|
23
24
|
InferenceBackend,
|
|
24
25
|
ResolutionProposal,
|
|
25
26
|
)
|
|
27
|
+
from ..object_permissions import (
|
|
28
|
+
ObjectPermissionDenied,
|
|
29
|
+
delete_permission_scoped_objects,
|
|
30
|
+
save_permission_scoped_object,
|
|
31
|
+
)
|
|
26
32
|
from .serializers import (
|
|
27
33
|
ImportProfileSerializer,
|
|
34
|
+
CableClassMappingSerializer,
|
|
35
|
+
CableImportSourceSerializer,
|
|
28
36
|
ColumnMappingSerializer,
|
|
29
37
|
ClassRoleMappingSerializer,
|
|
30
38
|
DeviceTypeMappingSerializer,
|
|
@@ -84,6 +92,72 @@ class _PluginModelViewSet(_ProfileScopedQuerySetMixin, viewsets.ModelViewSet):
|
|
|
84
92
|
|
|
85
93
|
permission_classes = [permissions.IsAuthenticated, DjangoModelPermissionsWithView]
|
|
86
94
|
|
|
95
|
+
def perform_create(self, serializer):
|
|
96
|
+
"""Create one policy row inside its profile and object-permission scope."""
|
|
97
|
+
model = serializer.Meta.model
|
|
98
|
+
profile_id = serializer.validated_data["profile"].pk
|
|
99
|
+
try:
|
|
100
|
+
with locked_profile_policy(profile_id):
|
|
101
|
+
serializer.run_validation(serializer.initial_data)
|
|
102
|
+
values = serializer.model_cleaned_values()
|
|
103
|
+
profile = values.pop("profile")
|
|
104
|
+
result = save_permission_scoped_object(
|
|
105
|
+
self.request.user,
|
|
106
|
+
model,
|
|
107
|
+
{"pk": None, "profile": profile},
|
|
108
|
+
values,
|
|
109
|
+
on_existing="reject",
|
|
110
|
+
)
|
|
111
|
+
except ImportProfile.DoesNotExist:
|
|
112
|
+
raise Http404 from None
|
|
113
|
+
except ObjectPermissionDenied as exc:
|
|
114
|
+
raise PermissionDenied from exc
|
|
115
|
+
serializer.instance = result.instance
|
|
116
|
+
|
|
117
|
+
def perform_update(self, serializer):
|
|
118
|
+
"""Update one policy row under every affected profile and object scope."""
|
|
119
|
+
model = serializer.Meta.model
|
|
120
|
+
row_pk = serializer.instance.pk
|
|
121
|
+
stored_profile_id = model.objects.filter(pk=row_pk).values_list("profile_id", flat=True).first()
|
|
122
|
+
if stored_profile_id is None:
|
|
123
|
+
raise Http404
|
|
124
|
+
requested_profile = serializer.validated_data.get("profile")
|
|
125
|
+
requested_profile_id = requested_profile.pk if requested_profile is not None else stored_profile_id
|
|
126
|
+
try:
|
|
127
|
+
with locked_profile_policy(stored_profile_id, requested_profile_id):
|
|
128
|
+
serializer.instance = model.objects.get(pk=row_pk, profile_id=stored_profile_id)
|
|
129
|
+
serializer.run_validation(serializer.initial_data)
|
|
130
|
+
result = save_permission_scoped_object(
|
|
131
|
+
self.request.user,
|
|
132
|
+
model,
|
|
133
|
+
{"pk": row_pk, "profile_id": stored_profile_id},
|
|
134
|
+
serializer.model_cleaned_values(),
|
|
135
|
+
)
|
|
136
|
+
except (model.DoesNotExist, ImportProfile.DoesNotExist):
|
|
137
|
+
raise Http404 from None
|
|
138
|
+
except ObjectPermissionDenied as exc:
|
|
139
|
+
raise PermissionDenied from exc
|
|
140
|
+
serializer.instance = result.instance
|
|
141
|
+
|
|
142
|
+
def perform_destroy(self, instance):
|
|
143
|
+
"""Delete one policy row under its current profile and object scope."""
|
|
144
|
+
model = type(instance)
|
|
145
|
+
profile_id = model.objects.filter(pk=instance.pk).values_list("profile_id", flat=True).first()
|
|
146
|
+
if profile_id is None:
|
|
147
|
+
raise Http404
|
|
148
|
+
try:
|
|
149
|
+
with locked_profile_policy(profile_id):
|
|
150
|
+
deleted = delete_permission_scoped_objects(
|
|
151
|
+
self.request.user,
|
|
152
|
+
model.objects.filter(pk=instance.pk, profile_id=profile_id),
|
|
153
|
+
)
|
|
154
|
+
if deleted != 1:
|
|
155
|
+
raise Http404
|
|
156
|
+
except ImportProfile.DoesNotExist:
|
|
157
|
+
raise Http404 from None
|
|
158
|
+
except ObjectPermissionDenied as exc:
|
|
159
|
+
raise PermissionDenied from exc
|
|
160
|
+
|
|
87
161
|
|
|
88
162
|
class ColumnMappingViewSet(_PluginModelViewSet):
|
|
89
163
|
"""CRUD viewset for ColumnMapping."""
|
|
@@ -92,6 +166,13 @@ class ColumnMappingViewSet(_PluginModelViewSet):
|
|
|
92
166
|
serializer_class = ColumnMappingSerializer
|
|
93
167
|
|
|
94
168
|
|
|
169
|
+
class CableClassMappingViewSet(_PluginModelViewSet):
|
|
170
|
+
"""CRUD viewset for CableClassMapping."""
|
|
171
|
+
|
|
172
|
+
queryset = CableClassMapping.objects.select_related("profile")
|
|
173
|
+
serializer_class = CableClassMappingSerializer
|
|
174
|
+
|
|
175
|
+
|
|
95
176
|
class ClassRoleMappingViewSet(_PluginModelViewSet):
|
|
96
177
|
"""CRUD viewset for ClassRoleMapping."""
|
|
97
178
|
|
|
@@ -120,53 +201,12 @@ class ColumnTransformRuleViewSet(_PluginModelViewSet):
|
|
|
120
201
|
serializer_class = ColumnTransformRuleSerializer
|
|
121
202
|
|
|
122
203
|
|
|
123
|
-
def _revalidate_against_the_stored_row(serializer):
|
|
124
|
-
"""Read the resolution again and check the request against the row as it now stands.
|
|
125
|
-
|
|
126
|
-
save() writes every field, so a request that changed another field first would otherwise be
|
|
127
|
-
undone. The whole validation runs again rather than validate() alone, because the field checks
|
|
128
|
-
read the stored row too, and the profile lock makes this reading of it authoritative. The
|
|
129
|
-
result is discarded: the values are the request's own, which the first pass already holds.
|
|
130
|
-
"""
|
|
131
|
-
serializer.instance = SourceResolution.objects.get(pk=serializer.instance.pk)
|
|
132
|
-
serializer.run_validation(serializer.initial_data)
|
|
133
|
-
|
|
134
|
-
|
|
135
204
|
class SourceResolutionViewSet(_PluginModelViewSet):
|
|
136
205
|
"""CRUD viewset for SourceResolution (rerere)."""
|
|
137
206
|
|
|
138
207
|
queryset = SourceResolution.objects.select_related("profile")
|
|
139
208
|
serializer_class = SourceResolutionSerializer
|
|
140
209
|
|
|
141
|
-
# Each write serializes against an executing import, which holds the same profile row.
|
|
142
|
-
def perform_create(self, serializer):
|
|
143
|
-
"""Create the resolution under the profile lock."""
|
|
144
|
-
try:
|
|
145
|
-
with locked_profile_policy(serializer.validated_data["profile"].pk):
|
|
146
|
-
serializer.save()
|
|
147
|
-
except ImportProfile.DoesNotExist:
|
|
148
|
-
# The profile is read to validate the request, and can be deleted before the lock.
|
|
149
|
-
raise Http404 from None
|
|
150
|
-
|
|
151
|
-
def perform_update(self, serializer):
|
|
152
|
-
"""Update the resolution under its profile lock."""
|
|
153
|
-
# ValidatedModelSerializer.validate() writes the request values onto the instance, so only
|
|
154
|
-
# its primary key still names the stored row.
|
|
155
|
-
try:
|
|
156
|
-
with locked_resolution_policy(serializer.instance.pk):
|
|
157
|
-
_revalidate_against_the_stored_row(serializer)
|
|
158
|
-
serializer.save()
|
|
159
|
-
except (SourceResolution.DoesNotExist, ImportProfile.DoesNotExist):
|
|
160
|
-
raise Http404 from None
|
|
161
|
-
|
|
162
|
-
def perform_destroy(self, instance):
|
|
163
|
-
"""Delete the resolution under its profile lock."""
|
|
164
|
-
try:
|
|
165
|
-
with locked_resolution_policy(instance.pk):
|
|
166
|
-
instance.delete()
|
|
167
|
-
except (SourceResolution.DoesNotExist, ImportProfile.DoesNotExist):
|
|
168
|
-
raise Http404 from None
|
|
169
|
-
|
|
170
210
|
|
|
171
211
|
class ImportExecutionViewSet(_ProfileScopedQuerySetMixin, viewsets.ReadOnlyModelViewSet):
|
|
172
212
|
"""Read-only viewset for the Import Execution audit history."""
|
|
@@ -176,6 +216,27 @@ class ImportExecutionViewSet(_ProfileScopedQuerySetMixin, viewsets.ReadOnlyModel
|
|
|
176
216
|
permission_classes = [permissions.IsAuthenticated, DjangoModelPermissionsWithView]
|
|
177
217
|
|
|
178
218
|
|
|
219
|
+
class CableImportSourceViewSet(_ProfileScopedQuerySetMixin, viewsets.ReadOnlyModelViewSet):
|
|
220
|
+
"""Read-only viewset for per-Cable import provenance."""
|
|
221
|
+
|
|
222
|
+
queryset = CableImportSource.objects.select_related("cable", "profile")
|
|
223
|
+
serializer_class = CableImportSourceSerializer
|
|
224
|
+
permission_classes = [permissions.IsAuthenticated, DjangoModelPermissionsWithView]
|
|
225
|
+
|
|
226
|
+
def get_queryset(self):
|
|
227
|
+
"""Apply the profile scope and an optional Cable ID filter."""
|
|
228
|
+
profile_scope = ImportProfile.objects.restrict(self.request.user, "view")
|
|
229
|
+
qs = super().get_queryset().filter(profile__in=profile_scope)
|
|
230
|
+
cable_id = self.request.query_params.get("cable_id")
|
|
231
|
+
if cable_id is not None:
|
|
232
|
+
try:
|
|
233
|
+
cable_id = int(cable_id)
|
|
234
|
+
except (TypeError, ValueError) as exc:
|
|
235
|
+
raise ValidationError({"cable_id": "Enter a whole number."}) from exc
|
|
236
|
+
qs = qs.filter(cable_id=cable_id)
|
|
237
|
+
return qs
|
|
238
|
+
|
|
239
|
+
|
|
179
240
|
class ResolutionProposalViewSet(_ProfileScopedQuerySetMixin, viewsets.ReadOnlyModelViewSet):
|
|
180
241
|
"""Read-only viewset for Resolution Proposal history."""
|
|
181
242
|
|
|
@@ -225,12 +286,8 @@ class ResolutionProposalHistoryViewSet(mixins.ListModelMixin, viewsets.GenericVi
|
|
|
225
286
|
)
|
|
226
287
|
|
|
227
288
|
|
|
228
|
-
class InferenceBackendViewSet(
|
|
229
|
-
"""
|
|
230
|
-
|
|
231
|
-
Read-only on purpose: a backend row carries the destination NetBox itself calls, and the UI form
|
|
232
|
-
is the one place that validates the `api_root` trust boundary against the allowlist.
|
|
233
|
-
"""
|
|
289
|
+
class InferenceBackendViewSet(NetBoxModelViewSet):
|
|
290
|
+
"""CRUD viewset whose configuration fields derive from the Inference Backend form."""
|
|
234
291
|
|
|
235
292
|
queryset = InferenceBackend.objects.prefetch_related("tags")
|
|
236
293
|
serializer_class = InferenceBackendSerializer
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Copyright (C) 2025 Marcin Zieba <marcinpsk@gmail.com>
|
|
3
|
+
|
|
4
|
+
"""GraphQL queries registered with NetBox."""
|
|
5
|
+
|
|
6
|
+
import strawberry
|
|
7
|
+
import strawberry_django
|
|
8
|
+
|
|
9
|
+
from .types import CableClassMappingType, ImportProfileType
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@strawberry.type(name="Query")
|
|
13
|
+
class ImportProfilesQuery:
|
|
14
|
+
"""Expose import profile detail and list queries."""
|
|
15
|
+
|
|
16
|
+
import_profile: ImportProfileType = strawberry_django.field()
|
|
17
|
+
import_profile_list: list[ImportProfileType] = strawberry_django.field()
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@strawberry.type(name="Query")
|
|
21
|
+
class CableClassMappingsQuery:
|
|
22
|
+
"""Expose CableClass mapping detail and list queries."""
|
|
23
|
+
|
|
24
|
+
cable_class_mapping: CableClassMappingType = strawberry_django.field()
|
|
25
|
+
cable_class_mapping_list: list[CableClassMappingType] = strawberry_django.field()
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
schema = [ImportProfilesQuery, CableClassMappingsQuery]
|
|
29
|
+
|
|
30
|
+
__all__ = ("CableClassMappingsQuery", "ImportProfilesQuery", "schema")
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
# Copyright (C) 2025 Marcin Zieba <marcinpsk@gmail.com>
|
|
3
|
+
|
|
4
|
+
"""GraphQL object types for import profile configuration."""
|
|
5
|
+
|
|
6
|
+
import strawberry_django
|
|
7
|
+
from netbox.graphql.types import BaseObjectType, NetBoxObjectType
|
|
8
|
+
|
|
9
|
+
from netbox_data_import.models import CableClassMapping, ImportProfile
|
|
10
|
+
|
|
11
|
+
from .filters import ImportProfileFilter
|
|
12
|
+
|
|
13
|
+
_type_kwargs = {"fields": "__all__", "pagination": True}
|
|
14
|
+
if ImportProfileFilter is not None: # pragma: no cover
|
|
15
|
+
_type_kwargs["filters"] = ImportProfileFilter
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@strawberry_django.type(ImportProfile, **_type_kwargs)
|
|
19
|
+
class ImportProfileType(NetBoxObjectType):
|
|
20
|
+
"""One saved source-file import configuration."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@strawberry_django.type(
|
|
24
|
+
CableClassMapping,
|
|
25
|
+
fields=[
|
|
26
|
+
"id",
|
|
27
|
+
"profile",
|
|
28
|
+
"cable_class",
|
|
29
|
+
"cable_type_resolved",
|
|
30
|
+
"cable_type",
|
|
31
|
+
"cable_profile_resolved",
|
|
32
|
+
"cable_profile",
|
|
33
|
+
],
|
|
34
|
+
pagination=True,
|
|
35
|
+
)
|
|
36
|
+
class CableClassMappingType(BaseObjectType):
|
|
37
|
+
"""One trace CableClass policy decision."""
|
|
38
|
+
|
|
39
|
+
profile: ImportProfileType
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
__all__ = ("CableClassMappingType", "ImportProfileType")
|
|
@@ -1784,6 +1784,8 @@ class CableImportSource(DigestIndexedMixin):
|
|
|
1784
1784
|
DIGEST_SOURCE_FIELD = "trace_identity"
|
|
1785
1785
|
DIGEST_FIELD = "trace_key"
|
|
1786
1786
|
|
|
1787
|
+
objects = RestrictedQuerySet.as_manager()
|
|
1788
|
+
|
|
1787
1789
|
cable = models.ForeignKey(
|
|
1788
1790
|
to="dcim.Cable",
|
|
1789
1791
|
on_delete=models.CASCADE,
|