Skip to content
Draft
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
2 changes: 2 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ jobs:
- store_artifacts: *store_logs
- run: *install_cached_packages
- restore_cache: *restore_npm_cache
- run: sudo cpanm -n 'Mojolicious::Plugin::OpenAPI'
- run: *make_test
- persist_to_workspace:
root: .
Expand All @@ -212,6 +213,7 @@ jobs:
- run: *install_cached_packages
- restore_cache: *restore_npm_cache
- run: *build_autoinst
- run: sudo cpanm -n 'Mojolicious::Plugin::OpenAPI'
- run: *make_test
- persist_to_workspace:
root: .
Expand Down
17 changes: 13 additions & 4 deletions lib/OpenQA/WebAPI.pm
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ sub startup ($self) {
# Some plugins are shared between openQA micro services
push @{$self->plugins->namespaces}, 'OpenQA::Shared::Plugin';
$self->plugin('SharedHelpers');
push @{$self->app->static()->paths()}, $self->home->rel_file('swagger');
push @{$self->app->renderer()->paths()}, $self->home->rel_file('swagger');

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

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

# register basic routes
my $logged_in = $r->under('/')->to('session#ensure_user');
Expand Down Expand Up @@ -203,6 +206,7 @@ sub startup ($self) {
$r->get('/dashboard_build_results' => [format => ['json', 'html']])->name('dashboard_build_results')
->to('main#dashboard_build_results', format => undef);
$r->get('/api_help' => sub ($c) { $c->render('admin/api_help') })->name('api_help');
$r->get('/swagger' => sub ($c) { $c->render('swagger') })->name('swagger');

# Default route
$r->get('/' => sub ($c) { $c->render('main/index') })->name('index');
Expand Down Expand Up @@ -325,12 +329,12 @@ sub startup ($self) {

my $job_r = $api_ro->any('/jobs/<jobid:num>');
push @api_routes, $job_r;
$api_public_r->any('/jobs/<jobid:num>')->name('apiv1_job')->to('job#show');
# $api_public_r->any('/jobs/<jobid:num>')->name('apiv1_job')->to('job#show');
$api_public_r->get('/experimental/jobs/<jobid:num>/status')->name('apiv1_get_status')->to('job#get_status');
$api_public_r->any('/jobs/<jobid:num>/details')->name('apiv1_job')->to('job#show', details => 1);
# $api_public_r->any('/jobs/<jobid:num>/details')->name('apiv1_job')->to('job#show', details => 1);
$job_r->put('/')->name('apiv1_put_job')->to('job#update');
$job_r->delete('/')->name('apiv1_delete_job')->to('job#destroy');
$job_r->post('/prio')->name('apiv1_job_prio')->to('job#prio');
# $job_r->post('/prio')->name('apiv1_job_prio')->to('job#prio');
$job_r->post('/set_done')->name('apiv1_set_done')->to('job#done');
$job_r->post('/status')->name('apiv1_update_status')->to('job#update_status');
$job_r->post('/artefact')->name('apiv1_create_artefact')->to('job#create_artefact');
Expand Down Expand Up @@ -487,6 +491,11 @@ sub startup ($self) {
set_api_desc(\%api_description, $api_rt);
}

$self->plugin(
OpenAPI => {
url => $self->home->rel_file('public/openapi.yaml'),
},
);
OpenQA::Setup::setup_validator_check_for_datetime($self);

$self->plugin('OpenQA::WebAPI::Plugin::MemoryLimit');
Expand Down
34 changes: 8 additions & 26 deletions lib/OpenQA/WebAPI/Controller/API/V1/Job.pm
Original file line number Diff line number Diff line change
Expand Up @@ -441,20 +441,8 @@ sub create ($self) {
$self->render(json => $json, status => ($json->{error} ? 400 : 200));
}

=over 4

=item show()

Shows details for a specific job, such as the assets associated, assigned worker id,
children and parents, job id, group id, name, parent group id and name, priority, result,
settings, state and times of startup and finish of the job.
Pass follow=1 as query param to follow job clones and report most recent result for given id.

=back

=cut

sub show ($self) {
$self->openapi->valid_input or return;
my $job_id = int($self->stash('jobid'));
my $details = $self->stash('details') || 0;
my $check_assets = !!$self->param('check_assets');
Expand All @@ -466,6 +454,11 @@ sub show ($self) {
$self->render(json => {job => $job});
}

sub show_details ($self) {
$self->stash(details => 1);
show($self);
}

=over 4

=item destroy()
Expand All @@ -483,21 +476,10 @@ sub destroy ($self) {
$self->render(json => {result => 1});
}

=over 4

=item prio()

Sets priority for a given job.

=back

=cut

sub prio ($self) {
$self->openapi->valid_input or return;
return unless my $job = $self->find_job_or_render_not_found($self->stash('jobid'));
my $v = $self->validation;
my $prio = $v->required('prio')->num->param;
return $self->reply->validation_error({format => 'json'}) if $v->has_error;
my $prio = $self->param('prio');

# Referencing the scalar will result in true or false (see http://mojolicio.us/perldoc/Mojo/JSON)
$self->render(json => {result => \$job->set_prio($prio)});
Expand Down
64 changes: 64 additions & 0 deletions public/openapi.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
openapi: 3.1.1
info:
title: OpenQA API
version: v1
servers:
- url: /api/v1

paths:

/jobs/{jobid}:
parameters:
- &jobid
name: jobid
in: path
required: true
schema:
type: integer
get:
description: >
Shows details for a specific job, such as the assets associated, assigned
worker id, children and parents, job id, group id, name, parent group id
and name, priority, result, settings, state and times of startup and
finish of the job.
tags: [ job ]
x-mojo-name: apiv1_job
x-mojo-to: job#show
parameters:
- name: follow
description: >
Enable following job clones and report most recent result for a given
job id
in: query
schema:
type: integer
enum: [0, 1]
responses: {}

/jobs/{jobid}/details:
parameters:
- *jobid
get:
description: Show job with details (logs, testresults)
tags: [ job ]
x-mojo-name: apiv1_job_details
x-mojo-to: job#show_details
responses: {}

/jobs/{jobid}/prio:
parameters:
- *jobid
post:
description: Sets priority for a given job.
tags: [ job ]
x-mojo-name: apiv1_job_prio
x-mojo-to: job#prio
requestBody:
content:
application/x-www-form-urlencoded:
schema:
required: [ prio ]
properties:
prio:
type: integer
responses: {}
16 changes: 16 additions & 0 deletions public/swagger/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
html {
box-sizing: border-box;
overflow: -moz-scrollbars-vertical;
overflow-y: scroll;
}

*,
*:before,
*:after {
box-sizing: inherit;
}

body {
margin: 0;
background: #fafafa;
}
23 changes: 23 additions & 0 deletions public/swagger/swagger-initializer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
window.onload = function() {
const defaultDefinitionUrl = "http://localhost:9526/openapi.yaml";
const definitionURL = defaultDefinitionUrl;

//<editor-fold desc="Changeable Configuration Block">
window.ui = SwaggerUIBundle({
url: definitionURL,
"dom_id": "#swagger-ui",
deepLinking: true,
presets: [
SwaggerUIBundle.presets.apis,
SwaggerUIStandalonePreset
],
plugins: [
SwaggerUIBundle.plugins.DownloadUrl
],
layout: "StandaloneLayout",
queryConfigEnabled: true,
validatorUrl: "https://validator.swagger.io/validator",
})
//</editor-fold>

};
2 changes: 2 additions & 0 deletions public/swagger/swagger-ui-bundle.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions public/swagger/swagger-ui-standalone-preset.js

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions public/swagger/swagger-ui.css

Large diffs are not rendered by default.

32 changes: 32 additions & 0 deletions t/43-openapi.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Copyright SUSE LLC
# SPDX-License-Identifier: GPL-2.0-or-later.

use Test::Most;
use Test::Warnings qw(:report_warnings);
use Mojo::Base -strict, -signatures;
use FindBin;
use lib "$FindBin::Bin/lib", "$FindBin::Bin/../external/os-autoinst-common/lib";
use OpenQA::Test::Case;
use OpenQA::Test::TimeLimit '10';
use Test::Mojo;

OpenQA::Test::Case->new->init_data(fixtures_glob => '01-jobs.pl');
my $t = Test::Mojo->new('OpenQA::WebAPI');

subtest jobs => sub {
$t->get_ok('/api/v1/jobs/23')->status_is(404)->json_is('/error', 'Job does not exist');
$t->get_ok('/api/v1/jobs/23a')->status_is(400)->json_like('/errors/0/message', qr{Expected integer - got string});

$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);
$t->get_ok('/api/v1/jobs/80000/details')->status_is(200)->json_is('/job/id', '80000')
->json_is('/job/testresults', []);
$t->get_ok('/api/v1/jobs/99945?follow=1')->status_is(200)->json_is('/job/id', '99946');
$t->get_ok('/api/v1/jobs/99945?follow=2')->status_is(400)->json_like('/errors/0/message', qr{Not in enum list});

$t->post_ok('/api/v1/jobs/80000/prio', form => {prio => 99})->status_is(200);
$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});
};

done_testing;
20 changes: 20 additions & 0 deletions templates/webapi/swagger.html.ep
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!-- HTML for static distribution bundle build -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Swagger UI</title>
<link rel="stylesheet" type="text/css" href="/swagger/swagger-ui.css" />
<link rel="stylesheet" type="text/css" href="index.css" />
<link rel="icon" type="image/png" href="./favicon-32x32.png" sizes="32x32" />
<link rel="icon" type="image/png" href="./favicon-16x16.png" sizes="16x16" />
</head>

<body>
<div id="swagger-ui"></div>
<script src="/swagger/swagger-ui-bundle.js" charset="UTF-8"> </script>
<script src="/swagger/swagger-ui-standalone-preset.js" charset="UTF-8"> </script>
<script src="/swagger/swagger-initializer.js" charset="UTF-8"> </script>
</body>
</html>