Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 36 additions & 37 deletions backend/infrahub/core/schema/schema_branch.py
Original file line number Diff line number Diff line change
Expand Up @@ -1909,10 +1909,8 @@ def manage_object_template_relationships(self) -> None:

self.set(name=node_name, schema=node_schema)

def add_relationships_to_template(self, node: NodeSchema) -> None:
def add_relationships_to_template(self, node: NodeSchema | GenericSchema) -> None:
template_schema = self.get(name=self._get_object_template_kind(node_kind=node.kind), duplicate=False)
if template_schema.is_generic_schema:
return

# Remove previous relationships to account for new ones
template_schema.relationships = [
Expand Down Expand Up @@ -1954,11 +1952,16 @@ def add_relationships_to_template(self, node: NodeSchema) -> None:
label=f"{relationship.name} template".title()
if relationship.kind in [RelationshipKind.COMPONENT, RelationshipKind.PARENT]
else relationship.name.title(),
inherited=relationship.inherited,
)
)

parent_hfid = f"{relationship.name}__template_name__value"
if relationship.kind == RelationshipKind.PARENT and parent_hfid not in template_schema.human_friendly_id:
if (
not node.is_generic_schema
and relationship.kind == RelationshipKind.PARENT
and parent_hfid not in template_schema.human_friendly_id
):
template_schema.human_friendly_id = [parent_hfid] + template_schema.human_friendly_id
template_schema.uniqueness_constraints[0].append(relationship.name)

Expand All @@ -1983,9 +1986,6 @@ def generate_object_template_from_node(
need_template_kinds = [n.kind for n in need_templates]

if node.is_generic_schema:
# When needing a template for a generic, we generate an empty shell mostly to make sure that schemas (including the GraphQL one) will
# look right. We don't really care about applying inheritance of fields as it was already processed and actual templates will have the
# correct attributes and relationships
template = GenericSchema(
name=node.kind,
namespace="Template",
Expand All @@ -2001,44 +2001,43 @@ def generate_object_template_from_node(
if used in need_template_kinds:
template.used_by.append(self._get_object_template_kind(node_kind=used))

return template

template = TemplateSchema(
name=node.kind,
namespace="Template",
label=f"Object template {node.label}",
description=f"Object template for {node.kind}",
branch=node.branch,
include_in_menu=False,
display_labels=["template_name__value"],
human_friendly_id=["template_name__value"],
uniqueness_constraints=[["template_name__value"]],
inherit_from=[InfrahubKind.LINEAGESOURCE, InfrahubKind.NODE, core_template_schema.kind],
default_filter="template_name__value",
attributes=[template_name_attr],
relationships=[
RelationshipSchema(
name="related_nodes",
identifier="node__objecttemplate",
peer=node.kind,
kind=RelationshipKind.TEMPLATE,
cardinality=RelationshipCardinality.MANY,
branch=BranchSupportType.AWARE,
)
],
)
else:
template = TemplateSchema(
name=node.kind,
namespace="Template",
label=f"Object template {node.label}",
description=f"Object template for {node.kind}",
branch=node.branch,
include_in_menu=False,
display_labels=["template_name__value"],
human_friendly_id=["template_name__value"],
uniqueness_constraints=[["template_name__value"]],
inherit_from=[InfrahubKind.LINEAGESOURCE, InfrahubKind.NODE, core_template_schema.kind],
default_filter="template_name__value",
attributes=[template_name_attr],
relationships=[
RelationshipSchema(
name="related_nodes",
identifier="node__objecttemplate",
peer=node.kind,
kind=RelationshipKind.TEMPLATE,
cardinality=RelationshipCardinality.MANY,
branch=BranchSupportType.AWARE,
)
],
)

for inherited in node.inherit_from:
if inherited in need_template_kinds:
template.inherit_from.append(self._get_object_template_kind(node_kind=inherited))
for inherited in node.inherit_from:
if inherited in need_template_kinds:
template.inherit_from.append(self._get_object_template_kind(node_kind=inherited))

for node_attr in node.attributes:
if node_attr.unique or node_attr.read_only:
continue

attr = AttributeSchema(
optional=node_attr.optional if is_autogenerated_subtemplate else True,
**node_attr.model_dump(exclude=["id", "unique", "optional", "read_only", "inherited"]),
**node_attr.model_dump(exclude=["id", "unique", "optional", "read_only"]),
)
template.attributes.append(attr)

Expand Down
12 changes: 12 additions & 0 deletions backend/tests/unit/core/schema_manager/test_manager_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -3068,6 +3068,18 @@ async def test_manage_object_templates_with_component_relationships():
# Optional value in component template should match original's
assert attr.optional == template_attr.optional

# Verify the generic by checking its attributes and relationships
test_interface_template = schema_branch.get(name=f"Template{TestKind.INTERFACE}", duplicate=False)
assert test_interface_template.is_generic_schema
test_interface = schema_branch.get(name=TestKind.INTERFACE, duplicate=False)
assert test_interface.is_generic_schema
for attr in test_interface.attributes:
template_attr = test_interface_template.get_attribute(name=attr.name)
assert attr.optional == template_attr.optional
for rel in test_interface.relationships:
template_rel = test_interface_template.get_relationship(name=rel.name)
assert template_rel.peer == f"Template{rel.peer}"

# Verify when a node is marked as absent
ABSENT_VIRTUAL_INTERFACE = copy.deepcopy(DEVICE_SCHEMA)
ABSENT_VIRTUAL_INTERFACE.get(name=TestKind.VIRTUAL_INTERFACE).state = HashableModelState.ABSENT
Expand Down
1 change: 1 addition & 0 deletions changelog/6287.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add attributes and relationships to generic templates to ensure proper GraphQL schema generation
4 changes: 2 additions & 2 deletions docs/docs/guides/computed-attributes.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ nodes:

:::note

In your template, you can utilize most of the built-in filters provided by Jinja2!
In your template, you can utilize most of the **filters** provided by **Jinja2** and **Netutils**!

For more information, please consult the [Schema Reference](../reference).
For more information, please consult the [SDK Templating Reference]($(base_url)python-sdk/reference/templating).

:::

Expand Down
8 changes: 8 additions & 0 deletions docs/docs/guides/jinja2-transform.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,14 @@ end
{% endif %}
```

:::note

In your template, you can utilize most of the **filters** provided by **Jinja2** and **Netutils**!

For more information, please consult the [SDK Templating Reference]($(base_url)python-sdk/reference/templating).

:::

## 4. Create a .infrahub.yml file

In the .infrahub.yml file you define what transforms you have in your repository that you want to make available for Infrahub.
Expand Down
Loading