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,
|
status => 1,
|
||||||
history => 2,
|
history => 2,
|
||||||
action => 3,
|
action => 3,
|
||||||
|
import => 4,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
$self->attr(
|
$self->attr(
|
||||||
token_types => sub {
|
token_types => sub {
|
||||||
return [qw(status history action)];
|
return [qw(status history action import)];
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -330,7 +331,7 @@ sub startup {
|
||||||
my ( $self, %opt ) = @_;
|
my ( $self, %opt ) = @_;
|
||||||
|
|
||||||
my $db = $opt{db};
|
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 $now = DateTime->now( time_zone => 'Europe/Berlin' );
|
||||||
my $dep_station = get_station( $opt{dep_station} );
|
my $dep_station = get_station( $opt{dep_station} );
|
||||||
my $arr_station = get_station( $opt{arr_station} );
|
my $arr_station = get_station( $opt{arr_station} );
|
||||||
|
@ -410,7 +411,7 @@ sub startup {
|
||||||
$journey_id
|
$journey_id
|
||||||
= $db->insert( 'journeys', $entry, { returning => 'id' } )
|
= $db->insert( 'journeys', $entry, { returning => 'id' } )
|
||||||
->hash->{id};
|
->hash->{id};
|
||||||
$self->invalidate_stats_cache( $opt{rt_departure}, $db );
|
$self->invalidate_stats_cache( $opt{rt_departure}, $db, $uid );
|
||||||
};
|
};
|
||||||
|
|
||||||
if ($@) {
|
if ($@) {
|
||||||
|
@ -3232,6 +3233,7 @@ sub startup {
|
||||||
$r->get('/status/:name/:ts')->to('traveling#user_status');
|
$r->get('/status/:name/:ts')->to('traveling#user_status');
|
||||||
$r->get('/ajax/status/:name')->to('traveling#public_status_card');
|
$r->get('/ajax/status/:name')->to('traveling#public_status_card');
|
||||||
$r->get('/ajax/status/:name/:ts')->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('/action')->to('traveling#log_action');
|
||||||
$r->post('/geolocation')->to('traveling#geolocation');
|
$r->post('/geolocation')->to('traveling#geolocation');
|
||||||
$r->post('/list_departures')->to('traveling#redirect_to_station');
|
$r->post('/list_departures')->to('traveling#redirect_to_station');
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package Travelynx::Controller::Api;
|
package Travelynx::Controller::Api;
|
||||||
use Mojo::Base 'Mojolicious::Controller';
|
use Mojo::Base 'Mojolicious::Controller';
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
use Travel::Status::DE::IRIS::Stations;
|
use Travel::Status::DE::IRIS::Stations;
|
||||||
use UUID::Tiny qw(:std);
|
use UUID::Tiny qw(:std);
|
||||||
|
|
||||||
|
@ -8,6 +9,17 @@ sub make_token {
|
||||||
return create_uuid_as_string(UUID_V4);
|
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 {
|
sub documentation {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
|
|
||||||
|
@ -71,9 +83,9 @@ sub get_v0 {
|
||||||
or $status->{cancelled}
|
or $status->{cancelled}
|
||||||
) ? \1 : \0,
|
) ? \1 : \0,
|
||||||
station => {
|
station => {
|
||||||
ds100 => $status->{arr_ds100} // $status->{dep_ds100},
|
ds100 => $status->{arr_ds100} // $status->{dep_ds100},
|
||||||
name => $status->{arr_name} // $status->{dep_name},
|
name => $status->{arr_name} // $status->{dep_name},
|
||||||
uic => $station_eva,
|
uic => $station_eva,
|
||||||
longitude => $station_lon,
|
longitude => $station_lon,
|
||||||
latitude => $station_lat,
|
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 {
|
sub set_token {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
if ( $self->validation->csrf_protect->has_error('csrf_token') ) {
|
if ( $self->validation->csrf_protect->has_error('csrf_token') ) {
|
||||||
|
|
|
@ -169,7 +169,7 @@
|
||||||
</button>
|
</button>
|
||||||
%= end
|
%= end
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>-->
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">Travel</th>
|
<th scope="row">Travel</th>
|
||||||
<td>
|
<td>
|
||||||
|
@ -192,7 +192,30 @@
|
||||||
</button>
|
</button>
|
||||||
%= end
|
%= end
|
||||||
</td>
|
</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>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
"scheduledTime": 1556083680,<br/>
|
"scheduledTime": 1556083680,<br/>
|
||||||
"realTime": 1556083680,<br/>
|
"realTime": 1556083680,<br/>
|
||||||
},<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/>
|
"name" : "Essen Stadtwald",<br/>
|
||||||
"ds100" : "EESA",<br/>
|
"ds100" : "EESA",<br/>
|
||||||
"uic" : 8001896,<br/>
|
"uic" : 8001896,<br/>
|
||||||
|
@ -72,14 +72,64 @@
|
||||||
Coming soon.
|
Coming soon.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>-->
|
||||||
|
|
||||||
<h3>Travel</h3>
|
<h3>Import</h3>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col s12">
|
<div class="col s12">
|
||||||
<p>
|
<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>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
-->
|
|
||||||
|
|
Loading…
Reference in a new issue