From 09e80be40e9a8cc29302dc1f141def43bc8f0545 Mon Sep 17 00:00:00 2001 From: Johannes Eschrig Date: Tue, 2 Jun 2020 21:34:25 +0200 Subject: [PATCH 1/8] New 0.5.6-SNAPSHOT (#425) * Introduce new version 0.5.5 * New 0.5.6-SNAPSHOT --- .mvn/maven.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/maven.config b/.mvn/maven.config index 94419977ac..b9601dfe69 100644 --- a/.mvn/maven.config +++ b/.mvn/maven.config @@ -1,4 +1,4 @@ --Drevision=0.5.5-SNAPSHOT +-Drevision=0.5.6-SNAPSHOT -Dlicense.projectName=Corona-Warn-App -Dlicense.inceptionYear=2020 -Dlicense.licenseName=apache_v2 From 04e6a4ab9938fad4fbfbb4c5796f997ad4a483c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yavuz=20G=C3=BCnay?= Date: Tue, 2 Jun 2020 21:56:41 +0200 Subject: [PATCH 2/8] Fixing S3ClientWrapper's bucketExists method (#409) - restore functional correctness - limit required permissions - check if configured bucket exists during initialization --- .../objectstore/ObjectStoreAccess.java | 4 ++++ .../objectstore/client/S3ClientWrapper.java | 7 ++++++- .../objectstore/client/S3ClientWrapperTest.java | 14 +++----------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/objectstore/ObjectStoreAccess.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/objectstore/ObjectStoreAccess.java index 0e626b0164..40e813e76b 100644 --- a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/objectstore/ObjectStoreAccess.java +++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/objectstore/ObjectStoreAccess.java @@ -74,6 +74,10 @@ public class ObjectStoreAccess { this.client = objectStoreClient; this.bucket = distributionServiceConfig.getObjectStore().getBucket(); this.isSetPublicReadAclOnPutObject = distributionServiceConfig.getObjectStore().isSetPublicReadAclOnPutObject(); + + if (!this.client.bucketExists(this.bucket)) { + throw new IllegalArgumentException("No bucket with the specified name exists: " + bucket); + } } /** diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/objectstore/client/S3ClientWrapper.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/objectstore/client/S3ClientWrapper.java index 3da5e7677c..fef0e09cc5 100644 --- a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/objectstore/client/S3ClientWrapper.java +++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/objectstore/client/S3ClientWrapper.java @@ -36,6 +36,7 @@ import software.amazon.awssdk.services.s3.model.DeleteObjectsResponse; import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; +import software.amazon.awssdk.services.s3.model.NoSuchBucketException; import software.amazon.awssdk.services.s3.model.ObjectIdentifier; import software.amazon.awssdk.services.s3.model.PutObjectRequest; @@ -55,7 +56,11 @@ public S3ClientWrapper(S3Client s3Client) { @Override public boolean bucketExists(String bucketName) { try { - return !s3Client.listBuckets().buckets().stream().findFirst().isEmpty(); + // using S3Client.listObjectsV2 instead of S3Client.listBuckets/headBucket in order to limit required permissions + s3Client.listObjectsV2(ListObjectsV2Request.builder().bucket(bucketName).maxKeys(0).build()); + return true; + } catch (NoSuchBucketException e) { + return false; } catch (SdkException e) { throw new ObjectStoreOperationFailedException("Failed to determine if bucket exists.", e); } diff --git a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/objectstore/client/S3ClientWrapperTest.java b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/objectstore/client/S3ClientWrapperTest.java index 4c7968ceb9..fa458654e5 100644 --- a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/objectstore/client/S3ClientWrapperTest.java +++ b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/objectstore/client/S3ClientWrapperTest.java @@ -33,7 +33,6 @@ import app.coronawarn.server.services.distribution.objectstore.client.ObjectStoreClient.HeaderKey; import java.nio.file.Path; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -50,11 +49,9 @@ import software.amazon.awssdk.core.exception.SdkException; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; -import software.amazon.awssdk.services.s3.model.Bucket; import software.amazon.awssdk.services.s3.model.Delete; import software.amazon.awssdk.services.s3.model.DeleteObjectsRequest; import software.amazon.awssdk.services.s3.model.DeleteObjectsResponse; -import software.amazon.awssdk.services.s3.model.ListBucketsResponse; import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; import software.amazon.awssdk.services.s3.model.NoSuchBucketException; @@ -73,34 +70,29 @@ class S3ClientWrapperTest { private S3Client s3Client; private S3ClientWrapper s3ClientWrapper; - private List existingBuckets; @BeforeEach public void setUpMocks() { s3Client = mock(S3Client.class); s3ClientWrapper = new S3ClientWrapper(s3Client); - existingBuckets = new ArrayList<>(); - ListBucketsResponse listBucketsResponse = mock(ListBucketsResponse.class); - - when(listBucketsResponse.buckets()).thenReturn(existingBuckets); - when(s3Client.listBuckets()).thenReturn(listBucketsResponse); } @Test void testBucketExistsIfBucketExists() { - existingBuckets.add(Bucket.builder().name(VALID_BUCKET_NAME).build()); + when(s3Client.listObjectsV2((any(ListObjectsV2Request.class)))).thenReturn(ListObjectsV2Response.builder().build()); assertThat(s3ClientWrapper.bucketExists(VALID_BUCKET_NAME)).isTrue(); } @Test void testBucketExistsIfBucketDoesNotExist() { + when(s3Client.listObjectsV2(any(ListObjectsV2Request.class))).thenThrow(NoSuchBucketException.class); assertThat(s3ClientWrapper.bucketExists(VALID_BUCKET_NAME)).isFalse(); } @ParameterizedTest @ValueSource(classes = {S3Exception.class, SdkClientException.class, SdkException.class}) void bucketExistsThrowsObjectStoreOperationFailedExceptionIfClientThrows(Class cause) { - when(s3Client.listBuckets()).thenThrow(cause); + when(s3Client.listObjectsV2(any(ListObjectsV2Request.class))).thenThrow(cause); assertThatExceptionOfType(ObjectStoreOperationFailedException.class) .isThrownBy(() -> s3ClientWrapper.bucketExists(VALID_BUCKET_NAME)); } From d8dd80a6ff8786fd3e16fd1bc433fcd15f0ff0c1 Mon Sep 17 00:00:00 2001 From: Christopher Fenner <26137398+CCFenner@users.noreply.github.com> Date: Tue, 2 Jun 2020 22:18:23 +0200 Subject: [PATCH 3/8] add workflow to ensure license file headers (#354) (#421) * add workflow to ensure license file headers * correct maven properties Co-authored-by: Johannes Eschrig --- .github/workflows/license-analysis.yml | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .github/workflows/license-analysis.yml diff --git a/.github/workflows/license-analysis.yml b/.github/workflows/license-analysis.yml new file mode 100644 index 0000000000..c8cbd9a0e6 --- /dev/null +++ b/.github/workflows/license-analysis.yml @@ -0,0 +1,19 @@ +name: "CI" + +on: + pull_request: + branches: + - master + +jobs: + fileheader: + runs-on: ubuntu-latest + name: 'license file header' + steps: + - uses: actions/checkout@v2 + - name: Java Setup + uses: actions/setup-java@v1 + with: + java-version: 11 + - name: Verify License File Headers + run: mvn --batch-mode clean license:check-file-header -Dlicense.failOnMissingHeader=true -Dlicense.failOnNotUptodateHeader=true From 46235bd5c7112aa1d35ed2de072945120b1fbd87 Mon Sep 17 00:00:00 2001 From: Pit Humke Date: Tue, 2 Jun 2020 22:40:30 +0200 Subject: [PATCH 4/8] Fix minor sonar smells (#427) --- .../appconfig/ApplicationVersionConfigurationProvider.java | 3 +++ .../assembly/appconfig/validation/GeneralValidationError.java | 2 +- .../appconfig/ApplicationVersionConfigurationProviderTest.java | 2 +- .../ApplicationVersionConfigurationValidatorTest.java | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/ApplicationVersionConfigurationProvider.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/ApplicationVersionConfigurationProvider.java index 7e5fd8a83b..febcbe9e39 100644 --- a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/ApplicationVersionConfigurationProvider.java +++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/ApplicationVersionConfigurationProvider.java @@ -28,6 +28,9 @@ */ public class ApplicationVersionConfigurationProvider { + private ApplicationVersionConfigurationProvider() { + } + /** * The location of the app version config master file. */ diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/GeneralValidationError.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/GeneralValidationError.java index 0bb4619329..0f24ae555b 100644 --- a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/GeneralValidationError.java +++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/GeneralValidationError.java @@ -44,7 +44,7 @@ public GeneralValidationError(String errorSource, Object value, ErrorType reason @Override public String toString() { - return "RiskScoreClassificationValidationError{" + return "GeneralValidationError{" + "errorType=" + reason + ", parameter='" + errorSource + '\'' + ", givenValue=" + value diff --git a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/ApplicationVersionConfigurationProviderTest.java b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/ApplicationVersionConfigurationProviderTest.java index 3c76ac354e..08be400220 100644 --- a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/ApplicationVersionConfigurationProviderTest.java +++ b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/ApplicationVersionConfigurationProviderTest.java @@ -26,7 +26,7 @@ import app.coronawarn.server.common.protocols.internal.ApplicationVersionConfiguration; import org.junit.jupiter.api.Test; -public class ApplicationVersionConfigurationProviderTest { +class ApplicationVersionConfigurationProviderTest { @Test void okFile() throws UnableToLoadFileException { diff --git a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ApplicationVersionConfigurationValidatorTest.java b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ApplicationVersionConfigurationValidatorTest.java index 9105cef550..dc73d8f89e 100644 --- a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ApplicationVersionConfigurationValidatorTest.java +++ b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ApplicationVersionConfigurationValidatorTest.java @@ -30,7 +30,7 @@ import app.coronawarn.server.services.distribution.assembly.appconfig.validation.GeneralValidationError.ErrorType; import org.junit.jupiter.api.Test; -public class ApplicationVersionConfigurationValidatorTest { +class ApplicationVersionConfigurationValidatorTest { private static final ValidationResult SUCCESS = new ValidationResult(); From 9e5af3c6cc8f029c196c613a4d0d3be58a793d5e Mon Sep 17 00:00:00 2001 From: Steve BE <5719743+stevesap@users.noreply.github.com> Date: Tue, 2 Jun 2020 22:52:53 +0200 Subject: [PATCH 5/8] Remove unnecessary annotation (#426) Co-authored-by: Steve Co-authored-by: Johannes Eschrig --- .../server/services/submission/ServerApplication.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/services/submission/src/main/java/app/coronawarn/server/services/submission/ServerApplication.java b/services/submission/src/main/java/app/coronawarn/server/services/submission/ServerApplication.java index 3a1f744e94..48eeecc4fb 100644 --- a/services/submission/src/main/java/app/coronawarn/server/services/submission/ServerApplication.java +++ b/services/submission/src/main/java/app/coronawarn/server/services/submission/ServerApplication.java @@ -24,7 +24,6 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.servlet.ServletComponentScan; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; @@ -32,7 +31,6 @@ import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter; @SpringBootApplication -@ServletComponentScan @EnableJpaRepositories(basePackages = "app.coronawarn.server.common.persistence") @EntityScan(basePackages = "app.coronawarn.server.common.persistence") @ComponentScan({"app.coronawarn.server.common.persistence", From 20492aaed28176142700486f99877b602b9f1cde Mon Sep 17 00:00:00 2001 From: Michael Burwig Date: Tue, 2 Jun 2020 23:15:17 +0200 Subject: [PATCH 6/8] Add Google-Style IntelliJ formatter settings to .editor.conf (#397) --- .editorconfig | 248 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 246 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 6772e9086b..b382d1f347 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,8 +1,252 @@ root = true [*] -indent_style = space -end_of_line = lf charset = utf-8 +end_of_line = lf +indent_size = 2 +indent_style = space trim_trailing_whitespace = true insert_final_newline = true +max_line_length = 120 +tab_width = 2 +ij_continuation_indent_size = 4 + +[*.java] +ij_java_align_consecutive_assignments = false +ij_java_align_consecutive_variable_declarations = false +ij_java_align_group_field_declarations = false +ij_java_align_multiline_annotation_parameters = false +ij_java_align_multiline_array_initializer_expression = false +ij_java_align_multiline_assignment = false +ij_java_align_multiline_binary_operation = false +ij_java_align_multiline_chained_methods = false +ij_java_align_multiline_extends_list = false +ij_java_align_multiline_for = false +ij_java_align_multiline_method_parentheses = false +ij_java_align_multiline_parameters = false +ij_java_align_multiline_parameters_in_calls = false +ij_java_align_multiline_parenthesized_expression = false +ij_java_align_multiline_records = true +ij_java_align_multiline_resources = false +ij_java_align_multiline_ternary_operation = false +ij_java_align_multiline_text_blocks = false +ij_java_align_multiline_throws_list = false +ij_java_align_subsequent_simple_methods = false +ij_java_align_throws_keyword = false +ij_java_annotation_parameter_wrap = off +ij_java_array_initializer_new_line_after_left_brace = false +ij_java_array_initializer_right_brace_on_new_line = false +ij_java_array_initializer_wrap = normal +ij_java_assert_statement_colon_on_next_line = false +ij_java_assert_statement_wrap = off +ij_java_assignment_wrap = off +ij_java_binary_operation_sign_on_next_line = true +ij_java_binary_operation_wrap = normal +ij_java_blank_lines_after_anonymous_class_header = 0 +ij_java_blank_lines_after_class_header = 1 +ij_java_blank_lines_after_imports = 1 +ij_java_blank_lines_after_package = 1 +ij_java_blank_lines_around_class = 1 +ij_java_blank_lines_around_field = 0 +ij_java_blank_lines_around_field_in_interface = 0 +ij_java_blank_lines_around_initializer = 1 +ij_java_blank_lines_around_method = 1 +ij_java_blank_lines_around_method_in_interface = 1 +ij_java_blank_lines_before_class_end = 0 +ij_java_blank_lines_before_imports = 1 +ij_java_blank_lines_before_method_body = 0 +ij_java_blank_lines_before_package = 0 +ij_java_block_brace_style = end_of_line +ij_java_block_comment_at_first_column = true +ij_java_call_parameters_new_line_after_left_paren = false +ij_java_call_parameters_right_paren_on_new_line = false +ij_java_call_parameters_wrap = normal +ij_java_case_statement_on_separate_line = true +ij_java_catch_on_new_line = false +ij_java_class_annotation_wrap = split_into_lines +ij_java_class_brace_style = end_of_line +ij_java_class_count_to_use_import_on_demand = 999 +ij_java_class_names_in_javadoc = 1 +ij_java_do_not_indent_top_level_class_members = false +ij_java_do_not_wrap_after_single_annotation = false +ij_java_do_while_brace_force = always +ij_java_doc_add_blank_line_after_description = true +ij_java_doc_add_blank_line_after_param_comments = false +ij_java_doc_add_blank_line_after_return = false +ij_java_doc_add_p_tag_on_empty_lines = true +ij_java_doc_align_exception_comments = true +ij_java_doc_align_param_comments = true +ij_java_doc_do_not_wrap_if_one_line = false +ij_java_doc_enable_formatting = true +ij_java_doc_enable_leading_asterisks = true +ij_java_doc_indent_on_continuation = false +ij_java_doc_keep_empty_lines = true +ij_java_doc_keep_empty_parameter_tag = true +ij_java_doc_keep_empty_return_tag = true +ij_java_doc_keep_empty_throws_tag = true +ij_java_doc_keep_invalid_tags = true +ij_java_doc_param_description_on_new_line = false +ij_java_doc_preserve_line_breaks = false +ij_java_doc_use_throws_not_exception_tag = true +ij_java_else_on_new_line = false +ij_java_enum_constants_wrap = off +ij_java_extends_keyword_wrap = off +ij_java_extends_list_wrap = normal +ij_java_field_annotation_wrap = split_into_lines +ij_java_finally_on_new_line = false +ij_java_for_brace_force = always +ij_java_for_statement_new_line_after_left_paren = false +ij_java_for_statement_right_paren_on_new_line = false +ij_java_for_statement_wrap = normal +ij_java_generate_final_locals = false +ij_java_generate_final_parameters = false +ij_java_if_brace_force = always +ij_java_imports_layout = $*,|,* +ij_java_indent_case_from_switch = true +ij_java_insert_inner_class_imports = true +ij_java_insert_override_annotation = true +ij_java_keep_blank_lines_before_right_brace = 2 +ij_java_keep_blank_lines_between_package_declaration_and_header = 2 +ij_java_keep_blank_lines_in_code = 1 +ij_java_keep_blank_lines_in_declarations = 2 +ij_java_keep_control_statement_in_one_line = false +ij_java_keep_first_column_comment = true +ij_java_keep_indents_on_empty_lines = false +ij_java_keep_line_breaks = true +ij_java_keep_multiple_expressions_in_one_line = false +ij_java_keep_simple_blocks_in_one_line = false +ij_java_keep_simple_classes_in_one_line = false +ij_java_keep_simple_lambdas_in_one_line = false +ij_java_keep_simple_methods_in_one_line = false +ij_java_label_indent_absolute = false +ij_java_label_indent_size = 0 +ij_java_lambda_brace_style = end_of_line +ij_java_layout_static_imports_separately = true +ij_java_line_comment_add_space = false +ij_java_line_comment_at_first_column = true +ij_java_method_annotation_wrap = split_into_lines +ij_java_method_brace_style = end_of_line +ij_java_method_call_chain_wrap = normal +ij_java_method_parameters_new_line_after_left_paren = false +ij_java_method_parameters_right_paren_on_new_line = false +ij_java_method_parameters_wrap = normal +ij_java_modifier_list_wrap = false +ij_java_names_count_to_use_import_on_demand = 999 +ij_java_new_line_after_lparen_in_record_header = false +ij_java_parameter_annotation_wrap = off +ij_java_parentheses_expression_new_line_after_left_paren = false +ij_java_parentheses_expression_right_paren_on_new_line = false +ij_java_place_assignment_sign_on_next_line = false +ij_java_prefer_longer_names = true +ij_java_prefer_parameters_wrap = false +ij_java_record_components_wrap = normal +ij_java_repeat_synchronized = true +ij_java_replace_instanceof_and_cast = false +ij_java_replace_null_check = true +ij_java_replace_sum_lambda_with_method_ref = true +ij_java_resource_list_new_line_after_left_paren = false +ij_java_resource_list_right_paren_on_new_line = false +ij_java_resource_list_wrap = off +ij_java_rparen_on_new_line_in_record_header = false +ij_java_space_after_closing_angle_bracket_in_type_argument = false +ij_java_space_after_colon = true +ij_java_space_after_comma = true +ij_java_space_after_comma_in_type_arguments = true +ij_java_space_after_for_semicolon = true +ij_java_space_after_quest = true +ij_java_space_after_type_cast = true +ij_java_space_before_annotation_array_initializer_left_brace = false +ij_java_space_before_annotation_parameter_list = false +ij_java_space_before_array_initializer_left_brace = false +ij_java_space_before_catch_keyword = true +ij_java_space_before_catch_left_brace = true +ij_java_space_before_catch_parentheses = true +ij_java_space_before_class_left_brace = true +ij_java_space_before_colon = true +ij_java_space_before_colon_in_foreach = true +ij_java_space_before_comma = false +ij_java_space_before_do_left_brace = true +ij_java_space_before_else_keyword = true +ij_java_space_before_else_left_brace = true +ij_java_space_before_finally_keyword = true +ij_java_space_before_finally_left_brace = true +ij_java_space_before_for_left_brace = true +ij_java_space_before_for_parentheses = true +ij_java_space_before_for_semicolon = false +ij_java_space_before_if_left_brace = true +ij_java_space_before_if_parentheses = true +ij_java_space_before_method_call_parentheses = false +ij_java_space_before_method_left_brace = true +ij_java_space_before_method_parentheses = false +ij_java_space_before_opening_angle_bracket_in_type_parameter = false +ij_java_space_before_quest = true +ij_java_space_before_switch_left_brace = true +ij_java_space_before_switch_parentheses = true +ij_java_space_before_synchronized_left_brace = true +ij_java_space_before_synchronized_parentheses = true +ij_java_space_before_try_left_brace = true +ij_java_space_before_try_parentheses = true +ij_java_space_before_type_parameter_list = false +ij_java_space_before_while_keyword = true +ij_java_space_before_while_left_brace = true +ij_java_space_before_while_parentheses = true +ij_java_space_inside_one_line_enum_braces = false +ij_java_space_within_empty_array_initializer_braces = false +ij_java_space_within_empty_method_call_parentheses = false +ij_java_space_within_empty_method_parentheses = false +ij_java_spaces_around_additive_operators = true +ij_java_spaces_around_assignment_operators = true +ij_java_spaces_around_bitwise_operators = true +ij_java_spaces_around_equality_operators = true +ij_java_spaces_around_lambda_arrow = true +ij_java_spaces_around_logical_operators = true +ij_java_spaces_around_method_ref_dbl_colon = false +ij_java_spaces_around_multiplicative_operators = true +ij_java_spaces_around_relational_operators = true +ij_java_spaces_around_shift_operators = true +ij_java_spaces_around_type_bounds_in_type_parameters = true +ij_java_spaces_around_unary_operator = false +ij_java_spaces_within_angle_brackets = false +ij_java_spaces_within_annotation_parentheses = false +ij_java_spaces_within_array_initializer_braces = false +ij_java_spaces_within_braces = false +ij_java_spaces_within_brackets = false +ij_java_spaces_within_cast_parentheses = false +ij_java_spaces_within_catch_parentheses = false +ij_java_spaces_within_for_parentheses = false +ij_java_spaces_within_if_parentheses = false +ij_java_spaces_within_method_call_parentheses = false +ij_java_spaces_within_method_parentheses = false +ij_java_spaces_within_parentheses = false +ij_java_spaces_within_switch_parentheses = false +ij_java_spaces_within_synchronized_parentheses = false +ij_java_spaces_within_try_parentheses = false +ij_java_spaces_within_while_parentheses = false +ij_java_special_else_if_treatment = true +ij_java_subclass_name_suffix = Impl +ij_java_ternary_operation_signs_on_next_line = true +ij_java_ternary_operation_wrap = normal +ij_java_test_name_suffix = Test +ij_java_throws_keyword_wrap = normal +ij_java_throws_list_wrap = off +ij_java_use_external_annotations = false +ij_java_use_fq_class_names = false +ij_java_use_relative_indents = false +ij_java_use_single_class_imports = true +ij_java_variable_annotation_wrap = off +ij_java_visibility = public +ij_java_while_brace_force = always +ij_java_while_on_new_line = false +ij_java_wrap_comments = true +ij_java_wrap_first_method_in_call_chain = false +ij_java_wrap_long_lines = false + +[*.properties] +ij_properties_align_group_field_declarations = false +ij_properties_keep_blank_lines = false +ij_properties_key_value_delimiter = equals +ij_properties_spaces_around_key_value_delimiter = false + +[{*.yaml,*.yml}] +ij_yaml_keep_indents_on_empty_lines = false +ij_yaml_keep_line_breaks = true From f778e0476557484652e1d2cad26bc30c28836835 Mon Sep 17 00:00:00 2001 From: Michael Burwig Date: Wed, 3 Jun 2020 11:29:18 +0200 Subject: [PATCH 7/8] Update attenuation-duration configuration spec (#431) --- .../protocols/internal/app_config.proto | 8 +- .../internal/attenuation_duration.proto | 20 +++ .../ApplicationConfigurationValidator.java | 35 +----- .../AttenuationDurationValidator.java | 94 +++++++++++++++ .../appconfig/validation/ParameterSpec.java | 15 +++ .../resources/master-config/app-config.yaml | 6 +- .../master-config/attenuation-duration.yaml | 15 +++ .../AttenuationDurationMasterFileTest.java | 47 ++++++++ ...ApplicationConfigurationValidatorTest.java | 47 -------- .../AttenuationDurationValidatorTest.java | 114 ++++++++++++++++++ .../RiskScoreClassificationValidatorTest.java | 2 +- .../resources/configtests/app-config_ok.yaml | 6 +- .../configtests/attenuation-duration.yaml | 7 ++ 13 files changed, 324 insertions(+), 92 deletions(-) create mode 100644 common/protocols/src/main/proto/app/coronawarn/server/common/protocols/internal/attenuation_duration.proto create mode 100644 services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/AttenuationDurationValidator.java create mode 100644 services/distribution/src/main/resources/master-config/attenuation-duration.yaml create mode 100644 services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/AttenuationDurationMasterFileTest.java create mode 100644 services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/AttenuationDurationValidatorTest.java create mode 100644 services/distribution/src/test/resources/configtests/attenuation-duration.yaml diff --git a/common/protocols/src/main/proto/app/coronawarn/server/common/protocols/internal/app_config.proto b/common/protocols/src/main/proto/app/coronawarn/server/common/protocols/internal/app_config.proto index 57feba6449..2aa33b9a00 100644 --- a/common/protocols/src/main/proto/app/coronawarn/server/common/protocols/internal/app_config.proto +++ b/common/protocols/src/main/proto/app/coronawarn/server/common/protocols/internal/app_config.proto @@ -5,6 +5,7 @@ option java_multiple_files = true; import "app/coronawarn/server/common/protocols/internal/risk_score_classification.proto"; import "app/coronawarn/server/common/protocols/internal/risk_score_parameters.proto"; import "app/coronawarn/server/common/protocols/internal/app_version_config.proto"; +import "app/coronawarn/server/common/protocols/internal/attenuation_duration.proto"; message ApplicationConfiguration { @@ -14,12 +15,7 @@ message ApplicationConfiguration { app.coronawarn.server.common.protocols.internal.RiskScoreParameters exposureConfig = 3; - AttenuationDurationThresholds attenuationDurationThresholds = 4; + app.coronawarn.server.common.protocols.internal.AttenuationDuration attenuationDuration = 4; app.coronawarn.server.common.protocols.internal.ApplicationVersionConfiguration appVersion = 5; } - -message AttenuationDurationThresholds { - int32 lower = 1; - int32 upper = 2; -} \ No newline at end of file diff --git a/common/protocols/src/main/proto/app/coronawarn/server/common/protocols/internal/attenuation_duration.proto b/common/protocols/src/main/proto/app/coronawarn/server/common/protocols/internal/attenuation_duration.proto new file mode 100644 index 0000000000..e22fc656ae --- /dev/null +++ b/common/protocols/src/main/proto/app/coronawarn/server/common/protocols/internal/attenuation_duration.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package app.coronawarn.server.common.protocols.internal; +option java_package = "app.coronawarn.server.common.protocols.internal"; +option java_multiple_files = true; + +message AttenuationDuration { + Thresholds thresholds = 1; + Weights weights = 2; +} + +message Thresholds { + int32 lower = 1; + int32 upper = 2; +} + +message Weights { + double low = 1; + double mid = 2; + double high = 3; +} diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ApplicationConfigurationValidator.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ApplicationConfigurationValidator.java index 80d8a8b20c..104f2d7311 100644 --- a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ApplicationConfigurationValidator.java +++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ApplicationConfigurationValidator.java @@ -20,11 +20,6 @@ package app.coronawarn.server.services.distribution.assembly.appconfig.validation; -import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.GeneralValidationError.ErrorType.MIN_GREATER_THAN_MAX; -import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.GeneralValidationError.ErrorType.VALUE_OUT_OF_BOUNDS; -import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ParameterSpec.ATTENUATION_DURATION_THRESHOLD_MAX; -import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ParameterSpec.ATTENUATION_DURATION_THRESHOLD_MIN; - import app.coronawarn.server.common.protocols.internal.ApplicationConfiguration; import app.coronawarn.server.common.protocols.internal.RiskScoreClassification; import app.coronawarn.server.common.protocols.internal.RiskScoreParameters; @@ -51,12 +46,13 @@ public ValidationResult validate() { this.errors = new ValidationResult(); validateMinRisk(); - validateAttenuationDurationThresholds(); - ValidationResult exposureResult = new ExposureConfigurationValidator(config.getExposureConfig()).validate(); - ValidationResult riskScoreResult = new RiskScoreClassificationValidator(config.getRiskScoreClasses()).validate(); + errors.with(new ExposureConfigurationValidator(config.getExposureConfig()).validate()); + errors.with(new RiskScoreClassificationValidator(config.getRiskScoreClasses()).validate()); + errors.with(new ApplicationVersionConfigurationValidator(config.getAppVersion()).validate()); + errors.with(new AttenuationDurationValidator(config.getAttenuationDuration()).validate()); - return errors.with(exposureResult).with(riskScoreResult); + return errors; } private void validateMinRisk() { @@ -66,25 +62,4 @@ private void validateMinRisk() { this.errors.add(new MinimumRiskLevelValidationError(minLevel)); } } - - private void validateAttenuationDurationThresholds() { - int lower = config.getAttenuationDurationThresholds().getLower(); - int upper = config.getAttenuationDurationThresholds().getUpper(); - - checkThresholdBound("lower", lower); - checkThresholdBound("upper", upper); - - if (lower > upper) { - String parameters = "attenuationDurationThreshold.lower, attenuationDurationThreshold.upper"; - String values = lower + ", " + upper; - this.errors.add(new GeneralValidationError(parameters, values, MIN_GREATER_THAN_MAX)); - } - } - - private void checkThresholdBound(String boundLabel, int boundValue) { - if (boundValue < ATTENUATION_DURATION_THRESHOLD_MIN || boundValue > ATTENUATION_DURATION_THRESHOLD_MAX) { - this.errors.add( - new GeneralValidationError("attenuationDurationThreshold." + boundLabel, boundValue, VALUE_OUT_OF_BOUNDS)); - } - } } diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/AttenuationDurationValidator.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/AttenuationDurationValidator.java new file mode 100644 index 0000000000..d901723479 --- /dev/null +++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/AttenuationDurationValidator.java @@ -0,0 +1,94 @@ +/*- + * ---license-start + * Corona-Warn-App + * --- + * Copyright (C) 2020 SAP SE and all other contributors + * --- + * 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. + * ---license-end + */ + +package app.coronawarn.server.services.distribution.assembly.appconfig.validation; + +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.GeneralValidationError.ErrorType.MIN_GREATER_THAN_MAX; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.GeneralValidationError.ErrorType.VALUE_OUT_OF_BOUNDS; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ParameterSpec.ATTENUATION_DURATION_THRESHOLD_MAX; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ParameterSpec.ATTENUATION_DURATION_THRESHOLD_MIN; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ParameterSpec.ATTENUATION_DURATION_WEIGHT_MAX; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ParameterSpec.ATTENUATION_DURATION_WEIGHT_MIN; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.WeightValidationError.ErrorType.OUT_OF_RANGE; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.WeightValidationError.ErrorType.TOO_MANY_DECIMAL_PLACES; + +import app.coronawarn.server.common.protocols.internal.AttenuationDuration; +import java.math.BigDecimal; + +/** + * The AttenuationDurationValidator validates the values of an associated {@link AttenuationDuration} instance. + */ +public class AttenuationDurationValidator extends ConfigurationValidator { + + private final AttenuationDuration attenuationDuration; + + public AttenuationDurationValidator(AttenuationDuration attenuationDuration) { + this.attenuationDuration = attenuationDuration; + } + + @Override + public ValidationResult validate() { + errors = new ValidationResult(); + + validateThresholds(); + validateWeights(); + + return errors; + } + + private void validateThresholds() { + int lower = attenuationDuration.getThresholds().getLower(); + int upper = attenuationDuration.getThresholds().getUpper(); + + checkThresholdBound("lower", lower); + checkThresholdBound("upper", upper); + + if (lower > upper) { + String parameters = "attenuation-duration.thresholds.lower, attenuation-duration.thresholds.upper"; + String values = lower + ", " + upper; + this.errors.add(new GeneralValidationError(parameters, values, MIN_GREATER_THAN_MAX)); + } + } + + private void checkThresholdBound(String thresholdLabel, int thresholdValue) { + if (thresholdValue < ATTENUATION_DURATION_THRESHOLD_MIN || thresholdValue > ATTENUATION_DURATION_THRESHOLD_MAX) { + this.errors.add(new GeneralValidationError( + "attenuation-duration.thresholds." + thresholdLabel, thresholdValue, VALUE_OUT_OF_BOUNDS)); + } + } + + private void validateWeights() { + checkWeight("low", attenuationDuration.getWeights().getLow()); + checkWeight("mid", attenuationDuration.getWeights().getMid()); + checkWeight("high", attenuationDuration.getWeights().getHigh()); + } + + private void checkWeight(String weightLabel, double weightValue) { + if (weightValue < ATTENUATION_DURATION_WEIGHT_MIN || weightValue > ATTENUATION_DURATION_WEIGHT_MAX) { + this.errors.add(new WeightValidationError( + "attenuation-duration.weights." + weightLabel, weightValue, OUT_OF_RANGE)); + } + + if (BigDecimal.valueOf(weightValue).scale() > ParameterSpec.ATTENUATION_DURATION_WEIGHT_MAX_DECIMALS) { + this.errors.add(new WeightValidationError( + "attenuation-duration.weights." + weightLabel, weightValue, TOO_MANY_DECIMAL_PLACES)); + } + } +} diff --git a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ParameterSpec.java b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ParameterSpec.java index 39b986f285..4a79d8e1fc 100644 --- a/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ParameterSpec.java +++ b/services/distribution/src/main/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ParameterSpec.java @@ -63,4 +63,19 @@ private ParameterSpec() { * The allowed maximum value for an attenuation threshold. */ public static final int ATTENUATION_DURATION_THRESHOLD_MAX = 100; + + /** + * The allowed minimum value for an attenuation weight. + */ + public static final double ATTENUATION_DURATION_WEIGHT_MIN = .0; + + /** + * The allowed maximum value for an attenuation weight. + */ + public static final double ATTENUATION_DURATION_WEIGHT_MAX = 1.; + + /** + * Maximum number of allowed decimals for an attenuation weight. + */ + public static final int ATTENUATION_DURATION_WEIGHT_MAX_DECIMALS = 3; } diff --git a/services/distribution/src/main/resources/master-config/app-config.yaml b/services/distribution/src/main/resources/master-config/app-config.yaml index 1b26e3a4c1..874ef44fd9 100644 --- a/services/distribution/src/main/resources/master-config/app-config.yaml +++ b/services/distribution/src/main/resources/master-config/app-config.yaml @@ -9,9 +9,7 @@ # Change this file with caution! min-risk-score: 90 -attenuationDurationThresholds: - lower: 50 - upper: 70 +attenuation-duration: !include attenuation-duration.yaml risk-score-classes: !include risk-score-classification.yaml exposure-config: !include exposure-config.yaml -app-version: !include app-version-config.yaml \ No newline at end of file +app-version: !include app-version-config.yaml diff --git a/services/distribution/src/main/resources/master-config/attenuation-duration.yaml b/services/distribution/src/main/resources/master-config/attenuation-duration.yaml new file mode 100644 index 0000000000..420f1cc410 --- /dev/null +++ b/services/distribution/src/main/resources/master-config/attenuation-duration.yaml @@ -0,0 +1,15 @@ +# This is the attenuation and duration parameter thresholds. The lower and +# upper threshold partitions the value range into 3 subsets: low, mid, high +# +# Each of the aforementioned partitions has a weight in the range of [0, 1] +# assigned to it. The number of decimal places is restricted to 3. +# +# Change this file with caution! + +thresholds: + lower: 50 + upper: 70 +weights: + low: 1.0 + mid: 0.5 + high: 0.0 diff --git a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/AttenuationDurationMasterFileTest.java b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/AttenuationDurationMasterFileTest.java new file mode 100644 index 0000000000..512fbd5c55 --- /dev/null +++ b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/AttenuationDurationMasterFileTest.java @@ -0,0 +1,47 @@ +/*- + * ---license-start + * Corona-Warn-App + * --- + * Copyright (C) 2020 SAP SE and all other contributors + * --- + * 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. + * ---license-end + */ + +package app.coronawarn.server.services.distribution.assembly.appconfig; + +import static org.assertj.core.api.Assertions.assertThat; + +import app.coronawarn.server.common.protocols.internal.AttenuationDuration; +import app.coronawarn.server.services.distribution.assembly.appconfig.validation.AttenuationDurationValidator; +import app.coronawarn.server.services.distribution.assembly.appconfig.validation.ConfigurationValidator; +import app.coronawarn.server.services.distribution.assembly.appconfig.validation.ValidationResult; +import org.junit.jupiter.api.Test; + +/** + * This test will verify that the provided attenuation/duration parameters master file is syntactically correct and + * according to spec. There should never be any deployment when this test is failing. + */ +class AttenuationDurationMasterFileTest { + + private static final ValidationResult SUCCESS = new ValidationResult(); + + @Test + void testMasterFile() throws UnableToLoadFileException { + AttenuationDuration config = ApplicationConfigurationProvider.readMasterFile().getAttenuationDuration(); + + ConfigurationValidator validator = new AttenuationDurationValidator(config); + + assertThat(validator.validate()).isEqualTo(SUCCESS); + } +} diff --git a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ApplicationConfigurationValidatorTest.java b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ApplicationConfigurationValidatorTest.java index 39a91bbf01..73d43f7574 100644 --- a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ApplicationConfigurationValidatorTest.java +++ b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/ApplicationConfigurationValidatorTest.java @@ -20,26 +20,16 @@ package app.coronawarn.server.services.distribution.assembly.appconfig.validation; -import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.GeneralValidationError.ErrorType.MIN_GREATER_THAN_MAX; -import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.GeneralValidationError.ErrorType.VALUE_OUT_OF_BOUNDS; -import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ParameterSpec.ATTENUATION_DURATION_THRESHOLD_MAX; -import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ParameterSpec.ATTENUATION_DURATION_THRESHOLD_MIN; -import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.RiskScoreClassificationValidatorTest.MINIMAL_RISK_SCORE_CLASSIFICATION; -import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.RiskScoreClassificationValidatorTest.buildExpectedResult; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; -import app.coronawarn.server.common.protocols.internal.ApplicationConfiguration; -import app.coronawarn.server.common.protocols.internal.AttenuationDurationThresholds; import app.coronawarn.server.services.distribution.assembly.appconfig.ApplicationConfigurationProvider; -import app.coronawarn.server.services.distribution.assembly.appconfig.ExposureConfigurationProvider; import app.coronawarn.server.services.distribution.assembly.appconfig.UnableToLoadFileException; import java.util.stream.Stream; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.junit.jupiter.params.provider.ValueSource; class ApplicationConfigurationValidatorTest { @@ -59,43 +49,6 @@ void negative(TestWithExpectedResult test) throws UnableToLoadFileException { assertThat(getResultForTest(test)).isEqualTo(test.result); } - @ParameterizedTest - @ValueSource(ints = {ATTENUATION_DURATION_THRESHOLD_MIN - 1, ATTENUATION_DURATION_THRESHOLD_MAX + 1}) - void negativeForAttenuationDurationThresholdOutOfBounds(int invalidThresholdValue) throws Exception { - ApplicationConfigurationValidator validator = getValidatorForAttenuationDurationThreshold( - invalidThresholdValue, invalidThresholdValue); - - ValidationResult expectedResult = buildExpectedResult( - new GeneralValidationError("attenuationDurationThreshold.upper", invalidThresholdValue, VALUE_OUT_OF_BOUNDS), - new GeneralValidationError("attenuationDurationThreshold.lower", invalidThresholdValue, VALUE_OUT_OF_BOUNDS)); - - assertThat(validator.validate()).isEqualTo(expectedResult); - } - - @Test - void negativeForUpperAttenuationDurationThresholdLesserThanLower() throws Exception { - ApplicationConfigurationValidator validator = getValidatorForAttenuationDurationThreshold( - ATTENUATION_DURATION_THRESHOLD_MAX, ATTENUATION_DURATION_THRESHOLD_MIN); - - ValidationResult expectedResult = buildExpectedResult( - new GeneralValidationError("attenuationDurationThreshold.lower, attenuationDurationThreshold.upper", - (ATTENUATION_DURATION_THRESHOLD_MAX + ", " + ATTENUATION_DURATION_THRESHOLD_MIN), MIN_GREATER_THAN_MAX)); - - assertThat(validator.validate()).isEqualTo(expectedResult); - } - - private ApplicationConfigurationValidator getValidatorForAttenuationDurationThreshold(int lower, int upper) - throws Exception { - ApplicationConfiguration appConfig = ApplicationConfiguration.newBuilder() - .setMinRiskScore(100) - .setRiskScoreClasses(MINIMAL_RISK_SCORE_CLASSIFICATION) - .setExposureConfig(ExposureConfigurationProvider.readFile("configtests/exposure-config_ok.yaml")) - .setAttenuationDurationThresholds(AttenuationDurationThresholds.newBuilder() - .setLower(lower) - .setUpper(upper)).build(); - return new ApplicationConfigurationValidator(appConfig); - } - @Test void circular() { assertThatThrownBy(() -> { diff --git a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/AttenuationDurationValidatorTest.java b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/AttenuationDurationValidatorTest.java new file mode 100644 index 0000000000..ce857bc94a --- /dev/null +++ b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/AttenuationDurationValidatorTest.java @@ -0,0 +1,114 @@ +/*- + * ---license-start + * Corona-Warn-App + * --- + * Copyright (C) 2020 SAP SE and all other contributors + * --- + * 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. + * ---license-end + */ + +package app.coronawarn.server.services.distribution.assembly.appconfig.validation; + +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.GeneralValidationError.ErrorType.MIN_GREATER_THAN_MAX; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.GeneralValidationError.ErrorType.VALUE_OUT_OF_BOUNDS; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ParameterSpec.ATTENUATION_DURATION_THRESHOLD_MAX; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ParameterSpec.ATTENUATION_DURATION_THRESHOLD_MIN; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ParameterSpec.ATTENUATION_DURATION_WEIGHT_MAX; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.ParameterSpec.ATTENUATION_DURATION_WEIGHT_MIN; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.RiskScoreClassificationValidatorTest.buildError; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.RiskScoreClassificationValidatorTest.buildExpectedResult; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.WeightValidationError.ErrorType.OUT_OF_RANGE; +import static app.coronawarn.server.services.distribution.assembly.appconfig.validation.WeightValidationError.ErrorType.TOO_MANY_DECIMAL_PLACES; +import static org.assertj.core.api.Assertions.assertThat; + +import app.coronawarn.server.common.protocols.internal.AttenuationDuration; +import app.coronawarn.server.common.protocols.internal.Thresholds; +import app.coronawarn.server.common.protocols.internal.Weights; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +public class AttenuationDurationValidatorTest { + private static final Thresholds VALID_THRESHOLDS = + buildThresholds(ATTENUATION_DURATION_THRESHOLD_MIN, ATTENUATION_DURATION_THRESHOLD_MAX); + private static final Weights VALID_WEIGHTS = + buildWeights(ATTENUATION_DURATION_WEIGHT_MAX, ATTENUATION_DURATION_WEIGHT_MAX, ATTENUATION_DURATION_WEIGHT_MAX); + + @ParameterizedTest + @ValueSource(ints = {ATTENUATION_DURATION_THRESHOLD_MIN - 1, ATTENUATION_DURATION_THRESHOLD_MAX + 1}) + void failsIfAttenuationDurationThresholdOutOfBounds(int invalidThresholdValue) { + AttenuationDurationValidator validator = buildValidator( + buildThresholds(invalidThresholdValue, invalidThresholdValue), VALID_WEIGHTS); + + ValidationResult expectedResult = buildExpectedResult( + buildError("attenuation-duration.thresholds.lower", invalidThresholdValue, VALUE_OUT_OF_BOUNDS), + buildError("attenuation-duration.thresholds.upper", invalidThresholdValue, VALUE_OUT_OF_BOUNDS)); + + assertThat(validator.validate()).isEqualTo(expectedResult); + } + + @Test + void failsIfUpperAttenuationDurationThresholdLesserThanLower() { + AttenuationDurationValidator validator = buildValidator( + buildThresholds(ATTENUATION_DURATION_THRESHOLD_MAX, ATTENUATION_DURATION_THRESHOLD_MIN), VALID_WEIGHTS); + + ValidationResult expectedResult = buildExpectedResult( + new GeneralValidationError("attenuation-duration.thresholds.lower, attenuation-duration.thresholds.upper", + (ATTENUATION_DURATION_THRESHOLD_MAX + ", " + ATTENUATION_DURATION_THRESHOLD_MIN), MIN_GREATER_THAN_MAX)); + + assertThat(validator.validate()).isEqualTo(expectedResult); + } + + @ParameterizedTest + @ValueSource(doubles = {ATTENUATION_DURATION_WEIGHT_MIN - .1, ATTENUATION_DURATION_WEIGHT_MAX + .1}) + void failsIfWeightsOutOfBounds(double invalidWeightValue) { + AttenuationDurationValidator validator = buildValidator(VALID_THRESHOLDS, + buildWeights(invalidWeightValue, invalidWeightValue, invalidWeightValue)); + + ValidationResult expectedResult = buildExpectedResult( + new WeightValidationError("attenuation-duration.weights.low", invalidWeightValue, OUT_OF_RANGE), + new WeightValidationError("attenuation-duration.weights.mid", invalidWeightValue, OUT_OF_RANGE), + new WeightValidationError("attenuation-duration.weights.high", invalidWeightValue, OUT_OF_RANGE)); + + assertThat(validator.validate()).isEqualTo(expectedResult); + } + + @Test + void failsIfWeightsHaveTooManyDecimalPlaces() { + double invalidWeightValue = ATTENUATION_DURATION_WEIGHT_MAX - 0.0000001; + AttenuationDurationValidator validator = buildValidator(VALID_THRESHOLDS, + buildWeights(invalidWeightValue, invalidWeightValue, invalidWeightValue)); + + ValidationResult expectedResult = buildExpectedResult( + new WeightValidationError("attenuation-duration.weights.low", invalidWeightValue, TOO_MANY_DECIMAL_PLACES), + new WeightValidationError("attenuation-duration.weights.mid", invalidWeightValue, TOO_MANY_DECIMAL_PLACES), + new WeightValidationError("attenuation-duration.weights.high", invalidWeightValue, TOO_MANY_DECIMAL_PLACES)); + + assertThat(validator.validate()).isEqualTo(expectedResult); + } + + private static AttenuationDurationValidator buildValidator(Thresholds thresholds, Weights weights) { + return new AttenuationDurationValidator(AttenuationDuration.newBuilder() + .setThresholds(thresholds) + .setWeights(weights).build()); + } + + private static Thresholds buildThresholds(int lower, int upper) { + return Thresholds.newBuilder().setLower(lower).setUpper(upper).build(); + } + + private static Weights buildWeights(double low, double mid, double high) { + return Weights.newBuilder().setLow(low).setMid(mid).setHigh(high).build(); + } +} diff --git a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/RiskScoreClassificationValidatorTest.java b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/RiskScoreClassificationValidatorTest.java index fb72b639e6..29602ebd31 100644 --- a/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/RiskScoreClassificationValidatorTest.java +++ b/services/distribution/src/test/java/app/coronawarn/server/services/distribution/assembly/appconfig/validation/RiskScoreClassificationValidatorTest.java @@ -166,7 +166,7 @@ private static RiskScoreClass buildRiskClass(String label, int min, int max, Str return RiskScoreClass.newBuilder().setLabel(label).setMin(min).setMax(max).setUrl(url).build(); } - public static ValidationResult buildExpectedResult(GeneralValidationError... errors) { + public static ValidationResult buildExpectedResult(ValidationError... errors) { var validationResult = new ValidationResult(); Arrays.stream(errors).forEach(validationResult::add); return validationResult; diff --git a/services/distribution/src/test/resources/configtests/app-config_ok.yaml b/services/distribution/src/test/resources/configtests/app-config_ok.yaml index 0e8ae2a664..04bd94cd74 100644 --- a/services/distribution/src/test/resources/configtests/app-config_ok.yaml +++ b/services/distribution/src/test/resources/configtests/app-config_ok.yaml @@ -1,7 +1,5 @@ min-risk-score: 100 -attenuationDurationThresholds: - lower: 50 - upper: 70 +attenuation-duration: !include attenuation-duration.yaml risk-score-classes: !include risk-score-class_ok.yaml exposure-config: !include exposure-config_ok.yaml -app-version: !include app-version-config_ok.yaml \ No newline at end of file +app-version: !include app-version-config_ok.yaml diff --git a/services/distribution/src/test/resources/configtests/attenuation-duration.yaml b/services/distribution/src/test/resources/configtests/attenuation-duration.yaml new file mode 100644 index 0000000000..347d6bed0e --- /dev/null +++ b/services/distribution/src/test/resources/configtests/attenuation-duration.yaml @@ -0,0 +1,7 @@ +thresholds: + lower: 50 + upper: 70 +weights: + low: 1.0 + mid: 0.5 + high: 0.0 From b4fdd16a78268c690d9919ec17e407e719266b04 Mon Sep 17 00:00:00 2001 From: Johannes Eschrig Date: Wed, 3 Jun 2020 11:42:52 +0200 Subject: [PATCH 8/8] Introduce new version 0.5.6 --- .mvn/maven.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.mvn/maven.config b/.mvn/maven.config index b9601dfe69..915c34d61e 100644 --- a/.mvn/maven.config +++ b/.mvn/maven.config @@ -1,4 +1,4 @@ --Drevision=0.5.6-SNAPSHOT +-Drevision=0.5.6 -Dlicense.projectName=Corona-Warn-App -Dlicense.inceptionYear=2020 -Dlicense.licenseName=apache_v2