ipfabric_netbox 4.3.2b10__py3-none-any.whl → 4.3.2b11__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 ipfabric_netbox might be problematic. Click here for more details.

@@ -1843,6 +1843,99 @@ class IPFabricSyncFormTestCase(TestCase):
1843
1843
  fieldset_names2 = [fs.name for fs in fieldsets2]
1844
1844
  self.assertEqual(fieldset_names1, fieldset_names2)
1845
1845
 
1846
+ def test_save_method_with_filters(self):
1847
+ """Test that filters many-to-many relationship is properly saved"""
1848
+ # Create some filter objects
1849
+ endpoint = IPFabricEndpoint.objects.create(
1850
+ name="Test Endpoint",
1851
+ endpoint="inventory/devices",
1852
+ )
1853
+ filter1 = IPFabricFilter.objects.create(
1854
+ name="Filter 1",
1855
+ filter_type="and",
1856
+ )
1857
+ filter1.endpoints.add(endpoint)
1858
+
1859
+ filter2 = IPFabricFilter.objects.create(
1860
+ name="Filter 2",
1861
+ filter_type="or",
1862
+ )
1863
+ filter2.endpoints.add(endpoint)
1864
+
1865
+ # Create a sync with filters
1866
+ form = IPFabricSyncForm(
1867
+ data={
1868
+ "name": "Test Sync With Filters",
1869
+ "source": self.source.pk,
1870
+ "snapshot_data": self.snapshot.pk,
1871
+ "filters": [filter1.pk, filter2.pk],
1872
+ }
1873
+ )
1874
+
1875
+ self.assertTrue(form.is_valid(), form.errors)
1876
+ sync_instance = form.save()
1877
+
1878
+ # Verify filters were saved correctly
1879
+ self.assertEqual(sync_instance.filters.count(), 2)
1880
+ self.assertIn(filter1, sync_instance.filters.all())
1881
+ self.assertIn(filter2, sync_instance.filters.all())
1882
+
1883
+ def test_update_method_with_filters(self):
1884
+ """Test that filters can be updated on existing sync"""
1885
+ # Create some filter objects
1886
+ endpoint = IPFabricEndpoint.objects.create(
1887
+ name="Test Endpoint Update",
1888
+ endpoint="inventory/devices",
1889
+ )
1890
+ filter1 = IPFabricFilter.objects.create(
1891
+ name="Filter Update 1",
1892
+ filter_type="and",
1893
+ )
1894
+ filter1.endpoints.add(endpoint)
1895
+
1896
+ filter2 = IPFabricFilter.objects.create(
1897
+ name="Filter Update 2",
1898
+ filter_type="or",
1899
+ )
1900
+ filter2.endpoints.add(endpoint)
1901
+
1902
+ filter3 = IPFabricFilter.objects.create(
1903
+ name="Filter Update 3",
1904
+ filter_type="and",
1905
+ )
1906
+ filter3.endpoints.add(endpoint)
1907
+
1908
+ # Create a sync with initial filters
1909
+ sync_instance = IPFabricSync.objects.create(
1910
+ name="Existing Sync With Filters",
1911
+ snapshot_data=self.snapshot,
1912
+ parameters={},
1913
+ )
1914
+ sync_instance.filters.set([filter1, filter2])
1915
+
1916
+ # Verify initial state
1917
+ self.assertEqual(sync_instance.filters.count(), 2)
1918
+
1919
+ # Update the sync with different filters
1920
+ form = IPFabricSyncForm(
1921
+ instance=sync_instance,
1922
+ data={
1923
+ "name": "Existing Sync With Filters",
1924
+ "source": self.source.pk,
1925
+ "snapshot_data": self.snapshot.pk,
1926
+ "filters": [filter2.pk, filter3.pk], # Changed filters
1927
+ },
1928
+ )
1929
+
1930
+ self.assertTrue(form.is_valid(), form.errors)
1931
+ updated_instance = form.save()
1932
+
1933
+ # Verify filters were updated correctly
1934
+ self.assertEqual(updated_instance.filters.count(), 2)
1935
+ self.assertNotIn(filter1, updated_instance.filters.all())
1936
+ self.assertIn(filter2, updated_instance.filters.all())
1937
+ self.assertIn(filter3, updated_instance.filters.all())
1938
+
1846
1939
 
1847
1940
  class IPFabricFilterFormTestCase(TestCase):
1848
1941
  """Test cases for IPFabricFilterForm"""
@@ -1692,7 +1692,7 @@ class IPFabricTransformMapTestCase(
1692
1692
  self.assertHttpStatus(response, 200)
1693
1693
  self.assertContains(
1694
1694
  response,
1695
- "A transform map with group 'Test Group' and target model 'DCIM | site' already exists.",
1695
+ "A transform map with group 'Test Group', target model 'DCIM | site', and source endpoint '/inventory/sites/overview' already exists.",
1696
1696
  )
1697
1697
  self.assertIn("X-Debug-HTMX-Partial", response)
1698
1698
 
@@ -28,3 +28,56 @@ def get_endpoint_data() -> dict:
28
28
 
29
29
 
30
30
  # endregion Endpoint Creation
31
+
32
+ # region Endpoint Updating
33
+
34
+
35
+ class EndpointRecord:
36
+ """Record for endpoint creation/deletion in migrations."""
37
+
38
+ def __init__(self, name: str, description: str, endpoint: str):
39
+ self.name = name
40
+ self.description = description
41
+ self.endpoint = endpoint
42
+
43
+
44
+ def do_endpoint_change(
45
+ apps, schema_editor, endpoints: tuple[EndpointRecord, ...], forward: bool = True
46
+ ):
47
+ """
48
+ Apply endpoint changes, `forward` determines direction.
49
+
50
+ Forward: Creates endpoints if they don't exist (idempotent using get_or_create).
51
+ Reverse: Deletes endpoints by endpoint path.
52
+
53
+ Args:
54
+ apps: Django apps registry (historical models)
55
+ schema_editor: Database schema editor
56
+ endpoints: Tuple of EndpointRecord objects to process
57
+ forward: True for forward migration (create), False for reverse (delete)
58
+ """
59
+ IPFabricEndpoint = apps.get_model("ipfabric_netbox", "IPFabricEndpoint")
60
+ db_alias = schema_editor.connection.alias
61
+
62
+ try:
63
+ for endpoint_record in endpoints:
64
+ if forward:
65
+ # Forward: Create endpoint if it doesn't exist (idempotent)
66
+ IPFabricEndpoint.objects.using(db_alias).get_or_create(
67
+ endpoint=endpoint_record.endpoint,
68
+ defaults={
69
+ "name": endpoint_record.name,
70
+ "description": endpoint_record.description,
71
+ },
72
+ )
73
+ else:
74
+ # Reverse: Delete endpoint by endpoint path
75
+ IPFabricEndpoint.objects.using(db_alias).filter(
76
+ endpoint=endpoint_record.endpoint
77
+ ).delete()
78
+
79
+ except Exception as e:
80
+ print(f"Error applying endpoint changes: {e}")
81
+
82
+
83
+ # endregion