Skip to content

Commit 1206927

Browse files
committed
Loosen requirements for Upsert mutations (for templates)
1 parent 13a1fcb commit 1206927

File tree

4 files changed

+85
-10
lines changed

4 files changed

+85
-10
lines changed

backend/infrahub/graphql/manager.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -787,25 +787,19 @@ class StatusUpsertInput(InputObjectType):
787787
attr_kind = get_attr_kind(schema, attr)
788788
attr_type = get_attribute_type(kind=attr_kind).get_graphql_update()
789789

790-
# A Field is not required if explicitly indicated or if a default value has been provided
791-
required = not attr.optional if not attr.default_value else False
792-
793-
attrs[attr.name] = graphene.InputField(attr_type, required=required, description=attr.description)
790+
attrs[attr.name] = graphene.InputField(attr_type, description=attr.description)
794791

795792
for rel in schema.relationships:
796793
if rel.internal_peer or rel.read_only:
797794
continue
798795

799796
input_type = self._get_related_input_type(relationship=rel)
800797

801-
required = not rel.optional
802798
if rel.cardinality == RelationshipCardinality.ONE:
803-
attrs[rel.name] = graphene.InputField(input_type, required=required, description=rel.description)
799+
attrs[rel.name] = graphene.InputField(input_type, description=rel.description)
804800

805801
elif rel.cardinality == RelationshipCardinality.MANY:
806-
attrs[rel.name] = graphene.InputField(
807-
graphene.List(input_type), required=required, description=rel.description
808-
)
802+
attrs[rel.name] = graphene.InputField(graphene.List(input_type), description=rel.description)
809803

810804
return type(f"{schema.kind}UpsertInput", (graphene.InputObjectType,), attrs)
811805

backend/tests/helpers/schema/tshirt.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
default_filter="name__value",
1212
display_labels=["name__value"],
1313
uniqueness_constraints=[["name__value"]],
14+
generate_template=True,
1415
attributes=[
1516
AttributeSchema(name="name", kind="Text"),
1617
AttributeSchema(

backend/tests/unit/graphql/test_mutation_upsert.py

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from tests.adapters.event import MemoryInfrahubEvent
1313
from tests.constants import TestKind
1414
from tests.helpers.graphql import graphql
15-
from tests.helpers.schema import TICKET
15+
from tests.helpers.schema import COLOR, TICKET, TSHIRT
1616
from tests.node_creation import create_and_save
1717

1818

@@ -673,3 +673,82 @@ async def test_upsert_node_on_branch_with_hfid_on_default(db: InfrahubDatabase,
673673
in result.errors[0].message
674674
)
675675
assert f"Please rebase this branch to access {person.id} / TestPerson" in result.errors[0].message
676+
677+
678+
async def test_upsert_with_required_relationship_from_template(
679+
db: InfrahubDatabase, default_branch: Branch, register_core_models_schema: None
680+
) -> None:
681+
"""Validate that we can use a template to populate required relationships in upsert mutations.
682+
683+
Steps:
684+
- Create a color node and a Tshirt template node.
685+
- Try to upsert a Tshirt without specifying color or template (should fail).
686+
- Upsert a Tshirt specifying the template (should succeed and apply the color from the template).
687+
"""
688+
registry.schema.register_schema(schema=SchemaRoot(nodes=[TSHIRT, COLOR]), branch=default_branch.name)
689+
690+
# Create a color node
691+
color_node = await Node.init(db=db, schema="TestingColor", branch=default_branch)
692+
await color_node.new(db=db, name="Red", description="Bright Red Color")
693+
await color_node.save(db=db)
694+
695+
# Create a Tshirt template node with the color relationship set
696+
template_node = await Node.init(db=db, schema="TemplateTestingTShirt", branch=default_branch)
697+
await template_node.new(db=db, template_name="Basic Red Tshirt", color=color_node)
698+
await template_node.save(db=db)
699+
700+
# Try to upsert a TShirt without specifying color or template (should fail)
701+
query_missing_required = """
702+
mutation {
703+
TestingTShirtUpsert(data: {name: {value: "My Shirt"} }) {
704+
ok
705+
object {
706+
id
707+
name { value }
708+
color { node { id name { value } } }
709+
}
710+
}
711+
}
712+
"""
713+
gql_params = await prepare_graphql_params(db=db, include_subscription=False, branch=default_branch)
714+
result_missing = await graphql(
715+
schema=gql_params.schema,
716+
source=query_missing_required,
717+
context_value=gql_params.context,
718+
root_value=None,
719+
variable_values={},
720+
)
721+
assert result_missing.errors
722+
assert "color is mandatory for TestingTShirt at color" in str(result_missing.errors)
723+
724+
# Upsert a Tshirt specifying the template (should succeed and apply the color from the template)
725+
query_with_template = """
726+
mutation UpsertTShirt($template_id: String!) {
727+
TestingTShirtUpsert(data: {
728+
name: {value: "My Tshirt"},
729+
object_template: {id: $template_id}
730+
}) {
731+
ok
732+
object {
733+
id
734+
name { value }
735+
color { node { id name { value } } }
736+
}
737+
}
738+
}
739+
"""
740+
741+
result_with_template = await graphql(
742+
schema=gql_params.schema,
743+
source=query_with_template,
744+
context_value=gql_params.context,
745+
root_value=None,
746+
variable_values={"template_id": template_node.id},
747+
)
748+
assert result_with_template.errors is None
749+
assert result_with_template.data
750+
assert result_with_template.data["TestingTShirtUpsert"]["ok"] is True
751+
tshirt_obj = result_with_template.data["TestingTShirtUpsert"]["object"]
752+
assert tshirt_obj["name"]["value"] == "My Tshirt"
753+
assert tshirt_obj["color"]["node"]["id"] == color_node.id
754+
assert tshirt_obj["color"]["node"]["name"]["value"] == "Red"

changelog/7398.fixed.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Loosen requirements for upsert mutations in the GraphQL schema so that required fields can be supplied by a template.

0 commit comments

Comments
 (0)