How Core Utilities work
Author:
Kirill Gaiduk
Changed on:
10 July 2025
Overview
The articles below will walk you through the Core Utilities (`util-core`
), designed to simplify rule development and reduce repetitive code. Each utility focuses on a specific area of functionality to make development easier and more efficient.
Prerequisites
Before diving in, make sure you have:
- A basic understanding of the Utility Bundles
- Familiarity with writing Rules using the Rules SDK
- Completed the Getting Started with the Utility Bundles guide
Core Utilities Overview
Author:
Kirill Gaiduk
Changed on:
10 July 2025
Overview
The Core Utility (`util-core`
) is a standard library of utility functions designed to simplify and accelerate Rule development on the Fluent Commerce platform. It provides a foundational set of helper classes that address common development challenges - such as safely handling event attributes, retrieving and converting settings, logging consistently, and minimizing boilerplate code when forwarding events or querying data.
Prerequisites
These articles assumes you're familiar with:
- Java
- Maven
- JUnit
Key points
- Event Management (
`EventUtils`
): Simplifies creating and forwarding events, ensuring attributes are copied correctly. - Rule Properties (
`RuleUtils`
): Streamlines retrieving and validating rule properties to avoid configuration errors. - Query Simplification (
`QueryUtils`
): Eases the execution of GraphQL queries, especially for paginated data. - Settings Management (
`SettingUtils`
) : Simplifies retrieving settings and automatically converts them from JSON to POJOs. - Logging (
`LogUtils`
): Provides structured logging for easier debugging and auditing. - JSON Handling (
`JsonUtils`
): Offers helpers for JSON serialization, deserialization, and comparison. - Date Formatting (
`GqlDateUtils`
): A simple utility for handling GraphQL-compatible dates.
Value Proposition
The `util-core`
bundle delivers significant value to Fluent Commerce developers by addressing common pain points and accelerating development velocity:
Development Efficiency
- Eliminate Boilerplate: Pre-built utility methods handle repetitive tasks like event forwarding, property validation, and JSON processing
- Faster Implementation: Common patterns are abstracted into reusable, tested components
- Reduced Debugging: Standardized approaches prevent common errors and edge cases
Code Quality & Maintainability
- Consistent Patterns: Enforce best practices across your rule implementations
- Better Maintainability: Centralized utilities make code easier to understand and modify
- Type Safety: Strongly-typed utilities prevent runtime errors
Team & Production Benefits
- Battle-Tested: Utilities are used across production environments
- Team Productivity: Standard utilities create a common language and accelerate onboarding
- Comprehensive Documentation: Detailed JavaDocs and examples for every utility
Explanation through an Example
At this point, it’s assumed you’ve already set up a plugin project and have some logic ready to implement. Let’s focus on how to write a Rule using the Core Utilities.
In this example, we’ll explore how to send a Webhook to a defined endpoint by writing a Rule using Core Utilities.
1import com.fluentretail.rubix.rule.meta.ParamString;
2import com.fluentretail.rubix.rule.meta.RuleInfo;
3import com.fluentretail.rubix.v2.context.Context;
4import com.fluentretail.rubix.v2.rule.Rule;
5import lombok.AllArgsConstructor;
6import lombok.Data;
7
8import java.util.Optional;
9
10import static com.fluentcommerce.util.core.EventUtils.cloneAndRenameEvent;
11import static com.fluentcommerce.util.core.LogUtils.logOnce;
12import static com.fluentcommerce.util.core.SettingUtils.getSetting;
13
14@RuleInfo(name = "SendWebhook", description = "Send webhook {setting}")
15@ParamString(name = "setting", description = "Name of the setting to load webhook info from")
16public class SendWebhook implements Rule {
17 @Override
18 public void run(Context context) {
19 String settingName = context.getProp("setting");
20
21 Optional<WebhookSetting> setting = getSetting(context, settingName).as(WebhookSetting.class);
22
23 if(setting.isPresent()) {
24 context.action().postWebhook(setting.get().url, cloneAndRenameEvent(context, setting.get().name));
25 } else {
26 logOnce(context, SendWebhook.class, "No webhook config found at '%s'", settingName);
27 }
28 }
29
30 @Data
31 @AllArgsConstructor
32 private static final class WebhookSetting {
33 String url;
34 String name;
35 }
36}
Related content
Event Utilities
Authors:
Kirill Gaiduk, Holger Lierse
Changed on:
10 July 2025
Overview
The `EventUtils`
class in the `util-core`
bundle provides a set of essential helper methods to simplify event creation, modification, and forwarding. Using these utilities helps to eliminate boilerplate code and prevent common errors, such as forgetting to copy event attributes or improperly handling event schedules.
Key points
- Safe Event Handling: The utilities eliminate common errors by automatically handling attribute copying and schedule management when cloning or forwarding events.
- Immutability:
`Event`
objects are immutable.`EventUtils`
methods always return a new event instance when a modification is made. - Simplified Forwarding: Provides simple, one-line methods for common forwarding patterns, like cloning and sending an event immediately (
`forwardInboundEventWithNewName`
) or adding attributes (`forwardInlineEventWithAttributes`
). - Type-Safe Attribute Access: Use
`getEventAttributeAs`
to retrieve and convert an event attribute to a specific type (including complex POJOs) in a single, null-safe operation.
Cloning and Forwarding Events
`cloneAndRenameEvent`
You can use the `EventUtils.cloneAndRenameEvent`
method to create a copy of the context event with a new name, which can then be forwarded to trigger the next Ruleset or used as a payload for sending a webhook.
1Event clonedEvent = EventUtils.cloneAndRenameEvent(context, "newEventName");
`cloneAndRenameEvent`
(using a transformer)
The `EventUtils.cloneAndRenameEvent`
method can also be used to copy the context event with a new name, apply a custom transformation, which can then be forwarded to trigger the next Ruleset or used as a payload for sending a webhook. Inbound scheduled events will automatically be converted to non-scheduled unless the outbound event has a different root entity type. The third parameter is optional and can be `null`
; it accepts an `Event.Builder`
to apply additional parameters to the event.
1Event clonedEvent = EventUtils.cloneAndRenameEvent(context, "newEventName",
2 e -> e.scheduledOn(new Date()))
`forwardInboundEventWithNewName`
To send an event with the same structure but a new name, use the `EventUtils.forwardInboundEventWithNewName`
method. This is a convenient wrapper around the `cloneAndRenameEvent`
method, which will send the newly cloned event.
Inbound scheduled events will automatically be converted to non-scheduled events, and all event attributes will be preserved.
1EventUtils.forwardInboundEventWithNewName(context, "newEventName")
`forwardInlineEventWithAttributes`
Use the `EventUtils.forwardInlineEventWithAttributes`
method to send an event with the same structure but a new name and custom attributes.
Originating External Events or Cross Workflow Events will automatically be converted to Inline Events, and all event attributes will be preserved.
1Map<String, Object> attributes = ImmutableMap.of("attribute1", "attribute1Value");
2EventUtils.forwardInlineEventWithAttributes(context, "newEventName", attributes);
`forwardCustomInlineEvent`
To send a new event with the same structure but a different name and apply a custom transformation, use the `EventUtils.forwardCustomInlineEvent`
method. This enhanced version of `forwardEvent`
includes a transformer, allowing you to modify the event before it is sent.
If the transformer changes the type of the root entity (`rootEntityType`
), the event is treated as a Cross Workflow Event, and a `scheduledOn`
value is set ensuring a new Execution Context. If not, the event is treated as an Inline Event, and any original External Event will automatically be converted to an Inline Event.
1EventUtils.forwardCustomInlineEvent(context, "newEventName",
2 (e) -> e.rootEntityType("Location"))
Scheduling Events
`scheduleEvent`
Use the `EventUtils.scheduleEvent`
method to create a Scheduled Event that will be executed a specified number of seconds in the future.
1EventUtils.scheduleEvent(context, event, 10)
`scheduleEvent`
(with name)
To schedule an event with a new name to occur a specified number of seconds in the future, use the `EventUtils.scheduleEvent`
method. This method uses the current `context.getEvent()`
and changes the event name while preserving all other fields.
1EventUtils.scheduleEvent(context, "eventName", 10)
Event Attributes
`appendEventAttribute`
The `EventUtils.appendEventAttribute`
method adds a new attribute to an event. Since events are immutable, this method returns a new event instance with the added attribute.
1Event appendedEvent = EventUtils.appendEventAttribute(event, "myAttribute", "test")
This will return a new instance of the `Event`
class based on the inbound event, but with the newly added attribute `"myAttribute"`
and its value `"test"`
.
`getEventAttributeAs`
Retrieves a named attribute from the event and safely converts it to the desired type. This method uses `JsonUtils.anyToPojo`
under the hood, so it can handle complex type conversions.
It is particularly useful for ensuring consistent attribute handling across different plugins, as it can convert between similar types. However, it does not support conversions between completely incompatible types.
1TestAttribute myAttribute = EventUtils.getEventAttributeAs(context, "myPojo",
2 TestAttribute.class).orElse(null)
Related content
Rule Utilities
Author:
Kirill Gaiduk
Changed on:
10 July 2025
Overview
The `RuleUtils`
class in the `util-core`
bundle provides helper methods to streamline the process of retrieving and validating Rule properties. Using these utilities helps ensure that your Rules are configured correctly and reduces the risk of runtime errors caused by missing or invalid parameters.
Key points
- Flexible List Properties:
`rulePropAsList`
reliably parses a rule property into a`List`
, whether it's configured as a comma-separated string or a JSON array. This is ideal for properties that require multiple values. - Configuration Validation:
`validateRulePropsIsNotEmpty`
provides a simple, one-line way to ensure that all required properties have been provided to a rule, preventing runtime errors. - Fail-Fast Errors: When validation fails, the utility throws a
`PropertyNotFoundException`
with a clear error message. This stops the rule from executing with an invalid configuration and makes debugging easier.
Core Methods
`rulePropAsList`
To retrieve rule properties as a list, you can use the `RuleUtils.rulePropAsList`
method. This utility method is designed to handle several common formats for structuring list-based properties, providing flexibility when a "standard" format is not defined. Here’s what it supports:
- A JSON array literal of strings or objects (e.g.,
`["a", "b", "c"]`
). - A comma-separated string (e.g.,
`"a,b,c"`
).
Example:
Imagine a rule that needs a list of statuses to check. You could configure this property in the workflow in several ways:
- as a JSON array:
`"props": { "statusList": ["CREATED", "AWAITING_FULFILMENT"] }`
- as a comma-separated string:
`"props": { "statusList": "CREATED,AWAITING_FULFILMENT" }`
In your rule, you can reliably retrieve this property as a `List<String>`
using a single line of code:
1import com.fluentcommerce.util.core.RuleUtils;
2import java.util.List;
3
4//...
5
6@Override
7public void run(Context context) {
8 // Retrieves the "statusList" property, regardless of whether it's a
9 // JSON array or a comma-separated string.
10 List<String> statuses = RuleUtils.rulePropAsList(
11 context,
12 "statusList",
13 String.class
14 );
15
16 // Now you can work with the list
17 for (String status : statuses) {
18 context.action().log("Processing status: " + status);
19 }
20}
`validateRulePropsIsNotEmpty`
The `RuleUtils.validateRulePropsIsNotEmpty`
method is a convenient way to verify that one or more required properties have been provided to your rule. It checks that the properties exist in the context and that their values are not null or empty.
If a property is missing or empty, a `PropertyNotFoundException`
is thrown. This exception includes a descriptive error message indicating which property is missing, and it stops the rule execution immediately.
1import com.fluentcommerce.util.core.RuleUtils;
2
3//...
4
5@Override
6public void run(Context context) {
7 // Validate that all required properties are present before executing logic
8 RuleUtils.validateRulePropsIsNotEmpty(
9 context,
10 "endpointUrl",
11 "eventName",
12 "retryCount"
13 );
14
15 // If validation passes, you can safely retrieve the properties
16 String url = context.getProp("endpointUrl", String.class);
17 String eventName = context.getProp("eventName", String.class);
18
19 // ... rest of your rule logic
20}
Related content
Query Utilities
Author:
Kirill Gaiduk
Changed on:
10 July 2025
Overview
The `QueryUtils`
class in the `util-core`
bundle offers a set of functions to simplify GraphQL queries from within your Rules. These helpers make it easier to work with query results and handle common data structures like paginated connections.
Key points
- Type-Safe Queries: The
`query`
method is a wrapper that eliminates the need to manually cast GraphQL responses to the correct type, making your code cleaner and safer. - Simplified Connections:
`connectionToList`
is a powerful helper that converts a standard GraphQL "connection" (with its`edges`
and`nodes`
) into a simple`List`
of objects, removing a significant amount of boilerplate code. - Inline Transformation:
`connectionToList`
can also accept a function, allowing you to transform the list of GraphQL nodes into a list of your own domain objects (POJOs) in a single operation.
Core Methods
`query`
This method is a wrapper around the standard `context.api().query()`
method. Its main advantage is that it is properly generified, meaning you don't need to manually cast the response to the correct `Operation.Data`
type.
1import static com.fluentcommerce.util.core.QueryUtils.query;
2
3// ...
4
5// The 'query' helper automatically returns the correct response type,
6// so no casting is needed.
7GetMyEntityQuery.Data response = query(
8 context,
9 GetMyEntityQuery.builder()
10 .id(context.getEvent().getEntityId())
11 .build()
12);
13
14// You can now access the response data directly
15String status = response.entity().status();
`connectionToList`
This utility is designed to solve a common problem when working with GraphQL: converting a paginated "connection" into a `List`
. A connection is a standard GraphQL pattern for representing one-to-many relationships (like an order having multiple items) that includes pagination cursors and edges.
The `connectionToList`
method automatically extracts the `node`
from each `edge`
in the connection and returns an `ImmutableList`
of nodes.
When using this method, make sure that the GraphQL query aliases for the connection fields are correctly named or suffixed with "edge" and "node." If you have multiple connections in a single query, Apollo might automatically name them as `Node1`
, `Node2`
, and so on, which can cause issues. To prevent this, explicitly alias each connection with meaningful names like `itemNode`
or `fulfilmentNode`
.
An overloaded method provides a second boolean which controls error handling:
`true`
will throw an exception if the connection is invalid`false`
will simply return an empty list in those cases.
Example
Imagine a `GetOrderByIdQuery`
that returns an order with a connection of `items`
. The response structure might look like this:
1{
2 "data": {
3 "order": {
4 "id": "123",
5 "items": {
6 "edges": [
7 { "node": { "id": "item-A", "ref": "SKU-A" } },
8 { "node": { "id": "item-B", "ref": "SKU-B" } }
9 ]
10 }
11 }
12 }
13}
Without the utility, you would need to write nested loops and null checks to get a list of items. With `QueryUtils`
, it's a single line:
1import com.fluentcommerce.util.core.QueryUtils;
2import com.google.common.collect.ImmutableList;
3
4// ...
5
6// Assume 'response' is the result from the GetOrderByIdQuery
7GetOrderByIdQuery.Items itemsConnection = response.order().items();
8
9// Convert the entire connection to a simple list of item nodes
10ImmutableList<GetOrderByIdQuery.Node> items = QueryUtils.connectionToList(itemsConnection);
11
12// You now have a simple list to work with
13for (GetOrderByIdQuery.Node item : items) {
14 context.action().log("Item ref: " + item.ref());
15}
`connectionToList`
(using a converter)
An overloaded version of `connectionToList`
accepts a `Function`
to convert the nodes into a different type. This is useful for mapping GraphQL response objects to your own domain models.
This variant not only extracts the "node" elements from the "edge" structure but also applies a converter function (from type `T`
to type `R`
) to each node. As a result, it returns a list where each item is the result of applying the converter function to the original nodes.
1// Convert the connection directly to a list of MyItem domain objects
2ImmutableList<MyItem> myItems = QueryUtils.connectionToList(
3 itemsConnection,
4 itemNode -> new MyItem(itemNode.id(), itemNode.ref())
5);
Related content
Setting Utilities
Author:
Kirill Gaiduk
Changed on:
10 July 2025
Overview
The `SettingUtils`
class in the `util-core`
bundle provides a robust set of tools for retrieving configuration settings from the Fluent Commerce API. A key feature of this utility is that it automatically respects the platform's setting hierarchy, ensuring that the most specific setting value is always returned.
Key points
- Automatic Hierarchy:
`SettingUtils`
automatically respects the Fluent Commerce setting hierarchy (Location > Retailer > Account > Global), so you always get the most specific value without any extra work. - Type-Safe Conversion: The
`.as(MyClass.class)`
method allows you to convert a setting's value to any type—from simple primitives (`String`
,`Integer`
) to complex POJOs—in a single, null-safe operation. - Efficient Bulk Fetching: You can retrieve multiple settings (
`getSettings`
) or a group of settings by a common prefix (`getSettingsByNamespace`
) in a single API call, which is more performant than fetching them one by one.
The Setting Hierarchy
Fluent Commerce allows settings to be defined at multiple levels. When you request a setting, the platform searches for a value in the following order of precedence:
- Location: Settings for a specific location (also known as Agent).
- Retailer: Settings for a retailer.
- Account: Settings across the account.
- Global: Reserved for platform use.
`SettingUtils`
automatically handles this hierarchy for you. When you request a setting, it returns the value from the most specific context that has one.
Core Methods
`getSetting`
This is the simplest method for retrieving a single setting. You provide the setting's name, and it returns a `Setting`
object.
1import com.fluentcommerce.util.core.SettingUtils;
2import com.fluentcommerce.util.core.SettingUtils.Setting;
3import java.util.Optional;
4
5// ...
6
7// Get a single setting by its name
8Setting mySetting = SettingUtils.getSetting(context, "my.setting.name");
The `Setting`
Object
The `getSetting`
method returns a `Setting`
object, which has a powerful `.as()`
method. This allows you to convert the setting's value into the data type you need, including complex POJOs if the setting is stored as JSON.
1// Get a setting and convert it to a String, with a fallback default value
2String endpointUrl = SettingUtils.getSetting(context, "my.endpoint.url")
3 .as(String.class)
4 .orElse("https://default.api.com/endpoint");
5
6// Get a setting and convert it to an Integer
7int retryCount = SettingUtils.getSetting(context, "my.retry.count")
8 .as(Integer.class)
9 .orElse(3);
10
11// If a setting stores a JSON object, you can convert it directly to a POJO
12Optional<MyConfig> config = SettingUtils.getSetting(context, "my.json.config")
13 .as(MyConfig.class);
`getSettings`
If you need to retrieve multiple settings at once, `getSettings`
is more efficient than calling `getSetting`
repeatedly, as it fetches all specified settings in a single API call.
You provide a `Map`
where the keys are your own identifiers and the values are the setting names. The method returns a new `Map`
with the same keys, but with each value replaced by a `Setting`
object containing the corresponding setting value.
1Map<String, String> settingsToFetch = ImmutableMap.of(
2 "endpoint", "my.endpoint.url",
3 "timelimit", "my.timelimit.value"
4);
5
6// Get multiple settings in one call
7Map<String, Setting> fetchedSettings = SettingUtils.getSettings(context, settingsToFetch);
8
9// Access the settings using the keys you provided
10String endpoint = fetchedSettings.get("endpoint").as(String.class).orElse(null);
11int timeout = fetchedSettings.get("timelimit").as(Integer.class).orElse(5000);
`getSettingsByNamespace`
This method allows you to fetch all settings that share a common prefix or "namespace". This is useful for retrieving a group of related settings without having to know all their individual names.
If a setting contains both a `value`
and a `lobValue`
(line-of-business-specific value), this method will prioritize `value`
, giving preference to the primary configuration over the business-specific one. The result is returned as a `Map`
, where the keys match those provided in the request, but each value is replaced with a `Setting`
object containing the resolved setting value.
1// Fetch all settings that start with "my.feature."
2Map<String, Setting> featureSettings = SettingUtils.getSettingsByNamespace(context, "my.feature");
3
4String featureName = featureSettings.get("my.feature.name").as(String.class).orElse(null);
5boolean isEnabled = featureSettings.get("my.feature.enabled").as(Boolean.class).orElse(false);
Related content
Log Utilities
Author:
Kirill Gaiduk
Changed on:
10 July 2025
Overview
The `LogUtils`
class in the `util-core`
bundle provides structured logging helpers that remove boilerplate code when using logging actions within your rules. These utilities make it easy to record key actions, errors, and checkpoints to the event log for debugging and auditing purposes.
Key points
- Standardized Logging: The utilities provide a standard, structured format for log messages, making the event log easier to read and search.
- Automatic Titling: The log entry's title is automatically generated from the rule's class name, ensuring consistency and saving you from writing boilerplate code.
- Simple Checkpoints:
`logOnce`
is an easy way to record a specific outcome or checkpoint in your rule's execution with a formatted message. - Collections:
`logAttributeCollection`
is a convenient helper for logging a list of attributes.
Key Concepts
The logging utilities are wrappers around the standard `context.action().log()`
method. They provide two main benefits:
- Automatic Titling: The title of the log entry is automatically generated from the
`@RuleInfo`
name of the rule class you provide. This creates consistent`LogEvents`
which can be searched and filtered through the Event API. - Structured Formatting: The methods provide a clear structure for the log's title and subtitle (using
`String.format()`
), making the event log easier to read and query.
Core Methods
`logOnce`
This method logs a single, formatted message to the event log. It is ideal for recording a specific, meaningful outcome or checkpoint in your rule's execution.
The log title is automatically set to "Rule logs for rule YourRuleName". The subtitle is generated from your format string and arguments.
1import com.fluentcommerce.util.core.LogUtils;
2
3// ...
4
5public class ProcessOrderRule implements Rule {
6 @Override
7 public void run(Context context) {
8 String orderRef = context.getEvent().getEntityRef();
9 String orderStatus = context.getEvent().getEntityStatus();
10
11 // Log successful order processing with order details
12 LogUtils.logOnce(
13 context,
14 ProcessOrderRule.class,
15 "Successfully processed order %s with status '%s'. Order contains %d line items.",
16 orderRef,
17 orderStatus,
18 getLineItemCount(context)
19 );
20
21 // ... rest of rule logic
22 }
23
24 private int getLineItemCount(Context context) {
25 // Implementation to get line item count
26 return 5; // Example value
27 }
28}
`logAttributeCollection`
This method is used to log a `List`
of `Attribute`
objects. The log title is the same as `logOnce`
, and the subtitle is formatted as "LogCollection:[size]", where size is the number of attributes in the list.
1import com.fluentcommerce.util.core.LogUtils;
2import com.fluentretail.api.model.attribute.Attribute;
3import java.util.List;
4
5// ...
6
7public class LogAttributesRule implements Rule {
8 @Override
9 public void run(Context context) {
10 // Assume 'getAttributesFromSomeplace()' returns a list of attributes
11 List<Attribute> attributesToLog = getAttributesFromSomeplace();
12
13 if (!attributesToLog.isEmpty()) {
14 // Log the entire collection of attributes for debugging
15 LogUtils.logAttributeCollection(
16 context,
17 LogAttributesRule.class,
18 attributesToLog
19 );
20 }
21 }
22}
Related content
JSON Utilities
Author:
Kirill Gaiduk
Changed on:
28 July 2025
Overview
The `JsonUtils`
class in the `util-core`
bundle provides a foundational set of helper methods for working with JSON data. Built on top of the Jackson library, these utilities simplify the conversion of raw JSON into usable Plain Old Java Objects (POJOs) and vice-versa.
JSON is a common data format found in event attributes, entity attributes, and setting values, and these utilities are essential for effective rule development.
Key points
- Universal Conversion (
`anyToPojo`
):This method can convert almost any Java object (especially`Map`
s from event attributes) into a strongly-typed POJO, making the code safer and easier to read. - POJO to Map (
`pojoToMap`
): The reverse of`anyToPojo`
. This is essential for when it is necessary to need to add a complex object as an attribute to a new event. - String to POJO (
`stringToPojo`
): Directly converts a raw JSON string into a typed POJO, which is useful when dealing with data from external systems or settings. - Built on Jackson: The utilities are built on the Jackson library.
Core Methods
`anyToPojo`
This is one of the most frequently used methods in the entire utility library. It can convert almost any Java `Object`
into a typed Plain Old Java Object (POJO). Use this method for converting unstructured `Map`
objects from event attributes or settings into strongly-typed objects that are easier and safer to work with.
1// Attribute value might be a map like this:
2// { "productRef": "SKU-123", "quantity": 10 }
3
4// Define a POJO to represent this data structure
5@Data // from lombok
6public class OrderItemData {
7 private String productRef;
8 private int quantity;
9}
10
11// In your rule, get the attribute from the event
12Object itemDataAttribute = context.getEvent().getAttributes().get("itemData");
13
14// Use anyToPojo to convert the map to your typed object
15OrderItemData itemData = JsonUtils.anyToPojo(itemDataAttribute, OrderItemData.class);
16
17// Now you can work with the typed object
18String productRef = itemData.getProductRef();
`pojoToMap`
This method does the reverse of `anyToPojo`
. It converts a Java object (POJO) into a `Map<String, Object>`
, which is useful when you need to add complex data as an attribute to an event.
1MyCustomObject data = new MyCustomObject("value1", 123);
2
3// Convert the POJO to a map
4Map<String, Object> dataAsMap = JsonUtils.pojoToMap(data);
5
6// Add the map as an event attribute
7EventUtils.forwardInlineEventWithAttributes(context, "EVENT_WITH_DATA", dataAsMap);
`stringToNode`
/ `stringToPojo`
These methods handle the conversion of a JSON string into either a generic `JsonNode`
(for manual tree traversal) or directly into a typed POJO.
1String jsonString = "{\"key\":\"value\"}";
2
3// Convert string to a generic JsonNode
4JsonNode node = JsonUtils.stringToNode(jsonString);
5String value = node.get("key").asText();
6
7// Convert string directly to a POJO
8MyPojo pojo = JsonUtils.stringToPojo(jsonString, MyPojo.class);
`objectToNode`
This method converts any object into its `ObjectNode`
representation, which can be useful for more complex JSON manipulation.
1MyPojo pojo = new MyPojo("data");
2ObjectNode node = JsonUtils.objectToNode(pojo);
3node.put("newField", "newValue");
Related content
GraphQL Date Utilities
Author:
Kirill Gaiduk
Changed on:
24 June 2025
Overview
The `GqlDateUtils`
class in the `util-core`
bundle provides a simple and effective set of functions for handling the specific date format required by the Fluent Commerce GraphQL API. The platform uses the ISO 8601 format (`yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`
), and this utility ensures you can easily parse and format dates to be compatible.
Key points
- GraphQL Compatibility: This utility ensures that dates are always in the precise ISO 8601 string format required by the Fluent Commerce GraphQL API (
`yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`
). - Parsing (
`parseGqlDate`
): Safely parses a string from an event or external system into a standard Java`Date`
object, preventing common parsing errors. - Formatting (
`toGqlDate`
): Converts a Java`Date`
object into the specific string format required when passing dates as variables in GraphQL queries or mutations. - Centralized Logic: Centralizes date handling to avoid bugs and ensure consistency across your entire implementation.
Key Concepts
GraphQL APIs require a precise string format for `DateTime`
fields. This utility centralizes the logic for converting to and from this format, preventing common errors related to date parsing and formatting.
The standard format is: `yyyy-MM-dd'T'HH:mm:ss.SSS'Z'`
- Example:
`2023-10-27T10:30:00.123Z`
Core Methods
`parseGqlDate`
This method parses an object into a standard Java `Date`
. It is designed to be flexible and can handle two types of input:
- A
`java.util.Date`
object (which it will simply pass through). - A
`String`
that is in the correct GraphQL date format.
This is particularly useful when receiving dates from external systems or in event attributes, which may be in string format.
1import com.fluentcommerce.util.core.GqlDateUtils;
2import java.util.Date;
3
4// ...
5
6// An incoming date string from an event attribute or external system
7String dateString = "2023-11-21T15:45:10.000Z";
8
9// Use the utility to parse it into a Date object
10try {
11 Date parsedDate = GqlDateUtils.parseGqlDate(dateString);
12 // Now you can work with the Date object
13} catch (IllegalArgumentException e) {
14 LogUtils.logOnce(
15 context,
16 MyClass.class,
17 "Error parsing date: '%s'",
18 e.getMessage()
19 );
20}
21
22// It also safely handles objects that are already a Date
23Date alreadyADate = new Date();
24Date result = GqlDateUtils.parseGqlDate(alreadyADate); // This works perfectly
`toGqlDate`
To parse a `Date`
object into a `String`
with the format `"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"`
, use the `GqlDateUtils.toGqlDate`
method:
1String currentDate = GqlDateUtils.toGqlDate(new Date());
`getFormat`
To retrieve the date format used by GraphQL, which is `"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"`
, use the `GqlDateUtils.getFormat`
method:
1assertEquals("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", GqlDateUtils.getFormat().toPattern())