infrahub-server 1.7.0rc0__py3-none-any.whl → 1.7.2__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.
Files changed (124) hide show
  1. infrahub/actions/gather.py +2 -2
  2. infrahub/api/query.py +3 -2
  3. infrahub/api/schema.py +5 -0
  4. infrahub/api/transformation.py +3 -3
  5. infrahub/cli/db.py +6 -2
  6. infrahub/computed_attribute/gather.py +2 -0
  7. infrahub/config.py +2 -2
  8. infrahub/core/attribute.py +21 -2
  9. infrahub/core/branch/models.py +11 -117
  10. infrahub/core/branch/tasks.py +7 -3
  11. infrahub/core/diff/merger/merger.py +5 -1
  12. infrahub/core/diff/model/path.py +43 -0
  13. infrahub/core/graph/__init__.py +1 -1
  14. infrahub/core/graph/index.py +2 -0
  15. infrahub/core/initialization.py +2 -1
  16. infrahub/core/ipam/resource_allocator.py +229 -0
  17. infrahub/core/migrations/graph/__init__.py +10 -0
  18. infrahub/core/migrations/graph/m014_remove_index_attr_value.py +3 -2
  19. infrahub/core/migrations/graph/m015_diff_format_update.py +3 -2
  20. infrahub/core/migrations/graph/m016_diff_delete_bug_fix.py +3 -2
  21. infrahub/core/migrations/graph/m017_add_core_profile.py +6 -4
  22. infrahub/core/migrations/graph/m018_uniqueness_nulls.py +3 -4
  23. infrahub/core/migrations/graph/m020_duplicate_edges.py +3 -3
  24. infrahub/core/migrations/graph/m025_uniqueness_nulls.py +3 -4
  25. infrahub/core/migrations/graph/m026_0000_prefix_fix.py +4 -5
  26. infrahub/core/migrations/graph/m028_delete_diffs.py +3 -2
  27. infrahub/core/migrations/graph/m029_duplicates_cleanup.py +3 -2
  28. infrahub/core/migrations/graph/m031_check_number_attributes.py +4 -3
  29. infrahub/core/migrations/graph/m032_cleanup_orphaned_branch_relationships.py +3 -2
  30. infrahub/core/migrations/graph/m034_find_orphaned_schema_fields.py +3 -2
  31. infrahub/core/migrations/graph/m035_orphan_relationships.py +3 -3
  32. infrahub/core/migrations/graph/m036_drop_attr_value_index.py +3 -2
  33. infrahub/core/migrations/graph/m037_index_attr_vals.py +3 -2
  34. infrahub/core/migrations/graph/m038_redo_0000_prefix_fix.py +4 -5
  35. infrahub/core/migrations/graph/m039_ipam_reconcile.py +3 -2
  36. infrahub/core/migrations/graph/m041_deleted_dup_edges.py +3 -2
  37. infrahub/core/migrations/graph/m042_profile_attrs_in_db.py +5 -4
  38. infrahub/core/migrations/graph/m043_create_hfid_display_label_in_db.py +12 -5
  39. infrahub/core/migrations/graph/m044_backfill_hfid_display_label_in_db.py +15 -4
  40. infrahub/core/migrations/graph/m045_backfill_hfid_display_label_in_db_profile_template.py +10 -4
  41. infrahub/core/migrations/graph/m046_fill_agnostic_hfid_display_labels.py +6 -5
  42. infrahub/core/migrations/graph/m047_backfill_or_null_display_label.py +19 -5
  43. infrahub/core/migrations/graph/m048_undelete_rel_props.py +6 -4
  44. infrahub/core/migrations/graph/m049_remove_is_visible_relationship.py +3 -3
  45. infrahub/core/migrations/graph/m050_backfill_vertex_metadata.py +3 -3
  46. infrahub/core/migrations/graph/m051_subtract_branched_from_microsecond.py +39 -0
  47. infrahub/core/migrations/graph/m052_fix_global_branch_level.py +51 -0
  48. infrahub/core/migrations/graph/m053_fix_branch_level_zero.py +61 -0
  49. infrahub/core/migrations/graph/m054_cleanup_orphaned_nodes.py +87 -0
  50. infrahub/core/migrations/graph/m055_remove_webhook_validate_certificates_default.py +86 -0
  51. infrahub/core/migrations/runner.py +6 -3
  52. infrahub/core/migrations/schema/attribute_kind_update.py +8 -11
  53. infrahub/core/migrations/schema/attribute_supports_profile.py +3 -8
  54. infrahub/core/migrations/schema/models.py +8 -0
  55. infrahub/core/migrations/schema/node_attribute_add.py +24 -29
  56. infrahub/core/migrations/schema/tasks.py +7 -1
  57. infrahub/core/migrations/shared.py +37 -30
  58. infrahub/core/node/__init__.py +2 -1
  59. infrahub/core/node/lock_utils.py +23 -2
  60. infrahub/core/node/resource_manager/ip_address_pool.py +5 -11
  61. infrahub/core/node/resource_manager/ip_prefix_pool.py +5 -21
  62. infrahub/core/node/resource_manager/number_pool.py +109 -39
  63. infrahub/core/query/__init__.py +7 -1
  64. infrahub/core/query/branch.py +18 -2
  65. infrahub/core/query/ipam.py +629 -40
  66. infrahub/core/query/node.py +128 -0
  67. infrahub/core/query/resource_manager.py +114 -1
  68. infrahub/core/relationship/model.py +9 -3
  69. infrahub/core/schema/attribute_parameters.py +28 -1
  70. infrahub/core/schema/attribute_schema.py +9 -2
  71. infrahub/core/schema/definitions/core/webhook.py +0 -1
  72. infrahub/core/schema/definitions/internal.py +7 -4
  73. infrahub/core/schema/manager.py +50 -38
  74. infrahub/core/validators/attribute/kind.py +5 -2
  75. infrahub/core/validators/determiner.py +4 -0
  76. infrahub/graphql/analyzer.py +3 -1
  77. infrahub/graphql/app.py +7 -10
  78. infrahub/graphql/execution.py +95 -0
  79. infrahub/graphql/manager.py +8 -2
  80. infrahub/graphql/mutations/proposed_change.py +15 -0
  81. infrahub/graphql/parser.py +10 -7
  82. infrahub/graphql/queries/ipam.py +20 -25
  83. infrahub/graphql/queries/search.py +29 -9
  84. infrahub/lock.py +7 -0
  85. infrahub/proposed_change/tasks.py +2 -0
  86. infrahub/services/adapters/cache/redis.py +7 -0
  87. infrahub/services/adapters/http/httpx.py +27 -0
  88. infrahub/trigger/catalogue.py +2 -0
  89. infrahub/trigger/models.py +73 -4
  90. infrahub/trigger/setup.py +1 -1
  91. infrahub/trigger/system.py +36 -0
  92. infrahub/webhook/models.py +4 -2
  93. infrahub/webhook/tasks.py +2 -2
  94. infrahub/workflows/initialization.py +2 -2
  95. infrahub_sdk/analyzer.py +2 -2
  96. infrahub_sdk/branch.py +12 -39
  97. infrahub_sdk/checks.py +4 -4
  98. infrahub_sdk/client.py +36 -0
  99. infrahub_sdk/ctl/cli_commands.py +2 -1
  100. infrahub_sdk/ctl/graphql.py +15 -4
  101. infrahub_sdk/ctl/utils.py +2 -2
  102. infrahub_sdk/enums.py +6 -0
  103. infrahub_sdk/graphql/renderers.py +21 -0
  104. infrahub_sdk/graphql/utils.py +85 -0
  105. infrahub_sdk/node/attribute.py +12 -2
  106. infrahub_sdk/node/constants.py +11 -0
  107. infrahub_sdk/node/metadata.py +69 -0
  108. infrahub_sdk/node/node.py +65 -14
  109. infrahub_sdk/node/property.py +3 -0
  110. infrahub_sdk/node/related_node.py +24 -1
  111. infrahub_sdk/node/relationship.py +10 -1
  112. infrahub_sdk/operation.py +2 -2
  113. infrahub_sdk/schema/repository.py +1 -2
  114. infrahub_sdk/transforms.py +2 -2
  115. infrahub_sdk/types.py +18 -2
  116. {infrahub_server-1.7.0rc0.dist-info → infrahub_server-1.7.2.dist-info}/METADATA +8 -8
  117. {infrahub_server-1.7.0rc0.dist-info → infrahub_server-1.7.2.dist-info}/RECORD +123 -114
  118. {infrahub_server-1.7.0rc0.dist-info → infrahub_server-1.7.2.dist-info}/entry_points.txt +0 -1
  119. infrahub_testcontainers/docker-compose-cluster.test.yml +16 -10
  120. infrahub_testcontainers/docker-compose.test.yml +11 -10
  121. infrahub_testcontainers/performance_test.py +1 -1
  122. infrahub/pools/address.py +0 -16
  123. {infrahub_server-1.7.0rc0.dist-info → infrahub_server-1.7.2.dist-info}/WHEEL +0 -0
  124. {infrahub_server-1.7.0rc0.dist-info → infrahub_server-1.7.2.dist-info}/licenses/LICENSE.txt +0 -0
infrahub_sdk/checks.py CHANGED
@@ -1,8 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
- import asyncio
4
3
  import importlib
5
- import os
4
+ import inspect
5
+ import pathlib
6
6
  import warnings
7
7
  from abc import abstractmethod
8
8
  from typing import TYPE_CHECKING, Any
@@ -55,7 +55,7 @@ class InfrahubCheck:
55
55
  self.branch = branch
56
56
  self.params = params or {}
57
57
 
58
- self.root_directory = root_directory or os.getcwd()
58
+ self.root_directory = root_directory or str(pathlib.Path.cwd())
59
59
 
60
60
  self._client = client
61
61
 
@@ -160,7 +160,7 @@ class InfrahubCheck:
160
160
  data = await self.collect_data()
161
161
  unpacked = data.get("data") or data
162
162
 
163
- if asyncio.iscoroutinefunction(self.validate):
163
+ if inspect.iscoroutinefunction(self.validate):
164
164
  await self.validate(data=unpacked)
165
165
  else:
166
166
  self.validate(data=unpacked)
infrahub_sdk/client.py CHANGED
@@ -401,6 +401,7 @@ class InfrahubClient(BaseClient):
401
401
  fragment: bool = ...,
402
402
  prefetch_relationships: bool = ...,
403
403
  property: bool = ...,
404
+ include_metadata: bool = ...,
404
405
  **kwargs: Any,
405
406
  ) -> SchemaType | None: ...
406
407
 
@@ -420,6 +421,7 @@ class InfrahubClient(BaseClient):
420
421
  fragment: bool = ...,
421
422
  prefetch_relationships: bool = ...,
422
423
  property: bool = ...,
424
+ include_metadata: bool = ...,
423
425
  **kwargs: Any,
424
426
  ) -> SchemaType: ...
425
427
 
@@ -439,6 +441,7 @@ class InfrahubClient(BaseClient):
439
441
  fragment: bool = ...,
440
442
  prefetch_relationships: bool = ...,
441
443
  property: bool = ...,
444
+ include_metadata: bool = ...,
442
445
  **kwargs: Any,
443
446
  ) -> SchemaType: ...
444
447
 
@@ -458,6 +461,7 @@ class InfrahubClient(BaseClient):
458
461
  fragment: bool = ...,
459
462
  prefetch_relationships: bool = ...,
460
463
  property: bool = ...,
464
+ include_metadata: bool = ...,
461
465
  **kwargs: Any,
462
466
  ) -> InfrahubNode | None: ...
463
467
 
@@ -477,6 +481,7 @@ class InfrahubClient(BaseClient):
477
481
  fragment: bool = ...,
478
482
  prefetch_relationships: bool = ...,
479
483
  property: bool = ...,
484
+ include_metadata: bool = ...,
480
485
  **kwargs: Any,
481
486
  ) -> InfrahubNode: ...
482
487
 
@@ -496,6 +501,7 @@ class InfrahubClient(BaseClient):
496
501
  fragment: bool = ...,
497
502
  prefetch_relationships: bool = ...,
498
503
  property: bool = ...,
504
+ include_metadata: bool = ...,
499
505
  **kwargs: Any,
500
506
  ) -> InfrahubNode: ...
501
507
 
@@ -514,6 +520,7 @@ class InfrahubClient(BaseClient):
514
520
  fragment: bool = False,
515
521
  prefetch_relationships: bool = False,
516
522
  property: bool = False,
523
+ include_metadata: bool = False,
517
524
  **kwargs: Any,
518
525
  ) -> InfrahubNode | SchemaType | None:
519
526
  branch = branch or self.default_branch
@@ -547,6 +554,7 @@ class InfrahubClient(BaseClient):
547
554
  fragment=fragment,
548
555
  prefetch_relationships=prefetch_relationships,
549
556
  property=property,
557
+ include_metadata=include_metadata,
550
558
  **filters,
551
559
  )
552
560
 
@@ -650,6 +658,7 @@ class InfrahubClient(BaseClient):
650
658
  property: bool = ...,
651
659
  parallel: bool = ...,
652
660
  order: Order | None = ...,
661
+ include_metadata: bool = ...,
653
662
  ) -> list[SchemaType]: ...
654
663
 
655
664
  @overload
@@ -669,6 +678,7 @@ class InfrahubClient(BaseClient):
669
678
  property: bool = ...,
670
679
  parallel: bool = ...,
671
680
  order: Order | None = ...,
681
+ include_metadata: bool = ...,
672
682
  ) -> list[InfrahubNode]: ...
673
683
 
674
684
  async def all(
@@ -687,6 +697,7 @@ class InfrahubClient(BaseClient):
687
697
  property: bool = False,
688
698
  parallel: bool = False,
689
699
  order: Order | None = None,
700
+ include_metadata: bool = False,
690
701
  ) -> list[InfrahubNode] | list[SchemaType]:
691
702
  """Retrieve all nodes of a given kind
692
703
 
@@ -704,6 +715,7 @@ class InfrahubClient(BaseClient):
704
715
  prefetch_relationships (bool, optional): Flag to indicate whether to prefetch related node data.
705
716
  parallel (bool, optional): Whether to use parallel processing for the query.
706
717
  order (Order, optional): Ordering related options. Setting `disable=True` enhances performances.
718
+ include_metadata (bool, optional): If True, includes node_metadata and relationship_metadata in the query.
707
719
 
708
720
  Returns:
709
721
  list[InfrahubNode]: List of Nodes
@@ -723,6 +735,7 @@ class InfrahubClient(BaseClient):
723
735
  property=property,
724
736
  parallel=parallel,
725
737
  order=order,
738
+ include_metadata=include_metadata,
726
739
  )
727
740
 
728
741
  @overload
@@ -743,6 +756,7 @@ class InfrahubClient(BaseClient):
743
756
  property: bool = ...,
744
757
  parallel: bool = ...,
745
758
  order: Order | None = ...,
759
+ include_metadata: bool = ...,
746
760
  **kwargs: Any,
747
761
  ) -> list[SchemaType]: ...
748
762
 
@@ -764,6 +778,7 @@ class InfrahubClient(BaseClient):
764
778
  property: bool = ...,
765
779
  parallel: bool = ...,
766
780
  order: Order | None = ...,
781
+ include_metadata: bool = ...,
767
782
  **kwargs: Any,
768
783
  ) -> list[InfrahubNode]: ...
769
784
 
@@ -784,6 +799,7 @@ class InfrahubClient(BaseClient):
784
799
  property: bool = False,
785
800
  parallel: bool = False,
786
801
  order: Order | None = None,
802
+ include_metadata: bool = False,
787
803
  **kwargs: Any,
788
804
  ) -> list[InfrahubNode] | list[SchemaType]:
789
805
  """Retrieve nodes of a given kind based on provided filters.
@@ -803,6 +819,7 @@ class InfrahubClient(BaseClient):
803
819
  partial_match (bool, optional): Allow partial match of filter criteria for the query.
804
820
  parallel (bool, optional): Whether to use parallel processing for the query.
805
821
  order (Order, optional): Ordering related options. Setting `disable=True` enhances performances.
822
+ include_metadata (bool, optional): If True, includes node_metadata and relationship_metadata in the query.
806
823
  **kwargs (Any): Additional filter criteria for the query.
807
824
 
808
825
  Returns:
@@ -829,6 +846,7 @@ class InfrahubClient(BaseClient):
829
846
  partial_match=partial_match,
830
847
  property=property,
831
848
  order=order,
849
+ include_metadata=include_metadata,
832
850
  )
833
851
  query = Query(query=query_data)
834
852
  response = await self.execute_graphql(
@@ -1957,6 +1975,7 @@ class InfrahubClientSync(BaseClient):
1957
1975
  property: bool = ...,
1958
1976
  parallel: bool = ...,
1959
1977
  order: Order | None = ...,
1978
+ include_metadata: bool = ...,
1960
1979
  ) -> list[SchemaTypeSync]: ...
1961
1980
 
1962
1981
  @overload
@@ -1976,6 +1995,7 @@ class InfrahubClientSync(BaseClient):
1976
1995
  property: bool = ...,
1977
1996
  parallel: bool = ...,
1978
1997
  order: Order | None = ...,
1998
+ include_metadata: bool = ...,
1979
1999
  ) -> list[InfrahubNodeSync]: ...
1980
2000
 
1981
2001
  def all(
@@ -1994,6 +2014,7 @@ class InfrahubClientSync(BaseClient):
1994
2014
  property: bool = False,
1995
2015
  parallel: bool = False,
1996
2016
  order: Order | None = None,
2017
+ include_metadata: bool = False,
1997
2018
  ) -> list[InfrahubNodeSync] | list[SchemaTypeSync]:
1998
2019
  """Retrieve all nodes of a given kind
1999
2020
 
@@ -2011,6 +2032,7 @@ class InfrahubClientSync(BaseClient):
2011
2032
  prefetch_relationships (bool, optional): Flag to indicate whether to prefetch related node data.
2012
2033
  parallel (bool, optional): Whether to use parallel processing for the query.
2013
2034
  order (Order, optional): Ordering related options. Setting `disable=True` enhances performances.
2035
+ include_metadata (bool, optional): If True, includes node_metadata and relationship_metadata in the query.
2014
2036
 
2015
2037
  Returns:
2016
2038
  list[InfrahubNodeSync]: List of Nodes
@@ -2030,6 +2052,7 @@ class InfrahubClientSync(BaseClient):
2030
2052
  property=property,
2031
2053
  parallel=parallel,
2032
2054
  order=order,
2055
+ include_metadata=include_metadata,
2033
2056
  )
2034
2057
 
2035
2058
  def _process_nodes_and_relationships(
@@ -2091,6 +2114,7 @@ class InfrahubClientSync(BaseClient):
2091
2114
  property: bool = ...,
2092
2115
  parallel: bool = ...,
2093
2116
  order: Order | None = ...,
2117
+ include_metadata: bool = ...,
2094
2118
  **kwargs: Any,
2095
2119
  ) -> list[SchemaTypeSync]: ...
2096
2120
 
@@ -2112,6 +2136,7 @@ class InfrahubClientSync(BaseClient):
2112
2136
  property: bool = ...,
2113
2137
  parallel: bool = ...,
2114
2138
  order: Order | None = ...,
2139
+ include_metadata: bool = ...,
2115
2140
  **kwargs: Any,
2116
2141
  ) -> list[InfrahubNodeSync]: ...
2117
2142
 
@@ -2132,6 +2157,7 @@ class InfrahubClientSync(BaseClient):
2132
2157
  property: bool = False,
2133
2158
  parallel: bool = False,
2134
2159
  order: Order | None = None,
2160
+ include_metadata: bool = False,
2135
2161
  **kwargs: Any,
2136
2162
  ) -> list[InfrahubNodeSync] | list[SchemaTypeSync]:
2137
2163
  """Retrieve nodes of a given kind based on provided filters.
@@ -2151,6 +2177,7 @@ class InfrahubClientSync(BaseClient):
2151
2177
  partial_match (bool, optional): Allow partial match of filter criteria for the query.
2152
2178
  parallel (bool, optional): Whether to use parallel processing for the query.
2153
2179
  order (Order, optional): Ordering related options. Setting `disable=True` enhances performances.
2180
+ include_metadata (bool, optional): If True, includes node_metadata and relationship_metadata in the query.
2154
2181
  **kwargs (Any): Additional filter criteria for the query.
2155
2182
 
2156
2183
  Returns:
@@ -2177,6 +2204,7 @@ class InfrahubClientSync(BaseClient):
2177
2204
  partial_match=partial_match,
2178
2205
  property=property,
2179
2206
  order=order,
2207
+ include_metadata=include_metadata,
2180
2208
  )
2181
2209
  query = Query(query=query_data)
2182
2210
  response = self.execute_graphql(
@@ -2266,6 +2294,7 @@ class InfrahubClientSync(BaseClient):
2266
2294
  fragment: bool = ...,
2267
2295
  prefetch_relationships: bool = ...,
2268
2296
  property: bool = ...,
2297
+ include_metadata: bool = ...,
2269
2298
  **kwargs: Any,
2270
2299
  ) -> SchemaTypeSync | None: ...
2271
2300
 
@@ -2285,6 +2314,7 @@ class InfrahubClientSync(BaseClient):
2285
2314
  fragment: bool = ...,
2286
2315
  prefetch_relationships: bool = ...,
2287
2316
  property: bool = ...,
2317
+ include_metadata: bool = ...,
2288
2318
  **kwargs: Any,
2289
2319
  ) -> SchemaTypeSync: ...
2290
2320
 
@@ -2304,6 +2334,7 @@ class InfrahubClientSync(BaseClient):
2304
2334
  fragment: bool = ...,
2305
2335
  prefetch_relationships: bool = ...,
2306
2336
  property: bool = ...,
2337
+ include_metadata: bool = ...,
2307
2338
  **kwargs: Any,
2308
2339
  ) -> SchemaTypeSync: ...
2309
2340
 
@@ -2323,6 +2354,7 @@ class InfrahubClientSync(BaseClient):
2323
2354
  fragment: bool = ...,
2324
2355
  prefetch_relationships: bool = ...,
2325
2356
  property: bool = ...,
2357
+ include_metadata: bool = ...,
2326
2358
  **kwargs: Any,
2327
2359
  ) -> InfrahubNodeSync | None: ...
2328
2360
 
@@ -2342,6 +2374,7 @@ class InfrahubClientSync(BaseClient):
2342
2374
  fragment: bool = ...,
2343
2375
  prefetch_relationships: bool = ...,
2344
2376
  property: bool = ...,
2377
+ include_metadata: bool = ...,
2345
2378
  **kwargs: Any,
2346
2379
  ) -> InfrahubNodeSync: ...
2347
2380
 
@@ -2361,6 +2394,7 @@ class InfrahubClientSync(BaseClient):
2361
2394
  fragment: bool = ...,
2362
2395
  prefetch_relationships: bool = ...,
2363
2396
  property: bool = ...,
2397
+ include_metadata: bool = ...,
2364
2398
  **kwargs: Any,
2365
2399
  ) -> InfrahubNodeSync: ...
2366
2400
 
@@ -2379,6 +2413,7 @@ class InfrahubClientSync(BaseClient):
2379
2413
  fragment: bool = False,
2380
2414
  prefetch_relationships: bool = False,
2381
2415
  property: bool = False,
2416
+ include_metadata: bool = False,
2382
2417
  **kwargs: Any,
2383
2418
  ) -> InfrahubNodeSync | SchemaTypeSync | None:
2384
2419
  branch = branch or self.default_branch
@@ -2412,6 +2447,7 @@ class InfrahubClientSync(BaseClient):
2412
2447
  fragment=fragment,
2413
2448
  prefetch_relationships=prefetch_relationships,
2414
2449
  property=property,
2450
+ include_metadata=include_metadata,
2415
2451
  **filters,
2416
2452
  )
2417
2453
 
@@ -3,6 +3,7 @@ from __future__ import annotations
3
3
  import asyncio
4
4
  import functools
5
5
  import importlib
6
+ import inspect
6
7
  import logging
7
8
  import platform
8
9
  import sys
@@ -240,7 +241,7 @@ async def _run_transform(
240
241
  console.print("[yellow] you can specify a different branch with --branch")
241
242
  raise typer.Abort()
242
243
 
243
- if asyncio.iscoroutinefunction(transform_func):
244
+ if inspect.iscoroutinefunction(transform_func):
244
245
  output = await transform_func(response)
245
246
  else:
246
247
  output = transform_func(response)
@@ -22,7 +22,12 @@ from rich.console import Console
22
22
  from ..async_typer import AsyncTyper
23
23
  from ..ctl.client import initialize_client
24
24
  from ..ctl.utils import catch_exception
25
- from ..graphql.utils import insert_fragments_inline, remove_fragment_import
25
+ from ..graphql.utils import (
26
+ insert_fragments_inline,
27
+ remove_fragment_import,
28
+ strip_typename_from_fragment,
29
+ strip_typename_from_operation,
30
+ )
26
31
  from .parameters import CONFIG_PARAM
27
32
 
28
33
  app = AsyncTyper()
@@ -152,12 +157,18 @@ async def generate_return_types(
152
157
  queries = filter_operations_definitions(definitions)
153
158
  fragments = filter_fragments_definitions(definitions)
154
159
 
160
+ # Strip __typename fields from operations and fragments before code generation.
161
+ # __typename is a GraphQL introspection meta-field that isn't part of the schema's
162
+ # type definitions, causing ariadne-codegen to fail with "Redefinition of reserved type 'String'"
163
+ stripped_queries = [strip_typename_from_operation(q) for q in queries]
164
+ stripped_fragments = [strip_typename_from_fragment(f) for f in fragments]
165
+
155
166
  package_generator = get_package_generator(
156
167
  schema=graphql_schema,
157
- fragments=fragments,
168
+ fragments=stripped_fragments,
158
169
  settings=ClientSettings(
159
170
  schema_path=str(schema),
160
- target_package_name=directory.name,
171
+ target_package_name=directory.name or "graphql_client",
161
172
  queries_path=str(directory),
162
173
  include_comments=CommentsStrategy.NONE,
163
174
  ),
@@ -166,7 +177,7 @@ async def generate_return_types(
166
177
 
167
178
  parsing_failed = False
168
179
  try:
169
- for query_operation in queries:
180
+ for query_operation in stripped_queries:
170
181
  package_generator.add_operation(query_operation)
171
182
  except ParsingError as exc:
172
183
  console.print(f"[red]Unable to process {gql_file.name}: {exc}")
infrahub_sdk/ctl/utils.py CHANGED
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- import asyncio
3
+ import inspect
4
4
  import logging
5
5
  import traceback
6
6
  from collections.abc import Callable, Coroutine
@@ -83,7 +83,7 @@ def catch_exception(
83
83
  console = Console()
84
84
 
85
85
  def decorator(func: Callable[..., T]) -> Callable[..., T | Coroutine[Any, Any, T]]:
86
- if asyncio.iscoroutinefunction(func):
86
+ if inspect.iscoroutinefunction(func):
87
87
 
88
88
  @wraps(func)
89
89
  async def async_wrapper(*args: Any, **kwargs: Any) -> T:
infrahub_sdk/enums.py ADDED
@@ -0,0 +1,6 @@
1
+ from enum import Enum
2
+
3
+
4
+ class OrderDirection(str, Enum):
5
+ ASC = "ASC"
6
+ DESC = "DESC"
@@ -7,6 +7,8 @@ from typing import Any
7
7
 
8
8
  from pydantic import BaseModel
9
9
 
10
+ from infrahub_sdk.types import Order
11
+
10
12
  from .constants import VARIABLE_TYPE_MAPPING
11
13
 
12
14
 
@@ -53,6 +55,16 @@ def convert_to_graphql_as_string(value: Any, convert_enum: bool = False) -> str:
53
55
  if isinstance(value, list):
54
56
  values_as_string = [convert_to_graphql_as_string(value=item, convert_enum=convert_enum) for item in value]
55
57
  return "[" + ", ".join(values_as_string) + "]"
58
+ if isinstance(value, Order):
59
+ data = value.model_dump(exclude_none=True)
60
+ return (
61
+ "{ "
62
+ + ", ".join(
63
+ f"{key}: {convert_to_graphql_as_string(value=val, convert_enum=convert_enum)}"
64
+ for key, val in data.items()
65
+ )
66
+ + " }"
67
+ )
56
68
  if isinstance(value, BaseModel):
57
69
  data = value.model_dump()
58
70
  return (
@@ -63,6 +75,15 @@ def convert_to_graphql_as_string(value: Any, convert_enum: bool = False) -> str:
63
75
  )
64
76
  + " }"
65
77
  )
78
+ if isinstance(value, dict):
79
+ return (
80
+ "{ "
81
+ + ", ".join(
82
+ f"{key}: {convert_to_graphql_as_string(value=val, convert_enum=convert_enum)}"
83
+ for key, val in value.items()
84
+ )
85
+ + " }"
86
+ )
66
87
 
67
88
  return str(value)
68
89
 
@@ -1,5 +1,90 @@
1
1
  import ast
2
2
 
3
+ from graphql import (
4
+ FieldNode,
5
+ FragmentDefinitionNode,
6
+ FragmentSpreadNode,
7
+ InlineFragmentNode,
8
+ OperationDefinitionNode,
9
+ SelectionNode,
10
+ SelectionSetNode,
11
+ )
12
+
13
+
14
+ def strip_typename_from_selection_set(selection_set: SelectionSetNode | None) -> SelectionSetNode | None:
15
+ """Recursively strip __typename fields from a SelectionSetNode.
16
+
17
+ The __typename meta-field is an introspection field that is not part of the schema's
18
+ type definitions. When code generation tools like ariadne-codegen try to look up
19
+ __typename in the schema, they fail because it's a reserved introspection field.
20
+
21
+ This function removes all __typename fields from the selection set, allowing
22
+ code generation to proceed without errors.
23
+ """
24
+ if selection_set is None:
25
+ return None
26
+
27
+ new_selections: list[SelectionNode] = []
28
+ for selection in selection_set.selections:
29
+ if isinstance(selection, FieldNode):
30
+ # Skip __typename fields
31
+ if selection.name.value == "__typename":
32
+ continue
33
+ # Recursively process nested selection sets
34
+ new_field = FieldNode(
35
+ alias=selection.alias,
36
+ name=selection.name,
37
+ arguments=selection.arguments,
38
+ directives=selection.directives,
39
+ selection_set=strip_typename_from_selection_set(selection.selection_set),
40
+ )
41
+ new_selections.append(new_field)
42
+ elif isinstance(selection, InlineFragmentNode):
43
+ # Process inline fragments
44
+ new_inline = InlineFragmentNode(
45
+ type_condition=selection.type_condition,
46
+ directives=selection.directives,
47
+ selection_set=strip_typename_from_selection_set(selection.selection_set),
48
+ )
49
+ new_selections.append(new_inline)
50
+ elif isinstance(selection, FragmentSpreadNode):
51
+ # FragmentSpread references a named fragment - keep as-is
52
+ new_selections.append(selection)
53
+ else:
54
+ raise TypeError(f"Unexpected GraphQL selection node type '{type(selection).__name__}'.")
55
+
56
+ return SelectionSetNode(selections=tuple(new_selections))
57
+
58
+
59
+ def strip_typename_from_operation(operation: OperationDefinitionNode) -> OperationDefinitionNode:
60
+ """Strip __typename fields from an operation definition.
61
+
62
+ Returns a new OperationDefinitionNode with all __typename fields removed
63
+ from its selection set and any nested selection sets.
64
+ """
65
+ return OperationDefinitionNode(
66
+ operation=operation.operation,
67
+ name=operation.name,
68
+ variable_definitions=operation.variable_definitions,
69
+ directives=operation.directives,
70
+ selection_set=strip_typename_from_selection_set(operation.selection_set),
71
+ )
72
+
73
+
74
+ def strip_typename_from_fragment(fragment: FragmentDefinitionNode) -> FragmentDefinitionNode:
75
+ """Strip __typename fields from a fragment definition.
76
+
77
+ Returns a new FragmentDefinitionNode with all __typename fields removed
78
+ from its selection set and any nested selection sets.
79
+ """
80
+ return FragmentDefinitionNode(
81
+ name=fragment.name,
82
+ type_condition=fragment.type_condition,
83
+ variable_definitions=fragment.variable_definitions,
84
+ directives=fragment.directives,
85
+ selection_set=strip_typename_from_selection_set(fragment.selection_set),
86
+ )
87
+
3
88
 
4
89
  def get_class_def_index(module: ast.Module) -> int:
5
90
  """Get the index of the first class definition in the module.
@@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Any, get_args
6
6
 
7
7
  from ..protocols_base import CoreNodeBase
8
8
  from ..uuidt import UUIDT
9
- from .constants import IP_TYPES, PROPERTIES_FLAG, PROPERTIES_OBJECT, SAFE_VALUE
9
+ from .constants import ATTRIBUTE_METADATA_OBJECT, IP_TYPES, PROPERTIES_FLAG, PROPERTIES_OBJECT, SAFE_VALUE
10
10
  from .property import NodeProperty
11
11
 
12
12
  if TYPE_CHECKING:
@@ -57,11 +57,16 @@ class Attribute:
57
57
 
58
58
  self.source: NodeProperty | None = None
59
59
  self.owner: NodeProperty | None = None
60
+ self.updated_by: NodeProperty | None = None
60
61
 
61
62
  for prop_name in self._properties_object:
62
63
  if data.get(prop_name):
63
64
  setattr(self, prop_name, NodeProperty(data=data.get(prop_name))) # type: ignore[arg-type]
64
65
 
66
+ for prop_name in ATTRIBUTE_METADATA_OBJECT:
67
+ if data.get(prop_name):
68
+ setattr(self, prop_name, NodeProperty(data=data.get(prop_name))) # type: ignore[arg-type]
69
+
65
70
  @property
66
71
  def value(self) -> Any:
67
72
  return self._value
@@ -104,7 +109,7 @@ class Attribute:
104
109
 
105
110
  return {"data": data, "variables": variables}
106
111
 
107
- def _generate_query_data(self, property: bool = False) -> dict | None:
112
+ def _generate_query_data(self, property: bool = False, include_metadata: bool = False) -> dict | None:
108
113
  data: dict[str, Any] = {"value": None}
109
114
 
110
115
  if property:
@@ -115,6 +120,11 @@ class Attribute:
115
120
  for prop_name in self._properties_object:
116
121
  data[prop_name] = {"id": None, "display_label": None, "__typename": None}
117
122
 
123
+ if include_metadata:
124
+ data["updated_at"] = None
125
+ for prop_name in ATTRIBUTE_METADATA_OBJECT:
126
+ data[prop_name] = {"id": None, "display_label": None, "__typename": None}
127
+
118
128
  return data
119
129
 
120
130
  def _generate_mutation_query(self) -> dict[str, Any]:
@@ -3,6 +3,17 @@ import re
3
3
 
4
4
  PROPERTIES_FLAG = ["is_protected", "updated_at"]
5
5
  PROPERTIES_OBJECT = ["source", "owner"]
6
+
7
+ # Attribute-level metadata object fields (in addition to PROPERTIES_OBJECT)
8
+ ATTRIBUTE_METADATA_OBJECT = ["updated_by"]
9
+
10
+ # Node metadata fields (for node_metadata in GraphQL response)
11
+ NODE_METADATA_FIELDS_FLAG = ["created_at", "updated_at"]
12
+ NODE_METADATA_FIELDS_OBJECT = ["created_by", "updated_by"]
13
+
14
+ # Relationship metadata fields (for relationship_metadata in GraphQL response)
15
+ RELATIONSHIP_METADATA_FIELDS_FLAG = ["updated_at"]
16
+ RELATIONSHIP_METADATA_FIELDS_OBJECT = ["updated_by"]
6
17
  SAFE_VALUE = re.compile(r"(^[\. /:a-zA-Z0-9_-]+$)|(^$)")
7
18
 
8
19
  IP_TYPES = ipaddress.IPv4Interface | ipaddress.IPv6Interface | ipaddress.IPv4Network | ipaddress.IPv6Network
@@ -0,0 +1,69 @@
1
+ from __future__ import annotations
2
+
3
+ from .property import NodeProperty
4
+
5
+
6
+ class NodeMetadata:
7
+ """Represents metadata about a node (created_at, created_by, updated_at, updated_by)."""
8
+
9
+ def __init__(self, data: dict | None = None) -> None:
10
+ """
11
+ Args:
12
+ data: Data containing the metadata fields from the GraphQL response.
13
+ """
14
+ self.created_at: str | None = None
15
+ self.created_by: NodeProperty | None = None
16
+ self.updated_at: str | None = None
17
+ self.updated_by: NodeProperty | None = None
18
+
19
+ if data:
20
+ self.created_at = data.get("created_at")
21
+ self.updated_at = data.get("updated_at")
22
+ if data.get("created_by"):
23
+ self.created_by = NodeProperty(data["created_by"])
24
+ if data.get("updated_by"):
25
+ self.updated_by = NodeProperty(data["updated_by"])
26
+
27
+ def __repr__(self) -> str:
28
+ return (
29
+ f"NodeMetadata(created_at={self.created_at!r}, created_by={self.created_by!r}, "
30
+ f"updated_at={self.updated_at!r}, updated_by={self.updated_by!r})"
31
+ )
32
+
33
+ @classmethod
34
+ def _generate_query_data(cls) -> dict:
35
+ """Generate the query structure for node_metadata fields."""
36
+ return {
37
+ "created_at": None,
38
+ "created_by": {"id": None, "__typename": None, "display_label": None},
39
+ "updated_at": None,
40
+ "updated_by": {"id": None, "__typename": None, "display_label": None},
41
+ }
42
+
43
+
44
+ class RelationshipMetadata:
45
+ """Represents metadata about a relationship edge (updated_at, updated_by)."""
46
+
47
+ def __init__(self, data: dict | None = None) -> None:
48
+ """
49
+ Args:
50
+ data: Data containing the metadata fields from the GraphQL response.
51
+ """
52
+ self.updated_at: str | None = None
53
+ self.updated_by: NodeProperty | None = None
54
+
55
+ if data:
56
+ self.updated_at = data.get("updated_at")
57
+ if data.get("updated_by"):
58
+ self.updated_by = NodeProperty(data["updated_by"])
59
+
60
+ def __repr__(self) -> str:
61
+ return f"RelationshipMetadata(updated_at={self.updated_at!r}, updated_by={self.updated_by!r})"
62
+
63
+ @classmethod
64
+ def _generate_query_data(cls) -> dict:
65
+ """Generate the query structure for relationship_metadata fields."""
66
+ return {
67
+ "updated_at": None,
68
+ "updated_by": {"id": None, "__typename": None, "display_label": None},
69
+ }