infrahub-server 1.5.5__py3-none-any.whl → 1.6.0b0__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 (100) hide show
  1. infrahub/api/artifact.py +5 -3
  2. infrahub/auth.py +5 -6
  3. infrahub/cli/db.py +3 -3
  4. infrahub/cli/db_commands/clean_duplicate_schema_fields.py +2 -2
  5. infrahub/cli/dev.py +30 -0
  6. infrahub/config.py +62 -14
  7. infrahub/constants/database.py +5 -5
  8. infrahub/core/branch/models.py +24 -6
  9. infrahub/core/diff/model/diff.py +2 -2
  10. infrahub/core/graph/constraints.py +2 -2
  11. infrahub/core/manager.py +155 -29
  12. infrahub/core/merge.py +29 -2
  13. infrahub/core/migrations/graph/m041_deleted_dup_edges.py +2 -3
  14. infrahub/core/migrations/shared.py +2 -2
  15. infrahub/core/node/__init__.py +1 -1
  16. infrahub/core/node/ipam.py +4 -4
  17. infrahub/core/node/node_property_attribute.py +2 -2
  18. infrahub/core/protocols.py +7 -1
  19. infrahub/core/query/branch.py +11 -0
  20. infrahub/core/query/standard_node.py +3 -0
  21. infrahub/core/relationship/model.py +3 -9
  22. infrahub/core/schema/__init__.py +3 -3
  23. infrahub/core/task/user_task.py +2 -2
  24. infrahub/core/validators/enum.py +2 -2
  25. infrahub/dependencies/interface.py +2 -2
  26. infrahub/events/constants.py +2 -2
  27. infrahub/git/base.py +43 -1
  28. infrahub/git/models.py +2 -1
  29. infrahub/git/repository.py +5 -1
  30. infrahub/git/tasks.py +28 -1
  31. infrahub/git/utils.py +9 -0
  32. infrahub/graphql/analyzer.py +4 -4
  33. infrahub/graphql/mutations/computed_attribute.py +1 -1
  34. infrahub/graphql/mutations/convert_object_type.py +1 -1
  35. infrahub/graphql/mutations/display_label.py +1 -1
  36. infrahub/graphql/mutations/hfid.py +1 -1
  37. infrahub/graphql/mutations/ipam.py +1 -1
  38. infrahub/graphql/mutations/profile.py +1 -0
  39. infrahub/graphql/mutations/relationship.py +2 -2
  40. infrahub/graphql/mutations/resource_manager.py +1 -1
  41. infrahub/graphql/queries/__init__.py +2 -1
  42. infrahub/graphql/queries/branch.py +58 -3
  43. infrahub/graphql/queries/ipam.py +9 -4
  44. infrahub/graphql/queries/resource_manager.py +5 -8
  45. infrahub/graphql/queries/search.py +3 -3
  46. infrahub/graphql/schema.py +2 -0
  47. infrahub/graphql/types/__init__.py +3 -1
  48. infrahub/graphql/types/branch.py +98 -2
  49. infrahub/lock.py +6 -6
  50. infrahub/patch/constants.py +2 -2
  51. infrahub/task_manager/task.py +2 -2
  52. infrahub/telemetry/constants.py +2 -2
  53. infrahub/trigger/models.py +2 -2
  54. infrahub/utils.py +1 -1
  55. infrahub/validators/tasks.py +1 -1
  56. infrahub/workers/infrahub_async.py +37 -0
  57. infrahub_sdk/async_typer.py +2 -1
  58. infrahub_sdk/batch.py +2 -2
  59. infrahub_sdk/client.py +8 -9
  60. infrahub_sdk/config.py +2 -2
  61. infrahub_sdk/ctl/branch.py +1 -1
  62. infrahub_sdk/ctl/cli.py +2 -2
  63. infrahub_sdk/ctl/cli_commands.py +2 -1
  64. infrahub_sdk/ctl/graphql.py +2 -2
  65. infrahub_sdk/ctl/importer.py +1 -1
  66. infrahub_sdk/ctl/utils.py +3 -3
  67. infrahub_sdk/node/attribute.py +11 -10
  68. infrahub_sdk/node/constants.py +1 -2
  69. infrahub_sdk/node/node.py +54 -11
  70. infrahub_sdk/node/related_node.py +1 -1
  71. infrahub_sdk/object_store.py +4 -4
  72. infrahub_sdk/operation.py +2 -2
  73. infrahub_sdk/protocols_generator/generator.py +1 -1
  74. infrahub_sdk/pytest_plugin/items/jinja2_transform.py +1 -1
  75. infrahub_sdk/pytest_plugin/models.py +1 -1
  76. infrahub_sdk/pytest_plugin/plugin.py +1 -1
  77. infrahub_sdk/query_groups.py +2 -2
  78. infrahub_sdk/schema/__init__.py +10 -11
  79. infrahub_sdk/schema/main.py +2 -2
  80. infrahub_sdk/schema/repository.py +2 -2
  81. infrahub_sdk/spec/object.py +2 -2
  82. infrahub_sdk/spec/range_expansion.py +1 -1
  83. infrahub_sdk/template/__init__.py +2 -1
  84. infrahub_sdk/transfer/importer/json.py +3 -3
  85. infrahub_sdk/types.py +2 -2
  86. infrahub_sdk/utils.py +2 -2
  87. {infrahub_server-1.5.5.dist-info → infrahub_server-1.6.0b0.dist-info}/METADATA +58 -59
  88. {infrahub_server-1.5.5.dist-info → infrahub_server-1.6.0b0.dist-info}/RECORD +217 -223
  89. {infrahub_server-1.5.5.dist-info → infrahub_server-1.6.0b0.dist-info}/WHEEL +1 -1
  90. infrahub_server-1.6.0b0.dist-info/entry_points.txt +12 -0
  91. infrahub_testcontainers/docker-compose-cluster.test.yml +1 -1
  92. infrahub_testcontainers/docker-compose.test.yml +1 -1
  93. infrahub/core/schema/generated/__init__.py +0 -0
  94. infrahub/core/schema/generated/attribute_schema.py +0 -133
  95. infrahub/core/schema/generated/base_node_schema.py +0 -111
  96. infrahub/core/schema/generated/genericnode_schema.py +0 -30
  97. infrahub/core/schema/generated/node_schema.py +0 -40
  98. infrahub/core/schema/generated/relationship_schema.py +0 -141
  99. infrahub_server-1.5.5.dist-info/entry_points.txt +0 -13
  100. {infrahub_server-1.5.5.dist-info → infrahub_server-1.6.0b0.dist-info/licenses}/LICENSE.txt +0 -0
infrahub/git/base.py CHANGED
@@ -16,10 +16,12 @@ from prefect.logging import get_run_logger
16
16
  from pydantic import BaseModel, ConfigDict, Field
17
17
  from pydantic import ValidationError as PydanticValidationError
18
18
 
19
+ from infrahub import config
19
20
  from infrahub.core.branch import Branch
20
21
  from infrahub.core.constants import InfrahubKind, RepositoryOperationalStatus, RepositorySyncStatus
21
22
  from infrahub.core.registry import registry
22
23
  from infrahub.exceptions import (
24
+ BranchNotFoundError,
23
25
  CommitNotFoundError,
24
26
  FileOutOfRepositoryError,
25
27
  RepositoryConnectionError,
@@ -31,6 +33,7 @@ from infrahub.exceptions import (
31
33
  )
32
34
  from infrahub.git.constants import BRANCHES_DIRECTORY_NAME, COMMITS_DIRECTORY_NAME, TEMPORARY_DIRECTORY_NAME
33
35
  from infrahub.git.directory import get_repositories_directory, initialize_repositories_directory
36
+ from infrahub.git.utils import branch_name_in_import_sync_branches
34
37
  from infrahub.git.worktree import Worktree
35
38
  from infrahub.log import get_logger
36
39
  from infrahub.workers.dependencies import get_client
@@ -733,6 +736,45 @@ class InfrahubRepositoryBase(BaseModel, ABC):
733
736
 
734
737
  return True
735
738
 
739
+ async def get_filtered_remote_branches(self) -> dict[str, BranchInRemote]:
740
+ branches = self.get_branches_from_remote()
741
+
742
+ if not config.SETTINGS.git.import_sync_branch_names:
743
+ return branches
744
+
745
+ filtered_branches = {}
746
+ skipped_branch_names = []
747
+
748
+ for short_name, branch_data in branches.items():
749
+ branch = None
750
+
751
+ try:
752
+ branch = registry.get_branch_from_registry(branch=short_name)
753
+ except BranchNotFoundError:
754
+ ...
755
+
756
+ branch_exists_import_sync_condition = branch and (
757
+ branch.name not in {registry.default_branch, self.default_branch}
758
+ and not branch.sync_with_git
759
+ and not branch_name_in_import_sync_branches(branch_short_name=short_name)
760
+ )
761
+ branch_does_not_exist_import_sync_condition = not branch and not branch_name_in_import_sync_branches(
762
+ branch_short_name=short_name
763
+ )
764
+
765
+ if branch_exists_import_sync_condition or branch_does_not_exist_import_sync_condition:
766
+ skipped_branch_names.append(short_name)
767
+ continue
768
+
769
+ filtered_branches[short_name] = branch_data
770
+
771
+ if skipped_branch_names:
772
+ log.debug(
773
+ f"Skipped the following branches {skipped_branch_names} "
774
+ f"because no match was found in import_sync_branch_names {config.SETTINGS.git.import_sync_branch_names}"
775
+ )
776
+ return filtered_branches
777
+
736
778
  async def compare_local_remote(self) -> tuple[list[str], list[str]]:
737
779
  """
738
780
  Returns:
@@ -745,7 +787,7 @@ class InfrahubRepositoryBase(BaseModel, ABC):
745
787
  # TODO move this section into a dedicated function to compare and bring in sync the remote repo with the local one.
746
788
  # It can be useful just after a clone etc ...
747
789
  local_branches = self.get_branches_from_local()
748
- remote_branches = self.get_branches_from_remote()
790
+ remote_branches = await self.get_filtered_remote_branches()
749
791
 
750
792
  new_branches = set(remote_branches.keys()) - set(local_branches.keys())
751
793
  existing_branches = set(local_branches.keys()) - new_branches
infrahub/git/models.py CHANGED
@@ -92,7 +92,8 @@ class GitRepositoryMerge(BaseModel):
92
92
  source_branch: str = Field(..., description="The source branch")
93
93
  destination_branch: str = Field(..., description="The destination branch")
94
94
  destination_branch_id: str = Field(..., description="The ID of the destination branch")
95
- default_branch: str = Field(..., description="The default branch in Git")
95
+ default_branch: str | None = Field(default=None, description="The default branch in Git")
96
+ repository_kind: str = Field(..., description="The kind of the repository.")
96
97
 
97
98
 
98
99
  class GitRepositoryImportObjects(BaseModel):
@@ -11,6 +11,7 @@ from prefect import task
11
11
  from prefect.cache_policies import NONE
12
12
  from pydantic import Field
13
13
 
14
+ from infrahub import config
14
15
  from infrahub.core.constants import InfrahubKind, RepositoryInternalStatus
15
16
  from infrahub.exceptions import RepositoryError
16
17
  from infrahub.git.integrator import InfrahubRepositoryIntegrator
@@ -170,7 +171,10 @@ class InfrahubRepository(InfrahubRepositoryIntegrator):
170
171
  commit = self.get_commit_value(branch_name=source_branch, remote=False)
171
172
 
172
173
  try:
173
- repo.git.merge(commit)
174
+ if config.SETTINGS.git.use_explicit_merge_commit:
175
+ repo.git.merge(commit, "--no-ff", m="Merged by Infrahub")
176
+ else:
177
+ repo.git.merge(commit)
174
178
  except GitCommandError as exc:
175
179
  repo.git.merge("--abort")
176
180
  raise RepositoryError(identifier=self.name, message=exc.stderr) from exc
infrahub/git/tasks.py CHANGED
@@ -502,6 +502,7 @@ async def pull_read_only(model: GitRepositoryPullReadOnly) -> None:
502
502
  flow_run_name="Merge {model.source_branch} > {model.destination_branch} in git repository",
503
503
  )
504
504
  async def merge_git_repository(model: GitRepositoryMerge) -> None:
505
+ log = get_run_logger()
505
506
  await add_tags(branches=[model.source_branch, model.destination_branch], nodes=[model.repository_id])
506
507
 
507
508
  client = get_client()
@@ -510,7 +511,11 @@ async def merge_git_repository(model: GitRepositoryMerge) -> None:
510
511
  id=model.repository_id, name=model.repository_name, client=client, default_branch_name=model.default_branch
511
512
  )
512
513
 
513
- if model.internal_status == RepositoryInternalStatus.STAGING.value:
514
+ if (
515
+ model.internal_status == RepositoryInternalStatus.STAGING.value
516
+ and model.repository_kind == InfrahubKind.REPOSITORY
517
+ ):
518
+ log.info(f"Merging {model.repository_kind}")
514
519
  repo_source = await client.get(
515
520
  kind=InfrahubKind.GENERICREPOSITORY, id=model.repository_id, branch=model.source_branch
516
521
  )
@@ -522,6 +527,28 @@ async def merge_git_repository(model: GitRepositoryMerge) -> None:
522
527
  repo_main.commit.value = commit
523
528
 
524
529
  await repo_main.save()
530
+ log.info(f"Finished merging {model.repository_kind}")
531
+
532
+ elif model.repository_kind == InfrahubKind.READONLYREPOSITORY:
533
+ repo_source = await client.get(
534
+ kind=InfrahubKind.READONLYREPOSITORY, id=model.repository_id, branch=model.source_branch
535
+ )
536
+ repo_destination = await client.get(
537
+ kind=InfrahubKind.READONLYREPOSITORY, id=model.repository_id, branch=model.destination_branch
538
+ )
539
+
540
+ if (
541
+ repo_destination.ref.value != repo_source.ref.value
542
+ or repo_destination.commit.value != repo_source.commit.value
543
+ ):
544
+ log.info(f"Merging {model.repository_kind}")
545
+
546
+ repo_destination.ref.value = repo_source.ref.value
547
+ repo_destination.commit.value = repo_source.commit.value
548
+ await repo_destination.save()
549
+
550
+ log.info(f"Finished merging {model.repository_kind}")
551
+
525
552
  else:
526
553
  async with lock.registry.get(name=model.repository_name, namespace="repository"):
527
554
  await repo.merge(source_branch=model.source_branch, dest_branch=model.destination_branch)
infrahub/git/utils.py CHANGED
@@ -1,3 +1,4 @@
1
+ import re
1
2
  from collections import defaultdict
2
3
  from typing import TYPE_CHECKING, Any
3
4
 
@@ -12,6 +13,7 @@ from infrahub.core.manager import NodeManager
12
13
  from infrahub.database import InfrahubDatabase
13
14
  from infrahub.generators.models import ProposedChangeGeneratorDefinition
14
15
 
16
+ from .. import config
15
17
  from .models import RepositoryBranchInfo, RepositoryData
16
18
 
17
19
  if TYPE_CHECKING:
@@ -168,3 +170,10 @@ async def fetch_proposed_change_generator_definition_targets(
168
170
  return await _fetch_definition_targets(
169
171
  client=client, branch=branch, group_id=definition.group_id, parameters=definition.parameters
170
172
  )
173
+
174
+
175
+ def branch_name_in_import_sync_branches(branch_short_name: str) -> bool:
176
+ for branch_filter in config.SETTINGS.git.import_sync_branch_names:
177
+ if re.fullmatch(branch_filter, branch_short_name) or branch_filter == branch_short_name:
178
+ return True
179
+ return False
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  from collections import deque
4
4
  from copy import deepcopy
5
5
  from dataclasses import dataclass, field
6
- from enum import Enum
6
+ from enum import StrEnum
7
7
  from functools import cached_property
8
8
  from typing import TYPE_CHECKING, Any
9
9
 
@@ -49,13 +49,13 @@ if TYPE_CHECKING:
49
49
  from infrahub.core.schema.schema_branch import SchemaBranch
50
50
 
51
51
 
52
- class MutateAction(str, Enum):
52
+ class MutateAction(StrEnum):
53
53
  CREATE = "create"
54
54
  DELETE = "delete"
55
55
  UPDATE = "update"
56
56
 
57
57
 
58
- class ContextType(str, Enum):
58
+ class ContextType(StrEnum):
59
59
  EDGE = "edge"
60
60
  NODE = "node"
61
61
  DIRECT = "direct"
@@ -80,7 +80,7 @@ class ContextType(str, Enum):
80
80
  return cls.NODE
81
81
 
82
82
 
83
- class GraphQLOperation(str, Enum):
83
+ class GraphQLOperation(StrEnum):
84
84
  QUERY = "query"
85
85
  MUTATION = "mutation"
86
86
  SUBSCRIPTION = "subscription"
@@ -110,7 +110,7 @@ class UpdateComputedAttribute(Mutation):
110
110
  event = NodeUpdatedEvent(
111
111
  kind=node_schema.kind,
112
112
  node_id=target_node.get_id(),
113
- changelog=target_node.node_changelog.model_dump(),
113
+ changelog=target_node.node_changelog,
114
114
  fields=[str(data.attribute)],
115
115
  meta=EventMeta(
116
116
  context=graphql_context.get_context(),
@@ -76,7 +76,7 @@ class ConvertObjectType(Mutation):
76
76
 
77
77
  if target_schema.kind in [REPOSITORY, READONLYREPOSITORY]:
78
78
  new_node = await convert_repository_type(
79
- repository=node_to_convert,
79
+ repository=node_to_convert, # type: ignore[arg-type]
80
80
  target_schema=target_schema,
81
81
  mapping=fields_mapping,
82
82
  branch=graphql_context.branch,
@@ -101,7 +101,7 @@ class UpdateDisplayLabel(Mutation):
101
101
  event = NodeUpdatedEvent(
102
102
  kind=node_schema.kind,
103
103
  node_id=target_node.get_id(),
104
- changelog=target_node.node_changelog.model_dump(),
104
+ changelog=target_node.node_changelog,
105
105
  fields=["display_label"],
106
106
  meta=EventMeta(
107
107
  context=graphql_context.get_context(),
@@ -108,7 +108,7 @@ class UpdateHFID(Mutation):
108
108
  event = NodeUpdatedEvent(
109
109
  kind=node_schema.kind,
110
110
  node_id=target_node.get_id(),
111
- changelog=target_node.node_changelog.model_dump(),
111
+ changelog=target_node.node_changelog,
112
112
  fields=["human_friendly_id"],
113
113
  meta=EventMeta(
114
114
  context=graphql_context.get_context(),
@@ -48,7 +48,7 @@ async def validate_namespace(
48
48
  namespace_id = namespace.id
49
49
  elif "hfid" in data["ip_namespace"]:
50
50
  namespace = await registry.manager.get_one_by_hfid(
51
- db=db, branch=branch, kind=InfrahubKind.IPNAMESPACE, hfid=data["ip_namespace"]["hfid"]
51
+ db=db, branch=branch, kind=InfrahubKind.IPNAMESPACE, hfid=data["ip_namespace"]["hfid"], raise_on_error=True
52
52
  )
53
53
  namespace_id = namespace.id
54
54
  else:
@@ -186,6 +186,7 @@ class InfrahubProfilesRefresh(Mutation):
186
186
  branch=branch,
187
187
  id=str(data.id),
188
188
  include_source=True,
189
+ raise_on_error=True,
189
190
  )
190
191
  node_profiles_applier = NodeProfilesApplier(db=db, branch=branch)
191
192
  updated_fields = await node_profiles_applier.apply_profiles(node=obj)
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from enum import Enum
3
+ from enum import StrEnum
4
4
  from typing import TYPE_CHECKING, Self
5
5
 
6
6
  from graphene import Boolean, InputField, InputObjectType, List, Mutation, String
@@ -48,7 +48,7 @@ if TYPE_CHECKING:
48
48
  RELATIONSHIP_PEERS_TO_IGNORE = [InfrahubKind.NODE]
49
49
 
50
50
 
51
- class GroupUpdateType(str, Enum):
51
+ class GroupUpdateType(StrEnum):
52
52
  NONE = "none"
53
53
  MEMBERS = "members"
54
54
  MEMBER_OF_GROUPS = "member_of_groups"
@@ -122,7 +122,7 @@ class IPAddressPoolGetResource(Mutation):
122
122
  ) -> Self:
123
123
  graphql_context: GraphqlContext = info.context
124
124
 
125
- obj: CoreIPAddressPool = await registry.manager.find_object(
125
+ obj: CoreIPAddressPool = await registry.manager.find_object( # type: ignore[assignment]
126
126
  db=graphql_context.db,
127
127
  kind=InfrahubKind.IPADDRESSPOOL,
128
128
  id=data.get("id"),
@@ -1,5 +1,5 @@
1
1
  from .account import AccountPermissions, AccountToken
2
- from .branch import BranchQueryList
2
+ from .branch import BranchQueryList, InfrahubBranchQueryList
3
3
  from .internal import InfrahubInfo
4
4
  from .ipam import (
5
5
  DeprecatedIPAddressGetNextAvailable,
@@ -20,6 +20,7 @@ __all__ = [
20
20
  "BranchQueryList",
21
21
  "DeprecatedIPAddressGetNextAvailable",
22
22
  "DeprecatedIPPrefixGetNextAvailable",
23
+ "InfrahubBranchQueryList",
23
24
  "InfrahubIPAddressGetNextAvailable",
24
25
  "InfrahubIPPrefixGetNextAvailable",
25
26
  "InfrahubInfo",
@@ -2,10 +2,12 @@ from __future__ import annotations
2
2
 
3
3
  from typing import TYPE_CHECKING, Any
4
4
 
5
- from graphene import ID, Field, List, NonNull, String
5
+ from graphene import ID, Field, Int, List, NonNull, String
6
6
 
7
+ from infrahub.core.registry import registry
8
+ from infrahub.exceptions import ValidationError
7
9
  from infrahub.graphql.field_extractor import extract_graphql_fields
8
- from infrahub.graphql.types import BranchType
10
+ from infrahub.graphql.types import BranchType, InfrahubBranch, InfrahubBranchType
9
11
 
10
12
  if TYPE_CHECKING:
11
13
  from graphql import GraphQLResolveInfo
@@ -17,7 +19,7 @@ async def branch_resolver(
17
19
  **kwargs: Any,
18
20
  ) -> list[dict[str, Any]]:
19
21
  fields = extract_graphql_fields(info)
20
- return await BranchType.get_list(graphql_context=info.context, fields=fields, **kwargs)
22
+ return await BranchType.get_list(graphql_context=info.context, fields=fields, exclude_global=True, **kwargs)
21
23
 
22
24
 
23
25
  BranchQueryList = Field(
@@ -28,3 +30,56 @@ BranchQueryList = Field(
28
30
  resolver=branch_resolver,
29
31
  required=True,
30
32
  )
33
+
34
+
35
+ async def infrahub_branch_resolver(
36
+ root: dict, # noqa: ARG001
37
+ info: GraphQLResolveInfo,
38
+ limit: int | None = None,
39
+ offset: int | None = None,
40
+ name__value: str | None = None,
41
+ ids: list[str] | None = None,
42
+ ) -> dict[str, Any]:
43
+ if isinstance(limit, int) and limit < 1:
44
+ raise ValidationError("limit must be >= 1")
45
+ if isinstance(offset, int) and offset < 0:
46
+ raise ValidationError("offset must be >= 0")
47
+
48
+ fields = extract_graphql_fields(info)
49
+ result: dict[str, Any] = {}
50
+ if "edges" in fields:
51
+ branches = await InfrahubBranch.get_list(
52
+ graphql_context=info.context,
53
+ fields=fields.get("edges", {}).get("node", {}),
54
+ limit=limit,
55
+ offset=offset,
56
+ name=name__value,
57
+ ids=ids,
58
+ exclude_global=True,
59
+ )
60
+ result["edges"] = [{"node": branch} for branch in branches]
61
+ if "count" in fields:
62
+ result["count"] = await InfrahubBranchType.get_list_count(
63
+ graphql_context=info.context, name=name__value, ids=ids
64
+ )
65
+
66
+ if "default_branch" in fields:
67
+ result["default_branch"] = await InfrahubBranch.get_by_name(
68
+ graphql_context=info.context,
69
+ fields=fields["default_branch"],
70
+ name=registry.default_branch,
71
+ )
72
+
73
+ return result
74
+
75
+
76
+ InfrahubBranchQueryList = Field(
77
+ InfrahubBranchType,
78
+ offset=Int(),
79
+ limit=Int(),
80
+ name__value=String(),
81
+ ids=List(ID),
82
+ description="Retrieve paginated information about active branches.",
83
+ resolver=infrahub_branch_resolver,
84
+ required=True,
85
+ )
@@ -8,6 +8,7 @@ from netaddr import IPSet
8
8
 
9
9
  from infrahub.core.constants import InfrahubKind
10
10
  from infrahub.core.manager import NodeManager
11
+ from infrahub.core.protocols import BuiltinIPPrefix
11
12
  from infrahub.core.query.ipam import get_ip_addresses, get_subnets
12
13
  from infrahub.exceptions import NodeNotFoundError, ValidationError
13
14
  from infrahub.pools.address import get_available
@@ -31,7 +32,9 @@ class IPAddressGetNextAvailable(ObjectType):
31
32
  ) -> dict[str, str]:
32
33
  graphql_context: GraphqlContext = info.context
33
34
 
34
- prefix = await NodeManager.get_one(id=prefix_id, db=graphql_context.db, branch=graphql_context.branch)
35
+ prefix = await NodeManager.get_one(
36
+ id=prefix_id, kind=BuiltinIPPrefix, db=graphql_context.db, branch=graphql_context.branch
37
+ )
35
38
 
36
39
  if not prefix:
37
40
  raise NodeNotFoundError(
@@ -78,17 +81,19 @@ class IPPrefixGetNextAvailable(ObjectType):
78
81
  ) -> dict[str, str]:
79
82
  graphql_context: GraphqlContext = info.context
80
83
 
81
- prefix = await NodeManager.get_one(id=prefix_id, db=graphql_context.db, branch=graphql_context.branch)
84
+ prefix = await NodeManager.get_one(
85
+ id=prefix_id, db=graphql_context.db, branch=graphql_context.branch, kind=BuiltinIPPrefix
86
+ )
82
87
 
83
88
  if not prefix:
84
89
  raise NodeNotFoundError(
85
90
  branch_name=graphql_context.branch.name, node_type=InfrahubKind.IPPREFIX, identifier=prefix_id
86
91
  )
87
92
 
88
- namespace = await prefix.ip_namespace.get_peer(db=graphql_context.db) # type: ignore[attr-defined]
93
+ namespace = await prefix.ip_namespace.get_peer(db=graphql_context.db)
89
94
  subnets = await get_subnets(
90
95
  db=graphql_context.db,
91
- ip_prefix=ipaddress.ip_network(prefix.prefix.value), # type: ignore[attr-defined]
96
+ ip_prefix=ipaddress.ip_network(prefix.prefix.value),
92
97
  namespace=namespace,
93
98
  branch=graphql_context.branch,
94
99
  )
@@ -23,7 +23,6 @@ if TYPE_CHECKING:
23
23
 
24
24
  from infrahub.core.branch import Branch
25
25
  from infrahub.core.node import Node
26
- from infrahub.core.protocols import CoreNode
27
26
  from infrahub.core.timestamp import Timestamp
28
27
  from infrahub.database import InfrahubDatabase
29
28
  from infrahub.graphql.initialization import GraphqlContext
@@ -59,7 +58,7 @@ class PoolAllocatedEdge(ObjectType):
59
58
  node = Field(PoolAllocatedNode, required=True)
60
59
 
61
60
 
62
- def _validate_pool_type(pool_id: str, pool: CoreNode | None = None) -> CoreNode:
61
+ def _validate_pool_type(pool_id: str, pool: Node | None = None) -> Node:
63
62
  if not pool or pool.get_kind() not in [
64
63
  InfrahubKind.IPADDRESSPOOL,
65
64
  InfrahubKind.IPPREFIXPOOL,
@@ -83,9 +82,7 @@ class PoolAllocated(ObjectType):
83
82
  limit: int = 10,
84
83
  ) -> dict:
85
84
  graphql_context: GraphqlContext = info.context
86
- pool: CoreNode | None = await NodeManager.get_one(
87
- id=pool_id, db=graphql_context.db, branch=graphql_context.branch
88
- )
85
+ pool = await NodeManager.get_one(id=pool_id, db=graphql_context.db, branch=graphql_context.branch)
89
86
 
90
87
  fields = extract_graphql_fields(info=info)
91
88
 
@@ -190,7 +187,7 @@ class PoolUtilization(ObjectType):
190
187
  ) -> dict:
191
188
  graphql_context: GraphqlContext = info.context
192
189
  db: InfrahubDatabase = graphql_context.db
193
- pool: CoreNode | None = await NodeManager.get_one(id=pool_id, db=db, branch=graphql_context.branch)
190
+ pool = await NodeManager.get_one(id=pool_id, db=db, branch=graphql_context.branch)
194
191
  pool = _validate_pool_type(pool_id=pool_id, pool=pool)
195
192
  if pool.get_kind() == "CoreNumberPool":
196
193
  return await resolve_number_pool_utilization(
@@ -275,7 +272,7 @@ class PoolUtilization(ObjectType):
275
272
 
276
273
 
277
274
  async def resolve_number_pool_allocation(
278
- db: InfrahubDatabase, graphql_context: GraphqlContext, pool: CoreNode, fields: dict, offset: int, limit: int
275
+ db: InfrahubDatabase, graphql_context: GraphqlContext, pool: Node, fields: dict, offset: int, limit: int
279
276
  ) -> dict:
280
277
  response: dict[str, Any] = {}
281
278
  query = await NumberPoolGetAllocated.init(
@@ -304,7 +301,7 @@ async def resolve_number_pool_allocation(
304
301
 
305
302
 
306
303
  async def resolve_number_pool_utilization(
307
- db: InfrahubDatabase, pool: CoreNode, at: Timestamp | str | None, branch: Branch
304
+ db: InfrahubDatabase, pool: Node, at: Timestamp | str | None, branch: Branch
308
305
  ) -> dict:
309
306
  """
310
307
  Returns a mapping containg utilization info of a number pool.
@@ -13,7 +13,7 @@ from infrahub.graphql.field_extractor import extract_graphql_fields
13
13
  if TYPE_CHECKING:
14
14
  from graphql import GraphQLResolveInfo
15
15
 
16
- from infrahub.core.protocols import CoreNode
16
+ from infrahub.core.node import Node as InfrahubNode
17
17
  from infrahub.graphql.initialization import GraphqlContext
18
18
 
19
19
 
@@ -106,12 +106,12 @@ async def search_resolver(
106
106
  ) -> dict[str, Any]:
107
107
  graphql_context: GraphqlContext = info.context
108
108
  response: dict[str, Any] = {}
109
- results: list[CoreNode] = []
109
+ results: list[InfrahubNode] = []
110
110
 
111
111
  fields = extract_graphql_fields(info=info)
112
112
 
113
113
  if is_valid_uuid(q):
114
- matching: CoreNode | None = await NodeManager.get_one(
114
+ matching = await NodeManager.get_one(
115
115
  db=graphql_context.db, branch=graphql_context.branch, at=graphql_context.at, id=q
116
116
  )
117
117
  if matching:
@@ -39,6 +39,7 @@ from .queries import (
39
39
  BranchQueryList,
40
40
  DeprecatedIPAddressGetNextAvailable,
41
41
  DeprecatedIPPrefixGetNextAvailable,
42
+ InfrahubBranchQueryList,
42
43
  InfrahubInfo,
43
44
  InfrahubIPAddressGetNextAvailable,
44
45
  InfrahubIPPrefixGetNextAvailable,
@@ -65,6 +66,7 @@ class InfrahubBaseQuery(ObjectType):
65
66
 
66
67
  Relationship = Relationship
67
68
 
69
+ InfrahubBranch = InfrahubBranchQueryList
68
70
  InfrahubInfo = InfrahubInfo
69
71
  InfrahubStatus = InfrahubStatus
70
72
 
@@ -21,7 +21,7 @@ from .attribute import (
21
21
  StrAttributeType,
22
22
  TextAttributeType,
23
23
  )
24
- from .branch import BranchType
24
+ from .branch import BranchType, InfrahubBranch, InfrahubBranchType
25
25
  from .interface import InfrahubInterface
26
26
  from .node import InfrahubObject
27
27
  from .permission import PaginatedObjectPermission
@@ -41,6 +41,8 @@ __all__ = [
41
41
  "DropdownType",
42
42
  "IPHostType",
43
43
  "IPNetworkType",
44
+ "InfrahubBranch",
45
+ "InfrahubBranchType",
44
46
  "InfrahubInterface",
45
47
  "InfrahubObject",
46
48
  "InfrahubObjectType",