Skip to content

Commit a19c7a1

Browse files
Implement error details provider to get more information about exceptons from GCP plugins
1 parent a1a4049 commit a19c7a1

File tree

2 files changed

+85
-0
lines changed

2 files changed

+85
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright © 2024 Cask Data, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
5+
* use this file except in compliance with the License. You may obtain a copy of
6+
* the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations under
14+
* the License.
15+
*/
16+
17+
package io.cdap.plugin.gcp.common;
18+
19+
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
20+
import com.google.api.client.http.HttpResponseException;
21+
import com.google.common.base.Throwables;
22+
import io.cdap.cdap.api.exception.ErrorCategory;
23+
import io.cdap.cdap.api.exception.ErrorCategory.ErrorCategoryEnum;
24+
import io.cdap.cdap.api.exception.ErrorDetailsProvider;
25+
import io.cdap.cdap.api.exception.ErrorUtils;
26+
import io.cdap.cdap.api.exception.ProgramFailureException;
27+
28+
import java.util.List;
29+
30+
/**
31+
* A custom ErrorDetailsProvider for GCP plugins.
32+
*/
33+
public class GCPErrorDetailsProvider implements ErrorDetailsProvider {
34+
35+
/**
36+
* Get a ProgramFailureException with the given error
37+
* information from generic exceptions like {@link java.io.IOException}.
38+
*
39+
* @param e The Throwable to get the error information from.
40+
* @return A ProgramFailureException with the given error information, otherwise null.
41+
*/
42+
@Override
43+
public ProgramFailureException getExceptionDetails(Exception e) {
44+
List<Throwable> causalChain = Throwables.getCausalChain(e);
45+
for (Throwable t : causalChain) {
46+
if (t instanceof ProgramFailureException) {
47+
// if causal chain already has program failure exception, return null to avoid double wrap.
48+
return null;
49+
}
50+
if (t instanceof HttpResponseException) {
51+
return getProgramFailureException((HttpResponseException) t);
52+
}
53+
}
54+
return null;
55+
}
56+
57+
/**
58+
* Get a ProgramFailureException with the given error
59+
* information from {@link HttpResponseException}.
60+
*
61+
* @param e The HttpResponseException to get the error information from.
62+
* @return A ProgramFailureException with the given error information.
63+
*/
64+
private ProgramFailureException getProgramFailureException(HttpResponseException e) {
65+
Integer statusCode = e.getStatusCode();
66+
ErrorUtils.ActionErrorPair pair = ErrorUtils.getActionErrorByStatusCode(statusCode);
67+
String errorReason = String.format("%s %s %s", e.getStatusCode(), e.getStatusMessage(),
68+
pair.getCorrectiveAction());
69+
70+
String errorMessage = e.getMessage();
71+
if (e instanceof GoogleJsonResponseException) {
72+
GoogleJsonResponseException exception = (GoogleJsonResponseException) e;
73+
errorMessage = exception.getDetails() != null ? exception.getDetails().getMessage() :
74+
exception.getMessage();
75+
}
76+
77+
return ErrorUtils.getProgramFailureException(new ErrorCategory(ErrorCategoryEnum.PLUGIN),
78+
errorReason, errorMessage, pair.getErrorType(), true, e);
79+
}
80+
}

src/main/java/io/cdap/plugin/gcp/gcs/source/GCSSource.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import io.cdap.cdap.api.annotation.MetadataProperty;
2929
import io.cdap.cdap.api.annotation.Name;
3030
import io.cdap.cdap.api.annotation.Plugin;
31+
import io.cdap.cdap.api.exception.ErrorUtils;
3132
import io.cdap.cdap.etl.api.FailureCollector;
3233
import io.cdap.cdap.etl.api.PipelineConfigurer;
3334
import io.cdap.cdap.etl.api.batch.BatchSource;
@@ -42,6 +43,7 @@
4243
import io.cdap.plugin.format.plugin.AbstractFileSourceConfig;
4344
import io.cdap.plugin.format.plugin.FileSourceProperties;
4445
import io.cdap.plugin.gcp.common.GCPConnectorConfig;
46+
import io.cdap.plugin.gcp.common.GCPErrorDetailsProvider;
4547
import io.cdap.plugin.gcp.common.GCPUtils;
4648
import io.cdap.plugin.gcp.common.GCSEmptyInputFormat;
4749
import io.cdap.plugin.gcp.crypto.EncryptedFileSystem;
@@ -108,6 +110,9 @@ public void prepareRun(BatchSourceContext context) throws Exception {
108110
asset = Asset.builder(referenceName)
109111
.setFqn(fqn).setLocation(location).build();
110112

113+
// set error details provider
114+
context.setErrorDetailsProvider(GCPErrorDetailsProvider.class.getName());
115+
111116
// super is called down here to avoid instantiating the lineage recorder with a null asset
112117
super.prepareRun(context);
113118
}

0 commit comments

Comments
 (0)