diff --git a/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/AddVersionBuilder.java b/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/AddVersionBuilder.java index dc6a9dca..5b8218ae 100644 --- a/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/AddVersionBuilder.java +++ b/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/AddVersionBuilder.java @@ -290,6 +290,7 @@ public void perform(@Nonnull Run run, @Nonnull FilePath workspace, @Nonnul applicationService = injector.getInstance(ApplicationService.class); } + apiService.setVerbose(getDescriptor().configuration.isVerbose()); String apiServerUrl = getAipConsoleUrl(); String apiKey = Secret.toString(getApiKey()); String username = getDescriptor().getAipConsoleUsername(); diff --git a/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/AnalyzeBuilder.java b/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/AnalyzeBuilder.java index 87281d4b..3b4b583c 100644 --- a/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/AnalyzeBuilder.java +++ b/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/AnalyzeBuilder.java @@ -174,6 +174,8 @@ public void perform(@Nonnull Run run, @Nonnull FilePath workspace, @Nonnul jobsService = injector.getInstance(JobsService.class); applicationService = injector.getInstance(ApplicationService.class); } + + apiService.setVerbose(getDescriptor().configuration.isVerbose()); String apiServerUrl = getAipConsoleUrl(); String apiKey = Secret.toString(getApiKey()); diff --git a/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/CreateApplicationBuilder.java b/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/CreateApplicationBuilder.java index ba0cfdc4..12625395 100644 --- a/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/CreateApplicationBuilder.java +++ b/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/CreateApplicationBuilder.java @@ -144,6 +144,9 @@ public void perform(@Nonnull Run run, @Nonnull FilePath workspace, @Nonnul Guice.createInjector(new AipConsoleModule()).injectMembers(this); } + if (apiService != null) { + apiService.setVerbose(getDescriptor().configuration.isVerbose()); + } String apiServerUrl = getAipConsoleUrl(); String apiKey = Secret.toString(getApiKey()); String username = getDescriptor().getAipConsoleUsername(); diff --git a/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/DeliverBuilder.java b/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/DeliverBuilder.java index 627d5874..5c9fbb38 100644 --- a/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/DeliverBuilder.java +++ b/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/DeliverBuilder.java @@ -313,6 +313,8 @@ public void perform(@Nonnull Run run, @Nonnull FilePath workspace, @Nonnul jobsService = injector.getInstance(JobsService.class); applicationService = injector.getInstance(ApplicationService.class); } + + apiService.setVerbose(getDescriptor().configuration.isVerbose()); String apiServerUrl = getAipConsoleUrl(); String apiKey = Secret.toString(getApiKey()); diff --git a/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/SnapshotBuilder.java b/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/SnapshotBuilder.java index b0a6b10e..9a0e077f 100644 --- a/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/SnapshotBuilder.java +++ b/aip-console-jenkins/src/main/java/io/jenkins/plugins/aipconsole/SnapshotBuilder.java @@ -166,6 +166,7 @@ public void perform(@Nonnull Run run, @Nonnull FilePath workspace, @Nonnul applicationService = injector.getInstance(ApplicationService.class); } + apiService.setVerbose(getDescriptor().configuration.isVerbose()); String apiServerUrl = getAipConsoleUrl(); String apiKey = Secret.toString(getApiKey()); String username = getDescriptor().getAipConsoleUsername(); diff --git a/aip-console-jenkins/src/main/resources/io/jenkins/plugins/aipconsole/DeliverBuilder/config_fr.properties b/aip-console-jenkins/src/main/resources/io/jenkins/plugins/aipconsole/DeliverBuilder/config_fr.properties index ff6aaea2..ad24f1a1 100644 --- a/aip-console-jenkins/src/main/resources/io/jenkins/plugins/aipconsole/DeliverBuilder/config_fr.properties +++ b/aip-console-jenkins/src/main/resources/io/jenkins/plugins/aipconsole/DeliverBuilder/config_fr.properties @@ -1,7 +1,7 @@ setAsCurrent=Utiliser cette version pour l'analyse -setAsCurrent.descr=La version ajoutée sera définie comme la version d'analyse +setAsCurrent.descr=La version ajoutée sera définie comme la version d'analyse aipConsoleUrl=URL d''AIP Console -aipConsoleUrl.descr=L''URL d''accès à AIP Console -apiKey=Clé d'API -apiKey.descr=La clé d'API permettant l''accès à AIP Console +aipConsoleUrl.descr=L''URL d''accès à AIP Console +apiKey=Clé d'API +apiKey.descr=La clé d'API permettant l''accès à AIP Console diff --git a/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/AddVersionCommand.java b/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/AddVersionCommand.java index 2867c89f..5fe526ca 100644 --- a/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/AddVersionCommand.java +++ b/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/AddVersionCommand.java @@ -30,7 +30,6 @@ import java.nio.file.Files; import java.util.Date; import java.util.List; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -44,17 +43,13 @@ @Slf4j @Getter @Setter -public class AddVersionCommand implements Callable { - private final RestApiService restApiService; +public class AddVersionCommand extends BaseCollableCommand { private final JobsService jobsService; private final UploadService uploadService; private final ApplicationService applicationService; - @CommandLine.Mixin - private SharedOptions sharedOptions; - public AddVersionCommand(RestApiService restApiService, JobsService jobsService, UploadService uploadService, ApplicationService applicationService) { - this.restApiService = restApiService; + super(restApiService); this.jobsService = jobsService; this.uploadService = uploadService; this.applicationService = applicationService; @@ -150,7 +145,7 @@ public AddVersionCommand(RestApiService restApiService, JobsService jobsService, private List unmatchedOptions; @Override - public Integer call() { + public Integer doCall() { ApiInfoDto apiInfo = null; try { if (sharedOptions.getTimeout() != Constants.DEFAULT_HTTP_TIMEOUT) { @@ -164,7 +159,6 @@ public Integer call() { return Constants.RETURN_LOGIN_ERROR; } - log.info("AddVersion version command has triggered with log output = '{}'", sharedOptions.isVerbose()); log.info("[Debug options] Show Sql is '{}'", showSql); log.info("[Debug options] AMT Profiling is '{}'", amtProfiling); diff --git a/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/AnalyzeCommand.java b/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/AnalyzeCommand.java index cb283cf0..94c1c892 100644 --- a/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/AnalyzeCommand.java +++ b/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/AnalyzeCommand.java @@ -29,7 +29,6 @@ import java.util.Comparator; import java.util.Date; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -46,13 +45,10 @@ @Slf4j @Getter @Setter -public class AnalyzeCommand implements Callable { +public class AnalyzeCommand extends BaseCollableCommand { private static final DateFormat RELEASE_DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - private final RestApiService restApiService; private final JobsService jobsService; private final ApplicationService applicationService; - @CommandLine.Mixin - private SharedOptions sharedOptions; @CommandLine.Option(names = {"-n", "--app-name"}, paramLabel = "APPLICATION_NAME", @@ -88,13 +84,13 @@ public class AnalyzeCommand implements Callable { private boolean amtProfiling; public AnalyzeCommand(RestApiService restApiService, JobsService jobsService, ApplicationService applicationService) { - this.restApiService = restApiService; + super(restApiService); this.jobsService = jobsService; this.applicationService = applicationService; } @Override - public Integer call() throws Exception { + public Integer doCall() throws Exception { if (StringUtils.isBlank(applicationName)) { log.error("No application name provided. Exiting."); return Constants.RETURN_APPLICATION_INFO_MISSING; diff --git a/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/BaseCollableCommand.java b/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/BaseCollableCommand.java new file mode 100644 index 00000000..93b97064 --- /dev/null +++ b/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/BaseCollableCommand.java @@ -0,0 +1,32 @@ +package com.castsoftware.aip.console.tools.commands; + +import com.castsoftware.aip.console.tools.core.services.RestApiService; +import lombok.extern.slf4j.Slf4j; +import picocli.CommandLine; + +import java.util.concurrent.Callable; + +@Slf4j +public abstract class BaseCollableCommand implements Callable { + protected final RestApiService restApiService; + + @CommandLine.Mixin + protected SharedOptions sharedOptions; + + protected BaseCollableCommand(RestApiService restApiService) { + this.restApiService = restApiService; + } + + protected abstract Integer doCall() throws Exception; + + public SharedOptions getSharedOptions() { + return sharedOptions; + } + + @Override + public Integer call() throws Exception { + log.info("AddVersion version command has triggered with log verbose mode = '{}'", sharedOptions.isVerbose()); + restApiService.setVerbose(sharedOptions.isVerbose()); + return doCall(); + } +} diff --git a/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/CreateApplicationCommand.java b/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/CreateApplicationCommand.java index 8f2be68c..07365671 100644 --- a/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/CreateApplicationCommand.java +++ b/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/CreateApplicationCommand.java @@ -8,19 +8,15 @@ import com.castsoftware.aip.console.tools.core.services.JobsService; import com.castsoftware.aip.console.tools.core.services.RestApiService; import com.castsoftware.aip.console.tools.core.utils.Constants; -import lombok.AllArgsConstructor; import lombok.Getter; -import lombok.NoArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import picocli.CommandLine; import java.util.Arrays; import java.util.List; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; @Component @@ -33,18 +29,13 @@ @Slf4j @Getter @Setter -@NoArgsConstructor -@AllArgsConstructor -public class CreateApplicationCommand implements Callable { +public class CreateApplicationCommand extends BaseCollableCommand { + private final JobsService jobsService; - @Autowired - private JobsService jobsService; - - @Autowired - private RestApiService restApiService; - - @CommandLine.Mixin - private SharedOptions sharedOptions; + public CreateApplicationCommand(RestApiService restApiService, JobsService jobsService) { + super(restApiService); + this.jobsService = jobsService; + } /** * options for the upload and job startup @@ -70,7 +61,7 @@ public class CreateApplicationCommand implements Callable { private List unmatchedOptions; @Override - public Integer call() { + public Integer doCall() { try { if (sharedOptions.getTimeout() != Constants.DEFAULT_HTTP_TIMEOUT) { restApiService.setTimeout(sharedOptions.getTimeout(), TimeUnit.SECONDS); @@ -82,8 +73,6 @@ public Integer call() { return Constants.RETURN_LOGIN_ERROR; } - log.info("Create application command has triggered with log output = '{}'", sharedOptions.isVerbose()); - try { String nodeGuid = null; if (StringUtils.isNotBlank(nodeName)) { diff --git a/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/DeliverVersionCommand.java b/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/DeliverVersionCommand.java index 9fa0b76c..1afa5720 100644 --- a/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/DeliverVersionCommand.java +++ b/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/DeliverVersionCommand.java @@ -26,7 +26,6 @@ import java.io.File; import java.nio.file.Files; import java.util.Date; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -43,15 +42,11 @@ @Slf4j @Getter @Setter -public class DeliverVersionCommand implements Callable { - private final RestApiService restApiService; +public class DeliverVersionCommand extends BaseCollableCommand { private final JobsService jobsService; private final UploadService uploadService; private final ApplicationService applicationService; - @CommandLine.Mixin - private SharedOptions sharedOptions; - @CommandLine.Option(names = {"-n", "--app-name"}, paramLabel = "APPLICATION_NAME", description = "The Name of the application to rescan", @@ -136,14 +131,14 @@ public class DeliverVersionCommand implements Callable { private String domainName; public DeliverVersionCommand(RestApiService restApiService, JobsService jobsService, UploadService uploadService, ApplicationService applicationService) { - this.restApiService = restApiService; + super(restApiService); this.jobsService = jobsService; this.uploadService = uploadService; this.applicationService = applicationService; } @Override - public Integer call() throws Exception { + public Integer doCall() { // Same as a part of the AddVersion command // Upload a local file or register a remote path // And then starts a job up to "Delivery" @@ -163,9 +158,7 @@ public Integer call() throws Exception { } catch (ApiCallException e) { return Constants.RETURN_LOGIN_ERROR; } - - log.info("Deliver version command has triggered with log output = '{}'", sharedOptions.isVerbose()); - + String applicationGuid; Thread shutdownHook = null; diff --git a/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/SnapshotCommand.java b/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/SnapshotCommand.java index f28a052b..bbb800d4 100644 --- a/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/SnapshotCommand.java +++ b/aip-console-tools-cli/src/main/java/com/castsoftware/aip/console/tools/commands/SnapshotCommand.java @@ -29,7 +29,6 @@ import java.util.Date; import java.util.Optional; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.TimeUnit; import java.util.function.Function; @@ -46,13 +45,10 @@ @Slf4j @Getter @Setter -public class SnapshotCommand implements Callable { +public class SnapshotCommand extends BaseCollableCommand { private static final DateFormat RELEASE_DATE_FORMATTER = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); - private final RestApiService restApiService; private final JobsService jobsService; private final ApplicationService applicationService; - @CommandLine.Mixin - private SharedOptions sharedOptions; @CommandLine.Option(names = {"-n", "--app-name"}, paramLabel = "APPLICATION_NAME", @@ -75,14 +71,13 @@ public class SnapshotCommand implements Callable { private boolean processImaging = false; public SnapshotCommand(RestApiService restApiService, JobsService jobsService, ApplicationService applicationService) { - this.restApiService = restApiService; + super(restApiService); this.jobsService = jobsService; this.applicationService = applicationService; } - @Override - public Integer call() throws Exception { + public Integer doCall() { // Runs snapshot + upload if (StringUtils.isBlank(applicationName)) { log.error("No application name provided. Exiting."); diff --git a/aip-console-tools-cli/src/test/java/com/castsoftware/aip/console/tools/AddVersionCommandIntegrationTest.java b/aip-console-tools-cli/src/test/java/com/castsoftware/aip/console/tools/AddVersionCommandIntegrationTest.java index 21048508..7b60126e 100644 --- a/aip-console-tools-cli/src/test/java/com/castsoftware/aip/console/tools/AddVersionCommandIntegrationTest.java +++ b/aip-console-tools-cli/src/test/java/com/castsoftware/aip/console/tools/AddVersionCommandIntegrationTest.java @@ -143,5 +143,4 @@ public void testAddVersionCommand() throws ApplicationServiceException { assertThat(spec, is(notNullValue())); } - } diff --git a/aip-console-tools-cli/src/test/java/com/castsoftware/aip/console/tools/AipConsoleToolsCliBaseTest.java b/aip-console-tools-cli/src/test/java/com/castsoftware/aip/console/tools/AipConsoleToolsCliBaseTest.java index 21235264..bc817c92 100644 --- a/aip-console-tools-cli/src/test/java/com/castsoftware/aip/console/tools/AipConsoleToolsCliBaseTest.java +++ b/aip-console-tools-cli/src/test/java/com/castsoftware/aip/console/tools/AipConsoleToolsCliBaseTest.java @@ -81,12 +81,14 @@ protected void resetSharedOptions(SharedOptions command) { if (unExpectedParameters != null) { unExpectedParameters.clear(); } - command.setApiKey(null); - command.setApiKeyEnvVariable(null); - command.setServerRootUrl(null); - command.setUsername(null); - if (command.getUnmatchedOptions() != null) { - command.getUnmatchedOptions().clear(); + if (command != null) { + command.setApiKey(null); + command.setApiKeyEnvVariable(null); + command.setServerRootUrl(null); + command.setUsername(null); + if (command.getUnmatchedOptions() != null) { + command.getUnmatchedOptions().clear(); + } } } diff --git a/aip-console-tools-cli/src/test/java/com/castsoftware/aip/console/tools/CallStackManagementTest.java b/aip-console-tools-cli/src/test/java/com/castsoftware/aip/console/tools/CallStackManagementTest.java new file mode 100644 index 00000000..ad84e90e --- /dev/null +++ b/aip-console-tools-cli/src/test/java/com/castsoftware/aip/console/tools/CallStackManagementTest.java @@ -0,0 +1,120 @@ +package com.castsoftware.aip.console.tools; + +import com.castsoftware.aip.console.tools.core.exceptions.ApiCallException; +import com.castsoftware.aip.console.tools.core.exceptions.ApiCallNoStackTraceException; +import com.castsoftware.aip.console.tools.core.exceptions.ApplicationServiceException; +import com.castsoftware.aip.console.tools.core.exceptions.JobServiceException; +import com.castsoftware.aip.console.tools.core.exceptions.PackagePathInvalidException; +import com.castsoftware.aip.console.tools.core.exceptions.UploadException; +import com.castsoftware.aip.console.tools.core.services.RestApiServiceImpl; +import okhttp3.Call; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.apache.commons.lang3.StringUtils; +import org.hamcrest.core.Is; +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; +import org.springframework.test.util.ReflectionTestUtils; + +import java.io.IOException; +import java.util.Arrays; +import java.util.stream.Collectors; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.is; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class CallStackManagementTest { + @Test + public void testAddVersionCommand_loginFailedWithVerbose() throws ApiCallException, ApplicationServiceException, UploadException, JobServiceException, PackagePathInvalidException, IOException { + OkHttpClient client = Mockito.mock(OkHttpClient.class); + Call realCall = Mockito.mock(Call.class); + final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + String bodyJson = "{\n" + + " \"error\": \"Unauthorized\",\n" + + " \"message\": \"fake Unauthorized user message\",\n" + + " \"path\": \"/api/applications/61bc26f5-365c-40b6-b821-29338d879f08/debug-options\",\n" + + " \"status\": 401,\n" + + " \"timestamp\": \"2021-05-31T13:47:33.089Z\"\n" + + "}"; + RequestBody body = RequestBody.create(JSON, bodyJson); + Request request = new Request.Builder() + .url("https://demo-eu.castsoftware.com/ui/index.html#/") + .post(body) + .build(); + + Response.Builder responseBuilder = new Response.Builder(); + Response response = responseBuilder.request(request).protocol(Protocol.HTTP_1_0).code(401) + .message(body.toString()) + .body(ResponseBody.create(JSON, bodyJson)) + .build(); + when(realCall.execute()).thenReturn(response); + when(client.newCall(any(Request.class))).thenReturn(realCall); + RestApiServiceImpl restApiService = new RestApiServiceImpl(); + ReflectionTestUtils.setField(restApiService, "client", client); + ReflectionTestUtils.setField(restApiService, "serverUrl", "https://demo-eu.castsoftware.com"); + ReflectionTestUtils.setField(restApiService, "verbose", true); + + try { + restApiService.login(); + fail(); + } catch (ApiCallException e) { + //Mage sure stack trace is there + assertThat(e.getStackTrace().length, greaterThan(1)); + assertThat(StringUtils.isEmpty(Arrays.stream(e.getStackTrace()).map(StackTraceElement::getClassName).collect(Collectors.joining(";"))), is(false)); + } + } + + @Test + public void testAddVersionCommand_loginFailedWithoutVerbose() throws ApiCallException, ApplicationServiceException, UploadException, JobServiceException, PackagePathInvalidException, IOException { + OkHttpClient client = Mockito.mock(OkHttpClient.class); + Call realCall = Mockito.mock(Call.class); + final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); + String bodyJson = "{\n" + + " \"error\": \"Unauthorized\",\n" + + " \"message\": \"fake Unauthorized user message\",\n" + + " \"path\": \"/api/applications/61bc26f5-365c-40b6-b821-29338d879f08/debug-options\",\n" + + " \"status\": 401,\n" + + " \"timestamp\": \"2021-05-31T13:47:33.089Z\"\n" + + "}"; + RequestBody body = RequestBody.create(JSON, bodyJson); + Request request = new Request.Builder() + .url("https://demo-eu.castsoftware.com/ui/index.html#/") + .post(body) + .build(); + + Response.Builder responseBuilder = new Response.Builder(); + Response response = responseBuilder.request(request).protocol(Protocol.HTTP_1_0).code(401) + .message(body.toString()) + .body(ResponseBody.create(JSON, bodyJson)) + .build(); + when(realCall.execute()).thenReturn(response); + when(client.newCall(any(Request.class))).thenReturn(realCall); + RestApiServiceImpl restApiService = new RestApiServiceImpl(); + ReflectionTestUtils.setField(restApiService, "client", client); + ReflectionTestUtils.setField(restApiService, "serverUrl", "https://demo-eu.castsoftware.com"); + ReflectionTestUtils.setField(restApiService, "verbose", false); + + try { + restApiService.login(); + fail(); + } catch (ApiCallException e) { + //Mage sure stack trace is not there + Assert.assertThat(e instanceof ApiCallNoStackTraceException, Is.is(true)); + assertThat(e.getStackTrace().length, is(0)); + assertThat(StringUtils.isEmpty(Arrays.stream(e.getStackTrace()).map(StackTraceElement::getClassName).collect(Collectors.joining(";"))), is(true)); + } + } +} diff --git a/aip-console-tools-core/pom.xml b/aip-console-tools-core/pom.xml index d8649440..0e88bf36 100644 --- a/aip-console-tools-core/pom.xml +++ b/aip-console-tools-core/pom.xml @@ -51,6 +51,11 @@ com.squareup.okhttp3 okhttp + + org.json + json + + junit diff --git a/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/exceptions/AipCallExceptionHelper.java b/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/exceptions/AipCallExceptionHelper.java new file mode 100644 index 00000000..c40a18d1 --- /dev/null +++ b/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/exceptions/AipCallExceptionHelper.java @@ -0,0 +1,49 @@ +package com.castsoftware.aip.console.tools.core.exceptions; + +import java.lang.reflect.InvocationTargetException; + +public final class AipCallExceptionHelper { + private static Class getCallExceptionClass(boolean verbose) { + return verbose ? ApiCallException.class : ApiCallNoStackTraceException.class; + } + + public static ApiCallException getThrowableApiCallException(boolean verbose, int httpStatus) { + try { + return (ApiCallException) getCallExceptionClass(verbose).getConstructor(int.class).newInstance(httpStatus); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + return new ApiCallException(httpStatus, e); //here with call stack anyway + } + } + + public static ApiCallException getThrowableApiCallException(boolean verbose, int httpStatus, String message) { + try { + return (ApiCallException) getCallExceptionClass(verbose).getConstructor(int.class, String.class).newInstance(httpStatus, message); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + return new ApiCallException(httpStatus, e); //here with call stack anyway + } + } + + public static ApiCallException getThrowableApiCallException(boolean verbose, int httpStatus, Throwable cause) { + try { + return (ApiCallException) getCallExceptionClass(verbose).getConstructor(int.class, Throwable.class).newInstance(httpStatus, cause); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + return new ApiCallException(httpStatus, e); //here with call stack anyway + } + } + + public static ApiCallException getThrowableApiCallException(boolean verbose, ApiCallException cause) { + return getThrowableApiCallException(verbose, cause.getHttpStatus(), cause); + } + + public static ApiCallException getThrowableApiCallException(boolean verbose, int httpStatus, String message, Throwable cause) { + try { + return (ApiCallException) getCallExceptionClass(verbose).getConstructor(int.class, String.class, Throwable.class).newInstance(httpStatus, message, cause); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { + e.printStackTrace(); + return new ApiCallException(httpStatus, e); //here with call stack anyway + } + } +} diff --git a/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/exceptions/ApiCallException.java b/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/exceptions/ApiCallException.java index 95635256..49299663 100644 --- a/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/exceptions/ApiCallException.java +++ b/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/exceptions/ApiCallException.java @@ -1,7 +1,7 @@ package com.castsoftware.aip.console.tools.core.exceptions; public class ApiCallException extends Exception { - private int httpStatus = 500; + protected int httpStatus = 500; public ApiCallException(int httpStatus) { super(); @@ -23,6 +23,15 @@ public ApiCallException(int httpStatus, String message, Throwable cause) { this.httpStatus = httpStatus; } + protected ApiCallException(String message, Throwable cause, boolean enableSuppression, boolean enableStackTrace) { + super(message, cause, enableSuppression, enableStackTrace); + } + + protected ApiCallException(int httpStatus, String message, Throwable cause, boolean enableSuppression, boolean enableStackTrace) { + super(message, cause, enableSuppression, enableStackTrace); + this.httpStatus = httpStatus; + } + public int getHttpStatus() { return httpStatus; } diff --git a/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/exceptions/ApiCallNoStackTraceException.java b/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/exceptions/ApiCallNoStackTraceException.java new file mode 100644 index 00000000..62f41f1b --- /dev/null +++ b/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/exceptions/ApiCallNoStackTraceException.java @@ -0,0 +1,40 @@ +package com.castsoftware.aip.console.tools.core.exceptions; + +public class ApiCallNoStackTraceException extends ApiCallException { + private static final String EMPTY_MESSAGE = ""; + private static final boolean ENABLE_STACK_TRACE = false; + private static final boolean ENABLE_SUPPRESSION = false; + + public ApiCallNoStackTraceException() { + super(EMPTY_MESSAGE, null, ENABLE_SUPPRESSION, ENABLE_STACK_TRACE); + } + + public ApiCallNoStackTraceException(Throwable cause) { + super(cause.toString(), cause, ENABLE_SUPPRESSION, ENABLE_STACK_TRACE); + } + + public ApiCallNoStackTraceException(String message, Throwable cause) { + super(message, cause, ENABLE_SUPPRESSION, ENABLE_STACK_TRACE); + } + + public ApiCallNoStackTraceException(String message) { + super(message, null, ENABLE_SUPPRESSION, ENABLE_STACK_TRACE); + } + + public ApiCallNoStackTraceException(int httpStatus) { + this(); + this.httpStatus = httpStatus; + } + + public ApiCallNoStackTraceException(int httpStatus, String message) { + this(httpStatus, message, null); + } + + public ApiCallNoStackTraceException(int httpStatus, Throwable cause) { + this(httpStatus, cause.toString(), cause); + } + + public ApiCallNoStackTraceException(int httpStatus, String message, Throwable cause) { + super(httpStatus, message, cause, ENABLE_SUPPRESSION, ENABLE_STACK_TRACE); + } +} diff --git a/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/services/RestApiService.java b/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/services/RestApiService.java index 197ed199..a1d36a37 100644 --- a/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/services/RestApiService.java +++ b/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/services/RestApiService.java @@ -3,7 +3,6 @@ import com.castsoftware.aip.console.tools.core.dto.ApiInfoDto; import com.castsoftware.aip.console.tools.core.exceptions.ApiCallException; import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.JavaType; import okhttp3.Response; import java.util.Map; @@ -46,6 +45,14 @@ public interface RestApiService { */ void login() throws ApiCallException; + /** + * Used by the service to display the stack trace depending on whether the argument + * is set to true or not.

Each Action or Builder should manage this by itself + * + * @param verbose enable or disable stack trace output. + */ + void setVerbose(boolean verbose); + /** * Retrieve the API Info from AIP Console * diff --git a/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/services/RestApiServiceImpl.java b/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/services/RestApiServiceImpl.java index f218d41c..4bd239c8 100644 --- a/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/services/RestApiServiceImpl.java +++ b/aip-console-tools-core/src/main/java/com/castsoftware/aip/console/tools/core/services/RestApiServiceImpl.java @@ -1,6 +1,7 @@ package com.castsoftware.aip.console.tools.core.services; import com.castsoftware.aip.console.tools.core.dto.ApiInfoDto; +import com.castsoftware.aip.console.tools.core.exceptions.AipCallExceptionHelper; import com.castsoftware.aip.console.tools.core.exceptions.ApiCallException; import com.castsoftware.aip.console.tools.core.exceptions.ApiKeyMissingException; import com.castsoftware.aip.console.tools.core.utils.ApiEndpointHelper; @@ -32,6 +33,8 @@ import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; +import org.json.JSONArray; +import org.json.JSONObject; import java.io.IOException; import java.io.InputStream; @@ -57,6 +60,7 @@ public class RestApiServiceImpl implements RestApiService { private String serverUrl; private String username; private String key; + private boolean verbose; public RestApiServiceImpl() { this.cookieJar = new QueryableCookieJar(); @@ -107,7 +111,7 @@ public void validateUrlAndKey(String serverUrl, String apiKey) throws ApiCallExc if(StringUtils.isBlank(apiKey)) { log.severe("No Password or API Key provided to log in to AIP Console."); - throw new ApiKeyMissingException("No Password or API Key provided to log in to AIP Console."); + throw AipCallExceptionHelper.getThrowableApiCallException(verbose, new ApiKeyMissingException("No Password or API Key provided to log in to AIP Console.")); } if (!StringUtils.startsWithIgnoreCase(serverUrl, "http")) { @@ -228,7 +232,12 @@ public T exchangeMultipartForEntity(String method, String endpoint, Map T exchangeForEntity(String method, String endpoint, Object entity, J return mapResponse(response, javaType); } String message = "Response code from API was unexpected : " + response.code(); - message += "\nContent was " + (response.body() == null ? "EMPTY" : response.body().string()); - throw new ApiCallException(response.code(), message); + String messageBody = "EMPTY"; + if (response.body() != null) { + messageBody = new JSONArray(response.body().string()).getJSONObject(0).getString("defaultMessage"); + } + message += "\nContent was: " + messageBody; + throw AipCallExceptionHelper.getThrowableApiCallException(verbose, response.code(), message); } catch (IOException e) { log.log(Level.SEVERE, "Unable to send request", e); throw new ApiCallException(500, e); @@ -321,11 +334,15 @@ public void login() throws ApiCallException { return; } log.severe("Login to AIP Console failed (http status is " + response.code() + ")"); - log.severe("Content was " + (response.body() == null ? "EMPTY" : response.body().string())); - throw new ApiCallException(response.code(), "Unable to login to AIP Console"); + String message = "EMPTY"; + if (response.body() != null) { + message = new JSONObject(response.body().string()).getString("error"); + } + log.severe("Content was " + message); + throw AipCallExceptionHelper.getThrowableApiCallException(verbose, response.code(), "Unable to login to AIP Console"); } catch (IOException e) { log.log(Level.SEVERE, "Unable to send request", e); - throw new ApiCallException(500, e); + throw AipCallExceptionHelper.getThrowableApiCallException(verbose, 500, e); } } @@ -351,7 +368,7 @@ private RequestBody getRequestBodyForEntity(Object entity) throws ApiCallExcepti entity == null ? "" : mapper.writeValueAsString(entity)); } catch (JsonProcessingException e) { log.log(Level.SEVERE, "Unable to map object of type " + entity.getClass().getName() + " to JSON", e); - throw new ApiCallException(500, e); + throw AipCallExceptionHelper.getThrowableApiCallException(verbose, 500, e); } } @@ -409,10 +426,10 @@ public Response intercept(Chain chain) throws IOException { // get xsrf cookie if (xsrfCookie != null) { - log.finest("Setting XSRF-TOKEN header to " + xsrfCookie.value()); + RestApiServiceImpl.log.finest("Setting XSRF-TOKEN header to " + xsrfCookie.value()); reqBuilder.header("X-XSRF-TOKEN", xsrfCookie.value()); } else { - log.finest("No xsrf cookie, next request should set it"); + RestApiServiceImpl.log.finest("No xsrf cookie, next request should set it"); } if (request.header("Authorization") != null || @@ -429,4 +446,9 @@ public Response intercept(Chain chain) throws IOException { } } } + + @Override + public void setVerbose(boolean verbose) { + this.verbose = verbose; + } } diff --git a/aip-console-tools-core/src/test/java/com/castsoftware/aip/console/tools/core/utils/AipCallExceptionHelperTests.java b/aip-console-tools-core/src/test/java/com/castsoftware/aip/console/tools/core/utils/AipCallExceptionHelperTests.java new file mode 100644 index 00000000..a067c4de --- /dev/null +++ b/aip-console-tools-core/src/test/java/com/castsoftware/aip/console/tools/core/utils/AipCallExceptionHelperTests.java @@ -0,0 +1,89 @@ +package com.castsoftware.aip.console.tools.core.utils; + +import com.castsoftware.aip.console.tools.core.exceptions.AipCallExceptionHelper; +import com.castsoftware.aip.console.tools.core.exceptions.ApiCallException; +import com.castsoftware.aip.console.tools.core.exceptions.ApiCallNoStackTraceException; +import com.castsoftware.aip.console.tools.core.exceptions.ApiKeyMissingException; +import com.castsoftware.aip.console.tools.core.exceptions.UploadException; +import org.apache.commons.lang3.StringUtils; +import org.hamcrest.Matchers; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.junit.MockitoJUnitRunner; + +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; + +@RunWith(MockitoJUnitRunner.StrictStubs.class) +public class AipCallExceptionHelperTests { + + @Test + public void testApiCallException_StatusAndMessageVverbose() { + ApiCallException thisException = AipCallExceptionHelper.getThrowableApiCallException(true, 201, "Unable to login to AIP Console"); + assertThat(thisException instanceof ApiCallException, is(true)); + } + + @Test + public void testApiCallException_StatusVerbose() { + ApiCallException thisException = AipCallExceptionHelper.getThrowableApiCallException(true, 201); + assertThat(thisException instanceof ApiCallException, is(true)); + } + + @Test + public void testApiCallException_StatusAndMessageAndThrowableVerbose() { + ApiCallException thisException = AipCallExceptionHelper.getThrowableApiCallException(true, 201, "Unable to login to AIP Console", new UploadException("Bla bla")); + assertThat(thisException instanceof ApiCallException, is(true)); + assertThat(thisException.getStackTrace().length, greaterThan(1)); + assertThat(StringUtils.isEmpty(thisException.getStackTrace().toString()), Matchers.is(false)); + } + + @Test + public void testApiCallException_StatusAndThrowableVerbose() { + ApiCallException thisException = AipCallExceptionHelper.getThrowableApiCallException(true, 201, new UploadException("Bla bla")); + assertThat(thisException instanceof ApiCallException, is(true)); + } + + @Test + public void testApiCallException_ThrowableVerbose() { + ApiCallException thisException = AipCallExceptionHelper.getThrowableApiCallException(true, new ApiKeyMissingException("No Password or API Key provided to log in to AIP Console.")); + assertThat(thisException instanceof ApiCallException, is(true)); + assertThat(thisException.getStackTrace().length, greaterThan(1)); + assertThat(StringUtils.isEmpty(thisException.getStackTrace().toString()), Matchers.is(false)); + } + + @Test + public void testApiCallException_ThrowableNoVerbose() { + ApiCallException thisException = AipCallExceptionHelper.getThrowableApiCallException(false, new ApiKeyMissingException("No Password or API Key provided to log in to AIP Console.")); + assertThat(thisException instanceof ApiCallNoStackTraceException, is(true)); + assertThat(thisException.getStackTrace().length, is(0)); + } + + @Test + public void testApiCallException_StatusAndMessageNoVerbose() { + ApiCallException thisException = AipCallExceptionHelper.getThrowableApiCallException(false, 201, "Unable to login to AIP Console"); + assertThat(thisException instanceof ApiCallNoStackTraceException, is(true)); + assertThat(thisException.getStackTrace().length, is(0)); + } + + @Test + public void testApiCallException_StatusNoVerbose() { + ApiCallException thisException = AipCallExceptionHelper.getThrowableApiCallException(false, 201); + assertThat(thisException instanceof ApiCallNoStackTraceException, is(true)); + assertThat(thisException.getStackTrace().length, is(0)); + } + + @Test + public void testApiCallException_StatusAndMessageAndThrowableNoVerbose() { + ApiCallException thisException = AipCallExceptionHelper.getThrowableApiCallException(false, 201, "Unable to login to AIP Console", new UploadException("Bla bla")); + assertThat(thisException instanceof ApiCallNoStackTraceException, is(true)); + assertThat(thisException.getStackTrace().length, is(0)); + } + + @Test + public void testApiCallException_StatusAndThrowableNoVerbose() { + ApiCallException thisException = AipCallExceptionHelper.getThrowableApiCallException(false, 201, new UploadException("Bla bla")); + assertThat(thisException instanceof ApiCallNoStackTraceException, is(true)); + assertThat(thisException.getStackTrace().length, is(0)); + } +} diff --git a/pom.xml b/pom.xml index e525cbf0..e9055533 100644 --- a/pom.xml +++ b/pom.xml @@ -91,6 +91,11 @@ okhttp 3.8.1 + + org.json + json + 20190722 +