Skip to content

Commit

Permalink
Merge pull request #911 from atlanhq/DVX-671
Browse files Browse the repository at this point in the history
Adds detailed search log entry option to adoption export
  • Loading branch information
cmgrote authored Oct 14, 2024
2 parents 6a90b77 + b641c40 commit a60a110
Show file tree
Hide file tree
Showing 13 changed files with 542 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import javax.annotation.processing.Generated
data class AdoptionExportCfg(
@JsonProperty("include_views") val includeViews: String? = null,
@JsonProperty("views_max") val viewsMax: Number? = null,
@JsonProperty("views_details") val viewsDetails: String? = null,
@JsonProperty("views_from") val viewsFrom: Long? = null,
@JsonProperty("views_to") val viewsTo: Long? = null,
@JsonProperty("include_changes") val includeChanges: String? = null,
@JsonDeserialize(using = WidgetSerde.MultiSelectDeserializer::class)
@JsonSerialize(using = WidgetSerde.MultiSelectSerializer::class)
Expand All @@ -26,8 +29,11 @@ data class AdoptionExportCfg(
@JsonProperty("changes_from") val changesFrom: Long? = null,
@JsonProperty("changes_to") val changesTo: Long? = null,
@JsonProperty("changes_max") val changesMax: Number? = null,
@JsonProperty("changes_details") val changesDetails: String? = null,
@JsonProperty("changes_automations") val changesAutomations: String? = null,
@JsonProperty("include_searches") val includeSearches: String? = null,
@JsonProperty("maximum_searches") val maximumSearches: Number? = null,
@JsonProperty("searches_from") val searchesFrom: Long? = null,
@JsonProperty("searches_to") val searchesTo: Long? = null,
@JsonProperty("delivery_type") val deliveryType: String? = null,
@JsonProperty("email_addresses") val emailAddresses: String? = null,
@JsonProperty("target_prefix") val targetPrefix: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ import com.atlan.model.assets.Asset
import com.atlan.pkg.Utils
import com.atlan.pkg.adoption.exports.AssetChanges
import com.atlan.pkg.adoption.exports.AssetViews
import com.atlan.pkg.adoption.exports.DetailedSearches
import com.atlan.pkg.adoption.exports.DetailedUserChanges
import com.atlan.pkg.adoption.exports.DetailedUserViews
import com.atlan.pkg.serde.xls.ExcelWriter
import mu.KotlinLogging
import java.io.File
Expand All @@ -33,20 +36,31 @@ object AdoptionExporter {
ExcelWriter(exportFile).use { xlsx ->
if (includeViews != "NONE") {
val maxAssets = Utils.getOrDefault(config.viewsMax, 100).toInt()
val includeDetails = Utils.getOrDefault(config.viewsDetails, "NO") == "YES"
val start = Utils.getOrDefault(config.viewsFrom, -1).toLong()
val end = Utils.getOrDefault(config.viewsTo, -1).toLong()
AssetViews(xlsx, logger, includeViews, maxAssets).export()
if (includeDetails) {
DetailedUserViews(xlsx, logger, start, end).export()
}
}
if (includeChanges) {
val byUsers = Utils.getOrDefault(config.changesByUser, listOf())
val byAction = Utils.getOrDefault(config.changesTypes, listOf())
val start = Utils.getOrDefault(config.changesFrom, -1).toLong()
val end = Utils.getOrDefault(config.changesTo, -1).toLong()
val maxAssets = Utils.getOrDefault(config.changesMax, 100).toInt()
val includeDetails = Utils.getOrDefault(config.changesDetails, "NO") == "YES"
AssetChanges(xlsx, logger, byUsers, byAction, start, end, maxAssets).export()
if (includeDetails) {
val includeAutomations = Utils.getOrDefault(config.changesAutomations, "NONE")
DetailedUserChanges(xlsx, logger, byUsers, byAction, start, end, includeAutomations).export()
}
}
if (includeSearches) {
val maxSearches = Utils.getOrDefault(config.maximumSearches, 50)
logger.error { "Search export is not yet implemented -- coming soon." }
// TODO: implement exports of searches
val start = Utils.getOrDefault(config.searchesFrom, -1).toLong()
val end = Utils.getOrDefault(config.searchesTo, -1).toLong()
DetailedSearches(xlsx, logger, start, end).export()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,13 @@ class AssetChanges(
private val end: Long,
private val maxAssets: Int,
) {
private val excludeTypes =
listOf(
AuthService.TYPE_NAME,
AuthPolicy.TYPE_NAME,
)
companion object {
val EXCLUDE_TYPES =
listOf(
AuthService.TYPE_NAME,
AuthPolicy.TYPE_NAME,
)
}

fun export() {
logger.info { "Exporting changed assets..." }
Expand All @@ -44,7 +46,7 @@ class AssetChanges(
val client = Atlan.getDefaultClient()
val builder =
AuditSearch.builder(client)
.whereNot(AuditSearchRequest.ENTITY_TYPE.`in`(excludeTypes))
.whereNot(AuditSearchRequest.ENTITY_TYPE.`in`(EXCLUDE_TYPES))
.aggregate("changes", AuditSearchRequest.ENTITY_ID.bucketBy(maxAssets))
if (users.isNotEmpty()) {
builder.where(AuditSearchRequest.USER.`in`(users))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: Apache-2.0
Copyright 2024 Atlan Pte. Ltd. */
package com.atlan.pkg.adoption.exports

import com.atlan.model.search.SearchLogRequest
import com.atlan.pkg.serde.xls.ExcelWriter
import mu.KLogger

class DetailedSearches(
private val xlsx: ExcelWriter,
private val logger: KLogger,
private val start: Long,
private val end: Long,
) {
fun export() {
logger.info { "Exporting details of all UI-based searches between [$start, $end]..." }
val sheet = xlsx.createSheet("User searches")
xlsx.addHeader(
sheet,
mapOf(
"Time" to "Time at which the search occurred",
"Username" to "User who searched",
"Query" to "Text the user entered for the search (if empty, the search was the result of filtering by some facet)",
"Total" to "Total number of assets included",
"Types" to "Type(s) of the first 20 assets that were found",
"Qualified names" to "Unique name(s) of the first 20 assets that were found",
),
)
SearchLogRequest.searches(start, end)
.stream()
.forEach {
xlsx.appendRow(
sheet,
listOf(
it.createdAt ?: it.timestamp ?: "",
it.userName ?: "",
it.searchInput ?: "",
it.resultsCount ?: "0",
it.resultTypeNamesAllowed ?: "",
it.resultQualifiedNamesAllowed ?: "",
),
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/* SPDX-License-Identifier: Apache-2.0
Copyright 2024 Atlan Pte. Ltd. */
package com.atlan.pkg.adoption.exports

import com.atlan.Atlan
import com.atlan.model.enums.AuditActionType
import com.atlan.model.search.AuditSearch
import com.atlan.model.search.AuditSearchRequest
import com.atlan.pkg.Utils
import com.atlan.pkg.adoption.exports.AssetChanges.Companion.EXCLUDE_TYPES
import com.atlan.pkg.serde.xls.ExcelWriter
import mu.KLogger

class DetailedUserChanges(
private val xlsx: ExcelWriter,
private val logger: KLogger,
private val users: List<String>,
private val actions: List<String>,
private val start: Long,
private val end: Long,
private val includeAutomations: String,
) {
fun export() {
logger.info { "Exporting details of all user-made changes between [$start, $end]..." }
val sheet = xlsx.createSheet("User changes")
xlsx.addHeader(
sheet,
mapOf(
"Time" to "Time at which the change occurred",
"Username" to "User who made the change",
"Action" to "Type of change the user made",
"Type" to "Type of asset",
"Qualified name" to "Unique name of the asset",
"Agent" to "Mechanism through which the asset was changed",
"Details" to "Further details about the mechanism through which the asset was changed",
"Link" to "Link to the asset's profile page in Atlan",
),
)
val builder =
AuditSearch.builder(Atlan.getDefaultClient())
.whereNot(AuditSearchRequest.ENTITY_TYPE.`in`(EXCLUDE_TYPES))
if (users.isNotEmpty()) {
builder.where(AuditSearchRequest.USER.`in`(users))
}
if (actions.isNotEmpty()) {
builder.where(AuditSearchRequest.ACTION.`in`(actions))
}
when (includeAutomations) {
"NONE" -> builder.whereNot(AuditSearchRequest.AGENT.`in`(listOf("sdk", "workflow")))
"WFL" -> builder.whereNot(AuditSearchRequest.AGENT.eq("sdk"))
"SDK" -> builder.whereNot(AuditSearchRequest.AGENT.eq("workflow"))
else -> logger.info { " ... including ALL automations -- this could be a large amount of data (and take a LONG time)." }
}
if (start > 0) {
builder.where(AuditSearchRequest.CREATED.gte(start))
}
if (end > 0) {
builder.where(AuditSearchRequest.CREATED.lt(end))
}
builder.stream()
.forEach {
val agent =
when (it.action) {
AuditActionType.PROPAGATED_ATLAN_TAG_ADD,
AuditActionType.PROPAGATED_ATLAN_TAG_UPDATE,
AuditActionType.PROPAGATED_ATLAN_TAG_DELETE,
-> "background"
else -> it.headers?.get("x-atlan-agent") ?: "UI"
}
xlsx.appendRow(
sheet,
listOf(
it.timestamp ?: "",
it.user ?: "",
it.action?.value ?: "",
it.typeName ?: "",
it.entityQualifiedName ?: "",
agent,
it.headers?.get("x-atlan-agent-id") ?: "",
Utils.getAssetLink(it.entityId),
),
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: Apache-2.0
Copyright 2024 Atlan Pte. Ltd. */
package com.atlan.pkg.adoption.exports

import com.atlan.model.search.SearchLogRequest
import com.atlan.pkg.Utils
import com.atlan.pkg.serde.xls.ExcelWriter
import mu.KLogger

class DetailedUserViews(
private val xlsx: ExcelWriter,
private val logger: KLogger,
private val start: Long,
private val end: Long,
) {
fun export() {
logger.info { "Exporting details of all asset views between [$start, $end]..." }
val sheet = xlsx.createSheet("User views")
xlsx.addHeader(
sheet,
mapOf(
"Time" to "Time at which the view / search occurred",
"Username" to "User who viewed / searched",
"Total" to "Total number of assets included",
"Type" to "Type(s) of asset that were viewed",
"Qualified name" to "Unique name(s) of the asset(s)",
"Link" to "Link to the asset's profile page in Atlan",
),
)
SearchLogRequest.views(start, end)
.stream()
.forEach {
val guid = it.resultGuidsAllowed?.get(0) ?: ""
xlsx.appendRow(
sheet,
listOf(
it.createdAt ?: it.timestamp ?: "",
it.userName ?: "",
it.resultsCount ?: "0",
it.resultTypeNamesAllowed ?: "",
it.resultQualifiedNamesAllowed ?: "",
if (guid.isNotBlank()) Utils.getAssetLink(guid) else "",
),
)
}
}
}
Loading

0 comments on commit a60a110

Please sign in to comment.