diff --git a/pom.xml b/pom.xml index 72c04a70..63159dc1 100644 --- a/pom.xml +++ b/pom.xml @@ -77,14 +77,13 @@ 3.1.6 6.11.0-SNAPSHOT + 2.13.0-SNAPSHOT 3.9 1.12 2.8.5 0.4.0 3.3.6 4.5.9 - 2.13.0-SNAPSHOT - 2.12.0 2.13.0 4.11 2.7.1 @@ -125,7 +124,7 @@ io.cdap.plugin hydrator-common - ${hydrator.version} + ${cdap.plugin.version} io.cdap.plugin diff --git a/src/main/java/io/cdap/plugin/http/common/HttpErrorDetailsProvider.java b/src/main/java/io/cdap/plugin/http/common/HttpErrorDetailsProvider.java new file mode 100644 index 00000000..158723ae --- /dev/null +++ b/src/main/java/io/cdap/plugin/http/common/HttpErrorDetailsProvider.java @@ -0,0 +1,75 @@ +/* + * Copyright © 2025 Cask Data, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package io.cdap.plugin.http.common; + +import com.google.common.base.Throwables; +import io.cdap.cdap.api.exception.ErrorCategory; +import io.cdap.cdap.api.exception.ErrorType; +import io.cdap.cdap.api.exception.ErrorUtils; +import io.cdap.cdap.api.exception.ProgramFailureException; +import io.cdap.cdap.etl.api.exception.ErrorContext; +import io.cdap.cdap.etl.api.exception.ErrorDetailsProvider; +import io.cdap.cdap.etl.api.validation.InvalidConfigPropertyException; + +import java.util.List; + +import java.util.NoSuchElementException; + +/** + * Error details provided for the HTTP + **/ +public class HttpErrorDetailsProvider implements ErrorDetailsProvider { + @Override + public ProgramFailureException getExceptionDetails(Exception e, ErrorContext errorContext) { + List causalChain = Throwables.getCausalChain(e); + for (Throwable t : causalChain) { + if (t instanceof ProgramFailureException) { + // if causal chain already has program failure exception, return null to avoid double wrap. + return null; + } + if (t instanceof IllegalArgumentException) { + return getProgramFailureException((IllegalArgumentException) t, errorContext, ErrorType.USER); + } + if (t instanceof IllegalStateException) { + return getProgramFailureException((IllegalStateException) t, errorContext, ErrorType.SYSTEM); + } + if (t instanceof InvalidConfigPropertyException) { + return getProgramFailureException((InvalidConfigPropertyException) t, errorContext, ErrorType.USER); + } + if (t instanceof NoSuchElementException) { + return getProgramFailureException((NoSuchElementException) t, errorContext, ErrorType.SYSTEM); + } + } + return null; + } + + /** + * Get a ProgramFailureException with the given error + * information from {@link Exception}. + * + * @param exception The IllegalArgumentException to get the error information from. + * @return A ProgramFailureException with the given error information. + */ + private ProgramFailureException getProgramFailureException(Exception exception, ErrorContext errorContext, + ErrorType errorType) { + String errorMessage = exception.getMessage(); + String errorMessageFormat = "Error occurred in the phase: '%s'. Error message: %s"; + return ErrorUtils.getProgramFailureException(new ErrorCategory(ErrorCategory.ErrorCategoryEnum.PLUGIN), + errorMessage, + String.format(errorMessageFormat, errorContext.getPhase(), errorMessage), errorType, false, exception); + } +} diff --git a/src/main/java/io/cdap/plugin/http/sink/batch/HTTPSink.java b/src/main/java/io/cdap/plugin/http/sink/batch/HTTPSink.java index f33036f5..66fda5a0 100644 --- a/src/main/java/io/cdap/plugin/http/sink/batch/HTTPSink.java +++ b/src/main/java/io/cdap/plugin/http/sink/batch/HTTPSink.java @@ -30,9 +30,11 @@ import io.cdap.cdap.etl.api.StageConfigurer; import io.cdap.cdap.etl.api.batch.BatchSink; import io.cdap.cdap.etl.api.batch.BatchSinkContext; +import io.cdap.cdap.etl.api.exception.ErrorDetailsProviderSpec; import io.cdap.plugin.common.Asset; import io.cdap.plugin.common.LineageRecorder; +import io.cdap.plugin.http.common.HttpErrorDetailsProvider; import java.util.Collections; import java.util.List; import java.util.Map; @@ -77,7 +79,7 @@ public void prepareRun(BatchSinkContext context) { Collections.emptyList() : inputSchema.getFields().stream().map(Schema.Field::getName).collect(Collectors.toList()); lineageRecorder.recordWrite("Write", String.format("Wrote to HTTP '%s'", config.getUrl()), fields); - + context.setErrorDetailsProvider(new ErrorDetailsProviderSpec(HttpErrorDetailsProvider.class.getName())); context.addOutput(Output.of(config.getReferenceNameOrNormalizedFQN(), new HTTPSink.HTTPOutputFormatProvider(config, inputSchema))); }