Change log

Value resolver – Abstract

Purpose: Creates a change log that determines the differences between an object passed as an input value and a parameterized compare object and outputs them in structured form – as a 'change log'.

images/download/attachments/177910326/image-2024-9-10_14-55-49-version-1-modificationdate-1725972949243-api-v2.png


IMPORTANT◄ This value resolver is not available in a Client workflow!


The Change log value resolver generates a change log that determines the differences between an object passed as input value and a compare object defined by the Compare value parameter and outputs them in a structured form – as a 'change log'.

The type of the return value depends on the type of the input value.

Input value type

Examples

Return value type

Specific fields

Simple value

Text values, numeric values, enumeration values, etc.

'Change Log > Simple value' (SimpleValueChangeLog)

fromValue, toValue

Collection

List, Unique list, etc.

'Change Log > Collection' (CollectionChangeLog)

fromLength, toLength

Entity

Business object, Custom type definition, Line item, Configuration,
Attribute implementation (see also table below), etc.

'Change log> Entity' (EntityChangeLog)

fromId, toId

Other objects

Client object, date value, date range, attribute (see also table below), etc.

'Change log > Object' (ObjectChangeLog)

fromClass, toClass

IMPORTANT◄ Technically, each change log type offers the fields fromIndex and toIndex, which refer to the sequence position within a collection. These fields contain the value -1 if the index is not applicable (due to lack of reference to a collection).

NOTE◄ Changes to attributes of an entity can be determined in different ways. The return value type varies:

Input value for the change log

Input value type

Return value type

Specific fields

Attribute owner Parent entity of the attribute
e.g. a business object or also a line item

Entity

'Change log > Entity' (EntityChangeLog) for the attribute owner, which contains one of the following change log types for each attribute that has been changed:

  • 'Change log > Singular attribute' (SingularAttributeChangeLog)

  • 'Change log > Plural attribute' (PluralAttributeChangeLog)

  • 'Change log > Typed attribute' (TypedAttributeChangeLog)

attributeType

Attribute implementation e.g. 'Order > Text attribute' type

Entity

'ChangeLog > Entity' (EntityChangeLog) for an instance of the attribute implementation as a standalone entity containing the actual attribute as a value (see next line) in the value field.

no attribute

Attribute
e.g. 'Text attribute' type

Object

'Change Log > Object' (ObjectChangeLog) for the attribute containing change logs for other types (see above) to describe the actual changes.

no attribute

Compare logic:

  • If no differences are found between the input value and the compare object, the return value is 'No value' ($null)

  • If no Compare value is configured (default) and an entity is available as input value at runtime, its Original entity (i.e. the server-side state for the entity) is used as the compare object. This is the typical use case for the value resolver

    • The Ignore entity attributes option only works in this use case. It controls whether changes to the entity attributes ('Owner', Created by', 'Creation date', etc.) are ignored (default) or taken into account during a comparison.

    • If the entity in the input value contains other entities (e.g. line items or attributes), a change log is generated for them – recursively if necessary – which appears together with other 'changes' as part of the parent change log.

  • If the input value is not an entity, but e.g. a simple value, a list or a 'client object', then the Compare value should be configured explicitly, otherwise the input value is also a compare object and the value resolver returns 'No value' ($null).

    • If the input value is a list of entities, these will not be automatically compared with the respective Original entity if no Compare value is configured. However, if necessary, this can be achieved by explicit configuration for the Compare value (see Special use case in the 'Examples' section).

  • The Log details for deleted and created values option controls how much information the change log provides about objects that are classified as created or deleted in the context of the comparison:

    • For created or deleted values, only a minimal amount of information appears by default (option unchecked)

    • If the option is checked, the change log will output more data of the object in question:

      • An object classified as 'deleted' is reproduced in its entirety with all its details.

      • For an object classified as 'created', its data is reflected by a 'change log' with 'No value' ($null) as the compare object.

►IMPORTANT◄ What kind of 'changes' does the change log describe?


The change log is not a chronological list of manipulations actually performed on a specific object. In fact, two objects (the input value and the compare object) are compared, which may or may not be different versions of the same object. Generally formulated, the 'change log' describes one or more 'changes' that could transform the compare object into the input value. The change log therefore describes a possible transformation from the compare object to the input value, without claiming that the described 'changes' have taken place at all and, if exactly so.

Examples illustrating the evaluation logic in the change log:

  • The input value contains a volatile dataset for any entity that has a 'Name' (name) field. The server-side dataset (see Original entity) shows the text 'foo' for the name field. In the volatile dataset, the text 'bar' was assigned as 'Name' during the current transaction. .

    • As long as no other changes have been made to the entity in the transaction, the entity is effectively considered changed and the Change log value resolver shows the value change from 'foo' to 'bar' for the 'Name' field.

  • As an extension to the previous scenario, the 'Name' of the entity is changed back to the original value, i.e. 'foo', after the change from 'foo' to 'bar'.

    • As long as no other changes have been made to the entity in the transaction, the entity is thereby considered effectively unchanged and the Change log value resolver returns 'No value' ($null).

  • In the input value there is a list with three text characters (JSON image: ["O","X","O"]), which as a Compare value is a list with the JSON image ["O","O","O"] contrasted.

    • Intuitively, one would perhaps expect the second value of the list to be evaluated as 'changed from O to X'. In contrast, the Change log value resolver shows the following 'changes' accordingly:

      • The 'O' at the third list position in the compare value has been changed to an 'X' and moved to the second position.

      • The 'O' at the second list item has been moved to the third position.

    • The final result of these operations corresponds to the input value as well as the change of the second position described above as 'intuitive'. Why is this so complicated? The value resolver determines the changes to a list according to a system that first searches the list in the input value for entries already 'known' from the compare object and only then evaluates unassigned target values as 'added' or 'changed'. The 'secondary condition', intuitive for people, to get from the compare value to the input value by a minimum number of 'changes', does not play any role for this algorithm.

Example of a change log:

<?xml version="1.0" encoding="UTF-8"?>
<core:EntityChangeLog [...] fromIndex="-1" toIndex="-1" changeType="MODIFIED" fromClass="shp:Shipment" toClass="shp:Shipment" fromId="51" toId="51">
<changeLogs>
<core:TypedAttributeChangeLog name="attributes" fromIndex="-1" toIndex="-1" changeType="MODIFIED" fromClass="shp:ShipmentText" toClass="shp:ShipmentText" fromId="1" toId="1" attributeType="base:TextAttribute" type="base:TextType#DESCRIPTION">
<changeLogs>
<core:ObjectChangeLog name="value" fromIndex="-1" toIndex="-1" changeType="MODIFIED" fromClass="base:TextAttribute" toClass="base:TextAttribute">
<changeLogs>
<core:SimpleValueChangeLog name="textValue" fromIndex="-1" toIndex="-1">
<fromValue xsi:type="xsd:string">Description (original)</fromValue>
<toValue xsi:type="xsd:string">Description (updated)</toValue>
</core:SimpleValueChangeLog>
</changeLogs>
</core:ObjectChangeLog>
</changeLogs>
</core:TypedAttributeChangeLog>
</changeLogs>
</core:EntityChangeLog>
  • The return value of the type 'Change log > Entity' (EntityChangeLog) defines via the toClass field that a shipment (see Shipments) was passed as input value and refers to its ID (toId) with the value 51.

    • Since the fromClass and from Id fields do not specify any data that differs from the input value, the comparison apparently involves two data statuses for the shipment with ID 51. Typically, the Original entity serves as the compare object here because no Compare value has been configured.

  • In the example, the changeLogs list field contains exactly one element of the 'Change log > Typed attribute' (TypedAttributeChangeLog) type, whose changeType field names the 'change type' as MODIFIED.

    • It can be seen from the fromClass and toClass fields that the TypedAttributeChangeLog concerns a typed text attribute – more precisely: its implementation – for a shipment (ShipmentText).

    • The attributeType field refers to the attribute type (TextAttribute) used as the value, and the type field identifies the 'Description' subtype (DESCRIPTION) defined as the value from the dynamic enumeration Text type (TextType).

  • The changeLogs list field within the TypedAttributeChangeLog element contains a 'Change log > Object' (ObjectChangeLog) that classifies the attribute (TextAttribute) as changed (MODIFIED).

    • The name field here refers to the name of the value field in the data model of the parent ShipmentText object.

  • The changeLogs list field within the ObjectChangeLogs contains a 'Change log > Simple value' (SimpleValueChangeLog) for the textValue field (see name) in the TextAttribute object.

    • The SimpleValueChangeLog documents the change of the text value from 'Description (original)' (fromValue) to 'Description (updated)' (toValue).


images/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg CAUTIONimages/s/-95e2zf/9012/8yg2g7/_/images/icons/emoticons/warning.svg As the example shows, even a simple text value change within an existing typed attribute of a shipment generates a significant amount of information in the return value of the Change log value resolver.
When using the value resolver, it should be borne in mind that multiple changes within complex objects (such as a business object with a position hierarchy and attributes at all levels) require considerable 'computing effort' and can generate corresponding amounts of data. Often, a comprehensive Change log is subsequently evaluated specifically in order to obtain relevant information or to prepare it in a more easily 'readable' form.


Configuration

'Ignore entity attributes' option

The Ignore entity attributes option is checked by default.

When comparing 'entity data', the so-called entity attributes are then not evaluated.

Example: The input value is the volatile data of a random entity, which (without configuration for the Compare value) is matched with the server-side data state. In the default configuration for the Change log value resolver shown on the top right, the Ignore entity attributes option is checked.

If the only change in the entity's volatile data is a 'change of owner' – i.e. a value for the entity attribute 'owner' (ownerId) field that has changed compared to the Original entity, the value resolver still returns 'No value' ($null), since the volatile data state is evaluated as unchanged despite the change.

images/download/attachments/177910326/image-2024-9-10_14-58-26-version-1-modificationdate-1725973106535-api-v2.png

images/download/attachments/177910326/image-2024-9-10_14-58-47-version-1-modificationdate-1725973126861-api-v2.png

If the Ignore entity attributes option is unchecked, then the comparison between the input value and compare object for the change log will also include all entity attributes.

For the above example, this means that a 'change of owner' is also considered a change, so that in the return value of the Change log value resolver you will find, among other things, a SimpleValueChangeLog object for the ownerId field.

'Log details for deleted and created values' option

The Log details for deleted and created values option is unchecked by default.

If the compare value is classified as 'created' or 'deleted', only the minimum functionality appears in the return value of the Change log value resolver. The respective 'change type' (changeType), i.e. DELETED or CREATED is specified in the header of the respective change log entry, without giving all details about the object.

Example: The input value is volatile data for a company account in which the address (still) stored on the server side has been deleted. When the comparison is made with the Log details for deleted and created values option unchecked (see screenshot above right), an EntityChangeLog (with the embedded address as entity) is output for the address field, which shows the 'change type' (changeType) DELETED and identifies the deleted entity only via the fromClass and fromId fields.

images/download/attachments/177910326/image-2024-9-10_14-59-12-version-1-modificationdate-1725973152531-api-v2.png


images/download/attachments/177910326/image-2024-9-10_14-59-30-version-1-modificationdate-1725973170232-api-v2.png

If the Log details for deleted and created values option is checked (see screenshot below right), then in the scenario described above the comparison will provide detailed information for the deleted address, which for this purpose is marked with 'No value' ($null). The EntityChangeLog for the address field contains, in addition to the data also listed in the default case (fromClass,fromId) in the changeLogs list field, a possibly very extensive listing of child change logs, as if each feature within the address had been deleted individually. By recursion, this also involves attributes of the address, address contacts, their attributes, etc.

Compare value configuration

The optional Compare value parameter can be used to contrast the input value with a compare object.

If a configuration for the compare value is completely omitted (default, see screenshot on the right), then the 'original object' for the input value is used as the compare object.

  • For an entity as an input value, this means that a volatile and possibly changed data state is compared with the server-side state from the beginning of the transaction.

  • For all other input value types, the input value itself is considered as the compare object, so no change log is expected. The return value is then 'No value' ($null).

images/download/attachments/177910326/image-2024-9-10_14-59-12-version-1-modificationdate-1725973152531-api-v2.png

images/download/attachments/177910326/image-2024-9-10_15-4-19-version-1-modificationdate-1725973459178-api-v2.png

In the configuration on the right, the address of the Company of session (in the input value) is compared with the address specified for the User of session as a compare object.

  • The company address in the input value is provided here by a concatenation with the Company of session value resolver and an Object property value resolver that accesses the address field of the company account.

  • In the Compare value parameter, starting from the User of session value resolver, the Input object (type safe) value resolver is used to ensure that a user account (and not, for example, a guest user account) is present. Then the address field is accessed in the same way as the input value.

NOTE◄ If a guest user (see Guest users) is logged in, the User of session value resolver does not return a user account (see Users). Then the concatenated Input object (type safe) value resolver passes 'No value' ($null) to the Object property value resolver. However, since a guest user account does not have an address field, this would not work anyway. If a value resolver chain is configured for the Compare value that returns 'No value' ($null) at runtime, $null is used as the compare object. If an entity is present as input value, this results in an EntityChangeLog that evaluates the entity in the input value as 'created' (CREATED). Without configuration for the Compare value, on the other hand, $null would be returned, as described above.

Examples

Change log > Simple value

The following examples use the JSON format to describe data structures.

Input value

Compare value

Change log

"ABC"
"XYZ"
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": "DEF",
"name": null,
"toIndex": -1,
"toValue": "ABC"
}
  • For a text value (String), the string as a whole is considered a value (fromValue, toValue).

  • A comparison for the length (length), which is offered in the Object property value resolution as a 'field', does not take place, since it is a calculated quantity.

  • For an independent String the index (fromIndex, toIndex) and the name (name) does not matter.

4711
3210
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": {
"class": "java.lang.Integer",
"value": 3210
},
"name": null,
"toIndex": -1,
"toValue": {
"class": "java.lang.Integer",
"value": 4711
}
}
  • A numerical value is considered as a value (fromValue, toValue) considering the data type (class).

  • In the example above, the sequence of digits (in JSON) for the compared values is interpreted as an Integer.

  • For an independent String the index (fromIndex, toIndex) and the name (name) does not matter.

{ "class": "java.lang.Long", "value": "4711" }


e.g. from a Long value resolver

{ "class": "java.lang.Integer", "value": "4711" }

images/download/attachments/177910326/image-2024-9-10_15-6-51-version-1-modificationdate-1725973611137-api-v2.png

e.g. from an Integer value resolver

{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": {
"class": "java.lang.Integer",
"value": 4711
},
"name": null,
"toIndex": -1,
"toValue": {
"class": "java.lang.Long",
"value": "4711"
}
}
  • Input value and Compare value differ here only in the data type (class).

  • The SimpleValueChangeLog therefore registers a change of the value, but does not indicate that the class field has changed.


Enumeration value 'East' (E) from the user-defined dynamic enumeration DIRECTIONS:

images/download/attachments/177910326/image-2024-9-10_15-13-19-version-1-modificationdate-1725973998599-api-v2.png

see also Any dynamic enumeration

Enumeration value 'North' (N) from the user-defined dynamic enumeration DIRECTIONS:

images/download/attachments/177910326/image-2024-9-10_15-13-54-version-1-modificationdate-1725974033823-api-v2.png

see also Any dynamic enumeration

{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": {
"class": "de.lobster.scm.denum.DynamicEnumDto",
"enumName": "DIRECTIONS",
"name": "N"
},
"name": null,
"toIndex": -1,
"toValue": {
"class": "de.lobster.scm.denum.DynamicEnumDto",
"enumName": "DIRECTIONS",
"name": "E"
}
}
  • The values of static and dynamic enumerations are compared as 'Simple values'.

  • For independent enumeration values the index (fromIndex, toIndex) and name (name) does not matter.

Change log > Collection

The following examples use the JSON format to describe data structures.

Input value

Compare value

Change log

["Tom","Dick","Harry","Sally"]
["Tom","Dick","Harry"]
{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": null,
"name": null,
"toIndex": 3,
"toValue": "Sally"
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 3,
"name": null,
"toIndex": -1,
"toLength": 4
}
  • An entry has been added (at the end) to a list (List) of text values.

    NOTE◄ In fact, the list in the input value is compared with another list in the Compare value, which contains the same static entries at the first three positions. A real 'change' to the statically defined list (e.g. by Modify list) could not be examined by Change log, because no Original entity is available as a reference.

  • The CollectionChangeLog registers the change of the length of the list (fromLength, toLength).

  • On the other hand, a SimpleValueChangeLog appears in the changeLogs list field, specifying the new value (toValue) for the added list item (toIndex).

["Tom","Dick","Harry"]
["Tom","Dick","Harry","Sally"]
{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": 3,
"fromValue": "Sally",
"name": null,
"toIndex": -1,
"toValue": null
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 4,
"name": null,
"toIndex": -1,
"toLength": 3
}
  • From a list (List) of text values the last entry is 'removed' (see note above).

  • The CollectionChangeLog registers on the one hand the change of the length of the list (fromLength, toLength).

  • On the other hand, a SimpleValueChangeLog appears in the changeLogs list field, which logs the 'disappearance' of the deleted list item (fromIndex, fromValue) as a value change to 'No value' ($null).

    ►NOTE◄ It can be seen that the list position was not really assigned the value $null from the fact that the index (toIndex) changes to -1. With the list ["Tom","Dick","Harry",null] as input value the toIndex here would be 3 unchanged.

{ "class": "set", "data": [ "Tom", "Harry" ] }


{ "class": "set", "data": [ "Tom", "Harry", "Dick"] }
{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": null,
"name": null,
"toIndex": -1,
"toValue": "Dick"
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 2,
"name": null,
"toIndex": -1,
"toLength": 3
}
  • An entry is added to a 'Unique list' (Set) (see note above).

  • The CollectionChangeLog registers the change in the length of the list (fromLength, toLength).

  • On the other hand, a SimpleValueChangeLog appears in the changeLogs list box, naming the additional entry (toValue).

NOTE◄ The 'Unique list' does not manage index numbers for the entries. Therefore, the fromIndex and toIndex fields show the default value -1.

[1,2,3]
[3,2,1,0]
{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": 3,
"fromValue": {
"class": "java.lang.Integer",
"value": 0
},
"name": null,
"toIndex": -1,
"toValue": null
},
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": 2,
"fromValue": {
"class": "java.lang.Integer",
"value": 1
},
"name": null,
"toIndex": 0,
"toValue": {
"class": "java.lang.Integer",
"value": 1
}
},
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": 0,
"fromValue": {
"class": "java.lang.Integer",
"value": 3
},
"name": null,
"toIndex": 2,
"toValue": {
"class": "java.lang.Integer",
"value": 3
}
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 4,
"name": null,
"toIndex": -1,
"toLength": 3
}
  • Two lists with static Integer values are compared. All values in the input value also appear in the Compare value, but in a partially different order. The Compare value contains an additional element (0) in the last position.

  • The CollectionChangeLog registers on the one hand the change of the length of the list (fromLength, toLength).

  • The changeLogs field shows changes per SimpleValueChangeLog for the following list items:

    • The last position (fromIndex 3) is considered changed to 'No value' ($null), which means it is deleted. This can also be seen from the change of the index.

    • The value 1 is considered 'moved unchanged' to the first position in the input value (toIndex 0).

    • The value 3 is considered as 'moved unchanged' to the last position in the input value (toIndex 2).

NOTE◄ The value 2 does not appear in the changeLogs field because it is considered completely unchanged.

Special use case: Compare all entities in a list

A collection created with the Create list contains volatile data from different entities that may have changed from the server-side state:

images/download/attachments/177910326/image-2024-9-10_14-50-33-version-1-modificationdate-1725972632624-api-v2.png

  • The first list element defined is the value of a cneCompany variable that refers to a specific company account (see Company accounts accounts).

  • The second list element is the value of a cneKeyUser variable that refers to a specific user account (see Users).

Both accounts have already been created and may have been changed in the current context. The Change log value resolver should detect any 'volatile' changes to the accounts listed as entities.

images/download/attachments/177910326/image-2024-9-10_14-46-16-version-1-modificationdate-1725972376144-api-v2.png

As a Compare value, the list of entities in the input value is contrasted with a list of the associated 'original objects'.

This can be achieved with the Collect values value resolver, which here uses the Original entity value resolver as the Value to collect in order to retrieve the server-side state for each element of the list of entities present as input value.

{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.EntityChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": true,
"name": "active",
"toIndex": -1,
"toValue": false
}
],
"changeType": "MODIFIED",
"fromClass": "de.lobster.scm.base.security.user.User",
"fromId": "1901",
"fromIndex": 1,
"name": null,
"toClass": "de.lobster.scm.base.security.user.User",
"toId": "1901",
"toIndex": 1
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 2,
"name": null,
"toIndex": -1,
"toLength": 2
}
  • The CollectionChangeLog (right) lists only a single entry in the changeLogs field, which affects the user account at the second list position.

  • The changeLogs field in the EntityChangelog for this list entry indicates that the value for the active field has changed from true to false . This means the user account should be be disabled.

  • No changes appear to have been made to the company account.

Change log > Object

The following examples use the JSON format to describe data structures.

Input value

Compare value

Change log

{ "class": "de.lobster.scm.utils.date.DateTime",
"dateValue": "...", "timeZone": "Europe/Berlin" }

A 'Date with time' object determined as Relative date with time:

images/download/attachments/177910326/image-2024-9-10_15-15-38-version-1-modificationdate-1725974137792-api-v2.png

  • 'Start of today' in the Time zone Europe/Berlin .

{ "class": "de.lobster.scm.utils.date.DateTime",
"dateValue": "...", "timeZone": "Europe/Rome" }

images/download/attachments/177910326/image-2024-9-10_15-16-17-version-1-modificationdate-1725974177309-api-v2.png

  • 'Start of today' in the Time zone Europe/Rome.

{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": {
"timeZoneId": "Europe/Rome"
},
"name": "timeZone",
"toIndex": -1,
"toValue": {
"timeZoneId": "Europe/Berlin"
}
}
],
"changeType": "MODIFIED",
"fromClass": "de.lobster.scm.utils.date.DateTime",
"fromIndex": -1,
"name": null,
"toClass": "de.lobster.scm.utils.date.DateTime",
"toIndex": -1
}
  • Since the data type 'Date with time' is defined in Lobster Data Platform / Orchestration as an object with the dateValue and timeZone fields, the comparison between two date values by Change log value resolver results in an ObjectChangeLog.

  • In the example, the 'Start of next day' is compared for two different time zones. Since the compared time zones have the same offset from UTC, the compared objects differ only in the timeZone field, while the dateValue field shows the same numerical value for the milliseconds since 01.01.19700 00:00:00.000 (UTC) in both cases.

{ "name": "Darth Vader", "nickname": "Dee-Vee" }
{ "name": "Anakin" }
{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": "Anakin",
"name": "name",
"toIndex": -1,
"toValue": "Darth Vader"
},
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": null,
"name": "nickname",
"toIndex": -1,
"toValue": "Dee-Vee"
}
],
"changeType": "MODIFIED",
"fromClass": "flex.messaging.io.amf.ASObject",
"fromIndex": -1,
"name": null,
"toClass": "flex.messaging.io.amf.ASObject",
"toIndex": -1
}
  • Two 'client objects' are compared with each other.

  • The ObjectChangeLog evaluates the object state in the changeType field as 'changed' (MODIFIED).

  • In the changeLogs field two changes are shown by SimpleChangeLog:

    • The name field is evaluated as changed from "Anakin" (fromValue) to "Darth Vader" (toValue).

    • The nickname field, which occurs only in the input value, is evaluated as changed from 'No value' ($null).

{ "name": "Luke", 
"tools": { "class": "list", "data": [
{ "skill": "TheForce™" },
{ "item": "LightSaber™" } ]
           }
}
{ "name": "Luke" }
{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
],
"changeType": "CREATED",
"fromClass": null,
"fromIndex": -1,
"name": null,
"toClass": "flex.messaging.io.amf.ASObject",
"toIndex": 0
},
{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
],
"changeType": "CREATED",
"fromClass": null,
"fromIndex": -1,
"name": null,
"toClass": "flex.messaging.io.amf.ASObject",
"toIndex": 1
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 0,
"name": "tools",
"toIndex": -1,
"toLength": 2
}
],
"changeType": "MODIFIED",
"fromClass": "flex.messaging.io.amf.ASObject",
"fromIndex": -1,
"name": null,
"toClass": "flex.messaging.io.amf.ASObject",
"toIndex": -1
}
  • Two 'client objects' are compared with each other.

  • The ObjectChangeLog evaluates the object state in the changeType field as 'changed' (MODIFIED).

  • The changeLogs field of the (outer) ObjectChangeLog contains a CollectionChangeLog, because an additional list field tools was detected in the input value.

  • The changeLogs field of the CollectionChangeLog contains two ObjectChangeLog instances that record no details about the added "tools" other than the list position (toIndex) and the 'change type' (CREATED).

    NOTE◄ Obviously the Change log value resolver was used here without selecting the Log details for deleted and created values option. Otherwise, the return value would also contain information about the object fields (skill and item) as SimpleValueChangeLog in the changeLogs field of the respective ObjectChangeLog.

Change log > Entity

The following examples optionally use the XML format and the JSON format to describe data structures.

Input value

Compare value

Return value

An event handling is triggered by the 'Change' event (see Common action event) so that it reacts to the saving of already created entities. A Check type ensures that the event action becomes active only in the context of Association criteria.

The input value is always the changed volatile data of the association criterion that is saved. Before saving changes, a Change log should be created so that – depending on the type of changes – an administrator can be notified if necessary.

Example:

<?xml version="1.0" encoding="UTF-8"?>
<core:AssociationCriteria ... priority="100" name="XF_MFST_AGV" description="PLEASE CHECK!" onlyForLogic="false">
<core:RuleJunction junctionType="CONJUNCTION">
<core:CheckTypeRule type="mfst:Manifest" isCollectionOf="false" checkNullValue="false"/>
<core:EntityPropertyRule>
<core:EqualsMatcher deepCompare="false">
<core:LiteralValueResolver>
<value enumName="mfst:ManifestType" name="AGV" xsi:type="core:DynamicEnumValue"/>
</core:LiteralValueResolver>
</core:EqualsMatcher>
<core:PropertyResolver property="manifestType"/>
</core:EntityPropertyRule>
</core:RuleJunction>
</core:AssociationCriteria>

  • In the example, only one text for the 'description' (description) field was added to the association criteria. The EntityChangeLog documents exactly this one change via SimpleChangeLog in the changeLogs list field.

not configured, that is the Original entity to the entity in question

{
"class": "de.lobster.scm.persistence.history.EntityChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": "",
"name": "description",
"toIndex": -1,
"toValue": "PLEASE CHECK!"
}
],
"changeType": "MODIFIED",
"fromClass": "de.lobster.scm.association.criteria.AssociationCriteria",
"fromId": "9901",
"fromIndex": -1,
"name": null,
"toClass": "de.lobster.scm.association.criteria.AssociationCriteria",
"toId": "9901",
"toIndex": -1
}

Based on the changed state of the association criteria in the previous example, the notified administrator has reviewed the association criteria and now wants to save it again with changes.

The event handling reacts again and determines the changes to the association criteria via the Change log value resolver.

Example:

<?xml version="1.0" encoding="UTF-8"?>
<core:AssociationCriteria ... priority="50" name="XF_MFST_AGV" description="CHECKED AND APPROVED W/CHANGES" onlyForLogic="false">
<core:RuleJunction junctionType="CONJUNCTION">
<core:CheckTypeRule type="mfst:Manifest" isCollectionOf="false" checkNullValue="false"/>
<core:EntityPropertyRule>
<core:EqualsMatcher deepCompare="false">
<core:LiteralValueResolver>
<value enumName="mfst:ManifestType" name="AGV" xsi:type="core:DynamicEnumValue"/>
</core:LiteralValueResolver>
</core:EqualsMatcher>
<core:PropertyResolver property="manifestType"/>
</core:EntityPropertyRule>
<core:AssociationSubCriteriaRule id="2651"/>
</core:RuleJunction>
</core:AssociationCriteria>

  • The changeLog field in the EntityChangeLog now lists multiple child change logs for the association criteria:

    • The SimpleValueChangeLog in the first position proves that the 'description' (description) field has been changed again.

    • The SimpleValueChangeLog in the second position proves that the 'priority' (priority) has been reduced from 100 to 50.

    • The following ObjectChangeLog shows a change in the 'rule set' in the rule field of the association criteria, which can be 'examined' step by step along the nesting:

      • The CollectionChangeLog affects the rules list field within the outer RuleJunction, i.e. the list of child rules within the AND conjunction (CONJUNCTION):

        • The fact that an additional element was added to this collection is already proven by the change in its 'length' (fromLength, toLength).

        • For the created element, a child ObjectChangeLog appears for which the changeType field specifies the CREATED value. The position of the created element is also specified at this level (toIndex).

          NOTE◄ Apparently, the Log details for deleted and created values option was also checked for the Change log value resolver, so the changeLogs list box contains more details of the added object:

          • The included SimpleValueChangeLog reflects the value (2651) assigned to the id field of the added Sub criterion rule rule (AssociationSubCriteriaRule) in the configuration.

            The following screenshot below shows the rule configuration in the administrator revised state of the association criteria:

images/download/attachments/177910326/image-2024-9-10_15-41-15-version-1-modificationdate-1725975675291-api-v2.png

nicht konfiguriert, also das Original entity zur betreffenden Entität

{
"class": "de.lobster.scm.persistence.history.EntityChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": "PLEASE CHECK!",
"name": "description",
"toIndex": -1,
"toValue": "CHECKED AND APPROVED W/CHANGES"
},
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": {
"class": "java.lang.Integer",
"value": 100
},
"name": "priority",
"toIndex": -1,
"toValue": {
"class": "java.lang.Integer",
"value": 50
}
},
{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.CollectionChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.ObjectChangeLog",
"changeLogs": [
{
"class": "de.lobster.scm.persistence.history.SimpleValueChangeLog",
"fromIndex": -1,
"fromValue": null,
"name": "id",
"toIndex": -1,
"toValue": {
"class": "java.lang.Long",
"value": "2651"
}
}
],
"changeType": "CREATED",
"fromClass": null,
"fromIndex": -1,
"name": null,
"toClass": "de.lobster.scm.association.criteria.rule.AssociationSubCriteriaRule",
"toIndex": 2
}
],
"changeType": null,
"fromIndex": -1,
"fromLength": 2,
"name": "rules",
"toIndex": -1,
"toLength": 3
}
],
"changeType": "MODIFIED",
"fromClass": "de.lobster.scm.association.criteria.rule.RuleJunction",
"fromIndex": -1,
"name": "rule",
"toClass": "de.lobster.scm.association.criteria.rule.RuleJunction",
"toIndex": -1
}
],
"changeType": "MODIFIED",
"fromClass": "de.lobster.scm.association.criteria.AssociationCriteria",
"fromId": "9901",
"fromIndex": -1,
"name": null,
"toClass": "de.lobster.scm.association.criteria.AssociationCriteria",
"toId": "9901",
"toIndex": -1
}