This repository has been archived by the owner on Dec 7, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #59 from atlanhq/DX-266
Handle assigned terms serde in human-readable way
- Loading branch information
Showing
10 changed files
with
282 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
/* SPDX-License-Identifier: Apache-2.0 */ | ||
/* Copyright 2023 Atlan Pte. Ltd. */ | ||
package cache | ||
|
||
import com.atlan.model.assets.Asset | ||
import java.util.concurrent.ConcurrentHashMap | ||
|
||
/** | ||
* Utility class for lazy-loading a cache of assets based on some human-constructable identity. | ||
*/ | ||
abstract class AssetCache { | ||
private val byIdentity: MutableMap<String, String?> = ConcurrentHashMap() | ||
private val byGuid: MutableMap<String, Asset?> = ConcurrentHashMap() | ||
|
||
/** | ||
* Retrieve an asset from the cache by its human-readable identity, lazily-loading it on any cache misses. | ||
* | ||
* @param identity of the asset to retrieve | ||
* @return the asset with the specified identity | ||
*/ | ||
fun getByIdentity(identity: String): Asset? { | ||
if (!this.containsIdentity(identity)) { | ||
val asset = lookupAssetByIdentity(identity)!! | ||
byIdentity[identity] = asset.guid | ||
byGuid[asset.guid] = asset | ||
} | ||
return byGuid[byIdentity[identity]] | ||
} | ||
|
||
/** | ||
* Retrieve an asset from the cache by its globally-unique identifier, lazily-loading it on any cache misses. | ||
* | ||
* @param guid unique identifier (GUID) of the asset to retrieve | ||
* @return the asset with the specified GUID | ||
*/ | ||
fun getByGuid(guid: String): Asset? { | ||
if (!this.containsGuid(guid)) { | ||
val asset = lookupAssetByGuid(guid)!! | ||
byIdentity[getIdentityForAsset(asset)] = guid | ||
byGuid[guid] = asset | ||
} | ||
return byGuid[guid] | ||
} | ||
|
||
/** | ||
* Indicates whether the cache already contains an asset with a given identity. | ||
* | ||
* @param identity of the asset to check for presence in the cache | ||
* @return true if this identity is already in the cache, false otherwise | ||
*/ | ||
fun containsIdentity(identity: String): Boolean { | ||
return byIdentity.containsKey(identity) | ||
} | ||
|
||
/** | ||
* Indicates whether the cache already contains an asset with a given GUID. | ||
* | ||
* @param guid unique identifier (GUID) of the asset to check for presence in the cache | ||
* @return true if this GUID is already in the cache, false otherwise | ||
*/ | ||
fun containsGuid(guid: String): Boolean { | ||
return byGuid.containsKey(guid) | ||
} | ||
|
||
/** | ||
* Actually go to Atlan and find the asset with the provided identity. | ||
* Note: this should also populate the byGuid cache | ||
* | ||
* @param identity of the asset to lookup | ||
* @return the asset, from Atlan | ||
*/ | ||
protected abstract fun lookupAssetByIdentity(identity: String?): Asset? | ||
|
||
/** | ||
* Actually go to Atlan and find the asset with the provided GUID. | ||
* Note: this should also populate the byIdentity cache | ||
* | ||
* @param guid unique identifier (GUID) of the asset to lookup | ||
* @return the asset, from Atlan | ||
*/ | ||
protected abstract fun lookupAssetByGuid(guid: String?): Asset? | ||
|
||
/** | ||
* Create a unique, reconstructable identity for the provided asset. | ||
* | ||
* @param asset for which to construct the identity | ||
* @return the identity of the asset | ||
*/ | ||
protected abstract fun getIdentityForAsset(asset: Asset): String | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
/* SPDX-License-Identifier: Apache-2.0 */ | ||
/* Copyright 2023 Atlan Pte. Ltd. */ | ||
package cache | ||
|
||
import com.atlan.exception.AtlanException | ||
import com.atlan.model.assets.Asset | ||
import com.atlan.model.assets.Glossary | ||
import mu.KotlinLogging | ||
|
||
object GlossaryCache : AssetCache() { | ||
|
||
private val logger = KotlinLogging.logger {} | ||
|
||
/** {@inheritDoc} */ | ||
override fun lookupAssetByIdentity(identity: String?): Asset? { | ||
try { | ||
return Glossary.findByName(identity) | ||
} catch (e: AtlanException) { | ||
logger.error("Unable to lookup or find glossary: {}", identity, e) | ||
} | ||
return null | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
override fun lookupAssetByGuid(guid: String?): Asset? { | ||
try { | ||
val glossary = Glossary.select() | ||
.where(Glossary.GUID.eq(guid)) | ||
.includeOnResults(Glossary.NAME) | ||
.pageSize(2) | ||
.stream() | ||
.findFirst() | ||
if (glossary.isPresent) { | ||
return glossary.get() | ||
} | ||
} catch (e: AtlanException) { | ||
logger.error("Unable to lookup or find glossary: {}", guid, e) | ||
} | ||
return null | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
override fun getIdentityForAsset(asset: Asset): String { | ||
return asset.name | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
/* SPDX-License-Identifier: Apache-2.0 */ | ||
/* Copyright 2023 Atlan Pte. Ltd. */ | ||
package cache | ||
|
||
import com.atlan.exception.AtlanException | ||
import com.atlan.model.assets.Asset | ||
import com.atlan.model.assets.Glossary | ||
import com.atlan.model.assets.GlossaryTerm | ||
import com.atlan.model.fields.AtlanField | ||
import mu.KotlinLogging | ||
import xformers.cell.AssignedTermXformer | ||
|
||
object TermCache : AssetCache() { | ||
|
||
private val logger = KotlinLogging.logger {} | ||
|
||
private val includesOnResults: List<AtlanField> = listOf(GlossaryTerm.NAME, GlossaryTerm.ANCHOR) | ||
private val includesOnRelations: List<AtlanField> = listOf(Glossary.NAME) | ||
|
||
/** {@inheritDoc} */ | ||
override fun lookupAssetByIdentity(identity: String?): Asset? { | ||
val tokens = identity?.split(AssignedTermXformer.TERM_GLOSSARY_DELIMITER) | ||
if (tokens?.size == 2) { | ||
val termName = tokens[0] | ||
val glossaryName = tokens[1] | ||
val glossary = GlossaryCache.getByIdentity(glossaryName) | ||
if (glossary != null) { | ||
try { | ||
val term = GlossaryTerm.select() | ||
.where(GlossaryTerm.NAME.eq(termName)) | ||
.where(GlossaryTerm.ANCHOR.eq(glossary.qualifiedName)) | ||
.includesOnResults(includesOnResults) | ||
.includesOnRelations(includesOnRelations) | ||
.pageSize(2) | ||
.stream() | ||
.findFirst() | ||
if (term.isPresent) { | ||
return term.get() | ||
} | ||
} catch (e: AtlanException) { | ||
logger.error("Unable to lookup or find term: {}", identity, e) | ||
} | ||
} else { | ||
logger.error("Unable to find glossary {} for term reference: {}", glossaryName, identity) | ||
} | ||
} else { | ||
logger.error("Unable to lookup or find term, unexpected reference: {}", identity) | ||
} | ||
return null | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
override fun lookupAssetByGuid(guid: String?): Asset? { | ||
try { | ||
val term = GlossaryTerm.select() | ||
.where(GlossaryTerm.GUID.eq(guid)) | ||
.includesOnResults(includesOnResults) | ||
.includesOnRelations(includesOnRelations) | ||
.pageSize(2) | ||
.stream() | ||
.findFirst() | ||
if (term.isPresent) { | ||
return term.get() | ||
} | ||
} catch (e: AtlanException) { | ||
logger.error("Unable to lookup or find term: {}", guid, e) | ||
} | ||
return null | ||
} | ||
|
||
/** {@inheritDoc} */ | ||
override fun getIdentityForAsset(asset: Asset): String { | ||
return when (asset) { | ||
is GlossaryTerm -> { | ||
"${asset.name}${AssignedTermXformer.TERM_GLOSSARY_DELIMITER}${asset.anchor.name}" | ||
} | ||
else -> "" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
serde/src/main/kotlin/xformers/cell/AssignedTermXformer.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
/* SPDX-License-Identifier: Apache-2.0 */ | ||
/* Copyright 2023 Atlan Pte. Ltd. */ | ||
package xformers.cell | ||
|
||
import cache.TermCache | ||
import com.atlan.model.assets.Asset | ||
import com.atlan.model.assets.GlossaryTerm | ||
import mu.KotlinLogging | ||
|
||
/** | ||
* Static object to transform term assignment references. | ||
*/ | ||
object AssignedTermXformer { | ||
|
||
private val logger = KotlinLogging.logger {} | ||
|
||
const val TERM_GLOSSARY_DELIMITER = "@@@" | ||
|
||
/** | ||
* Encodes (serializes) a term assignment into a string form. | ||
* | ||
* @param asset to be encoded | ||
* @return the string-encoded form for that asset | ||
*/ | ||
fun encode(asset: Asset): String { | ||
// Handle some assets as direct embeds | ||
return when (asset) { | ||
is GlossaryTerm -> { | ||
val term = TermCache.getByGuid(asset.guid) | ||
if (term is GlossaryTerm) { | ||
"${term.name}$TERM_GLOSSARY_DELIMITER${term.anchor.name}" | ||
} else { | ||
logger.error("Unable to find any term with GUID: {}", asset.guid) | ||
"" | ||
} | ||
} | ||
else -> AssetRefXformer.encode(asset) | ||
} | ||
} | ||
|
||
/** | ||
* Decodes (deserializes) a string form into a term assignment object. | ||
* | ||
* @param assetRef the string form to be decoded | ||
* @param fieldName the name of the field containing the string-encoded value | ||
* @return the term assignment represented by the string | ||
*/ | ||
fun decode(assetRef: String, fieldName: String): Asset { | ||
return when (fieldName) { | ||
"meanings" -> { | ||
TermCache.getByIdentity(assetRef)?.trimToReference()!! | ||
} | ||
else -> AssetRefXformer.decode(assetRef, fieldName) | ||
} | ||
} | ||
} |