Commit 5fe4174f authored by Birte Kristina Friesel's avatar Birte Kristina Friesel
Browse files

Add API entry point for journey import

parent 7e9a2ebf
Loading
Loading
Loading
Loading
+5 −3
Original line number Diff line number Diff line
@@ -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');
+164 −3
Original line number Diff line number Diff line
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) = @_;

@@ -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') ) {
+25 −2
Original line number Diff line number Diff line
@@ -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>
+55 −5
Original line number Diff line number Diff line
@@ -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>
-->