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/api/artifact.py CHANGED
@@ -17,7 +17,7 @@ from infrahub.core import registry
17
17
  from infrahub.core.account import ObjectPermission
18
18
  from infrahub.core.branch.needs_rebase_status import check_need_rebase_status
19
19
  from infrahub.core.constants import GLOBAL_BRANCH_NAME, InfrahubKind, PermissionAction
20
- from infrahub.core.protocols import CoreArtifactDefinition
20
+ from infrahub.core.protocols import CoreArtifact, CoreArtifactDefinition
21
21
  from infrahub.database import InfrahubDatabase # noqa: TC001
22
22
  from infrahub.exceptions import NodeNotFoundError
23
23
  from infrahub.git.models import RequestArtifactDefinitionGenerate
@@ -50,14 +50,16 @@ async def get_artifact(
50
50
  branch_params: BranchParams = Depends(get_branch_params),
51
51
  _: AccountSession = Depends(get_current_user),
52
52
  ) -> Response:
53
- artifact = await registry.manager.get_one(db=db, id=artifact_id, branch=branch_params.branch, at=branch_params.at)
53
+ artifact = await registry.manager.get_one(
54
+ db=db, id=artifact_id, branch=branch_params.branch, at=branch_params.at, kind=CoreArtifact
55
+ )
54
56
  if not artifact:
55
57
  raise NodeNotFoundError(
56
58
  branch_name=branch_params.branch.name, node_type=InfrahubKind.ARTIFACT, identifier=artifact_id
57
59
  )
58
60
 
59
61
  return Response(
60
- content=registry.storage.retrieve(identifier=artifact.storage_id.value),
62
+ content=registry.storage.retrieve(identifier=str(artifact.storage_id.value)),
61
63
  headers={"Content-Type": artifact.content_type.value.value},
62
64
  )
63
65
 
infrahub/auth.py CHANGED
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  import uuid
4
4
  from datetime import UTC, datetime, timedelta
5
- from enum import Enum
5
+ from enum import StrEnum
6
6
  from typing import TYPE_CHECKING, Any
7
7
 
8
8
  import bcrypt
@@ -20,7 +20,7 @@ from infrahub.core.account import validate_token
20
20
  from infrahub.core.constants import AccountStatus, InfrahubKind
21
21
  from infrahub.core.manager import NodeManager
22
22
  from infrahub.core.node import Node
23
- from infrahub.core.protocols import CoreAccount, CoreAccountGroup
23
+ from infrahub.core.protocols import CoreAccount, CoreAccountGroup, CoreGenericAccount
24
24
  from infrahub.core.registry import registry
25
25
  from infrahub.exceptions import AuthorizationError, GatewayError, NodeNotFoundError
26
26
  from infrahub.log import get_logger
@@ -28,14 +28,13 @@ from infrahub.log import get_logger
28
28
  if TYPE_CHECKING:
29
29
  import httpx
30
30
 
31
- from infrahub.core.protocols import CoreGenericAccount
32
31
  from infrahub.database import InfrahubDatabase
33
32
  from infrahub.services import InfrahubServices
34
33
 
35
34
  log = get_logger()
36
35
 
37
36
 
38
- class AuthType(str, Enum):
37
+ class AuthType(StrEnum):
39
38
  NONE = "none"
40
39
  JWT = "jwt"
41
40
  API = "api"
@@ -53,7 +52,7 @@ class AccountSession(BaseModel):
53
52
 
54
53
 
55
54
  async def validate_active_account(db: InfrahubDatabase, account_id: str) -> None:
56
- account: CoreGenericAccount = await NodeManager.get_one(db=db, id=account_id, raise_on_error=True)
55
+ account = await NodeManager.get_one(db=db, kind=CoreGenericAccount, id=account_id, raise_on_error=True)
57
56
  if account.status.value != AccountStatus.ACTIVE.value:
58
57
  raise AuthorizationError("This account has been deactivated")
59
58
 
@@ -114,7 +113,7 @@ async def create_fresh_access_token(
114
113
  if not refresh_token:
115
114
  raise AuthorizationError("The provided refresh token has been invalidated in the database")
116
115
 
117
- account: CoreGenericAccount | None = await NodeManager.get_one(id=refresh_data.account_id, db=db)
116
+ account = await NodeManager.get_one(id=refresh_data.account_id, kind=CoreGenericAccount, db=db)
118
117
  if not account:
119
118
  raise NodeNotFoundError(
120
119
  branch_name=selected_branch.name,
infrahub/cli/db.py CHANGED
@@ -5,7 +5,7 @@ import os
5
5
  from collections import defaultdict
6
6
  from csv import DictReader, DictWriter
7
7
  from datetime import UTC, datetime
8
- from enum import Enum
8
+ from enum import StrEnum
9
9
  from pathlib import Path
10
10
  from typing import TYPE_CHECKING, Any, Sequence
11
11
 
@@ -74,13 +74,13 @@ app.add_typer(patch_app, name="patch")
74
74
  PERMISSIONS_AVAILABLE = ["read", "write", "admin"]
75
75
 
76
76
 
77
- class ConstraintAction(str, Enum):
77
+ class ConstraintAction(StrEnum):
78
78
  SHOW = "show"
79
79
  ADD = "add"
80
80
  DROP = "drop"
81
81
 
82
82
 
83
- class IndexAction(str, Enum):
83
+ class IndexAction(StrEnum):
84
84
  SHOW = "show"
85
85
  ADD = "add"
86
86
  DROP = "drop"
@@ -1,5 +1,5 @@
1
1
  from dataclasses import dataclass
2
- from enum import Enum
2
+ from enum import StrEnum
3
3
  from typing import Any
4
4
 
5
5
  from rich import print as rprint
@@ -11,7 +11,7 @@ from infrahub.core.query import Query, QueryType
11
11
  from infrahub.database import InfrahubDatabase
12
12
 
13
13
 
14
- class SchemaFieldType(str, Enum):
14
+ class SchemaFieldType(StrEnum):
15
15
  ATTRIBUTE = "attribute"
16
16
  RELATIONSHIP = "relationship"
17
17
 
infrahub/cli/dev.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import importlib
4
+ import json
4
5
  import logging
5
6
  from pathlib import Path # noqa: TC003
6
7
  from typing import TYPE_CHECKING
@@ -11,6 +12,7 @@ from infrahub_sdk.async_typer import AsyncTyper
11
12
  from rich.logging import RichHandler
12
13
 
13
14
  from infrahub import config
15
+ from infrahub.api.schema import SchemaLoadAPI
14
16
  from infrahub.core.initialization import (
15
17
  first_time_initialization,
16
18
  initialization,
@@ -21,6 +23,7 @@ from infrahub.core.utils import delete_all_nodes
21
23
  from infrahub.graphql.manager import GraphQLSchemaManager
22
24
  from infrahub.graphql.schema_sort import sort_schema_ast
23
25
  from infrahub.log import get_logger
26
+ from infrahub.server import app as server_app
24
27
 
25
28
  if TYPE_CHECKING:
26
29
  from infrahub.cli.context import CliContext
@@ -57,6 +60,33 @@ async def export_graphql_schema(
57
60
  out.write_text(sorted_schema_str)
58
61
 
59
62
 
63
+ @app.command(name="export-json-schema")
64
+ async def export_json_schema(
65
+ ctx: typer.Context, # noqa: ARG001
66
+ out: Path = typer.Option("openapi.json"), # noqa: B008
67
+ ) -> None:
68
+ """Export the REST API OpenAPI schema to a file."""
69
+ openapi_dict = server_app.openapi()
70
+ openapi_dict["info"]["version"] = "latest"
71
+ content = json.dumps(openapi_dict, indent=4)
72
+ out.write_text(content)
73
+
74
+
75
+ @app.command(name="export-node-schema")
76
+ async def export_node_schema(
77
+ ctx: typer.Context, # noqa: ARG001
78
+ config_file: str = typer.Option("infrahub.toml", envvar="INFRAHUB_CONFIG"),
79
+ out: Path = typer.Option("develop.json"), # noqa: B008
80
+ ) -> None:
81
+ """Export the repository configuration to a file."""
82
+ config.load_and_exit(config_file_name=config_file)
83
+ schema = SchemaLoadAPI.model_json_schema()
84
+ schema["title"] = "InfrahubSchema"
85
+ content = json.dumps(schema, indent=4)
86
+ out.parent.mkdir(parents=True, exist_ok=True)
87
+ out.write_text(content)
88
+
89
+
60
90
  @app.command(name="db-init")
61
91
  async def database_init(
62
92
  ctx: typer.Context,
infrahub/config.py CHANGED
@@ -1,18 +1,20 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import os
4
+ import re
4
5
  import ssl
5
6
  import sys
7
+ import tomllib
6
8
  from dataclasses import dataclass
7
- from enum import Enum
9
+ from enum import StrEnum
8
10
  from pathlib import Path
9
11
  from typing import TYPE_CHECKING, Any
10
12
 
11
- import tomllib
12
13
  from infrahub_sdk.utils import generate_uuid
13
14
  from pydantic import (
14
15
  AliasChoices,
15
16
  BaseModel,
17
+ EmailStr,
16
18
  Field,
17
19
  PrivateAttr,
18
20
  ValidationError,
@@ -48,28 +50,28 @@ def default_append_git_suffix_domains() -> list[str]:
48
50
  return ["github.com", "gitlab.com"]
49
51
 
50
52
 
51
- class EnterpriseFeatures(str, Enum):
53
+ class EnterpriseFeatures(StrEnum):
52
54
  PROPOSED_CHANGE_REQUIRE_APPROVAL = "proposed_change_require_approval"
53
55
  REVOKE_PROPOSED_CHANGE_APPROVALS = "revoke_proposed_change_approvals"
54
56
 
55
57
 
56
- class UserInfoMethod(str, Enum):
58
+ class UserInfoMethod(StrEnum):
57
59
  POST = "post"
58
60
  GET = "get"
59
61
 
60
62
 
61
- class SSOProtocol(str, Enum):
63
+ class SSOProtocol(StrEnum):
62
64
  OAUTH2 = "oauth2"
63
65
  OIDC = "oidc"
64
66
 
65
67
 
66
- class Oauth2Provider(str, Enum):
68
+ class Oauth2Provider(StrEnum):
67
69
  GOOGLE = "google"
68
70
  PROVIDER1 = "provider1"
69
71
  PROVIDER2 = "provider2"
70
72
 
71
73
 
72
- class OIDCProvider(str, Enum):
74
+ class OIDCProvider(StrEnum):
73
75
  GOOGLE = "google"
74
76
  PROVIDER1 = "provider1"
75
77
  PROVIDER2 = "provider2"
@@ -98,25 +100,25 @@ class SSOProviderInfo(BaseModel):
98
100
  return f"/api/{self.protocol.value}/{self.name}/token"
99
101
 
100
102
 
101
- class StorageDriver(str, Enum):
103
+ class StorageDriver(StrEnum):
102
104
  FileSystemStorage = "local"
103
105
  InfrahubS3ObjectStorage = "s3"
104
106
 
105
107
 
106
- class TraceExporterType(str, Enum):
108
+ class TraceExporterType(StrEnum):
107
109
  CONSOLE = "console"
108
110
  OTLP = "otlp"
109
111
  # JAEGER = "jaeger"
110
112
  # ZIPKIN = "zipkin"
111
113
 
112
114
 
113
- class TraceTransportProtocol(str, Enum):
115
+ class TraceTransportProtocol(StrEnum):
114
116
  GRPC = "grpc"
115
117
  HTTP_PROTOBUF = "http/protobuf"
116
118
  # HTTP_JSON = "http/json"
117
119
 
118
120
 
119
- class BrokerDriver(str, Enum):
121
+ class BrokerDriver(StrEnum):
120
122
  RabbitMQ = "rabbitmq"
121
123
  NATS = "nats"
122
124
 
@@ -137,7 +139,7 @@ class BrokerDriver(str, Enum):
137
139
  return "RabbitMQMessageBus"
138
140
 
139
141
 
140
- class CacheDriver(str, Enum):
142
+ class CacheDriver(StrEnum):
141
143
  Redis = "redis"
142
144
  NATS = "nats"
143
145
 
@@ -158,12 +160,12 @@ class CacheDriver(str, Enum):
158
160
  return "RedisCache"
159
161
 
160
162
 
161
- class WorkflowDriver(str, Enum):
163
+ class WorkflowDriver(StrEnum):
162
164
  LOCAL = "local"
163
165
  WORKER = "worker"
164
166
 
165
167
 
166
- class ExtraLogLevel(str, Enum):
168
+ class ExtraLogLevel(StrEnum):
167
169
  CRITICAL = "CRITICAL"
168
170
  ERROR = "ERROR"
169
171
  WARNING = "WARNING"
@@ -324,6 +326,10 @@ class DevelopmentSettings(BaseSettings):
324
326
  default=False,
325
327
  description="Allow enterprise configuration in development mode, this will not enable the features just allow the configuration.",
326
328
  )
329
+ git_credential_helper: str = Field(
330
+ default="/usr/local/bin/infrahub-git-credential",
331
+ description="Location of git credential helper",
332
+ )
327
333
 
328
334
 
329
335
  class BrokerSettings(BaseSettings):
@@ -449,6 +455,48 @@ class GitSettings(BaseSettings):
449
455
  default_factory=default_append_git_suffix_domains,
450
456
  description="Automatically append '.git' to HTTP URLs if for these domains.",
451
457
  )
458
+ import_sync_branch_names: list[str] = Field(
459
+ default_factory=list,
460
+ description=(
461
+ "Names or regex of branches to be created in infrahub during import "
462
+ "e.g. 'infrahub/.*', 'release/.*', '^branch-'. "
463
+ "Note: other branches created with sync with git will be imported also"
464
+ ),
465
+ )
466
+ user_name: str = Field(
467
+ default="Infrahub",
468
+ description=(
469
+ "User name of the git user. This will be used as the user name when Infrahub commits code to a repository"
470
+ ),
471
+ )
472
+ user_email: EmailStr = Field(
473
+ default="infrahub@opsmill.com",
474
+ description=(
475
+ "Email of the git user. This will be used as the user email when Infrahub commits code to a repository"
476
+ ),
477
+ )
478
+ global_config_file: str = Field(
479
+ default="/opt/infrahub/.gitconfig",
480
+ description=(
481
+ "The location of the git config file. "
482
+ "This will be set as the system `GIT_CONFIG_GLOBAL` environment variable "
483
+ "if the environment variable is not initially set"
484
+ ),
485
+ )
486
+ use_explicit_merge_commit: bool = Field(
487
+ default=False, description="Whether to allow explicit merge commits when infrahub merges branches"
488
+ )
489
+
490
+ @model_validator(mode="after")
491
+ def validate_sync_branch_names(self) -> Self:
492
+ for branch_filter in self.import_sync_branch_names:
493
+ try:
494
+ re.compile(branch_filter)
495
+ except re.error as exc:
496
+ raise ValueError(
497
+ f"Invalid regex pattern for import_sync_branch_names: '{branch_filter}' — {exc}"
498
+ ) from exc
499
+ return self
452
500
 
453
501
 
454
502
  class HTTPSettings(BaseSettings):
@@ -1,12 +1,12 @@
1
- from enum import Enum
1
+ from enum import StrEnum
2
2
 
3
3
 
4
- class DatabaseType(str, Enum):
4
+ class DatabaseType(StrEnum):
5
5
  NEO4J = "neo4j"
6
6
  MEMGRAPH = "memgraph"
7
7
 
8
8
 
9
- class Neo4jRuntime(str, Enum):
9
+ class Neo4jRuntime(StrEnum):
10
10
  DEFAULT = "default"
11
11
  INTERPRETED = "interpreted"
12
12
  SLOTTED = "slotted"
@@ -15,13 +15,13 @@ class Neo4jRuntime(str, Enum):
15
15
  UNDEFINED = "undefined"
16
16
 
17
17
 
18
- class IndexType(str, Enum):
18
+ class IndexType(StrEnum):
19
19
  TEXT = "text"
20
20
  RANGE = "range"
21
21
  LOOKUP = "lookup"
22
22
  NOT_APPLICABLE = "not_applicable"
23
23
 
24
24
 
25
- class EntityType(str, Enum):
25
+ class EntityType(StrEnum):
26
26
  NODE = "node"
27
27
  RELATIONSHIP = "relationship"
@@ -1,8 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import re
4
- from typing import TYPE_CHECKING, Any, Optional, Self, Union
4
+ from typing import TYPE_CHECKING, Any, Optional, Self, Union, cast
5
5
 
6
+ from neo4j.graph import Node as Neo4jNode
6
7
  from pydantic import Field, field_validator
7
8
 
8
9
  from infrahub.core.branch.enums import BranchStatus
@@ -10,8 +11,9 @@ from infrahub.core.constants import GLOBAL_BRANCH_NAME
10
11
  from infrahub.core.graph import GRAPH_VERSION
11
12
  from infrahub.core.models import SchemaBranchHash # noqa: TC001
12
13
  from infrahub.core.node.standard import StandardNode
13
- from infrahub.core.query import QueryType
14
+ from infrahub.core.query import Query, QueryType
14
15
  from infrahub.core.query.branch import (
16
+ BranchNodeGetListQuery,
15
17
  DeleteBranchRelationshipsQuery,
16
18
  GetAllBranchInternalRelationshipQuery,
17
19
  RebaseBranchDeleteRelationshipQuery,
@@ -159,12 +161,28 @@ class Branch(StandardNode):
159
161
  limit: int = 1000,
160
162
  ids: list[str] | None = None,
161
163
  name: str | None = None,
162
- **kwargs: dict[str, Any],
164
+ **kwargs: Any,
163
165
  ) -> list[Self]:
164
- branches = await super().get_list(db=db, limit=limit, ids=ids, name=name, **kwargs)
165
- branches = [branch for branch in branches if branch.status != BranchStatus.DELETING]
166
+ query: Query = await BranchNodeGetListQuery.init(
167
+ db=db, node_class=cls, ids=ids, node_name=name, limit=limit, **kwargs
168
+ )
169
+ await query.execute(db=db)
170
+
171
+ return [cls.from_db(node=cast(Neo4jNode, result.get("n"))) for result in query.get_results()]
166
172
 
167
- return branches
173
+ @classmethod
174
+ async def get_list_count(
175
+ cls,
176
+ db: InfrahubDatabase,
177
+ limit: int = 1000,
178
+ ids: list[str] | None = None,
179
+ name: str | None = None,
180
+ **kwargs: Any,
181
+ ) -> int:
182
+ query: Query = await BranchNodeGetListQuery.init(
183
+ db=db, node_class=cls, ids=ids, node_name=name, limit=limit, exclude_global=True, **kwargs
184
+ )
185
+ return await query.count(db=db)
168
186
 
169
187
  @classmethod
170
188
  def isinstance(cls, obj: Any) -> bool:
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- from enum import Enum
3
+ from enum import Enum, StrEnum
4
4
  from typing import Any
5
5
 
6
6
  from pydantic import BaseModel, ConfigDict, Field
@@ -271,7 +271,7 @@ class SchemaConflict(ObjectConflict):
271
271
  return self.value
272
272
 
273
273
 
274
- class DiffElementType(str, Enum):
274
+ class DiffElementType(StrEnum):
275
275
  ATTRIBUTE = "Attribute"
276
276
  RELATIONSHIP_ONE = "RelationshipOne"
277
277
  RELATIONSHIP_MANY = "RelationshipMany"
@@ -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, ForwardRef, Optional, Union, get_origin
5
5
 
6
6
  from pydantic import BaseModel
@@ -12,7 +12,7 @@ if TYPE_CHECKING:
12
12
  from infrahub.database import InfrahubDatabase
13
13
 
14
14
 
15
- class GraphPropertyType(str, Enum):
15
+ class GraphPropertyType(StrEnum):
16
16
  BOOLEAN = "BOOLEAN"
17
17
  STRING = "STRING"
18
18
  INTEGER = "INTEGER"