diff --git a/gradle.properties b/gradle.properties index d20bb09..d180543 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ # -# Copyright 2020-2022 Exactpro (Exactpro Systems Limited) +# Copyright 2020-2023 Exactpro (Exactpro Systems Limited) # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. diff --git a/src/main/java/com/exactpro/th2/inframgr/DeploymentController.java b/src/main/java/com/exactpro/th2/inframgr/DeploymentController.java index a37736f..7817426 100644 --- a/src/main/java/com/exactpro/th2/inframgr/DeploymentController.java +++ b/src/main/java/com/exactpro/th2/inframgr/DeploymentController.java @@ -23,21 +23,20 @@ import com.exactpro.th2.inframgr.statuswatcher.ResourceCondition; import com.exactpro.th2.inframgr.statuswatcher.StatusCache; import com.fasterxml.jackson.annotation.JsonProperty; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.ResponseBody; import java.util.ArrayList; import java.util.List; @Controller +@SuppressWarnings("unused") public class DeploymentController { - private static final Logger logger = LoggerFactory.getLogger(DeploymentController.class); - public static final String UNKNOWN_ERROR = "UNKNOWN_ERROR"; public static final String BAD_RESOURCE_NAME = "BAD_RESOURCE_NAME"; @@ -64,12 +63,11 @@ public List getResourceDeploymentStatuses( } return response; - } catch (ServiceException e) { throw e; } catch (Exception e) { - logger.error("Exception retrieving schema {} from repository", schemaName, e); - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, + "Exception retrieving schema " + schemaName + " from repository", e); } } diff --git a/src/main/java/com/exactpro/th2/inframgr/DescriptorController.java b/src/main/java/com/exactpro/th2/inframgr/DescriptorController.java index 72012aa..f0bc929 100644 --- a/src/main/java/com/exactpro/th2/inframgr/DescriptorController.java +++ b/src/main/java/com/exactpro/th2/inframgr/DescriptorController.java @@ -37,6 +37,7 @@ import static com.exactpro.th2.inframgr.statuswatcher.ResourcePath.annotationFor; @Controller +@SuppressWarnings("unused") public class DescriptorController { private static final String PROTOBUF_DESCRIPTOR = "protobuf-description-base64"; @@ -77,13 +78,13 @@ public Response getDescriptor(@PathVariable(name = "schema") String schemaName, String resourceLabel = annotationFor(schemaName, kind, box); descriptor = descriptorExtractor.getImageDescriptor(resourceLabel, kind, box, PROTOBUF_DESCRIPTOR); } catch (ResourceNotFoundException e) { - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.NOT_FOUND.name(), e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, HttpStatus.NOT_FOUND.name(), e); } catch (InvalidImageNameFormatException e) { - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, FORMATTING_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, FORMATTING_ERROR, e); } catch (RegistryRequestException e) { - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REGISTRY_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REGISTRY_ERROR, e); } catch (Exception e) { - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, e); } if (descriptor != null) { return new Response(PROTOBUF_DESCRIPTOR, descriptor); @@ -92,23 +93,5 @@ public Response getDescriptor(@PathVariable(name = "schema") String schemaName, return null; } - public static class Response { - private final String descriptor; - - private final String content; - - public Response(String descriptor, String content) { - this.descriptor = descriptor; - this.content = content; - } - - public String getDescriptor() { - return descriptor; - } - - public String getContent() { - return content; - } - } - + public record Response(String descriptor, String content) { } } diff --git a/src/main/java/com/exactpro/th2/inframgr/JobController.java b/src/main/java/com/exactpro/th2/inframgr/JobController.java index 060ae5e..983526f 100644 --- a/src/main/java/com/exactpro/th2/inframgr/JobController.java +++ b/src/main/java/com/exactpro/th2/inframgr/JobController.java @@ -36,6 +36,7 @@ import static com.exactpro.th2.inframgr.statuswatcher.ResourcePath.annotationFor; @RestController +@SuppressWarnings("unused") public class JobController { private static final String UNKNOWN_ERROR = "UNKNOWN_ERROR"; @@ -46,12 +47,12 @@ public class JobController { private static final String BAD_RESOURCE_NAME = "BAD_RESOURCE_NAME"; - private static final Logger logger = LoggerFactory.getLogger(JobController.class); + private static final Logger LOGGER = LoggerFactory.getLogger(JobController.class); @PutMapping("/jobs/{schemaName}/{jobName}") public void putSecrets(@PathVariable(name = "schemaName") String schemaName, @PathVariable(name = "jobName") String jobName) { - logger.debug("received request for job creation, job name: {}", jobName); + LOGGER.debug("received request for job creation, job name: {}", jobName); if (!K8sCustomResource.isSchemaNameValid(schemaName)) { throw new NotAcceptableException(BAD_RESOURCE_NAME, "Invalid schema name"); } @@ -72,16 +73,16 @@ public void putSecrets(@PathVariable(name = "schemaName") String schemaName, resource = Repository.getResource(gitter, ResourceType.Th2Job.kind(), jobName); resourceLabel = annotationFor(kube.getNamespaceName(), ResourceType.Th2Job.kind(), jobName); } catch (GitAPIException e) { - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, GIT_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, GIT_ERROR, e); } finally { gitter.unlock(); } kube.deleteCustomResource(resource); - logger.info("Delete resource : {}", resourceLabel); + LOGGER.info("Delete resource : {}", resourceLabel); kube.createCustomResource(resource); - logger.info("Created job with name : {}", resourceLabel); + LOGGER.info("Created job with name : {}", resourceLabel); } catch (IOException e) { - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, CONFIG_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, CONFIG_ERROR, e); } } } diff --git a/src/main/java/com/exactpro/th2/inframgr/PodController.java b/src/main/java/com/exactpro/th2/inframgr/PodController.java index 7da4c78..3b4cca5 100644 --- a/src/main/java/com/exactpro/th2/inframgr/PodController.java +++ b/src/main/java/com/exactpro/th2/inframgr/PodController.java @@ -35,7 +35,7 @@ import static com.exactpro.th2.inframgr.statuswatcher.ResourcePath.annotationFor; @Controller - +@SuppressWarnings("unused") public class PodController { private static final Logger LOGGER = LoggerFactory.getLogger(PodController.class); @@ -60,25 +60,28 @@ public ResponseEntity deleteResourcePods( } Config config = Config.getInstance(); - Kubernetes kubernetes = new Kubernetes(config.getBehaviour(), config.getKubernetes(), schemaName); - for (var resource : statusCache.getResourceDependencyStatuses(schemaName, kind, resourceName)) { - if (resource.getKind().equals(Kubernetes.KIND_POD)) { - try { - kubernetes.deletePodWithName(resource.getName(), force); - } catch (KubernetesClientException e) { - LOGGER.error("Could not delete pod \"{}\"", - annotationFor(kubernetes.getNamespaceName(), Kubernetes.KIND_POD, resource.getName())); + try(Kubernetes kubernetes = new Kubernetes(config.getBehaviour(), config.getKubernetes(), schemaName)) { + for (var resource : statusCache.getResourceDependencyStatuses(schemaName, kind, resourceName)) { + if (resource.getKind().equals(Kubernetes.KIND_POD)) { + String annotation = annotationFor(kubernetes.getNamespaceName(), + Kubernetes.KIND_POD, resource.getName()); + try { + kubernetes.deletePodWithName(resource.getName(), force); + LOGGER.info("Deleted pod \"{}\", schema name \"{}\"", annotation, schemaName); + } catch (KubernetesClientException e) { + LOGGER.error("Could not delete pod \"{}\"", annotation, e); + } } } } - + // TODO: return correct HTTP response return new ResponseEntity<>(HttpStatus.NO_CONTENT); } catch (ServiceException e) { - LOGGER.error(e.getMessage(), e); throw e; } catch (Exception e) { - LOGGER.error("Exception deleting pods for \"{}/{}\" in schema \"{}\"", kind, resourceName, schemaName, e); - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, + "Exception deleting pods for \"" + kind + "/" + resourceName + + "\" in schema \"" + schemaName + "\"", e); } } } diff --git a/src/main/java/com/exactpro/th2/inframgr/SchemaController.java b/src/main/java/com/exactpro/th2/inframgr/SchemaController.java index 6b2f2b2..9b5063f 100644 --- a/src/main/java/com/exactpro/th2/inframgr/SchemaController.java +++ b/src/main/java/com/exactpro/th2/inframgr/SchemaController.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,6 @@ import com.exactpro.th2.validator.SchemaValidator; import com.exactpro.th2.validator.ValidationReport; import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.module.kotlin.KotlinModule; @@ -47,13 +46,25 @@ import org.slf4j.LoggerFactory; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.*; - -import java.util.*; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.ResponseBody; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; @Controller +@SuppressWarnings("unused") public class SchemaController { + private static final Logger LOGGER = LoggerFactory.getLogger(SchemaController.class); + public static final String SCHEMA_EXISTS = "SCHEMA_EXISTS"; private static final String REPOSITORY_ERROR = "REPOSITORY_ERROR"; @@ -62,8 +73,6 @@ public class SchemaController { public static final String SOURCE_BRANCH = "master"; - private static final Logger logger = LoggerFactory.getLogger(SchemaController.class); - @GetMapping("/schemas") @ResponseBody public Set getAvailableSchemas() throws ServiceException { @@ -74,7 +83,7 @@ public Set getAvailableSchemas() throws ServiceException { schemas.remove(SOURCE_BRANCH); return schemas; } catch (Exception e) { - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, e); } } @@ -93,10 +102,10 @@ public SchemaControllerResponse getSchemaFiles(@PathVariable(name = "name") Stri gitter.lock(); return new SchemaControllerResponse(Repository.getSnapshot(gitter)); } catch (RefNotAdvertisedException | RefNotFoundException e) { - throw new ServiceException(HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND.name(), "schema does not exists"); + throw new ServiceException(HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND.name(), "schema does not exists", e); } catch (Exception e) { - logger.error("Exception retrieving schema {} from repository", schemaName, e); - throw new NotAcceptableException(REPOSITORY_ERROR, e.getMessage()); + LOGGER.error("Exception retrieving schema {} from repository", schemaName, e); + throw new NotAcceptableException(REPOSITORY_ERROR, e); } finally { gitter.unlock(); } @@ -136,13 +145,14 @@ public SchemaControllerResponse createSchema(@PathVariable(name = "name") String issueRepoUpdateEvent(schemaName, snapshot); + LOGGER.info("Created schema \"{}\"", schemaName); return new SchemaControllerResponse(snapshot); } catch (ServiceException se) { throw se; } catch (Exception e) { - logger.error("Exception creating schema \"{}\"", schemaName, e); - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, + "Exception creating schema \"" + schemaName + "\"", e); } } @@ -151,7 +161,7 @@ private boolean schemaAlreadyExists(String schemaName, GitterContext ctx) { try { branches = ctx.getBranches(); } catch (Exception e) { - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, e); } return branches.contains(schemaName); } @@ -175,7 +185,7 @@ public SchemaControllerResponse updateSchema(@PathVariable(name = "name") String operations = mapper.readValue(requestBody, new TypeReference<>() { }); } catch (Exception e) { - throw new BadRequestException(e.getMessage()); + throw new BadRequestException(e); } validateResourceNames(operations); @@ -185,7 +195,8 @@ public SchemaControllerResponse updateSchema(@PathVariable(name = "name") String GitterContext ctx = GitterContext.getContext(gitConfig); if (!schemaAlreadyExists(schemaName, ctx)) { - throw new ServiceException(HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND.name(), "Schema does not exist"); + throw new ServiceException(HttpStatus.NOT_FOUND, + HttpStatus.NOT_FOUND.name(), "Schema does not exist", null); } //validate schema and apply updates if valid @@ -207,7 +218,7 @@ public SchemaControllerResponse updateSchema(@PathVariable(name = "name") String ); if (!validationContext.isValid()) { // do not update repository and kubernetes if requested changes contain errors. - logger.error("Schema \"{}\" contains errors, update request will be ignored", schemaName); + LOGGER.error("Schema \"{}\" contains errors, update request will be ignored", schemaName); ValidationReport report = validationContext.getReport(); SchemaErrorPrinter.printErrors(report, "editor"); return new SchemaControllerResponse(report); @@ -220,7 +231,7 @@ public SchemaControllerResponse updateSchema(@PathVariable(name = "name") String } if (commitRef == null) { - logger.info("Nothing changed, leaving"); + LOGGER.info("Nothing changed, leaving"); } else { issueRepoUpdateEvent(schemaName, snapshot); } @@ -228,13 +239,12 @@ public SchemaControllerResponse updateSchema(@PathVariable(name = "name") String } catch (ServiceException se) { throw se; } catch (Exception e) { - logger.error("Exception updating schema \"{}\" request", schemaName, e); - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, + "Exception updating schema \"" + schemaName + "\" request", e); } } - private void issueRepoUpdateEvent(String schemaName, RepositorySnapshot snapshot) - throws JsonProcessingException { + private void issueRepoUpdateEvent(String schemaName, RepositorySnapshot snapshot) { SchemaEventRouter router = SchemaEventRouter.getInstance(); RepositoryUpdateEvent event = new RepositoryUpdateEvent(schemaName, snapshot.getCommitRef()); RepositorySettingsSpec rs = snapshot.getRepositorySettingsSpec(); @@ -265,23 +275,23 @@ private String updateRepository(Gitter gitter, List operations) th } catch (InconsistentRepositoryStateException irse) { // this exception is thrown when inconsistent state of git repository is expected // discard local cache and re-download repository - logger.error("Inconsistent repository state exception for branch \"{}\"", branchName, irse); + LOGGER.error("Inconsistent repository state exception for branch \"{}\"", branchName, irse); - var se = new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, irse.getMessage()); + var se = new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, irse); se.addSuppressed(irse); try { gitter.recreateCache(); } catch (Exception re) { - logger.error("Exception recreating repository's local cache for branch \"{}\"", branchName, re); + LOGGER.error("Exception recreating repository's local cache for branch \"{}\"", branchName, re); se.addSuppressed(re); } throw se; } catch (Exception e) { - logger.error("Exception updating repository for branch \"{}\"", branchName, e); + LOGGER.error("Exception updating repository for branch \"{}\"", branchName, e); gitter.reset(); - throw new NotAcceptableException(REPOSITORY_ERROR, e.getMessage()); + throw new NotAcceptableException(REPOSITORY_ERROR, e); } } @@ -293,7 +303,7 @@ public static void validateResourceNames(List operations) { String resourceName = entry.getPayload().getName(); if (!K8sCustomResource.isNameValid(resourceName)) { - logger.error("Invalid resource name: \"{}\"", resourceName); + LOGGER.error("Invalid resource name: \"{}\"", resourceName); throw new NotAcceptableException(BAD_RESOURCE_NAME, String.format( "Invalid resource name : \"%s\" (%s)" , entry.getPayload().getName() @@ -302,7 +312,7 @@ public static void validateResourceNames(List operations) { } if (!names.add(resourceName)) { - logger.error("Multiple operations on the same resource: \"{}\"", resourceName); + LOGGER.error("Multiple operations on the same resource: \"{}\"", resourceName); throw new NotAcceptableException(REPOSITORY_ERROR, "Multiple operation on the resource"); } } diff --git a/src/main/java/com/exactpro/th2/inframgr/SchemaValidationController.java b/src/main/java/com/exactpro/th2/inframgr/SchemaValidationController.java index 6d1c734..79473ed 100644 --- a/src/main/java/com/exactpro/th2/inframgr/SchemaValidationController.java +++ b/src/main/java/com/exactpro/th2/inframgr/SchemaValidationController.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,6 +51,7 @@ import static com.exactpro.th2.inframgr.SchemaController.*; @Controller +@SuppressWarnings("unused") public class SchemaValidationController { private static final Logger logger = LoggerFactory.getLogger(SchemaController.class); @@ -145,7 +146,7 @@ public SchemaValidationContext validateSchema(@PathVariable(name = "schemaName") request = mapper.readValue(requestBody, new TypeReference<>() { }); } catch (Exception e) { - throw new BadRequestException(e.getMessage()); + throw new BadRequestException(e); } List operations = request.operations; @@ -174,7 +175,7 @@ public SchemaValidationContext validateSchema(@PathVariable(name = "schemaName") try { branches = ctx.getBranches(); } catch (Exception e) { - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, e); } if (!branches.contains(schemaName)) { throw new ServiceException(HttpStatus.NOT_FOUND, HttpStatus.NOT_FOUND.name(), "schema does not exists"); @@ -201,7 +202,7 @@ public SchemaValidationContext validateSchema(@PathVariable(name = "schemaName") return validationContext; } catch (Exception e) { logger.error("Exception updating schema \"{}\" request", schemaName, e); - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, REPOSITORY_ERROR, e); } } @@ -217,7 +218,7 @@ private Map> toRepositoryMap(List getSecrets(@PathVariable(name = "schemaName") String schemaNa Map secretData = secretsManager.getCustomSecret(schemaName).getData(); return secretData != null ? secretData.keySet() : Collections.emptySet(); } catch (Exception e) { - LOGGER.error("get secrets failure, schema name: {}", schemaName, e); - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, + "get secrets failure, schema name: \"" + schemaName + "\"", e); } } @@ -78,9 +84,7 @@ public Set putSecrets(@PathVariable(name = "schemaName") String schemaNa .registerModule(new KotlinModule.Builder().build()); secretEntries = mapper.readValue(requestBody, new TypeReference<>() { }); } catch (Exception e) { - // TODO: exclude secret body form log - LOGGER.error("Parsing secret body failure, schema name: {}", schemaName, e); - throw new BadRequestException(e.getMessage()); + throw new BadRequestException("Parsing secret body failure, schema name: \"" + schemaName + "\"", e); } Set secretKeys = new HashSet<>(); @@ -97,10 +101,12 @@ public Set putSecrets(@PathVariable(name = "schemaName") String schemaNa try { SecretsManager secretsManager = new SecretsManager(); - return secretsManager.createOrReplaceSecrets(schemaName, secretEntries); + Set secrets = secretsManager.createOrReplaceSecrets(schemaName, secretEntries); + LOGGER.info("Updated secrets \"{}\", schema name: \"{}\"", secrets, schemaName); + return secrets; } catch (Exception e) { - LOGGER.error("Create or replace secrets failure, schema name: {}", schemaName, e); - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, + "Create or replace secrets failure, schema name: \"" + schemaName + "\"", e); } } @@ -119,16 +125,19 @@ public Set deleteSecrets(@PathVariable(name = "schemaName") String schem secretsNames = mapper.readValue(requestBody, new TypeReference<>() { }); } catch (Exception e) { - LOGGER.error("Parsing secret body failure, schema name: {}, body: {}", schemaName, requestBody, e); - throw new BadRequestException(e.getMessage()); + throw new BadRequestException( + "Parsing secret body failure, schema name: \"" + schemaName + "\", body: \"" + requestBody + "\"", + e); } try { SecretsManager secretsManager = new SecretsManager(); - return secretsManager.deleteSecrets(schemaName, secretsNames); + Set secrets = secretsManager.deleteSecrets(schemaName, secretsNames); + LOGGER.info("Deleted secrets \"{}\", schema name: \"{}\"", secrets, schemaName); + return secrets; } catch (Exception e) { - LOGGER.error("Delete secrets failure, schema name: {}", schemaName, e); - throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, e.getMessage()); + throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, UNKNOWN_ERROR, + "Delete secrets failure, schema name: \"" + schemaName + "\"", e); } } diff --git a/src/main/java/com/exactpro/th2/inframgr/errors/BadRequestException.java b/src/main/java/com/exactpro/th2/inframgr/errors/BadRequestException.java index 8f558f2..e9d1997 100644 --- a/src/main/java/com/exactpro/th2/inframgr/errors/BadRequestException.java +++ b/src/main/java/com/exactpro/th2/inframgr/errors/BadRequestException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,16 @@ import org.springframework.http.HttpStatus; public class BadRequestException extends ServiceException { + + public BadRequestException(String message, Exception e) { + super(HttpStatus.BAD_REQUEST, HttpStatus.BAD_REQUEST.name(), message, e); + } + public BadRequestException(String message) { - super(HttpStatus.BAD_REQUEST, HttpStatus.BAD_REQUEST.name(), message); + this(message, null); + } + + public BadRequestException(Exception e) { + this(e.getMessage(), e); } } diff --git a/src/main/java/com/exactpro/th2/inframgr/errors/ErrorResponse.java b/src/main/java/com/exactpro/th2/inframgr/errors/ErrorResponse.java index ebb089b..4d00bbd 100644 --- a/src/main/java/com/exactpro/th2/inframgr/errors/ErrorResponse.java +++ b/src/main/java/com/exactpro/th2/inframgr/errors/ErrorResponse.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,11 @@ import com.fasterxml.jackson.annotation.JsonProperty; import org.springframework.http.HttpStatus; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +@SuppressWarnings("unused") public class ErrorResponse { public static final String STATUS_CODE = "status_code"; @@ -28,20 +33,29 @@ public class ErrorResponse { public static final String MESSAGE = "message"; - private HttpStatus httpStatus; + public static final String CAUSES = "causes"; - private String errorCode; + private final HttpStatus httpStatus; - private String message; + private final String errorCode; - public ErrorResponse(HttpStatus httpStatus, String errorCode) { - this(httpStatus, errorCode, null); - } + private final String message; - public ErrorResponse(HttpStatus httpStatus, String errorCode, String message) { + private final List causes; + + public ErrorResponse(HttpStatus httpStatus, String errorCode, String message, Exception e) { this.httpStatus = httpStatus; this.errorCode = errorCode; this.message = message; + this.causes = collectCauses(e); + } + + public ErrorResponse(HttpStatus httpStatus, String errorCode, Exception e) { + this(httpStatus, errorCode, e.getMessage(), e); + } + + public ErrorResponse(HttpStatus httpStatus, String errorCode, String message) { + this(httpStatus, errorCode, message, null); } @JsonIgnore @@ -63,4 +77,23 @@ public String getErrorCode() { public String getMessage() { return message; } + + @JsonProperty(CAUSES) + public List getCauses() { + return causes; + } + + private List collectCauses(Exception e) { + if (e == null || e.getCause() == null) { + return Collections.emptyList(); + } + + Throwable cause = e.getCause(); + List causes = new ArrayList<>(); + while (cause != null) { + causes.add(cause.getMessage()); + cause = cause.getCause(); + } + return causes; + } } diff --git a/src/main/java/com/exactpro/th2/inframgr/errors/NotAcceptableException.java b/src/main/java/com/exactpro/th2/inframgr/errors/NotAcceptableException.java index b26e7f5..df018d1 100644 --- a/src/main/java/com/exactpro/th2/inframgr/errors/NotAcceptableException.java +++ b/src/main/java/com/exactpro/th2/inframgr/errors/NotAcceptableException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,7 +19,15 @@ import org.springframework.http.HttpStatus; public class NotAcceptableException extends ServiceException { + public NotAcceptableException(String errorCode, String message, Exception e) { + super(HttpStatus.NOT_ACCEPTABLE, errorCode, message, e); + } + public NotAcceptableException(String errorCode, String message) { - super(HttpStatus.NOT_ACCEPTABLE, errorCode, message); + this(errorCode, message, null); + } + + public NotAcceptableException(String errorCode, Exception e) { + this(errorCode, e.getMessage(), e); } } diff --git a/src/main/java/com/exactpro/th2/inframgr/errors/ServiceException.java b/src/main/java/com/exactpro/th2/inframgr/errors/ServiceException.java index 6b71586..f6e9d0f 100644 --- a/src/main/java/com/exactpro/th2/inframgr/errors/ServiceException.java +++ b/src/main/java/com/exactpro/th2/inframgr/errors/ServiceException.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,14 +20,25 @@ public class ServiceException extends RuntimeException { - public ErrorResponse getErrorResponse() { - return errorResponse; - } + private final ErrorResponse errorResponse; - private ErrorResponse errorResponse; + public ServiceException(HttpStatus statusCode, String errorCode, String message, Exception e) { + super(message, e); + errorResponse = new ErrorResponse(statusCode, errorCode, message, e); + } public ServiceException(HttpStatus statusCode, String errorCode, String message) { + super(message); errorResponse = new ErrorResponse(statusCode, errorCode, message); } + public ServiceException(HttpStatus statusCode, String errorCode, Exception e) { + super(e); + errorResponse = new ErrorResponse(statusCode, errorCode, e); + } + + public ErrorResponse getErrorResponse() { + return errorResponse; + } + } diff --git a/src/main/java/com/exactpro/th2/inframgr/errors/ServiceExceptionHandler.java b/src/main/java/com/exactpro/th2/inframgr/errors/ServiceExceptionHandler.java index 23390e4..73b53f0 100644 --- a/src/main/java/com/exactpro/th2/inframgr/errors/ServiceExceptionHandler.java +++ b/src/main/java/com/exactpro/th2/inframgr/errors/ServiceExceptionHandler.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 Exactpro (Exactpro Systems Limited) + * Copyright 2020-2023 Exactpro (Exactpro Systems Limited) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,18 +16,22 @@ package com.exactpro.th2.inframgr.errors; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.context.request.WebRequest; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @ControllerAdvice +@SuppressWarnings("unused") public class ServiceExceptionHandler extends ResponseEntityExceptionHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceExceptionHandler.class); @ExceptionHandler(ServiceException.class) - public ResponseEntity handleServiceException(ServiceException e, WebRequest request) { - + public ResponseEntity handleServiceException(HttpServletRequest req, ServiceException e) { + LOGGER.error("Request: {} failure", req.getRequestURL(), e); ErrorResponse response = e.getErrorResponse(); return new ResponseEntity<>(response, response.getHttpStatus()); }