infrahub-server 1.2.7__py3-none-any.whl → 1.2.9rc0__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 (54) hide show
  1. infrahub/api/transformation.py +1 -0
  2. infrahub/artifacts/models.py +4 -0
  3. infrahub/cli/db.py +15 -6
  4. infrahub/computed_attribute/tasks.py +1 -0
  5. infrahub/config.py +2 -1
  6. infrahub/constants/__init__.py +0 -0
  7. infrahub/core/constants/__init__.py +1 -0
  8. infrahub/core/graph/index.py +3 -1
  9. infrahub/core/initialization.py +23 -7
  10. infrahub/core/manager.py +16 -5
  11. infrahub/core/migrations/graph/m014_remove_index_attr_value.py +9 -8
  12. infrahub/core/protocols.py +1 -0
  13. infrahub/core/query/node.py +96 -29
  14. infrahub/core/schema/definitions/core/builtin.py +2 -4
  15. infrahub/core/schema/definitions/core/transform.py +1 -0
  16. infrahub/core/validators/aggregated_checker.py +2 -2
  17. infrahub/core/validators/uniqueness/query.py +30 -9
  18. infrahub/database/__init__.py +1 -16
  19. infrahub/database/index.py +1 -1
  20. infrahub/database/memgraph.py +1 -12
  21. infrahub/database/neo4j.py +1 -13
  22. infrahub/git/integrator.py +27 -3
  23. infrahub/git/models.py +4 -0
  24. infrahub/git/tasks.py +3 -0
  25. infrahub/git_credential/helper.py +2 -2
  26. infrahub/graphql/mutations/computed_attribute.py +5 -1
  27. infrahub/message_bus/operations/requests/proposed_change.py +6 -0
  28. infrahub/message_bus/types.py +3 -0
  29. infrahub/patch/queries/consolidate_duplicated_nodes.py +109 -0
  30. infrahub/patch/queries/delete_duplicated_edges.py +138 -0
  31. infrahub/proposed_change/tasks.py +1 -0
  32. infrahub/server.py +1 -3
  33. infrahub/transformations/models.py +3 -0
  34. infrahub/transformations/tasks.py +1 -0
  35. infrahub/webhook/models.py +3 -0
  36. infrahub_sdk/client.py +4 -4
  37. infrahub_sdk/config.py +17 -0
  38. infrahub_sdk/ctl/cli_commands.py +7 -1
  39. infrahub_sdk/ctl/generator.py +2 -2
  40. infrahub_sdk/generator.py +12 -66
  41. infrahub_sdk/operation.py +80 -0
  42. infrahub_sdk/protocols.py +12 -0
  43. infrahub_sdk/recorder.py +3 -0
  44. infrahub_sdk/schema/repository.py +4 -0
  45. infrahub_sdk/transforms.py +15 -27
  46. {infrahub_server-1.2.7.dist-info → infrahub_server-1.2.9rc0.dist-info}/METADATA +2 -2
  47. {infrahub_server-1.2.7.dist-info → infrahub_server-1.2.9rc0.dist-info}/RECORD +53 -50
  48. infrahub_testcontainers/container.py +1 -0
  49. infrahub_testcontainers/docker-compose.test.yml +4 -0
  50. infrahub/database/manager.py +0 -15
  51. /infrahub/{database/constants.py → constants/database.py} +0 -0
  52. {infrahub_server-1.2.7.dist-info → infrahub_server-1.2.9rc0.dist-info}/LICENSE.txt +0 -0
  53. {infrahub_server-1.2.7.dist-info → infrahub_server-1.2.9rc0.dist-info}/WHEEL +0 -0
  54. {infrahub_server-1.2.7.dist-info → infrahub_server-1.2.9rc0.dist-info}/entry_points.txt +0 -0
@@ -62,7 +62,7 @@ async def run(
62
62
  generator = generator_class(
63
63
  query=generator_config.query,
64
64
  client=client,
65
- branch=branch,
65
+ branch=branch or "",
66
66
  params=variables_dict,
67
67
  convert_query_response=generator_config.convert_query_response,
68
68
  infrahub_node=InfrahubNode,
@@ -91,7 +91,7 @@ async def run(
91
91
  generator = generator_class(
92
92
  query=generator_config.query,
93
93
  client=client,
94
- branch=branch,
94
+ branch=branch or "",
95
95
  params=params,
96
96
  convert_query_response=generator_config.convert_query_response,
97
97
  infrahub_node=InfrahubNode,
infrahub_sdk/generator.py CHANGED
@@ -1,22 +1,19 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import logging
4
- import os
5
4
  from abc import abstractmethod
6
5
  from typing import TYPE_CHECKING
7
6
 
8
- from infrahub_sdk.repository import GitRepoManager
9
-
10
7
  from .exceptions import UninitializedError
8
+ from .operation import InfrahubOperation
11
9
 
12
10
  if TYPE_CHECKING:
13
11
  from .client import InfrahubClient
14
12
  from .context import RequestContext
15
13
  from .node import InfrahubNode
16
- from .store import NodeStore
17
14
 
18
15
 
19
- class InfrahubGenerator:
16
+ class InfrahubGenerator(InfrahubOperation):
20
17
  """Infrahub Generator class"""
21
18
 
22
19
  def __init__(
@@ -24,7 +21,7 @@ class InfrahubGenerator:
24
21
  query: str,
25
22
  client: InfrahubClient,
26
23
  infrahub_node: type[InfrahubNode],
27
- branch: str | None = None,
24
+ branch: str = "",
28
25
  root_directory: str = "",
29
26
  generator_instance: str = "",
30
27
  params: dict | None = None,
@@ -33,37 +30,21 @@ class InfrahubGenerator:
33
30
  request_context: RequestContext | None = None,
34
31
  ) -> None:
35
32
  self.query = query
36
- self.branch = branch
37
- self.git: GitRepoManager | None = None
33
+
34
+ super().__init__(
35
+ client=client,
36
+ infrahub_node=infrahub_node,
37
+ convert_query_response=convert_query_response,
38
+ branch=branch,
39
+ root_directory=root_directory,
40
+ )
41
+
38
42
  self.params = params or {}
39
- self.root_directory = root_directory or os.getcwd()
40
43
  self.generator_instance = generator_instance
41
- self._init_client = client.clone()
42
- self._init_client.config.default_branch = self._init_client.default_branch = self.branch_name
43
- self._init_client.store._default_branch = self.branch_name
44
44
  self._client: InfrahubClient | None = None
45
- self._nodes: list[InfrahubNode] = []
46
- self._related_nodes: list[InfrahubNode] = []
47
- self.infrahub_node = infrahub_node
48
- self.convert_query_response = convert_query_response
49
45
  self.logger = logger if logger else logging.getLogger("infrahub.tasks")
50
46
  self.request_context = request_context
51
47
 
52
- @property
53
- def store(self) -> NodeStore:
54
- """The store will be populated with nodes based on the query during the collection of data if activated"""
55
- return self._init_client.store
56
-
57
- @property
58
- def nodes(self) -> list[InfrahubNode]:
59
- """Returns nodes collected and parsed during the data collection process if this feature is enables"""
60
- return self._nodes
61
-
62
- @property
63
- def related_nodes(self) -> list[InfrahubNode]:
64
- """Returns nodes collected and parsed during the data collection process if this feature is enables"""
65
- return self._related_nodes
66
-
67
48
  @property
68
49
  def subscribers(self) -> list[str] | None:
69
50
  if self.generator_instance:
@@ -80,20 +61,6 @@ class InfrahubGenerator:
80
61
  def client(self, value: InfrahubClient) -> None:
81
62
  self._client = value
82
63
 
83
- @property
84
- def branch_name(self) -> str:
85
- """Return the name of the current git branch."""
86
-
87
- if self.branch:
88
- return self.branch
89
-
90
- if not self.git:
91
- self.git = GitRepoManager(self.root_directory)
92
-
93
- self.branch = str(self.git.active_branch)
94
-
95
- return self.branch
96
-
97
64
  async def collect_data(self) -> dict:
98
65
  """Query the result of the GraphQL Query defined in self.query and return the result"""
99
66
 
@@ -119,27 +86,6 @@ class InfrahubGenerator:
119
86
  ) as self.client:
120
87
  await self.generate(data=unpacked)
121
88
 
122
- async def process_nodes(self, data: dict) -> None:
123
- if not self.convert_query_response:
124
- return
125
-
126
- await self._init_client.schema.all(branch=self.branch_name)
127
-
128
- for kind in data:
129
- if kind in self._init_client.schema.cache[self.branch_name].nodes.keys():
130
- for result in data[kind].get("edges", []):
131
- node = await self.infrahub_node.from_graphql(
132
- client=self._init_client, branch=self.branch_name, data=result
133
- )
134
- self._nodes.append(node)
135
- await node._process_relationships(
136
- node_data=result, branch=self.branch_name, related_nodes=self._related_nodes
137
- )
138
-
139
- for node in self._nodes + self._related_nodes:
140
- if node.id:
141
- self._init_client.store.set(node=node)
142
-
143
89
  @abstractmethod
144
90
  async def generate(self, data: dict) -> None:
145
91
  """Code to run the generator
@@ -0,0 +1,80 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import TYPE_CHECKING
5
+
6
+ from .repository import GitRepoManager
7
+
8
+ if TYPE_CHECKING:
9
+ from . import InfrahubClient
10
+ from .node import InfrahubNode
11
+ from .store import NodeStore
12
+
13
+
14
+ class InfrahubOperation:
15
+ def __init__(
16
+ self,
17
+ client: InfrahubClient,
18
+ infrahub_node: type[InfrahubNode],
19
+ convert_query_response: bool,
20
+ branch: str,
21
+ root_directory: str,
22
+ ):
23
+ self.branch = branch
24
+ self.convert_query_response = convert_query_response
25
+ self.root_directory = root_directory or os.getcwd()
26
+ self.infrahub_node = infrahub_node
27
+ self._nodes: list[InfrahubNode] = []
28
+ self._related_nodes: list[InfrahubNode] = []
29
+ self._init_client = client.clone(branch=self.branch_name)
30
+ self.git: GitRepoManager | None = None
31
+
32
+ @property
33
+ def branch_name(self) -> str:
34
+ """Return the name of the current git branch."""
35
+
36
+ if self.branch:
37
+ return self.branch
38
+
39
+ if not hasattr(self, "git") or not self.git:
40
+ self.git = GitRepoManager(self.root_directory)
41
+
42
+ self.branch = str(self.git.active_branch)
43
+
44
+ return self.branch
45
+
46
+ @property
47
+ def store(self) -> NodeStore:
48
+ """The store will be populated with nodes based on the query during the collection of data if activated"""
49
+ return self._init_client.store
50
+
51
+ @property
52
+ def nodes(self) -> list[InfrahubNode]:
53
+ """Returns nodes collected and parsed during the data collection process if this feature is enabled"""
54
+ return self._nodes
55
+
56
+ @property
57
+ def related_nodes(self) -> list[InfrahubNode]:
58
+ """Returns nodes collected and parsed during the data collection process if this feature is enabled"""
59
+ return self._related_nodes
60
+
61
+ async def process_nodes(self, data: dict) -> None:
62
+ if not self.convert_query_response:
63
+ return
64
+
65
+ await self._init_client.schema.all(branch=self.branch_name)
66
+
67
+ for kind in data:
68
+ if kind in self._init_client.schema.cache[self.branch_name].nodes.keys():
69
+ for result in data[kind].get("edges", []):
70
+ node = await self.infrahub_node.from_graphql(
71
+ client=self._init_client, branch=self.branch_name, data=result
72
+ )
73
+ self._nodes.append(node)
74
+ await node._process_relationships(
75
+ node_data=result, branch=self.branch_name, related_nodes=self._related_nodes
76
+ )
77
+
78
+ for node in self._nodes + self._related_nodes:
79
+ if node.id:
80
+ self._init_client.store.set(node=node)
infrahub_sdk/protocols.py CHANGED
@@ -154,6 +154,10 @@ class CoreMenu(CoreNode):
154
154
  children: RelationshipManager
155
155
 
156
156
 
157
+ class CoreObjectComponentTemplate(CoreNode):
158
+ template_name: String
159
+
160
+
157
161
  class CoreObjectTemplate(CoreNode):
158
162
  template_name: String
159
163
 
@@ -205,6 +209,7 @@ class CoreWebhook(CoreNode):
205
209
  name: String
206
210
  event_type: Enum
207
211
  branch_scope: Dropdown
212
+ node_kind: StringOptional
208
213
  description: StringOptional
209
214
  url: URL
210
215
  validate_certificates: BooleanOptional
@@ -479,6 +484,7 @@ class CoreTransformJinja2(CoreTransformation):
479
484
  class CoreTransformPython(CoreTransformation):
480
485
  file_path: String
481
486
  class_name: String
487
+ convert_query_response: BooleanOptional
482
488
 
483
489
 
484
490
  class CoreUserValidator(CoreValidator):
@@ -625,6 +631,10 @@ class CoreMenuSync(CoreNodeSync):
625
631
  children: RelationshipManagerSync
626
632
 
627
633
 
634
+ class CoreObjectComponentTemplateSync(CoreNodeSync):
635
+ template_name: String
636
+
637
+
628
638
  class CoreObjectTemplateSync(CoreNodeSync):
629
639
  template_name: String
630
640
 
@@ -676,6 +686,7 @@ class CoreWebhookSync(CoreNodeSync):
676
686
  name: String
677
687
  event_type: Enum
678
688
  branch_scope: Dropdown
689
+ node_kind: StringOptional
679
690
  description: StringOptional
680
691
  url: URL
681
692
  validate_certificates: BooleanOptional
@@ -950,6 +961,7 @@ class CoreTransformJinja2Sync(CoreTransformationSync):
950
961
  class CoreTransformPythonSync(CoreTransformationSync):
951
962
  file_path: String
952
963
  class_name: String
964
+ convert_query_response: BooleanOptional
953
965
 
954
966
 
955
967
  class CoreUserValidatorSync(CoreValidatorSync):
infrahub_sdk/recorder.py CHANGED
@@ -31,6 +31,9 @@ class NoRecorder:
31
31
  def default(cls) -> NoRecorder:
32
32
  return cls()
33
33
 
34
+ def __eq__(self, other: object) -> bool:
35
+ return isinstance(other, NoRecorder)
36
+
34
37
 
35
38
  class JSONRecorder(BaseSettings):
36
39
  model_config = SettingsConfigDict(env_prefix="INFRAHUB_JSON_RECORDER_")
@@ -117,6 +117,10 @@ class InfrahubPythonTransformConfig(InfrahubRepositoryConfigElement):
117
117
  name: str = Field(..., description="The name of the Transform")
118
118
  file_path: Path = Field(..., description="The file within the repository with the transform code.")
119
119
  class_name: str = Field(default="Transform", description="The name of the transform class to run.")
120
+ convert_query_response: bool = Field(
121
+ default=False,
122
+ description="Decide if the transform should convert the result of the GraphQL query to SDK InfrahubNode objects.",
123
+ )
120
124
 
121
125
  def load_class(self, import_root: str | None = None, relative_path: str | None = None) -> type[InfrahubTransform]:
122
126
  module = import_module(module_path=self.file_path, import_root=import_root, relative_path=relative_path)
@@ -5,34 +5,38 @@ import os
5
5
  from abc import abstractmethod
6
6
  from typing import TYPE_CHECKING, Any
7
7
 
8
- from infrahub_sdk.repository import GitRepoManager
9
-
10
- from .exceptions import UninitializedError
8
+ from .operation import InfrahubOperation
11
9
 
12
10
  if TYPE_CHECKING:
13
11
  from . import InfrahubClient
12
+ from .node import InfrahubNode
14
13
 
15
14
  INFRAHUB_TRANSFORM_VARIABLE_TO_IMPORT = "INFRAHUB_TRANSFORMS"
16
15
 
17
16
 
18
- class InfrahubTransform:
17
+ class InfrahubTransform(InfrahubOperation):
19
18
  name: str | None = None
20
19
  query: str
21
20
  timeout: int = 10
22
21
 
23
22
  def __init__(
24
23
  self,
24
+ client: InfrahubClient,
25
+ infrahub_node: type[InfrahubNode],
26
+ convert_query_response: bool = False,
25
27
  branch: str = "",
26
28
  root_directory: str = "",
27
29
  server_url: str = "",
28
- client: InfrahubClient | None = None,
29
30
  ):
30
- self.git: GitRepoManager
31
+ super().__init__(
32
+ client=client,
33
+ infrahub_node=infrahub_node,
34
+ convert_query_response=convert_query_response,
35
+ branch=branch,
36
+ root_directory=root_directory,
37
+ )
31
38
 
32
- self.branch = branch
33
39
  self.server_url = server_url or os.environ.get("INFRAHUB_URL", "http://127.0.0.1:8000")
34
- self.root_directory = root_directory or os.getcwd()
35
-
36
40
  self._client = client
37
41
 
38
42
  if not self.name:
@@ -43,24 +47,7 @@ class InfrahubTransform:
43
47
 
44
48
  @property
45
49
  def client(self) -> InfrahubClient:
46
- if self._client:
47
- return self._client
48
-
49
- raise UninitializedError("The client has not been initialized")
50
-
51
- @property
52
- def branch_name(self) -> str:
53
- """Return the name of the current git branch."""
54
-
55
- if self.branch:
56
- return self.branch
57
-
58
- if not hasattr(self, "git") or not self.git:
59
- self.git = GitRepoManager(self.root_directory)
60
-
61
- self.branch = str(self.git.active_branch)
62
-
63
- return self.branch
50
+ return self._init_client
64
51
 
65
52
  @abstractmethod
66
53
  def transform(self, data: dict) -> Any:
@@ -86,6 +73,7 @@ class InfrahubTransform:
86
73
  data = await self.collect_data()
87
74
 
88
75
  unpacked = data.get("data") or data
76
+ await self.process_nodes(data=unpacked)
89
77
 
90
78
  if asyncio.iscoroutinefunction(self.transform):
91
79
  return await self.transform(data=unpacked)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: infrahub-server
3
- Version: 1.2.7
3
+ Version: 1.2.9rc0
4
4
  Summary: Infrahub is taking a new approach to Infrastructure Management by providing a new generation of datastore to organize and control all the data that defines how an infrastructure should run.
5
5
  Home-page: https://opsmill.com
6
6
  License: AGPL-3.0-only
@@ -39,7 +39,7 @@ Requires-Dist: opentelemetry-exporter-otlp-proto-grpc (==1.28.1)
39
39
  Requires-Dist: opentelemetry-exporter-otlp-proto-http (==1.28.1)
40
40
  Requires-Dist: opentelemetry-instrumentation-aio-pika (==0.49b1)
41
41
  Requires-Dist: opentelemetry-instrumentation-fastapi (==0.49b1)
42
- Requires-Dist: prefect (==3.3.4)
42
+ Requires-Dist: prefect (==3.3.7)
43
43
  Requires-Dist: prefect-redis (==0.2.2)
44
44
  Requires-Dist: pyarrow (>=14,<15)
45
45
  Requires-Dist: pydantic (>=2.10,<2.11)