infrahub-server 1.6.2__py3-none-any.whl → 1.7.0__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/actions/tasks.py +4 -2
- infrahub/api/exceptions.py +2 -2
- infrahub/api/schema.py +3 -1
- infrahub/artifacts/tasks.py +1 -0
- infrahub/auth.py +2 -2
- infrahub/cli/db.py +54 -28
- infrahub/computed_attribute/gather.py +3 -4
- infrahub/computed_attribute/tasks.py +23 -6
- infrahub/config.py +8 -0
- infrahub/constants/enums.py +12 -0
- infrahub/core/account.py +12 -9
- infrahub/core/attribute.py +106 -108
- infrahub/core/branch/models.py +44 -71
- infrahub/core/branch/tasks.py +5 -3
- infrahub/core/changelog/diff.py +1 -20
- infrahub/core/changelog/models.py +0 -7
- infrahub/core/constants/__init__.py +17 -0
- infrahub/core/constants/database.py +0 -1
- infrahub/core/constants/schema.py +0 -1
- infrahub/core/convert_object_type/repository_conversion.py +3 -4
- infrahub/core/diff/branch_differ.py +1 -1
- infrahub/core/diff/conflict_transferer.py +1 -1
- infrahub/core/diff/data_check_synchronizer.py +4 -3
- infrahub/core/diff/enricher/cardinality_one.py +2 -2
- infrahub/core/diff/enricher/hierarchy.py +1 -1
- infrahub/core/diff/enricher/labels.py +1 -1
- infrahub/core/diff/merger/merger.py +28 -2
- infrahub/core/diff/merger/serializer.py +3 -10
- infrahub/core/diff/model/diff.py +1 -1
- infrahub/core/diff/query/merge.py +376 -135
- infrahub/core/diff/repository/repository.py +3 -1
- infrahub/core/graph/__init__.py +1 -1
- infrahub/core/graph/constraints.py +3 -3
- infrahub/core/graph/schema.py +2 -12
- infrahub/core/ipam/reconciler.py +8 -6
- infrahub/core/ipam/utilization.py +8 -15
- infrahub/core/manager.py +133 -152
- infrahub/core/merge.py +1 -1
- infrahub/core/metadata/__init__.py +0 -0
- infrahub/core/metadata/interface.py +37 -0
- infrahub/core/metadata/model.py +31 -0
- infrahub/core/metadata/query/__init__.py +0 -0
- infrahub/core/metadata/query/node_metadata.py +301 -0
- infrahub/core/migrations/graph/__init__.py +4 -0
- infrahub/core/migrations/graph/m012_convert_account_generic.py +12 -12
- infrahub/core/migrations/graph/m013_convert_git_password_credential.py +7 -12
- infrahub/core/migrations/graph/m017_add_core_profile.py +5 -2
- infrahub/core/migrations/graph/m018_uniqueness_nulls.py +2 -1
- infrahub/core/migrations/graph/m019_restore_rels_to_time.py +0 -10
- infrahub/core/migrations/graph/m020_duplicate_edges.py +0 -8
- infrahub/core/migrations/graph/m025_uniqueness_nulls.py +2 -1
- infrahub/core/migrations/graph/m026_0000_prefix_fix.py +2 -1
- infrahub/core/migrations/graph/m029_duplicates_cleanup.py +0 -1
- infrahub/core/migrations/graph/m031_check_number_attributes.py +2 -2
- infrahub/core/migrations/graph/m038_redo_0000_prefix_fix.py +2 -1
- infrahub/core/migrations/graph/m041_deleted_dup_edges.py +1 -1
- infrahub/core/migrations/graph/m049_remove_is_visible_relationship.py +53 -0
- infrahub/core/migrations/graph/m050_backfill_vertex_metadata.py +168 -0
- infrahub/core/migrations/query/__init__.py +2 -2
- infrahub/core/migrations/query/attribute_add.py +17 -6
- infrahub/core/migrations/query/attribute_remove.py +19 -5
- infrahub/core/migrations/query/attribute_rename.py +21 -5
- infrahub/core/migrations/query/node_duplicate.py +19 -4
- infrahub/core/migrations/query/schema_attribute_update.py +1 -1
- infrahub/core/migrations/schema/attribute_kind_update.py +26 -6
- infrahub/core/migrations/schema/attribute_name_update.py +1 -1
- infrahub/core/migrations/schema/attribute_supports_profile.py +5 -3
- infrahub/core/migrations/schema/models.py +3 -0
- infrahub/core/migrations/schema/node_attribute_add.py +5 -2
- infrahub/core/migrations/schema/node_attribute_remove.py +1 -1
- infrahub/core/migrations/schema/node_kind_update.py +1 -1
- infrahub/core/migrations/schema/node_remove.py +24 -2
- infrahub/core/migrations/schema/tasks.py +4 -1
- infrahub/core/migrations/shared.py +13 -6
- infrahub/core/models.py +6 -6
- infrahub/core/node/__init__.py +157 -58
- infrahub/core/node/base.py +9 -5
- infrahub/core/node/create.py +7 -3
- infrahub/core/node/delete_validator.py +1 -1
- infrahub/core/node/standard.py +100 -14
- infrahub/core/order.py +30 -0
- infrahub/core/property.py +0 -1
- infrahub/core/protocols.py +1 -0
- infrahub/core/protocols_base.py +10 -2
- infrahub/core/query/__init__.py +11 -6
- infrahub/core/query/attribute.py +164 -49
- infrahub/core/query/branch.py +58 -70
- infrahub/core/query/delete.py +1 -1
- infrahub/core/query/diff.py +7 -7
- infrahub/core/query/ipam.py +104 -43
- infrahub/core/query/node.py +1072 -281
- infrahub/core/query/relationship.py +531 -325
- infrahub/core/query/resource_manager.py +107 -18
- infrahub/core/query/standard_node.py +25 -5
- infrahub/core/query/utils.py +2 -4
- infrahub/core/relationship/constraints/count.py +1 -1
- infrahub/core/relationship/constraints/peer_kind.py +1 -1
- infrahub/core/relationship/constraints/peer_parent.py +1 -1
- infrahub/core/relationship/constraints/peer_relatives.py +1 -1
- infrahub/core/relationship/constraints/profiles_kind.py +1 -1
- infrahub/core/relationship/constraints/profiles_removal.py +168 -0
- infrahub/core/relationship/model.py +293 -139
- infrahub/core/schema/attribute_parameters.py +28 -1
- infrahub/core/schema/attribute_schema.py +11 -17
- infrahub/core/schema/basenode_schema.py +3 -0
- infrahub/core/schema/definitions/core/__init__.py +8 -2
- infrahub/core/schema/definitions/core/account.py +10 -10
- infrahub/core/schema/definitions/core/artifact.py +14 -8
- infrahub/core/schema/definitions/core/check.py +10 -4
- infrahub/core/schema/definitions/core/generator.py +26 -6
- infrahub/core/schema/definitions/core/graphql_query.py +1 -1
- infrahub/core/schema/definitions/core/group.py +9 -2
- infrahub/core/schema/definitions/core/ipam.py +80 -10
- infrahub/core/schema/definitions/core/menu.py +41 -7
- infrahub/core/schema/definitions/core/permission.py +16 -2
- infrahub/core/schema/definitions/core/profile.py +16 -2
- infrahub/core/schema/definitions/core/propose_change.py +24 -4
- infrahub/core/schema/definitions/core/propose_change_comment.py +23 -11
- infrahub/core/schema/definitions/core/propose_change_validator.py +50 -21
- infrahub/core/schema/definitions/core/repository.py +10 -0
- infrahub/core/schema/definitions/core/resource_pool.py +8 -1
- infrahub/core/schema/definitions/core/template.py +19 -2
- infrahub/core/schema/definitions/core/transform.py +11 -5
- infrahub/core/schema/definitions/core/webhook.py +27 -9
- infrahub/core/schema/manager.py +63 -43
- infrahub/core/schema/relationship_schema.py +6 -2
- infrahub/core/schema/schema_branch.py +115 -11
- infrahub/core/task/task.py +4 -2
- infrahub/core/utils.py +3 -25
- infrahub/core/validators/aggregated_checker.py +1 -1
- infrahub/core/validators/attribute/choices.py +1 -1
- infrahub/core/validators/attribute/enum.py +1 -1
- infrahub/core/validators/attribute/kind.py +6 -3
- infrahub/core/validators/attribute/length.py +1 -1
- infrahub/core/validators/attribute/min_max.py +1 -1
- infrahub/core/validators/attribute/number_pool.py +1 -1
- infrahub/core/validators/attribute/optional.py +1 -1
- infrahub/core/validators/attribute/regex.py +1 -1
- infrahub/core/validators/determiner.py +3 -3
- infrahub/core/validators/node/attribute.py +1 -1
- infrahub/core/validators/node/relationship.py +1 -1
- infrahub/core/validators/relationship/peer.py +1 -1
- infrahub/database/__init__.py +4 -4
- infrahub/dependencies/builder/constraint/grouped/node_runner.py +2 -0
- infrahub/dependencies/builder/constraint/relationship_manager/profiles_removal.py +8 -0
- infrahub/dependencies/registry.py +2 -0
- infrahub/display_labels/tasks.py +12 -3
- infrahub/git/integrator.py +18 -18
- infrahub/git/tasks.py +1 -1
- infrahub/git/utils.py +1 -1
- infrahub/graphql/app.py +2 -2
- infrahub/graphql/constants.py +3 -0
- infrahub/graphql/context.py +1 -1
- infrahub/graphql/field_extractor.py +1 -1
- infrahub/graphql/initialization.py +11 -0
- infrahub/graphql/loaders/account.py +134 -0
- infrahub/graphql/loaders/node.py +5 -12
- infrahub/graphql/loaders/peers.py +5 -7
- infrahub/graphql/manager.py +175 -21
- infrahub/graphql/metadata.py +91 -0
- infrahub/graphql/mutations/account.py +6 -6
- infrahub/graphql/mutations/attribute.py +0 -2
- infrahub/graphql/mutations/branch.py +9 -5
- infrahub/graphql/mutations/computed_attribute.py +1 -1
- infrahub/graphql/mutations/display_label.py +1 -1
- infrahub/graphql/mutations/hfid.py +1 -1
- infrahub/graphql/mutations/ipam.py +4 -6
- infrahub/graphql/mutations/main.py +9 -4
- infrahub/graphql/mutations/profile.py +16 -22
- infrahub/graphql/mutations/proposed_change.py +4 -4
- infrahub/graphql/mutations/relationship.py +40 -10
- infrahub/graphql/mutations/repository.py +14 -12
- infrahub/graphql/mutations/schema.py +2 -2
- infrahub/graphql/order.py +14 -0
- infrahub/graphql/queries/branch.py +62 -6
- infrahub/graphql/queries/diff/tree.py +5 -5
- infrahub/graphql/queries/resource_manager.py +25 -24
- infrahub/graphql/resolvers/account_metadata.py +84 -0
- infrahub/graphql/resolvers/ipam.py +6 -8
- infrahub/graphql/resolvers/many_relationship.py +77 -35
- infrahub/graphql/resolvers/resolver.py +59 -14
- infrahub/graphql/resolvers/single_relationship.py +87 -23
- infrahub/graphql/subscription/graphql_query.py +2 -0
- infrahub/graphql/types/__init__.py +0 -1
- infrahub/graphql/types/attribute.py +10 -5
- infrahub/graphql/types/branch.py +40 -53
- infrahub/graphql/types/enums.py +3 -0
- infrahub/graphql/types/metadata.py +28 -0
- infrahub/graphql/types/node.py +22 -2
- infrahub/graphql/types/relationship.py +10 -2
- infrahub/graphql/types/standard_node.py +12 -7
- infrahub/hfid/tasks.py +12 -3
- infrahub/lock.py +7 -0
- infrahub/menu/repository.py +1 -1
- infrahub/patch/queries/base.py +1 -1
- infrahub/pools/number.py +1 -8
- infrahub/profiles/gather.py +56 -0
- infrahub/profiles/mandatory_fields_checker.py +116 -0
- infrahub/profiles/models.py +66 -0
- infrahub/profiles/node_applier.py +154 -13
- infrahub/profiles/queries/get_profile_data.py +143 -31
- infrahub/profiles/tasks.py +79 -27
- infrahub/profiles/triggers.py +22 -0
- infrahub/proposed_change/action_checker.py +1 -1
- infrahub/proposed_change/tasks.py +4 -1
- infrahub/services/__init__.py +1 -1
- infrahub/services/adapters/cache/nats.py +1 -1
- infrahub/services/adapters/cache/redis.py +7 -0
- infrahub/tasks/artifact.py +1 -0
- infrahub/transformations/tasks.py +2 -2
- infrahub/trigger/catalogue.py +2 -0
- infrahub/trigger/models.py +1 -0
- infrahub/trigger/setup.py +3 -3
- infrahub/trigger/tasks.py +3 -0
- infrahub/validators/tasks.py +1 -0
- infrahub/webhook/gather.py +1 -1
- infrahub/webhook/models.py +1 -1
- infrahub/webhook/tasks.py +23 -7
- infrahub/workers/dependencies.py +9 -3
- infrahub/workers/infrahub_async.py +13 -4
- infrahub/workflows/catalogue.py +19 -0
- infrahub_sdk/analyzer.py +2 -2
- infrahub_sdk/branch.py +12 -39
- infrahub_sdk/checks.py +4 -4
- infrahub_sdk/client.py +36 -0
- infrahub_sdk/ctl/cli_commands.py +2 -1
- infrahub_sdk/ctl/graphql.py +15 -4
- infrahub_sdk/ctl/utils.py +2 -2
- infrahub_sdk/enums.py +6 -0
- infrahub_sdk/graphql/renderers.py +21 -0
- infrahub_sdk/graphql/utils.py +85 -0
- infrahub_sdk/node/attribute.py +12 -2
- infrahub_sdk/node/constants.py +12 -0
- infrahub_sdk/node/metadata.py +69 -0
- infrahub_sdk/node/node.py +65 -14
- infrahub_sdk/node/property.py +3 -0
- infrahub_sdk/node/related_node.py +37 -5
- infrahub_sdk/node/relationship.py +18 -1
- infrahub_sdk/operation.py +2 -2
- infrahub_sdk/schema/repository.py +1 -2
- infrahub_sdk/transforms.py +2 -2
- infrahub_sdk/types.py +18 -2
- {infrahub_server-1.6.2.dist-info → infrahub_server-1.7.0.dist-info}/METADATA +17 -16
- {infrahub_server-1.6.2.dist-info → infrahub_server-1.7.0.dist-info}/RECORD +252 -231
- infrahub_testcontainers/container.py +3 -3
- infrahub_testcontainers/docker-compose-cluster.test.yml +7 -7
- infrahub_testcontainers/docker-compose.test.yml +13 -5
- infrahub_testcontainers/models.py +3 -3
- infrahub_testcontainers/performance_test.py +1 -1
- infrahub/graphql/models.py +0 -6
- {infrahub_server-1.6.2.dist-info → infrahub_server-1.7.0.dist-info}/WHEEL +0 -0
- {infrahub_server-1.6.2.dist-info → infrahub_server-1.7.0.dist-info}/entry_points.txt +0 -0
- {infrahub_server-1.6.2.dist-info → infrahub_server-1.7.0.dist-info}/licenses/LICENSE.txt +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import sys
|
|
4
|
-
from typing import Self
|
|
4
|
+
from typing import Any, Self
|
|
5
5
|
|
|
6
6
|
from pydantic import ConfigDict, Field, model_validator
|
|
7
7
|
|
|
@@ -24,6 +24,33 @@ def get_attribute_parameters_class_for_kind(kind: str) -> type[AttributeParamete
|
|
|
24
24
|
class AttributeParameters(HashableModel):
|
|
25
25
|
model_config = ConfigDict(extra="forbid")
|
|
26
26
|
|
|
27
|
+
@classmethod
|
|
28
|
+
def convert_from(cls, source: AttributeParameters) -> Self:
|
|
29
|
+
"""Convert from another AttributeParameters subclass.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
source: The source AttributeParameters instance to convert from
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
A new instance of the target class with compatible fields populated
|
|
36
|
+
"""
|
|
37
|
+
source_data = source.model_dump()
|
|
38
|
+
return cls.convert_from_dict(source_data=source_data)
|
|
39
|
+
|
|
40
|
+
@classmethod
|
|
41
|
+
def convert_from_dict(cls, source_data: dict[str, Any]) -> Self:
|
|
42
|
+
"""Convert from a dictionary to the target class.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
source_data: The source dictionary to convert from
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
A new instance of the target class with compatible fields populated
|
|
49
|
+
"""
|
|
50
|
+
target_fields = set(cls.model_fields.keys())
|
|
51
|
+
filtered_data = {k: v for k, v in source_data.items() if k in target_fields}
|
|
52
|
+
return cls(**filtered_data)
|
|
53
|
+
|
|
27
54
|
|
|
28
55
|
class TextAttributeParameters(AttributeParameters):
|
|
29
56
|
regex: str | None = Field(
|
|
@@ -70,7 +70,7 @@ class AttributeSchema(GeneratedAttributeSchema):
|
|
|
70
70
|
|
|
71
71
|
@property
|
|
72
72
|
def support_profiles(self) -> bool:
|
|
73
|
-
return self.read_only is False and self.
|
|
73
|
+
return self.read_only is False and self.unique is False
|
|
74
74
|
|
|
75
75
|
def get_id(self) -> str:
|
|
76
76
|
if self.id is None:
|
|
@@ -114,13 +114,20 @@ class AttributeSchema(GeneratedAttributeSchema):
|
|
|
114
114
|
@field_validator("parameters", mode="before")
|
|
115
115
|
@classmethod
|
|
116
116
|
def set_parameters_type(cls, value: Any, info: ValidationInfo) -> Any:
|
|
117
|
-
"""Override parameters class if using base AttributeParameters class and should be using a subclass
|
|
117
|
+
"""Override parameters class if using base AttributeParameters class and should be using a subclass.
|
|
118
|
+
|
|
119
|
+
This validator handles parameter type conversion when an attribute's kind changes.
|
|
120
|
+
Fields from the source that don't exist in the target are silently dropped.
|
|
121
|
+
Fields with the same name in both classes are preserved.
|
|
122
|
+
"""
|
|
118
123
|
kind = info.data["kind"]
|
|
119
124
|
expected_parameters_class = get_attribute_parameters_class_for_kind(kind=kind)
|
|
120
125
|
if value is None:
|
|
121
126
|
return expected_parameters_class()
|
|
122
127
|
if not isinstance(value, expected_parameters_class) and isinstance(value, AttributeParameters):
|
|
123
|
-
return expected_parameters_class(
|
|
128
|
+
return expected_parameters_class.convert_from(value)
|
|
129
|
+
if isinstance(value, dict):
|
|
130
|
+
return expected_parameters_class.convert_from_dict(source_data=value)
|
|
124
131
|
return value
|
|
125
132
|
|
|
126
133
|
@model_validator(mode="after")
|
|
@@ -168,7 +175,7 @@ class AttributeSchema(GeneratedAttributeSchema):
|
|
|
168
175
|
|
|
169
176
|
def update_from_generic(self, other: AttributeSchema) -> None:
|
|
170
177
|
fields_to_exclude = ("id", "order_weight", "branch", "inherited")
|
|
171
|
-
for name in self.model_fields:
|
|
178
|
+
for name in self.__class__.model_fields:
|
|
172
179
|
if name in fields_to_exclude:
|
|
173
180
|
continue
|
|
174
181
|
if getattr(self, name) != getattr(other, name):
|
|
@@ -238,19 +245,6 @@ class TextAttributeSchema(AttributeSchema):
|
|
|
238
245
|
json_schema_extra={"update": UpdateSupport.VALIDATE_CONSTRAINT.value},
|
|
239
246
|
)
|
|
240
247
|
|
|
241
|
-
@model_validator(mode="after")
|
|
242
|
-
def reconcile_parameters(self) -> Self:
|
|
243
|
-
if self.regex != self.parameters.regex:
|
|
244
|
-
final_regex = self.parameters.regex if self.parameters.regex is not None else self.regex
|
|
245
|
-
self.regex = self.parameters.regex = final_regex
|
|
246
|
-
if self.min_length != self.parameters.min_length:
|
|
247
|
-
final_min_length = self.parameters.min_length if self.parameters.min_length is not None else self.min_length
|
|
248
|
-
self.min_length = self.parameters.min_length = final_min_length
|
|
249
|
-
if self.max_length != self.parameters.max_length:
|
|
250
|
-
final_max_length = self.parameters.max_length if self.parameters.max_length is not None else self.max_length
|
|
251
|
-
self.max_length = self.parameters.max_length = final_max_length
|
|
252
|
-
return self
|
|
253
|
-
|
|
254
248
|
def get_regex(self) -> str | None:
|
|
255
249
|
return self.parameters.regex
|
|
256
250
|
|
|
@@ -263,6 +263,9 @@ class BaseNodeSchema(GeneratedBaseNodeSchema):
|
|
|
263
263
|
) -> AttributeSchema | RelationshipSchema | None: ...
|
|
264
264
|
|
|
265
265
|
def get_field(self, name: str, raise_on_error: bool = True) -> AttributeSchema | RelationshipSchema | None:
|
|
266
|
+
if name in NODE_PROPERTY_ATTRIBUTES:
|
|
267
|
+
return self.get_attribute(name=name)
|
|
268
|
+
|
|
266
269
|
if field := self.get_attribute_or_none(name=name):
|
|
267
270
|
return field
|
|
268
271
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Any
|
|
1
|
+
from typing import Any, TypedDict
|
|
2
2
|
|
|
3
3
|
from infrahub.actions.schema import (
|
|
4
4
|
core_action,
|
|
@@ -93,7 +93,13 @@ from .template import core_object_component_template, core_object_template
|
|
|
93
93
|
from .transform import core_transform, core_transform_jinja2, core_transform_python
|
|
94
94
|
from .webhook import core_custom_webhook, core_standard_webhook, core_webhook
|
|
95
95
|
|
|
96
|
-
|
|
96
|
+
|
|
97
|
+
class CoreModelsMixedType(TypedDict):
|
|
98
|
+
generics: list[GenericSchema]
|
|
99
|
+
nodes: list[NodeSchema]
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
core_models_mixed: CoreModelsMixedType = {
|
|
97
103
|
"generics": [
|
|
98
104
|
core_action,
|
|
99
105
|
core_trigger_rule,
|
|
@@ -43,8 +43,8 @@ core_account_token = NodeSchema(
|
|
|
43
43
|
documentation="/topics/auth",
|
|
44
44
|
attributes=[
|
|
45
45
|
Attr(name="name", kind="Text", optional=True),
|
|
46
|
-
Attr(name="token", kind="Text", unique=True),
|
|
47
|
-
Attr(name="expiration", kind="DateTime", optional=True),
|
|
46
|
+
Attr(name="token", kind="Text", description="The authentication token value", unique=True),
|
|
47
|
+
Attr(name="expiration", kind="DateTime", description="Date and time when the token expires", optional=True),
|
|
48
48
|
],
|
|
49
49
|
relationships=[
|
|
50
50
|
Rel(
|
|
@@ -68,13 +68,7 @@ core_password_credential = NodeSchema(
|
|
|
68
68
|
inherit_from=[InfrahubKind.CREDENTIAL],
|
|
69
69
|
attributes=[
|
|
70
70
|
Attr(name="username", kind="Text", optional=True, branch=BranchSupportType.AGNOSTIC, order_weight=6000),
|
|
71
|
-
Attr(
|
|
72
|
-
name="password",
|
|
73
|
-
kind="Password",
|
|
74
|
-
optional=True,
|
|
75
|
-
branch=BranchSupportType.AGNOSTIC,
|
|
76
|
-
order_weight=7000,
|
|
77
|
-
),
|
|
71
|
+
Attr(name="password", kind="Password", optional=True, branch=BranchSupportType.AGNOSTIC, order_weight=7000),
|
|
78
72
|
],
|
|
79
73
|
)
|
|
80
74
|
|
|
@@ -88,7 +82,12 @@ core_refresh_token = NodeSchema(
|
|
|
88
82
|
generate_profile=False,
|
|
89
83
|
branch=BranchSupportType.AGNOSTIC,
|
|
90
84
|
attributes=[
|
|
91
|
-
Attr(
|
|
85
|
+
Attr(
|
|
86
|
+
name="expiration",
|
|
87
|
+
kind="DateTime",
|
|
88
|
+
description="Date and time when the refresh token expires",
|
|
89
|
+
optional=False,
|
|
90
|
+
),
|
|
92
91
|
],
|
|
93
92
|
relationships=[
|
|
94
93
|
Rel(
|
|
@@ -144,6 +143,7 @@ core_generic_account = GenericSchema(
|
|
|
144
143
|
Attr(
|
|
145
144
|
name="account_type",
|
|
146
145
|
kind="Text",
|
|
146
|
+
description="Type of account (user, script, etc.)",
|
|
147
147
|
default_value=AccountType.USER.value,
|
|
148
148
|
enum=AccountType.available_types(),
|
|
149
149
|
),
|
|
@@ -35,6 +35,7 @@ core_artifact_target = GenericSchema(
|
|
|
35
35
|
core_artifact = NodeSchema(
|
|
36
36
|
name="Artifact",
|
|
37
37
|
namespace="Core",
|
|
38
|
+
description="A file generated by a transformation, associated with a specific object",
|
|
38
39
|
label="Artifact",
|
|
39
40
|
include_in_menu=False,
|
|
40
41
|
icon="mdi:file-document-outline",
|
|
@@ -47,19 +48,17 @@ core_artifact = NodeSchema(
|
|
|
47
48
|
documentation="/topics/artifact",
|
|
48
49
|
attributes=[
|
|
49
50
|
Attr(name="name", kind="Text"),
|
|
50
|
-
Attr(
|
|
51
|
-
name="status",
|
|
52
|
-
kind="Text",
|
|
53
|
-
enum=ArtifactStatus.available_types(),
|
|
54
|
-
),
|
|
51
|
+
Attr(name="status", kind="Text", enum=ArtifactStatus.available_types()),
|
|
55
52
|
Attr(
|
|
56
53
|
name="content_type",
|
|
57
54
|
kind="Text",
|
|
55
|
+
description="MIME type of the artifact content",
|
|
58
56
|
enum=ContentType.available_types(),
|
|
59
57
|
),
|
|
60
58
|
Attr(
|
|
61
59
|
name="checksum",
|
|
62
60
|
kind="Text",
|
|
61
|
+
description="Hash checksum of the artifact content for integrity verification",
|
|
63
62
|
optional=True,
|
|
64
63
|
),
|
|
65
64
|
Attr(
|
|
@@ -71,6 +70,7 @@ core_artifact = NodeSchema(
|
|
|
71
70
|
Attr(
|
|
72
71
|
name="parameters",
|
|
73
72
|
kind="JSON",
|
|
73
|
+
description="Input parameters used to generate this artifact",
|
|
74
74
|
optional=True,
|
|
75
75
|
),
|
|
76
76
|
],
|
|
@@ -97,6 +97,7 @@ core_artifact = NodeSchema(
|
|
|
97
97
|
core_artifact_definition = NodeSchema(
|
|
98
98
|
name="ArtifactDefinition",
|
|
99
99
|
namespace="Core",
|
|
100
|
+
description="Defines how artifacts are generated from a transformation for a group of targets",
|
|
100
101
|
include_in_menu=False,
|
|
101
102
|
icon="mdi:file-document-multiple-outline",
|
|
102
103
|
label="Artifact Definition",
|
|
@@ -110,10 +111,15 @@ core_artifact_definition = NodeSchema(
|
|
|
110
111
|
documentation="/topics/artifact",
|
|
111
112
|
attributes=[
|
|
112
113
|
Attr(name="name", kind="Text", unique=True),
|
|
113
|
-
Attr(name="artifact_name", kind="Text"),
|
|
114
|
+
Attr(name="artifact_name", kind="Text", description="Name template for generated artifacts"),
|
|
114
115
|
Attr(name="description", kind="Text", optional=True),
|
|
115
|
-
Attr(name="parameters", kind="JSON"),
|
|
116
|
-
Attr(
|
|
116
|
+
Attr(name="parameters", kind="JSON", description="GraphQL query parameters for the transformation"),
|
|
117
|
+
Attr(
|
|
118
|
+
name="content_type",
|
|
119
|
+
kind="Text",
|
|
120
|
+
description="MIME type of the generated artifacts",
|
|
121
|
+
enum=ContentType.available_types(),
|
|
122
|
+
),
|
|
117
123
|
],
|
|
118
124
|
relationships=[
|
|
119
125
|
Rel(
|
|
@@ -14,6 +14,7 @@ from ...relationship_schema import (
|
|
|
14
14
|
core_check_definition = NodeSchema(
|
|
15
15
|
name="CheckDefinition",
|
|
16
16
|
namespace="Core",
|
|
17
|
+
description="Defines a user-defined check that validates data in a proposed change",
|
|
17
18
|
include_in_menu=False,
|
|
18
19
|
icon="mdi:check-all",
|
|
19
20
|
label="Check Definition",
|
|
@@ -27,10 +28,15 @@ core_check_definition = NodeSchema(
|
|
|
27
28
|
attributes=[
|
|
28
29
|
Attr(name="name", kind="Text", unique=True),
|
|
29
30
|
Attr(name="description", kind="Text", optional=True),
|
|
30
|
-
Attr(name="file_path", kind="Text"),
|
|
31
|
-
Attr(name="class_name", kind="Text"),
|
|
32
|
-
Attr(
|
|
33
|
-
|
|
31
|
+
Attr(name="file_path", kind="Text", description="Path to the Python file containing the check class"),
|
|
32
|
+
Attr(name="class_name", kind="Text", description="Name of the Python class implementing the check"),
|
|
33
|
+
Attr(
|
|
34
|
+
name="timeout",
|
|
35
|
+
kind="Number",
|
|
36
|
+
description="Maximum execution time in seconds before the check times out",
|
|
37
|
+
default_value=60,
|
|
38
|
+
),
|
|
39
|
+
Attr(name="parameters", kind="JSON", description="Additional parameters to pass to the check", optional=True),
|
|
34
40
|
],
|
|
35
41
|
relationships=[
|
|
36
42
|
Rel(
|
|
@@ -15,6 +15,7 @@ from ...relationship_schema import (
|
|
|
15
15
|
core_generator_definition = NodeSchema(
|
|
16
16
|
name="GeneratorDefinition",
|
|
17
17
|
namespace="Core",
|
|
18
|
+
description="Defines a generator that creates or updates objects based on a GraphQL query",
|
|
18
19
|
include_in_menu=False,
|
|
19
20
|
icon="mdi:state-machine",
|
|
20
21
|
label="Generator Definition",
|
|
@@ -29,12 +30,30 @@ core_generator_definition = NodeSchema(
|
|
|
29
30
|
attributes=[
|
|
30
31
|
Attr(name="name", kind="Text", unique=True),
|
|
31
32
|
Attr(name="description", kind="Text", optional=True),
|
|
32
|
-
Attr(name="parameters", kind="JSON"),
|
|
33
|
-
Attr(name="file_path", kind="Text"),
|
|
34
|
-
Attr(name="class_name", kind="Text"),
|
|
35
|
-
Attr(
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
Attr(name="parameters", kind="JSON", description="GraphQL query parameters for the generator"),
|
|
34
|
+
Attr(name="file_path", kind="Text", description="Path to the Python file containing the generator class"),
|
|
35
|
+
Attr(name="class_name", kind="Text", description="Name of the Python class implementing the generator"),
|
|
36
|
+
Attr(
|
|
37
|
+
name="convert_query_response",
|
|
38
|
+
kind="Boolean",
|
|
39
|
+
description="Whether to convert the GraphQL response to SDK objects",
|
|
40
|
+
optional=True,
|
|
41
|
+
default_value=False,
|
|
42
|
+
),
|
|
43
|
+
Attr(
|
|
44
|
+
name="execute_in_proposed_change",
|
|
45
|
+
kind="Boolean",
|
|
46
|
+
description="Whether to run this generator in proposed changes",
|
|
47
|
+
optional=True,
|
|
48
|
+
default_value=True,
|
|
49
|
+
),
|
|
50
|
+
Attr(
|
|
51
|
+
name="execute_after_merge",
|
|
52
|
+
kind="Boolean",
|
|
53
|
+
description="Whether to run this generator after a merge",
|
|
54
|
+
optional=True,
|
|
55
|
+
default_value=True,
|
|
56
|
+
),
|
|
38
57
|
],
|
|
39
58
|
relationships=[
|
|
40
59
|
Rel(
|
|
@@ -67,6 +86,7 @@ core_generator_definition = NodeSchema(
|
|
|
67
86
|
core_generator_instance = NodeSchema(
|
|
68
87
|
name="GeneratorInstance",
|
|
69
88
|
namespace="Core",
|
|
89
|
+
description="An instance of a generator execution for a specific object",
|
|
70
90
|
label="Generator Instance",
|
|
71
91
|
include_in_menu=False,
|
|
72
92
|
icon="mdi:file-document-outline",
|
|
@@ -28,7 +28,7 @@ core_graphql_query = NodeSchema(
|
|
|
28
28
|
attributes=[
|
|
29
29
|
Attr(name="name", kind="Text", unique=True),
|
|
30
30
|
Attr(name="description", kind="Text", optional=True),
|
|
31
|
-
Attr(name="query", kind="TextArea"),
|
|
31
|
+
Attr(name="query", kind="TextArea", description="The GraphQL query string"),
|
|
32
32
|
Attr(name="variables", kind="JSON", description="variables in use in the query", optional=True, read_only=True),
|
|
33
33
|
Attr(
|
|
34
34
|
name="operations",
|
|
@@ -31,7 +31,14 @@ core_group = GenericSchema(
|
|
|
31
31
|
Attr(name="name", kind="Text", unique=True),
|
|
32
32
|
Attr(name="label", kind="Text", optional=True),
|
|
33
33
|
Attr(name="description", kind="Text", optional=True),
|
|
34
|
-
Attr(
|
|
34
|
+
Attr(
|
|
35
|
+
name="group_type",
|
|
36
|
+
kind="Text",
|
|
37
|
+
description="Type of group (default or internal)",
|
|
38
|
+
enum=["default", "internal"],
|
|
39
|
+
default_value="default",
|
|
40
|
+
optional=False,
|
|
41
|
+
),
|
|
35
42
|
],
|
|
36
43
|
relationships=[
|
|
37
44
|
Rel(
|
|
@@ -111,7 +118,7 @@ core_graphql_query_group = NodeSchema(
|
|
|
111
118
|
inherit_from=[InfrahubKind.GENERICGROUP],
|
|
112
119
|
generate_profile=False,
|
|
113
120
|
attributes=[
|
|
114
|
-
Attr(name="parameters", kind="JSON", optional=True),
|
|
121
|
+
Attr(name="parameters", kind="JSON", description="GraphQL query parameters for the group", optional=True),
|
|
115
122
|
],
|
|
116
123
|
relationships=[
|
|
117
124
|
Rel(
|
|
@@ -68,11 +68,18 @@ builtin_ip_prefix = GenericSchema(
|
|
|
68
68
|
branch=BranchSupportType.AWARE,
|
|
69
69
|
hierarchical=True,
|
|
70
70
|
attributes=[
|
|
71
|
-
Attr(
|
|
71
|
+
Attr(
|
|
72
|
+
name="prefix",
|
|
73
|
+
kind="IPNetwork",
|
|
74
|
+
description="The IP prefix in CIDR notation",
|
|
75
|
+
branch=BranchSupportType.AWARE,
|
|
76
|
+
order_weight=1000,
|
|
77
|
+
),
|
|
72
78
|
Attr(name="description", kind="Text", optional=True, branch=BranchSupportType.AWARE, order_weight=2000),
|
|
73
79
|
Attr(
|
|
74
80
|
name="member_type",
|
|
75
81
|
kind="Dropdown",
|
|
82
|
+
description="Whether this prefix contains other prefixes or IP addresses",
|
|
76
83
|
choices=[
|
|
77
84
|
DropdownChoice(
|
|
78
85
|
name="prefix",
|
|
@@ -97,13 +104,53 @@ builtin_ip_prefix = GenericSchema(
|
|
|
97
104
|
order_weight=4000,
|
|
98
105
|
description="All IP addresses within this prefix are considered usable",
|
|
99
106
|
),
|
|
100
|
-
Attr(name="is_top_level", kind="Boolean", read_only=True, optional=True, allow_override=AllowOverrideType.NONE),
|
|
101
|
-
Attr(name="utilization", kind="Number", read_only=True, optional=True, allow_override=AllowOverrideType.NONE),
|
|
102
|
-
Attr(name="netmask", kind="Text", read_only=True, optional=True, allow_override=AllowOverrideType.NONE),
|
|
103
|
-
Attr(name="hostmask", kind="Text", read_only=True, optional=True, allow_override=AllowOverrideType.NONE),
|
|
104
|
-
Attr(name="network_address", kind="Text", read_only=True, optional=True, allow_override=AllowOverrideType.NONE),
|
|
105
107
|
Attr(
|
|
106
|
-
name="
|
|
108
|
+
name="is_top_level",
|
|
109
|
+
kind="Boolean",
|
|
110
|
+
description="Whether this prefix has no parent prefix",
|
|
111
|
+
read_only=True,
|
|
112
|
+
optional=True,
|
|
113
|
+
allow_override=AllowOverrideType.NONE,
|
|
114
|
+
),
|
|
115
|
+
Attr(
|
|
116
|
+
name="utilization",
|
|
117
|
+
kind="Number",
|
|
118
|
+
description="Percentage of the prefix that is allocated",
|
|
119
|
+
read_only=True,
|
|
120
|
+
optional=True,
|
|
121
|
+
allow_override=AllowOverrideType.NONE,
|
|
122
|
+
),
|
|
123
|
+
Attr(
|
|
124
|
+
name="netmask",
|
|
125
|
+
kind="Text",
|
|
126
|
+
description="Subnet mask in dotted decimal notation",
|
|
127
|
+
read_only=True,
|
|
128
|
+
optional=True,
|
|
129
|
+
allow_override=AllowOverrideType.NONE,
|
|
130
|
+
),
|
|
131
|
+
Attr(
|
|
132
|
+
name="hostmask",
|
|
133
|
+
kind="Text",
|
|
134
|
+
description="Wildcard mask in dotted decimal notation",
|
|
135
|
+
read_only=True,
|
|
136
|
+
optional=True,
|
|
137
|
+
allow_override=AllowOverrideType.NONE,
|
|
138
|
+
),
|
|
139
|
+
Attr(
|
|
140
|
+
name="network_address",
|
|
141
|
+
kind="Text",
|
|
142
|
+
description="Network address of the prefix",
|
|
143
|
+
read_only=True,
|
|
144
|
+
optional=True,
|
|
145
|
+
allow_override=AllowOverrideType.NONE,
|
|
146
|
+
),
|
|
147
|
+
Attr(
|
|
148
|
+
name="broadcast_address",
|
|
149
|
+
kind="Text",
|
|
150
|
+
description="Broadcast address of the prefix",
|
|
151
|
+
read_only=True,
|
|
152
|
+
optional=True,
|
|
153
|
+
allow_override=AllowOverrideType.NONE,
|
|
107
154
|
),
|
|
108
155
|
],
|
|
109
156
|
relationships=[
|
|
@@ -150,7 +197,13 @@ builtin_ip_address = GenericSchema(
|
|
|
150
197
|
icon="mdi:ip-outline",
|
|
151
198
|
branch=BranchSupportType.AWARE,
|
|
152
199
|
attributes=[
|
|
153
|
-
Attr(
|
|
200
|
+
Attr(
|
|
201
|
+
name="address",
|
|
202
|
+
kind="IPHost",
|
|
203
|
+
description="The IP address with prefix length",
|
|
204
|
+
branch=BranchSupportType.AWARE,
|
|
205
|
+
order_weight=1000,
|
|
206
|
+
),
|
|
154
207
|
Attr(name="description", kind="Text", optional=True, branch=BranchSupportType.AWARE, order_weight=2000),
|
|
155
208
|
],
|
|
156
209
|
relationships=[
|
|
@@ -186,7 +239,15 @@ internal_ipam_ip_range_available = NodeSchema(
|
|
|
186
239
|
branch=BranchSupportType.AWARE,
|
|
187
240
|
inherit_from=[InfrahubKind.IPADDRESS],
|
|
188
241
|
generate_profile=False,
|
|
189
|
-
attributes=[
|
|
242
|
+
attributes=[
|
|
243
|
+
Attr(
|
|
244
|
+
name="last_address",
|
|
245
|
+
kind="IPHost",
|
|
246
|
+
description="Last IP address in the available range",
|
|
247
|
+
branch=BranchSupportType.AWARE,
|
|
248
|
+
order_weight=2000,
|
|
249
|
+
)
|
|
250
|
+
],
|
|
190
251
|
)
|
|
191
252
|
|
|
192
253
|
internal_ipam_ip_prefix_available = NodeSchema(
|
|
@@ -215,5 +276,14 @@ core_ipam_namespace = NodeSchema(
|
|
|
215
276
|
icon="mdi:format-list-group",
|
|
216
277
|
branch=BranchSupportType.AWARE,
|
|
217
278
|
inherit_from=[InfrahubKind.IPNAMESPACE],
|
|
218
|
-
attributes=[
|
|
279
|
+
attributes=[
|
|
280
|
+
Attr(
|
|
281
|
+
name="default",
|
|
282
|
+
kind="Boolean",
|
|
283
|
+
description="Whether this is the default IPAM namespace",
|
|
284
|
+
optional=True,
|
|
285
|
+
read_only=True,
|
|
286
|
+
order_weight=9000,
|
|
287
|
+
)
|
|
288
|
+
],
|
|
219
289
|
)
|
|
@@ -17,19 +17,53 @@ generic_menu_item = GenericSchema(
|
|
|
17
17
|
display_labels=["label__value"],
|
|
18
18
|
generate_profile=False,
|
|
19
19
|
attributes=[
|
|
20
|
-
Attr(
|
|
20
|
+
Attr(
|
|
21
|
+
name="namespace",
|
|
22
|
+
kind="Text",
|
|
23
|
+
description="Namespace for the menu item",
|
|
24
|
+
regex=NAMESPACE_REGEX,
|
|
25
|
+
order_weight=1000,
|
|
26
|
+
),
|
|
21
27
|
Attr(name="name", kind="Text", order_weight=1000),
|
|
22
28
|
Attr(name="label", kind="Text", optional=True, order_weight=2000),
|
|
23
|
-
Attr(
|
|
24
|
-
|
|
29
|
+
Attr(
|
|
30
|
+
name="kind",
|
|
31
|
+
kind="Text",
|
|
32
|
+
description="Kind of object this menu item links to",
|
|
33
|
+
optional=True,
|
|
34
|
+
order_weight=2500,
|
|
35
|
+
),
|
|
36
|
+
Attr(name="path", kind="Text", description="URL path for the menu item", optional=True, order_weight=2500),
|
|
25
37
|
Attr(name="description", kind="Text", optional=True, order_weight=3000),
|
|
26
|
-
Attr(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
Attr(
|
|
38
|
+
Attr(
|
|
39
|
+
name="icon", kind="Text", description="Icon identifier for the menu item", optional=True, order_weight=4000
|
|
40
|
+
),
|
|
41
|
+
Attr(
|
|
42
|
+
name="protected",
|
|
43
|
+
kind="Boolean",
|
|
44
|
+
description="Whether the menu item is protected from modification",
|
|
45
|
+
default_value=False,
|
|
46
|
+
read_only=True,
|
|
47
|
+
order_weight=5000,
|
|
48
|
+
),
|
|
49
|
+
Attr(
|
|
50
|
+
name="order_weight",
|
|
51
|
+
kind="Number",
|
|
52
|
+
description="Ordering weight for menu item placement",
|
|
53
|
+
default_value=2000,
|
|
54
|
+
order_weight=6000,
|
|
55
|
+
),
|
|
56
|
+
Attr(
|
|
57
|
+
name="required_permissions",
|
|
58
|
+
kind="List",
|
|
59
|
+
description="List of permissions required to view this menu item",
|
|
60
|
+
optional=True,
|
|
61
|
+
order_weight=7000,
|
|
62
|
+
),
|
|
30
63
|
Attr(
|
|
31
64
|
name="section",
|
|
32
65
|
kind="Text",
|
|
66
|
+
description="Section where the menu item appears",
|
|
33
67
|
enum=["object", "internal"],
|
|
34
68
|
default_value="object",
|
|
35
69
|
order_weight=8000,
|
|
@@ -27,7 +27,14 @@ core_base_permission = GenericSchema(
|
|
|
27
27
|
generate_profile=False,
|
|
28
28
|
attributes=[
|
|
29
29
|
Attr(name="description", kind="Text", optional=True),
|
|
30
|
-
Attr(
|
|
30
|
+
Attr(
|
|
31
|
+
name="identifier",
|
|
32
|
+
kind="Text",
|
|
33
|
+
description="Identifier for the permission",
|
|
34
|
+
read_only=True,
|
|
35
|
+
optional=True,
|
|
36
|
+
allow_override=AllowOverrideType.NONE,
|
|
37
|
+
),
|
|
31
38
|
],
|
|
32
39
|
relationships=[
|
|
33
40
|
Rel(
|
|
@@ -54,11 +61,17 @@ core_object_permission = NodeSchema(
|
|
|
54
61
|
generate_profile=False,
|
|
55
62
|
inherit_from=[InfrahubKind.BASEPERMISSION],
|
|
56
63
|
attributes=[
|
|
57
|
-
Attr(
|
|
64
|
+
Attr(
|
|
65
|
+
name="namespace",
|
|
66
|
+
kind="Text",
|
|
67
|
+
description="Namespace of the object type this permission applies to",
|
|
68
|
+
order_weight=2000,
|
|
69
|
+
),
|
|
58
70
|
Attr(name="name", kind="Text", order_weight=3000),
|
|
59
71
|
Attr(
|
|
60
72
|
name="action",
|
|
61
73
|
kind="Text",
|
|
74
|
+
description="The action this permission grants or denies",
|
|
62
75
|
enum=PermissionAction.available_types(),
|
|
63
76
|
default_value=PermissionAction.ANY.value,
|
|
64
77
|
order_weight=4000,
|
|
@@ -91,6 +104,7 @@ core_global_permission = NodeSchema(
|
|
|
91
104
|
Attr(
|
|
92
105
|
name="action",
|
|
93
106
|
kind="Dropdown",
|
|
107
|
+
description="The global action this permission grants or denies",
|
|
94
108
|
choices=[DropdownChoice(name=permission.value) for permission in GlobalPermissions],
|
|
95
109
|
order_weight=2000,
|
|
96
110
|
),
|
|
@@ -12,7 +12,21 @@ core_profile_schema_definition = GenericSchema(
|
|
|
12
12
|
default_filter="profile_name__value",
|
|
13
13
|
uniqueness_constraints=[["profile_name__value"]],
|
|
14
14
|
attributes=[
|
|
15
|
-
Attr(
|
|
16
|
-
|
|
15
|
+
Attr(
|
|
16
|
+
name="profile_name",
|
|
17
|
+
kind="Text",
|
|
18
|
+
description="Unique name identifier for the profile",
|
|
19
|
+
min_length=3,
|
|
20
|
+
max_length=32,
|
|
21
|
+
unique=True,
|
|
22
|
+
optional=False,
|
|
23
|
+
),
|
|
24
|
+
Attr(
|
|
25
|
+
name="profile_priority",
|
|
26
|
+
kind="Number",
|
|
27
|
+
description="Priority level for profile application (lower numbers have higher priority)",
|
|
28
|
+
default_value=1000,
|
|
29
|
+
optional=True,
|
|
30
|
+
),
|
|
17
31
|
],
|
|
18
32
|
)
|
|
@@ -29,18 +29,38 @@ core_proposed_change = NodeSchema(
|
|
|
29
29
|
attributes=[
|
|
30
30
|
Attr(name="name", kind="Text", optional=False),
|
|
31
31
|
Attr(name="description", kind="TextArea", optional=True),
|
|
32
|
-
Attr(
|
|
33
|
-
|
|
32
|
+
Attr(
|
|
33
|
+
name="source_branch", kind="Text", description="Name of the branch containing the changes", optional=False
|
|
34
|
+
),
|
|
35
|
+
Attr(
|
|
36
|
+
name="destination_branch",
|
|
37
|
+
kind="Text",
|
|
38
|
+
description="Name of the branch to merge changes into",
|
|
39
|
+
optional=False,
|
|
40
|
+
),
|
|
34
41
|
Attr(
|
|
35
42
|
name="state",
|
|
36
43
|
kind="Text",
|
|
44
|
+
description="Current state of the proposed change",
|
|
37
45
|
enum=ProposedChangeState.available_types(),
|
|
38
46
|
default_value=ProposedChangeState.OPEN.value,
|
|
39
47
|
optional=True,
|
|
40
48
|
),
|
|
41
|
-
Attr(
|
|
49
|
+
Attr(
|
|
50
|
+
name="is_draft",
|
|
51
|
+
kind="Boolean",
|
|
52
|
+
description="Whether the proposed change is still a draft",
|
|
53
|
+
optional=False,
|
|
54
|
+
default_value=False,
|
|
55
|
+
),
|
|
42
56
|
# Ideally we should support some "runtime-attribute" that could not even be stored in the database.
|
|
43
|
-
Attr(
|
|
57
|
+
Attr(
|
|
58
|
+
name="total_comments",
|
|
59
|
+
kind="Number",
|
|
60
|
+
description="Total number of comments on this proposed change",
|
|
61
|
+
optional=True,
|
|
62
|
+
read_only=True,
|
|
63
|
+
),
|
|
44
64
|
],
|
|
45
65
|
relationships=[
|
|
46
66
|
Rel(
|