infrahub-server 1.6.0b0__py3-none-any.whl → 1.6.1__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 (88) hide show
  1. infrahub/api/oauth2.py +33 -6
  2. infrahub/api/oidc.py +36 -6
  3. infrahub/auth.py +11 -0
  4. infrahub/auth_pkce.py +41 -0
  5. infrahub/config.py +9 -3
  6. infrahub/core/branch/models.py +3 -2
  7. infrahub/core/changelog/models.py +2 -2
  8. infrahub/core/constants/__init__.py +1 -0
  9. infrahub/core/graph/__init__.py +1 -1
  10. infrahub/core/integrity/object_conflict/conflict_recorder.py +1 -1
  11. infrahub/core/manager.py +36 -31
  12. infrahub/core/migrations/graph/__init__.py +2 -0
  13. infrahub/core/migrations/graph/m047_backfill_or_null_display_label.py +606 -0
  14. infrahub/core/models.py +5 -6
  15. infrahub/core/node/__init__.py +16 -13
  16. infrahub/core/node/create.py +36 -8
  17. infrahub/core/node/proposed_change.py +5 -3
  18. infrahub/core/node/standard.py +1 -1
  19. infrahub/core/protocols.py +1 -7
  20. infrahub/core/query/attribute.py +1 -1
  21. infrahub/core/query/node.py +9 -5
  22. infrahub/core/relationship/model.py +21 -4
  23. infrahub/core/schema/generic_schema.py +1 -1
  24. infrahub/core/schema/manager.py +8 -3
  25. infrahub/core/schema/schema_branch.py +35 -16
  26. infrahub/core/validators/attribute/choices.py +2 -2
  27. infrahub/core/validators/determiner.py +3 -6
  28. infrahub/database/__init__.py +1 -1
  29. infrahub/git/base.py +2 -3
  30. infrahub/git/models.py +13 -0
  31. infrahub/git/tasks.py +23 -19
  32. infrahub/git/utils.py +16 -9
  33. infrahub/graphql/app.py +6 -6
  34. infrahub/graphql/loaders/peers.py +6 -0
  35. infrahub/graphql/mutations/action.py +15 -7
  36. infrahub/graphql/mutations/hfid.py +1 -1
  37. infrahub/graphql/mutations/profile.py +8 -1
  38. infrahub/graphql/mutations/repository.py +3 -3
  39. infrahub/graphql/mutations/schema.py +4 -4
  40. infrahub/graphql/mutations/webhook.py +2 -2
  41. infrahub/graphql/queries/resource_manager.py +2 -3
  42. infrahub/graphql/queries/search.py +2 -3
  43. infrahub/graphql/resolvers/ipam.py +20 -0
  44. infrahub/graphql/resolvers/many_relationship.py +12 -11
  45. infrahub/graphql/resolvers/resolver.py +6 -2
  46. infrahub/graphql/resolvers/single_relationship.py +1 -11
  47. infrahub/log.py +1 -1
  48. infrahub/message_bus/messages/__init__.py +0 -12
  49. infrahub/profiles/node_applier.py +9 -0
  50. infrahub/proposed_change/branch_diff.py +1 -1
  51. infrahub/proposed_change/tasks.py +1 -1
  52. infrahub/repositories/create_repository.py +3 -3
  53. infrahub/task_manager/models.py +1 -1
  54. infrahub/task_manager/task.py +5 -5
  55. infrahub/trigger/setup.py +6 -9
  56. infrahub/utils.py +18 -0
  57. infrahub/validators/tasks.py +1 -1
  58. infrahub/workers/infrahub_async.py +7 -6
  59. infrahub_sdk/client.py +113 -1
  60. infrahub_sdk/ctl/AGENTS.md +67 -0
  61. infrahub_sdk/ctl/branch.py +175 -1
  62. infrahub_sdk/ctl/check.py +3 -3
  63. infrahub_sdk/ctl/cli_commands.py +9 -9
  64. infrahub_sdk/ctl/generator.py +2 -2
  65. infrahub_sdk/ctl/graphql.py +1 -2
  66. infrahub_sdk/ctl/importer.py +1 -2
  67. infrahub_sdk/ctl/repository.py +6 -49
  68. infrahub_sdk/ctl/task.py +2 -4
  69. infrahub_sdk/ctl/utils.py +2 -2
  70. infrahub_sdk/ctl/validate.py +1 -2
  71. infrahub_sdk/diff.py +80 -3
  72. infrahub_sdk/graphql/constants.py +14 -1
  73. infrahub_sdk/graphql/renderers.py +5 -1
  74. infrahub_sdk/node/attribute.py +0 -1
  75. infrahub_sdk/node/constants.py +3 -1
  76. infrahub_sdk/node/node.py +303 -3
  77. infrahub_sdk/node/related_node.py +1 -2
  78. infrahub_sdk/node/relationship.py +1 -2
  79. infrahub_sdk/protocols_base.py +0 -1
  80. infrahub_sdk/pytest_plugin/AGENTS.md +67 -0
  81. infrahub_sdk/schema/__init__.py +0 -3
  82. infrahub_sdk/timestamp.py +7 -7
  83. {infrahub_server-1.6.0b0.dist-info → infrahub_server-1.6.1.dist-info}/METADATA +2 -3
  84. {infrahub_server-1.6.0b0.dist-info → infrahub_server-1.6.1.dist-info}/RECORD +88 -84
  85. {infrahub_server-1.6.0b0.dist-info → infrahub_server-1.6.1.dist-info}/WHEEL +1 -1
  86. infrahub_testcontainers/container.py +2 -2
  87. {infrahub_server-1.6.0b0.dist-info → infrahub_server-1.6.1.dist-info}/entry_points.txt +0 -0
  88. {infrahub_server-1.6.0b0.dist-info → infrahub_server-1.6.1.dist-info}/licenses/LICENSE.txt +0 -0
infrahub/git/tasks.py CHANGED
@@ -60,7 +60,7 @@ from .models import (
60
60
  UserCheckDefinitionData,
61
61
  )
62
62
  from .repository import InfrahubReadOnlyRepository, InfrahubRepository, get_initialized_repo
63
- from .utils import fetch_artifact_definition_targets, fetch_check_definition_targets
63
+ from .utils import fetch_artifact_definition_targets, fetch_check_definition_targets, get_repositories_commit_per_branch
64
64
 
65
65
 
66
66
  @flow(
@@ -195,13 +195,17 @@ async def sync_git_repo_with_origin_and_tag_on_failure(
195
195
  @flow(name="git_repositories_sync", flow_run_name="Sync Git Repositories")
196
196
  async def sync_remote_repositories() -> None:
197
197
  log = get_run_logger()
198
+ db = await get_database()
198
199
 
199
200
  client = get_client()
200
201
 
201
202
  branches = await client.branch.all()
202
- repositories = await client.get_list_repositories(branches=branches, kind=InfrahubKind.REPOSITORY)
203
+ async with db.start_session() as dbs:
204
+ repositories = await get_repositories_commit_per_branch(db=dbs, kind=InfrahubKind.REPOSITORY)
203
205
 
204
206
  for repo_name, repository_data in repositories.items():
207
+ repository: CoreRepository = repository_data.repository
208
+
205
209
  active_internal_status = RepositoryInternalStatus.ACTIVE.value
206
210
  default_internal_status = repository_data.branch_info[registry.default_branch].internal_status
207
211
  staging_branch = None
@@ -215,12 +219,12 @@ async def sync_remote_repositories() -> None:
215
219
  init_failed = False
216
220
  try:
217
221
  repo = await InfrahubRepository.init(
218
- id=repository_data.repository.id,
219
- name=repository_data.repository.name.value,
220
- location=repository_data.repository.location.value,
222
+ id=repository.id,
223
+ name=repository.name.value,
224
+ location=repository.location.value,
221
225
  client=client,
222
226
  internal_status=active_internal_status,
223
- default_branch_name=repository_data.repository.default_branch.value,
227
+ default_branch_name=repository.default_branch.value,
224
228
  )
225
229
  except RepositoryError as exc:
226
230
  get_logger().error(str(exc))
@@ -229,12 +233,12 @@ async def sync_remote_repositories() -> None:
229
233
  if init_failed:
230
234
  try:
231
235
  repo = await InfrahubRepository.new(
232
- id=repository_data.repository.id,
233
- name=repository_data.repository.name.value,
234
- location=repository_data.repository.location.value,
236
+ id=repository.id,
237
+ name=repository.name.value,
238
+ location=repository.location.value,
235
239
  client=client,
236
240
  internal_status=active_internal_status,
237
- default_branch_name=repository_data.repository.default_branch.value,
241
+ default_branch_name=repository.default_branch.value,
238
242
  )
239
243
  await repo.import_objects_from_files( # type: ignore[call-overload]
240
244
  git_branch_name=registry.default_branch, infrahub_branch_name=infrahub_branch
@@ -246,22 +250,22 @@ async def sync_remote_repositories() -> None:
246
250
  try:
247
251
  await sync_git_repo_with_origin_and_tag_on_failure(
248
252
  client=client,
249
- repository_id=repository_data.repository.id,
250
- repository_name=repository_data.repository.name.value,
251
- repository_location=repository_data.repository.location.value,
253
+ repository_id=repository.id,
254
+ repository_name=repository.name.value,
255
+ repository_location=repository.location.value,
252
256
  internal_status=active_internal_status,
253
- default_branch_name=repository_data.repository.default_branch.value,
254
- operational_status=repository_data.repository.operational_status.value,
257
+ default_branch_name=repository.default_branch.value,
258
+ operational_status=repository.operational_status.value,
255
259
  staging_branch=staging_branch,
256
260
  infrahub_branch=infrahub_branch,
257
261
  )
258
262
  # Tell workers to fetch to stay in sync
259
263
  message = messages.RefreshGitFetch(
260
264
  meta=Meta(initiator_id=WORKER_IDENTITY, request_id=get_log_data().get("request_id", "")),
261
- location=repository_data.repository.location.value,
262
- repository_id=repository_data.repository.id,
263
- repository_name=repository_data.repository.name.value,
264
- repository_kind=repository_data.repository.get_kind(),
265
+ location=repository.location.value,
266
+ repository_id=repository.id,
267
+ repository_name=repository.name.value,
268
+ repository_kind=repository.get_kind(),
265
269
  infrahub_branch_name=infrahub_branch,
266
270
  infrahub_branch_id=branches[infrahub_branch].id,
267
271
  )
infrahub/git/utils.py CHANGED
@@ -1,10 +1,16 @@
1
1
  import re
2
2
  from collections import defaultdict
3
- from typing import TYPE_CHECKING, Any
3
+ from typing import Any
4
4
 
5
5
  from infrahub_sdk import InfrahubClient
6
6
  from infrahub_sdk.node import RelationshipManager
7
- from infrahub_sdk.protocols import CoreArtifactDefinition, CoreCheckDefinition, CoreGroup
7
+ from infrahub_sdk.protocols import (
8
+ CoreArtifactDefinition,
9
+ CoreCheckDefinition,
10
+ CoreGroup,
11
+ CoreReadOnlyRepository,
12
+ CoreRepository,
13
+ )
8
14
  from infrahub_sdk.types import Order
9
15
 
10
16
  from infrahub.core import registry
@@ -12,16 +18,15 @@ from infrahub.core.constants import InfrahubKind
12
18
  from infrahub.core.manager import NodeManager
13
19
  from infrahub.database import InfrahubDatabase
14
20
  from infrahub.generators.models import ProposedChangeGeneratorDefinition
21
+ from infrahub.graphql.models import OrderModel
15
22
 
16
23
  from .. import config
17
24
  from .models import RepositoryBranchInfo, RepositoryData
18
25
 
19
- if TYPE_CHECKING:
20
- from infrahub.core.protocols import CoreGenericRepository
21
-
22
26
 
23
27
  async def get_repositories_commit_per_branch(
24
28
  db: InfrahubDatabase,
29
+ kind: str = InfrahubKind.GENERICREPOSITORY,
25
30
  ) -> dict[str, RepositoryData]:
26
31
  """Get a list of all repositories and their commit on each branches.
27
32
 
@@ -33,11 +38,12 @@ async def get_repositories_commit_per_branch(
33
38
  repositories: dict[str, RepositoryData] = {}
34
39
 
35
40
  for branch in list(registry.branch.values()):
36
- repos: list[CoreGenericRepository] = await NodeManager.query(
41
+ repos: list[CoreRepository | CoreReadOnlyRepository] = await NodeManager.query(
37
42
  db=db,
38
43
  branch=branch,
39
- fields={"id": None, "name": None, "commit": None, "internal_status": None},
40
- schema=InfrahubKind.GENERICREPOSITORY,
44
+ fields={"id": None, "name": None, "commit": None, "internal_status": None, "location": None, "ref": None},
45
+ schema=kind,
46
+ order=OrderModel(disable=True),
41
47
  )
42
48
 
43
49
  for repository in repos:
@@ -46,10 +52,11 @@ async def get_repositories_commit_per_branch(
46
52
  repositories[repo_name] = RepositoryData(
47
53
  repository_id=repository.get_id(),
48
54
  repository_name=repo_name,
55
+ repository=repository,
49
56
  branches={},
50
57
  )
51
58
 
52
- repositories[repo_name].branches[branch.name] = repository.commit.value # type: ignore[attr-defined]
59
+ repositories[repo_name].branches[branch.name] = repository.commit.value
53
60
  repositories[repo_name].branch_info[branch.name] = RepositoryBranchInfo(
54
61
  internal_status=repository.internal_status.value
55
62
  )
infrahub/graphql/app.py CHANGED
@@ -172,9 +172,9 @@ class InfrahubGraphQLApp:
172
172
 
173
173
  response = handler(request)
174
174
  if isawaitable(response):
175
- return await cast(Awaitable[Response], response)
175
+ return await cast("Awaitable[Response]", response)
176
176
 
177
- return cast(Response, response)
177
+ return cast("Response", response)
178
178
 
179
179
  async def _handle_http_request(
180
180
  self, request: Request, db: InfrahubDatabase, branch: Branch, account_session: AccountSession
@@ -350,8 +350,8 @@ class InfrahubGraphQLApp:
350
350
  websocket: WebSocket,
351
351
  subscriptions: dict[str, AsyncGenerator[Any, None]],
352
352
  ) -> None:
353
- operation_id = cast(str, message.get("id"))
354
- message_type = cast(str, message.get("type"))
353
+ operation_id = cast("str", message.get("id"))
354
+ message_type = cast("str", message.get("type"))
355
355
 
356
356
  if message_type == GQL_CONNECTION_INIT:
357
357
  websocket.scope["connection_params"] = message.get("payload")
@@ -445,7 +445,7 @@ class InfrahubGraphQLApp:
445
445
  if isinstance(result, ExecutionResult) and result.errors:
446
446
  return result.errors
447
447
 
448
- asyncgen = cast(AsyncGenerator[Any, None], result)
448
+ asyncgen = cast("AsyncGenerator[Any, None]", result)
449
449
  subscriptions[operation_id] = asyncgen
450
450
  task = asyncio.create_task(self._observe_subscription(asyncgen, operation_id, websocket))
451
451
  subscription_tasks.add(task)
@@ -479,7 +479,7 @@ async def _get_operation_from_request(request: Request) -> dict[str, Any] | list
479
479
  content_type = request.headers.get("Content-Type", "").split(";")[0]
480
480
  if content_type == "application/json":
481
481
  try:
482
- return cast(dict[str, Any] | list[Any], await request.json())
482
+ return cast("dict[str, Any] | list[Any]", await request.json())
483
483
  except (TypeError, ValueError) as err:
484
484
  raise ValueError("Request body is not a valid JSON") from err
485
485
  elif content_type == "multipart/form-data":
@@ -22,6 +22,8 @@ class QueryPeerParams:
22
22
  fields: dict | None = None
23
23
  at: Timestamp | str | None = None
24
24
  branch_agnostic: bool = False
25
+ include_source: bool = False
26
+ include_owner: bool = False
25
27
 
26
28
  def __hash__(self) -> int:
27
29
  frozen_fields: frozenset | None = None
@@ -39,6 +41,8 @@ class QueryPeerParams:
39
41
  self.schema.name,
40
42
  str(self.source_kind),
41
43
  str(self.branch_agnostic),
44
+ str(self.include_source),
45
+ str(self.include_owner),
42
46
  ]
43
47
  )
44
48
  return hash(hash_str)
@@ -63,6 +67,8 @@ class PeerRelationshipsDataLoader(DataLoader[str, list[Relationship]]):
63
67
  branch=self.query_params.branch,
64
68
  branch_agnostic=self.query_params.branch_agnostic,
65
69
  fetch_peers=True,
70
+ include_source=self.query_params.include_source,
71
+ include_owner=self.query_params.include_owner,
66
72
  )
67
73
  peer_rels_by_node_id: dict[str, list[Relationship]] = {}
68
74
  for rel in peer_rels:
@@ -5,7 +5,6 @@ from typing import TYPE_CHECKING, Any, cast
5
5
  from graphene import InputObjectType, Mutation
6
6
  from typing_extensions import Self
7
7
 
8
- from infrahub.core.protocols import CoreNodeTriggerAttributeMatch, CoreNodeTriggerRelationshipMatch, CoreNodeTriggerRule
9
8
  from infrahub.exceptions import SchemaNotFoundError, ValidationError
10
9
  from infrahub.log import get_logger
11
10
 
@@ -16,6 +15,11 @@ if TYPE_CHECKING:
16
15
 
17
16
  from infrahub.core.branch import Branch
18
17
  from infrahub.core.node import Node
18
+ from infrahub.core.protocols import (
19
+ CoreNodeTriggerAttributeMatch,
20
+ CoreNodeTriggerRelationshipMatch,
21
+ CoreNodeTriggerRule,
22
+ )
19
23
  from infrahub.core.schema import NodeSchema
20
24
  from infrahub.database import InfrahubDatabase
21
25
 
@@ -104,9 +108,11 @@ class InfrahubTriggerRuleMatchMutation(InfrahubMutationMixin, Mutation):
104
108
  trigger_match, result = await super().mutate_create(
105
109
  info=info, data=data, branch=branch, database=dbt, override_data=override_data
106
110
  )
107
- trigger_match_model = cast(CoreNodeTriggerAttributeMatch | CoreNodeTriggerRelationshipMatch, trigger_match)
111
+ trigger_match_model = cast(
112
+ "CoreNodeTriggerAttributeMatch | CoreNodeTriggerRelationshipMatch", trigger_match
113
+ )
108
114
  node_trigger_rule = await trigger_match_model.trigger.get_peer(db=dbt, raise_on_error=True)
109
- node_trigger_rule_model = cast(CoreNodeTriggerRule, node_trigger_rule)
115
+ node_trigger_rule_model = cast("CoreNodeTriggerRule", node_trigger_rule)
110
116
  node_schema = dbt.schema.get_node_schema(name=node_trigger_rule_model.node_kind.value, duplicate=False)
111
117
  _validate_node_kind_field(data=data, node_schema=node_schema)
112
118
 
@@ -124,9 +130,11 @@ class InfrahubTriggerRuleMatchMutation(InfrahubMutationMixin, Mutation):
124
130
  graphql_context: GraphqlContext = info.context
125
131
  async with graphql_context.db.start_transaction() as dbt:
126
132
  trigger_match, result = await super().mutate_update(info=info, data=data, branch=branch, database=dbt)
127
- trigger_match_model = cast(CoreNodeTriggerAttributeMatch | CoreNodeTriggerRelationshipMatch, trigger_match)
133
+ trigger_match_model = cast(
134
+ "CoreNodeTriggerAttributeMatch | CoreNodeTriggerRelationshipMatch", trigger_match
135
+ )
128
136
  node_trigger_rule = await trigger_match_model.trigger.get_peer(db=dbt, raise_on_error=True)
129
- node_trigger_rule_model = cast(CoreNodeTriggerRule, node_trigger_rule)
137
+ node_trigger_rule_model = cast("CoreNodeTriggerRule", node_trigger_rule)
130
138
  node_schema = dbt.schema.get_node_schema(name=node_trigger_rule_model.node_kind.value, duplicate=False)
131
139
  _validate_node_kind_field(data=data, node_schema=node_schema)
132
140
 
@@ -134,7 +142,7 @@ class InfrahubTriggerRuleMatchMutation(InfrahubMutationMixin, Mutation):
134
142
 
135
143
 
136
144
  def _validate_node_kind(data: InputObjectType, db: InfrahubDatabase) -> None:
137
- input_data = cast(dict[str, dict[str, Any]], data)
145
+ input_data = cast("dict[str, dict[str, Any]]", data)
138
146
  if node_kind := input_data.get("node_kind"):
139
147
  value = node_kind.get("value")
140
148
  if isinstance(value, str):
@@ -149,7 +157,7 @@ def _validate_node_kind(data: InputObjectType, db: InfrahubDatabase) -> None:
149
157
 
150
158
 
151
159
  def _validate_node_kind_field(data: InputObjectType, node_schema: NodeSchema) -> None:
152
- input_data = cast(dict[str, dict[str, Any]], data)
160
+ input_data = cast("dict[str, dict[str, Any]]", data)
153
161
  if attribute_name := input_data.get("attribute_name"):
154
162
  value = attribute_name.get("value")
155
163
  if isinstance(value, str):
@@ -55,7 +55,7 @@ class UpdateHFID(Mutation):
55
55
  input_value=f"{node_schema.kind}.human_friendly_id has not been defined for this kind."
56
56
  )
57
57
 
58
- updated_hfid = cast(list[str], data.value)
58
+ updated_hfid = cast("list[str]", data.value)
59
59
 
60
60
  if len(node_schema.human_friendly_id) != len(updated_hfid):
61
61
  raise ValidationError(
@@ -57,6 +57,8 @@ class InfrahubProfileMutation(InfrahubMutationMixin, Mutation):
57
57
  ) -> None:
58
58
  if not node_ids:
59
59
  related_nodes = await obj.related_nodes.get_relationships(db=db) # type: ignore[attr-defined]
60
+ if hasattr(obj, "related_templates"):
61
+ related_nodes.extend(await obj.related_templates.get_relationships(db=db)) # type: ignore[attr-defined]
60
62
  node_ids = [rel.peer_id for rel in related_nodes]
61
63
  if node_ids:
62
64
  await workflow_service.submit_workflow(
@@ -79,7 +81,12 @@ class InfrahubProfileMutation(InfrahubMutationMixin, Mutation):
79
81
 
80
82
  @classmethod
81
83
  async def _get_profile_related_node_ids(cls, db: InfrahubDatabase, obj: Node) -> set[str]:
82
- related_nodes = await obj.related_nodes.get_relationships(db=db) # type: ignore[attr-defined]
84
+ related_nodes = []
85
+ related_nodes.extend(await obj.related_nodes.get_relationships(db=db)) # type: ignore[attr-defined]
86
+
87
+ if hasattr(obj, "related_templates"):
88
+ related_nodes.extend(await obj.related_templates.get_relationships(db=db)) # type: ignore[attr-defined]
89
+
83
90
  if related_nodes:
84
91
  related_node_ids = {rel.peer_id for rel in related_nodes}
85
92
  else:
@@ -9,7 +9,6 @@ from graphene import Boolean, Field, InputObjectType, Mutation, String
9
9
  from infrahub import config
10
10
  from infrahub.core.constants import InfrahubKind
11
11
  from infrahub.core.manager import NodeManager
12
- from infrahub.core.protocols import CoreReadOnlyRepository, CoreRepository
13
12
  from infrahub.core.schema import NodeSchema
14
13
  from infrahub.git.models import (
15
14
  GitRepositoryImportObjects,
@@ -34,6 +33,7 @@ if TYPE_CHECKING:
34
33
 
35
34
  from infrahub.core.branch import Branch
36
35
  from infrahub.core.node import Node
36
+ from infrahub.core.protocols import CoreReadOnlyRepository, CoreRepository
37
37
  from infrahub.database import InfrahubDatabase
38
38
  from infrahub.graphql.initialization import GraphqlContext
39
39
 
@@ -107,7 +107,7 @@ class InfrahubRepositoryMutation(InfrahubMutationMixin, Mutation):
107
107
  if node.get_kind() != InfrahubKind.READONLYREPOSITORY:
108
108
  return await super().mutate_update(info, data, branch, database=graphql_context.db, node=node)
109
109
 
110
- node = cast(CoreReadOnlyRepository, node)
110
+ node = cast("CoreReadOnlyRepository", node)
111
111
  current_commit = node.commit.value
112
112
  current_ref = node.ref.value
113
113
  new_commit = None
@@ -118,7 +118,7 @@ class InfrahubRepositoryMutation(InfrahubMutationMixin, Mutation):
118
118
  new_ref = data.ref.value
119
119
 
120
120
  obj, result = await super().mutate_update(info, data, branch, database=graphql_context.db, node=node)
121
- obj = cast(CoreReadOnlyRepository, obj)
121
+ obj = cast("CoreReadOnlyRepository", obj)
122
122
 
123
123
  send_update_message = (new_commit and new_commit != current_commit) or (new_ref and new_ref != current_ref)
124
124
  if not send_update_message:
@@ -81,7 +81,7 @@ class SchemaDropdownAdd(Mutation):
81
81
  _validate_schema_permission(graphql_context=graphql_context)
82
82
  await apply_external_context(graphql_context=graphql_context, context_input=context)
83
83
 
84
- kind = graphql_context.db.schema.get(name=str(data.kind), branch=graphql_context.branch.name, duplicate=False)
84
+ kind = graphql_context.db.schema.get(name=str(data.kind), branch=graphql_context.branch.name, duplicate=True)
85
85
  attribute = str(data.attribute)
86
86
  validate_kind_dropdown(kind=kind, attribute=attribute)
87
87
  dropdown = str(data.dropdown)
@@ -141,7 +141,7 @@ class SchemaDropdownRemove(Mutation):
141
141
  graphql_context: GraphqlContext = info.context
142
142
 
143
143
  _validate_schema_permission(graphql_context=graphql_context)
144
- kind = graphql_context.db.schema.get(name=str(data.kind), branch=graphql_context.branch.name, duplicate=False)
144
+ kind = graphql_context.db.schema.get(name=str(data.kind), branch=graphql_context.branch.name, duplicate=True)
145
145
  await apply_external_context(graphql_context=graphql_context, context_input=context)
146
146
 
147
147
  attribute = str(data.attribute)
@@ -197,7 +197,7 @@ class SchemaEnumAdd(Mutation):
197
197
  graphql_context: GraphqlContext = info.context
198
198
 
199
199
  _validate_schema_permission(graphql_context=graphql_context)
200
- kind = graphql_context.db.schema.get(name=str(data.kind), branch=graphql_context.branch.name, duplicate=False)
200
+ kind = graphql_context.db.schema.get(name=str(data.kind), branch=graphql_context.branch.name, duplicate=True)
201
201
  await apply_external_context(graphql_context=graphql_context, context_input=context)
202
202
 
203
203
  attribute = str(data.attribute)
@@ -243,7 +243,7 @@ class SchemaEnumRemove(Mutation):
243
243
  graphql_context: GraphqlContext = info.context
244
244
 
245
245
  _validate_schema_permission(graphql_context=graphql_context)
246
- kind = graphql_context.db.schema.get(name=str(data.kind), branch=graphql_context.branch.name, duplicate=False)
246
+ kind = graphql_context.db.schema.get(name=str(data.kind), branch=graphql_context.branch.name, duplicate=True)
247
247
  await apply_external_context(graphql_context=graphql_context, context_input=context)
248
248
 
249
249
  attribute = str(data.attribute)
@@ -6,7 +6,6 @@ from typing import TYPE_CHECKING, Any, Self, cast
6
6
  from graphene import InputObjectType, Mutation
7
7
 
8
8
  from infrahub.core.manager import NodeManager
9
- from infrahub.core.protocols import CoreWebhook
10
9
  from infrahub.core.schema import NodeSchema
11
10
  from infrahub.database import retry_db_transaction
12
11
  from infrahub.events.utils import get_all_infrahub_node_kind_events
@@ -20,6 +19,7 @@ if TYPE_CHECKING:
20
19
 
21
20
  from infrahub.core.branch import Branch
22
21
  from infrahub.core.node import Node
22
+ from infrahub.core.protocols import CoreWebhook
23
23
  from infrahub.database import InfrahubDatabase
24
24
  from infrahub.graphql.initialization import GraphqlContext
25
25
 
@@ -107,7 +107,7 @@ class InfrahubWebhookMutation(InfrahubMutationMixin, Mutation):
107
107
  branch=branch,
108
108
  )
109
109
 
110
- webhook = cast(CoreWebhook, obj)
110
+ webhook = cast("CoreWebhook", obj)
111
111
 
112
112
  event_type = input_data.event_type.value if input_data.event_type else webhook.event_type.value.value
113
113
  node_kind = input_data.node_kind.value if input_data.node_kind else webhook.node_kind.value
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  from typing import TYPE_CHECKING, Any
4
5
 
5
6
  from graphene import BigInt, Field, Float, Int, List, NonNull, ObjectType, String
@@ -196,10 +197,8 @@ class PoolUtilization(ObjectType):
196
197
 
197
198
  resources_map: dict[str, Node] = {}
198
199
 
199
- try:
200
+ with contextlib.suppress(SchemaNotFoundError):
200
201
  resources_map = await pool.resources.get_peers(db=db, branch_agnostic=True) # type: ignore[attr-defined,union-attr]
201
- except SchemaNotFoundError:
202
- pass
203
202
 
204
203
  utilization_getter = PrefixUtilizationGetter(
205
204
  db=db, ip_prefixes=list(resources_map.values()), at=graphql_context.at
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import contextlib
3
4
  import ipaddress
4
5
  from typing import TYPE_CHECKING, Any
5
6
 
@@ -117,11 +118,9 @@ async def search_resolver(
117
118
  if matching:
118
119
  results.append(matching)
119
120
  else:
120
- try:
121
+ with contextlib.suppress(ValueError, ipaddress.AddressValueError):
121
122
  # Convert any IPv6 address, network or partial address to collapsed format as it might be stored in db.
122
123
  q = _collapse_ipv6(q)
123
- except (ValueError, ipaddress.AddressValueError):
124
- pass
125
124
 
126
125
  for kind in [InfrahubKind.NODE, InfrahubKind.GENERICGROUP]:
127
126
  objs = await NodeManager.query(
@@ -4,6 +4,7 @@ import ipaddress
4
4
  from typing import TYPE_CHECKING, Any
5
5
 
6
6
  from graphql.type.definition import GraphQLNonNull
7
+ from infrahub_sdk.utils import deep_merge_dict
7
8
  from netaddr import IPSet
8
9
  from opentelemetry import trace
9
10
 
@@ -233,6 +234,23 @@ async def _resolve_available_prefix_nodes(
233
234
  return available_nodes
234
235
 
235
236
 
237
+ def _ensure_display_label_fields(
238
+ db: InfrahubDatabase, branch: Branch, schema: NodeSchema | GenericSchema, node_fields: dict[str, Any]
239
+ ) -> None:
240
+ """Ensure fields needed to compute display_label are included in node_fields.
241
+
242
+ This is mostly for virtual nodes (InternalIPPrefixAvailable, InternalIPRangeAvailable) that are not stored in the
243
+ database.
244
+ """
245
+ if "display_label" not in node_fields or schema.kind not in [InfrahubKind.IPPREFIX, InfrahubKind.IPADDRESS]:
246
+ return
247
+
248
+ schema_branch = db.schema.get_schema_branch(name=branch.name)
249
+ display_label_fields = schema_branch.generate_fields_for_display_label(name=schema.kind)
250
+ if display_label_fields:
251
+ deep_merge_dict(dicta=node_fields, dictb=display_label_fields)
252
+
253
+
236
254
  def _filter_kinds(nodes: list[Node], kinds: list[str], limit: int | None) -> list[Node]:
237
255
  filtered: list[Node] = []
238
256
  available_node_kinds = [InfrahubKind.IPPREFIXAVAILABLE, InfrahubKind.IPRANGEAVAILABLE]
@@ -324,6 +342,8 @@ async def ipam_paginated_list_resolver( # noqa: PLR0915
324
342
  edges = fields.get("edges", {})
325
343
  node_fields = edges.get("node", {})
326
344
 
345
+ _ensure_display_label_fields(db=db, branch=graphql_context.branch, schema=schema, node_fields=node_fields)
346
+
327
347
  permission_set: dict[str, Any] | None = None
328
348
  permissions = (
329
349
  await get_permissions(schema=schema, graphql_context=graphql_context)
@@ -1,7 +1,6 @@
1
1
  from typing import TYPE_CHECKING, Any
2
2
 
3
3
  from graphql import GraphQLResolveInfo
4
- from infrahub_sdk.utils import deep_merge_dict
5
4
 
6
5
  from infrahub.core.branch.models import Branch
7
6
  from infrahub.core.constants import BranchSupportType, RelationshipHierarchyDirection
@@ -12,6 +11,7 @@ from infrahub.core.schema.relationship_schema import RelationshipSchema
12
11
  from infrahub.core.timestamp import Timestamp
13
12
  from infrahub.database import InfrahubDatabase
14
13
  from infrahub.graphql.field_extractor import extract_graphql_fields
14
+ from infrahub.utils import has_any_key
15
15
 
16
16
  from ..loaders.peers import PeerRelationshipsDataLoader, QueryPeerParams
17
17
  from ..types import RELATIONS_PROPERTY_MAP, RELATIONS_PROPERTY_MAP_REVERSED
@@ -195,6 +195,9 @@ class ManyRelationshipResolver:
195
195
  offset: int | None = None,
196
196
  limit: int | None = None,
197
197
  ) -> list[dict[str, Any]] | None:
198
+ include_source = has_any_key(data=node_fields, keys=["_relation__source", "source"])
199
+ include_owner = has_any_key(data=node_fields, keys=["_relation__owner", "owner"])
200
+
198
201
  async with db.start_session(read_only=True) as dbs:
199
202
  objs = await NodeManager.query_peers(
200
203
  db=dbs,
@@ -209,6 +212,8 @@ class ManyRelationshipResolver:
209
212
  branch=branch,
210
213
  branch_agnostic=rel_schema.branch is BranchSupportType.AGNOSTIC,
211
214
  fetch_peers=True,
215
+ include_source=include_source,
216
+ include_owner=include_owner,
212
217
  )
213
218
  if not objs:
214
219
  return None
@@ -226,17 +231,11 @@ class ManyRelationshipResolver:
226
231
  filters: dict[str, Any],
227
232
  node_fields: dict[str, Any],
228
233
  ) -> list[dict[str, Any]] | None:
229
- if node_fields and "display_label" in node_fields:
230
- schema_branch = db.schema.get_schema_branch(name=branch.name)
231
- display_label_fields = schema_branch.generate_fields_for_display_label(name=rel_schema.peer)
232
- if display_label_fields:
233
- node_fields = deep_merge_dict(dicta=node_fields, dictb=display_label_fields)
234
-
235
234
  if node_fields and "hfid" in node_fields:
236
- peer_schema = db.schema.get(name=rel_schema.peer, branch=branch, duplicate=False)
237
- hfid_fields = peer_schema.generate_fields_for_hfid()
238
- if hfid_fields:
239
- node_fields = deep_merge_dict(dicta=node_fields, dictb=hfid_fields)
235
+ node_fields["human_friendly_id"] = None
236
+
237
+ include_source = has_any_key(data=node_fields, keys=["_relation__source", "source"])
238
+ include_owner = has_any_key(data=node_fields, keys=["_relation__owner", "owner"])
240
239
 
241
240
  query_params = QueryPeerParams(
242
241
  branch=branch,
@@ -246,6 +245,8 @@ class ManyRelationshipResolver:
246
245
  fields=node_fields,
247
246
  at=at,
248
247
  branch_agnostic=rel_schema.branch is BranchSupportType.AGNOSTIC,
248
+ include_source=include_source,
249
+ include_owner=include_owner,
249
250
  )
250
251
  if query_params in self._data_loader_instances:
251
252
  loader = self._data_loader_instances[query_params]
@@ -9,6 +9,7 @@ from infrahub.core.constants import BranchSupportType, InfrahubKind, Relationshi
9
9
  from infrahub.core.manager import NodeManager
10
10
  from infrahub.exceptions import NodeNotFoundError
11
11
  from infrahub.graphql.field_extractor import extract_graphql_fields
12
+ from infrahub.utils import has_any_key
12
13
 
13
14
  from ..models import OrderModel
14
15
  from ..parser import extract_selection
@@ -185,6 +186,9 @@ async def default_paginated_list_resolver(
185
186
 
186
187
  objs = []
187
188
  if edges or "hfid" in filters:
189
+ include_source = has_any_key(data=node_fields, keys=["_relation__source", "source"])
190
+ include_owner = has_any_key(data=node_fields, keys=["_relation__owner", "owner"])
191
+
188
192
  objs = await NodeManager.query(
189
193
  db=db,
190
194
  schema=schema,
@@ -195,8 +199,8 @@ async def default_paginated_list_resolver(
195
199
  limit=limit,
196
200
  offset=offset,
197
201
  account=graphql_context.account_session,
198
- include_source=True,
199
- include_owner=True,
202
+ include_source=include_source,
203
+ include_owner=include_owner,
200
204
  partial_match=partial_match,
201
205
  order=order,
202
206
  )
@@ -2,7 +2,6 @@ from typing import TYPE_CHECKING, Any
2
2
 
3
3
  from graphql import GraphQLResolveInfo
4
4
  from graphql.type.definition import GraphQLNonNull
5
- from infrahub_sdk.utils import deep_merge_dict
6
5
 
7
6
  from infrahub.core.branch.models import Branch
8
7
  from infrahub.core.constants import BranchSupportType
@@ -142,17 +141,8 @@ class SingleRelationshipResolver:
142
141
  except (KeyError, IndexError):
143
142
  return None
144
143
 
145
- if node_fields and "display_label" in node_fields:
146
- schema_branch = db.schema.get_schema_branch(name=branch.name)
147
- display_label_fields = schema_branch.generate_fields_for_display_label(name=rel_schema.peer)
148
- if display_label_fields:
149
- node_fields = deep_merge_dict(dicta=node_fields, dictb=display_label_fields)
150
-
151
144
  if node_fields and "hfid" in node_fields:
152
- peer_schema = db.schema.get(name=rel_schema.peer, branch=branch, duplicate=False)
153
- hfid_fields = peer_schema.generate_fields_for_hfid()
154
- if hfid_fields:
155
- node_fields = deep_merge_dict(dicta=node_fields, dictb=hfid_fields)
145
+ node_fields["human_friendly_id"] = None
156
146
 
157
147
  query_params = GetManyParams(
158
148
  fields=node_fields,
infrahub/log.py CHANGED
@@ -10,7 +10,7 @@ from structlog.dev import plain_traceback
10
10
  if TYPE_CHECKING:
11
11
  from structlog.types import Processor
12
12
 
13
- INFRAHUB_PRODUCTION = TypeAdapter(bool).validate_python(os.environ.get("INFRAHUB_PRODUCTION", True))
13
+ INFRAHUB_PRODUCTION = TypeAdapter(bool).validate_python(os.environ.get("INFRAHUB_PRODUCTION", "true"))
14
14
  INFRAHUB_LOG_LEVEL = os.environ.get("INFRAHUB_LOG_LEVEL", "INFO")
15
15
 
16
16
 
@@ -22,20 +22,8 @@ RESPONSE_MAP: dict[str, type[InfrahubResponse]] = {
22
22
  }
23
23
 
24
24
  PRIORITY_MAP = {
25
- "check.artifact.create": 2,
26
- "check.repository.check_definition": 2,
27
- "check.repository.merge_conflicts": 2,
28
25
  "send.echo.request": 5, # Currently only for testing purposes, will be removed once all message bus have been migrated to prefect
29
- "event.branch.delete": 5,
30
- "event.branch.merge": 5,
31
- "event.schema.update": 5,
32
- "git.diff.names_only": 4,
33
26
  "git.file.get": 4,
34
- "request.artifact.generate": 2,
35
- "request.git.sync": 4,
36
- "request.proposed_change.pipeline": 5,
37
- "transform.jinja.template": 4,
38
- "transform.python.data": 4,
39
27
  }
40
28
 
41
29