diff --git a/song-java-sdk/src/main/java/bio/overture/song/sdk/SongApi.java b/song-java-sdk/src/main/java/bio/overture/song/sdk/SongApi.java index d1fbc17f1..21ee91628 100644 --- a/song-java-sdk/src/main/java/bio/overture/song/sdk/SongApi.java +++ b/song-java-sdk/src/main/java/bio/overture/song/sdk/SongApi.java @@ -16,9 +16,13 @@ */ package bio.overture.song.sdk; +import static bio.overture.song.core.exceptions.ServerErrors.UNAUTHORIZED_TOKEN; +import static bio.overture.song.core.exceptions.SongError.createSongError; import static bio.overture.song.core.utils.Booleans.convertToBoolean; import bio.overture.song.core.exceptions.BooleanConversionException; +import bio.overture.song.core.exceptions.ServerException; +import bio.overture.song.core.exceptions.SongError; import bio.overture.song.core.model.Analysis; import bio.overture.song.core.model.AnalysisType; import bio.overture.song.core.model.ExportedPayload; @@ -35,11 +39,14 @@ import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import lombok.val; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.web.client.ResourceAccessException; @RequiredArgsConstructor public class SongApi { + private static final Logger log = LoggerFactory.getLogger(SongApi.class); @NonNull private final RestClient restClient; @NonNull private final Endpoint endpoint; @@ -51,7 +58,40 @@ public SongApi(RestClient restClient) { public SubmitResponse submit( @NonNull String studyId, @NonNull String json, boolean allowDuplicates) { val url = endpoint.submit(studyId, allowDuplicates); - return restClient.post(url, json, SubmitResponse.class).getBody(); + val response = restClient.post(url, json, SubmitResponse.class); + + if (response == null || response.getBody() == null) { + String rawBody = null; + int statusCode = -1; + + try { + if (response != null && response.hasBody()) { + rawBody = response.getBody().toString(); + } + if (response != null) { + statusCode = response.getStatusCode().value(); + } + } catch (Exception e) { + log.error("Exception occurred ", e); + } + + SongError songError; + if (rawBody != null && rawBody.contains("invalid_token")) { + songError = + createSongError(UNAUTHORIZED_TOKEN, "Authentication failed: invalid or expired token."); + } else { + songError = + createSongError( + UNAUTHORIZED_TOKEN, + String.format( + "Submit failed with status %s. Raw body: %s", + statusCode == -1 ? "UNKNOWN" : statusCode, + rawBody == null ? "No response body" : rawBody)); + } + throw new ServerException(songError); + } + + return response.getBody(); } public List getAnalysisFiles(@NonNull String studyId, @NonNull String analysisId) { diff --git a/song-java-sdk/src/main/java/bio/overture/song/sdk/errors/ServerResponseErrorHandler.java b/song-java-sdk/src/main/java/bio/overture/song/sdk/errors/ServerResponseErrorHandler.java index 7d96dcc7c..6c2ef625b 100644 --- a/song-java-sdk/src/main/java/bio/overture/song/sdk/errors/ServerResponseErrorHandler.java +++ b/song-java-sdk/src/main/java/bio/overture/song/sdk/errors/ServerResponseErrorHandler.java @@ -18,6 +18,7 @@ package bio.overture.song.sdk.errors; import static bio.overture.song.core.exceptions.ServerErrors.UNAUTHORIZED_TOKEN; +import static bio.overture.song.core.exceptions.ServerErrors.UNKNOWN_ERROR; import static bio.overture.song.core.exceptions.SongError.createSongError; import static bio.overture.song.core.exceptions.SongError.parseErrorResponse; import static bio.overture.song.core.utils.Separators.NEWLINE; @@ -43,10 +44,14 @@ public class ServerResponseErrorHandler extends DefaultResponseErrorHandler { @SneakyThrows private static boolean isInvalidToken(String error) { - val response = JsonUtils.readTree(error); - if (response.has(ERROR)) { - val errorValue = response.path(ERROR).textValue(); - return errorValue.equals(INVALID_TOKEN); + try { + val response = JsonUtils.readTree(error); + if (response.has(ERROR)) { + val errorValue = response.path(ERROR).textValue(); + return errorValue.equals(INVALID_TOKEN); + } + } catch (Exception e) { + log.debug("Response body not JSON, cannot parse for token error: {}", error); } return false; } @@ -57,9 +62,22 @@ public void handleError(ClientHttpResponse clientHttpResponse) val httpStatusCode = clientHttpResponse.getStatusCode(); val br = new BufferedReader(new InputStreamReader(clientHttpResponse.getBody())); val body = NEWLINE.join(br.lines().iterator()); + + log.error( + "Server returned error: status={}({}), body={}", + httpStatusCode.value(), + httpStatusCode.getReasonPhrase(), + body); + SongError songError = parseErrorResponse(httpStatusCode, body); if (isNull(songError.getErrorId()) && isInvalidToken(body)) { songError = createSongError(UNAUTHORIZED_TOKEN, "Invalid token"); + } else if (isNull(songError.getMessage()) || songError.getMessage().isEmpty()) { + String fallbackMessage = + String.format( + "Request failed with status %s (%d). Response body: %s", + httpStatusCode.getReasonPhrase(), httpStatusCode.value(), body); + songError = createSongError(UNKNOWN_ERROR, fallbackMessage); } throw new ServerException(songError); } diff --git a/song-server/src/main/java/bio/overture/song/server/exceptions/CustomErrorReportValve.java b/song-server/src/main/java/bio/overture/song/server/exceptions/CustomErrorReportValve.java index 389389c72..40340ff33 100644 --- a/song-server/src/main/java/bio/overture/song/server/exceptions/CustomErrorReportValve.java +++ b/song-server/src/main/java/bio/overture/song/server/exceptions/CustomErrorReportValve.java @@ -24,6 +24,11 @@ public class CustomErrorReportValve extends ErrorReportValve { @Override protected void report(Request request, Response response, Throwable t) { - ServerExceptionHandler.report(request, response, t); + if(t==null){ + ServerExceptionHandler.report(request,response, new RuntimeException("Unauthorized request")); + }else{ + ServerExceptionHandler.report(request, response, t); + } + } }