diff --git a/sdk/src/main/java/com/atlan/api/UsersEndpoint.java b/sdk/src/main/java/com/atlan/api/UsersEndpoint.java index 638e16d9cb..3012a1bf2a 100644 --- a/sdk/src/main/java/com/atlan/api/UsersEndpoint.java +++ b/sdk/src/main/java/com/atlan/api/UsersEndpoint.java @@ -348,6 +348,36 @@ public List getByUsernames(List users, RequestOptions options } } + /** + * Retrieves the user with a unique ID (GUID) that exactly matches the provided string. + * + * @param guid unique identifier by which to retrieve the user + * @return the user whose GUID matches the provided string, or null if there is none + * @throws AtlanException on any error during API invocation + */ + public AtlanUser getByGuid(String guid) throws AtlanException { + return getByGuid(guid, null); + } + + /** + * Retrieves the user with a unique ID (GUID) that exactly matches the provided string. + * + * @param guid unique identifier by which to retrieve the user + * @param options to override default client settings + * @return the user whose GUID matches the provided string, or null if there is none + * @throws AtlanException on any error during API invocation + */ + public AtlanUser getByGuid(String guid, RequestOptions options) throws AtlanException { + UserResponse response = list("{\"id\":\"" + guid + "\"}", options); + if (response != null + && response.getRecords() != null + && !response.getRecords().isEmpty()) { + return response.getRecords().get(0); + } else { + return null; + } + } + /** * Create a new user. * diff --git a/sdk/src/main/java/com/atlan/cache/AbstractMassCache.java b/sdk/src/main/java/com/atlan/cache/AbstractMassCache.java index fc8665e62a..0681309ea0 100644 --- a/sdk/src/main/java/com/atlan/cache/AbstractMassCache.java +++ b/sdk/src/main/java/com/atlan/cache/AbstractMassCache.java @@ -8,6 +8,11 @@ import com.atlan.exception.NotFoundException; import java.util.*; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.atlan.model.core.AtlanObject; +import lombok.Getter; +import lombok.Setter; import lombok.extern.slf4j.Slf4j; /** @@ -15,27 +20,56 @@ * a cache is populated en-masse through batch refreshing. */ @Slf4j -public abstract class AbstractMassCache { +public abstract class AbstractMassCache { + + protected final Map mapIdToName = new ConcurrentHashMap<>(); + protected final Map mapNameToId = new ConcurrentHashMap<>(); + protected final Map mapIdToObject = new ConcurrentHashMap<>(); - private final Map mapIdToName = new ConcurrentHashMap<>(); - private final Map mapNameToId = new ConcurrentHashMap<>(); + /** Whether to refresh the cache by retrieving all objects up-front (true) or lazily, on-demand (false). */ + @Getter + protected AtomicBoolean bulkRefresh = new AtomicBoolean(true); /** * Logic to refresh the cache of objects from Atlan. * * @throws AtlanException on any error communicating with Atlan to refresh the cache of objects */ - public abstract void refreshCache() throws AtlanException; + public synchronized void refreshCache() throws AtlanException { + mapIdToName.clear(); + mapNameToId.clear(); + mapIdToObject.clear(); + } + + /** + * Logic to look up a single object for the cache. + * + * @param id unique internal identifier for the object + * @throws AtlanException on any error communicating with Atlan + */ + public abstract void lookupById(String id) throws AtlanException; + + /** + * Logic to look up a single object for the cache. + * + * @param name unique name for the object + * @throws AtlanException on any error communicating with Atlan + */ + public abstract void lookupByName(String name) throws AtlanException; /** * Add an entry to the cache * * @param id Atlan-internal ID * @param name human-readable name + * @param object the object to cache (if any) */ - protected void cache(String id, String name) { + protected void cache(String id, String name, T object) { mapIdToName.put(id, name); mapNameToId.put(name, id); + if (object != null) { + mapIdToObject.put(id, object); + } } /** @@ -88,7 +122,11 @@ public String getIdForName(String name, boolean allowRefresh) throws AtlanExcept String id = mapNameToId.get(name); if (id == null && allowRefresh) { // If not found, refresh the cache and look again (could be stale) - refreshCache(); + if (bulkRefresh.get()) { + refreshCache(); + } else { + lookupByName(name); + } id = mapNameToId.get(name); } if (id == null) { @@ -128,7 +166,11 @@ public String getNameForId(String id, boolean allowRefresh) throws AtlanExceptio String name = mapIdToName.get(id); if (name == null && allowRefresh) { // If not found, refresh the cache and look again (could be stale) - refreshCache(); + if (bulkRefresh.get()) { + refreshCache(); + } else { + lookupById(id); + } name = mapIdToName.get(id); } if (name == null) { @@ -139,4 +181,80 @@ public String getNameForId(String id, boolean allowRefresh) throws AtlanExceptio throw new InvalidRequestException(ErrorCode.MISSING_ID); } } + + /** + * Retrieve the actual object by Atlan-internal ID string. + * + * @param id Atlan-internal ID string + * @return the object with that ID + * @throws AtlanException on any API communication problem if the cache needs to be refreshed + * @throws NotFoundException if the object cannot be found (does not exist) in Atlan + * @throws InvalidRequestException if no ID was provided for the object to retrieve + */ + public T getById(String id) throws AtlanException { + return getById(id, true); + } + + /** + * Retrieve the actual object by Atlan-internal ID string. + * + * @param id Atlan-internal ID string + * @param allowRefresh whether to allow a refresh of the cache (true) or not (false) + * @return the object with that ID + * @throws AtlanException on any API communication problem if the cache needs to be refreshed + * @throws NotFoundException if the object cannot be found (does not exist) in Atlan + * @throws InvalidRequestException if no ID was provided for the object to retrieve + */ + public T getById(String id, boolean allowRefresh) throws AtlanException { + if (id != null && !id.isEmpty()) { + T result = mapIdToObject.get(id); + if (result == null && allowRefresh) { + // If not found, refresh the cache and look again (could be stale) + if (bulkRefresh.get()) { + refreshCache(); + } else { + lookupById(id); + } + result = mapIdToObject.get(id); + } + if (result == null) { + throw new NotFoundException(ErrorCode.NAME_NOT_FOUND_BY_ID, id); + } + return result; + } else { + throw new InvalidRequestException(ErrorCode.MISSING_ID); + } + } + + /** + * Retrieve the actual object by human-readable name. + * + * @param name human-readable name of the object + * @return the object with that name + * @throws AtlanException on any API communication problem if the cache needs to be refreshed + * @throws NotFoundException if the object cannot be found (does not exist) in Atlan + * @throws InvalidRequestException if no name was provided for the object to retrieve + */ + public T getByName(String name) throws AtlanException { + return getByName(name, true); + } + + /** + * Retrieve the actual object by human-readable name. + * + * @param name human-readable name of the object + * @param allowRefresh whether to allow a refresh of the cache (true) or not (false) + * @return the object with that name + * @throws AtlanException on any API communication problem if the cache needs to be refreshed + * @throws NotFoundException if the object cannot be found (does not exist) in Atlan + * @throws InvalidRequestException if no name was provided for the object to retrieve + */ + public T getByName(String name, boolean allowRefresh) throws AtlanException { + if (name != null && !name.isEmpty()) { + String id = getIdForName(name, allowRefresh); + return getById(id, false); + } else { + throw new InvalidRequestException(ErrorCode.MISSING_NAME); + } + } } diff --git a/sdk/src/main/java/com/atlan/cache/AtlanTagCache.java b/sdk/src/main/java/com/atlan/cache/AtlanTagCache.java index d10964c117..287c1c62a2 100644 --- a/sdk/src/main/java/com/atlan/cache/AtlanTagCache.java +++ b/sdk/src/main/java/com/atlan/cache/AtlanTagCache.java @@ -17,7 +17,7 @@ * Atlan tags. */ @Slf4j -public class AtlanTagCache extends AbstractMassCache { +public class AtlanTagCache extends AbstractMassCache { private Map mapIdToSourceTagsAttrId = new ConcurrentHashMap<>(); private Set deletedIds = ConcurrentHashMap.newKeySet(); @@ -32,6 +32,7 @@ public AtlanTagCache(TypeDefsEndpoint typeDefsEndpoint) { /** {@inheritDoc} */ @Override public synchronized void refreshCache() throws AtlanException { + super.refreshCache(); log.debug("Refreshing cache of Atlan tags..."); TypeDefResponse response = typeDefsEndpoint.list(List.of(AtlanTypeCategory.ATLAN_TAG, AtlanTypeCategory.STRUCT)); @@ -46,7 +47,7 @@ public synchronized void refreshCache() throws AtlanException { deletedNames = ConcurrentHashMap.newKeySet(); for (AtlanTagDef clsDef : tags) { String typeId = clsDef.getName(); - cache(typeId, clsDef.getDisplayName()); + cache(typeId, clsDef.getDisplayName(), clsDef); List attrs = clsDef.getAttributeDefs(); String sourceTagsId = ""; if (attrs != null && !attrs.isEmpty()) { @@ -60,6 +61,18 @@ public synchronized void refreshCache() throws AtlanException { } } + /** {@inheritDoc} */ + @Override + public void lookupByName(String name) { + // Nothing to do here, can only be looked up by internal ID + } + + /** {@inheritDoc} */ + @Override + public void lookupById(String id) { + // Since we can only look up in one direction, we should only allow bulk refresh + } + /** {@inheritDoc} */ @Override public String getIdForName(String name, boolean allowRefresh) throws AtlanException { diff --git a/sdk/src/main/java/com/atlan/cache/CustomMetadataCache.java b/sdk/src/main/java/com/atlan/cache/CustomMetadataCache.java index 431e713eb4..a115b5c5aa 100644 --- a/sdk/src/main/java/com/atlan/cache/CustomMetadataCache.java +++ b/sdk/src/main/java/com/atlan/cache/CustomMetadataCache.java @@ -21,9 +21,8 @@ * custom metadata (including attributes). */ @Slf4j -public class CustomMetadataCache extends AbstractMassCache { +public class CustomMetadataCache extends AbstractMassCache { - private Map cacheById = new ConcurrentHashMap<>(); private Map attrCacheById = new ConcurrentHashMap<>(); private Map> mapAttrIdToName = new ConcurrentHashMap<>(); @@ -40,6 +39,7 @@ public CustomMetadataCache(TypeDefsEndpoint typeDefsEndpoint) { @Override public synchronized void refreshCache() throws AtlanException { log.debug("Refreshing cache of custom metadata..."); + super.refreshCache(); TypeDefResponse response = typeDefsEndpoint.list(List.of(AtlanTypeCategory.CUSTOM_METADATA, AtlanTypeCategory.STRUCT)); if (response == null @@ -48,15 +48,13 @@ public synchronized void refreshCache() throws AtlanException { throw new AuthenticationException(ErrorCode.EXPIRED_API_TOKEN); } List customMetadata = response.getCustomMetadataDefs(); - cacheById = new ConcurrentHashMap<>(); attrCacheById = new ConcurrentHashMap<>(); mapAttrIdToName = new ConcurrentHashMap<>(); mapAttrNameToId = new ConcurrentHashMap<>(); archivedAttrIds = new ConcurrentHashMap<>(); for (CustomMetadataDef bmDef : customMetadata) { String typeId = bmDef.getName(); - cacheById.put(typeId, bmDef); - cache(typeId, bmDef.getDisplayName()); + cache(typeId, bmDef.getDisplayName(), bmDef); mapAttrIdToName.put(typeId, new ConcurrentHashMap<>()); mapAttrNameToId.put(typeId, new ConcurrentHashMap<>()); for (AttributeDef attributeDef : bmDef.getAttributeDefs()) { @@ -80,6 +78,18 @@ public synchronized void refreshCache() throws AtlanException { } } + /** {@inheritDoc} */ + @Override + public void lookupByName(String name) { + // Nothing to do here, can only be looked up by internal ID + } + + /** {@inheritDoc} */ + @Override + public void lookupById(String id) { + // Since we can only look up in one direction, we should only allow bulk refresh + } + /** * Retrieve all the (active) custom metadata attributes. The map will be keyed by custom metadata set * name, and the value will be a listing of all the (active) attributes within that set (with all the details @@ -118,11 +128,11 @@ public Map> getAllCustomAttributes(boolean includeDel */ public Map> getAllCustomAttributes(boolean includeDeleted, boolean forceRefresh) throws AtlanException { - if (cacheById.isEmpty() || forceRefresh) { + if (mapIdToObject.isEmpty() || forceRefresh) { refreshCache(); } Map> map = new HashMap<>(); - for (Map.Entry entry : cacheById.entrySet()) { + for (Map.Entry entry : mapIdToObject.entrySet()) { String typeId = entry.getKey(); String typeName = getNameForId(typeId); CustomMetadataDef typeDef = entry.getValue(); @@ -296,7 +306,7 @@ public CustomMetadataDef getCustomMetadataDef(String setName) throws AtlanExcept */ public CustomMetadataDef getCustomMetadataDef(String setName, boolean allowRefresh) throws AtlanException { String setId = getIdForName(setName, allowRefresh); - return cacheById.get(setId); + return mapIdToObject.get(setId); } /** diff --git a/sdk/src/main/java/com/atlan/cache/GroupCache.java b/sdk/src/main/java/com/atlan/cache/GroupCache.java index 8bccc74508..42ab42c3c9 100644 --- a/sdk/src/main/java/com/atlan/cache/GroupCache.java +++ b/sdk/src/main/java/com/atlan/cache/GroupCache.java @@ -10,13 +10,16 @@ import com.atlan.model.admin.AtlanGroup; import java.util.*; import java.util.concurrent.ConcurrentHashMap; + +import com.atlan.model.admin.AtlanUser; +import com.atlan.model.admin.GroupResponse; import lombok.extern.slf4j.Slf4j; /** * Lazily-loaded cache for translating Atlan-internal groups into their various IDs. */ @Slf4j -public class GroupCache extends AbstractMassCache { +public class GroupCache extends AbstractMassCache { private Map mapAliasToId = new ConcurrentHashMap<>(); @@ -29,15 +32,26 @@ public GroupCache(GroupsEndpoint groupsEndpoint) { /** {@inheritDoc} */ @Override public synchronized void refreshCache() throws AtlanException { + super.refreshCache(); log.debug("Refreshing cache of groups..."); List groups = groupsEndpoint.list(); mapAliasToId = new ConcurrentHashMap<>(); for (AtlanGroup group : groups) { String groupId = group.getId(); String groupName = group.getName(); - String groupAlias = group.getAlias(); - cache(groupId, groupName); - mapAliasToId.put(groupAlias, groupId); + cache(groupId, groupName, group); + } + } + + /** {@inheritDoc} */ + @Override + protected void cache(String id, String name, AtlanGroup object) { + super.cache(id, name, object); + if (object != null) { + String alias = object.getAlias(); + if (alias != null) { + mapAliasToId.put(alias, id); + } } } @@ -69,7 +83,11 @@ public String getIdForAlias(String alias, boolean allowRefresh) throws AtlanExce String groupId = mapAliasToId.get(alias); if (groupId == null && allowRefresh) { // If not found, refresh the cache and look again (could be stale) - refreshCache(); + if (bulkRefresh.get()) { + refreshCache(); + } else { + lookupByAlias(alias); + } groupId = mapAliasToId.get(alias); } if (groupId == null) { @@ -108,4 +126,39 @@ public String getNameForAlias(String alias, boolean allowRefresh) throws AtlanEx String guid = getIdForAlias(alias, allowRefresh); return getNameForId(guid, false); } + + /** {@inheritDoc} */ + @Override + public void lookupByName(String name) throws AtlanException { + GroupResponse response = groupsEndpoint.list("{\"name\":\"" + name + "\"}"); + cacheResponse(response); + } + + /** {@inheritDoc} */ + @Override + public void lookupById(String id) throws AtlanException { + GroupResponse response = groupsEndpoint.list("{\"id\":\"" + id + "\"}"); + cacheResponse(response); + } + + /** + * Logic to look up a single object for the cache. + * + * @param alias name of the group as it appears in the UI + * @throws AtlanException on any error communicating with Atlan + */ + public void lookupByAlias(String alias) throws AtlanException { + GroupResponse response = groupsEndpoint.list("{\"alias\":\"" + alias + "\"}"); + cacheResponse(response); + } + + private void cacheResponse(GroupResponse response) { + if (response != null && response.getRecords() != null && !response.getRecords().isEmpty()) { + List groups = response.getRecords(); + for (AtlanGroup group : groups) { + String groupId = group.getId(); + cache(groupId, group.getName(), group); + } + } + } } diff --git a/sdk/src/main/java/com/atlan/cache/RoleCache.java b/sdk/src/main/java/com/atlan/cache/RoleCache.java index 29065a207c..32a249f354 100644 --- a/sdk/src/main/java/com/atlan/cache/RoleCache.java +++ b/sdk/src/main/java/com/atlan/cache/RoleCache.java @@ -13,7 +13,7 @@ * Lazily-loaded cache for translating Atlan-internal roles into their various IDs. */ @Slf4j -public class RoleCache extends AbstractMassCache { +public class RoleCache extends AbstractMassCache { private final RolesEndpoint rolesEndpoint; @@ -24,10 +24,29 @@ public RoleCache(RolesEndpoint rolesEndpoint) { /** {@inheritDoc} */ @Override public synchronized void refreshCache() throws AtlanException { + super.refreshCache(); log.debug("Refreshing cache of roles..."); // Note: we will only retrieve and cache the workspace-level roles, which all // start with '$' RoleResponse response = rolesEndpoint.list("{\"name\":{\"$ilike\":\"$%\"}}"); + cacheResponse(response); + } + + /** {@inheritDoc} */ + @Override + public void lookupById(String id) throws AtlanException { + RoleResponse response = rolesEndpoint.list("{\"id\":\"" + id + "\"}"); + cacheResponse(response); + } + + /** {@inheritDoc} */ + @Override + public void lookupByName(String name) throws AtlanException { + RoleResponse response = rolesEndpoint.list("{\"name\":\"" + name + "\"}"); + cacheResponse(response); + } + + private void cacheResponse(RoleResponse response) { List roles; if (response != null) { roles = response.getRecords(); @@ -35,9 +54,7 @@ public synchronized void refreshCache() throws AtlanException { roles = Collections.emptyList(); } for (AtlanRole role : roles) { - String roleId = role.getId(); - String roleName = role.getName(); - cache(roleId, roleName); + cache(role.getId(), role.getName(), role); } } } diff --git a/sdk/src/main/java/com/atlan/cache/UserCache.java b/sdk/src/main/java/com/atlan/cache/UserCache.java index 1d706cf2cc..2cde5df93e 100644 --- a/sdk/src/main/java/com/atlan/cache/UserCache.java +++ b/sdk/src/main/java/com/atlan/cache/UserCache.java @@ -17,7 +17,7 @@ * Lazily-loaded cache for translating Atlan-internal users into their various IDs. */ @Slf4j -public class UserCache extends AbstractMassCache { +public class UserCache extends AbstractMassCache { private Map mapEmailToId = new ConcurrentHashMap<>(); @@ -25,13 +25,16 @@ public class UserCache extends AbstractMassCache { private final ApiTokensEndpoint apiTokensEndpoint; public UserCache(UsersEndpoint usersEndpoint, ApiTokensEndpoint apiTokensEndpoint) { + super(); this.usersEndpoint = usersEndpoint; this.apiTokensEndpoint = apiTokensEndpoint; + this.bulkRefresh.set(false); // Default to a lazily-loaded cache for users } /** {@inheritDoc} */ @Override public synchronized void refreshCache() throws AtlanException { + super.refreshCache(); log.debug("Refreshing cache of users..."); List users = usersEndpoint.list(); mapEmailToId = new ConcurrentHashMap<>(); @@ -40,8 +43,19 @@ public synchronized void refreshCache() throws AtlanException { String userName = user.getUsername(); String email = user.getEmail(); if (userId != null && userName != null && email != null) { - cache(userId, userName); - mapEmailToId.put(email, userId); + cache(userId, userName, user); + } + } + } + + /** {@inheritDoc} */ + @Override + protected void cache(String id, String name, AtlanUser object) { + super.cache(id, name, object); + if (object != null) { + String email = object.getEmail(); + if (email != null) { + mapEmailToId.put(email, id); } } } @@ -68,7 +82,7 @@ public String getIdForName(String username, boolean allowRefresh) throws AtlanEx if (token == null) { throw new NotFoundException(ErrorCode.API_TOKEN_NOT_FOUND_BY_NAME, username); } else { - cache(token.getId(), username); + cache(token.getId(), username, null); return token.getId(); } } @@ -105,7 +119,11 @@ public String getIdForEmail(String email, boolean allowRefresh) throws AtlanExce String userId = mapEmailToId.get(email); if (userId == null && allowRefresh) { // If not found, refresh the cache and look again (could be stale) - refreshCache(); + if (bulkRefresh.get()) { + refreshCache(); + } else { + lookupByEmail(email); + } userId = mapEmailToId.get(email); } if (userId == null) { @@ -137,11 +155,57 @@ public String getNameForId(String id, boolean allowRefresh) throws AtlanExceptio ApiToken token = apiTokensEndpoint.getByGuid(id); if (token != null) { String username = token.getApiTokenUsername(); - cache(id, username); + cache(id, username, null); return username; } // Otherwise, attempt to retrieve it and allow the cache to be refreshed when doing so return super.getNameForId(id, allowRefresh); } } + + /** {@inheritDoc} */ + @Override + public void lookupByName(String username) throws AtlanException { + if (username.startsWith(ApiToken.API_USERNAME_PREFIX)) { + ApiToken token = apiTokensEndpoint.getById(username); + if (token == null) { + throw new NotFoundException(ErrorCode.API_TOKEN_NOT_FOUND_BY_NAME, username); + } else { + cache(token.getId(), username, null); + } + } else { + AtlanUser user = usersEndpoint.getByUsername(username); + cache(user.getId(), username, user); + } + } + + /** {@inheritDoc} */ + @Override + public void lookupById(String id) throws AtlanException { + try { + AtlanUser user = usersEndpoint.getByGuid(id); + cache(id, user.getUsername(), user); + } catch (NotFoundException e) { + // Otherwise, check if it is an API token + ApiToken token = apiTokensEndpoint.getByGuid(id); + if (token != null) { + cache(id, token.getApiTokenUsername(), null); + } + } + } + + /** + * Logic to look up a single object for the cache. + * + * @param email unique email address for the user + * @throws AtlanException on any error communicating with Atlan + */ + public void lookupByEmail(String email) throws AtlanException { + List users = usersEndpoint.getByEmail(email); + if (users != null && !users.isEmpty()) { + for (AtlanUser user : users) { + cache(user.getId(), user.getUsername(), user); + } + } + } } diff --git a/sdk/src/main/java/com/atlan/model/admin/AtlanGroup.java b/sdk/src/main/java/com/atlan/model/admin/AtlanGroup.java index 6323d53019..948cf60297 100644 --- a/sdk/src/main/java/com/atlan/model/admin/AtlanGroup.java +++ b/sdk/src/main/java/com/atlan/model/admin/AtlanGroup.java @@ -9,6 +9,8 @@ import com.atlan.exception.InvalidRequestException; import com.atlan.model.core.AtlanObject; import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.util.Comparator; import java.util.List; import java.util.Locale; import java.util.SortedSet; @@ -43,13 +45,14 @@ public class AtlanGroup extends AtlanObject { /** TBC */ String path; - /** Personas the group is associated with. */ - @JsonIgnore // TODO - SortedSet personas; + /** Personas the group is associated with (limited details). */ + SortedSet personas; - /** Purposes the group is associated with. */ - @JsonIgnore // TODO - SortedSet purposes; + /** Purposes the group is associated with (limited details). */ + SortedSet purposes; + + /** TBC */ + SortedSet roles; /** Number of users in the group. */ Long userCount; @@ -291,4 +294,63 @@ public static final class GroupAttributes extends AtlanObject { /** Slack channels for this group. */ List channels; } + + @Getter + @Jacksonized + @SuperBuilder(toBuilder = true) + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static final class Persona extends AtlanObject implements Comparable { + private static final long serialVersionUID = 2L; + + private static final Comparator stringComparator = Comparator.nullsFirst(String::compareTo); + private static final Comparator personaComparator = + Comparator.comparing(AtlanGroup.Persona::getId, stringComparator); + + /** UUID of the persona. */ + String id; + + /** Name of the persona. */ + String name; + + /** Business name of the persona. */ + String displayName; + + /** Unique internal name of the persona. */ + String qualifiedName; + + /** {@inheritDoc} */ + @Override + public int compareTo(AtlanGroup.Persona o) { + return personaComparator.compare(this, o); + } + } + + @Getter + @Jacksonized + @SuperBuilder(toBuilder = true) + @EqualsAndHashCode(callSuper = true) + @ToString(callSuper = true) + public static final class Purpose extends AtlanObject implements Comparable { + private static final long serialVersionUID = 2L; + + private static final Comparator stringComparator = Comparator.nullsFirst(String::compareTo); + private static final Comparator purposeComparator = + Comparator.comparing(AtlanGroup.Purpose::getGuid, stringComparator); + + /** UUID of the purpose. */ + String guid; + + /** Name of the purpose. */ + String name; + + /** Unique internal name of the purpose. */ + String qualifiedName; + + /** {@inheritDoc} */ + @Override + public int compareTo(AtlanGroup.Purpose o) { + return purposeComparator.compare(this, o); + } + } } diff --git a/sdk/src/main/java/com/atlan/model/admin/AtlanUser.java b/sdk/src/main/java/com/atlan/model/admin/AtlanUser.java index 769e9e81a7..971e108c1d 100644 --- a/sdk/src/main/java/com/atlan/model/admin/AtlanUser.java +++ b/sdk/src/main/java/com/atlan/model/admin/AtlanUser.java @@ -403,9 +403,7 @@ public static final class Persona extends AtlanObject implements Comparable