infrahub-server 1.2.6__py3-none-any.whl → 1.2.7__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.
- infrahub/cli/db.py +2 -0
- infrahub/cli/patch.py +153 -0
- infrahub/computed_attribute/models.py +81 -1
- infrahub/computed_attribute/tasks.py +34 -53
- infrahub/core/node/__init__.py +4 -1
- infrahub/core/query/ipam.py +7 -5
- infrahub/patch/__init__.py +0 -0
- infrahub/patch/constants.py +13 -0
- infrahub/patch/edge_adder.py +64 -0
- infrahub/patch/edge_deleter.py +33 -0
- infrahub/patch/edge_updater.py +28 -0
- infrahub/patch/models.py +98 -0
- infrahub/patch/plan_reader.py +107 -0
- infrahub/patch/plan_writer.py +92 -0
- infrahub/patch/queries/__init__.py +0 -0
- infrahub/patch/queries/base.py +17 -0
- infrahub/patch/runner.py +254 -0
- infrahub/patch/vertex_adder.py +61 -0
- infrahub/patch/vertex_deleter.py +33 -0
- infrahub/patch/vertex_updater.py +28 -0
- infrahub_sdk/checks.py +1 -1
- infrahub_sdk/ctl/cli_commands.py +2 -2
- infrahub_sdk/ctl/menu.py +56 -13
- infrahub_sdk/ctl/object.py +55 -5
- infrahub_sdk/ctl/utils.py +22 -1
- infrahub_sdk/exceptions.py +19 -1
- infrahub_sdk/node.py +42 -26
- infrahub_sdk/protocols_generator/__init__.py +0 -0
- infrahub_sdk/protocols_generator/constants.py +28 -0
- infrahub_sdk/{code_generator.py → protocols_generator/generator.py} +47 -34
- infrahub_sdk/protocols_generator/template.j2 +114 -0
- infrahub_sdk/schema/__init__.py +110 -74
- infrahub_sdk/schema/main.py +36 -2
- infrahub_sdk/schema/repository.py +2 -0
- infrahub_sdk/spec/menu.py +3 -3
- infrahub_sdk/spec/object.py +522 -41
- infrahub_sdk/testing/docker.py +4 -5
- infrahub_sdk/testing/schemas/animal.py +7 -0
- infrahub_sdk/yaml.py +63 -7
- {infrahub_server-1.2.6.dist-info → infrahub_server-1.2.7.dist-info}/METADATA +1 -1
- {infrahub_server-1.2.6.dist-info → infrahub_server-1.2.7.dist-info}/RECORD +44 -27
- infrahub_sdk/ctl/constants.py +0 -115
- {infrahub_server-1.2.6.dist-info → infrahub_server-1.2.7.dist-info}/LICENSE.txt +0 -0
- {infrahub_server-1.2.6.dist-info → infrahub_server-1.2.7.dist-info}/WHEEL +0 -0
- {infrahub_server-1.2.6.dist-info → infrahub_server-1.2.7.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Generated by "infrahubctl protocols"
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING, Optional
|
|
8
|
+
|
|
9
|
+
from infrahub_sdk.protocols import {{ "CoreNode" | syncify(sync) }}, {{ base_protocols | join(', ') }}
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from infrahub_sdk.node import {{ "RelatedNode" | syncify(sync) }}, {{ "RelationshipManager" | syncify(sync) }}
|
|
13
|
+
from infrahub_sdk.protocols_base import (
|
|
14
|
+
AnyAttribute,
|
|
15
|
+
AnyAttributeOptional,
|
|
16
|
+
String,
|
|
17
|
+
StringOptional,
|
|
18
|
+
Integer,
|
|
19
|
+
IntegerOptional,
|
|
20
|
+
Boolean,
|
|
21
|
+
BooleanOptional,
|
|
22
|
+
DateTime,
|
|
23
|
+
DateTimeOptional,
|
|
24
|
+
Dropdown,
|
|
25
|
+
DropdownOptional,
|
|
26
|
+
HashedPassword,
|
|
27
|
+
HashedPasswordOptional,
|
|
28
|
+
MacAddress,
|
|
29
|
+
MacAddressOptional,
|
|
30
|
+
IPHost,
|
|
31
|
+
IPHostOptional,
|
|
32
|
+
IPNetwork,
|
|
33
|
+
IPNetworkOptional,
|
|
34
|
+
JSONAttribute,
|
|
35
|
+
JSONAttributeOptional,
|
|
36
|
+
ListAttribute,
|
|
37
|
+
ListAttributeOptional,
|
|
38
|
+
URL,
|
|
39
|
+
URLOptional,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
{% for generic in generics %}
|
|
43
|
+
|
|
44
|
+
class {{ generic.namespace + generic.name }}({{core_node_name}}):
|
|
45
|
+
{% if not generic.attributes|default([]) and not generic.relationships|default([]) %}
|
|
46
|
+
pass
|
|
47
|
+
{% endif %}
|
|
48
|
+
{% for attribute in generic.attributes | sort(attribute='name') | default([]) %}
|
|
49
|
+
{{ attribute | render_attribute }}
|
|
50
|
+
{% endfor %}
|
|
51
|
+
{% for relationship in generic.relationships | sort(attribute='name') | default([]) %}
|
|
52
|
+
{{ relationship | render_relationship(sync) }}
|
|
53
|
+
{% endfor %}
|
|
54
|
+
{% if generic.hierarchical | default(false) %}
|
|
55
|
+
parent: {{ "RelatedNode" | syncify(sync) }}
|
|
56
|
+
children: {{ "RelationshipManager" | syncify(sync) }}
|
|
57
|
+
{% endif %}
|
|
58
|
+
{% endfor %}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
{% for node in nodes %}
|
|
62
|
+
|
|
63
|
+
class {{ node.namespace + node.name }}({{ node.inherit_from | syncify(sync) | join(", ") or core_node_name }}):
|
|
64
|
+
{% if not node.attributes|default([]) and not node.relationships|default([]) %}
|
|
65
|
+
pass
|
|
66
|
+
{% endif %}
|
|
67
|
+
{% for attribute in node.attributes | sort(attribute='name') | default([]) %}
|
|
68
|
+
{{ attribute | render_attribute }}
|
|
69
|
+
{% endfor %}
|
|
70
|
+
{% for relationship in node.relationships | sort(attribute='name') | default([]) %}
|
|
71
|
+
{{ relationship | render_relationship(sync) }}
|
|
72
|
+
{% endfor %}
|
|
73
|
+
{% if node.hierarchical | default(false) %}
|
|
74
|
+
parent: {{ "RelatedNode" | syncify(sync) }}
|
|
75
|
+
children: {{ "RelationshipManager" | syncify(sync) }}
|
|
76
|
+
{% endif %}
|
|
77
|
+
|
|
78
|
+
{% endfor %}
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
{% for node in profiles %}
|
|
82
|
+
|
|
83
|
+
class {{ node.namespace + node.name }}({{ node.inherit_from | syncify(sync) | join(", ") or core_node_name }}):
|
|
84
|
+
{% if not node.attributes|default([]) and not node.relationships|default([]) %}
|
|
85
|
+
pass
|
|
86
|
+
{% endif %}
|
|
87
|
+
{% for attribute in node.attributes | sort(attribute='name') | default([]) %}
|
|
88
|
+
{{ attribute | render_attribute }}
|
|
89
|
+
{% endfor %}
|
|
90
|
+
{% for relationship in node.relationships | sort(attribute='name') | default([]) %}
|
|
91
|
+
{{ relationship | render_relationship(sync) }}
|
|
92
|
+
{% endfor %}
|
|
93
|
+
{% if node.hierarchical | default(false) %}
|
|
94
|
+
parent: {{ "RelatedNode" | syncify(sync) }}
|
|
95
|
+
children: {{ "RelationshipManager" | syncify(sync) }}
|
|
96
|
+
{% endif %}
|
|
97
|
+
|
|
98
|
+
{% endfor %}
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
{% for node in templates %}
|
|
102
|
+
|
|
103
|
+
class {{ node.namespace + node.name }}({{ node.inherit_from | syncify(sync) | join(", ") or core_node_name }}):
|
|
104
|
+
{% if not node.attributes|default([]) and not node.relationships|default([]) %}
|
|
105
|
+
pass
|
|
106
|
+
{% endif %}
|
|
107
|
+
{% for attribute in node.attributes | sort(attribute='name') | default([]) %}
|
|
108
|
+
{{ attribute | render_attribute }}
|
|
109
|
+
{% endfor %}
|
|
110
|
+
{% for relationship in node.relationships | sort(attribute='name') | default([]) %}
|
|
111
|
+
{{ relationship | render_relationship(sync) }}
|
|
112
|
+
{% endfor %}
|
|
113
|
+
|
|
114
|
+
{% endfor %}
|
infrahub_sdk/schema/__init__.py
CHANGED
|
@@ -2,6 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import json
|
|
5
|
+
import warnings
|
|
5
6
|
from collections.abc import MutableMapping
|
|
6
7
|
from enum import Enum
|
|
7
8
|
from time import sleep
|
|
@@ -13,6 +14,7 @@ from pydantic import BaseModel, Field
|
|
|
13
14
|
from typing_extensions import TypeAlias
|
|
14
15
|
|
|
15
16
|
from ..exceptions import (
|
|
17
|
+
BranchNotFoundError,
|
|
16
18
|
InvalidResponseError,
|
|
17
19
|
JsonDecodeError,
|
|
18
20
|
SchemaNotFoundError,
|
|
@@ -89,6 +91,13 @@ MainSchemaTypesAll: TypeAlias = Union[
|
|
|
89
91
|
|
|
90
92
|
|
|
91
93
|
class InfrahubSchemaBase:
|
|
94
|
+
client: InfrahubClient | InfrahubClientSync
|
|
95
|
+
cache: dict[str, BranchSchema]
|
|
96
|
+
|
|
97
|
+
def __init__(self, client: InfrahubClient | InfrahubClientSync):
|
|
98
|
+
self.client = client
|
|
99
|
+
self.cache = {}
|
|
100
|
+
|
|
92
101
|
def validate(self, data: dict[str, Any]) -> None:
|
|
93
102
|
SchemaRoot(**data)
|
|
94
103
|
|
|
@@ -101,6 +110,23 @@ class InfrahubSchemaBase:
|
|
|
101
110
|
message=f"{key} is not a valid value for {identifier}",
|
|
102
111
|
)
|
|
103
112
|
|
|
113
|
+
def set_cache(self, schema: dict[str, Any] | SchemaRootAPI | BranchSchema, branch: str | None = None) -> None:
|
|
114
|
+
"""
|
|
115
|
+
Set the cache manually (primarily for unit testing)
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
schema: The schema to set the cache as provided by the /api/schema endpoint either in dict or SchemaRootAPI format
|
|
119
|
+
branch: The name of the branch to set the cache for.
|
|
120
|
+
"""
|
|
121
|
+
branch = branch or self.client.default_branch
|
|
122
|
+
|
|
123
|
+
if isinstance(schema, SchemaRootAPI):
|
|
124
|
+
schema = BranchSchema.from_schema_root_api(data=schema)
|
|
125
|
+
elif isinstance(schema, dict):
|
|
126
|
+
schema = BranchSchema.from_api_response(data=schema)
|
|
127
|
+
|
|
128
|
+
self.cache[branch] = schema
|
|
129
|
+
|
|
104
130
|
def generate_payload_create(
|
|
105
131
|
self,
|
|
106
132
|
schema: MainSchemaTypesAPI,
|
|
@@ -167,11 +193,37 @@ class InfrahubSchemaBase:
|
|
|
167
193
|
|
|
168
194
|
raise ValueError("schema must be a protocol or a string")
|
|
169
195
|
|
|
196
|
+
@staticmethod
|
|
197
|
+
def _parse_schema_response(response: httpx.Response, branch: str) -> MutableMapping[str, Any]:
|
|
198
|
+
if response.status_code == 400:
|
|
199
|
+
raise BranchNotFoundError(
|
|
200
|
+
identifier=branch, message=f"The requested branch was not found on the server [{branch}]"
|
|
201
|
+
)
|
|
202
|
+
response.raise_for_status()
|
|
203
|
+
|
|
204
|
+
try:
|
|
205
|
+
data: MutableMapping[str, Any] = response.json()
|
|
206
|
+
except json.decoder.JSONDecodeError as exc:
|
|
207
|
+
raise JsonDecodeError(
|
|
208
|
+
message=f"Invalid Schema response received from the server at {response.url}: {response.text} [{response.status_code}] ",
|
|
209
|
+
content=response.text,
|
|
210
|
+
url=str(response.url),
|
|
211
|
+
) from exc
|
|
212
|
+
|
|
213
|
+
return data
|
|
214
|
+
|
|
215
|
+
@staticmethod
|
|
216
|
+
def _deprecated_schema_timeout() -> None:
|
|
217
|
+
warnings.warn(
|
|
218
|
+
"The 'timeout' parameter is deprecated while fetching the schema and will be removed version 2.0.0 of the Infrahub Python SDK. "
|
|
219
|
+
"Use client.default_timeout instead.",
|
|
220
|
+
DeprecationWarning,
|
|
221
|
+
stacklevel=2,
|
|
222
|
+
)
|
|
223
|
+
|
|
170
224
|
|
|
171
225
|
class InfrahubSchema(InfrahubSchemaBase):
|
|
172
|
-
|
|
173
|
-
self.client = client
|
|
174
|
-
self.cache: dict[str, BranchSchema] = {}
|
|
226
|
+
client: InfrahubClient
|
|
175
227
|
|
|
176
228
|
async def get(
|
|
177
229
|
self,
|
|
@@ -184,8 +236,11 @@ class InfrahubSchema(InfrahubSchemaBase):
|
|
|
184
236
|
|
|
185
237
|
kind_str = self._get_schema_name(schema=kind)
|
|
186
238
|
|
|
239
|
+
if timeout:
|
|
240
|
+
self._deprecated_schema_timeout()
|
|
241
|
+
|
|
187
242
|
if refresh:
|
|
188
|
-
self.cache[branch] = await self._fetch(branch=branch
|
|
243
|
+
self.cache[branch] = await self._fetch(branch=branch)
|
|
189
244
|
|
|
190
245
|
if branch in self.cache and kind_str in self.cache[branch].nodes:
|
|
191
246
|
return self.cache[branch].nodes[kind_str]
|
|
@@ -193,7 +248,7 @@ class InfrahubSchema(InfrahubSchemaBase):
|
|
|
193
248
|
# Fetching the latest schema from the server if we didn't fetch it earlier
|
|
194
249
|
# because we coulnd't find the object on the local cache
|
|
195
250
|
if not refresh:
|
|
196
|
-
self.cache[branch] = await self._fetch(branch=branch
|
|
251
|
+
self.cache[branch] = await self._fetch(branch=branch)
|
|
197
252
|
|
|
198
253
|
if branch in self.cache and kind_str in self.cache[branch].nodes:
|
|
199
254
|
return self.cache[branch].nodes[kind_str]
|
|
@@ -396,67 +451,45 @@ class InfrahubSchema(InfrahubSchemaBase):
|
|
|
396
451
|
)
|
|
397
452
|
|
|
398
453
|
async def fetch(
|
|
399
|
-
self, branch: str, namespaces: list[str] | None = None, timeout: int | None = None
|
|
454
|
+
self, branch: str, namespaces: list[str] | None = None, timeout: int | None = None, populate_cache: bool = True
|
|
400
455
|
) -> MutableMapping[str, MainSchemaTypesAPI]:
|
|
401
456
|
"""Fetch the schema from the server for a given branch.
|
|
402
457
|
|
|
403
458
|
Args:
|
|
404
|
-
branch
|
|
405
|
-
timeout
|
|
459
|
+
branch: Name of the branch to fetch the schema for.
|
|
460
|
+
timeout: Overrides default timeout used when querying the schema. deprecated.
|
|
461
|
+
populate_cache: Whether to populate the cache with the fetched schema. Defaults to True.
|
|
406
462
|
|
|
407
463
|
Returns:
|
|
408
464
|
dict[str, MainSchemaTypes]: Dictionary of all schema organized by kind
|
|
409
465
|
"""
|
|
410
|
-
|
|
466
|
+
|
|
467
|
+
if timeout:
|
|
468
|
+
self._deprecated_schema_timeout()
|
|
469
|
+
|
|
470
|
+
branch_schema = await self._fetch(branch=branch, namespaces=namespaces)
|
|
471
|
+
|
|
472
|
+
if populate_cache:
|
|
473
|
+
self.cache[branch] = branch_schema
|
|
474
|
+
|
|
411
475
|
return branch_schema.nodes
|
|
412
476
|
|
|
413
|
-
async def _fetch(
|
|
414
|
-
self, branch: str, namespaces: list[str] | None = None, timeout: int | None = None
|
|
415
|
-
) -> BranchSchema:
|
|
477
|
+
async def _fetch(self, branch: str, namespaces: list[str] | None = None) -> BranchSchema:
|
|
416
478
|
url_parts = [("branch", branch)]
|
|
417
479
|
if namespaces:
|
|
418
480
|
url_parts.extend([("namespaces", ns) for ns in namespaces])
|
|
419
481
|
query_params = urlencode(url_parts)
|
|
420
482
|
url = f"{self.client.address}/api/schema?{query_params}"
|
|
421
483
|
|
|
422
|
-
response = await self.client._get(url=url
|
|
423
|
-
response.raise_for_status()
|
|
424
|
-
|
|
425
|
-
try:
|
|
426
|
-
data: MutableMapping[str, Any] = response.json()
|
|
427
|
-
except json.decoder.JSONDecodeError as exc:
|
|
428
|
-
raise JsonDecodeError(
|
|
429
|
-
message=f"Invalid Schema response received from the server at {response.url}: {response.text} [{response.status_code}] ",
|
|
430
|
-
content=response.text,
|
|
431
|
-
url=response.url,
|
|
432
|
-
) from exc
|
|
433
|
-
|
|
434
|
-
nodes: MutableMapping[str, MainSchemaTypesAPI] = {}
|
|
435
|
-
for node_schema in data.get("nodes", []):
|
|
436
|
-
node = NodeSchemaAPI(**node_schema)
|
|
437
|
-
nodes[node.kind] = node
|
|
438
|
-
|
|
439
|
-
for generic_schema in data.get("generics", []):
|
|
440
|
-
generic = GenericSchemaAPI(**generic_schema)
|
|
441
|
-
nodes[generic.kind] = generic
|
|
442
|
-
|
|
443
|
-
for profile_schema in data.get("profiles", []):
|
|
444
|
-
profile = ProfileSchemaAPI(**profile_schema)
|
|
445
|
-
nodes[profile.kind] = profile
|
|
484
|
+
response = await self.client._get(url=url)
|
|
446
485
|
|
|
447
|
-
|
|
448
|
-
template = TemplateSchemaAPI(**template_schema)
|
|
449
|
-
nodes[template.kind] = template
|
|
486
|
+
data = self._parse_schema_response(response=response, branch=branch)
|
|
450
487
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
return BranchSchema(hash=schema_hash, nodes=nodes)
|
|
488
|
+
return BranchSchema.from_api_response(data=data)
|
|
454
489
|
|
|
455
490
|
|
|
456
491
|
class InfrahubSchemaSync(InfrahubSchemaBase):
|
|
457
|
-
|
|
458
|
-
self.client = client
|
|
459
|
-
self.cache: dict[str, BranchSchema] = {}
|
|
492
|
+
client: InfrahubClientSync
|
|
460
493
|
|
|
461
494
|
def all(
|
|
462
495
|
self,
|
|
@@ -494,10 +527,25 @@ class InfrahubSchemaSync(InfrahubSchemaBase):
|
|
|
494
527
|
refresh: bool = False,
|
|
495
528
|
timeout: int | None = None,
|
|
496
529
|
) -> MainSchemaTypesAPI:
|
|
530
|
+
"""
|
|
531
|
+
Retrieve a specific schema object from the server.
|
|
532
|
+
|
|
533
|
+
Args:
|
|
534
|
+
kind: The kind of schema object to retrieve.
|
|
535
|
+
branch: The branch to retrieve the schema from.
|
|
536
|
+
refresh: Whether to refresh the schema.
|
|
537
|
+
timeout: Overrides default timeout used when querying the GraphQL API. Specified in seconds (deprecated).
|
|
538
|
+
|
|
539
|
+
Returns:
|
|
540
|
+
MainSchemaTypes: The schema object.
|
|
541
|
+
"""
|
|
497
542
|
branch = branch or self.client.default_branch
|
|
498
543
|
|
|
499
544
|
kind_str = self._get_schema_name(schema=kind)
|
|
500
545
|
|
|
546
|
+
if timeout:
|
|
547
|
+
self._deprecated_schema_timeout()
|
|
548
|
+
|
|
501
549
|
if refresh:
|
|
502
550
|
self.cache[branch] = self._fetch(branch=branch)
|
|
503
551
|
|
|
@@ -507,7 +555,7 @@ class InfrahubSchemaSync(InfrahubSchemaBase):
|
|
|
507
555
|
# Fetching the latest schema from the server if we didn't fetch it earlier
|
|
508
556
|
# because we coulnd't find the object on the local cache
|
|
509
557
|
if not refresh:
|
|
510
|
-
self.cache[branch] = self._fetch(branch=branch
|
|
558
|
+
self.cache[branch] = self._fetch(branch=branch)
|
|
511
559
|
|
|
512
560
|
if branch in self.cache and kind_str in self.cache[branch].nodes:
|
|
513
561
|
return self.cache[branch].nodes[kind_str]
|
|
@@ -627,51 +675,39 @@ class InfrahubSchemaSync(InfrahubSchemaBase):
|
|
|
627
675
|
)
|
|
628
676
|
|
|
629
677
|
def fetch(
|
|
630
|
-
self, branch: str, namespaces: list[str] | None = None, timeout: int | None = None
|
|
678
|
+
self, branch: str, namespaces: list[str] | None = None, timeout: int | None = None, populate_cache: bool = True
|
|
631
679
|
) -> MutableMapping[str, MainSchemaTypesAPI]:
|
|
632
680
|
"""Fetch the schema from the server for a given branch.
|
|
633
681
|
|
|
634
682
|
Args:
|
|
635
|
-
branch
|
|
636
|
-
timeout
|
|
683
|
+
branch: Name of the branch to fetch the schema for.
|
|
684
|
+
timeout: Overrides default timeout used when querying the GraphQL API. Specified in seconds (deprecated).
|
|
685
|
+
populate_cache: Whether to populate the cache with the fetched schema. Defaults to True.
|
|
637
686
|
|
|
638
687
|
Returns:
|
|
639
688
|
dict[str, MainSchemaTypes]: Dictionary of all schema organized by kind
|
|
640
689
|
"""
|
|
641
|
-
|
|
690
|
+
if timeout:
|
|
691
|
+
self._deprecated_schema_timeout()
|
|
692
|
+
|
|
693
|
+
branch_schema = self._fetch(branch=branch, namespaces=namespaces)
|
|
694
|
+
|
|
695
|
+
if populate_cache:
|
|
696
|
+
self.cache[branch] = branch_schema
|
|
697
|
+
|
|
642
698
|
return branch_schema.nodes
|
|
643
699
|
|
|
644
|
-
def _fetch(self, branch: str, namespaces: list[str] | None = None
|
|
700
|
+
def _fetch(self, branch: str, namespaces: list[str] | None = None) -> BranchSchema:
|
|
645
701
|
url_parts = [("branch", branch)]
|
|
646
702
|
if namespaces:
|
|
647
703
|
url_parts.extend([("namespaces", ns) for ns in namespaces])
|
|
648
704
|
query_params = urlencode(url_parts)
|
|
649
705
|
url = f"{self.client.address}/api/schema?{query_params}"
|
|
650
|
-
response = self.client._get(url=url
|
|
651
|
-
response.raise_for_status()
|
|
652
|
-
|
|
653
|
-
data: MutableMapping[str, Any] = response.json()
|
|
654
|
-
|
|
655
|
-
nodes: MutableMapping[str, MainSchemaTypesAPI] = {}
|
|
656
|
-
for node_schema in data.get("nodes", []):
|
|
657
|
-
node = NodeSchemaAPI(**node_schema)
|
|
658
|
-
nodes[node.kind] = node
|
|
659
|
-
|
|
660
|
-
for generic_schema in data.get("generics", []):
|
|
661
|
-
generic = GenericSchemaAPI(**generic_schema)
|
|
662
|
-
nodes[generic.kind] = generic
|
|
663
|
-
|
|
664
|
-
for profile_schema in data.get("profiles", []):
|
|
665
|
-
profile = ProfileSchemaAPI(**profile_schema)
|
|
666
|
-
nodes[profile.kind] = profile
|
|
667
|
-
|
|
668
|
-
for template_schema in data.get("templates", []):
|
|
669
|
-
template = TemplateSchemaAPI(**template_schema)
|
|
670
|
-
nodes[template.kind] = template
|
|
706
|
+
response = self.client._get(url=url)
|
|
671
707
|
|
|
672
|
-
|
|
708
|
+
data = self._parse_schema_response(response=response, branch=branch)
|
|
673
709
|
|
|
674
|
-
return BranchSchema(
|
|
710
|
+
return BranchSchema.from_api_response(data=data)
|
|
675
711
|
|
|
676
712
|
def load(
|
|
677
713
|
self, schemas: list[dict], branch: str | None = None, wait_until_converged: bool = False
|
infrahub_sdk/schema/main.py
CHANGED
|
@@ -6,6 +6,7 @@ from enum import Enum
|
|
|
6
6
|
from typing import TYPE_CHECKING, Any, Union
|
|
7
7
|
|
|
8
8
|
from pydantic import BaseModel, ConfigDict, Field
|
|
9
|
+
from typing_extensions import Self
|
|
9
10
|
|
|
10
11
|
if TYPE_CHECKING:
|
|
11
12
|
from ..node import InfrahubNode, InfrahubNodeSync
|
|
@@ -231,7 +232,11 @@ class BaseSchemaAttrRelAPI(BaseModel):
|
|
|
231
232
|
|
|
232
233
|
@property
|
|
233
234
|
def mandatory_attribute_names(self) -> list[str]:
|
|
234
|
-
return [
|
|
235
|
+
return [
|
|
236
|
+
item.name
|
|
237
|
+
for item in self.attributes
|
|
238
|
+
if (not item.optional and item.default_value is None) and not item.read_only
|
|
239
|
+
]
|
|
235
240
|
|
|
236
241
|
@property
|
|
237
242
|
def mandatory_relationship_names(self) -> list[str]:
|
|
@@ -344,7 +349,7 @@ class SchemaRoot(BaseModel):
|
|
|
344
349
|
class SchemaRootAPI(BaseModel):
|
|
345
350
|
model_config = ConfigDict(use_enum_values=True)
|
|
346
351
|
|
|
347
|
-
|
|
352
|
+
main: str | None = None
|
|
348
353
|
generics: list[GenericSchemaAPI] = Field(default_factory=list)
|
|
349
354
|
nodes: list[NodeSchemaAPI] = Field(default_factory=list)
|
|
350
355
|
profiles: list[ProfileSchemaAPI] = Field(default_factory=list)
|
|
@@ -356,3 +361,32 @@ class BranchSchema(BaseModel):
|
|
|
356
361
|
nodes: MutableMapping[str, GenericSchemaAPI | NodeSchemaAPI | ProfileSchemaAPI | TemplateSchemaAPI] = Field(
|
|
357
362
|
default_factory=dict
|
|
358
363
|
)
|
|
364
|
+
|
|
365
|
+
@classmethod
|
|
366
|
+
def from_api_response(cls, data: MutableMapping[str, Any]) -> Self:
|
|
367
|
+
"""
|
|
368
|
+
Convert an API response from /api/schema into a BranchSchema object.
|
|
369
|
+
"""
|
|
370
|
+
return cls.from_schema_root_api(data=SchemaRootAPI(**data))
|
|
371
|
+
|
|
372
|
+
@classmethod
|
|
373
|
+
def from_schema_root_api(cls, data: SchemaRootAPI) -> Self:
|
|
374
|
+
"""
|
|
375
|
+
Convert a SchemaRootAPI object to a BranchSchema object.
|
|
376
|
+
"""
|
|
377
|
+
nodes: MutableMapping[str, GenericSchemaAPI | NodeSchemaAPI | ProfileSchemaAPI | TemplateSchemaAPI] = {}
|
|
378
|
+
for node in data.nodes:
|
|
379
|
+
nodes[node.kind] = node
|
|
380
|
+
|
|
381
|
+
for generic in data.generics:
|
|
382
|
+
nodes[generic.kind] = generic
|
|
383
|
+
|
|
384
|
+
for profile in data.profiles:
|
|
385
|
+
nodes[profile.kind] = profile
|
|
386
|
+
|
|
387
|
+
for template in data.templates:
|
|
388
|
+
nodes[template.kind] = template
|
|
389
|
+
|
|
390
|
+
schema_hash = data.main or ""
|
|
391
|
+
|
|
392
|
+
return cls(hash=schema_hash, nodes=nodes)
|
|
@@ -83,6 +83,7 @@ class InfrahubCheckDefinitionConfig(InfrahubRepositoryConfigElement):
|
|
|
83
83
|
|
|
84
84
|
class InfrahubGeneratorDefinitionConfig(InfrahubRepositoryConfigElement):
|
|
85
85
|
model_config = ConfigDict(extra="forbid")
|
|
86
|
+
|
|
86
87
|
name: str = Field(..., description="The name of the Generator Definition")
|
|
87
88
|
file_path: Path = Field(..., description="The file within the repository with the generator code.")
|
|
88
89
|
query: str = Field(..., description="The GraphQL query to use as input.")
|
|
@@ -112,6 +113,7 @@ class InfrahubGeneratorDefinitionConfig(InfrahubRepositoryConfigElement):
|
|
|
112
113
|
|
|
113
114
|
class InfrahubPythonTransformConfig(InfrahubRepositoryConfigElement):
|
|
114
115
|
model_config = ConfigDict(extra="forbid")
|
|
116
|
+
|
|
115
117
|
name: str = Field(..., description="The name of the Transform")
|
|
116
118
|
file_path: Path = Field(..., description="The file within the repository with the transform code.")
|
|
117
119
|
class_name: str = Field(default="Transform", description="The name of the transform class to run.")
|
infrahub_sdk/spec/menu.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from ..yaml import InfrahubFile, InfrahubFileKind
|
|
4
|
-
from .object import InfrahubObjectFileData
|
|
4
|
+
from .object import InfrahubObjectFileData, ObjectFile
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
class InfrahubMenuFileData(InfrahubObjectFileData):
|
|
@@ -18,7 +18,7 @@ class InfrahubMenuFileData(InfrahubObjectFileData):
|
|
|
18
18
|
return data
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
class MenuFile(
|
|
21
|
+
class MenuFile(ObjectFile):
|
|
22
22
|
_spec: InfrahubMenuFileData | None = None
|
|
23
23
|
|
|
24
24
|
@property
|
|
@@ -28,7 +28,7 @@ class MenuFile(InfrahubFile):
|
|
|
28
28
|
return self._spec
|
|
29
29
|
|
|
30
30
|
def validate_content(self) -> None:
|
|
31
|
-
|
|
31
|
+
InfrahubFile.validate_content(self)
|
|
32
32
|
if self.kind != InfrahubFileKind.MENU:
|
|
33
33
|
raise ValueError("File is not an Infrahub Menu file")
|
|
34
34
|
self._spec = InfrahubMenuFileData(**self.data.spec)
|