Broadleaf Microservices

Basic Audit (Since 1.5.0-GA)

Basic Audit functionality is provided automatically to all Trackable entities. The out-of-the-box information covered is detailed in the BasicAudit interface and includes: creator, creationTime, updater, and updateTime.

The behavioral characteristics of BasicAudit are as follows:

  1. During Trackable persistence flow, when a Trackable entity is mutated, its BasicAudit information will be curated.

  2. The Trackable flow includes sandbox transition and catalog propagation, so BasicAudit information may be updated on a number of entities based on the number of records mutated in the flow.

  3. Rest API is able to filter fetched records based on BasicAudit information. This leverages the existing RSQL support in most of the Trackable endpoint "findAll" style APIs.

  4. A given implementation may want to respond to an entity mutation by updating the BasicAudit state on another related entity (this could be a parent entity, for example). The solution provides a touch Java API to accomplish this.

  5. Since multiple points in a flow may involve mutation to different states of catalog versions of an entity, a Spring ApplicationEvent is fired to which a given implementation can listen and respond to each of these mutations - and presumably react to via the touch API.

Points (4) and (5) allow updating audit state on important related entities. For example, it may be required for a given implementation that the parent Customer entity receive an updated audit state when a related `CustomerAddress' is mutated. These types of requirements evolve on a case-by-case basis per Broadleaf implementation, and the touch API can be leveraged to accomplish that goal.

Note

The created date is set at the time of entity first creation, and in the case of Trackable entities with sandbox behavior, at the time of sandbox state creation. As a result, when the entity is deployed at the end of the sandbox lifecycle, the creation date on the production record will read with the creation date of the initial sandbox create.

Filtering Based On Audit Information

Most service REST APIs provide endpoints for fetching records using a flexible RSQL syntax for ad-hoc match restrictions. These same endpoints may be used to filter records based on audit information, including author and temporal aspects. When calling the appropriate fetch API, including the cq url param along with the correct RSQL syntax for the individual requirement should achieve the correctly filtered response. Here are several examples:

https:...?cq=tracking.basicAudit.creator=='cathy'

In this case, the Spring Security authentication principal value is used to find all entities whose creator is cathy.

https:...?cq=tracking.basicAudit.updateTime=between=(2021-08-04T18:33:16.769Z,2021-08-04T18:35:16.769Z)

In this case, a timestamp range is used to find all entities whose update time falls within the timeframe.

Note

The timestamp string values used for filtering are expressed as ISO8601 strings. The supported types are ZonedDateTime, LocalDateTime, LocalDate and OffsetDateTime. For ZonedDateTime, the String must follow the rules of DateTimeFormatter#ISO_ZONED_DATE_TIME. For all other types, the String must follow the rules of DateTimeFormatter#ISO_INSTANT.

Here’s some fairly simple Java code to create acceptable temporal strings for filtering:

Instant start = Instant.now(); // UTC timestamp
Instant end = now.plusSeconds(120L);
DateTimeFormatter instantFormatter = DateTimeFormatter.ISO_INSTANT;
String startingTime = instantFormatter.format(start);
String endingTime = instantFormatter.format(end);

There are a number of RSQL operations supported, including some non-standard options. Review com.broadleafcommerce.data.tracking.core.filtering.fetch.rsql.RsqlSearchOperation for a list of operations supported in Broadleaf. Refer to https://github.com/jirutka/rsql-parser for more information on RSQL grammar and semantics.

Reacting to a mutation change of one entity type by updating the audit state of another related entity is often useful, especially when interesting business logic tracks the related entity, but is actually interested in any changes to a group of entity types. Several steps are required to introduce this behavior:

  1. Register an event listener

  2. React to the event by touching the related entity using the BasicAuditUtility Java API.

Listener

@Configuration
class Config {

    @Bean
    ApplicationListener basicAuditListener(BasicAuditUtility auditUtility) {
        return new BasicAuditListener(auditUtility);
    }

}

@RequiredArgsConstructor
static class BasicAuditListener implements ApplicationListener<InternalPersistenceEvent> {

    private final BasicAuditUtility utility;

    @Override
    public void onApplicationEvent(InternalPersistenceEvent internalPersistenceEvent) {
        if (internalPersistenceEvent.getSource() instanceOf JpaItem) {
            JpaItem source = (JpaItem) internalPersistenceEvent.getSource();
            utility.touch(null, touchId, JpaItem.class,
                    (Trackable) internalPersistenceEvent.getSource());
        }
    }

}

We use a Spring Configuration class to register the listener with Spring. In this case, the listener uses the BasicAudit data available on the mutated entity from the event to set the audit data on the target entity. This ensures the timestamps match between the two. Review the BasicAuditUtility#touch API for more information on the options available.