Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add /job_groups/id/build_results API route #5433

Merged
merged 2 commits into from Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 17 additions & 2 deletions lib/OpenQA/BuildResults.pm
Expand Up @@ -110,7 +110,7 @@ sub find_child_groups ($group, $subgroup_filter) {
return filter_subgroups($group, $subgroup_filter);
}

sub compute_build_results ($group, $limit, $time_limit_days, $tags, $subgroup_filter) {
sub compute_build_results ($group, $limit, $time_limit_days, $tags, $subgroup_filter, $show_tags) {

# find relevant child groups taking filter into account
my $child_groups = find_child_groups($group, $subgroup_filter);
Expand Down Expand Up @@ -233,8 +233,23 @@ sub compute_build_results ($group, $limit, $time_limit_days, $tags, $subgroup_fi
$max_jobs = $jr{total} if ($jr{total} > $max_jobs);
}
$result{max_jobs} = $max_jobs;
_map_tags_into_build($result{build_results}, $show_tags) if $show_tags;
return \%result;
}

1;
sub _map_tags_into_build ($results, $tags) {
for my $res (@$results) {
if (my $full_tag = $tags->{$res->{key}}) {
$res->{tag} = $full_tag;
}
elsif (my $build_only_tag = $tags->{$res->{build}}) {
# as fallback we are looking for build and not other criteria we can end
# up with multiple tags if the build appears more than once, e.g.
# for each version
$res->{tag} = $build_only_tag;
}
}
}


1;
2 changes: 2 additions & 0 deletions lib/OpenQA/WebAPI.pm
Expand Up @@ -330,6 +330,8 @@ sub startup ($self) {
$api_public_r->get('/job_groups')->name('apiv1_list_job_groups')->to('job_group#list');
$api_public_r->get('/job_groups/<group_id:num>')->name('apiv1_get_job_group')->to('job_group#list');
$api_public_r->get('/job_groups/<group_id:num>/jobs')->name('apiv1_get_job_group_jobs')->to('job_group#list_jobs');
$api_public_r->get('/job_groups/<group_id:num>/build_results')->name('apiv1_get_job_group_jobs')
->to('job_group#build_results');
$api_ra->post('/job_groups')->name('apiv1_post_job_group')->to('job_group#create');
$api_ra->put('/job_groups/<group_id:num>')->name('apiv1_put_job_group')->to('job_group#update');
$api_ra->delete('/job_groups/<group_id:num>')->name('apiv1_delete_job_group')->to('job_group#delete');
Expand Down
83 changes: 53 additions & 30 deletions lib/OpenQA/WebAPI/Controller/API/V1/JobGroup.pm
Expand Up @@ -2,7 +2,7 @@
# SPDX-License-Identifier: GPL-2.0-or-later

package OpenQA::WebAPI::Controller::API::V1::JobGroup;
use Mojo::Base 'Mojolicious::Controller';
use Mojo::Base 'Mojolicious::Controller', -signatures;

use OpenQA::Schema::Result::JobGroups;

Expand Down Expand Up @@ -36,8 +36,7 @@ Reports true if the job group given to the method is a parent group.

=cut

sub is_parent {
my ($self) = @_;
sub is_parent ($self) {
return $self->req->url->path =~ qr/.*\/parent_groups/;
}

Expand All @@ -51,8 +50,7 @@ Returns results for a set of groups.

=cut

sub resultset {
my ($self) = @_;
sub resultset ($self) {
return $self->schema->resultset($self->is_parent ? 'JobGroupParents' : 'JobGroups');
}

Expand All @@ -66,9 +64,7 @@ Returns information from a job group given its ID.

=cut

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

sub find_group ($self) {
my $group_id = $self->param('group_id');
if (!defined $group_id) {
$self->render(json => {error => 'No group ID specified'}, status => 400);
Expand All @@ -95,8 +91,7 @@ create or update a job group via DBIx.

=cut

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

if (my $cached_properties = $self->{cached_properties}) {
return $cached_properties;
Expand Down Expand Up @@ -134,9 +129,7 @@ timestamps - is also returned as a list of indexed lists.

=cut

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

sub list ($self) {
my $groups;
my $group_id = $self->param('group_id');
if ($group_id) {
Expand Down Expand Up @@ -170,18 +163,15 @@ Check existing job group on top level to prevent create/update duplicate.

=cut

sub check_top_level_group {
my ($self, $group_id) = @_;

sub check_top_level_group ($self, $group_id = undef) {
return 0 if $self->is_parent;
my $properties = $self->load_properties;
my $conditions = {name => $properties->{name}, parent_id => undef};
$conditions->{id} = {'!=', $group_id} if $group_id;
return $self->resultset->search($conditions);
}

sub _validate_common_properties {
my ($self) = @_;
sub _validate_common_properties ($self) {
my $validation = $self->validation;
$validation->optional('parent_id')->like(qr/^(none|[0-9]+)\z/);
$validation->optional('size_limit_gb')->like(qr/^(|[0-9]+)\z/);
Expand Down Expand Up @@ -215,9 +205,7 @@ Returns a 400 code on error or a 500 code if the group already exists.

=cut

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

sub create ($self) {
my $validation = $self->validation;
$validation->required('name')->like(qr/^(?!\s*$).+/);
$validation->optional('default_keep_logs_in_days')->num(0);
Expand Down Expand Up @@ -257,9 +245,7 @@ Updates the properties of a job group.

=cut

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

sub update ($self) {
my $group = $self->find_group;
return unless $group;

Expand Down Expand Up @@ -299,9 +285,7 @@ List jobs belonging to a job group.

=cut

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

sub list_jobs ($self) {
my $group = $self->find_group;
return unless $group;

Expand All @@ -325,9 +309,7 @@ Deletes a job group. Verifies that it is not empty before attempting to remove.

=cut

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

sub delete ($self) {
my $group = $self->find_group();
return unless $group;

Expand All @@ -344,4 +326,45 @@ sub delete {
$self->render(json => $event_data);
}

=over 4

=item build_results()

Shows build results for a job group, similar to what the group_overview page
provides.

Currently it does not support parent job groups.
perlpunk marked this conversation as resolved.
Show resolved Hide resolved

Use limit_builds=n to limit the number of returned builds. Default is 10.

Use time_limit_days=n to only go back n days.

Use only_tagged=1 to only return tagged builds.

Use show_tags=1 to show tags for each build. only_tagged implies show_tags.

=back

=cut

sub build_results ($self) {
my $group = $self->find_group() or return;
my $validation = $self->validation;
$validation->optional('limit_builds')->num;
$validation->optional('time_limit_days')->like(qr/^[0-9.]+$/);
$validation->optional('only_tagged');
$validation->optional('show_tags');
my $limit_builds = $validation->param('limit_builds') // 10;
my $time_limit_days = $validation->param('time_limit_days') // 0;
my $only_tagged = $validation->param('only_tagged') // 0;
my $show_tags = $validation->param('show_tags') // $only_tagged;

my $tags = $show_tags ? $group->tags : undef;
my $cbr
= OpenQA::BuildResults::compute_build_results($group, $limit_builds,
$time_limit_days, $only_tagged ? $tags : undef,
[], $tags);
$self->render(json => $cbr);
}

1;
26 changes: 5 additions & 21 deletions lib/OpenQA/WebAPI/Controller/Main.pm
Expand Up @@ -11,20 +11,6 @@ use OpenQA::BuildResults;
use OpenQA::Utils;
use Mojo::File qw(path);

sub _map_tags_into_build ($results, $tags) {
for my $res (@$results) {
if (my $full_tag = $tags->{$res->{key}}) {
$res->{tag} = $full_tag;
}
elsif (my $build_only_tag = $tags->{$res->{build}}) {
# as fallback we are looking for build and not other criteria we can end
# up with multiple tags if the build appears more than once, e.g.
# for each version
$res->{tag} = $build_only_tag;
}
}
}

sub dashboard_build_results ($self) {
my $validation = $self->validation;
$validation->optional('limit_builds')->num;
Expand Down Expand Up @@ -56,10 +42,9 @@ sub dashboard_build_results ($self) {
my $build_results
= OpenQA::BuildResults::compute_build_results($group, $limit_builds, $time_limit_days,
$only_tagged ? $tags : undef,
$group_params);
$group_params, $show_tags ? $tags : undef);

my $build_results_for_group = $build_results->{build_results};
_map_tags_into_build($build_results_for_group, $tags) if $show_tags;
push(@results, $build_results) if @{$build_results_for_group};
}
};
Expand Down Expand Up @@ -135,19 +120,18 @@ sub _group_overview ($self, $resultset, $template) {

my $tags = $group->tags;
my $cbr = eval {
OpenQA::BuildResults::compute_build_results($group, $limit_builds, $time_limit_days,
$only_tagged ? $tags : undef,
$group_params);
OpenQA::BuildResults::compute_build_results($group, $limit_builds,
$time_limit_days, $only_tagged ? $tags : undef,
$group_params, $tags);
};
if (my $error = $@) {
die $error unless $error =~ qr/^invalid regex: /;
return $self->_respond_error_for_group_overview($error);
}
my $build_results = $cbr->{build_results};
my $max_jobs = $cbr->{max_jobs};
$self->stash(children => $cbr->{children});

_map_tags_into_build($build_results, $tags);
$self->stash(children => $cbr->{children});
$self->stash(build_results => $build_results, max_jobs => $max_jobs);

my $is_parent_group = $group->can('children');
Expand Down
57 changes: 57 additions & 0 deletions t/api/10-jobgroups.t
Expand Up @@ -19,6 +19,63 @@ my $schema = $t->app->schema;
my $audit_events = $schema->resultset('AuditEvents');

my $opensuse_group = '1001';


subtest 'build results' => sub {
subtest 'default' => sub {
$t->get_ok("/api/v1/job_groups/$opensuse_group/build_results?time_limit_days=99999")->status_is(200);
$t->json_is('/build_results/4/all_passed' => '');
$t->json_is('/build_results/4/build' => '0091');
$t->json_is('/build_results/4/key' => '13.1-0091');
$t->json_is('/build_results/4/comments' => 0);
$t->json_is('/build_results/4/commented' => 1);
$t->json_is('/build_results/4/escaped_id' => '13_1-0091');
$t->json_is('/build_results/4/escaped_build' => '0091');
$t->json_is('/build_results/4/escaped_version' => '13_1');
$t->json_is('/build_results/4/failed' => 0);
$t->json_is('/build_results/4/labeled' => 0);
$t->json_is('/build_results/4/passed' => 2);
$t->json_is('/build_results/4/reviewed' => 1);
$t->json_is('/build_results/4/skipped' => 1);
$t->json_is('/build_results/4/softfailed' => 0);
$t->json_is('/build_results/4/total' => 5);
$t->json_is('/build_results/4/unfinished' => 2);
$t->json_is('/build_results/4/version' => '13.1');
$t->json_is('/build_results/3/build' => '0092');
$t->json_is('/build_results/2/build' => '0048');
$t->json_is('/build_results/1/build' => '0048@0815');
$t->json_is('/build_results/0/build' => '87.5011');
$t->json_is('/build_results/0/tag' => undef);
$t->json_is('/build_results/5' => undef);
$t->json_is('/max_jobs' => 5,);
$t->json_is('/group/id' => $opensuse_group);
$t->json_is('/group/name' => 'opensuse');
};

subtest 'tags' => sub {
$t->get_ok("/api/v1/job_groups/$opensuse_group/build_results?time_limit_days=99999&show_tags=1")
->status_is(200);
$t->json_is('/build_results/0/tag' => undef);

$t->get_ok("/api/v1/job_groups/$opensuse_group/build_results?time_limit_days=99999&only_tagged=1&show_tags=1")
->status_is(200);
$t->json_is('/build_results/0' => undef, 'no tagged build is shown');

my $comment = 'tag:0091:published:published';
$t->post_ok("/api/v1/groups/$opensuse_group/comments" => form => {text => $comment})
->status_is(200, 'comment can be created')->or(sub { diag 'error: ' . $t->tx->res->json->{error} });
my $cid = $t->tx->res->json->{id};
$t->get_ok("/api/v1/job_groups/$opensuse_group/build_results?time_limit_days=99999&only_tagged=1&show_tags=1")
->status_is(200);
my $json = $t->tx->res->json;
$t->json_is('/build_results/0/build' => '0091', 'one tagged build is shown');
$t->json_is('/build_results/0/tag/build' => '0091');
$t->json_is('/build_results/0/tag/description' => 'published');
$t->json_is('/build_results/0/tag/type' => 'published');
$t->json_is('/build_results/0/tag/version' => undef);
};
};

subtest 'list job groups' => sub() {
$t->get_ok('/api/v1/job_groups')->status_is(200);
is_deeply(
Expand Down