diff --git a/src/apps/monitoring/quanta/restapi/custom/api.pm b/src/apps/monitoring/quanta/restapi/custom/api.pm index 7ce23bafc..14e8751cf 100644 --- a/src/apps/monitoring/quanta/restapi/custom/api.pm +++ b/src/apps/monitoring/quanta/restapi/custom/api.pm @@ -1,5 +1,5 @@ # -# Copyright 2024 Centreon (http://www.centreon.com/) +# Copyright 2025 Centreon (http://www.centreon.com/) # # Centreon is a full-fledged industry-strength solution that meets # the needs in IT infrastructure and application monitoring for @@ -23,8 +23,10 @@ package apps::monitoring::quanta::restapi::custom::api; use strict; use warnings; use centreon::plugins::http; +use centreon::plugins::statefile; use DateTime; use JSON::XS; +use Digest::MD5 qw(md5_hex); sub new { my ($class, %options) = @_; @@ -42,18 +44,21 @@ sub new { if (!defined($options{noptions})) { $options{options}->add_options(arguments => { - 'hostname:s' => { name => 'hostname' }, - 'url-path:s' => { name => 'url_path' }, - 'port:s' => { name => 'port' }, - 'proto:s' => { name => 'proto' }, - 'api-token:s' => { name => 'api_token' }, - 'timeout:s' => { name => 'timeout' } + 'api-token:s' => { name => 'api_token' }, + 'api-path:s' => { name => 'api_path' }, + 'url-path:s' => { redirect => 'api_path' }, + 'hostname:s' => { name => 'hostname' }, + 'port:s' => { name => 'port' }, + 'proto:s' => { name => 'proto' }, + 'timeout:s' => { name => 'timeout' }, + 'reload-cache-time:s' => { name => 'reload_cache_time' }, }); } $options{options}->add_help(package => __PACKAGE__, sections => 'REST API OPTIONS', once => 1); $self->{output} = $options{output}; - $self->{http} = centreon::plugins::http->new(%options); + $self->{http} = centreon::plugins::http->new(%options, default_backend => 'curl'); + $self->{cache_objects} = centreon::plugins::statefile->new(%options); return $self; } @@ -72,15 +77,19 @@ sub check_options { $self->{hostname} = (defined($self->{option_results}->{hostname})) ? $self->{option_results}->{hostname} : 'app.quanta.io'; $self->{port} = (defined($self->{option_results}->{port})) ? $self->{option_results}->{port} : 443; $self->{proto} = (defined($self->{option_results}->{proto})) ? $self->{option_results}->{proto} : 'https'; - $self->{url_path} = (defined($self->{option_results}->{url_path})) ? $self->{option_results}->{url_path} : '/api'; + $self->{api_path} = (defined($self->{option_results}->{api_path})) ? $self->{option_results}->{api_path} : '/api/v1'; $self->{timeout} = (defined($self->{option_results}->{timeout})) ? $self->{option_results}->{timeout} : 10; $self->{api_token} = (defined($self->{option_results}->{api_token})) ? $self->{option_results}->{api_token} : ''; + $self->{reload_cache_time} = (defined($self->{option_results}->{reload_cache_time})) ? $self->{option_results}->{reload_cache_time} : 86400; + $self->{force_cache_reload} = (defined($self->{option_results}->{force_cache_reload})) ? $self->{option_results}->{force_cache_reload} : undef; if (!defined($self->{api_token}) || $self->{api_token} eq '') { $self->{output}->add_option_msg(short_msg => "Need to specify --api-token option."); $self->{output}->option_exit(); } - + + $self->{cache_objects}->check_options(option_results => $self->{option_results}); + return 0; } @@ -94,6 +103,7 @@ sub build_options_for_httplib { $self->{option_results}->{url_path} = $self->{url_path}; $self->{option_results}->{warning_status} = ''; $self->{option_results}->{critical_status} = ''; + $self->{option_results}->{unknown_status} = ''; } sub settings { @@ -101,39 +111,113 @@ sub settings { $self->build_options_for_httplib(); $self->{http}->add_header(key => 'Accept', value => 'application/json'); + $self->{http}->add_header(key => 'Content-Type', value => 'application/json'); + $self->{http}->add_header(key => 'Authorization', value => 'Token ' . $self->{api_token}); $self->{http}->set_options(%{$self->{option_results}}); } -sub get_api_token { - my ($self, %options) = @_; - - return $self->{api_token}; -} - -sub request_api { +sub get_data_export_api { my ($self, %options) = @_; - $self->settings; - - $self->{output}->output_add(long_msg => "Query URL: '" . $self->{proto} . "://" . $self->{hostname} . - $self->{url_path} . $options{url_path} . "'", debug => 1); + $self->settings(); - my $content = $self->{http}->request(url_path => $self->{url_path} . $options{url_path}); - - my $decoded; + my ($json, $response, $encoded_form_post); eval { - $decoded = JSON::XS->new->utf8->decode($content); + $encoded_form_post = JSON::XS->new->utf8->encode($options{data}); + }; + my $endpoint = defined($options{is_rum}) ? '/rum_data_export' : '/data_export'; + $response = $self->{http}->request( + method => 'POST', + url_path => $self->{api_path} . $endpoint, + query_form_post => $encoded_form_post + ); + + if ($self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300) { + $self->{output}->add_option_msg(short_msg => "API returns empty content [code: '" . $self->{http}->get_code() . "'] [message: '" . $self->{http}->get_message() . "']"); + $self->{output}->output_add(long_msg => $response, debug => 1); + $self->{output}->option_exit(); + } + eval { + $json = JSON::XS->new->utf8->decode($response); }; if ($@) { - $self->{output}->add_option_msg(short_msg => "Cannot decode json response: $@"); - $self->{output}->option_exit(); - } - if (defined($decoded->{error})) { - $self->{output}->add_option_msg(short_msg => "API returned error '" . $decoded->{error} . "'"); + $self->{output}->add_option_msg(short_msg => "Cannot decode JSON response: $response"); $self->{output}->option_exit(); + }; + + return $json; +} + +sub get_configuration_api { + my ($self, %options) = @_; + + $self->settings(); + my ($json, $response); + + my $get_param = []; + if (defined($options{get_param})) { + push @$get_param, $options{get_param}; } - return $decoded; + $response = $self->{http}->request( + method => 'GET', + url_path => $self->{api_path} . $options{endpoint}, + get_param => $get_param + ); + + if ($self->{http}->get_code() < 200 || $self->{http}->get_code() >= 300) { + $self->{output}->add_option_msg(short_msg => "API returns empty content [code: '" . $self->{http}->get_code() . "'] [message: '" . $self->{http}->get_message() . "']"); + $self->{output}->output_add(long_msg => $response, debug => 1); + $self->{output}->option_exit(); + } + eval { + $json = JSON::XS->new->utf8->decode($response); + }; + if ($@) { + $self->{output}->add_option_msg(short_msg => "Cannot decode JSON response: $response"); + $self->{output}->option_exit(); + }; + + return $json; +} + +# Available calls depending on type: +# journeys: /sites/{site_id}/user_journeys to list all journeys for a site +# journey: /sites/{site_id}/user_journeys/{journey_id} unused +# interactions: /sites/{site_id}/user_journeys/{journey_id}/interactions to list all interactions for a journey +# interaction: /sites/{site_id}/user_journeys/{journey_id}/interactions/{interaction_id} to get details of a specific interaction +sub list_objects { + my ($self, %options) = @_; + + my $endpoint = '/sites/' . $options{site_id}; + if ($options{type} =~ /journey|interaction/) { + $endpoint .= '/user_journeys/'; + if ($options{type} eq 'journey') { + $endpoint .= $options{journey_id}; + } + if ($options{type} =~ /interaction/) { + $endpoint .= $options{journey_id} . '/interactions'; + if ($options{type} eq 'interaction') { + $endpoint .= '/' . $options{interaction_id}; + } + } + } + + # Results are cached to avoid too many API calls + my $has_cache_file = $self->{cache_objects}->read(statefile => 'quanta_cache_' . md5_hex($options{site_id}) . md5_hex($endpoint)); + my $response = $self->{cache_objects}->get(name => 'response'); + my $freshness = defined($self->{cache_objects}->get(name => 'update_time')) ? time() - $self->{cache_objects}->get(name => 'update_time') : undef; + + if ( $has_cache_file == 0 || !defined($response) || (defined($freshness)) && ($freshness > $self->{reload_cache_time}) ) { + $response = $self->get_configuration_api(endpoint => $endpoint); + } + + $self->{cache_objects}->write(data => { + update_time => time(), + response => $response + }); + + return $response; } 1; @@ -142,15 +226,15 @@ __END__ =head1 NAME -Quanta Rest API +Quanta by Centreon Rest API =head1 SYNOPSIS -Quanta Rest API custom mode +Quanta by Centreon Rest API custom mode =head1 REST API OPTIONS -Quanta Rest API +Quanta by Centreon Rest API =over 8 @@ -166,9 +250,9 @@ API port (default: 443) Specify https if needed (default: 'https') -=item B<--url-path> +=item B<--api-path> -API URL path (default: '/api') +API URL path (default: '/api/v1') =item B<--api-token> diff --git a/src/apps/monitoring/quanta/restapi/mode/listuserjourneys.pm b/src/apps/monitoring/quanta/restapi/mode/listuserjourneys.pm new file mode 100644 index 000000000..edd0a8e97 --- /dev/null +++ b/src/apps/monitoring/quanta/restapi/mode/listuserjourneys.pm @@ -0,0 +1,116 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# 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. +# + +package apps::monitoring::quanta::restapi::mode::listuserjourneys; + +use base qw(centreon::plugins::mode); + +use strict; +use warnings; + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options); + bless $self, $class; + + $options{options}->add_options(arguments => { + 'site-id:s' => { name => 'site_id', default => '' }, + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::init(%options); + + if ($self->{option_results}->{site_id} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --site-id option."); + $self->{output}->option_exit(); + } + + $self->{site_id} = $self->{option_results}->{site_id}; +} + +sub manage_selection { + my ($self, %options) = @_; + $self->{results} = $options{custom}->list_objects( + type => 'journeys', + site_id => $self->{site_id} + ); +} + +sub run { + my ($self, %options) = @_; + + $self->manage_selection(%options); + foreach my $user_journey (@{$self->{results}->{user_journeys}}){ + $self->{output}->output_add( + long_msg => sprintf("[name: %s][id: %s]", + $user_journey->{name}, + $user_journey->{id}, + ) + ); + } + + $self->{output}->output_add( + severity => 'OK', + short_msg => 'User journeys:' + ); + $self->{output}->display(nolabel => 1, force_ignore_perfdata => 1, force_long_output => 1); + $self->{output}->exit(); +} + +sub disco_format { + my ($self, %options) = @_; + + $self->{output}->add_disco_format(elements => ['name', 'id']); +} + +sub disco_show { + my ($self, %options) = @_; + + $self->manage_selection(%options); + + foreach my $user_journey (@{$self->{results}->{user_journeys}}){ + $self->{output}->add_disco_entry( + id => $user_journey->{id}, + name => $user_journey->{name} + ); + } +} + +1; + +__END__ + +=head1 MODE + +List Quanta by Centreon user journeys for a given site. + +=over 8 + +=item B<--site-id> + +Set ID of the site (mandatory option). + +=back + +=cut diff --git a/src/apps/monitoring/quanta/restapi/mode/rum.pm b/src/apps/monitoring/quanta/restapi/mode/rum.pm new file mode 100644 index 000000000..0c31a5bc6 --- /dev/null +++ b/src/apps/monitoring/quanta/restapi/mode/rum.pm @@ -0,0 +1,274 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# 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. +# + +package apps::monitoring::quanta::restapi::mode::rum; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'rum', type => 1, message_multiple => 'All RUM counters are OK', cb_prefix_output => 'prefix_output', skipped_code => { -10 => 1 } } + ]; + + + $self->{maps_counters}->{rum} = [ + { label => 'sessions', nlabel => 'sessions.count', set => { + key_values => [ { name => 'sessions' }, { name => 'display' } ], + output_template => 'sessions: %.d', + perfdatas => [ + { value => 'sessions', template => '%.d', + min => 0, label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'page-views', nlabel => 'pageviews.count', set => { + key_values => [ { name => 'page_views' }, { name => 'display' } ], + output_template => 'page views: %.d', + perfdatas => [ + { value => 'page_views', template => '%.d', + min => 0, label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'bounce-rate', nlabel => 'bounce.rate.percentage', set => { + key_values => [ { name => 'bounces' }, { name => 'display' } ], + output_template => 'bounce rate: %.d%%', + perfdatas => [ + { value => 'bounces', template => '%.d', + min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'ttfb', nlabel => 'ttfb.milliseconds', set => { + key_values => [ { name => 'backend_time' }, { name => 'display' } ], + output_template => 'ttfb: %.3fms', + perfdatas => [ + { value => 'backend_time', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'onload', nlabel => 'onload.time.milliseconds', set => { + key_values => [ { name => 'frontend_time' }, { name => 'display' } ], + output_template => 'onload time: %.2fms', + perfdatas => [ + { value => 'frontend_time', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'interaction-next-paint', nlabel => 'nextpaint.interaction.time.milliseconds', set => { + key_values => [ { name => 'interaction_to_next_paint' }, { name => 'display' } ], + output_template => 'interaction to next paint: %.2fms', + perfdatas => [ + { value => 'interaction_to_next_paint', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'speed-index', nlabel => 'speedindex.time.milliseconds', set => { + key_values => [ { name => 'speed_index' }, { name => 'display' } ], + output_template => 'speed index: %.2fms', + perfdatas => [ + { value => 'speed_index', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + ]; +} + +sub prefix_output { + my ($self, %options) = @_; + + return $self->{perspective} . ' ' . $options{instance_value}->{display} . ': '; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + "limit-results:s" => { name => 'limit_results', default => '10' }, + "perspective:s" => { name => 'perspective', default => 'all' }, + "timeframe:s" => { name => 'timeframe', default => '1800' }, + "site-id:s" => { name => 'site_id', default => '' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + + $self->{$_} = $self->{option_results}->{$_} foreach qw/limit_results perspective site_id timeframe/; + + if ($self->{site_id} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --site-id option."); + $self->{output}->option_exit(); + } + + if ($self->{perspective} !~ m/all|url|browser|country|city|os/) { + $self->{output}->add_option_msg(short_msg => 'Unknown perspective set in "--perspective" option.'); + $self->{output}->option_exit(); + } +} + +sub manage_selection { + my ($self, %options) = @_; + my $rum_metrics = [ + { name => 'sessions' }, + { name => 'page_views' }, + { name => 'bounces' }, + { name => 'speed_index', is_time => 1 }, + { name => 'frontend_time', is_time => 1 }, + { name => 'backend_time', is_time => 1 }, + { name => 'interaction_to_next_paint', is_time => 1 } + ]; + my $rum_payload; + my $rum_aggregations = [ 'mean' ]; + $rum_payload->{namespace} = 'rum'; + $rum_payload->{index} = $self->{perspective}; + # numifying is required with rum API for INT types + $rum_payload->{tenant_id} = int $self->{site_id}; + $rum_payload->{limit} = int $self->{limit_results}; + $rum_payload->{point_period} = int $self->{timeframe}; + $rum_payload->{range} = int $self->{timeframe}; + foreach my $metric (@$rum_metrics) { + foreach (@$rum_aggregations) { + push @{$rum_payload->{metrics_filter}->{$metric->{name}}->{aggregations}}, $_; + } + } + + my $results = $options{custom}->get_data_export_api(data => $rum_payload, is_rum => 1); + + foreach my $metric (@$rum_metrics) { + my $dimension = $self->{perspective}; + foreach my $result (@{$results->{results}}) { + if (scalar(keys %{$result->{dimensions}}) > 0) { + foreach (sort keys %{$result->{dimensions}}) { + $dimension = $result->{dimensions}->{$_} if $result->{dimensions}->{$_}; + } + } + $self->{rum}->{$dimension}->{display} = $dimension ne 'all' ? $dimension : 'pages'; + if (defined($metric->{is_time})) { + $self->{rum}->{$dimension}->{$metric->{name}} = $result->{total}->{$metric->{name}}->{mean}; + } else { + $self->{rum}->{$dimension}->{$metric->{name}} = $result->{total}->{$metric->{name}}->{count} if (defined($result->{total}->{$metric->{name}}->{count})); + } + } + } +} + +1; + +__END__ + +=head1 MODE + +Check Quanta by Centreon RUM metrics for a given site. + +=over 8 + +=item B<--site-id> + +Set ID of the site (mandatory option). + +=item B<--timeframe> + +Set timeframe in seconds (default: 1800). + +=item B<--perspective> + +Set the perspective in which the data will be applied. +Can be: 'all', 'url', 'browser', 'country', 'city', 'os' (default: 'all'). + +=item B<--limit-results> + +To be used with --perspective. Limit the number of results to be fetched (number of different URLs, browsers, etc...). +(default: 10). + +=item B<--warning-sessions> + +Warning threshold for sessions. + +=item B<--critical-sessions> + +Critical threshold for sessions. + +=item B<--warning-page-views> + +Warning threshold for page views. + +=item B<--critical-page-views> + +Critical threshold for page views. + +=item B<--warning-bounce-rate> + +Warning threshold for bounce rate. + +=item B<--critical-bounce-rate> + +Critical threshold for bounce rate. + +=item B<--warning-ttfb> + +Warning threshold for time to first byte (in ms). + +=item B<--critical-ttfb> + +Critical threshold for time to first byte (in ms). + +=item B<--warning-onload> + +Warning threshold for C time (in ms). + +=item B<--critical-onload> + +Critical threshold for C time (in ms). + +=item B<--warning-interaction-next-paint> + +Warning threshold for time to interaction next paint (in ms). + +=item B<--critical-interaction-next-paint> + +Critical threshold for time to interaction next paint (in ms). + +=item B<--warning-speed-index> + +Warning threshold for speed index. + +=item B<--critical-speed-index> + +Critical threshold for speed index. + +=back + +=cut diff --git a/src/apps/monitoring/quanta/restapi/mode/siteoverview.pm b/src/apps/monitoring/quanta/restapi/mode/siteoverview.pm new file mode 100644 index 000000000..695a68e9d --- /dev/null +++ b/src/apps/monitoring/quanta/restapi/mode/siteoverview.pm @@ -0,0 +1,185 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# 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. +# + +package apps::monitoring::quanta::restapi::mode::siteoverview; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'sites', type => 1, message_multiple => 'All sites are OK', cb_prefix_output => 'prefix_output' } + ]; + + $self->{maps_counters}->{sites} = [ + { label => 'performance-score', nlabel => 'performance.score', set => { + key_values => [ { name => 'performance_score' }, { name => 'display' } ], + output_template => 'performance score: %d', + perfdatas => [ + { value => 'performance_score', template => '%d', + min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'digital-sobriety-score', nlabel => 'digitalsobriety.score', set => { + key_values => [ { name => 'digital_sobriety_score' }, { name => 'display' } ], + output_template => 'digital sobriety score: %d', + perfdatas => [ + { value => 'digital_sobriety_score', template => '%d', + min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'eco-design-score', nlabel => 'ecodesign.score', set => { + key_values => [ { name => 'eco_design_score' }, { name => 'display' } ], + output_template => 'eco design score: %d', + perfdatas => [ + { value => 'eco_design_score', template => '%d', + min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'carbon-footprint', nlabel => 'perclick.carbon.footprint.gramm', set => { + key_values => [ { name => 'carbon_footprint_per_click' }, { name => 'display' } ], + output_template => 'carbon footprint per click: %.2fg', + perfdatas => [ + { value => 'carbon_footprint_per_click', template => '%.2f', + min => 0, unit => 'g', label_extra_instance => 1, instance_use => 'display' }, + ], + } + } + ]; +} + +sub prefix_output { + my ($self, %options) = @_; + + return "Site '" . $options{instance_value}->{display} . "' "; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + "site-id:s" => { name => 'site_id', default => '' }, + "timeframe:s" => { name => 'timeframe', default => '3600' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $self->{$_} = $self->{option_results}->{$_} foreach qw/site_id timeframe/; + + if ($self->{site_id} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --site-id option."); + $self->{output}->option_exit(); + } +} + +sub manage_selection { + my ($self, %options) = @_; + + my $site_metrics = [ + 'performance_score', + 'digital_sobriety_score', + 'eco_design_score', + 'carbon_footprint_per_click' + ]; + my ($site_payload, $resources_payload); + $site_payload->{type} = 'site'; + $site_payload->{id} = $self->{site_id}; + foreach (@$site_metrics) { + push @{$site_payload->{metrics}}, { name => $_}; + } + push @{$resources_payload->{resources}}, $site_payload; + $resources_payload->{range} = $self->{timeframe}; + + my $results = $options{custom}->get_data_export_api(data => $resources_payload); + foreach my $site (@{$results->{resources}}) { + $self->{sites}->{$site->{id}}->{display} = $site->{name}; + foreach my $metric (@{$site->{metrics}}) { + $self->{sites}->{$site->{id}}->{$metric->{name}} = $metric->{values}[0]->{average}; + } + } +} + +1; + +__END__ + +=head1 MODE + +Check Quanta by Centreon overview performance metrics for a given site. + +=over 8 + +=item B<--site-id> + +Set ID of the site (mandatory option). + +=item B<--timeframe> + +Set timeframe in seconds (default: 3600). + +=item B<--warning-performance-score> + +Warning threshold for performance score. + +=item B<--critical-performance-score> + +Critical threshold for performance score. + +=item B<--warning-digital-sobriety-score> + +Warning threshold for digital sobriety score. + +=item B<--critical-digital-sobriety-score> + +Critical threshold for digital sobriety score. + +=item B<--warning-eco-design-score> + +Warning threshold for C score. + +=item B<--critical-eco-design-score> + +Critical threshold for C score. + +=item B<--warning-carbon-footprint> + +Warning threshold for carbon footprint. + +=item B<--critical-carbon-footprint> + +Critical threshold for carbon footprint. + +=back + +=cut diff --git a/src/apps/monitoring/quanta/restapi/mode/userjourneyincidents.pm b/src/apps/monitoring/quanta/restapi/mode/userjourneyincidents.pm new file mode 100644 index 000000000..0ad549d37 --- /dev/null +++ b/src/apps/monitoring/quanta/restapi/mode/userjourneyincidents.pm @@ -0,0 +1,243 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# 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. +# + +package apps::monitoring::quanta::restapi::mode::userjourneyincidents; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; +use centreon::plugins::templates::catalog_functions qw(catalog_status_threshold_ng); + +sub prefix_output { + my ($self, %options) = @_; + + return "Incident for interaction '" . $options{instance_value}->{interaction_name} . "' "; +} + +sub prefix_output_global { + my ($self, %options) = @_; + + return 'Incidents '; +} + +sub custom_duration_output { + my ($self, %options) = @_; + + if ($self->{result_values}->{status} =~ 'Open') { + return sprintf( + 'start time: %s, duration: %s', + $self->{result_values}->{start_time}, + centreon::plugins::misc::change_seconds(value => $self->{result_values}->{duration}) + ); + } else { + return sprintf( + 'start time: %s, end time: %s, duration: %s', + $self->{result_values}->{start_time}, + $self->{result_values}->{end_time}, + centreon::plugins::misc::change_seconds(value => $self->{result_values}->{duration}) + ); + } +} + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'global', type => 0, cb_prefix_output => 'prefix_output_global' }, + { name => 'incidents', type => 1, message_multiple => 'No ongoing incident', cb_prefix_output => 'prefix_output' } + ]; + + $self->{maps_counters}->{global} = [ + { label => 'incidents-total', nlabel => 'quanta.incidents.total.count', set => { + key_values => [ { name => 'total' } ], + output_template => 'total: %s', + perfdatas => [ { template => '%d', min => 0 } ] + } + } + ]; + + $self->{maps_counters}->{incidents} = [ + { label => 'incident-status', + type => 2, + warning_default => '', + critical_default => '%{status} =~ "open"', + set => { + key_values => [ { name => 'status' }, { name => 'display' }, { name => 'interaction_name' } ], + output_template => 'status: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { label => 'incident-type', + type => 2, + warning_default => '', + critical_default => '', + set => { + key_values => [ { name => 'type' }, { name => 'display' }, { name => 'interaction_name' } ], + output_template => 'type: %s', + closure_custom_perfdata => sub { return 0; }, + closure_custom_threshold_check => \&catalog_status_threshold_ng + } + }, + { label => 'incident-duration', set => { + key_values => [ { name => 'duration' }, { name => 'start_time' }, { name => 'end_time' }, { name => 'status'} ], + closure_custom_output => $self->can('custom_duration_output'), + closure_custom_perfdata => sub { return 0; } + } + } + ]; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + "ignore-closed" => { name => 'ignore_closed' }, + "journey-id:s" => { name => 'journey_id', default => '' }, + "site-id:s" => { name => 'site_id', default => '' }, + "timeframe:s" => { name => 'timeframe', default => '300' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $self->{$_} = $self->{option_results}->{$_} foreach qw/journey_id site_id timeframe/; + + if ($self->{site_id} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --site-id option."); + $self->{output}->option_exit(); + } + if ($self->{journey_id} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --journey-id option."); + $self->{output}->option_exit(); + } +} + +sub manage_selection { + my ($self, %options) = @_; + + my $results = $options{custom}->get_configuration_api( + endpoint => '/sites/' . $self->{site_id} . '/user_journeys/' . $self->{journey_id} . '/incidents', + get_param => 'range=' . $self->{timeframe} + ); + $self->{global}->{total} = 0; + foreach my $incident (@{$results->{incidents}}) { + my $kind = $incident->{kind} =~ s/_/ /gr; + my $end_time = $incident->{end_clock} ? $incident->{end_clock} : time(); + next if defined($self->{option_results}->{ignore_closed}) && $incident->{end_clock}; + my $interaction = $options{custom}->list_objects(type => 'interaction', site_id => $self->{site_id}, journey_id => $self->{journey_id}, interaction_id => $incident->{interaction_id}); + $self->{incidents}->{$incident->{id}} = { + display => $incident->{id}, + type => $kind, + interaction_name => $interaction->{interaction}->{name}, + start_time => POSIX::strftime('%d-%m-%Y %H:%M:%S %Z', localtime($incident->{start_clock})), + end_time => POSIX::strftime('%d-%m-%Y %H:%M:%S %Z', localtime($end_time)), + duration => $end_time - $incident->{start_clock} + }; + $self->{incidents}->{$incident->{id}}->{status} = $incident->{end_clock} ? 'closed' : 'open'; + + $self->{global}->{total}++; + } +} + +1; + +__END__ + +=head1 MODE + +Check Quanta by Centreon incidents for a given user journey. + +=over 8 + +=item B<--site-id> + +Set ID of the site (mandatory option). + +=item B<--journey-id> + +Set ID of the user journey (mandatory option). + +=item B<--timeframe> + +Set timeframe in seconds (default: 300). + +=item B<--ignore-closed> + +Ignore closed incidents. + +=item B<--warning-incidents-total> + +Warning threshold for incidents total. + +=item B<--critical-incidents-total> + +Critical threshold for incidents total. + +=item B<--warning-incident-status> + +Define the conditions to match for the status to be B. + +You can use the following variables: C<%{status}>. + +Example: C<--warning-incident-status='%{status} =~ /open/i'> + +=item B<--critical-incident-status> + +Define the conditions to match for the status to be B. + +You can use the following variables: C<%{status}>. + +Default: C<--critical-incident-status='%{status} =~ /open/i'> + +=item B<--warning-incident-type> + +Define the conditions to match for the incident type to be B. + +You can use the following variables: C<%{type}>. + +Example: C<--warning-incident-type='%{type} =~ /error/i'> + +=item B<--critical-incident-type> + +Define the conditions to match for the incident type to be B. + +You can use the following variables: C<%{type}>. + +Example: C<--critical-incident-type='%{type} =~ /error/i'> + +=item B<--warning-incident-duration> + +Warning threshold for incident duration (in seconds). + +=item B<--critical-incident-duration> + +Critical threshold for incident duration (in seconds). + +=back + +=cut diff --git a/src/apps/monitoring/quanta/restapi/mode/userjourneystatistics.pm b/src/apps/monitoring/quanta/restapi/mode/userjourneystatistics.pm new file mode 100644 index 000000000..58db5c774 --- /dev/null +++ b/src/apps/monitoring/quanta/restapi/mode/userjourneystatistics.pm @@ -0,0 +1,304 @@ +# +# Copyright 2025 Centreon (http://www.centreon.com/) +# +# Centreon is a full-fledged industry-strength solution that meets +# the needs in IT infrastructure and application monitoring for +# service performance. +# +# 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. +# + +package apps::monitoring::quanta::restapi::mode::userjourneystatistics; + +use base qw(centreon::plugins::templates::counter); + +use strict; +use warnings; + +sub set_counters { + my ($self, %options) = @_; + + $self->{maps_counters_type} = [ + { name => 'metrics', type => 1, message_multiple => 'User journey is OK', cb_prefix_output => 'prefix_output', skipped_code => { -10 => 1 } } + ]; + + + $self->{maps_counters}->{metrics} = [ + { label => 'journey-performance-score', nlabel => 'journey.performance.score', set => { + key_values => [ { name => 'avg_lh_performance_score' }, { name => 'display' } ], + output_template => 'journey performance score: %d', + perfdatas => [ + { value => 'avg_lh_performance_score', template => '%d', + min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'journey-hero-time', nlabel => 'journey.herotime.milliseconds', set => { + key_values => [ { name => 'total_hero_time' }, { name => 'display' } ], + output_template => 'journey hero time: %.2fms', + perfdatas => [ + { value => 'total_hero_time', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'journey-speed-index', nlabel => 'journey.speedindex.time.milliseconds', set => { + key_values => [ { name => 'total_speed_index' }, { name => 'display' } ], + output_template => 'journey speed index: %.2fms', + perfdatas => [ + { value => 'total_speed_index', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'journey-ttfb', nlabel => 'journey.ttfb.milliseconds', set => { + key_values => [ { name => 'total_net_request_ttfb' }, { name => 'display' } ], + output_template => 'journey ttfb: %.2fms', + perfdatas => [ + { value => 'total_net_request_ttfb', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'interaction-performance-score', nlabel => 'interaction.performance.score', set => { + key_values => [ { name => 'lh_performance_score' }, { name => 'display' } ], + output_template => 'performance score: %d', + perfdatas => [ + { value => 'lh_performance_score', template => '%d', + min => 0, max => 100, label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'interaction-hero-time', nlabel => 'herotime.milliseconds', set => { + key_values => [ { name => 'hero_time' }, { name => 'display' } ], + output_template => 'hero time: %.2fms', + perfdatas => [ + { value => 'hero_time', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'interaction-speed-index', nlabel => 'speedindex.time.milliseconds', set => { + key_values => [ { name => 'speed_index' }, { name => 'display' } ], + output_template => 'speed index: %.2fms', + perfdatas => [ + { value => 'speed_index', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' }, + ], + } + }, + { label => 'interaction-ttfb', nlabel => 'ttfb.milliseconds', set => { + key_values => [ { name => 'net_request_ttfb' }, { name => 'display' } ], + output_template => 'ttfb: %.2fms', + perfdatas => [ + { value => 'net_request_ttfb', template => '%.2f', + min => 0, unit => 'ms', label_extra_instance => 1, instance_use => 'display' }, + ], + } + } + ]; +} + +sub prefix_output { + my ($self, %options) = @_; + + return $options{instance_value}->{type} . ' "' . $options{instance_value}->{name} . '" '; +} + +sub new { + my ($class, %options) = @_; + my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); + bless $self, $class; + + $options{options}->add_options(arguments => { + "show-interactions" => { name => 'add_interactions' }, + "journey-id:s" => { name => 'journey_id', default => '' }, + "site-id:s" => { name => 'site_id', default => '' }, + "timeframe:s" => { name => 'timeframe', default => '300' } + }); + + return $self; +} + +sub check_options { + my ($self, %options) = @_; + $self->SUPER::check_options(%options); + + $self->{$_} = $self->{option_results}->{$_} foreach qw/journey_id site_id timeframe/; + + if ($self->{journey_id} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --journey-id option."); + $self->{output}->option_exit(); + } + if ($self->{site_id} eq '') { + $self->{output}->add_option_msg(short_msg => "Need to specify --site-id option."); + $self->{output}->option_exit(); + } +} + +sub manage_selection { + my ($self, %options) = @_; + my $journey_metrics = [ + 'avg_lh_performance_score', + 'total_hero_time', + 'total_net_request_ttfb', + 'total_speed_index' + ]; + + if (defined($self->{option_results}->{add_interactions})) { + my $interaction_metrics = [ + 'lh_performance_score', + 'speed_index', + 'hero_time', + 'net_request_ttfb' + ]; + my $interactions_list = $options{custom}->list_objects(type => 'interactions', site_id => $self->{site_id}, journey_id => $self->{journey_id}); + foreach my $interaction (@{$interactions_list->{interactions}}) { + my $interaction_payload; + $interaction_payload->{type} = 'interaction'; + $interaction_payload->{id} = $interaction->{id}; + foreach (@$interaction_metrics) { + push @{$interaction_payload->{metrics}}, { name => $_}; + } + push @{$self->{resources_payload}->{resources}}, $interaction_payload; + }; + } + + my $journey_payload; + $journey_payload->{type} = 'journey'; + $journey_payload->{id} = $self->{journey_id}; + foreach (@$journey_metrics) { + push @{$journey_payload->{metrics}}, { name => $_ }; + } + push @{$self->{resources_payload}->{resources}}, $journey_payload; + $self->{resources_payload}->{range} = $self->{timeframe}; + + my $results = $options{custom}->get_data_export_api(data => $self->{resources_payload}); + + foreach my $result (@{$results->{resources}}) { + $self->{metrics}->{$result->{id}}->{display} = $result->{type} . "_" . $result->{name}; + $self->{metrics}->{$result->{id}}->{name} = $result->{name}; + $self->{metrics}->{$result->{id}}->{type} = $result->{type}; + foreach my $metric (@{$result->{metrics}}) { + my $timestamp = 0; + foreach my $timeserie (@{$metric->{values}}) { + if ($timeserie->{timestamp} > $timestamp) { + $self->{metrics}->{$result->{id}}->{$metric->{name}} = $timeserie->{average}; + $timestamp = $timeserie->{timestamp}; + } + } + } + } +} + +1; + +__END__ + +=head1 MODE + +Check Quanta by Centreon statistics for a user journey. + +=over 8 + +=item B<--site-id> + +Set ID of the site (mandatory option). + +=item B<--journey-id> + +Set ID of the user journey (mandatory option). + +=item B<--show-interactions> + +Also monitor interactions (scenario's steps) of a user journey. + +=item B<--timeframe> + +Set timeframe in seconds (default: 300). + +=item B<--warning-journey-performance-score> + +Warning threshold for journey performance score. + +=item B<--critical-journey-performance-score> + +Critical threshold for journey performance score. + +=item B<--warning-journey-hero-time> + +Warning threshold for journey hero time (in ms). + +=item B<--critical-journey-hero-time> + +Critical threshold for journey hero time (in ms). + +=item B<--warning-journey-speed-index> + +Warning threshold for journey speed index (in ms). + +=item B<--critical-journey-speed-index> + +Critical threshold for journey speed index (in ms). + +=item B<--warning-journey-ttfb> + +Warning threshold for journey time to first byte (in ms). + +=item B<--critical-journey-ttfb> + +Critical threshold for journey time to first byte (in ms). + +=back + +=head2 Interaction related metrics + +The following parameters take effect only if --show-interactions is set + +=over 8 + +=item B<--warning-interaction-performance-score> + +Warning threshold for interaction performance score. + +=item B<--critical-interaction-performance-score> + +Critical threshold for interaction performance score. + +=item B<--warning-interaction-hero-time> + +Warning threshold for interaction hero time (in ms). + +=item B<--critical-interaction-hero-time> + +Critical threshold for interaction hero time (in ms). + +=item B<--warning-interaction-speed-index> + +Warning threshold for interaction speed index (in ms). + +=item B<--critical-interaction-speed-index> + +Critical threshold for interaction speed index (in ms). + +=item B<--warning-interaction-ttfb> + +Warning threshold for interaction time to first byte (in ms). + +=item B<--critical-interaction-ttfb> + +Critical threshold for time to first byte (in ms). + +=back + +=cut diff --git a/src/apps/monitoring/quanta/restapi/mode/webscenariosavailability.pm b/src/apps/monitoring/quanta/restapi/mode/webscenariosavailability.pm deleted file mode 100644 index 04ea400fc..000000000 --- a/src/apps/monitoring/quanta/restapi/mode/webscenariosavailability.pm +++ /dev/null @@ -1,147 +0,0 @@ -# -# Copyright 2024 Centreon (http://www.centreon.com/) -# -# Centreon is a full-fledged industry-strength solution that meets -# the needs in IT infrastructure and application monitoring for -# service performance. -# -# 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. -# - -package apps::monitoring::quanta::restapi::mode::webscenariosavailability; - -use base qw(centreon::plugins::templates::counter); - -use strict; -use warnings; - -sub set_counters { - my ($self, %options) = @_; - - $self->{maps_counters_type} = [ - { name => 'global', type => 1, cb_prefix_output => 'prefix_output' } - ]; - - $self->{maps_counters}->{global} = [ - { label => 'total-response-time', nlabel => 'total.response.time.seconds', set => { - key_values => [ { name => 'response_time' }, { name => 'display' } ], - output_template => 'Total Response Time: %.3fs', - perfdatas => [ - { value => 'response_time', template => '%.3f', - min => 0, unit => 's', label_extra_instance => 1, instance_use => 'display' }, - ], - } - }, - { label => 'availability', nlabel => 'availability.percentage', set => { - key_values => [ { name => 'availability' }, { name => 'display' } ], - output_template => 'Availability: %.2f%%', - perfdatas => [ - { value => 'availability', template => '%s', - min => 0, max => 100, unit => '%', label_extra_instance => 1, instance_use => 'display' }, - ], - } - }, - { label => 'step-response-time', nlabel => 'step.response.time.seconds', set => { - key_values => [ { name => 'avg_step_response_time' }, { name => 'display' } ], - output_template => 'Step Average Response Time: %.3fs', - perfdatas => [ - { value => 'avg_step_response_time', template => '%.3f', - min => 0, unit => 's', label_extra_instance => 1, instance_use => 'display' }, - ], - } - }, - ]; -} - -sub prefix_output { - my ($self, %options) = @_; - - return "Scenario '" . $options{instance_value}->{display} . "' "; -} - -sub new { - my ($class, %options) = @_; - my $self = $class->SUPER::new(package => __PACKAGE__, %options, force_new_perfdata => 1); - bless $self, $class; - - $options{options}->add_options(arguments => { - "scenario-id:s" => { name => 'scenario_id' }, - "timeframe:s" => { name => 'timeframe', default => 900 }, - }); - - return $self; -} - -sub check_options { - my ($self, %options) = @_; - $self->SUPER::check_options(%options); - - if (!defined($self->{option_results}->{scenario_id}) || $self->{option_results}->{scenario_id} eq '') { - $self->{output}->add_option_msg(short_msg => "Need to specify --scenario-id option."); - $self->{output}->option_exit(); - } -} - -sub manage_selection { - my ($self, %options) = @_; - - $self->{global} = {}; - - my $from = DateTime->now->subtract(seconds => $self->{option_results}->{timeframe}); - my $to = DateTime->now; - my $from_epoch = $from->epoch; - my $to_epoch = $to->epoch; - my $url = '/partners/report/' . $options{custom}->get_api_token . - '?scenario=' . $self->{option_results}->{scenario_id} . - '&from=' . $from_epoch . '&to=' . $to_epoch; - - my $results = $options{custom}->request_api(url_path => $url); - - $self->{global}->{$results->{site}->{id}}->{display} = $results->{site}->{scenario}; - $self->{global}->{$results->{site}->{id}}->{availability} = $results->{site}->{availability} * 100; - $self->{global}->{$results->{site}->{id}}->{avg_step_response_time} = $results->{site}->{avg_step_response_time}; - foreach my $response (@{$results->{site}->{scenario_response_times}}) { - $self->{global}->{$results->{site}->{id}}->{response_time} += $response->{value}; - } - $self->{global}->{$results->{site}->{id}}->{response_time} /= scalar(@{$results->{site}->{scenario_response_times}}) - if (scalar(@{$results->{site}->{scenario_response_times}}) > 0); -} - -1; - -__END__ - -=head1 MODE - -Check web scenario availability metrics. - -(Data are delayed by a minimum of 3 hours) - -=over 8 - -=item B<--scenario-id> - -Set ID of the scenario (mandatory option). - -=item B<--timeframe> - -Set timeframe in seconds (default: 900). - -=item B<--warning-*> B<--critical-*> - -Can be: 'total-response-time', 'availability', -'step-response-time'. - -=back - -=cut diff --git a/src/apps/monitoring/quanta/restapi/plugin.pm b/src/apps/monitoring/quanta/restapi/plugin.pm index d47636c78..ee3b854ad 100644 --- a/src/apps/monitoring/quanta/restapi/plugin.pm +++ b/src/apps/monitoring/quanta/restapi/plugin.pm @@ -1,5 +1,5 @@ # -# Copyright 2024 Centreon (http://www.centreon.com/) +# Copyright 2025 Centreon (http://www.centreon.com/) # # Centreon is a full-fledged industry-strength solution that meets # the needs in IT infrastructure and application monitoring for @@ -30,11 +30,15 @@ sub new { bless $self, $class; $self->{version} = '0.1'; - %{$self->{modes}} = ( - 'web-scenarios-availability' => 'apps::monitoring::quanta::restapi::mode::webscenariosavailability', - ); + $self->{modes} = { + 'list-user-journeys' => 'apps::monitoring::quanta::restapi::mode::listuserjourneys', + 'rum' => 'apps::monitoring::quanta::restapi::mode::rum', + 'site-overview' => 'apps::monitoring::quanta::restapi::mode::siteoverview', + 'user-journey-incidents' => 'apps::monitoring::quanta::restapi::mode::userjourneyincidents', + 'user-journey-statistics' => 'apps::monitoring::quanta::restapi::mode::userjourneystatistics' + }; - $self->{custom_modes}{api} = 'apps::monitoring::quanta::restapi::custom::api'; + $self->{custom_modes}->{api} = 'apps::monitoring::quanta::restapi::custom::api'; return $self; } @@ -44,6 +48,6 @@ __END__ =head1 PLUGIN DESCRIPTION -Check Quanta.io application probes results. +Check Quanta application probes results. =cut diff --git a/tests/apps/monitoring/quanta/restapi/listeuserjourneys.robot b/tests/apps/monitoring/quanta/restapi/listeuserjourneys.robot new file mode 100644 index 000000000..385c52280 --- /dev/null +++ b/tests/apps/monitoring/quanta/restapi/listeuserjourneys.robot @@ -0,0 +1,33 @@ +*** Settings *** +Documentation Quanta + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}quanta.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=apps::monitoring::quanta::restapi::plugin +... --hostname=${HOSTNAME} +... --api-token=PaSsWoRd +... --site-id=10 +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +ListUserJourneys ${tc} + [Tags] quanta api + ${command} Catenate + ... ${CMD} + ... --mode=list-user-journeys + ... ${extra_options} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp} + Examples: tc extraoptions expected_regexp -- + ... 1 ${EMPTY} ^User journeys: diff --git a/tests/apps/monitoring/quanta/restapi/quanta.mockoon.json b/tests/apps/monitoring/quanta/restapi/quanta.mockoon.json new file mode 100644 index 000000000..082fcc270 --- /dev/null +++ b/tests/apps/monitoring/quanta/restapi/quanta.mockoon.json @@ -0,0 +1,754 @@ +{ + "uuid": "5469fd1e-4a50-463f-840a-df519d945878", + "lastMigration": 33, + "name": "Quanta.mockoon", + "endpointPrefix": "", + "latency": 0, + "port": 3006, + "hostname": "", + "folders": [], + "routes": [ + { + "uuid": "7959e15f-ca2e-42b4-a375-e26189dba421", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "", + "responses": [ + { + "uuid": "aef9fcfa-10ed-4bea-88ba-fc81947cc047", + "body": "{}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "2423bf6c-19eb-406e-8b3c-52e5e4298cc7", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/v1/sites/(\\d+)/user_journeys/", + "responses": [ + { + "uuid": "26203911-c40d-48bf-8c73-ddea70b9970f", + "body": "{\"user_journeys\":[{\"id\":3666,\"name\":\"Basic user journey\",\"site_id\":10767,\"enabled\":true,\"current_incident_id\":null,\"polling_interval\":180},{\"id\":3669,\"name\":\"Connector journey\",\"site_id\":10767,\"enabled\":true,\"current_incident_id\":null,\"polling_interval\":360}]}\n", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "header", + "modifier": "authorization", + "value": "", + "invert": true, + "operator": "null" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "90991ab4-7439-4883-998d-aa7b06f2dff4", + "type": "http", + "documentation": "", + "method": "post", + "endpoint": "api/v1/data_export", + "responses": [ + { + "uuid": "bf5fe5bc-9a07-4d56-8280-e3e01f6d3416", + "body": "{\"resources\":[{\"type\":\"site\",\"id\":10767,\"name\":\"www.ariege.com\",\"metrics\":[{\"name\":\"performance_score\",\"values\":[{\"timestamp\":1755475200,\"average\":72.0},{\"timestamp\":1755561600,\"average\":76.0},{\"timestamp\":1755648000,\"average\":75.0},{\"timestamp\":1755734400,\"average\":77.0}]},{\"name\":\"digital_sobriety_score\",\"values\":[{\"timestamp\":1755475200,\"average\":56.0},{\"timestamp\":1755561600,\"average\":62.0},{\"timestamp\":1755648000,\"average\":52.0},{\"timestamp\":1755734400,\"average\":52.0}]},{\"name\":\"eco_design_score\",\"values\":[{\"timestamp\":1755475200,\"average\":62.0},{\"timestamp\":1755561600,\"average\":64.3333},{\"timestamp\":1755648000,\"average\":61.6667},{\"timestamp\":1755734400,\"average\":61.6667}]},{\"name\":\"carbon_footprint_per_click\",\"values\":[{\"timestamp\":1755475200,\"average\":1.28432},{\"timestamp\":1755561600,\"average\":0.906906},{\"timestamp\":1755648000,\"average\":1.60231},{\"timestamp\":1755734400,\"average\":1.61868}]}]}]}", + "latency": 0, + "statusCode": 200, + "label": "site-overview", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "header", + "modifier": "authorization", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "$.resources[0].id", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "$.resources[0].type", + "value": "site", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "AND", + "disableTemplating": false, + "fallbackTo404": true, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "c0e5a0c9-2897-4712-936a-9d610c228e20", + "body": "{\"resources\":[{\"type\":\"interaction\",\"id\":19493,\"name\":\"Home\",\"metrics\":[{\"name\":\"hero_time\",\"values\":[{\"timestamp\":1756115640,\"average\":36656.8},{\"timestamp\":1756115820,\"average\":36787.3}]},{\"name\":\"speed_index\",\"values\":[{\"timestamp\":1756115640,\"average\":8993.9},{\"timestamp\":1756115820,\"average\":10660.5}]},{\"name\":\"net_request_ttfb\",\"values\":[{\"timestamp\":1756115640,\"average\":11.911},{\"timestamp\":1756115820,\"average\":1134.36}]},{\"name\":\"lh_performance_score\",\"values\":[{\"timestamp\":1756115640,\"average\":30},{\"timestamp\":1756115820,\"average\":10}]}]},{\"type\":\"interaction\",\"id\":19494,\"name\":\"Decline cookies\",\"metrics\":[{\"name\":\"hero_time\",\"values\":[{\"timestamp\":1756115640,\"average\":570.811},{\"timestamp\":1756115820,\"average\":727.254}]},{\"name\":\"lh_performance_score\",\"values\":[{\"timestamp\":1756115640,\"average\":30},{\"timestamp\":1756115820,\"average\":10}]}]},{\"type\":\"interaction\",\"id\":19495,\"name\":\"Category\",\"metrics\":[{\"name\":\"hero_time\",\"values\":[{\"timestamp\":1756115640,\"average\":1233.38},{\"timestamp\":1756115820,\"average\":1822.43}]},{\"name\":\"speed_index\",\"values\":[{\"timestamp\":1756115640,\"average\":6328.55},{\"timestamp\":1756115820,\"average\":5653.49}]},{\"name\":\"net_request_ttfb\",\"values\":[{\"timestamp\":1756115640,\"average\":10.313},{\"timestamp\":1756115820,\"average\":1208.27}]},{\"name\":\"lh_performance_score\",\"values\":[{\"timestamp\":1756115640,\"average\":30},{\"timestamp\":1756115820,\"average\":10}]}]},{\"type\":\"journey\",\"id\":3666,\"name\":\"Basic user journey\",\"metrics\":[{\"name\":\"total_hero_time\",\"values\":[{\"timestamp\":1756115640,\"average\":38461.0},{\"timestamp\":1756115820,\"average\":39337.0}]},{\"name\":\"total_speed_index\",\"values\":[{\"timestamp\":1756115640,\"average\":15322.4},{\"timestamp\":1756115820,\"average\":16314.0}]},{\"name\":\"total_net_request_ttfb\",\"values\":[{\"timestamp\":1756115640,\"average\":22.224},{\"timestamp\":1756115820,\"average\":2342.64}]},{\"name\":\"avg_lh_performance_score\",\"values\":[{\"timestamp\":1731871034,\"average\":73.6296},{\"timestamp\":1737932096,\"average\":76.85200043320032}]}]}]}\n", + "latency": 0, + "statusCode": 200, + "label": "interaction", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "header", + "modifier": "authorization", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "$.resources[0].id", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "$.resources[0].type", + "value": "interaction", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "AND", + "disableTemplating": false, + "fallbackTo404": true, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "3aa05ca7-7eea-4081-b248-e18334bf8b2f", + "body": "{\"resources\":[{\"type\":\"journey\",\"id\":3666,\"name\":\"Basic user journey\",\"metrics\":[{\"name\":\"total_hero_time\",\"values\":[{\"timestamp\":1755868500,\"average\":38933.4},{\"timestamp\":1755868680,\"average\":34607.9},{\"timestamp\":1755868860,\"average\":39407.7},{\"timestamp\":1755869040,\"average\":38513.6},{\"timestamp\":1755869220,\"average\":36120.3},{\"timestamp\":1755869400,\"average\":42274.8},{\"timestamp\":1755869580,\"average\":42229.5},{\"timestamp\":1755869760,\"average\":34202.7},{\"timestamp\":1755869940,\"average\":36813.0},{\"timestamp\":1755870120,\"average\":36049.8},{\"timestamp\":1755870300,\"average\":53831.2},{\"timestamp\":1755870480,\"average\":34042.3},{\"timestamp\":1755870660,\"average\":39423.9},{\"timestamp\":1755870840,\"average\":37858.8},{\"timestamp\":1755871020,\"average\":37836.5},{\"timestamp\":1755871200,\"average\":36322.6},{\"timestamp\":1755871380,\"average\":42674.1},{\"timestamp\":1755871560,\"average\":39852.2},{\"timestamp\":1755871740,\"average\":31803.4},{\"timestamp\":1755871920,\"average\":44416.1},{\"timestamp\":1755872100,\"average\":35933.1}]},{\"name\":\"total_speed_index\",\"values\":[{\"timestamp\":1755868500,\"average\":13687.7},{\"timestamp\":1755868680,\"average\":11300.4},{\"timestamp\":1755868860,\"average\":16571.3},{\"timestamp\":1755869040,\"average\":19220.5},{\"timestamp\":1755869220,\"average\":16938.3},{\"timestamp\":1755869400,\"average\":19875.6},{\"timestamp\":1755869580,\"average\":14047.5},{\"timestamp\":1755869760,\"average\":12821.4},{\"timestamp\":1755869940,\"average\":11490.3},{\"timestamp\":1755870120,\"average\":16767.4},{\"timestamp\":1755870300,\"average\":30773.8},{\"timestamp\":1755870480,\"average\":13725.9},{\"timestamp\":1755870660,\"average\":10890.2},{\"timestamp\":1755870840,\"average\":17891.7},{\"timestamp\":1755871020,\"average\":17390.6},{\"timestamp\":1755871200,\"average\":14207.4},{\"timestamp\":1755871380,\"average\":17310.6},{\"timestamp\":1755871560,\"average\":20124.4},{\"timestamp\":1755871740,\"average\":12337.0},{\"timestamp\":1755871920,\"average\":18064.3},{\"timestamp\":1755872100,\"average\":13754.2}]},{\"name\":\"total_net_request_ttfb\",\"values\":[{\"timestamp\":1755868500,\"average\":25.007},{\"timestamp\":1755868680,\"average\":26.938},{\"timestamp\":1755868860,\"average\":2374.37},{\"timestamp\":1755869040,\"average\":2270.23},{\"timestamp\":1755869220,\"average\":28.352},{\"timestamp\":1755869400,\"average\":4159.98},{\"timestamp\":1755869580,\"average\":46.453},{\"timestamp\":1755869760,\"average\":83.631},{\"timestamp\":1755869940,\"average\":33.742},{\"timestamp\":1755870120,\"average\":2349.4},{\"timestamp\":1755870300,\"average\":16032.7},{\"timestamp\":1755870480,\"average\":90.23},{\"timestamp\":1755870660,\"average\":53.019},{\"timestamp\":1755870840,\"average\":1735.43},{\"timestamp\":1755871020,\"average\":1975.22},{\"timestamp\":1755871200,\"average\":76.923},{\"timestamp\":1755871380,\"average\":66.28},{\"timestamp\":1755871560,\"average\":2350.19},{\"timestamp\":1755871740,\"average\":43.881},{\"timestamp\":1755871920,\"average\":2495.67},{\"timestamp\":1755872100,\"average\":33.563}]},{\"name\":\"avg_lh_performance_score\",\"values\":[{\"timestamp\":1731871034,\"average\":73.6296},{\"timestamp\":1737932096,\"average\":76.85200043320032}]}]}]}", + "latency": 0, + "statusCode": 200, + "label": "user--journey-statistics", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "header", + "modifier": "authorization", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "$.resources[0].id", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "$.resources[0].type", + "value": "journey", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "AND", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "dd6d5882-e0a9-4f13-80b6-ac157ebca4d0", + "body": "{pasbon}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": true, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "80a54f9f-9304-4e32-8629-bf0a61d9c4d7", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/v1/sites/(\\d+)/user_journeys/(\\d+)/incidents", + "responses": [ + { + "uuid": "a50c45ca-6b1d-4018-94ef-26facd0b2806", + "body": "{\"incidents\":[{\"id\":6249583,\"start_clock\":1752446880,\"end_clock\":1752447060,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6252544,\"start_clock\":1752530580,\"end_clock\":1752530760,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6254112,\"start_clock\":1752574860,\"end_clock\":1752575040,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19494,\"kind\":\"bad_response_code\"},{\"id\":6258742,\"start_clock\":1752700500,\"end_clock\":1752700680,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19493,\"kind\":\"interaction_timeout\"},{\"id\":6261903,\"start_clock\":1752787440,\"end_clock\":1752787620,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6262628,\"start_clock\":1752807780,\"end_clock\":1752807960,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6267737,\"start_clock\":1752968880,\"end_clock\":1752969060,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6270458,\"start_clock\":1753058520,\"end_clock\":1753058700,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19493,\"kind\":\"expectation_failed\"},{\"id\":6271593,\"start_clock\":1753093620,\"end_clock\":1753093800,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6285465,\"start_clock\":1753496100,\"end_clock\":1753496280,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6292630,\"start_clock\":1753693380,\"end_clock\":1753693560,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6292916,\"start_clock\":1753701120,\"end_clock\":1753701300,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19493,\"kind\":\"unknown_browser_error\"},{\"id\":6292923,\"start_clock\":1753701300,\"end_clock\":1753701480,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"unknown_browser_error\"},{\"id\":6292933,\"start_clock\":1753701480,\"end_clock\":1753701660,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19493,\"kind\":\"unknown_browser_error\"},{\"id\":6296215,\"start_clock\":1753783200,\"end_clock\":1753783380,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6298189,\"start_clock\":1753834860,\"end_clock\":1753835040,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19493,\"kind\":\"interaction_timeout\"},{\"id\":6302040,\"start_clock\":1753930620,\"end_clock\":1753930800,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6305818,\"start_clock\":1754022960,\"end_clock\":1754023140,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6311061,\"start_clock\":1754154900,\"end_clock\":1754155080,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6314803,\"start_clock\":1754253000,\"end_clock\":1754253180,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6318022,\"start_clock\":1754335080,\"end_clock\":1754335440,\"duration\":360,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6320016,\"start_clock\":1754385660,\"end_clock\":1754385840,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6320233,\"start_clock\":1754390700,\"end_clock\":1754390880,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6330359,\"start_clock\":1754664480,\"end_clock\":1754664660,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19493,\"kind\":\"expectation_failed\"},{\"id\":6330361,\"start_clock\":1754664660,\"end_clock\":1754665020,\"duration\":360,\"journey_id\":3666,\"interaction_id\":19493,\"kind\":\"expectation_failed\"},{\"id\":6330695,\"start_clock\":1754675100,\"end_clock\":1754675280,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6333267,\"start_clock\":1754750700,\"end_clock\":1754750880,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6346420,\"start_clock\":1755103500,\"end_clock\":1755103680,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6356576,\"start_clock\":1755385560,\"end_clock\":1755385740,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6360243,\"start_clock\":1755491400,\"end_clock\":1755491580,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6361167,\"start_clock\":1755516060,\"end_clock\":1755516240,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"interaction_timeout\"},{\"id\":6374087,\"start_clock\":1755852660,\"duration\":180,\"journey_id\":3666,\"interaction_id\":19495,\"kind\":\"expectation_failed\"}]}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "header", + "modifier": "authorization", + "value": "", + "invert": true, + "operator": "null" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "579b9b50-b41b-44b0-b44d-5313bd45336f", + "type": "http", + "documentation": "", + "method": "post", + "endpoint": "api/v1/rum_data_export", + "responses": [ + { + "uuid": "8923b083-0b32-4f78-b85e-6e8829be33a6", + "body": "{\"datapoints_period\":1800,\"end_ts\":1755873869,\"progress\":1.0,\"results\":[{\"datapoints\":[[1755872069,{\"backend_time\":{\"count\":46,\"mean\":1371.891304347826,\"sum\":63107},\"bounces\":{\"count\":27,\"mean\":0.4074074074074074,\"sum\":11},\"frontend_time\":{\"count\":49,\"mean\":5404.734693877551,\"sum\":264832},\"interaction_to_next_paint\":{\"count\":68,\"mean\":45.411764705882355,\"sum\":3088},\"page_views\":{\"count\":49,\"mean\":1.0,\"sum\":49},\"sessions\":{\"count\":21,\"mean\":1.0,\"sum\":21},\"speed_index\":{\"count\":38,\"mean\":2345.5789473684213,\"sum\":89132}}]],\"dimensions\":{\"os\":\"Windows\"},\"total\":{\"backend_time\":{\"count\":46,\"mean\":1371.891304347826,\"sum\":63107},\"bounces\":{\"count\":27,\"mean\":0.4074074074074074,\"sum\":11},\"frontend_time\":{\"count\":49,\"mean\":5404.734693877551,\"sum\":264832},\"interaction_to_next_paint\":{\"count\":68,\"mean\":45.411764705882355,\"sum\":3088},\"page_views\":{\"count\":49,\"mean\":1.0,\"sum\":49},\"sessions\":{\"count\":21,\"mean\":1.0,\"sum\":21},\"speed_index\":{\"count\":38,\"mean\":2345.5789473684213,\"sum\":89132}}},{\"datapoints\":[[1755872069,{\"backend_time\":{\"count\":1,\"mean\":1264.0,\"sum\":1264},\"bounces\":{\"count\":2,\"mean\":0.0,\"sum\":0},\"frontend_time\":{\"count\":3,\"mean\":1916.0,\"sum\":5748},\"page_views\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":1445.0,\"sum\":1445}}]],\"dimensions\":{\"os\":\"Mac OS X\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":1264.0,\"sum\":1264},\"bounces\":{\"count\":2,\"mean\":0.0,\"sum\":0},\"frontend_time\":{\"count\":3,\"mean\":1916.0,\"sum\":5748},\"page_views\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":1445.0,\"sum\":1445}}},{\"datapoints\":[[1755872069,{\"backend_time\":{\"count\":1,\"mean\":3092.0,\"sum\":3092},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":11991.0,\"sum\":11991},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":6095.0,\"sum\":6095}}]],\"dimensions\":{\"os\":\"Android\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":3092.0,\"sum\":3092},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":11991.0,\"sum\":11991},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":6095.0,\"sum\":6095}}},{\"datapoints\":[[1755872069,{\"backend_time\":{\"count\":1,\"mean\":6226.0,\"sum\":6226},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":8563.0,\"sum\":8563},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":6764.0,\"sum\":6764}}]],\"dimensions\":{\"os\":\"Linux\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":6226.0,\"sum\":6226},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":8563.0,\"sum\":8563},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":6764.0,\"sum\":6764}}},{\"datapoints\":[[1755872069,{\"backend_time\":{\"count\":1,\"mean\":1183.0,\"sum\":1183},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":2572.0,\"sum\":2572},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":1446.0,\"sum\":1446}}]],\"dimensions\":{\"os\":\"iOS\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":1183.0,\"sum\":1183},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":2572.0,\"sum\":2572},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":1446.0,\"sum\":1446}}}],\"start_ts\":1755872069}", + "latency": 0, + "statusCode": 200, + "label": "perspective=os", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "header", + "modifier": "authorization", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "$.index", + "value": "os", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "AND", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "1fee69d0-e80a-42c3-94be-8e86b4abbc3e", + "body": "{\"datapoints_period\":1800,\"end_ts\":1755874350,\"progress\":1.0,\"results\":[{\"datapoints\":[[1755872550,{\"backend_time\":{\"count\":13,\"mean\":1225.0,\"sum\":15925},\"frontend_time\":{\"count\":13,\"mean\":6997.461538461538,\"sum\":90967},\"interaction_to_next_paint\":{\"count\":20,\"mean\":21.2,\"sum\":424},\"page_views\":{\"count\":13,\"mean\":1.0,\"sum\":13},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":7,\"mean\":2967.8571428571427,\"sum\":20775}}]],\"dimensions\":{\"city\":\"Maisons-Alfort\",\"country_code\":\"FR\"},\"total\":{\"backend_time\":{\"count\":13,\"mean\":1225.0,\"sum\":15925},\"frontend_time\":{\"count\":13,\"mean\":6997.461538461538,\"sum\":90967},\"interaction_to_next_paint\":{\"count\":20,\"mean\":21.2,\"sum\":424},\"page_views\":{\"count\":13,\"mean\":1.0,\"sum\":13},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":7,\"mean\":2967.8571428571427,\"sum\":20775}}},{\"datapoints\":[[1755872550,{\"backend_time\":{\"count\":9,\"mean\":1380.0,\"sum\":12420},\"bounces\":{\"count\":7,\"mean\":0.14285714285714285,\"sum\":1},\"frontend_time\":{\"count\":11,\"mean\":3178.818181818182,\"sum\":34967},\"interaction_to_next_paint\":{\"count\":13,\"mean\":85.53846153846153,\"sum\":1112},\"page_views\":{\"count\":11,\"mean\":1.0,\"sum\":11},\"sessions\":{\"count\":5,\"mean\":1.0,\"sum\":5},\"speed_index\":{\"count\":9,\"mean\":1817.4444444444443,\"sum\":16357}}]],\"dimensions\":{\"city\":null,\"country_code\":\"FR\"},\"total\":{\"backend_time\":{\"count\":9,\"mean\":1380.0,\"sum\":12420},\"bounces\":{\"count\":7,\"mean\":0.14285714285714285,\"sum\":1},\"frontend_time\":{\"count\":11,\"mean\":3178.818181818182,\"sum\":34967},\"interaction_to_next_paint\":{\"count\":13,\"mean\":85.53846153846153,\"sum\":1112},\"page_views\":{\"count\":11,\"mean\":1.0,\"sum\":11},\"sessions\":{\"count\":5,\"mean\":1.0,\"sum\":5},\"speed_index\":{\"count\":9,\"mean\":1817.4444444444443,\"sum\":16357}}},{\"datapoints\":[[1755872550,{\"backend_time\":{\"count\":7,\"mean\":1128.7142857142858,\"sum\":7901},\"frontend_time\":{\"count\":7,\"mean\":1716.0,\"sum\":12012},\"interaction_to_next_paint\":{\"count\":12,\"mean\":32.0,\"sum\":384},\"page_views\":{\"count\":7,\"mean\":1.0,\"sum\":7},\"speed_index\":{\"count\":7,\"mean\":1300.857142857143,\"sum\":9106}}]],\"dimensions\":{\"city\":\"Tallinn\",\"country_code\":\"EE\"},\"total\":{\"backend_time\":{\"count\":7,\"mean\":1128.7142857142858,\"sum\":7901},\"frontend_time\":{\"count\":7,\"mean\":1716.0,\"sum\":12012},\"interaction_to_next_paint\":{\"count\":12,\"mean\":32.0,\"sum\":384},\"page_views\":{\"count\":7,\"mean\":1.0,\"sum\":7},\"speed_index\":{\"count\":7,\"mean\":1300.857142857143,\"sum\":9106}}},{\"datapoints\":[[1755872550,{\"backend_time\":{\"count\":4,\"mean\":2587.75,\"sum\":10351},\"bounces\":{\"count\":5,\"mean\":0.6,\"sum\":3},\"frontend_time\":{\"count\":6,\"mean\":4897.666666666667,\"sum\":29386},\"page_views\":{\"count\":6,\"mean\":1.0,\"sum\":6},\"sessions\":{\"count\":4,\"mean\":1.0,\"sum\":4},\"speed_index\":{\"count\":4,\"mean\":3131.0,\"sum\":12524}}]],\"dimensions\":{\"city\":null,\"country_code\":\"US\"},\"total\":{\"backend_time\":{\"count\":4,\"mean\":2587.75,\"sum\":10351},\"bounces\":{\"count\":5,\"mean\":0.6,\"sum\":3},\"frontend_time\":{\"count\":6,\"mean\":4897.666666666667,\"sum\":29386},\"page_views\":{\"count\":6,\"mean\":1.0,\"sum\":6},\"sessions\":{\"count\":4,\"mean\":1.0,\"sum\":4},\"speed_index\":{\"count\":4,\"mean\":3131.0,\"sum\":12524}}},{\"datapoints\":[[1755872550,{\"backend_time\":{\"count\":4,\"mean\":1305.25,\"sum\":5221},\"bounces\":{\"count\":6,\"mean\":0.0,\"sum\":0},\"frontend_time\":{\"count\":5,\"mean\":3521.0,\"sum\":17605},\"interaction_to_next_paint\":{\"count\":12,\"mean\":51.333333333333336,\"sum\":616},\"page_views\":{\"count\":5,\"mean\":1.0,\"sum\":5},\"sessions\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"speed_index\":{\"count\":5,\"mean\":1403.4,\"sum\":7017}}]],\"dimensions\":{\"city\":\"Paris\",\"country_code\":\"FR\"},\"total\":{\"backend_time\":{\"count\":4,\"mean\":1305.25,\"sum\":5221},\"bounces\":{\"count\":6,\"mean\":0.0,\"sum\":0},\"frontend_time\":{\"count\":5,\"mean\":3521.0,\"sum\":17605},\"interaction_to_next_paint\":{\"count\":12,\"mean\":51.333333333333336,\"sum\":616},\"page_views\":{\"count\":5,\"mean\":1.0,\"sum\":5},\"sessions\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"speed_index\":{\"count\":5,\"mean\":1403.4,\"sum\":7017}}},{\"datapoints\":[[1755872550,{\"backend_time\":{\"count\":3,\"mean\":1171.3333333333333,\"sum\":3514},\"bounces\":{\"count\":2,\"mean\":0.0,\"sum\":0},\"frontend_time\":{\"count\":3,\"mean\":4867.333333333333,\"sum\":14602},\"interaction_to_next_paint\":{\"count\":4,\"mean\":52.0,\"sum\":208},\"page_views\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":3,\"mean\":1357.6666666666667,\"sum\":4073}}]],\"dimensions\":{\"city\":\"Rasta\",\"country_code\":\"NO\"},\"total\":{\"backend_time\":{\"count\":3,\"mean\":1171.3333333333333,\"sum\":3514},\"bounces\":{\"count\":2,\"mean\":0.0,\"sum\":0},\"frontend_time\":{\"count\":3,\"mean\":4867.333333333333,\"sum\":14602},\"interaction_to_next_paint\":{\"count\":4,\"mean\":52.0,\"sum\":208},\"page_views\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":3,\"mean\":1357.6666666666667,\"sum\":4073}}},{\"datapoints\":[[1755872550,{\"backend_time\":{\"count\":3,\"mean\":1660.0,\"sum\":4980},\"bounces\":{\"count\":2,\"mean\":0.0,\"sum\":0},\"frontend_time\":{\"count\":3,\"mean\":3668.6666666666665,\"sum\":11006},\"interaction_to_next_paint\":{\"count\":6,\"mean\":36.0,\"sum\":216},\"page_views\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":3,\"mean\":2294.6666666666665,\"sum\":6884}}]],\"dimensions\":{\"city\":\"French Rocks\",\"country_code\":\"IN\"},\"total\":{\"backend_time\":{\"count\":3,\"mean\":1660.0,\"sum\":4980},\"bounces\":{\"count\":2,\"mean\":0.0,\"sum\":0},\"frontend_time\":{\"count\":3,\"mean\":3668.6666666666665,\"sum\":11006},\"interaction_to_next_paint\":{\"count\":6,\"mean\":36.0,\"sum\":216},\"page_views\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":3,\"mean\":2294.6666666666665,\"sum\":6884}}},{\"datapoints\":[[1755872550,{\"backend_time\":{\"count\":1,\"mean\":1183.0,\"sum\":1183},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":2572.0,\"sum\":2572},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":1446.0,\"sum\":1446}}]],\"dimensions\":{\"city\":\"Montreuil\",\"country_code\":\"FR\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":1183.0,\"sum\":1183},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":2572.0,\"sum\":2572},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":1446.0,\"sum\":1446}}},{\"datapoints\":[[1755872550,{\"backend_time\":{\"count\":1,\"mean\":3092.0,\"sum\":3092},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":11991.0,\"sum\":11991},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":6095.0,\"sum\":6095}}]],\"dimensions\":{\"city\":\"Casablanca\",\"country_code\":\"MA\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":3092.0,\"sum\":3092},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":11991.0,\"sum\":11991},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":6095.0,\"sum\":6095}}},{\"datapoints\":[[1755872550,{\"backend_time\":{\"count\":1,\"mean\":2693.0,\"sum\":2693},\"bounces\":{\"count\":2,\"mean\":0.0,\"sum\":0},\"frontend_time\":{\"count\":1,\"mean\":23566.0,\"sum\":23566},\"interaction_to_next_paint\":{\"count\":1,\"mean\":64.0,\"sum\":64},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":17320.0,\"sum\":17320}}]],\"dimensions\":{\"city\":null,\"country_code\":\"SN\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":2693.0,\"sum\":2693},\"bounces\":{\"count\":2,\"mean\":0.0,\"sum\":0},\"frontend_time\":{\"count\":1,\"mean\":23566.0,\"sum\":23566},\"interaction_to_next_paint\":{\"count\":1,\"mean\":64.0,\"sum\":64},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":17320.0,\"sum\":17320}}}],\"start_ts\":1755872550}", + "latency": 0, + "statusCode": 200, + "label": "perspective=city", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "header", + "modifier": "authorization", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "$.index", + "value": "city", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "AND", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "0ef8f845-c193-4d53-85ef-edcc0d7b800b", + "body": "{\"datapoints_period\":1800,\"end_ts\":1755874717,\"progress\":1.0,\"results\":[{\"datapoints\":[[1755872917,{\"backend_time\":{\"count\":6,\"mean\":1741.6666666666667,\"sum\":10450},\"frontend_time\":{\"count\":8,\"mean\":5020.375,\"sum\":40163},\"interaction_to_next_paint\":{\"count\":8,\"mean\":74.0,\"sum\":592},\"page_views\":{\"count\":8,\"mean\":1.0,\"sum\":8},\"speed_index\":{\"count\":7,\"mean\":3838.714285714286,\"sum\":26871}}]],\"dimensions\":{\"url\":\"/fr/\"},\"total\":{\"backend_time\":{\"count\":6,\"mean\":1741.6666666666667,\"sum\":10450},\"frontend_time\":{\"count\":8,\"mean\":5020.375,\"sum\":40163},\"interaction_to_next_paint\":{\"count\":8,\"mean\":74.0,\"sum\":592},\"page_views\":{\"count\":8,\"mean\":1.0,\"sum\":8},\"speed_index\":{\"count\":7,\"mean\":3838.714285714286,\"sum\":26871}}},{\"datapoints\":[[1755872917,{\"backend_time\":{\"count\":5,\"mean\":2308.0,\"sum\":11540},\"frontend_time\":{\"count\":6,\"mean\":5970.0,\"sum\":35820},\"interaction_to_next_paint\":{\"count\":9,\"mean\":58.666666666666664,\"sum\":528},\"page_views\":{\"count\":6,\"mean\":1.0,\"sum\":6},\"speed_index\":{\"count\":6,\"mean\":2710.0,\"sum\":16260}}]],\"dimensions\":{\"url\":\"/\"},\"total\":{\"backend_time\":{\"count\":5,\"mean\":2308.0,\"sum\":11540},\"frontend_time\":{\"count\":6,\"mean\":5970.0,\"sum\":35820},\"interaction_to_next_paint\":{\"count\":9,\"mean\":58.666666666666664,\"sum\":528},\"page_views\":{\"count\":6,\"mean\":1.0,\"sum\":6},\"speed_index\":{\"count\":6,\"mean\":2710.0,\"sum\":16260}}},{\"datapoints\":[[1755872917,{\"backend_time\":{\"count\":1,\"mean\":1264.0,\"sum\":1264},\"frontend_time\":{\"count\":3,\"mean\":1916.0,\"sum\":5748},\"interaction_to_next_paint\":{\"count\":3,\"mean\":64.0,\"sum\":192},\"page_views\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"speed_index\":{\"count\":1,\"mean\":1445.0,\"sum\":1445}}]],\"dimensions\":{\"url\":\"/guzet/\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":1264.0,\"sum\":1264},\"frontend_time\":{\"count\":3,\"mean\":1916.0,\"sum\":5748},\"interaction_to_next_paint\":{\"count\":3,\"mean\":64.0,\"sum\":192},\"page_views\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"speed_index\":{\"count\":1,\"mean\":1445.0,\"sum\":1445}}},{\"datapoints\":[[1755872917,{\"backend_time\":{\"count\":2,\"mean\":543.5,\"sum\":1087},\"frontend_time\":{\"count\":3,\"mean\":2847.6666666666665,\"sum\":8543},\"interaction_to_next_paint\":{\"count\":4,\"mean\":112.0,\"sum\":448},\"page_views\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"speed_index\":{\"count\":2,\"mean\":1062.5,\"sum\":2125}}]],\"dimensions\":{\"url\":\"/fr/stgirons/\"},\"total\":{\"backend_time\":{\"count\":2,\"mean\":543.5,\"sum\":1087},\"frontend_time\":{\"count\":3,\"mean\":2847.6666666666665,\"sum\":8543},\"interaction_to_next_paint\":{\"count\":4,\"mean\":112.0,\"sum\":448},\"page_views\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"speed_index\":{\"count\":2,\"mean\":1062.5,\"sum\":2125}}},{\"datapoints\":[[1755872917,{\"backend_time\":{\"count\":3,\"mean\":1171.3333333333333,\"sum\":3514},\"frontend_time\":{\"count\":3,\"mean\":4867.333333333333,\"sum\":14602},\"interaction_to_next_paint\":{\"count\":4,\"mean\":52.0,\"sum\":208},\"page_views\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"speed_index\":{\"count\":3,\"mean\":1357.6666666666667,\"sum\":4073}}]],\"dimensions\":{\"url\":\"/montardit/\"},\"total\":{\"backend_time\":{\"count\":3,\"mean\":1171.3333333333333,\"sum\":3514},\"frontend_time\":{\"count\":3,\"mean\":4867.333333333333,\"sum\":14602},\"interaction_to_next_paint\":{\"count\":4,\"mean\":52.0,\"sum\":208},\"page_views\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"speed_index\":{\"count\":3,\"mean\":1357.6666666666667,\"sum\":4073}}},{\"datapoints\":[[1755872917,{\"backend_time\":{\"count\":2,\"mean\":1380.0,\"sum\":2760},\"frontend_time\":{\"count\":2,\"mean\":2198.0,\"sum\":4396},\"interaction_to_next_paint\":{\"count\":3,\"mean\":50.666666666666664,\"sum\":152},\"page_views\":{\"count\":2,\"mean\":1.0,\"sum\":2},\"speed_index\":{\"count\":2,\"mean\":1521.0,\"sum\":3042}}]],\"dimensions\":{\"url\":\"/ariege/departement/\"},\"total\":{\"backend_time\":{\"count\":2,\"mean\":1380.0,\"sum\":2760},\"frontend_time\":{\"count\":2,\"mean\":2198.0,\"sum\":4396},\"interaction_to_next_paint\":{\"count\":3,\"mean\":50.666666666666664,\"sum\":152},\"page_views\":{\"count\":2,\"mean\":1.0,\"sum\":2},\"speed_index\":{\"count\":2,\"mean\":1521.0,\"sum\":3042}}},{\"datapoints\":[[1755872917,{\"backend_time\":{\"count\":2,\"mean\":3469.5,\"sum\":6939},\"frontend_time\":{\"count\":2,\"mean\":9203.5,\"sum\":18407},\"page_views\":{\"count\":2,\"mean\":1.0,\"sum\":2},\"speed_index\":{\"count\":2,\"mean\":4092.5,\"sum\":8185}}]],\"dimensions\":{\"url\":\"/fr/contrazy/\"},\"total\":{\"backend_time\":{\"count\":2,\"mean\":3469.5,\"sum\":6939},\"frontend_time\":{\"count\":2,\"mean\":9203.5,\"sum\":18407},\"page_views\":{\"count\":2,\"mean\":1.0,\"sum\":2},\"speed_index\":{\"count\":2,\"mean\":4092.5,\"sum\":8185}}},{\"datapoints\":[[1755872917,{\"backend_time\":{\"count\":2,\"mean\":1174.5,\"sum\":2349},\"frontend_time\":{\"count\":2,\"mean\":5819.5,\"sum\":11639},\"interaction_to_next_paint\":{\"count\":3,\"mean\":29.333333333333332,\"sum\":88},\"page_views\":{\"count\":2,\"mean\":1.0,\"sum\":2},\"speed_index\":{\"count\":2,\"mean\":6318.0,\"sum\":12636}}]],\"dimensions\":{\"url\":\"/fr/merigon/\"},\"total\":{\"backend_time\":{\"count\":2,\"mean\":1174.5,\"sum\":2349},\"frontend_time\":{\"count\":2,\"mean\":5819.5,\"sum\":11639},\"interaction_to_next_paint\":{\"count\":3,\"mean\":29.333333333333332,\"sum\":88},\"page_views\":{\"count\":2,\"mean\":1.0,\"sum\":2},\"speed_index\":{\"count\":2,\"mean\":6318.0,\"sum\":12636}}},{\"datapoints\":[[1755872917,{\"backend_time\":{\"count\":1,\"mean\":2484.0,\"sum\":2484},\"frontend_time\":{\"count\":1,\"mean\":13258.0,\"sum\":13258},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1}}]],\"dimensions\":{\"url\":\"/massat/\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":2484.0,\"sum\":2484},\"frontend_time\":{\"count\":1,\"mean\":13258.0,\"sum\":13258},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1}}},{\"datapoints\":[[1755872917,{\"backend_time\":{\"count\":1,\"mean\":1491.0,\"sum\":1491},\"frontend_time\":{\"count\":1,\"mean\":2713.0,\"sum\":2713},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":1697.0,\"sum\":1697}}]],\"dimensions\":{\"url\":\"/fr/ariege/\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":1491.0,\"sum\":1491},\"frontend_time\":{\"count\":1,\"mean\":2713.0,\"sum\":2713},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":1697.0,\"sum\":1697}}}],\"start_ts\":1755872917}", + "latency": 0, + "statusCode": 200, + "label": "perspective=url", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "header", + "modifier": "authorization", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "$.index", + "value": "url", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "AND", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "1fd11bdd-d6f0-430a-af7c-4b382ae10e0f", + "body": "{\"datapoints_period\":1800,\"end_ts\":1755874532,\"progress\":1.0,\"results\":[{\"datapoints\":[[1755872732,{\"backend_time\":{\"count\":16,\"mean\":1554.3125,\"sum\":24869},\"bounces\":{\"count\":20,\"mean\":0.5,\"sum\":10},\"frontend_time\":{\"count\":22,\"mean\":5495.909090909091,\"sum\":120910},\"interaction_to_next_paint\":{\"count\":21,\"mean\":80.38095238095238,\"sum\":1688},\"page_views\":{\"count\":22,\"mean\":1.0,\"sum\":22},\"sessions\":{\"count\":16,\"mean\":1.0,\"sum\":16},\"speed_index\":{\"count\":16,\"mean\":2706.625,\"sum\":43306}}]],\"dimensions\":{\"browser\":\"Chrome\"},\"total\":{\"backend_time\":{\"count\":16,\"mean\":1554.3125,\"sum\":24869},\"bounces\":{\"count\":20,\"mean\":0.5,\"sum\":10},\"frontend_time\":{\"count\":22,\"mean\":5495.909090909091,\"sum\":120910},\"interaction_to_next_paint\":{\"count\":21,\"mean\":80.38095238095238,\"sum\":1688},\"page_views\":{\"count\":22,\"mean\":1.0,\"sum\":22},\"sessions\":{\"count\":16,\"mean\":1.0,\"sum\":16},\"speed_index\":{\"count\":16,\"mean\":2706.625,\"sum\":43306}}},{\"datapoints\":[[1755872732,{\"backend_time\":{\"count\":18,\"mean\":1330.0555555555557,\"sum\":23941},\"bounces\":{\"count\":12,\"mean\":0.3333333333333333,\"sum\":4},\"frontend_time\":{\"count\":18,\"mean\":4063.1666666666665,\"sum\":73137},\"interaction_to_next_paint\":{\"count\":30,\"mean\":41.86666666666667,\"sum\":1256},\"page_views\":{\"count\":18,\"mean\":1.0,\"sum\":18},\"sessions\":{\"count\":8,\"mean\":1.0,\"sum\":8},\"speed_index\":{\"count\":16,\"mean\":1592.8125,\"sum\":25485}}]],\"dimensions\":{\"browser\":\"Edge\"},\"total\":{\"backend_time\":{\"count\":18,\"mean\":1330.0555555555557,\"sum\":23941},\"bounces\":{\"count\":12,\"mean\":0.3333333333333333,\"sum\":4},\"frontend_time\":{\"count\":18,\"mean\":4063.1666666666665,\"sum\":73137},\"interaction_to_next_paint\":{\"count\":30,\"mean\":41.86666666666667,\"sum\":1256},\"page_views\":{\"count\":18,\"mean\":1.0,\"sum\":18},\"sessions\":{\"count\":8,\"mean\":1.0,\"sum\":8},\"speed_index\":{\"count\":16,\"mean\":1592.8125,\"sum\":25485}}},{\"datapoints\":[[1755872732,{\"backend_time\":{\"count\":15,\"mean\":1233.4,\"sum\":18501},\"bounces\":{\"count\":3,\"mean\":0.3333333333333333,\"sum\":1},\"frontend_time\":{\"count\":15,\"mean\":6396.266666666666,\"sum\":95944},\"interaction_to_next_paint\":{\"count\":22,\"mean\":20.727272727272727,\"sum\":456},\"page_views\":{\"count\":15,\"mean\":1.0,\"sum\":15},\"sessions\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"speed_index\":{\"count\":9,\"mean\":2624.5555555555557,\"sum\":23621}}]],\"dimensions\":{\"browser\":\"Firefox\"},\"total\":{\"backend_time\":{\"count\":15,\"mean\":1233.4,\"sum\":18501},\"bounces\":{\"count\":3,\"mean\":0.3333333333333333,\"sum\":1},\"frontend_time\":{\"count\":15,\"mean\":6396.266666666666,\"sum\":95944},\"interaction_to_next_paint\":{\"count\":22,\"mean\":20.727272727272727,\"sum\":456},\"page_views\":{\"count\":15,\"mean\":1.0,\"sum\":15},\"sessions\":{\"count\":3,\"mean\":1.0,\"sum\":3},\"speed_index\":{\"count\":9,\"mean\":2624.5555555555557,\"sum\":23621}}},{\"datapoints\":[[1755872732,{\"backend_time\":{\"count\":1,\"mean\":5021.0,\"sum\":5021},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":7429.0,\"sum\":7429},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":5260.0,\"sum\":5260}}]],\"dimensions\":{\"browser\":\"Chrome Mobile\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":5021.0,\"sum\":5021},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":7429.0,\"sum\":7429},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":5260.0,\"sum\":5260}}},{\"datapoints\":[[1755872732,{\"backend_time\":{\"count\":1,\"mean\":3092.0,\"sum\":3092},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":11991.0,\"sum\":11991},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":6095.0,\"sum\":6095}}]],\"dimensions\":{\"browser\":\"Opera Mobile\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":3092.0,\"sum\":3092},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":11991.0,\"sum\":11991},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":6095.0,\"sum\":6095}}},{\"datapoints\":[[1755872732,{\"backend_time\":{\"count\":1,\"mean\":6226.0,\"sum\":6226},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":8563.0,\"sum\":8563},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":6764.0,\"sum\":6764}}]],\"dimensions\":{\"browser\":\"HeadlessChrome\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":6226.0,\"sum\":6226},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":8563.0,\"sum\":8563},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":6764.0,\"sum\":6764}}},{\"datapoints\":[[1755872732,{\"backend_time\":{\"count\":1,\"mean\":1183.0,\"sum\":1183},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":2572.0,\"sum\":2572},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":1446.0,\"sum\":1446}}]],\"dimensions\":{\"browser\":\"Mobile Safari\"},\"total\":{\"backend_time\":{\"count\":1,\"mean\":1183.0,\"sum\":1183},\"bounces\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"frontend_time\":{\"count\":1,\"mean\":2572.0,\"sum\":2572},\"page_views\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"sessions\":{\"count\":1,\"mean\":1.0,\"sum\":1},\"speed_index\":{\"count\":1,\"mean\":1446.0,\"sum\":1446}}}],\"start_ts\":1755872732}", + "latency": 0, + "statusCode": 200, + "label": "perspective=browser", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "header", + "modifier": "authorization", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "$.index", + "value": "browser", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "AND", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "c75f7efd-4a6a-44ce-ba33-50f3cfe8e4d9", + "body": "{\"datapoints_period\":1800,\"end_ts\":1755874944,\"progress\":1.0,\"results\":[{\"datapoints\":[[1755873144,{\"backend_time\":{\"count\":17,\"mean\":1859.1764705882354,\"sum\":31606},\"bounces\":{\"count\":19,\"mean\":0.5789473684210527,\"sum\":11},\"frontend_time\":{\"count\":20,\"mean\":5606.7,\"sum\":112134},\"interaction_to_next_paint\":{\"count\":22,\"mean\":79.63636363636364,\"sum\":1752},\"page_views\":{\"count\":20,\"mean\":1.0,\"sum\":20},\"sessions\":{\"count\":16,\"mean\":1.0,\"sum\":16},\"speed_index\":{\"count\":15,\"mean\":2104.9333333333334,\"sum\":31574}}]],\"dimensions\":{},\"total\":{\"backend_time\":{\"count\":56,\"mean\":1617.7142857142858,\"sum\":90592},\"bounces\":{\"count\":43,\"mean\":0.4883720930232558,\"sum\":21},\"frontend_time\":{\"count\":62,\"mean\":5494.822580645161,\"sum\":340679},\"interaction_to_next_paint\":{\"count\":74,\"mean\":46.7027027027027,\"sum\":3456},\"page_views\":{\"count\":62,\"mean\":1.0,\"sum\":62},\"sessions\":{\"count\":34,\"mean\":1.0,\"sum\":34},\"speed_index\":{\"count\":48,\"mean\":2536.6666666666665,\"sum\":121760}}}],\"start_ts\":1755873144}", + "latency": 0, + "statusCode": 200, + "label": "perspective=all", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "header", + "modifier": "authorization", + "value": "", + "invert": true, + "operator": "null" + }, + { + "target": "body", + "modifier": "$.index", + "value": "all", + "invert": false, + "operator": "equals" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + }, + { + "uuid": "dc0a228d-cbe6-456b-9435-a525f61208c1", + "body": "{}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "98aa7182-a68d-4e82-a1d4-2e9e3ec6d7ce", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/v1/sites/(\\d+)/user_journeys/(\\d+)/interactions/(\\d+)", + "responses": [ + { + "uuid": "10f1b177-286b-4ff8-924e-683a7e286d4c", + "body": "{\"interaction\":{\"id\":19494,\"name\":\"Decline cookies\",\"no\":1,\"enabled\":true,\"measured\":true,\"timeout\":null,\"available_resources\":[\"metrics\",\"incidents\"]}}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "header", + "modifier": "authorization", + "value": "", + "invert": true, + "operator": "null" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + }, + { + "uuid": "5c3c6cde-d2db-4033-a80e-5ae41bc21b84", + "type": "http", + "documentation": "", + "method": "get", + "endpoint": "api/v1/sites/(\\d+)/user_journeys/(\\d+)/interactions", + "responses": [ + { + "uuid": "006bb82c-1ad4-4cd8-b468-1ccaaa55e402", + "body": "{\"interactions\":[{\"id\":19493,\"name\":\"Home\",\"no\":0,\"enabled\":true,\"measured\":true,\"timeout\":null},{\"id\":19494,\"name\":\"Decline cookies\",\"no\":1,\"enabled\":true,\"measured\":true,\"timeout\":null},{\"id\":19495,\"name\":\"Category\",\"no\":2,\"enabled\":true,\"measured\":true,\"timeout\":null}]}", + "latency": 0, + "statusCode": 200, + "label": "", + "headers": [ + { + "key": "access-control-allow-headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + }, + { + "key": "access-control-allow-methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "access-control-allow-origin", + "value": "*" + }, + { + "key": "content-security-policy", + "value": "default-src 'none'" + }, + { + "key": "content-type", + "value": "text/html; charset=utf-8" + }, + { + "key": "x-content-type-options", + "value": "nosniff" + } + ], + "bodyType": "INLINE", + "filePath": "", + "databucketID": "", + "sendFileAsBody": false, + "rules": [ + { + "target": "header", + "modifier": "Authorization", + "value": "", + "invert": true, + "operator": "null" + } + ], + "rulesOperator": "OR", + "disableTemplating": false, + "fallbackTo404": false, + "default": false, + "crudKey": "id", + "callbacks": [] + } + ], + "responseMode": null, + "streamingMode": null, + "streamingInterval": 0 + } + ], + "rootChildren": [ + { + "type": "route", + "uuid": "7959e15f-ca2e-42b4-a375-e26189dba421" + }, + { + "type": "route", + "uuid": "2423bf6c-19eb-406e-8b3c-52e5e4298cc7" + }, + { + "type": "route", + "uuid": "90991ab4-7439-4883-998d-aa7b06f2dff4" + }, + { + "type": "route", + "uuid": "80a54f9f-9304-4e32-8629-bf0a61d9c4d7" + }, + { + "type": "route", + "uuid": "579b9b50-b41b-44b0-b44d-5313bd45336f" + }, + { + "type": "route", + "uuid": "98aa7182-a68d-4e82-a1d4-2e9e3ec6d7ce" + }, + { + "type": "route", + "uuid": "5c3c6cde-d2db-4033-a80e-5ae41bc21b84" + } + ], + "proxyMode": false, + "proxyHost": "", + "proxyRemovePrefix": false, + "tlsOptions": { + "enabled": false, + "type": "CERT", + "pfxPath": "", + "certPath": "", + "keyPath": "", + "caPath": "", + "passphrase": "" + }, + "cors": true, + "headers": [ + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "Access-Control-Allow-Origin", + "value": "*" + }, + { + "key": "Access-Control-Allow-Methods", + "value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS" + }, + { + "key": "Access-Control-Allow-Headers", + "value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With" + } + ], + "proxyReqHeaders": [ + { + "key": "", + "value": "" + } + ], + "proxyResHeaders": [ + { + "key": "", + "value": "" + } + ], + "data": [], + "callbacks": [] +} \ No newline at end of file diff --git a/tests/apps/monitoring/quanta/restapi/rum.robot b/tests/apps/monitoring/quanta/restapi/rum.robot new file mode 100644 index 000000000..8b6729f17 --- /dev/null +++ b/tests/apps/monitoring/quanta/restapi/rum.robot @@ -0,0 +1,54 @@ +*** Settings *** +Documentation Quanta + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}quanta.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=apps::monitoring::quanta::restapi::plugin +... --hostname=${HOSTNAME} +... --api-token=PaSsWoRd +... --site-id=10 +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +Rum ${tc} + [Tags] quanta api + ${command} Catenate + ... ${CMD} + ... --mode=rum + ... ${extra_options} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp} + + Examples: tc extraoptions expected_regexp -- + ... 1 ${EMPTY} OK: all pages: sessions: 34, page views: 62, bounce rate: 43%, ttfb: 1617.714ms, onload time: 5494.82ms, interaction to next paint: 46.70ms, speed index: 2536.67ms \\\\| 'pages#sessions.count'=34;;;0; 'pages#pageviews.count'=62;;;0; 'pages#bounce.rate.percentage'=43%;;;0;100 'pages#ttfb.milliseconds'=1617.71ms;;;0; 'pages#onload.time.milliseconds'=5494.82ms;;;0; 'pages#nextpaint.interaction.time.milliseconds'=46.70ms;;;0; 'pages#speedindex.time.milliseconds'=2536.67ms;;;0; + ... 2 --perspective=all OK: all pages: sessions: 34, page views: 62, bounce rate: 43%, ttfb: 1617.714ms, onload time: 5494.82ms, interaction to next paint: 46.70ms, speed index: 2536.67ms \\\\| 'pages#sessions.count'=34;;;0; 'pages#pageviews.count'=62;;;0; 'pages#bounce.rate.percentage'=43%;;;0;100 'pages#ttfb.milliseconds'=1617.71ms;;;0; 'pages#onload.time.milliseconds'=5494.82ms;;;0; 'pages#nextpaint.interaction.time.milliseconds'=46.70ms;;;0; 'pages#speedindex.time.milliseconds'=2536.67ms;;;0; + ... 3 --perspective=all --critical-page-views=:10 ^CRITICAL: all pages.+$ + ... 4 --perspective=all --warning-page-views=:10 ^WARNING: all pages.+$ + ... 5 --perspective=url OK: All RUM counters are OK \\\\| '/#pageviews.count'=6;;;0; '/#ttfb.milliseconds'=2308.00ms;;;0; '/#onload.time.milliseconds'=5970.00ms;;;0; '/#nextpaint.interaction.time.milliseconds'=58.67ms;;;0; '/#speedindex.time.milliseconds'=2710.00ms;;;0; '/ariege/departement/#pageviews.count'=2;;;0; '/ariege/departement/#ttfb.milliseconds'=1380.00ms;;;0; '/ariege/departement/#onload.time.milliseconds'=2198.00ms;;;0; '/ariege/departement/#nextpaint.interaction.time.milliseconds'=50.67ms;;;0; '/ariege/departement/#speedindex.time.milliseconds'=1521.00ms;;;0; '/fr/#pageviews.count'=8;;;0; '/fr/#ttfb.milliseconds'=1741.67ms;;;0; '/fr/#onload.time.milliseconds'=5020.38ms;;;0; '/fr/#nextpaint.interaction.time.milliseconds'=74.00ms;;;0; '/fr/#speedindex.time.milliseconds'=3838.71ms;;;0; '/fr/ariege/#pageviews.count'=1;;;0; '/fr/ariege/#ttfb.milliseconds'=1491.00ms;;;0; '/fr/ariege/#onload.time.milliseconds'=2713.00ms;;;0; '/fr/ariege/#speedindex.time.milliseconds'=1697.00ms;;;0; '/fr/contrazy/#pageviews.count'=2;;;0; '/fr/contrazy/#ttfb.milliseconds'=3469.50ms;;;0; '/fr/contrazy/#onload.time.milliseconds'=9203.50ms;;;0; '/fr/contrazy/#speedindex.time.milliseconds'=4092.50ms;;;0; '/fr/merigon/#pageviews.count'=2;;;0; '/fr/merigon/#ttfb.milliseconds'=1174.50ms;;;0; '/fr/merigon/#onload.time.milliseconds'=5819.50ms;;;0; '/fr/merigon/#nextpaint.interaction.time.milliseconds'=29.33ms;;;0; '/fr/merigon/#speedindex.time.milliseconds'=6318.00ms;;;0; '/fr/stgirons/#pageviews.count'=3;;;0; '/fr/stgirons/#ttfb.milliseconds'=543.50ms;;;0; '/fr/stgirons/#onload.time.milliseconds'=2847.67ms;;;0; '/fr/stgirons/#nextpaint.interaction.time.milliseconds'=112.00ms;;;0; '/fr/stgirons/#speedindex.time.milliseconds'=1062.50ms;;;0; '/guzet/#pageviews.count'=3;;;0; '/guzet/#ttfb.milliseconds'=1264.00ms;;;0; '/guzet/#onload.time.milliseconds'=1916.00ms;;;0; '/guzet/#nextpaint.interaction.time.milliseconds'=64.00ms;;;0; '/guzet/#speedindex.time.milliseconds'=1445.00ms;;;0; '/massat/#pageviews.count'=1;;;0; '/massat/#ttfb.milliseconds'=2484.00ms;;;0; '/massat/#onload.time.milliseconds'=13258.00ms;;;0; '/montardit/#pageviews.count'=3;;;0; '/montardit/#ttfb.milliseconds'=1171.33ms;;;0; '/montardit/#onload.time.milliseconds'=4867.33ms;;;0; '/montardit/#nextpaint.interaction.time.milliseconds'=52.00ms;;;0; '/montardit/#speedindex.time.milliseconds'=1357.67ms;;;0; + ... 6 --perspective=url --critical-interaction-next-paint=:10 ^CRITICAL: url.+$ + ... 7 --perspective=url --warning-interaction-next-paint=:10 ^WARNING: url.+$ + ... 8 --perspective=browser OK: All RUM counters are OK \\\\| 'Chrome#sessions.count'=16;;;0; 'Chrome#pageviews.count'=22;;;0; 'Chrome#bounce.rate.percentage'=20%;;;0;100 'Chrome#ttfb.milliseconds'=1554.31ms;;;0; 'Chrome#onload.time.milliseconds'=5495.91ms;;;0; 'Chrome#nextpaint.interaction.time.milliseconds'=80.38ms;;;0; 'Chrome#speedindex.time.milliseconds'=2706.62ms;;;0; 'Chrome Mobile#sessions.count'=1;;;0; 'Chrome Mobile#pageviews.count'=1;;;0; 'Chrome Mobile#bounce.rate.percentage'=1%;;;0;100 'Chrome Mobile#ttfb.milliseconds'=5021.00ms;;;0; 'Chrome Mobile#onload.time.milliseconds'=7429.00ms;;;0; 'Chrome Mobile#speedindex.time.milliseconds'=5260.00ms;;;0; 'Edge#sessions.count'=8;;;0; 'Edge#pageviews.count'=18;;;0; 'Edge#bounce.rate.percentage'=12%;;;0;100 'Edge#ttfb.milliseconds'=1330.06ms;;;0; 'Edge#onload.time.milliseconds'=4063.17ms;;;0; 'Edge#nextpaint.interaction.time.milliseconds'=41.87ms;;;0; 'Edge#speedindex.time.milliseconds'=1592.81ms;;;0; 'Firefox#sessions.count'=3;;;0; 'Firefox#pageviews.count'=15;;;0; 'Firefox#bounce.rate.percentage'=3%;;;0;100 'Firefox#ttfb.milliseconds'=1233.40ms;;;0; 'Firefox#onload.time.milliseconds'=6396.27ms;;;0; 'Firefox#nextpaint.interaction.time.milliseconds'=20.73ms;;;0; 'Firefox#speedindex.time.milliseconds'=2624.56ms;;;0; 'HeadlessChrome#sessions.count'=1;;;0; 'HeadlessChrome#pageviews.count'=1;;;0; 'HeadlessChrome#bounce.rate.percentage'=1%;;;0;100 'HeadlessChrome#ttfb.milliseconds'=6226.00ms;;;0; 'HeadlessChrome#onload.time.milliseconds'=8563.00ms;;;0; 'HeadlessChrome#speedindex.time.milliseconds'=6764.00ms;;;0; 'Mobile Safari#sessions.count'=1;;;0; 'Mobile Safari#pageviews.count'=1;;;0; 'Mobile Safari#bounce.rate.percentage'=1%;;;0;100 'Mobile Safari#ttfb.milliseconds'=1183.00ms;;;0; 'Mobile Safari#onload.time.milliseconds'=2572.00ms;;;0; 'Mobile Safari#speedindex.time.milliseconds'=1446.00ms;;;0; 'Opera Mobile#sessions.count'=1;;;0; 'Opera Mobile#pageviews.count'=1;;;0; 'Opera Mobile#bounce.rate.percentage'=1%;;;0;100 'Opera Mobile#ttfb.milliseconds'=3092.00ms;;;0; 'Opera Mobile#onload.time.milliseconds'=11991.00ms;;;0; 'Opera Mobile#speedindex.time.milliseconds'=6095.00ms;;;0; + ... 9 --perspective=browser --critical-ttfb=:10 ^CRITICAL: browser.+$ + ... 10 --perspective=browser --warning-ttfb=:10 ^WARNING: browser.+$ + ... 11 --perspective=country OK: country country: sessions: 34, page views: 62, bounce rate: 43%, ttfb: 1617.714ms, onload time: 5494.82ms, interaction to next paint: 46.70ms, speed index: 2536.67ms \\\\| 'country#sessions.count'=34;;;0; 'country#pageviews.count'=62;;;0; 'country#bounce.rate.percentage'=43%;;;0;100 'country#ttfb.milliseconds'=1617.71ms;;;0; 'country#onload.time.milliseconds'=5494.82ms;;;0; 'country#nextpaint.interaction.time.milliseconds'=46.70ms;;;0; 'country#speedindex.time.milliseconds'=2536.67ms;;;0; + ... 12 --perspective=country --critical-bounce-rate=:10 ^CRITICAL: country.+$ + ... 13 --perspective=country --warning-bounce-rate=:10 ^WARNING: country.+$ + ... 14 --perspective=city OK: All RUM counters are OK \\\\| 'EE#pageviews.count'=7;;;0; 'EE#ttfb.milliseconds'=1128.71ms;;;0; 'EE#onload.time.milliseconds'=1716.00ms;;;0; 'EE#nextpaint.interaction.time.milliseconds'=32.00ms;;;0; 'EE#speedindex.time.milliseconds'=1300.86ms;;;0; 'FR#sessions.count'=1;;;0; 'FR#pageviews.count'=1;;;0; 'FR#bounce.rate.percentage'=1%;;;0;100 'FR#ttfb.milliseconds'=1183.00ms;;;0; 'FR#onload.time.milliseconds'=2572.00ms;;;0; 'FR#speedindex.time.milliseconds'=1446.00ms;;;0; 'IN#sessions.count'=1;;;0; 'IN#pageviews.count'=3;;;0; 'IN#bounce.rate.percentage'=2%;;;0;100 'IN#ttfb.milliseconds'=1660.00ms;;;0; 'IN#onload.time.milliseconds'=3668.67ms;;;0; 'IN#nextpaint.interaction.time.milliseconds'=36.00ms;;;0; 'IN#speedindex.time.milliseconds'=2294.67ms;;;0; 'MA#sessions.count'=1;;;0; 'MA#pageviews.count'=1;;;0; 'MA#bounce.rate.percentage'=1%;;;0;100 'MA#ttfb.milliseconds'=3092.00ms;;;0; 'MA#onload.time.milliseconds'=11991.00ms;;;0; 'MA#speedindex.time.milliseconds'=6095.00ms;;;0; 'NO#sessions.count'=1;;;0; 'NO#pageviews.count'=3;;;0; 'NO#bounce.rate.percentage'=2%;;;0;100 'NO#ttfb.milliseconds'=1171.33ms;;;0; 'NO#onload.time.milliseconds'=4867.33ms;;;0; 'NO#nextpaint.interaction.time.milliseconds'=52.00ms;;;0; 'NO#speedindex.time.milliseconds'=1357.67ms;;;0; 'SN#sessions.count'=1;;;0; 'SN#pageviews.count'=1;;;0; 'SN#bounce.rate.percentage'=2%;;;0;100 'SN#ttfb.milliseconds'=2693.00ms;;;0; 'SN#onload.time.milliseconds'=23566.00ms;;;0; 'SN#nextpaint.interaction.time.milliseconds'=64.00ms;;;0; 'SN#speedindex.time.milliseconds'=17320.00ms;;;0; 'US#sessions.count'=4;;;0; 'US#pageviews.count'=6;;;0; 'US#bounce.rate.percentage'=5%;;;0;100 'US#ttfb.milliseconds'=2587.75ms;;;0; 'US#onload.time.milliseconds'=4897.67ms;;;0; 'US#speedindex.time.milliseconds'=3131.00ms;;;0; + ... 15 --perspective=city --critical-speed-index=:10 ^CRITICAL: city.+$ + ... 16 --perspective=city --warning-speed-index=:10 ^WARNING: city.+$ + ... 17 --perspective=os OK: All RUM counters are OK \\\\| 'Android#sessions.count'=1;;;0; 'Android#pageviews.count'=1;;;0; 'Android#bounce.rate.percentage'=1%;;;0;100 'Android#ttfb.milliseconds'=3092.00ms;;;0; 'Android#onload.time.milliseconds'=11991.00ms;;;0; 'Android#speedindex.time.milliseconds'=6095.00ms;;;0; 'Linux#sessions.count'=1;;;0; 'Linux#pageviews.count'=1;;;0; 'Linux#bounce.rate.percentage'=1%;;;0;100 'Linux#ttfb.milliseconds'=6226.00ms;;;0; 'Linux#onload.time.milliseconds'=8563.00ms;;;0; 'Linux#speedindex.time.milliseconds'=6764.00ms;;;0; 'Mac OS X#sessions.count'=1;;;0; 'Mac OS X#pageviews.count'=3;;;0; 'Mac OS X#bounce.rate.percentage'=2%;;;0;100 'Mac OS X#ttfb.milliseconds'=1264.00ms;;;0; 'Mac OS X#onload.time.milliseconds'=1916.00ms;;;0; 'Mac OS X#speedindex.time.milliseconds'=1445.00ms;;;0; 'Windows#sessions.count'=21;;;0; 'Windows#pageviews.count'=49;;;0; 'Windows#bounce.rate.percentage'=27%;;;0;100 'Windows#ttfb.milliseconds'=1371.89ms;;;0; 'Windows#onload.time.milliseconds'=5404.73ms;;;0; 'Windows#nextpaint.interaction.time.milliseconds'=45.41ms;;;0; 'Windows#speedindex.time.milliseconds'=2345.58ms;;;0; 'iOS#sessions.count'=1;;;0; 'iOS#pageviews.count'=1;;;0; 'iOS#bounce.rate.percentage'=1%;;;0;100 'iOS#ttfb.milliseconds'=1183.00ms;;;0; 'iOS#onload.time.milliseconds'=2572.00ms;;;0; 'iOS#speedindex.time.milliseconds'=1446.00ms;;;0; + ... 18 --perspective=os --critical-onload=:10 ^CRITICAL: os.+$ + ... 19 --perspective=os --warning-onload=:10 ^WARNING: os.+$ + ... 20 --perspective=os --critical-sessions=:10 ^CRITICAL: os.+$ + ... 21 --perspective=os --warning-sessions=:10 ^WARNING: os.+$ diff --git a/tests/apps/monitoring/quanta/restapi/siteoverview.robot b/tests/apps/monitoring/quanta/restapi/siteoverview.robot new file mode 100644 index 000000000..0ae8e9245 --- /dev/null +++ b/tests/apps/monitoring/quanta/restapi/siteoverview.robot @@ -0,0 +1,34 @@ +*** Settings *** +Documentation Quanta + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}quanta.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=apps::monitoring::quanta::restapi::plugin +... --hostname=${HOSTNAME} +... --api-token=PaSsWoRd +... --site-id=10 +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +SiteOverview ${tc} + [Tags] quanta api + ${command} Catenate +... ${CMD} + ... --mode=site-overview + ... ${extra_options} + + Ctn Run Command And Check Result As Strings ${command} ${expected_result} + + Examples: tc extraoptions expected_result -- + ... 1 ${EMPTY} OK: Site 'www.ariege.com' performance score: 72, digital sobriety score: 56, eco design score: 62, carbon footprint per click: 1.28g | 'www.ariege.com#performance.score'=72;;;0;100 'www.ariege.com#digitalsobriety.score'=56;;;0;100 'www.ariege.com#ecodesign.score'=62;;;0;100 'www.ariege.com#perclick.carbon.footprint.gramm'=1.28g;;;0; diff --git a/tests/apps/monitoring/quanta/restapi/userjourneyincidents.robot b/tests/apps/monitoring/quanta/restapi/userjourneyincidents.robot new file mode 100644 index 000000000..9f09e6d90 --- /dev/null +++ b/tests/apps/monitoring/quanta/restapi/userjourneyincidents.robot @@ -0,0 +1,39 @@ +*** Settings *** +Documentation Quanta + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}quanta.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=apps::monitoring::quanta::restapi::plugin +... --hostname=${HOSTNAME} +... --api-token=PaSsWoRd +... --site-id=10 +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +UserJourneyIncidents ${tc} + [Tags] quanta api + ${command} Catenate + ... ${CMD} + ... --mode=user-journey-incidents + ... --journey-id=3666 + ... ${extra_options} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp} + + Examples: tc extraoptions expected_regexp -- + ... 1 ${EMPTY} CRITICAL: Incident for interaction 'Decline cookies' status: open \\\\| 'quanta.incidents.total.count'=32;;;0; + ... 2 --ignore-closed CRITICAL: Incident for interaction 'Decline cookies' status: open \\\\| 'quanta.incidents.total.count'=1;;;0; + ... 3 --critical-incident-status='' --warning-incident-status='\\\%{status} =~ /open/i' WARNING: Incident for interaction 'Decline cookies' status: open \\\\| 'quanta.incidents.total.count'=32;;;0; + ... 4 --critical-incident-status='' --warning-incident-type='\\\%{type} =~ /timeout/i' ^WARNING: Incident for interaction.+$ + ... 5 --critical-incident-status='' --warning-incident-duration=:10 ^WARNING: Incident for interaction.+$ diff --git a/tests/apps/monitoring/quanta/restapi/userjourneystatistics.robot b/tests/apps/monitoring/quanta/restapi/userjourneystatistics.robot new file mode 100644 index 000000000..5987bc5fd --- /dev/null +++ b/tests/apps/monitoring/quanta/restapi/userjourneystatistics.robot @@ -0,0 +1,43 @@ +*** Settings *** +Documentation Quanta + +Resource ${CURDIR}${/}..${/}..${/}..${/}..${/}resources/import.resource + +Suite Setup Start Mockoon ${MOCKOON_JSON} +Suite Teardown Stop Mockoon +Test Timeout 120s + + +*** Variables *** +${MOCKOON_JSON} ${CURDIR}${/}quanta.mockoon.json +${HOSTNAME} 127.0.0.1 +${APIPORT} 3000 +${CMD} ${CENTREON_PLUGINS} +... --plugin=apps::monitoring::quanta::restapi::plugin +... --hostname=${HOSTNAME} +... --api-token=PaSsWoRd +... --site-id=10 +... --proto=http +... --port=${APIPORT} + +*** Test Cases *** +UserJourneyStatistics ${tc} + [Tags] quanta api + ${command} Catenate + ... ${CMD} + ... --mode=user-journey-statistics + ... --journey-id=3666 + ... ${extra_options} + + Ctn Run Command And Check Result As Regexp ${command} ${expected_regexp} + + Examples: tc extraoptions expected_regexp -- + ... 1 ${EMPTY} OK: journey "Basic user journey" journey performance score: 76, journey hero time: 35933.10ms, journey speed index: 13754.20ms, journey ttfb: 33.56ms \\\\| 'journey_Basic user journey#journey.performance.score'=76;;;0;100 'journey_Basic user journey#journey.herotime.milliseconds'=35933.10ms;;;0; 'journey_Basic user journey#journey.speedindex.time.milliseconds'=13754.20ms;;;0; 'journey_Basic user journey#journey.ttfb.milliseconds'=33.56ms;;;0; + ... 2 --warning-journey-hero-time=:10 ^WARNING: journey "Basic user journey" journey hero time.+$ + ... 3 --warning-journey-speed-index=:10 ^WARNING: journey "Basic user journey" journey speed index.+$ + ... 4 --critical-journey-ttfb=:10 ^CRITICAL: journey "Basic user journey" journey ttfb.+$ + ... 5 --critical-journey-performance-score=:10 ^CRITICAL: journey "Basic user journey" journey performance score.+$ + ... 6 --show-interactions --critical-interaction-ttfb=:10 ^CRITICAL: interaction "Home".+$ + ... 7 --show-interactions --warning-interaction-speed-index=:10 ^WARNING: interaction "Home".+$ + ... 8 --show-interactions --critical-interaction-hero-time=:10 ^CRITICAL: interaction "Home".+$ + ... 9 --show-interactions=1 --warning-interaction-performance-score=11: ^WARNING: interaction "Home".+$