Dynamic Mutations
Author:
Kirill Gaiduk
Changed on:
10 July 2025
Overview
`DynamicUpdateMutation`
automatically derives the correct update Mutation for the primary Entity based on the `RulesetContext`
and then performs the update on one or more fields.
Key points
- Automatic Mutation Selection: GraphQL mutations are automatically selected from the
`RulesetContext`
based on entity type. - Flexible Field Updates: Update single fields or multiple fields in bulk using simple key-value pairs.
- Type-Based Safety: Use POJO classes for strongly-typed updates with automatic field derivation.
- Runtime Validation: Built-in validation ensures only valid fields are included in mutations.
- Universal Application: Works across different entity types without requiring entity-specific mutation code.
Here is a collection of common scenarios for the Dynamic Mutations usage:
Field-based Updates
Fields can be updated directly by name. You can:
- Update a single field
- Provide multiple fields in a
`Map<String, Object>`
for bulk updates
1context.action().mutation(
2 new DynamicUpdateMutation(context, "status", context.getProp("status"))
3);
1context.action().mutation(
2 new DynamicUpdateMutation(context, ImmutableMap.of(
3 "status", context.getProp("status"),
4 "attributes", Attribute.of("New Attribute", "New Value")
5 )));
Type-based Update
For improved type safety, `DynamicUpdateMutation`
can also recursively derive update fields from a defined POJO. This allows you to work with strongly typed objects while still benefiting from dynamic field updates, ensuring both flexibility and safety in your Rule logic.
1@lombok.Data
2@lombok.AllArgsConstructor
3private static class Sample {
4 String status;
5 List<Attribute> attributes;
6}
7
8// Create the update object
9Sample sample = new Sample(
10 context.getProp("status"),
11 Attribute.of("New Attribute", "New Value")
12);
13
14// Execute the mutation
15context.action().mutation(new DynamicUpdateMutation(context, sample));
Error Handling
The `DynamicUpdateMutation`
provides built-in error handling:
1// Strict validation - throws exception for invalid fields
2new DynamicUpdateMutation(context, updates, true);
3
4// Lenient validation - silently excludes invalid fields
5new DynamicUpdateMutation(context, updates, false);
This ensures that your mutations are robust and handle edge cases gracefully.
Complete Examples
Example 1: Generic Field Setter Rule
This rule demonstrates how to create a reusable field setter that works for any entity type:
1import com.fluentcommerce.util.dynamic.DynamicUtils;
2import com.fluentretail.rubix.rule.meta.ParamString;
3import com.fluentretail.rubix.rule.meta.RuleInfo;
4import com.fluentretail.rubix.v2.context.Context;
5import com.fluentretail.rubix.v2.rule.Rule;
6
7import java.util.Map;
8
9@RuleInfo(
10 name = "SetEntityField",
11 description = "Set field {fieldName} to {fieldValue} on the current entity"
12)
13@ParamString(name = "fieldName", description = "Name of the field to update")
14@ParamString(name = "fieldValue", description = "Value to set for the field")
15public class SetEntityField implements Rule {
16 @Override
17 public void run(Context context) {
18 String fieldName = context.getProp("fieldName");
19 String fieldValue = context.getProp("fieldValue");
20
21 // Create a simple map with the field update
22 Map<String, Object> updates = Map.of(fieldName, fieldValue);
23
24 // Use DynamicUtils for cleaner syntax
25 DynamicUtils.mutate(context, updates);
26 }
27}
1{
2 "name": "example.core.SetEntityField",
3 "props": {
4 "fieldName": "tag1",
5 "fieldValue": "newTag"
6 }
7}
Example 2: Complex Object Update
This example shows how to update complex nested objects using type-based mutations:
1import com.fluentcommerce.dto.common.Attribute;
2import com.fluentcommerce.util.dynamic.DynamicUtils;
3import com.fluentretail.rubix.rule.meta.EventInfo;
4import com.fluentretail.rubix.rule.meta.ParamString;
5import com.fluentretail.rubix.rule.meta.RuleInfo;
6import com.fluentretail.rubix.v2.context.Context;
7import com.fluentretail.rubix.v2.rule.Rule;
8
9import java.util.List;
10
11@RuleInfo(
12 name = "UpdateOrderWithAttributes",
13 description = "Update order tag1 and add custom attributes",
14 accepts = { @EventInfo(entityType = "ORDER") }
15)
16@ParamString(name = "tag1", description = "New tag 1 for the order")
17@ParamString(name = "attributeName", description = "Name of the attribute to add")
18@ParamString(name = "attributeValue", description = "Value of the attribute to add")
19public class UpdateOrderWithAttributes implements Rule {
20 @Override
21 public void run(Context context) {
22 String tag1 = context.getProp("tag1");
23 String attributeName = context.getProp("attributeName");
24 String attributeValue = context.getProp("attributeValue");
25
26 // Create a typed update object
27 OrderUpdate update = new OrderUpdate(
28 tag1,
29 List.of(Attribute.of(attributeName, attributeValue))
30 );
31
32 // Execute the mutation
33 context.action().mutation(new DynamicUpdateMutation(context, update));
34 }
35
36 @lombok.Data
37 @lombok.AllArgsConstructor
38 private static class OrderUpdate {
39 private String tag1;
40 private List<Attribute> attributes;
41 }
42}