Skip to content

Commit 65971e8

Browse files
committed
Mojolicious::Plugin::OpenAPI PoC
https://progress.opensuse.org/issues/186675
1 parent 8ba6f76 commit 65971e8

File tree

4 files changed

+111
-7
lines changed

4 files changed

+111
-7
lines changed

lib/OpenQA/WebAPI.pm

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@ sub startup ($self) {
2424
# Some plugins are shared between openQA micro services
2525
push @{$self->plugins->namespaces}, 'OpenQA::Shared::Plugin';
2626
$self->plugin('SharedHelpers');
27+
$self->plugin(
28+
OpenAPI => {
29+
url => $self->home->rel_file('public/openapi.yaml'),
30+
},
31+
);
2732

2833
# Provide help to users early to prevent failing later on misconfigurations
2934
# note: Loading plugins for the current configuration so the help of commands provided by plugins is
@@ -52,7 +57,7 @@ sub startup ($self) {
5257
OpenQA::Schema->singleton;
5358

5459
# Some controllers are shared between openQA micro services
55-
my $r = $self->routes->namespaces(['OpenQA::Shared::Controller', 'OpenQA::WebAPI::Controller', 'OpenQA::WebAPI']);
60+
my $r = $self->routes->namespaces([qw(OpenQA::Shared::Controller OpenQA::WebAPI::Controller OpenQA::WebAPI OpenQA::WebAPI::Controller::API::V1)]);
5661

5762
# register basic routes
5863
my $logged_in = $r->under('/')->to('session#ensure_user');
@@ -325,12 +330,12 @@ sub startup ($self) {
325330

326331
my $job_r = $api_ro->any('/jobs/<jobid:num>');
327332
push @api_routes, $job_r;
328-
$api_public_r->any('/jobs/<jobid:num>')->name('apiv1_job')->to('job#show');
333+
# $api_public_r->any('/jobs/<jobid:num>')->name('apiv1_job')->to('job#show');
329334
$api_public_r->get('/experimental/jobs/<jobid:num>/status')->name('apiv1_get_status')->to('job#get_status');
330-
$api_public_r->any('/jobs/<jobid:num>/details')->name('apiv1_job')->to('job#show', details => 1);
335+
# $api_public_r->any('/jobs/<jobid:num>/details')->name('apiv1_job')->to('job#show', details => 1);
331336
$job_r->put('/')->name('apiv1_put_job')->to('job#update');
332337
$job_r->delete('/')->name('apiv1_delete_job')->to('job#destroy');
333-
$job_r->post('/prio')->name('apiv1_job_prio')->to('job#prio');
338+
# $job_r->post('/prio')->name('apiv1_job_prio')->to('job#prio');
334339
$job_r->post('/set_done')->name('apiv1_set_done')->to('job#done');
335340
$job_r->post('/status')->name('apiv1_update_status')->to('job#update_status');
336341
$job_r->post('/artefact')->name('apiv1_create_artefact')->to('job#create_artefact');

lib/OpenQA/WebAPI/Controller/API/V1/Job.pm

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -455,6 +455,7 @@ Pass follow=1 as query param to follow job clones and report most recent result
455455
=cut
456456

457457
sub show ($self) {
458+
$self->openapi->valid_input or return;
458459
my $job_id = int($self->stash('jobid'));
459460
my $details = $self->stash('details') || 0;
460461
my $check_assets = !!$self->param('check_assets');
@@ -466,6 +467,11 @@ sub show ($self) {
466467
$self->render(json => {job => $job});
467468
}
468469

470+
sub show_details ($self) {
471+
$self->stash(details => 1);
472+
show($self);
473+
}
474+
469475
=over 4
470476
471477
=item destroy()
@@ -494,10 +500,9 @@ Sets priority for a given job.
494500
=cut
495501

496502
sub prio ($self) {
503+
$self->openapi->valid_input or return;
497504
return unless my $job = $self->find_job_or_render_not_found($self->stash('jobid'));
498-
my $v = $self->validation;
499-
my $prio = $v->required('prio')->num->param;
500-
return $self->reply->validation_error({format => 'json'}) if $v->has_error;
505+
my $prio = $self->param('prio');
501506

502507
# Referencing the scalar will result in true or false (see http://mojolicio.us/perldoc/Mojo/JSON)
503508
$self->render(json => {result => \$job->set_prio($prio)});

public/openapi.yaml

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
openapi: 3.1.1
2+
info:
3+
title: OpenQA API
4+
version: v1
5+
servers:
6+
- url: /api/v1
7+
8+
paths:
9+
10+
/jobs/{jobid}:
11+
parameters:
12+
- &jobid
13+
name: jobid
14+
in: path
15+
required: true
16+
schema:
17+
type: integer
18+
get:
19+
description: >
20+
Shows details for a specific job, such as the assets associated, assigned
21+
worker id, children and parents, job id, group id, name, parent group id
22+
and name, priority, result, settings, state and times of startup and
23+
finish of the job.
24+
tags: [ job ]
25+
x-mojo-name: apiv1_job
26+
x-mojo-to: job#show
27+
parameters:
28+
- name: follow
29+
description: >
30+
Enable following job clones and report most recent result for a given
31+
job id
32+
in: query
33+
schema:
34+
type: integer
35+
enum: [0, 1]
36+
responses: {}
37+
38+
/jobs/{jobid}/details:
39+
parameters:
40+
- *jobid
41+
get:
42+
description: Show job with details (logs, testresults)
43+
tags: [ job ]
44+
x-mojo-name: apiv1_job_details
45+
x-mojo-to: job#show_details
46+
responses: {}
47+
48+
/jobs/{jobid}/prio:
49+
parameters:
50+
- *jobid
51+
post:
52+
description: Sets priority for a given job.
53+
tags: [ job ]
54+
x-mojo-name: apiv1_job_prio
55+
x-mojo-to: job#prio
56+
requestBody:
57+
content:
58+
application/x-www-form-urlencoded:
59+
schema:
60+
required: [ prio ]
61+
properties:
62+
prio:
63+
type: integer
64+
pattern: ^$
65+
responses: {}

t/43-openapi.t

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Copyright SUSE LLC
2+
# SPDX-License-Identifier: GPL-2.0-or-later.
3+
4+
use Test::Most;
5+
use Test::Warnings qw(:report_warnings);
6+
use Mojo::Base -strict, -signatures;
7+
use FindBin;
8+
use lib "$FindBin::Bin/lib", "$FindBin::Bin/../external/os-autoinst-common/lib";
9+
use OpenQA::Test::Case;
10+
use OpenQA::Test::TimeLimit '10';
11+
use Test::Mojo;
12+
13+
OpenQA::Test::Case->new->init_data(fixtures_glob => '01-jobs.pl');
14+
my $t = Test::Mojo->new('OpenQA::WebAPI');
15+
16+
subtest jobs => sub {
17+
$t->get_ok('/api/v1/jobs/23')->status_is(404)->json_is('/error', 'Job does not exist');
18+
$t->get_ok('/api/v1/jobs/23a')->status_is(400)->json_like('/errors/0/message', qr{Expected integer - got string});
19+
20+
$t->get_ok('/api/v1/jobs/80000')->status_is(200)->json_is('/job/id', '80000')->json_is('/job/testresults', undef)->json_is('/job/priority', 50);
21+
$t->get_ok('/api/v1/jobs/80000/details')->status_is(200)->json_is('/job/id', '80000')->json_is('/job/testresults', []);
22+
$t->get_ok('/api/v1/jobs/99945?follow=1')->status_is(200)->json_is('/job/id', '99946');
23+
$t->get_ok('/api/v1/jobs/99945?follow=2')->status_is(400)->json_like('/errors/0/message', qr{Not in enum list});
24+
25+
$t->post_ok('/api/v1/jobs/80000/prio', form => {prio => 99})->status_is(200);
26+
$t->post_ok('/api/v1/jobs/80000/prio', form => {prio => 'not a number'})->status_is(400)->json_like('/errors/0/message', qr{Expected integer - got string})
27+
};
28+
29+
done_testing;

0 commit comments

Comments
 (0)