Accessing the history of an asset in Atlan is a flexible operation. This also makes it a bit more complex to understand than the other operations. To encapsulate the full flexibility of Atlan's search, the SDK provides a dedicated AuditSearchRequest object.
Similar but not identical to searching in general
Atlan's audit log that contains the history of an asset uses Elasticsearch. This makes the approach you use to access history similar to searching. However, there are differences as the audit log uses a different index than the broader search. If you're feeling brave, feel free to experiment with the more complex search mechanisms outlined in the searching section. But this should be sufficient to get you started with accessing asset history.
To retrieve an asset's history in Atlan, you need to define the request. For simplicity, we provide helper methods to retrieve a defined number of entries in reverse-chronological order (most recent entries first).
The getCount() method gives the total number of activities. Note that this could be smaller than the number requested, if fewer activities have occurred against the asset than the number used in the request.
The details of each activity can be accessed through the getEntityAudits() method on the response.
The total_count property gives the total number of activities. Note that this could be smaller than the number requested, if fewer activities have occurred against the asset than the number used in the request.
The .count member gives the total number of activities. Note that this could be smaller than the number requested, if fewer activities have occurred against the asset than the number used in the request.
The details of each activity can be accessed through the entityAudits member on the response.
You can then iterate through each activity, in reverse-chronological order (most recent first).
You can access the type of activity through getAction(). This will tell you whether attributes were updated on the asset, an Atlan tag was added, custom metadata was changed, and so on.
You can access who carried out the activity through getUser(). This will give you the username (or API token) that made the change.
You can review when the activity occurred through getTimestamp(). This gives an epoch-based time (in milliseconds) for when the activity occurred.
You can also review what specifically changed through the activity, using getDetail(). More information on what this includes is in the section below.
You can then iterate through each activity, in reverse-chronological order (most recent first).
You can access the type of activity through the action property. This will tell you whether attributes were updated on the asset, an Atlan tag was added, custom metadata was changed, and so on.
You can access who carried out the activity through the user property. This will give you the username (or API token) that made the change.
You can review when the activity occurred through then timestamp property. This gives an epoch-based time (in milliseconds) for when the activity occurred.
You can also review what specifically changed through the activity, using the detail property. More information on what this includes is in the section below.
You can then iterate through each activity, in reverse-chronological order (most recent first).
You can access the type of activity through .action. This will tell you whether attributes were updated on the asset, an Atlan tag was added, custom metadata was changed, and so on.
You can access who carried out the activity through .user. This will give you the username (or API token) that made the change.
You can review when the activity occurred through .timestamp. This gives an epoch-based time (in milliseconds) for when the activity occurred.
You can also review what specifically changed through the activity, using .detail. More information on what this includes is in the section below.
Response contains contextual details
Each object entry in the entityAudits portion of the response will contain contextual details about a single activity on the asset.
Each detail of each record in the activity log tells you the details of what specifically changed through one specific activity. This can be one of three kinds of objects:
Action
Detail type
Contents
ENTITY_UPDATE, ENTITY_CREATE
An asset object (the specific subtype, such as Column or Table)
You can safely type-check the detailed object. You could generically use Asset here instead of Table, but if you know the type of asset you've requested the history for then the detailed object should be the same detailed type.
Once you've type-checked it, you can then coerce it.
From there you can access any properties. Note that only properties actually set by the activity will have values in this detail object. So in this example, only if the description was actually changed to a new value would the description variable now have any content.
This also means that if a field was actually removed (or cleared) by an activity you won't be able to distinguish that by just attempting to retrieve it. (It will be null whether it was removed by the activity or simply wasn't changed by the activity.) To distinguish what was actually removed by an activity, you need to use getNullFields(). The set returned by this method will contain the names of any fields that were actually removed (cleared) by the activity.
You can then take whatever action you like if a field was removed (cleared) by checking for its existence within the getNullFields() set.
You can type-check the detailed object to see if it is an Atlan tag.
Once you've type-checked it, you can then coerce it.
You can access the Atlan tag name using getTypeName().
You can then compare this human-readable Atlan tag name to your expectations to take whatever action you like.
You can type-check the detailed object to see if it details changes to custom metadata.
Once you've type-checked it, you can then coerce it.
You can access the name of the custom metadata using getTypeName().
You can retrieve which custom metadata attributes were changed using getAttributes(). Since the result is a map, it will only contain attributes that were changed. If an attribute was removed (cleared) it will have a null value in the map but the name of the attribute will still be a key in the map. If a custom metadata attribute was not changed by the activity, it will not be a key in this map.
You can then compare these human-readable names to your expectations to take whatever action you like.
You can safely type-check the detailed object. You could generically use Asset here instead of Table, but if you know the type of asset you've requested the history for then the detailed object should be the same detailed type.
From there you can access any properties. Note that only properties actually set by the activity will have values in this detail object. So in this example, only if the description was actually changed to a new value would the description variable now have any content.
You can then take whatever action you like
You can type-check the detailed object to see if it is a 'AtlanTag'.
You can access the 'AtlanTag' name using `type_name' property.
You can then compare this human-readable Atlan tag name to your expectations to take whatever action you like.
You can type-check the detailed object to see if it details changes to custom metadata.
You can access the name of the custom metadata using then type_name attribute.
You can retrieve which custom metadata attributes were changed using the attributes propery. Since the result is a dict, it will only contain attributes that were changed. If an attribute was removed (cleared) it will have a null value in the dict but the name of the attribute will still be a key in the map. If a custom metadata attribute was not changed by the activity, it will not be a key in this map.
You can then compare these human-readable names to your expectations to take whatever action you like.
You can safely type-check the detailed object. You could generically use Asset here instead of Table, but if you know the type of asset you've requested the history for then the detailed object should be the same detailed type.
From there you can access any properties. Note that only properties actually set by the activity will have values in this detail object. So in this example, only if the description was actually changed to a new value would the description variable now have any content.
This also means that if a field was actually removed (or cleared) by an activity you won't be able to distinguish that by just attempting to retrieve it. (It will be null whether it was removed by the activity or simply wasn't changed by the activity.) To distinguish what was actually removed by an activity, you need to use .nullFields. The set returned by this method will contain the names of any fields that were actually removed (cleared) by the activity.
You can then take whatever action you like if a field was removed (cleared) by checking for its existence within the .nullFields set.
You can type-check the detailed object to see if it is an Atlan tag.
You can access the Atlan tag name using .typeName.
You can then compare this human-readable Atlan tag name to your expectations to take whatever action you like.
You can type-check the detailed object to see if it details changes to custom metadata.
You can access the name of the custom metadata using .typeName.
You can retrieve which custom metadata attributes were changed using .attributes. Since the result is a map, it will only contain attributes that were changed. If an attribute was removed (cleared) it will have a null value in the map but the name of the attribute will still be a key in the map. If a custom metadata attribute was not changed by the activity, it will not be a key in this map.
You can then compare these human-readable names to your expectations to take whatever action you like.
You will need to implement your own detection and inference
The key point to note is that the format of the object within the detail of each record will vary, depending on the type of activity that occurred. You will therefore need to implement your own logic for detecting and inferring what kind of details are included when retrieving these from a raw API response.