Add API entry point for journey import
This commit is contained in:
parent
7e9a2ebfef
commit
5fe4174feb
4 changed files with 249 additions and 13 deletions
|
@ -159,12 +159,13 @@ sub startup {
|
|||
status => 1,
|
||||
history => 2,
|
||||
action => 3,
|
||||
import => 4,
|
||||
};
|
||||
}
|
||||
);
|
||||
$self->attr(
|
||||
token_types => sub {
|
||||
return [qw(status history action)];
|
||||
return [qw(status history action import)];
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -330,7 +331,7 @@ sub startup {
|
|||
my ( $self, %opt ) = @_;
|
||||
|
||||
my $db = $opt{db};
|
||||
my $uid = $self->current_user->{id};
|
||||
my $uid = $opt{uid} // $self->current_user->{id};
|
||||
my $now = DateTime->now( time_zone => 'Europe/Berlin' );
|
||||
my $dep_station = get_station( $opt{dep_station} );
|
||||
my $arr_station = get_station( $opt{arr_station} );
|
||||
|
@ -410,7 +411,7 @@ sub startup {
|
|||
$journey_id
|
||||
= $db->insert( 'journeys', $entry, { returning => 'id' } )
|
||||
->hash->{id};
|
||||
$self->invalidate_stats_cache( $opt{rt_departure}, $db );
|
||||
$self->invalidate_stats_cache( $opt{rt_departure}, $db, $uid );
|
||||
};
|
||||
|
||||
if ($@) {
|
||||
|
@ -3232,6 +3233,7 @@ sub startup {
|
|||
$r->get('/status/:name/:ts')->to('traveling#user_status');
|
||||
$r->get('/ajax/status/:name')->to('traveling#public_status_card');
|
||||
$r->get('/ajax/status/:name/:ts')->to('traveling#public_status_card');
|
||||
$r->post('/api/v1/import')->to('api#import_v1');
|
||||
$r->post('/action')->to('traveling#log_action');
|
||||
$r->post('/geolocation')->to('traveling#geolocation');
|
||||
$r->post('/list_departures')->to('traveling#redirect_to_station');
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package Travelynx::Controller::Api;
|
||||
use Mojo::Base 'Mojolicious::Controller';
|
||||
|
||||
use DateTime;
|
||||
use Travel::Status::DE::IRIS::Stations;
|
||||
use UUID::Tiny qw(:std);
|
||||
|
||||
|
@ -8,6 +9,17 @@ sub make_token {
|
|||
return create_uuid_as_string(UUID_V4);
|
||||
}
|
||||
|
||||
sub sanitize {
|
||||
my ( $type, $value ) = @_;
|
||||
if ( not defined $value ) {
|
||||
return undef;
|
||||
}
|
||||
if ( $type eq '' ) {
|
||||
return '' . $value;
|
||||
}
|
||||
return 0 + $value;
|
||||
}
|
||||
|
||||
sub documentation {
|
||||
my ($self) = @_;
|
||||
|
||||
|
@ -71,9 +83,9 @@ sub get_v0 {
|
|||
or $status->{cancelled}
|
||||
) ? \1 : \0,
|
||||
station => {
|
||||
ds100 => $status->{arr_ds100} // $status->{dep_ds100},
|
||||
name => $status->{arr_name} // $status->{dep_name},
|
||||
uic => $station_eva,
|
||||
ds100 => $status->{arr_ds100} // $status->{dep_ds100},
|
||||
name => $status->{arr_name} // $status->{dep_name},
|
||||
uic => $station_eva,
|
||||
longitude => $station_lon,
|
||||
latitude => $station_lat,
|
||||
},
|
||||
|
@ -153,6 +165,155 @@ sub get_v1 {
|
|||
}
|
||||
}
|
||||
|
||||
sub import_v1 {
|
||||
my ($self) = @_;
|
||||
|
||||
my $payload = $self->req->json;
|
||||
my $api_token = $payload->{token} // '';
|
||||
|
||||
if ( $api_token !~ qr{ ^ (?<id> \d+ ) - (?<token> .* ) $ }x ) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => 'Malformed JSON or malformed token',
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
my $uid = $+{id};
|
||||
$api_token = $+{token};
|
||||
|
||||
if ( $uid > 2147483647 ) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => 'Malformed token',
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
my $token = $self->get_api_token($uid);
|
||||
if ( $api_token ne $token->{'import'} ) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => 'Invalid token',
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if ( not exists $payload->{fromStation}
|
||||
or not exists $payload->{toStation} )
|
||||
{
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => 'missing fromStation or toStation',
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
my %opt;
|
||||
|
||||
eval {
|
||||
%opt = (
|
||||
uid => $uid,
|
||||
train_type => sanitize( q{}, $payload->{train}{type} ),
|
||||
train_no => sanitize( q{}, $payload->{train}{no} ),
|
||||
train_line => sanitize( q{}, $payload->{train}{line} ),
|
||||
cancelled => $payload->{cancelled} ? 1 : 0,
|
||||
dep_station => sanitize( q{}, $payload->{fromStation}{name} ),
|
||||
arr_station => sanitize( q{}, $payload->{toStation}{name} ),
|
||||
sched_departure =>
|
||||
sanitize( 0, $payload->{fromStation}{scheduledTime} ),
|
||||
rt_departure => sanitize(
|
||||
0,
|
||||
$payload->{fromStation}{realTime}
|
||||
// $payload->{fromStation}{scheduledTime}
|
||||
),
|
||||
sched_arrival =>
|
||||
sanitize( 0, $payload->{toStation}{scheduledTime} ),
|
||||
rt_arrival => sanitize(
|
||||
0,
|
||||
$payload->{toStation}{realTime}
|
||||
// $payload->{toStation}{scheduledTime}
|
||||
),
|
||||
comment => sanitize( q{}, $payload->{comment} ),
|
||||
);
|
||||
|
||||
if ( $payload->{route} and ref( $payload->{route} ) eq 'ARRAY' ) {
|
||||
$opt{route}
|
||||
= [ map { sanitize( q{}, $_ ) } @{ $payload->{route} } ];
|
||||
}
|
||||
|
||||
for my $key (qw(sched_departure rt_departure sched_arrival rt_arrival))
|
||||
{
|
||||
$opt{$key} = DateTime->from_epoch(
|
||||
time_zone => 'Europe/Berlin',
|
||||
epoch => $opt{$key}
|
||||
);
|
||||
}
|
||||
};
|
||||
if ($@) {
|
||||
my ($first_line) = split( qr{\n}, $@ );
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => $first_line
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
my $db = $self->pg->db;
|
||||
my $tx = $db->begin;
|
||||
|
||||
$opt{db} = $db;
|
||||
my ( $journey_id, $error ) = $self->add_journey(%opt);
|
||||
my $journey;
|
||||
|
||||
if ( not $error ) {
|
||||
$journey = $self->get_journey(
|
||||
uid => $uid,
|
||||
db => $db,
|
||||
journey_id => $journey_id,
|
||||
verbose => 1
|
||||
);
|
||||
$error = $self->journey_sanity_check($journey);
|
||||
}
|
||||
|
||||
if ($error) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \0,
|
||||
error => $error
|
||||
}
|
||||
);
|
||||
}
|
||||
elsif ( $payload->{dryRun} ) {
|
||||
$self->render(
|
||||
json => {
|
||||
success => \1,
|
||||
id => $journey_id,
|
||||
result => $journey
|
||||
}
|
||||
);
|
||||
}
|
||||
else {
|
||||
$tx->commit;
|
||||
$self->render(
|
||||
json => {
|
||||
success => \1,
|
||||
id => $journey_id,
|
||||
result => $journey
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sub set_token {
|
||||
my ($self) = @_;
|
||||
if ( $self->validation->csrf_protect->has_error('csrf_token') ) {
|
||||
|
|
|
@ -169,7 +169,7 @@
|
|||
</button>
|
||||
%= end
|
||||
</td>
|
||||
</tr>
|
||||
</tr>-->
|
||||
<tr>
|
||||
<th scope="row">Travel</th>
|
||||
<td>
|
||||
|
@ -192,7 +192,30 @@
|
|||
</button>
|
||||
%= end
|
||||
</td>
|
||||
</tr> -->
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">Import</th>
|
||||
<td>
|
||||
% if ($token->{import}) {
|
||||
%= $acc->{id} . '-' . $token->{import}
|
||||
% }
|
||||
% else {
|
||||
—
|
||||
% }
|
||||
</td>
|
||||
<td>
|
||||
%= form_for 'set_token' => begin
|
||||
%= csrf_field
|
||||
%= hidden_field 'token' => 'import'
|
||||
<button class="btn waves-effect waves-light" type="submit" name="action" value="generate">
|
||||
Generieren
|
||||
</button>
|
||||
<button class="btn waves-effect waves-light red" type="submit" name="action" value="delete">
|
||||
Löschen
|
||||
</button>
|
||||
%= end
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
"scheduledTime": 1556083680,<br/>
|
||||
"realTime": 1556083680,<br/>
|
||||
},<br/>
|
||||
"fromStation" : { (zugehöriger Checkout. Wenn noch nicht eingetragen, sind alle Felder null)<br/>
|
||||
"toStation" : { (zugehöriger Checkout. Wenn noch nicht eingetragen, sind alle Felder null)<br/>
|
||||
"name" : "Essen Stadtwald",<br/>
|
||||
"ds100" : "EESA",<br/>
|
||||
"uic" : 8001896,<br/>
|
||||
|
@ -72,14 +72,64 @@
|
|||
Coming soon.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>-->
|
||||
|
||||
<h3>Travel</h3>
|
||||
<h3>Import</h3>
|
||||
<div class="row">
|
||||
<div class="col s12">
|
||||
<p>
|
||||
Ein- und Auschecken per API. Coming soon.
|
||||
Manueller Import vergangener Zugfahrten (eine Fahrt pro API-Aufruf).
|
||||
</p>
|
||||
<p style="font-family: Monospace;">
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"token":"<%= $uid %>-<%= $token->{status} // 'TOKEN' %>"}' <%= $api_root %>/import
|
||||
</p>
|
||||
<p>Payload (alle nicht als optional markierten Felder sind Pflicht):</p>
|
||||
<p style="font-family: Monospace;">
|
||||
{<br/>
|
||||
"token" : "<%= $token->{import} // 'TOKEN' %>",<br/>
|
||||
"dryRun" : True/False, (optional: wenn True, wird die Eingabe validiert, aber keine Zugfahrt angelegt)<br/>
|
||||
"cancelled" : True/False, (Zugausfall?)<br/>
|
||||
"train" : {<br/>
|
||||
"type" : "S", (Zugtyp, z.B. ICE, RE, S)<br/>
|
||||
"line" : "6", (Linie als String, bei Zügen ohne Linie wie IC/ICE u.ä. null)<br/>
|
||||
"no" : "30634", (Zugnummer als String)<br/>
|
||||
},<br/>
|
||||
"fromStation" : { (Start / Checkin)<br/>
|
||||
"name" : "Essen Hbf", (Name oder DS100)<br/>
|
||||
"scheduledTime": 1556083680, (UNIX-Timestamp)<br/>
|
||||
"realTime": 1556083680, (UNIX-Timestamp, optional, default == scheduledTime)<br/>
|
||||
},<br/>
|
||||
"toStation" : { (Ziel / Checkout)<br/>
|
||||
"name" : "Essen Stadtwald", (Name oder DS100)<br/>
|
||||
"scheduledTime": 1556083980, (UNIX-Timestamp)<br/>
|
||||
"realTime": 1556083980, (UNIX-Timestamp, optional, default == scheduledTime)<br/>
|
||||
},<br/>
|
||||
"route" : [ (optionale Liste mit Unterwegshalten als Name oder DS100, darf keine Stationen vor Checkin oder nach Checkout beinhalten)<br/>
|
||||
"Essen Hbf",<br/>
|
||||
"Essen Süd",<br/>
|
||||
"Essen Stadtwald"<br/>
|
||||
],<br/>
|
||||
"comment" : "Beliebiger Text" (optionaler Freitext-Kommentar)<br/>
|
||||
}
|
||||
</p>
|
||||
<p>
|
||||
Antwort bei Erfolg (der Inhalt von "result" ist von dryRun unabhängig):
|
||||
</p>
|
||||
<p style="font-family: Monospace;">
|
||||
{<br/>
|
||||
"success" : True,<br/>
|
||||
"id" : 1234, (ID der eingetragenen Zugfahrt)<br/>
|
||||
"result" : { ... } (Eingetragene Daten, Inhalt ist variabel)<br/>
|
||||
}
|
||||
</p>
|
||||
<p>
|
||||
Antwort bei Fehler:
|
||||
</p>
|
||||
<p style="font-family: Monospace;">
|
||||
{<br/>
|
||||
"success" : False,<br/>
|
||||
"error" : "Begründung"<br/>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
|
Loading…
Reference in a new issue