diff --git a/samples/packages/asset-import/src/main/kotlin/com/atlan/pkg/aim/AssetImporter.kt b/samples/packages/asset-import/src/main/kotlin/com/atlan/pkg/aim/AssetImporter.kt index 1aff6b9f8e..f0967c017b 100644 --- a/samples/packages/asset-import/src/main/kotlin/com/atlan/pkg/aim/AssetImporter.kt +++ b/samples/packages/asset-import/src/main/kotlin/com/atlan/pkg/aim/AssetImporter.kt @@ -236,15 +236,17 @@ class AssetImporter( ) { private var header = emptyList<String>() private var typeToProcess = "" - private val cyclicalRelationships = mutableMapOf<String, Set<String>>() - private val mapToSecondPass = mutableMapOf<String, Set<String>>() + private val cyclicalRelationships = mutableMapOf<String, MutableSet<RelationshipEnds>>() + private val mapToSecondPass = mutableMapOf<String, MutableSet<String>>() private val secondPassRemain = setOf( Asset.QUALIFIED_NAME.atlanFieldName, Asset.NAME.atlanFieldName, Folder.PARENT_QUALIFIED_NAME.atlanFieldName, Folder.COLLECTION_QUALIFIED_NAME.atlanFieldName, - ) // TODO: other required fields, across ALL (non-GTC, non-mesh) types + ) + + private data class RelationshipEnds(val name: String, val end1: String, val end2: String) /** {@inheritDoc} */ override fun preprocess( @@ -255,8 +257,8 @@ class AssetImporter( // (meaning relationships where both ends are of the same type) val typeDefs = ctx.client.typeDefs.list(AtlanTypeCategory.RELATIONSHIP) typeDefs.relationshipDefs.stream() - .filter { it.endDef1.type == it.endDef2.type && it.endDef1.cardinality == it.endDef2.cardinality } - .forEach { cyclicalRelationships[it.endDef1.type] = setOf(it.endDef1.name, it.endDef2.name) } + .filter { it.endDef1.type == it.endDef2.type } + .forEach { cyclicalRelationships.getOrPut(it.endDef1.type) { mutableSetOf() }.add(RelationshipEnds(it.name, it.endDef1.name, it.endDef2.name)) } val results = super.preprocess(outputFile, outputHeaders) return results } @@ -270,26 +272,26 @@ class AssetImporter( ): List<String> { // Check if the type on this row has any cyclical relationships as headers in the input file val typeName = CSVXformer.trimWhitespace(row.getOrElse(typeIdx) { "" }) - if (!mapToSecondPass.containsKey(typeName)) { - if (this.header.isEmpty()) this.header = header - val cyclical = cyclicalRelationships.getOrElse(typeName) { emptySet() }.toList() - if (cyclical.size == 2) { - val one = cyclical[0] - val two = cyclical[1] - if (header.contains(one) && header.contains(two)) { - // If both ends of the same relationship are in the input file, throw an error - // alerting the user that this can't work and they'll need to pick one end or the other - throw IllegalStateException( - """ - Both ends of the same relationship found in the input file for type $typeName: $one <> $two. - You should only use one end of this relationship or the other when importing. - """.trimIndent(), - ) - } + if (this.header.isEmpty()) this.header = header + cyclicalRelationships.getOrElse(typeName) { emptySet() }.toList().forEach { relationship -> + val one = relationship.end1 + val two = relationship.end2 + if (header.contains(one) && header.contains(two)) { + // If both ends of the same relationship are in the input file, throw an error + // alerting the user that this can't work, and they'll need to pick one end or the other + throw IllegalStateException( + """ + Both ends of the same relationship found in the input file for type $typeName: $one <> $two. + You should only use one end of this relationship or the other when importing. + """.trimIndent(), + ) } // Retain any of the cyclical relationships that remain so that we can second-pass process them - val secondPassColumns = cyclical.filter { header.contains(it) }.toSet() - mapToSecondPass[typeName] = secondPassColumns + if (header.contains(one)) { + mapToSecondPass.getOrPut(typeName) { mutableSetOf() }.add(one) + } else if (header.contains(two)) { + mapToSecondPass.getOrPut(typeName) { mutableSetOf() }.add(two) + } } return row } diff --git a/sdk/src/main/java/com/atlan/net/LiveAtlanResponseGetter.java b/sdk/src/main/java/com/atlan/net/LiveAtlanResponseGetter.java index 041d6cd6a4..251d460560 100644 --- a/sdk/src/main/java/com/atlan/net/LiveAtlanResponseGetter.java +++ b/sdk/src/main/java/com/atlan/net/LiveAtlanResponseGetter.java @@ -263,8 +263,7 @@ private static HttpClient buildDefaultHttpClient() { */ private static void raiseMalformedJsonError(String responseBody, int responseCode, Throwable e) throws ApiException { - String details = e == null ? "none" : e.getMessage(); - throw new ApiException(ErrorCode.JSON_ERROR, e, responseBody, "" + responseCode, details); + throw new ApiException(ErrorCode.ERROR_PASSTHROUGH, e, "" + responseCode, responseBody, ""); } /** @@ -274,16 +273,9 @@ private static void raiseMalformedJsonError(String responseBody, int responseCod * @throws AtlanException a more specific exception, based on the details of that response */ private static void handleApiError(AtlanResponse response) throws AtlanException { + // Attempt to parse the error into an AtlanError object, but if that fails, fallback + // to just using the raw message body (wasn't JSON to begin with) AtlanError error = null; - - // Check for a 500 response first -- if found, we won't have a JSON body to parse, - // so preemptively exit with a generic ApiException pass-through. - int rc = response.code(); - if (rc == 500) { - throw new ApiException( - ErrorCode.ERROR_PASSTHROUGH, null, "" + rc, response.body() == null ? "" : response.body()); - } - try { error = Serde.allInclusiveMapper.readValue(response.body(), AtlanError.class); } catch (IOException e) { @@ -307,8 +299,8 @@ private static void handleApiError(int code, String body) throws AtlanException // Check for a 500 response first -- if found, we won't have a JSON body to parse, // so preemptively exit with a generic ApiException pass-through. - if (code == 500) { - throw new ApiException(ErrorCode.ERROR_PASSTHROUGH, null, "" + code, body == null ? "" : body); + if (code >= 500) { + raiseMalformedJsonError(body, code, null); } AtlanError error = new AtlanError(); @@ -326,17 +318,8 @@ private static void handleApiError(int code, String body) throws AtlanException private static void handleApiError(AtlanEventStreamResponse response) throws AtlanException { AtlanError error = null; - // Check for a 500 response first -- if found, we won't have a JSON body to parse, - // so preemptively exit with a generic ApiException pass-through. - int rc = response.code(); - if (rc == 500) { - throw new ApiException( - ErrorCode.ERROR_PASSTHROUGH, - null, - "" + rc, - response.body() == null ? "" : response.body().toString()); - } - + // Attempt to parse the error into an AtlanError object, but if that fails, fallback + // to just using the raw message body (wasn't JSON to begin with) try { error = Serde.allInclusiveMapper.readValue(response.body().get(0), AtlanError.class); } catch (IOException e) {