diff --git a/.editorconfig b/.editorconfig index e391ae95..322b027f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -34,6 +34,10 @@ ij_css_space_before_opening_brace = true ij_css_use_double_quotes = true ij_css_value_alignment = do_not_align +[*.feature] +indent_size = 2 +ij_gherkin_keep_indents_on_empty_lines = false + [*.java] indent_size = 2 max_line_length = 100 @@ -340,6 +344,53 @@ ij_java_wrap_comments = true ij_java_wrap_first_method_in_call_chain = false ij_java_wrap_long_lines = false +[*.less] +indent_size = 2 +ij_less_align_closing_brace_with_properties = false +ij_less_blank_lines_around_nested_selector = 1 +ij_less_blank_lines_between_blocks = 1 +ij_less_block_comment_add_space = false +ij_less_brace_placement = 0 +ij_less_enforce_quotes_on_format = false +ij_less_hex_color_long_format = false +ij_less_hex_color_lower_case = false +ij_less_hex_color_short_format = false +ij_less_hex_color_upper_case = false +ij_less_keep_blank_lines_in_code = 2 +ij_less_keep_indents_on_empty_lines = false +ij_less_keep_single_line_blocks = false +ij_less_line_comment_add_space = false +ij_less_line_comment_at_first_column = false +ij_less_properties_order = font,font-family,font-size,font-weight,font-style,font-variant,font-size-adjust,font-stretch,line-height,position,z-index,top,right,bottom,left,display,visibility,float,clear,overflow,overflow-x,overflow-y,clip,zoom,align-content,align-items,align-self,flex,flex-flow,flex-basis,flex-direction,flex-grow,flex-shrink,flex-wrap,justify-content,order,box-sizing,width,min-width,max-width,height,min-height,max-height,margin,margin-top,margin-right,margin-bottom,margin-left,padding,padding-top,padding-right,padding-bottom,padding-left,table-layout,empty-cells,caption-side,border-spacing,border-collapse,list-style,list-style-position,list-style-type,list-style-image,content,quotes,counter-reset,counter-increment,resize,cursor,user-select,nav-index,nav-up,nav-right,nav-down,nav-left,transition,transition-delay,transition-timing-function,transition-duration,transition-property,transform,transform-origin,animation,animation-name,animation-duration,animation-play-state,animation-timing-function,animation-delay,animation-iteration-count,animation-direction,text-align,text-align-last,vertical-align,white-space,text-decoration,text-emphasis,text-emphasis-color,text-emphasis-style,text-emphasis-position,text-indent,text-justify,letter-spacing,word-spacing,text-outline,text-transform,text-wrap,text-overflow,text-overflow-ellipsis,text-overflow-mode,word-wrap,word-break,tab-size,hyphens,pointer-events,opacity,color,border,border-width,border-style,border-color,border-top,border-top-width,border-top-style,border-top-color,border-right,border-right-width,border-right-style,border-right-color,border-bottom,border-bottom-width,border-bottom-style,border-bottom-color,border-left,border-left-width,border-left-style,border-left-color,border-radius,border-top-left-radius,border-top-right-radius,border-bottom-right-radius,border-bottom-left-radius,border-image,border-image-source,border-image-slice,border-image-width,border-image-outset,border-image-repeat,outline,outline-width,outline-style,outline-color,outline-offset,background,background-color,background-image,background-repeat,background-attachment,background-position,background-position-x,background-position-y,background-clip,background-origin,background-size,box-decoration-break,box-shadow,text-shadow +ij_less_space_after_colon = true +ij_less_space_before_opening_brace = true +ij_less_use_double_quotes = true +ij_less_value_alignment = 0 + +[*.proto] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_protobuf_keep_blank_lines_in_code = 2 +ij_protobuf_keep_indents_on_empty_lines = false +ij_protobuf_keep_line_breaks = true +ij_protobuf_space_after_comma = true +ij_protobuf_space_before_comma = false +ij_protobuf_spaces_around_assignment_operators = true +ij_protobuf_spaces_within_braces = false +ij_protobuf_spaces_within_brackets = false + +[*.vue] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_vue_indent_children_of_top_level = template +ij_vue_interpolation_new_line_after_start_delimiter = true +ij_vue_interpolation_new_line_before_end_delimiter = true +ij_vue_interpolation_wrap = off +ij_vue_keep_indents_on_empty_lines = false +ij_vue_spaces_within_interpolation_expressions = true + [.editorconfig] ij_editorconfig_align_group_field_declarations = false ij_editorconfig_space_after_colon = false @@ -348,7 +399,7 @@ ij_editorconfig_space_before_colon = false ij_editorconfig_space_before_comma = false ij_editorconfig_spaces_around_assignment_operators = true -[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdl,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}] +[{*.ant,*.fxml,*.jhm,*.jnlp,*.jrxml,*.pom,*.rng,*.tld,*.wadl,*.wsdd,*.wsdl,*.xjb,*.xml,*.xsd,*.xsl,*.xslt,*.xul,phpunit.xml.dist}] ij_xml_align_attributes = true ij_xml_align_text = false ij_xml_attribute_wrap = normal @@ -946,7 +997,198 @@ ij_php_version_weight = 28 ij_php_while_brace_force = never ij_php_while_on_new_line = false -[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,composer.lock,jest.config}] +[{*.gant,*.groovy,*.gy}] +ij_groovy_align_group_field_declarations = false +ij_groovy_align_multiline_array_initializer_expression = false +ij_groovy_align_multiline_assignment = false +ij_groovy_align_multiline_binary_operation = false +ij_groovy_align_multiline_chained_methods = false +ij_groovy_align_multiline_extends_list = false +ij_groovy_align_multiline_for = true +ij_groovy_align_multiline_list_or_map = true +ij_groovy_align_multiline_method_parentheses = false +ij_groovy_align_multiline_parameters = true +ij_groovy_align_multiline_parameters_in_calls = false +ij_groovy_align_multiline_resources = true +ij_groovy_align_multiline_ternary_operation = false +ij_groovy_align_multiline_throws_list = false +ij_groovy_align_named_args_in_map = true +ij_groovy_align_throws_keyword = false +ij_groovy_array_initializer_new_line_after_left_brace = false +ij_groovy_array_initializer_right_brace_on_new_line = false +ij_groovy_array_initializer_wrap = off +ij_groovy_assert_statement_wrap = off +ij_groovy_assignment_wrap = off +ij_groovy_binary_operation_wrap = off +ij_groovy_blank_lines_after_class_header = 0 +ij_groovy_blank_lines_after_imports = 1 +ij_groovy_blank_lines_after_package = 1 +ij_groovy_blank_lines_around_class = 1 +ij_groovy_blank_lines_around_field = 0 +ij_groovy_blank_lines_around_field_in_interface = 0 +ij_groovy_blank_lines_around_method = 1 +ij_groovy_blank_lines_around_method_in_interface = 1 +ij_groovy_blank_lines_before_imports = 1 +ij_groovy_blank_lines_before_method_body = 0 +ij_groovy_blank_lines_before_package = 0 +ij_groovy_block_brace_style = end_of_line +ij_groovy_block_comment_add_space = false +ij_groovy_block_comment_at_first_column = true +ij_groovy_call_parameters_new_line_after_left_paren = false +ij_groovy_call_parameters_right_paren_on_new_line = false +ij_groovy_call_parameters_wrap = off +ij_groovy_catch_on_new_line = false +ij_groovy_class_annotation_wrap = split_into_lines +ij_groovy_class_brace_style = end_of_line +ij_groovy_class_count_to_use_import_on_demand = 5 +ij_groovy_do_while_brace_force = never +ij_groovy_else_on_new_line = false +ij_groovy_enable_groovydoc_formatting = true +ij_groovy_enum_constants_wrap = off +ij_groovy_extends_keyword_wrap = off +ij_groovy_extends_list_wrap = off +ij_groovy_field_annotation_wrap = split_into_lines +ij_groovy_finally_on_new_line = false +ij_groovy_for_brace_force = never +ij_groovy_for_statement_new_line_after_left_paren = false +ij_groovy_for_statement_right_paren_on_new_line = false +ij_groovy_for_statement_wrap = off +ij_groovy_ginq_general_clause_wrap_policy = 2 +ij_groovy_ginq_having_wrap_policy = 1 +ij_groovy_ginq_indent_having_clause = true +ij_groovy_ginq_indent_on_clause = true +ij_groovy_ginq_on_wrap_policy = 1 +ij_groovy_ginq_space_after_keyword = true +ij_groovy_if_brace_force = never +ij_groovy_import_annotation_wrap = 2 +ij_groovy_imports_layout = *,|,javax.**,java.**,|,$* +ij_groovy_indent_case_from_switch = true +ij_groovy_indent_label_blocks = true +ij_groovy_insert_inner_class_imports = false +ij_groovy_keep_blank_lines_before_right_brace = 2 +ij_groovy_keep_blank_lines_in_code = 2 +ij_groovy_keep_blank_lines_in_declarations = 2 +ij_groovy_keep_control_statement_in_one_line = true +ij_groovy_keep_first_column_comment = true +ij_groovy_keep_indents_on_empty_lines = false +ij_groovy_keep_line_breaks = true +ij_groovy_keep_multiple_expressions_in_one_line = false +ij_groovy_keep_simple_blocks_in_one_line = false +ij_groovy_keep_simple_classes_in_one_line = true +ij_groovy_keep_simple_lambdas_in_one_line = true +ij_groovy_keep_simple_methods_in_one_line = true +ij_groovy_label_indent_absolute = false +ij_groovy_label_indent_size = 0 +ij_groovy_lambda_brace_style = end_of_line +ij_groovy_layout_static_imports_separately = true +ij_groovy_line_comment_add_space = false +ij_groovy_line_comment_add_space_on_reformat = false +ij_groovy_line_comment_at_first_column = true +ij_groovy_method_annotation_wrap = split_into_lines +ij_groovy_method_brace_style = end_of_line +ij_groovy_method_call_chain_wrap = off +ij_groovy_method_parameters_new_line_after_left_paren = false +ij_groovy_method_parameters_right_paren_on_new_line = false +ij_groovy_method_parameters_wrap = off +ij_groovy_modifier_list_wrap = false +ij_groovy_names_count_to_use_import_on_demand = 3 +ij_groovy_packages_to_use_import_on_demand = java.awt.*,javax.swing.* +ij_groovy_parameter_annotation_wrap = off +ij_groovy_parentheses_expression_new_line_after_left_paren = false +ij_groovy_parentheses_expression_right_paren_on_new_line = false +ij_groovy_prefer_parameters_wrap = false +ij_groovy_resource_list_new_line_after_left_paren = false +ij_groovy_resource_list_right_paren_on_new_line = false +ij_groovy_resource_list_wrap = off +ij_groovy_space_after_assert_separator = true +ij_groovy_space_after_colon = true +ij_groovy_space_after_comma = true +ij_groovy_space_after_comma_in_type_arguments = true +ij_groovy_space_after_for_semicolon = true +ij_groovy_space_after_quest = true +ij_groovy_space_after_type_cast = true +ij_groovy_space_before_annotation_parameter_list = false +ij_groovy_space_before_array_initializer_left_brace = false +ij_groovy_space_before_assert_separator = false +ij_groovy_space_before_catch_keyword = true +ij_groovy_space_before_catch_left_brace = true +ij_groovy_space_before_catch_parentheses = true +ij_groovy_space_before_class_left_brace = true +ij_groovy_space_before_closure_left_brace = true +ij_groovy_space_before_colon = true +ij_groovy_space_before_comma = false +ij_groovy_space_before_do_left_brace = true +ij_groovy_space_before_else_keyword = true +ij_groovy_space_before_else_left_brace = true +ij_groovy_space_before_finally_keyword = true +ij_groovy_space_before_finally_left_brace = true +ij_groovy_space_before_for_left_brace = true +ij_groovy_space_before_for_parentheses = true +ij_groovy_space_before_for_semicolon = false +ij_groovy_space_before_if_left_brace = true +ij_groovy_space_before_if_parentheses = true +ij_groovy_space_before_method_call_parentheses = false +ij_groovy_space_before_method_left_brace = true +ij_groovy_space_before_method_parentheses = false +ij_groovy_space_before_quest = true +ij_groovy_space_before_record_parentheses = false +ij_groovy_space_before_switch_left_brace = true +ij_groovy_space_before_switch_parentheses = true +ij_groovy_space_before_synchronized_left_brace = true +ij_groovy_space_before_synchronized_parentheses = true +ij_groovy_space_before_try_left_brace = true +ij_groovy_space_before_try_parentheses = true +ij_groovy_space_before_while_keyword = true +ij_groovy_space_before_while_left_brace = true +ij_groovy_space_before_while_parentheses = true +ij_groovy_space_in_named_argument = true +ij_groovy_space_in_named_argument_before_colon = false +ij_groovy_space_within_empty_array_initializer_braces = false +ij_groovy_space_within_empty_method_call_parentheses = false +ij_groovy_spaces_around_additive_operators = true +ij_groovy_spaces_around_assignment_operators = true +ij_groovy_spaces_around_bitwise_operators = true +ij_groovy_spaces_around_equality_operators = true +ij_groovy_spaces_around_lambda_arrow = true +ij_groovy_spaces_around_logical_operators = true +ij_groovy_spaces_around_multiplicative_operators = true +ij_groovy_spaces_around_regex_operators = true +ij_groovy_spaces_around_relational_operators = true +ij_groovy_spaces_around_shift_operators = true +ij_groovy_spaces_within_annotation_parentheses = false +ij_groovy_spaces_within_array_initializer_braces = false +ij_groovy_spaces_within_braces = true +ij_groovy_spaces_within_brackets = false +ij_groovy_spaces_within_cast_parentheses = false +ij_groovy_spaces_within_catch_parentheses = false +ij_groovy_spaces_within_for_parentheses = false +ij_groovy_spaces_within_gstring_injection_braces = false +ij_groovy_spaces_within_if_parentheses = false +ij_groovy_spaces_within_list_or_map = false +ij_groovy_spaces_within_method_call_parentheses = false +ij_groovy_spaces_within_method_parentheses = false +ij_groovy_spaces_within_parentheses = false +ij_groovy_spaces_within_switch_parentheses = false +ij_groovy_spaces_within_synchronized_parentheses = false +ij_groovy_spaces_within_try_parentheses = false +ij_groovy_spaces_within_tuple_expression = false +ij_groovy_spaces_within_while_parentheses = false +ij_groovy_special_else_if_treatment = true +ij_groovy_ternary_operation_wrap = off +ij_groovy_throws_keyword_wrap = off +ij_groovy_throws_list_wrap = off +ij_groovy_use_flying_geese_braces = false +ij_groovy_use_fq_class_names = false +ij_groovy_use_fq_class_names_in_javadoc = true +ij_groovy_use_relative_indents = false +ij_groovy_use_single_class_imports = true +ij_groovy_variable_annotation_wrap = off +ij_groovy_while_brace_force = never +ij_groovy_while_on_new_line = false +ij_groovy_wrap_chain_calls_after_dot = false +ij_groovy_wrap_long_lines = false + +[{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.prettierrc,.stylelintrc,bowerrc,composer.lock,jest.config}] indent_size = 2 ij_json_array_wrapping = split_into_lines ij_json_keep_blank_lines_in_code = 0 @@ -963,7 +1205,7 @@ ij_json_spaces_within_braces = false ij_json_spaces_within_brackets = false ij_json_wrap_long_lines = false -[{*.htm,*.html,*.sht,*.shtm,*.shtml}] +[{*.htm,*.html,*.ng,*.sht,*.shtm,*.shtml}] ij_html_add_new_line_before_tags = body,div,p,form,h1,h2,h3 ij_html_align_attributes = true ij_html_align_text = false @@ -1101,6 +1343,20 @@ ij_markdown_min_lines_between_paragraphs = 1 ij_markdown_wrap_text_if_long = true ij_markdown_wrap_text_inside_blockquotes = true +[{*.pb,*.textproto}] +indent_size = 2 +tab_width = 2 +ij_continuation_indent_size = 4 +ij_prototext_keep_blank_lines_in_code = 2 +ij_prototext_keep_indents_on_empty_lines = false +ij_prototext_keep_line_breaks = true +ij_prototext_space_after_colon = true +ij_prototext_space_after_comma = true +ij_prototext_space_before_colon = false +ij_prototext_space_before_comma = false +ij_prototext_spaces_within_braces = true +ij_prototext_spaces_within_brackets = false + [{*.properties,spring.handlers,spring.schemas}] ij_properties_align_group_field_declarations = false ij_properties_keep_blank_lines = false diff --git a/README.md b/README.md index d47af883..fa22bf5d 100644 --- a/README.md +++ b/README.md @@ -28,6 +28,8 @@ Features include: Please prefer the Java 11 version as the Java 8 will become obsolete in the future. +You can find our [Developer Guide here](https://developer.matomo.org/api-reference/tracking-java) + Further information on Matomo and Matomo HTTP tracking: * [Matomo PHP Tracker](https://github.com/matomo-org/matomo-php-tracker) @@ -71,12 +73,14 @@ dependencies. Release notes can be found here: https://github.com/matomo-org/mat Here are the most important changes: -* Matomo Java Tracker 3.x is compatible with Matomo 4 and 5 +* Matomo Java Tracker 3.2.0 is compatible with Matomo 4 and 5 * less dependencies * new dimension parameter * special types allow to provide valid parameters now * a new implementation for Java 11 uses the HttpClient available since Java 11 +See also the [Developer Guide here](https://developer.matomo.org/api-reference/tracking-java) + ## Javadoc The Javadoc for all versions can be found @@ -85,6 +89,7 @@ The Javadoc for all versions can be found ## Need help? +* Check the [Developer Guide](https://developer.matomo.org/api-reference/tracking-java) * Open an issue in the [Issue Tracker](https://github.com/matomo-org/matomo-java-tracker/issues) * Use [our GitHub discussions](https://github.com/matomo-org/matomo-java-tracker/discussions) * Ask your question on [Stackoverflow with the tag `matomo`](https://stackoverflow.com/questions/tagged/matomo) @@ -106,7 +111,7 @@ Add a dependency on Matomo Java Tracker using Maven. For Java 8: org.piwik.java.tracking matomo-java-tracker - 3.0.0 + 3.2.0 ``` @@ -116,7 +121,7 @@ For Java 11: org.piwik.java.tracking matomo-java-tracker-java11 - 3.0.0 + 3.2.0 ``` @@ -124,7 +129,7 @@ or Gradle (Java 8): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0") + implementation("org.piwik.java.tracking:matomo-java-tracker:3.2.0") } ``` @@ -132,20 +137,20 @@ or Gradle (Java 11): ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.0.0") + implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.2.0") } ``` or Gradle with Kotlin DSL (Java 8) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker:3.0.0") +implementation("org.piwik.java.tracking:matomo-java-tracker:3.2.0") ``` or Gradle with Kotlin DSL (Java 11) ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.0.0") +implementation("org.piwik.java.tracking:matomo-java-tracker-java11:3.2.0") ``` ### Spring Boot Module @@ -158,7 +163,7 @@ and allows you to configure the tracker via application properties. Add the foll org.piwik.java.tracking matomo-java-tracker-spring-boot-starter - 3.0.0 + 3.2.0 ``` @@ -166,14 +171,14 @@ or Gradle: ```groovy dependencies { - implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.0.0") + implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.2.0") } ``` or Gradle with Kotlin DSL ```kotlin -implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.0.0") +implementation("org.piwik.java.tracking:matomo-java-tracker-spring-boot-starter:3.2.0") ``` The following properties are supported: @@ -272,6 +277,49 @@ the site with id 1. The request will be sent asynchronously, that means the meth application will not wait for the response of the Matomo server. In the configuration we set the default site id to 1 and configure the default auth token. With `logFailedTracking` we enable logging of failed tracking requests. +If you want to perform an operation after a successful asynchronous call to Matomo, you can use the completable future +result like this: + +```java + +import java.net.URI; +import org.matomo.java.tracking.MatomoRequests; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.parameters.VisitorId; + +public class ConsumerExample { + + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoTracker tracker = new MatomoTracker(configuration); + + MatomoRequest request = MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build(); + + tracker.sendRequestAsync(request) + .thenAccept(req -> System.out.printf("Sent request %s%n", req)) + .exceptionally(throwable -> { + System.err.printf("Failed to send request: %s%n", throwable.getMessage()); + return null; + }); + + } + +} + +``` + If you have multiple requests to wish to track, it may be more efficient to send them in a single HTTP call. To do this, send a bulk request. Place your requests in an _Iterable_ data structure and call @@ -329,6 +377,64 @@ Per default every request has the following default parameters: Overwrite these properties as desired. We strongly recommend your to determine the visitor id for every user using a unique identifier, e.g. an email address. If you do not provide a visitor id, a random visitor id will be generated. +Ecommerce requests contain ecommerce items, that can be fluently build: + +```java + +import java.net.URI; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequests; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.parameters.EcommerceItem; +import org.matomo.java.tracking.parameters.EcommerceItems; +import org.matomo.java.tracking.parameters.VisitorId; + +public class EcommerceExample { + + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoTracker tracker = new MatomoTracker(configuration); + + tracker.sendBulkRequestAsync(MatomoRequests + .ecommerceCartUpdate(50.0) + .ecommerceItems(EcommerceItems + .builder() + .item(EcommerceItem + .builder() + .sku("XYZ12345") + .name("Matomo - The big book about web analytics") + .category("Education & Teaching") + .price(23.1) + .quantity(2) + .build()) + .item(EcommerceItem + .builder() + .sku("B0C2WV3MRJ") + .name("Matomo for data visualization") + .category("Education & Teaching") + .price(15.0) + .quantity(1) + .build()) + .build()) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + + } + +} + +``` + Note that if you want to be able to track campaigns using *Referrers > Campaigns*, you must add the correct URL parameters to your actionUrl. See [Tracking Campaigns](https://matomo.org/docs/tracking-campaigns/) for more information. All HTTP query parameters denoted on the [Matomo Tracking HTTP API](https://developer.matomo.org/api-reference/tracking-api) can be set using the appropriate getters and setters. See @@ -517,7 +623,7 @@ version can be used in your local Maven repository for testing purposes, e.g. org.piwik.java.tracking matomo-java-tracker - 3.0.0-rc2-SNAPSHOT + 3.2.1-SNAPSHOT ``` @@ -608,6 +714,7 @@ provide tests for your changes. We use JUnit 5 for testing. Coverage should be a ## Other Java Matomo Tracker Implementations +* [The original Piwik Java Tracker Implementation](https://github.com/summitsystemsinc/piwik-java-tracking) * [Matomo SDK for Android](https://github.com/matomo-org/matomo-sdk-android) * [Piwik SDK Android]( https://github.com/lkogler/piwik-sdk-android) * [piwik-tracking](https://github.com/ralscha/piwik-tracking) diff --git a/core/pom.xml b/core/pom.xml index dedb5618..e05e2923 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.1-SNAPSHOT ../pom.xml diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java index 620ce4fe..ea2ddbfc 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequest.java @@ -7,16 +7,10 @@ package org.matomo.java.tracking; -import static java.util.Collections.singleton; - import edu.umd.cs.findbugs.annotations.Nullable; import java.nio.charset.Charset; import java.time.Instant; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.UUID; import lombok.AllArgsConstructor; @@ -41,7 +35,7 @@ /** * A class that implements the - * Matomo Tracking HTTP API. These requests can be sent using {@link MatomoTracker}. + * Matomo Tracking HTTP API. These requests can be sent using {@link MatomoTracker}. * * @author brettcsorba */ @@ -55,7 +49,7 @@ public class MatomoRequest { /** * The ID of the website we're tracking a visit/action for. Only needed, if no default site id is - * configured + * configured. */ @TrackingParameter(name = "rec") @Default @@ -63,9 +57,12 @@ public class MatomoRequest { /** * The ID of the website we're tracking a visit/action for. Only needed, if no default site id is - * configured + * configured. */ - @TrackingParameter(name = "idsite") + @TrackingParameter( + name = "idsite", + min = 1 + ) private Integer siteId; /** @@ -130,7 +127,7 @@ public class MatomoRequest { * database). Then you would manually increment the counts by one on each new visit or "session", * depending on how you choose to define a visit. */ - @TrackingParameter(name = "_idvc") + @TrackingParameter(name = "_idvc", min = 0) private Integer visitorVisitCount; /** @@ -156,9 +153,9 @@ public class MatomoRequest { /** * The campaign keyword (see * Tracking Campaigns). Used to - * populate - * the Referrers > Campaigns report (clicking on a campaign loads all keywords for - * this campaign). This parameter will only be used for the first pageview of a visit. + * populate the Referrers > Campaigns report (clicking on a campaign loads all + * keywords for this campaign). This parameter will only be used for the first pageview of a + * visit. */ @TrackingParameter(name = "_rck") private String campaignKeyword; @@ -172,19 +169,31 @@ public class MatomoRequest { /** * The current hour (local time). */ - @TrackingParameter(name = "h") + @TrackingParameter( + name = "h", + min = 0, + max = 23 + ) private Integer currentHour; /** * The current minute (local time). */ - @TrackingParameter(name = "m") + @TrackingParameter( + name = "m", + min = 0, + max = 59 + ) private Integer currentMinute; /** * The current second (local time). */ - @TrackingParameter(name = "s") + @TrackingParameter( + name = "s", + min = 0, + max = 59 + ) private Integer currentSecond; /** @@ -321,7 +330,10 @@ public class MatomoRequest { * results displayed on the results page. When keywords are tracked with &search_count=0 they will * appear in the "No Result Search Keyword" report. */ - @TrackingParameter(name = "search_count") + @TrackingParameter( + name = "search_count", + min = 0 + ) private Long searchResultsCount; /** @@ -335,9 +347,9 @@ public class MatomoRequest { /** * If specified, the tracking request will trigger a conversion for the goal of the website being - * tracked with this ID. + * tracked with this ID. The value 0 tracks an ecommerce interaction. */ - @TrackingParameter(name = "idgoal") + @TrackingParameter(name = "idgoal", min = 0) private Integer goalId; /** @@ -365,39 +377,39 @@ public class MatomoRequest { /** * How long it took to connect to server. */ - @TrackingParameter(name = "pf_net") + @TrackingParameter(name = "pf_net", min = 0) private Long networkTime; /** * How long it took the server to generate page. */ - @TrackingParameter(name = "pf_srv") + @TrackingParameter(name = "pf_srv", min = 0) private Long serverTime; /** * How long it takes the browser to download the response from the server. */ - @TrackingParameter(name = "pf_tfr") + @TrackingParameter(name = "pf_tfr", min = 0) private Long transferTime; /** * How long the browser spends loading the webpage after the response was fully received until the * user can start interacting with it. */ - @TrackingParameter(name = "pf_dm1") + @TrackingParameter(name = "pf_dm1", min = 0) private Long domProcessingTime; /** * How long it takes for the browser to load media and execute any Javascript code listening for * the DOMContentLoaded event. */ - @TrackingParameter(name = "pf_dm2") + @TrackingParameter(name = "pf_dm2", min = 0) private Long domCompletionTime; /** * How long it takes the browser to execute Javascript code waiting for the window.load event. */ - @TrackingParameter(name = "pf_onl") + @TrackingParameter(name = "pf_onl", min = 0) private Long onloadTime; /** @@ -450,8 +462,7 @@ public class MatomoRequest { /** * The unique string identifier for the ecommerce order (required when tracking an ecommerce - * order). you must set &idgoal=0 in the request to track an ecommerce interaction: cart update or - * an ecommerce order. + * order). */ @TrackingParameter(name = "ec_id") private String ecommerceId; @@ -546,13 +557,13 @@ public class MatomoRequest { /** * An override value for the visitor's latitude, eg 22.456. */ - @TrackingParameter(name = "lat") + @TrackingParameter(name = "lat", min = -90, max = 90) private Double visitorLatitude; /** * An override value for the visitor's longitude, eg 22.456. */ - @TrackingParameter(name = "long") + @TrackingParameter(name = "long", min = -180, max = 180) private Double visitorLongitude; /** @@ -670,7 +681,7 @@ public class MatomoRequest { * *

Optional for crash analytics */ - @TrackingParameter(name = "cra_rl") + @TrackingParameter(name = "cra_rl", min = 0) private Integer crashLine; /** @@ -678,15 +689,14 @@ public class MatomoRequest { * *

Optional for crash analytics */ - @TrackingParameter(name = "cra_rc") + @TrackingParameter(name = "cra_rc", min = 0) private Integer crashColumn; /** * The Matomo session ID sent as a cookie {@code MATOMO_SESSID}. * *

If not null a cookie with the name {@code MATOMO_SESSID} will be sent with the value of - * this - * parameter. + * this parameter. */ private String sessionId; @@ -706,7 +716,7 @@ public class MatomoRequest { *

For example, you can use this to set the Accept-Language header, or to set the * Content-Type. */ - private Map> additionalParameters; + private Map additionalParameters; /** * You can set additional HTTP headers for the request sent to Matomo. @@ -760,17 +770,14 @@ public MatomoRequest(int siteId, String actionUrl) { * * @param key the key of the parameter whose list of objects to get. Cannot be null * - * @return the list of objects currently stored at the specified key + * @return the parameter at the specified key, null if nothing at this key */ - public List getCustomTrackingParameter(@NonNull String key) { + @Nullable + public Object getCustomTrackingParameter(@NonNull String key) { if (additionalParameters == null || additionalParameters.isEmpty()) { - return Collections.emptyList(); - } - Collection parameterValues = additionalParameters.get(key); - if (parameterValues == null || parameterValues.isEmpty()) { - return Collections.emptyList(); + return null; } - return Collections.unmodifiableList(new ArrayList<>(parameterValues)); + return additionalParameters.get(key); } /** @@ -797,28 +804,25 @@ public void setCustomTrackingParameter( if (additionalParameters == null) { additionalParameters = new LinkedHashMap<>(); } - Collection values = additionalParameters.computeIfAbsent(key, k -> new ArrayList<>()); - values.clear(); - values.add(value); + additionalParameters.put(key, value); } } /** - * Add a custom tracking parameter to the specified key. This allows users to have multiple - * parameters with the same name and different values, commonly used during situations where list - * parameters are needed + * Add a custom tracking parameter to the specified key. If there is already a parameter at this + * key, the new value replaces the old value. * * @param key the parameter's key. Cannot be null - * @param value the parameter's value. Cannot be null + * @param value the parameter's value. May be null * * @deprecated Use {@link MatomoRequest.MatomoRequestBuilder#additionalParameters(Map)} instead. */ @Deprecated - public void addCustomTrackingParameter(@NonNull String key, @NonNull Object value) { + public void addCustomTrackingParameter(@NonNull String key, @Nullable Object value) { if (additionalParameters == null) { additionalParameters = new LinkedHashMap<>(); } - additionalParameters.computeIfAbsent(key, k -> new ArrayList<>()).add(value); + additionalParameters.put(key, value); } /** @@ -1133,7 +1137,7 @@ public void setParameter(@NonNull String parameterName, Object value) { if (value == null) { additionalParameters.remove(parameterName); } else { - additionalParameters.put(parameterName, singleton(value)); + additionalParameters.put(parameterName, value); } } diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java b/core/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java index 1dd8f9db..01c43cd2 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoRequestBuilder.java @@ -1,13 +1,7 @@ package org.matomo.java.tracking; -import static java.util.Collections.singleton; - import edu.umd.cs.findbugs.annotations.Nullable; -import java.util.ArrayList; -import java.util.Collection; import java.util.Map; -import java.util.stream.Collectors; -import lombok.NonNull; import org.matomo.java.tracking.parameters.AcceptLanguage; /** @@ -48,22 +42,8 @@ public MatomoRequestBuilder headerAcceptLanguage(@Nullable String headerAcceptLa */ @Deprecated public MatomoRequestBuilder customTrackingParameters(@Nullable Map parameters) { - if (parameters == null || parameters.isEmpty()) { - additionalParameters(null); - } else { - additionalParameters(parameters.entrySet().stream().collect(Collectors.toMap( - e -> e.getKey(), - e -> toCollection(e) - ))); - } + additionalParameters(parameters); return this; } - private static Collection toCollection(@NonNull Map.Entry e) { - if (e.getValue() instanceof Collection) { - return (Collection) e.getValue(); - } - return new ArrayList<>(singleton(e.getValue())); - } - } diff --git a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java index a06de566..51a585d5 100644 --- a/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java +++ b/core/src/main/java/org/matomo/java/tracking/MatomoTracker.java @@ -13,6 +13,7 @@ import java.util.Arrays; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; +import java.util.function.Function; import lombok.AccessLevel; import lombok.NonNull; import lombok.Setter; @@ -141,7 +142,7 @@ public MatomoTracker( public void sendRequest(@NonNull MatomoRequest request) { if (trackerConfiguration.isEnabled()) { log.debug("Sending request via GET: {}", request); - validate(request); + applyGoalIdAndCheckSiteId(request); initializeSender(); sender.sendSingle(request); } else { @@ -163,12 +164,12 @@ private void initializeSender() { * {@link #sendRequest(MatomoRequest)} or {@link #sendBulkRequest(Iterable)} instead. * * @param request request to send - * @return completable future to let you know when the request is done + * @return completable future to let you know when the request is done. Contains the request. */ - public CompletableFuture sendRequestAsync( + public CompletableFuture sendRequestAsync( @NonNull MatomoRequest request ) { - return sendRequestAsync(request, null); + return sendRequestAsync(request, Function.identity()); } /** @@ -179,34 +180,39 @@ public CompletableFuture sendRequestAsync( * {@link #sendRequest(MatomoRequest)} or {@link #sendBulkRequest(Iterable)} instead. * * @param request request to send - * @param callback callback that gets executed when response arrives, null allowed - * @return a completable future to let you know when the request is done. The future contains either the request (if - * no callback is specified) or null (if a callback is specified) + * @param callback callback that gets executed when response arrives, must not be null + * @return a completable future to let you know when the request is done. The future contains + * the callback result. * @deprecated Please use {@link MatomoTracker#sendRequestAsync(MatomoRequest)} in combination with * {@link CompletableFuture#thenAccept(Consumer)} instead */ @Deprecated - public CompletableFuture sendRequestAsync( + public CompletableFuture sendRequestAsync( @NonNull MatomoRequest request, - @Nullable Consumer callback + @NonNull Function callback ) { if (trackerConfiguration.isEnabled()) { - validate(request); + applyGoalIdAndCheckSiteId(request); log.debug("Sending async request via GET: {}", request); initializeSender(); CompletableFuture future = sender.sendSingleAsync(request); - if (callback != null) { - return future.thenAccept(callback); - } - return future; + return future.thenApply(callback); } log.warn("Not sending request, because tracker is disabled"); return CompletableFuture.completedFuture(null); } - private void validate( + private void applyGoalIdAndCheckSiteId( @NonNull MatomoRequest request ) { + if (request.getGoalId() == null && ( + request.getEcommerceId() != null || request.getEcommerceRevenue() != null + || request.getEcommerceDiscount() != null || request.getEcommerceItems() != null + || request.getEcommerceLastOrderTimestamp() != null + || request.getEcommerceShippingCost() != null || request.getEcommerceSubtotal() != null + || request.getEcommerceTax() != null)) { + request.setGoalId(0); + } if (trackerConfiguration.getDefaultSiteId() == null && request.getSiteId() == null) { throw new IllegalArgumentException("No default site ID and no request site ID is given"); } @@ -258,7 +264,7 @@ public void sendBulkRequest( ) { if (trackerConfiguration.isEnabled()) { for (MatomoRequest request : requests) { - validate(request); + applyGoalIdAndCheckSiteId(request); } log.debug("Sending requests via POST: {}", requests); initializeSender(); @@ -313,7 +319,7 @@ public CompletableFuture sendBulkRequestAsync( ) { if (trackerConfiguration.isEnabled()) { for (MatomoRequest request : requests) { - validate(request); + applyGoalIdAndCheckSiteId(request); } log.debug("Sending async requests via POST: {}", requests); initializeSender(); diff --git a/core/src/main/java/org/matomo/java/tracking/QueryCreator.java b/core/src/main/java/org/matomo/java/tracking/QueryCreator.java index 55211fea..63a2b9d2 100644 --- a/core/src/main/java/org/matomo/java/tracking/QueryCreator.java +++ b/core/src/main/java/org/matomo/java/tracking/QueryCreator.java @@ -28,7 +28,8 @@ @RequiredArgsConstructor class QueryCreator { - private static final TrackingParameterMethod[] TRACKING_PARAMETER_METHODS = initializeTrackingParameterMethods(); + private static final TrackingParameterMethod[] TRACKING_PARAMETER_METHODS = + initializeTrackingParameterMethods(); private final TrackerConfiguration trackerConfiguration; @@ -44,15 +45,20 @@ private static TrackingParameterMethod[] initializeTrackingParameterMethods() { } private static void addMethods( - Collection methods, Member member, TrackingParameter trackingParameter + Collection methods, + Member member, + TrackingParameter trackingParameter ) { try { - for (PropertyDescriptor pd : Introspector.getBeanInfo(MatomoRequest.class).getPropertyDescriptors()) { + for (PropertyDescriptor pd : Introspector.getBeanInfo(MatomoRequest.class) + .getPropertyDescriptors()) { if (member.getName().equals(pd.getName())) { String regex = trackingParameter.regex(); methods.add(TrackingParameterMethod .builder() .parameterName(trackingParameter.name()) + .min(trackingParameter.min()) + .max(trackingParameter.max()) .maxLength(trackingParameter.maxLength()) .method(pd.getReadMethod()) .pattern(regex == null || regex.isEmpty() || regex.trim().isEmpty() ? null : @@ -84,12 +90,11 @@ String createQuery( appendParameter(method, request, query); } if (request.getAdditionalParameters() != null) { - for (Entry> entry : request.getAdditionalParameters().entrySet()) { - for (Object value : entry.getValue()) { - if (value != null && !value.toString().trim().isEmpty()) { - appendAmpersand(query); - query.append(encode(entry.getKey())).append('=').append(encode(value.toString())); - } + for (Entry entry : request.getAdditionalParameters().entrySet()) { + Object value = entry.getValue(); + if (value != null && !value.toString().trim().isEmpty()) { + appendAmpersand(query); + query.append(encode(entry.getKey())).append('=').append(encode(value.toString())); } } } @@ -97,7 +102,10 @@ String createQuery( for (Entry entry : request.getDimensions().entrySet()) { if (entry.getKey() != null && entry.getValue() != null) { appendAmpersand(query); - query.append("dimension").append(entry.getKey()).append('=').append(encode(entry.getValue().toString())); + query.append("dimension") + .append(entry.getKey()) + .append('=') + .append(encode(entry.getValue().toString())); } } } diff --git a/core/src/main/java/org/matomo/java/tracking/RequestValidator.java b/core/src/main/java/org/matomo/java/tracking/RequestValidator.java index a9233c4e..f604e1c5 100644 --- a/core/src/main/java/org/matomo/java/tracking/RequestValidator.java +++ b/core/src/main/java/org/matomo/java/tracking/RequestValidator.java @@ -25,26 +25,16 @@ static void validate( @Nullable CharSequence authToken ) { - if (request.getSiteId() != null && request.getSiteId() < 0) { - throw new IllegalArgumentException("Site ID must not be negative"); - } - if (request.getGoalId() == null && ( - request.getEcommerceId() != null || request.getEcommerceRevenue() != null - || request.getEcommerceDiscount() != null || request.getEcommerceItems() != null - || request.getEcommerceLastOrderTimestamp() != null - || request.getEcommerceShippingCost() != null || request.getEcommerceSubtotal() != null - || request.getEcommerceTax() != null)) { - throw new MatomoException("Goal ID must be set if ecommerce parameters are used"); - } + if (request.getSearchResultsCount() != null && request.getSearchQuery() == null) { throw new MatomoException("Search query must be set if search results count is set"); } if (authToken == null) { if (request.getVisitorLongitude() != null || request.getVisitorLatitude() != null || request.getVisitorRegion() != null || request.getVisitorCity() != null - || request.getVisitorCountry() != null) { + || request.getVisitorCountry() != null || request.getVisitorIp() != null) { throw new MatomoException( - "Auth token must be present if longitude, latitude, region, city or country are set"); + "Auth token must be present if visitor longitude, latitude, region, city, country or IP are set"); } if (request.getRequestTimestamp() != null && request .getRequestTimestamp() diff --git a/core/src/main/java/org/matomo/java/tracking/TrackingParameter.java b/core/src/main/java/org/matomo/java/tracking/TrackingParameter.java index f94e226e..0b9d82bc 100644 --- a/core/src/main/java/org/matomo/java/tracking/TrackingParameter.java +++ b/core/src/main/java/org/matomo/java/tracking/TrackingParameter.java @@ -20,6 +20,10 @@ String regex() default ""; + double min() default Double.MIN_VALUE; + + double max() default Double.MAX_VALUE; + int maxLength() default Integer.MAX_VALUE; } diff --git a/core/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java b/core/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java index bbfca14c..2540ea0c 100644 --- a/core/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java +++ b/core/src/main/java/org/matomo/java/tracking/TrackingParameterMethod.java @@ -23,6 +23,10 @@ class TrackingParameterMethod { Pattern pattern; + double min; + + double max; + int maxLength; void validateParameterValue(@NonNull Object parameterValue) { @@ -34,12 +38,30 @@ void validateParameterValue(@NonNull Object parameterValue) { pattern )); } - if (parameterValue.toString().length() > maxLength) { + if (maxLength != 0 && parameterValue.toString().length() > maxLength) { throw new MatomoException(String.format("Invalid value for %s. Must be less or equal than %d characters", parameterName, maxLength )); } + if (parameterValue instanceof Number) { + Number number = (Number) parameterValue; + if (number.doubleValue() < min) { + throw new MatomoException(String.format( + "Invalid value for %s. Must be greater or equal than %s", + parameterName, + min % 1 == 0 ? Long.toString((long) min) : min + )); + + } + if (number.doubleValue() > max) { + throw new MatomoException(String.format( + "Invalid value for %s. Must be less or equal than %s", + parameterName, + max % 1 == 0 ? Long.toString((long) max) : max + )); + } + } } } diff --git a/core/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java b/core/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java index 18eae8cb..87dab6d5 100644 --- a/core/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java +++ b/core/src/test/java/org/matomo/java/tracking/MatomoRequestBuilderTest.java @@ -1,6 +1,5 @@ package org.matomo.java.tracking; -import static java.util.Collections.singleton; import static java.util.Collections.singletonMap; import static org.assertj.core.api.Assertions.assertThat; @@ -24,7 +23,7 @@ void buildsRequest() { .referrerUrl("https://referrer.com") .additionalParameters(singletonMap( "trackingParameterName", - singleton("trackingParameterValue") + "trackingParameterValue" )) .pageCustomVariables(new CustomVariables().add(pageCustomVariable, 2)) .visitCustomVariables(new CustomVariables().add(visitCustomVariable, 3)) @@ -41,7 +40,7 @@ void buildsRequest() { assertThat(matomoRequest.getResponseAsImage()).isFalse(); assertThat(matomoRequest.getRequired()).isTrue(); assertThat(matomoRequest.getReferrerUrl()).isEqualTo("https://referrer.com"); - assertThat(matomoRequest.getCustomTrackingParameter("trackingParameterName")).containsExactly( + assertThat(matomoRequest.getCustomTrackingParameter("trackingParameterName")).isEqualTo( "trackingParameterValue"); assertThat(matomoRequest.getPageCustomVariables()).hasToString( "{\"2\":[\"pageCustomVariableName\",\"pageCustomVariableValue\"]}"); @@ -61,20 +60,20 @@ void setCustomTrackingParameters() { .referrerUrl("https://referrer.com") .build(); - assertThat(matomoRequest.getCustomTrackingParameter("foo")).containsExactly("bar"); + assertThat(matomoRequest.getCustomTrackingParameter("foo")).isEqualTo("bar"); } @Test void setCustomTrackingParametersWithCollectopm() { MatomoRequest matomoRequest = new MatomoRequestBuilder() - .customTrackingParameters(singletonMap("foo", singleton("bar"))) + .customTrackingParameters(singletonMap("foo", "bar")) .siteId(42) .actionName("ACTION_NAME") .actionUrl("https://www.your-domain.tld/some/page?query=foo") .referrerUrl("https://referrer.com") .build(); - assertThat(matomoRequest.getCustomTrackingParameter("foo")).containsExactly("bar"); + assertThat(matomoRequest.getCustomTrackingParameter("foo")).isEqualTo("bar"); } @Test diff --git a/core/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java b/core/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java index 65e83def..b4ad0e04 100644 --- a/core/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java +++ b/core/src/test/java/org/matomo/java/tracking/MatomoRequestTest.java @@ -14,9 +14,9 @@ void returnsEmptyListWhenCustomTrackingParametersDoesNotContainKey() { request.setCustomTrackingParameter("foo", "bar"); - assertThat(request.getCustomTrackingParameter("baz")).isEmpty(); + assertThat(request.getCustomTrackingParameter("baz")).isNull(); assertThat(request.getAdditionalParameters()).isNotEmpty(); - assertThat(request.getCustomTrackingParameter("foo")).isNotEmpty(); + assertThat(request.getCustomTrackingParameter("foo")).isEqualTo("bar"); } @Test @@ -95,7 +95,7 @@ void setVisitCustomVariableInitializesVisitCustomVariablesIfCustomVariableParame @Test void setsCustomParameter() { request.setParameter("foo", 1); - assertThat(request.getCustomTrackingParameter("foo")).contains(1); + assertThat(request.getCustomTrackingParameter("foo")).isEqualTo(1); } @Test diff --git a/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index 51c361b2..c312c717 100644 --- a/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/core/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -106,6 +106,7 @@ void sendsRequestAsyncWithCallback() { matomoTracker.sendRequestAsync(request, request -> { assertThat(request).isEqualTo(request); callbackCalled.set(true); + return null; }); TestSender testSender = senderFactory.getTestSender(); @@ -210,6 +211,21 @@ void sendsBulkRequestAsyncWithAuthToken() { } + @Test + void appliesGoalId() { + + matomoTracker = new MatomoTracker(HOST_URL); + matomoTracker.setSenderFactory(senderFactory); + request.setEcommerceId("some-id"); + + matomoTracker.sendRequest(request); + + TestSender testSender = senderFactory.getTestSender(); + thenContainsRequest(testSender, "rec=1&idsite=1&action_name=test&apiv=1&_id=00000000343efaf5&idgoal=0&ec_id=some-id&send_image=0&rand=test-random"); + assertThat(testSender.getTrackerConfiguration().getApiEndpoint()).hasToString(HOST_URL); + + } + private void thenContainsRequest(TestSender testSender, String query) { assertThat(testSender.getRequests()).containsExactly(request); assertThat(testSender.getQueries()).containsExactly(query); diff --git a/core/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java b/core/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java index 49432619..c88c7570 100644 --- a/core/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java +++ b/core/src/test/java/org/matomo/java/tracking/PiwikRequestTest.java @@ -10,7 +10,6 @@ import java.nio.charset.Charset; import java.time.Instant; import java.time.ZonedDateTime; -import java.util.List; import java.util.Locale; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -193,7 +192,7 @@ void testGetCustomTrackingParameter_T() { @Test void testGetCustomTrackingParameter_FT() { - assertThat(request.getCustomTrackingParameter("key")).isEmpty(); + assertThat(request.getCustomTrackingParameter("key")).isNull(); } @Test @@ -209,28 +208,26 @@ void testSetCustomTrackingParameter_T() { @Test void testSetCustomTrackingParameter1() { request.setCustomTrackingParameter("key", "value"); - List l = request.getCustomTrackingParameter("key"); - assertThat(l).hasSize(1); - assertThat(l.get(0)).isEqualTo("value"); + Object l = request.getCustomTrackingParameter("key"); + assertThat(l).isEqualTo("value"); request.setCustomTrackingParameter("key", "value2"); } @Test void testSetCustomTrackingParameter2() { request.setCustomTrackingParameter("key", "value2"); - List l = request.getCustomTrackingParameter("key"); - assertThat(l).hasSize(1); - assertThat(l.get(0)).isEqualTo("value2"); + Object l = request.getCustomTrackingParameter("key"); + assertThat(l).isEqualTo("value2"); request.setCustomTrackingParameter("key", null); l = request.getCustomTrackingParameter("key"); - assertThat(l).isEmpty(); + assertThat(l).isNull(); } @Test void testSetCustomTrackingParameter3() { request.setCustomTrackingParameter("key", null); - List l = request.getCustomTrackingParameter("key"); - assertThat(l).isEmpty(); + Object l = request.getCustomTrackingParameter("key"); + assertThat(l).isNull(); } @Test @@ -243,38 +240,27 @@ void testAddCustomTrackingParameter_T() { } } - @Test - void testAddCustomTrackingParameter_FT() { - try { - request.addCustomTrackingParameter("key", null); - fail("Exception should have been thrown."); - } catch (NullPointerException e) { - assertThat(e.getLocalizedMessage()).isEqualTo("value is marked non-null but is null"); - } - } - @Test void testAddCustomTrackingParameter1() { request.addCustomTrackingParameter("key", "value"); - List l = request.getCustomTrackingParameter("key"); - assertThat(l).hasSize(1); - assertThat(l.get(0)).isEqualTo("value"); + Object l = request.getCustomTrackingParameter("key"); + assertThat(l).isEqualTo("value"); } @Test void testAddCustomTrackingParameter2() { request.addCustomTrackingParameter("key", "value"); request.addCustomTrackingParameter("key", "value2"); - List l = request.getCustomTrackingParameter("key"); - assertThat(l).hasSize(2).contains(new String[] {"value"}).contains(new String[] {"value2"}); + Object l = request.getCustomTrackingParameter("key"); + assertThat(l).isEqualTo("value2"); } @Test void testClearCustomTrackingParameter() { request.setCustomTrackingParameter("key", "value"); request.clearCustomTrackingParameter(); - List l = request.getCustomTrackingParameter("key"); - assertThat(l).isEmpty(); + Object l = request.getCustomTrackingParameter("key"); + assertThat(l).isNull(); } /** diff --git a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java index 54d21373..1b1cecd4 100644 --- a/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java +++ b/core/src/test/java/org/matomo/java/tracking/QueryCreatorTest.java @@ -9,8 +9,6 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import java.time.Instant; -import java.util.Collection; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Locale.LanguageRange; @@ -30,7 +28,7 @@ class QueryCreatorTest { private final MatomoRequest.MatomoRequestBuilder matomoRequestBuilder = MatomoRequest - .builder() + .request() .visitorId(VisitorId.fromHash(1234567890123456789L)) .randomValue(RandomValue.fromString("random-value")); @@ -247,19 +245,19 @@ void testGetQueryString() { matomoRequestBuilder.additionalParameters(singletonMap("key", singleton("test"))); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=test"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=%5Btest%5D"); matomoRequestBuilder.additionalParameters(singletonMap("key", asList("test", "test2"))); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=test&key=test2"); - Map> customTrackingParameters = new HashMap<>(); - customTrackingParameters.put("key", asList("test", "test2")); - customTrackingParameters.put("key2", Collections.singletonList("test3")); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key=%5Btest%2C+test2%5D"); + Map customTrackingParameters = new HashMap<>(); + customTrackingParameters.put("key", "test2"); + customTrackingParameters.put("key2", "test3"); matomoRequestBuilder.additionalParameters(customTrackingParameters); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test&key=test2"); - customTrackingParameters.put("key", Collections.singletonList("test4")); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test2"); + customTrackingParameters.put("key", "test4"); whenCreatesQuery(); assertThat(query).isEqualTo( "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&cvar=%7B%227%22%3A%5B%22key%22%2C%22val%22%5D%7D&send_image=0&rand=random&key2=test3&key=test4"); @@ -301,24 +299,24 @@ void testGetUrlEncodedQueryString() { whenCreatesQuery(); assertThat(query).isEqualTo( "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random"); - Map> customTrackingParameters = new HashMap<>(); - customTrackingParameters.put("ke/y", Collections.singletonList("te:st")); + Map customTrackingParameters = new HashMap<>(); + customTrackingParameters.put("ke/y", "te:st"); matomoRequestBuilder.additionalParameters(customTrackingParameters); whenCreatesQuery(); assertThat(query).isEqualTo( "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast"); - customTrackingParameters.put("ke/y", asList("te:st", "te:st2")); + customTrackingParameters.put("ke/y", "te:st2"); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2"); - customTrackingParameters.put("ke/y2", Collections.singletonList("te:st3")); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast2"); + customTrackingParameters.put("ke/y2", "te:st3"); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3"); - customTrackingParameters.put("ke/y", asList("te:st3", "te:st4")); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast2&ke%2Fy2=te%3Ast3"); + customTrackingParameters.put("ke/y", "te:st3"); whenCreatesQuery(); assertThat(query).isEqualTo( - "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast3&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3"); + "rec=1&idsite=3&url=http%3A%2F%2Ftest.com&apiv=1&_id=1234567890123456&send_image=0&rand=random&ke%2Fy=te%3Ast3&ke%2Fy2=te%3Ast3"); matomoRequestBuilder .randomValue(null) .siteId(null) @@ -328,7 +326,7 @@ void testGetUrlEncodedQueryString() { .visitorId(null) .actionUrl(null); whenCreatesQuery(); - assertThat(query).isEqualTo("idsite=42&ke%2Fy=te%3Ast3&ke%2Fy=te%3Ast4&ke%2Fy2=te%3Ast3"); + assertThat(query).isEqualTo("idsite=42&ke%2Fy=te%3Ast3&ke%2Fy2=te%3Ast3"); } @@ -410,4 +408,134 @@ void appendsCharsetParameters() { "idsite=42&token_auth=876de1876fb2cda2816c362a61bfc712&rec=1&apiv=1&_id=112210f47de98115&cs=ISO-8859-1&send_image=0&rand=random-value"); } + @Test + void failsIfIdSiteIsNegative() { + matomoRequestBuilder.siteId(-1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for idsite. Must be greater or equal than 1"); + } + + @Test + void failsIfIdSiteIsZero() { + matomoRequestBuilder.siteId(0); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for idsite. Must be greater or equal than 1"); + } + + @Test + void failsIfCurrentHourIsNegative() { + matomoRequestBuilder.currentHour(-1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for h. Must be greater or equal than 0"); + } + + @Test + void failsIfCurrentHourIsGreaterThan23() { + matomoRequestBuilder.currentHour(24); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for h. Must be less or equal than 23"); + } + + @Test + void failsIfCurrentMinuteIsNegative() { + matomoRequestBuilder.currentMinute(-1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for m. Must be greater or equal than 0"); + } + + @Test + void failsIfCurrentMinuteIsGreaterThan59() { + matomoRequestBuilder.currentMinute(60); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for m. Must be less or equal than 59"); + } + + @Test + void failsIfCurrentSecondIsNegative() { + matomoRequestBuilder.currentSecond(-1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for s. Must be greater or equal than 0"); + } + + @Test + void failsIfCurrentSecondIsGreaterThan59() { + matomoRequestBuilder.currentSecond(60); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for s. Must be less or equal than 59"); + } + + @Test + void failsIfLatitudeIsLessThanMinus90() { + matomoRequestBuilder.visitorLatitude(-90.1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for lat. Must be greater or equal than -90"); + } + + @Test + void failsIfLatitudeIsGreaterThan90() { + matomoRequestBuilder.visitorLatitude(90.1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for lat. Must be less or equal than 90"); + } + + @Test + void failsIfLongitudeIsLessThanMinus180() { + matomoRequestBuilder.visitorLongitude(-180.1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseMessage("Invalid value for long. Must be greater or equal than -180"); + } + + @Test + void failsIfLongitudeIsGreaterThan180() { + matomoRequestBuilder.visitorLongitude(180.1); + + assertThatThrownBy(this::whenCreatesQuery) + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseMessage("Invalid value for long. Must be less or equal than 180"); + } + } diff --git a/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java b/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java index cdb8e90c..6d1ab348 100644 --- a/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java +++ b/core/src/test/java/org/matomo/java/tracking/RequestValidatorTest.java @@ -2,7 +2,6 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; -import java.time.Instant; import java.util.Locale; import org.junit.jupiter.api.Test; import org.piwik.java.tracking.PiwikDate; @@ -12,81 +11,6 @@ class RequestValidatorTest { private final MatomoRequest request = new MatomoRequest(); - @Test - void testEcommerceRevenue() { - - request.setEcommerceRevenue(20.0); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - - } - - @Test - void testEcommerceDiscount() { - request.setEcommerceDiscount(1.0); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - - } - - @Test - void testEcommerceId() { - request.setEcommerceId("1"); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - } - - @Test - void testEcommerceSubtotal() { - request.setEcommerceSubtotal(20.0); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - } - - @Test - void testEcommerceShippingCost() { - request.setEcommerceShippingCost(20.0); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - } - - @Test - void testEcommerceLastOrderTimestamp() { - request.setEcommerceLastOrderTimestamp(Instant.ofEpochSecond(1000L)); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - } - - @Test - void testEcommerceTax() { - request.setEcommerceTax(20.0); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - } - - @Test - void testEcommerceItemE() { - - request.addEcommerceItem(new EcommerceItem("sku", "name", "category", 1.0, 2)); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(MatomoException.class) - .hasMessage("Goal ID must be set if ecommerce parameters are used"); - } @Test void testSearchResultsCount() { @@ -106,7 +30,7 @@ void testVisitorLongitude() { assertThatThrownBy(() -> RequestValidator.validate(request, null)) .isInstanceOf(MatomoException.class) .hasMessage( - "Auth token must be present if longitude, latitude, region, city or country are set"); + "Auth token must be present if visitor longitude, latitude, region, city, country or IP are set"); } @Test @@ -116,7 +40,7 @@ void testVisitorLatitude() { assertThatThrownBy(() -> RequestValidator.validate(request, null)) .isInstanceOf(MatomoException.class) .hasMessage( - "Auth token must be present if longitude, latitude, region, city or country are set"); + "Auth token must be present if visitor longitude, latitude, region, city, country or IP are set"); } @Test @@ -126,7 +50,7 @@ void testVisitorCity() { assertThatThrownBy(() -> RequestValidator.validate(request, null)) .isInstanceOf(MatomoException.class) .hasMessage( - "Auth token must be present if longitude, latitude, region, city or country are set"); + "Auth token must be present if visitor longitude, latitude, region, city, country or IP are set"); } @Test @@ -136,7 +60,7 @@ void testVisitorRegion() { assertThatThrownBy(() -> RequestValidator.validate(request, null)) .isInstanceOf(MatomoException.class) .hasMessage( - "Auth token must be present if longitude, latitude, region, city or country are set"); + "Auth token must be present if visitor longitude, latitude, region, city, country or IP are set"); } @Test @@ -148,7 +72,7 @@ void testVisitorCountryTE() { assertThatThrownBy(() -> RequestValidator.validate(request, null)) .isInstanceOf(MatomoException.class) .hasMessage( - "Auth token must be present if longitude, latitude, region, city or country are set"); + "Auth token must be present if visitor longitude, latitude, region, city, country or IP are set"); } @Test @@ -163,15 +87,6 @@ void testRequestDatetime() { } - @Test - void failsIfSiteIdIsNegative() { - request.setSiteId(-1); - - assertThatThrownBy(() -> RequestValidator.validate(request, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Site ID must not be negative"); - } - @Test void failsIfAuthTokenIsNot32CharactersLong() { assertThatThrownBy(() -> RequestValidator.validate(request, "123456789012345678901234567890")) diff --git a/core/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java b/core/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java index 4458568f..8b68c950 100644 --- a/core/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java +++ b/core/src/test/java/org/matomo/java/tracking/TrackingParameterMethodTest.java @@ -35,6 +35,8 @@ void doNothingIfParameterValueIsNotCharSequence() { .parameterName("foo") .pattern(Pattern.compile("bar")) .maxLength(255) + .min(1) + .max(1) .build(); trackingParameterMethod.validateParameterValue(1); @@ -64,4 +66,24 @@ void validateParameterValueFailsIfMaxLengthIsExceeded() { .hasMessage("Invalid value for foo. Must be less or equal than 3 characters"); } + @Test + void failIfParameterValueIsLessThanMin() { + TrackingParameterMethod trackingParameterMethod = + TrackingParameterMethod.builder().parameterName("foo").min(3.0).build(); + + assertThatThrownBy(() -> trackingParameterMethod.validateParameterValue(1)) + .isInstanceOf(MatomoException.class) + .hasMessage("Invalid value for foo. Must be greater or equal than 3"); + } + + @Test + void failIfParameterValueIsGreaterThanMax() { + TrackingParameterMethod trackingParameterMethod = + TrackingParameterMethod.builder().parameterName("foo").max(3.0).build(); + + assertThatThrownBy(() -> trackingParameterMethod.validateParameterValue(4)) + .isInstanceOf(MatomoException.class) + .hasMessage("Invalid value for foo. Must be less or equal than 3"); + } + } diff --git a/java11/pom.xml b/java11/pom.xml index 18bf2b64..6aa9b2d0 100644 --- a/java11/pom.xml +++ b/java11/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.1-SNAPSHOT ../pom.xml matomo-java-tracker-java11 - 3.1.2-SNAPSHOT + 3.2.1-SNAPSHOT jar Matomo Java Tracker Java 11 diff --git a/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index 0f0f9c56..734d69e0 100644 --- a/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/java11/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -409,8 +409,10 @@ void failsOnNegativeSiteId(WireMockRuntimeInfo wireMockRuntimeInfo) { requestBuilder.siteId(-1); assertThatThrownBy(this::whenSendsRequestAsync) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("Site ID must not be negative"); + .isInstanceOf(MatomoException.class) + .hasMessage("Could not append parameter") + .hasRootCauseInstanceOf(MatomoException.class) + .hasRootCauseMessage("Invalid value for idsite. Must be greater or equal than 1"); } @@ -474,6 +476,7 @@ void sendsRequestAsyncAndAcceptsCallback(WireMockRuntimeInfo wireMockRuntimeInfo AtomicBoolean success = new AtomicBoolean(); future = matomoTracker.sendRequestAsync(requestBuilder.build(), request -> { success.set(true); + return null; }); assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { verify(getRequestedFor(urlPathEqualTo("/matomo.php"))); diff --git a/java8/pom.xml b/java8/pom.xml index e4c9a001..21c8ed91 100644 --- a/java8/pom.xml +++ b/java8/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.1-SNAPSHOT ../pom.xml matomo-java-tracker - 3.1.2-SNAPSHOT + 3.2.1-SNAPSHOT jar Matomo Java Tracker Java 8 diff --git a/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java b/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java index 6d935c8b..fe8eb7c1 100644 --- a/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java +++ b/java8/src/test/java/org/matomo/java/tracking/MatomoTrackerIT.java @@ -428,8 +428,8 @@ void failsOnNegativeSiteId() { .failsWithin(1, MINUTES) .withThrowableThat() .havingRootCause() - .isInstanceOf(IllegalArgumentException.class) - .withMessage("Site ID must not be negative"); + .isInstanceOf(MatomoException.class) + .withMessage("Invalid value for idsite. Must be greater or equal than 1"); ; } @@ -492,6 +492,7 @@ void sendsRequestAsyncAndAcceptsCallback() { AtomicBoolean success = new AtomicBoolean(); future = matomoTracker.sendRequestAsync(requestBuilder.build(), request -> { success.set(true); + return null; }); assertThat(future).succeedsWithin(1, MINUTES).satisfies(v -> { wireMockServer.verify(getRequestedFor(urlPathEqualTo("/matomo.php"))); diff --git a/pom.xml b/pom.xml index 482dd802..c19a6b46 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.1-SNAPSHOT pom Matomo Java Tracker Parent @@ -359,7 +359,7 @@ org.owasp dependency-check-maven - 8.4.2 + 8.4.3 true diff --git a/servlet-jakarta/pom.xml b/servlet-jakarta/pom.xml index 92ae57d2..9a8e4257 100644 --- a/servlet-jakarta/pom.xml +++ b/servlet-jakarta/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-jakarta - 3.1.2-SNAPSHOT + 3.2.1-SNAPSHOT jar Matomo Java Tracker Servlet Jakarta diff --git a/servlet-javax/pom.xml b/servlet-javax/pom.xml index 7e63d4a4..83205e5a 100644 --- a/servlet-javax/pom.xml +++ b/servlet-javax/pom.xml @@ -4,12 +4,12 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.1-SNAPSHOT ../pom.xml matomo-java-tracker-servlet-javax - 3.1.2-SNAPSHOT + 3.2.1-SNAPSHOT jar Matomo Java Tracker Servlet Javax @@ -53,7 +53,7 @@ org.eclipse.jetty jetty-servlet - 9.4.53.v20231009 + 10.0.18 test diff --git a/spring/pom.xml b/spring/pom.xml index 0932c7cd..aa334936 100644 --- a/spring/pom.xml +++ b/spring/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.1-SNAPSHOT ../pom.xml diff --git a/test/pom.xml b/test/pom.xml index 41c7fc6c..f60465d4 100644 --- a/test/pom.xml +++ b/test/pom.xml @@ -4,7 +4,7 @@ org.piwik.java.tracking matomo-java-tracker-parent - 3.1.2-SNAPSHOT + 3.2.1-SNAPSHOT ../pom.xml diff --git a/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java b/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java new file mode 100644 index 00000000..3c49b87a --- /dev/null +++ b/test/src/main/java/org/matomo/java/tracking/test/ConsumerExample.java @@ -0,0 +1,46 @@ +package org.matomo.java.tracking.test; + +import java.net.URI; +import org.matomo.java.tracking.MatomoRequest; +import org.matomo.java.tracking.MatomoRequests; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.parameters.VisitorId; + +/** + * Example for sending a request and performing an action when the request was sent successfully. + */ +public class ConsumerExample { + + /** + * Example for sending a request and performing an action when the request was sent successfully. + * + * @param args ignored + */ + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoTracker tracker = new MatomoTracker(configuration); + + MatomoRequest request = MatomoRequests + .event("Training", "Workout completed", "Bench press", 60.0) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build(); + + tracker.sendRequestAsync(request) + .thenAccept(req -> System.out.printf("Sent request %s%n", req)) + .exceptionally(throwable -> { + System.err.printf("Failed to send request: %s%n", throwable.getMessage()); + return null; + }); + + } + +} diff --git a/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java b/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java new file mode 100644 index 00000000..584aa32d --- /dev/null +++ b/test/src/main/java/org/matomo/java/tracking/test/EcommerceExample.java @@ -0,0 +1,60 @@ +package org.matomo.java.tracking.test; + +import java.net.URI; +import org.matomo.java.tracking.MatomoRequests; +import org.matomo.java.tracking.MatomoTracker; +import org.matomo.java.tracking.TrackerConfiguration; +import org.matomo.java.tracking.parameters.EcommerceItem; +import org.matomo.java.tracking.parameters.EcommerceItems; +import org.matomo.java.tracking.parameters.VisitorId; + +/** + * Example for sending an ecommerce request. + */ +public class EcommerceExample { + + /** + * Example for sending an ecommerce request. + * + * @param args ignored + */ + public static void main(String[] args) { + + TrackerConfiguration configuration = TrackerConfiguration + .builder() + .apiEndpoint(URI.create("https://www.yourdomain.com/matomo.php")) + .defaultSiteId(1) + .defaultAuthToken("ee6e3dd9ed1b61f5328cf5978b5a8c71") + .logFailedTracking(true) + .build(); + + MatomoTracker tracker = new MatomoTracker(configuration); + + tracker.sendBulkRequestAsync(MatomoRequests + .ecommerceCartUpdate(50.0) + .ecommerceItems(EcommerceItems + .builder() + .item(EcommerceItem + .builder() + .sku("XYZ12345") + .name("Matomo - The big book about web analytics") + .category("Education & Teaching") + .price(23.1) + .quantity(2) + .build()) + .item(EcommerceItem + .builder() + .sku("B0C2WV3MRJ") + .name("Matomo for data visualization") + .category("Education & Teaching") + .price(15.0) + .quantity(1) + .build()) + .build()) + .visitorId(VisitorId.fromString("customer@mail.com")) + .build() + ); + + } + +} diff --git a/test/src/main/java/org/matomo/java/tracking/test/SendExample.java b/test/src/main/java/org/matomo/java/tracking/test/SendExample.java index 03e4af5d..1fe55b3e 100644 --- a/test/src/main/java/org/matomo/java/tracking/test/SendExample.java +++ b/test/src/main/java/org/matomo/java/tracking/test/SendExample.java @@ -33,7 +33,6 @@ public static void main(String[] args) { .visitorId(VisitorId.fromString("customer@mail.com")) .build() ); - } }