All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.atlan.serde.AssetDeserializer Maven / Gradle / Ivy

// Generated by delombok at Wed Oct 16 22:16:04 UTC 2024
/* SPDX-License-Identifier: Apache-2.0
   Copyright 2022 Atlan Pte. Ltd. */
package com.atlan.serde;

import com.atlan.AtlanClient;
import com.atlan.cache.ReflectionCache;
import com.atlan.exception.AtlanException;
import com.atlan.exception.NotFoundException;
import com.atlan.model.assets.*;
import com.atlan.model.core.AtlanTag;
import com.atlan.model.core.CustomMetadataAttributes;
import com.atlan.util.JacksonUtils;
import com.atlan.util.StringUtils;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import com.fasterxml.jackson.databind.jsontype.TypeDeserializer;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.*;

/**
 * Deserialization of all {@link Asset} objects, down through the entire inheritance hierarchy.
 * This custom deserialization is necessary to flatten some specific aspects of complexity in Atlan's payloads:
 * 
    *
  • The nested attributes and relationshipAttributes structures.
  • *
  • The possibility that the same (relationship) attribute could appear in either of these nested structures.
  • *
  • Handling the extension of properties as you traverse down the inheritance structures, without needing to also extend these nested structures through inheritance.
  • *
  • Automatically translating the nested businessAttributes structure into custom metadata, including translating from Atlan's internal hashed-string representations into human-readable names.
  • *
*/ public class AssetDeserializer extends StdDeserializer { @java.lang.SuppressWarnings("all") @lombok.Generated private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AssetDeserializer.class); private static final long serialVersionUID = 2L; private final AtlanClient client; public AssetDeserializer(AtlanClient client) { this(Asset.class, client); } public AssetDeserializer(Class t, AtlanClient client) { super(t); this.client = client; } /** * {@inheritDoc} */ @Override public Object deserializeWithType(JsonParser parser, DeserializationContext context, TypeDeserializer typeDeserializer) throws IOException { return deserialize(parser, context); } /** * {@inheritDoc} */ @Override public Asset deserialize(JsonParser parser, DeserializationContext context) throws IOException { return deserialize(parser.getCodec().readTree(parser)); } // Suppress deprecation notice on use of atlanTagNames builder /** * Actually do the work of deserializing an asset. * * @param root of the parsed JSON tree * @return the deserialized asset * @throws IOException on any issues parsing the JSON */ @SuppressWarnings("deprecation") Asset deserialize(JsonNode root) throws IOException { JsonNode attributes = root.get("attributes"); JsonNode relationshipGuid = root.get("relationshipGuid"); JsonNode relationshipAttributes = root.get("relationshipAttributes"); JsonNode businessAttributes = root.get("businessAttributes"); JsonNode classificationNames = root.get("classificationNames"); Asset.AssetBuilder builder; Class assetClass; JsonNode typeNameJson = root.get("typeName"); String typeName = null; if (typeNameJson == null || typeNameJson.isNull()) { builder = IndistinctAsset._internal(); assetClass = IndistinctAsset.class; } else { typeName = root.get("typeName").asText(); try { assetClass = Serde.getAssetClassForType(typeName); Method method = assetClass.getMethod("_internal"); Object result = method.invoke(null); builder = (Asset.AssetBuilder) result; } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { log.warn("Unable to dynamically retrieve asset for typeName {}, falling back to an IndistinctAsset.", typeName, e); builder = IndistinctAsset._internal(); assetClass = IndistinctAsset.class; } } // Start by deserializing all the non-attribute properties (defined at Asset-level) // Include reference attributes, for related entities builder.typeName(JacksonUtils.deserializeString(root, "typeName")).guid(JacksonUtils.deserializeString(root, "guid")).displayText(JacksonUtils.deserializeString(root, "displayText")).entityStatus(JacksonUtils.deserializeString(root, "entityStatus")).relationshipType(JacksonUtils.deserializeString(root, "relationshipType")).relationshipGuid(JacksonUtils.deserializeString(root, "relationshipGuid")).relationshipStatus(JacksonUtils.deserializeObject(client, root, "relationshipStatus", new TypeReference<>() { })).uniqueAttributes(JacksonUtils.deserializeObject(client, root, "uniqueAttributes", new TypeReference<>() { })).status(JacksonUtils.deserializeObject(client, root, "status", new TypeReference<>() { })).createdBy(JacksonUtils.deserializeString(root, "createdBy")).updatedBy(JacksonUtils.deserializeString(root, "updatedBy")).createTime(JacksonUtils.deserializeLong(root, "createTime")).updateTime(JacksonUtils.deserializeLong(root, "updateTime")).deleteHandler(JacksonUtils.deserializeString(root, "deleteHandler")).isIncomplete(JacksonUtils.deserializeBoolean(root, "isIncomplete")).depth(JacksonUtils.deserializeLong(root, "depth")); Set atlanTags = JacksonUtils.deserializeObject(client, root, "classifications", new TypeReference<>() { }); if (atlanTags != null) { builder.atlanTags(atlanTags); } TreeSet meaningNames = JacksonUtils.deserializeObject(client, root, "meaningNames", new TypeReference<>() { }); if (meaningNames != null) { builder.meaningNames(meaningNames); } TreeSet meanings = JacksonUtils.deserializeObject(client, root, "meanings", new TypeReference<>() { }); if (meanings != null) { builder.meanings(meanings); } TreeSet pendingTasks = JacksonUtils.deserializeObject(client, root, "pendingTasks", new TypeReference<>() { }); if (pendingTasks != null) { builder.pendingTasks(pendingTasks); } List immediateUpstream = JacksonUtils.deserializeObject(client, root, "immediateUpstream", new TypeReference<>() { }); if (immediateUpstream != null) { builder.immediateUpstream(immediateUpstream); } List immediateDownstream = JacksonUtils.deserializeObject(client, root, "immediateDownstream", new TypeReference<>() { }); if (immediateDownstream != null) { builder.immediateDownstream(immediateDownstream); } TreeMap customAttributes = JacksonUtils.deserializeObject(client, root, "customAttributes", new TypeReference<>() { }); Class builderClass = builder.getClass(); Map leftOverAttributes = new HashMap<>(); // If the same attribute appears in both 'attributes' and 'relationshipAttributes' // then retain the 'relationshipAttributes' (more information) and skip the 'attributes' // copy of the same Set processedAttributes = new HashSet<>(); // Only process relationshipAttributes if this is a full asset, not a relationship // reference. (If it is a relationship reference, the relationshipGuid will be non-null.) if (relationshipGuid == null || relationshipGuid.isNull()) { if (relationshipAttributes != null && !relationshipAttributes.isNull()) { Iterator itr = relationshipAttributes.fieldNames(); while (itr.hasNext()) { String relnKey = itr.next(); String deserializeName = ReflectionCache.getDeserializedName(assetClass, relnKey); Method method = ReflectionCache.getSetter(builderClass, deserializeName); if (method != null) { try { Object value = Serde.deserialize(client, relationshipAttributes.get(relnKey), method, deserializeName); boolean set = ReflectionCache.setValue(builder, deserializeName, value); if (set) { processedAttributes.add(deserializeName); } } catch (NoSuchMethodException e) { throw new IOException("Missing fromValue method for enum.", e); } catch (IllegalAccessException | InvocationTargetException e) { throw new IOException("Failed to deserialize through reflection.", e); } } } } } if (attributes != null && !attributes.isNull()) { Iterator itr = attributes.fieldNames(); while (itr.hasNext()) { String attrKey = itr.next(); String deserializeName = ReflectionCache.getDeserializedName(assetClass, attrKey); // Only proceed with deserializing the 'attributes' copy of an attribute if // it was not already deserialized as a more complete relationship (above) if (!processedAttributes.contains(deserializeName)) { Method method = ReflectionCache.getSetter(builderClass, deserializeName); if (method != null) { try { Object value = Serde.deserialize(client, attributes.get(attrKey), method, deserializeName); ReflectionCache.setValue(builder, deserializeName, value); } catch (NoSuchMethodException e) { throw new IOException("Missing fromValue method for enum.", e); } catch (IllegalAccessException | InvocationTargetException e) { throw new IOException("Failed to deserialize through reflection.", e); } } else { // If the setter was not found, still retain it for later processing // (this is where custom attributes will end up for search results) leftOverAttributes.put(attrKey, attributes.get(attrKey)); } } } } // Custom (metadata) attributes can come from two places, only one of which should ever have data... Map cm = null; // 1. For search results, they're embedded in `attributes` in the form . (custom metadata) // or just __customAttributes (source-specific custom attributes) if (!leftOverAttributes.isEmpty()) { // Translate these into custom metadata structure try { cm = client.getCustomMetadataCache().getCustomMetadataFromSearchResult(leftOverAttributes); } catch (AtlanException e) { throw new IOException(e); } if (leftOverAttributes.containsKey("__customAttributes")) { JsonNode caSearch = leftOverAttributes.get("__customAttributes"); if (caSearch != null && !caSearch.isNull()) { customAttributes = Serde.allInclusiveMapper.readValue(caSearch.asText(), new TypeReference<>() { }); } } } // Note that these are source-provided custom attributes, not custom METADATA attributes (different things) if (customAttributes != null) { builder.customAttributes(customAttributes); } // 2. For asset retrievals, they're all in a `businessAttributes` dict (custom metadata) // or directly under `customAttributes` (source-specific custom attributes, handled above) if (businessAttributes != null) { // Translate these into custom metadata structure try { cm = client.getCustomMetadataCache().getCustomMetadataFromBusinessAttributes(businessAttributes); } catch (AtlanException e) { throw new IOException(e); } } Set clsNames = null; if (classificationNames != null && classificationNames.isArray()) { clsNames = new HashSet<>(); // Translate these IDs in to human-readable names for (JsonNode element : classificationNames) { String tagId = element.asText(); String name = null; try { name = client.getAtlanTagCache().getNameForId(tagId); } catch (NotFoundException e) { log.debug("Unable to find tag with ID {}, deserializing as {}.", tagId, Serde.DELETED_AUDIT_OBJECT, e); } catch (AtlanException e) { throw new IOException(e); } if (name == null) { // Note: the name could be null here either because it was not found in the // first place (NotFoundException), or because it is already tracked as deleted // (in which case it'll come back from getNameForId as null rather than a NFE). name = Serde.DELETED_AUDIT_OBJECT; } clsNames.add(name); } } // Special cases to wrap-up: // Decode the Readme's description after deserialization if (typeName != null && typeName.equals("Readme")) { builder.description(StringUtils.decodeContent(builder.build().getDescription())); } if (cm != null) { builder.customMetadataSets(cm); } if (clsNames != null) { builder.atlanTagNames(clsNames); } Asset result = builder.build(); result.setRawJsonObject(root); return result; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy