Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions Bugzilla/API/V1/Bugzilla.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
#
# This Source Code Form is "Incompatible With Secondary Licenses", as
# defined by the Mozilla Public License, v. 2.0.

package Bugzilla::API::V1::Bugzilla;

use 5.10.1;
use Mojo::Base qw( Mojolicious::Controller );

use DateTime;
use Try::Tiny;

use Bugzilla::Constants;
use Bugzilla::Logging;
use Bugzilla::Util qw(datetime_from);

sub setup_routes {
my ($class, $r) = @_;

$r->get('/version')->to('V1::Bugzilla#version');
$r->get('/extensions')->to('V1::Bugzilla#extensions');
$r->get('/timezone')->to('V1::Bugzilla#timezone');
$r->get('/time')->to('V1::Bugzilla#time');
$r->get('/jobqueue_status')->to('V1::Bugzilla#jobqueue_status');

foreach my $path (qw(/version /extensions /timezone /time /jobqueue_status)) {
$r->options($path)->to('V1::Bugzilla#options');
}
}

sub options {
my ($self) = @_;

$self->res->headers->header('Allow' => 'GET');
$self->res->headers->header('Access-Control-Allow-Methods' => 'GET');

return $self->rendered(200);
}

sub version {
my ($self) = @_;
Bugzilla->usage_mode(USAGE_MODE_MOJO_REST);

return $self->render(json => {version => BUGZILLA_VERSION});
}

sub extensions {
my ($self) = @_;
Bugzilla->usage_mode(USAGE_MODE_MOJO_REST);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extensions and time were not in the old LOGIN_EXEMPT list, so the legacy server called Bugzilla->login() (LOGIN_NORMAL) for them, meaning they were gated when the requirelogin param is on. neither handler calls $self->bugzilla->login now, so both become anonymously readable under requirelogin. Teams.pm calls $self->bugzilla->login() for exactly this reason even though it does not use the user


my %extensions;
foreach my $extension (@{Bugzilla->extensions}) {
$extensions{$extension->NAME} = {version => $extension->VERSION || 0};
}

return $self->render(json => {extensions => \%extensions});
}

sub timezone {
my ($self) = @_;
Bugzilla->usage_mode(USAGE_MODE_MOJO_REST);

# All Webservices return times in UTC; Use UTC here for backwards compat.
return $self->render(json => {timezone => '+0000'});
}

sub time {
my ($self) = @_;
Bugzilla->usage_mode(USAGE_MODE_MOJO_REST);

# All Webservices return times in UTC; Use UTC here for backwards compat.
my $dbh = Bugzilla->dbh;
my $db_time = $dbh->selectrow_array('SELECT LOCALTIMESTAMP(0)');
$db_time = datetime_from($db_time, 'UTC')->iso8601();
my $now_utc = DateTime->now()->iso8601();

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

response shape changes here: the legacy endpoint ran these through type('dateTime'), which is Bugzilla::WebService::Server::JSONRPC::datetime_format_outbound and appends a Z, so /rest/time used to return 2026-08-21T14:00:00Z. plain iso8601() drops the Z and clients that parse these as UTC will now read them as local time. suggest iso8601() . 'Z' for db_time, web_time and web_time_utc


return $self->render(
json => {
db_time => $db_time,
web_time => $now_utc,
web_time_utc => $now_utc,
tz_name => 'UTC',
tz_offset => '+0000',
tz_short_name => 'UTC',
}
);
}

sub jobqueue_status {
my ($self) = @_;

my $user = $self->bugzilla->login;
$user->id || return $self->user_error('login_required');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

usage_mode is still USAGE_MODE_REST here (set by _prepare_rest_request), so user_error hits neither branch in Bugzilla::App::Plugin::Error::_render_error and nothing gets rendered, an anonymous GET returns a 404 page instead of the JSON login_required error. every other native controller (Reminders, Teams, User) sets Bugzilla->usage_mode(USAGE_MODE_MOJO_REST) as the first statement, before login. same applies to any ThrowUserError raised inside login itself, eg a bad api key

suggest moving line 98 above line 95, and adding an anonymous-request case to qa/t/rest_bugzilla.t since the current test only covers the api-key path


Bugzilla->usage_mode(USAGE_MODE_MOJO_REST);

my $dbh = Bugzilla->dbh;
my $query = q{
SELECT
COUNT(*) AS total,
COALESCE(
(SELECT COUNT(*)
FROM ts_error
WHERE ts_error.jobid = j.jobid
)
, 0) AS errors
FROM ts_job j
INNER JOIN ts_funcmap f
ON f.funcid = j.funcid
GROUP BY errors
};

my $status;
try {
$status = $dbh->selectrow_hashref($query);
}
catch {
ERROR($_);
return $self->code_error('jobqueue_status_error');

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return inside a Try::Tiny catch block only returns from the block, not from jobqueue_status, so after code_error renders the 500 the sub falls through to the render below and overwrites the body with {"errors":0,"total":0} while the status stays 500. the old code died out of the catch via ThrowCodeError so it never reached that point

eg catch { ERROR($_) } then return $self->code_error('jobqueue_status_error') unless $status;

};

return $self->render(
json => {
errors => 0 + ($status->{errors} // 0),
total => 0 + ($status->{total} // 0),
}
);
}

1;

__END__

=head1 NAME

Bugzilla::API::V1::Bugzilla - Global functions for the webservice interface.

=head1 DESCRIPTION

This provides functions that tell you about Bugzilla in general.
2 changes: 0 additions & 2 deletions Bugzilla/WebService.pm
Original file line number Diff line number Diff line change
Expand Up @@ -413,8 +413,6 @@ objects.

=item L<Bugzilla::WebService::Bug>

=item L<Bugzilla::WebService::Bugzilla>

=item L<Bugzilla::WebService::Group>

=item L<Bugzilla::WebService::Product>
Expand Down
Loading