Fluent Commerce Logo
Docs
Sign In

Dynamic Mutations

Essential knowledge

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}