Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add fallback backends with respect to load + Backend rework #613

Merged
merged 32 commits into from
Jun 25, 2024
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8977497
Start rework load service
PaulPickhardt Jun 5, 2024
2cf05bf
Fix wordinf
PaulPickhardt Jun 6, 2024
a3e70d9
Remove sendStartNotification
PaulPickhardt Jun 6, 2024
d04ad23
Add first failover implementation
PaulPickhardt Jun 6, 2024
8596c3a
Add failover implementation when loading the app
PaulPickhardt Jun 7, 2024
0117560
Remove option to use beta deployment for normal users
PaulPickhardt Jun 7, 2024
f694d90
Merge dev
PaulPickhardt Jun 19, 2024
7eecd9d
Start reworking load service
PaulPickhardt Jun 19, 2024
0ac43b0
Rework failover implementation
PaulPickhardt Jun 20, 2024
26bda3a
Fix comment
PaulPickhardt Jun 20, 2024
322c4e0
Refactor code
PaulPickhardt Jun 20, 2024
77c9ce5
Refactor code
PaulPickhardt Jun 20, 2024
8fc634c
Add recommendFallback to model
PaulPickhardt Jun 21, 2024
f546d2b
Fix typo
PaulPickhardt Jun 21, 2024
3840f22
WIP
adeveloper-wq Jun 20, 2024
1cf736d
Fix syntax
adeveloper-wq Jun 21, 2024
9241e38
Refactor load servi e
adeveloper-wq Jun 21, 2024
74165e8
Fix title in home view
adeveloper-wq Jun 21, 2024
7428635
Fix persistence of city selection
adeveloper-wq Jun 21, 2024
3961460
Remove second get backend function in settings and refactor code
PaulPickhardt Jun 24, 2024
ee222b1
Rename node to backend
PaulPickhardt Jun 24, 2024
9de53cd
fix prev value set city
PaulPickhardt Jun 24, 2024
e5396b6
Remove unused load service
PaulPickhardt Jun 24, 2024
a1daee5
Remove unused isLoading and reset in load service
PaulPickhardt Jun 24, 2024
fb39397
Refactor checkLoad function to check both default and fallback backen…
PaulPickhardt Jun 24, 2024
c1a0aeb
Add naming convention for backends
PaulPickhardt Jun 24, 2024
3f1b9e0
Fix auth service to support multiple configs
PaulPickhardt Jun 25, 2024
c16713d
Refactor load service
adeveloper-wq Jun 25, 2024
2cb2ea9
Undo backend renaming
adeveloper-wq Jun 25, 2024
7983b5a
Fix fallback logic
PaulPickhardt Jun 25, 2024
fae2009
Merge branch 'backend-rework' of github.com:priobike/priobike-flutter…
PaulPickhardt Jun 25, 2024
f798644
Merge dev into backend-rework
adeveloper-wq Jun 25, 2024
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
4 changes: 2 additions & 2 deletions lib/common/layout/dialog.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@ void showInvalidShortcutSheet(context) {
),
),
pageBuilder: (BuildContext dialogContext, Animation<double> animation, Animation<double> secondaryAnimation) {
final backend = getIt<Settings>().backend;
final city = getIt<Settings>().city;
return DialogLayout(
title: 'Ungültige Strecke',
text:
"Die ausgewählte Strecke ist ungültig, da sie Wegpunkte enthält, die außerhalb des Stadtgebietes von ${backend.region} liegen.\nPrioBike wird aktuell nur innerhalb von ${backend.region} unterstützt.",
"Die ausgewählte Strecke ist ungültig, da sie Wegpunkte enthält, die außerhalb des Stadtgebietes von ${city.nameDE} liegen.\nPrioBike wird aktuell nur innerhalb von ${city.nameDE} unterstützt.",
actions: [
BigButtonPrimary(
label: 'Schließen',
Expand Down
3 changes: 2 additions & 1 deletion lib/common/map/image_cache.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:priobike/http.dart';
import 'package:priobike/logging/logger.dart';
import 'package:priobike/logging/toast.dart';
import 'package:priobike/main.dart';
import 'package:priobike/settings/models/backend.dart';
import 'package:priobike/settings/services/auth.dart';
import 'package:priobike/settings/services/settings.dart';
import 'package:shared_preferences/shared_preferences.dart';
Expand Down Expand Up @@ -58,7 +59,7 @@ class MapboxTileImageCache {
try {
// See: https://docs.mapbox.com/api/maps/static-images/
final settings = getIt<Settings>();
final auth = await Auth.load(settings.backend);
final auth = await Auth.load(settings.city.selectedBackend(true));
final accessTokenHeader = "access_token=${auth.mapboxAccessToken}";
String styleId = "";
// remove prefix "mapbox://styles/" from the styles
Expand Down
8 changes: 4 additions & 4 deletions lib/common/map/layers/poi_layers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class ParkingStationsLayer {
/// Install the source of the layer on the map controller.
_installSource(mapbox.MapboxMap mapController) async {
final settings = getIt<Settings>();
final baseUrl = settings.backend.path;
final baseUrl = settings.city.selectedBackend(true).path;
await mapController.style.addSource(
mapbox.GeoJsonSource(id: sourceId, data: "https://$baseUrl/map-data/bicycle_parking_v2.geojson"),
);
Expand Down Expand Up @@ -129,7 +129,7 @@ class RentalStationsLayer {
/// Install the source of the layer on the map controller.
_installSource(mapbox.MapboxMap mapController) async {
final settings = getIt<Settings>();
final baseUrl = settings.backend.path;
final baseUrl = settings.city.selectedBackend(true).path;
await mapController.style.addSource(
mapbox.GeoJsonSource(id: sourceId, data: "https://$baseUrl/map-data/bicycle_rental_v2.geojson"),
);
Expand Down Expand Up @@ -308,7 +308,7 @@ class BikeShopLayer {
/// Install the source of the layer on the map controller.
_installSource(mapbox.MapboxMap mapController) async {
final settings = getIt<Settings>();
final baseUrl = settings.backend.path;
final baseUrl = settings.city.selectedBackend(true).path;
await mapController.style.addSource(
mapbox.GeoJsonSource(id: sourceId, data: "https://$baseUrl/map-data/bicycle_shop_v2.geojson"),
);
Expand Down Expand Up @@ -482,7 +482,7 @@ class BikeAirStationLayer {
/// Install the source of the layer on the map controller.
_installSource(mapbox.MapboxMap mapController) async {
final settings = getIt<Settings>();
final baseUrl = settings.backend.path;
final baseUrl = settings.city.selectedBackend(true).path;
await mapController.style.addSource(
mapbox.GeoJsonSource(id: sourceId, data: "https://$baseUrl/map-data/bike_air_station_v2.geojson"),
);
Expand Down
6 changes: 3 additions & 3 deletions lib/common/map/layers/prio_layers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class GreenWaveLayer {
/// Install the source of the layer on the map controller.
_installSource(mapbox.MapboxMap mapController) async {
final settings = getIt<Settings>();
final baseUrl = settings.backend.path;
final baseUrl = settings.city.selectedBackend(true).path;
await mapController.style.addSource(
mapbox.GeoJsonSource(id: sourceId, data: "https://$baseUrl/map-data/static_green_waves_v2.geojson"),
);
Expand Down Expand Up @@ -83,7 +83,7 @@ class VeloRoutesLayer {
/// Install the source of the layer on the map controller.
_installSource(mapbox.MapboxMap mapController) async {
final settings = getIt<Settings>();
final baseUrl = settings.backend.path;
final baseUrl = settings.city.selectedBackend(true).path;
await mapController.style.addSource(
mapbox.GeoJsonSource(id: sourceId, data: "https://$baseUrl/map-data/velo_routes_v2.geojson", tolerance: 1),
);
Expand Down Expand Up @@ -135,7 +135,7 @@ class IntersectionsLayer {
_installSource(mapbox.MapboxMap mapController) async {
try {
final settings = getIt<Settings>();
final baseUrl = settings.backend.path;
final baseUrl = settings.city.selectedBackend(true).path;

final url = "https://$baseUrl/sg-selector-nginx/intersections.json.gz";
final endpoint = Uri.parse(url);
Expand Down
4 changes: 2 additions & 2 deletions lib/common/map/view.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ class AppMapState extends State<AppMap> {
cameraOptions: mapbox.CameraOptions(
center: mapbox.Point(
coordinates: mapbox.Position(
settings.backend.center.longitude,
settings.backend.center.latitude,
settings.city.center.longitude,
settings.city.center.latitude,
),
),
zoom: 12,
Expand Down
2 changes: 1 addition & 1 deletion lib/feedback/services/feedback.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ class Feedback with ChangeNotifier {

// Send all of the answered questions to the backend.
final settings = getIt<Settings>();
final baseUrl = settings.backend.path;
final baseUrl = settings.city.selectedBackend(true).path;
final endpoint = Uri.parse('https://$baseUrl/tracking-service/answers/post/');
for (final entry in pending.values.toList().asMap().entries) {
final request = PostAnswerRequest(
Expand Down
24 changes: 24 additions & 0 deletions lib/home/models/backend_status.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
class BackendStatus {
/// If another backend should be used.
final bool recommendOtherBackend;

/// If the backend has a load warning.
final bool warning;

/// The timestamp of the current load status data.
final DateTime timestamp;

BackendStatus({required this.recommendOtherBackend, required this.warning, required this.timestamp});

factory BackendStatus.fromJson(Map<String, dynamic> json) => BackendStatus(
recommendOtherBackend: json['recommendOtherBackend'],
warning: json['warning'],
timestamp: DateTime.fromMillisecondsSinceEpoch(json['timestamp'] * 1000),
);

Map<String, dynamic> toJson() => {
'recommendOtherBackend': recommendOtherBackend,
'warning': warning,
'timestamp': timestamp.millisecondsSinceEpoch,
};
}
4 changes: 2 additions & 2 deletions lib/home/services/link_shortener.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'package:priobike/settings/services/settings.dart';
class LinkShortener {
/// Shorten long link.
static Future<String?> createShortLink(String longLink) async {
final backend = getIt<Settings>().backend;
final backend = getIt<Settings>().city.selectedBackend(false);
String backendPath = backend.path;
final linkShortenerUrl = 'https://$backendPath/link/rest/v3/short-urls';
final linkShortenerEndpoint = Uri.parse(linkShortenerUrl);
Expand Down Expand Up @@ -40,7 +40,7 @@ class LinkShortener {
// Shortcuts from production and release should be working with each others backend.
// Therefore, try fetch from the current backend and (if failed) from the other backend.
// Only staging is not compatible with the other backends.
final backend = getIt<Settings>().backend;
final backend = getIt<Settings>().city.selectedBackend(false);
final String? result = await _fetch(backend, shortLink);
if (result != null) return result;
if (backend == Backend.staging) return null;
Expand Down
70 changes: 27 additions & 43 deletions lib/home/services/load.dart
Original file line number Diff line number Diff line change
@@ -1,91 +1,75 @@
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:priobike/home/models/backend_status.dart';
import 'package:priobike/http.dart';
import 'package:priobike/logging/logger.dart';
import 'package:priobike/logging/toast.dart';
import 'package:priobike/main.dart';
import 'package:priobike/settings/models/backend.dart';
import 'package:priobike/settings/services/settings.dart';
import 'package:priobike/settings/services/features.dart';

class LoadStatus with ChangeNotifier {
/// If the service is currently loading the status history.
bool isLoading = false;

/// The warning that should be displayed.
String? text;

/// If there exists a warning.
bool hasWarning = false;

/// If the fallback backend should be used.
bool useFallback = false;

/// Logger for the status history.
final log = Logger("Load");

LoadStatus();

/// Fetches the status data from the priobike-load-service.
Future<void> fetch() async {
if (isLoading) return;
isLoading = true;

/// Fetches the status data and returns if the given backend is usable.
Future<void> checkLoad(String baseUrl) async {
try {
final settings = getIt<Settings>();
final baseUrl = settings.backend.path;

final url = "https://$baseUrl/load-service/static/load_response.json";
final url = "https://$baseUrl/load-service/load.json";
final endpoint = Uri.parse(url);

final response = await Http.get(endpoint).timeout(const Duration(seconds: 4));

if (response.statusCode != 200) {
isLoading = false;
notifyListeners();
final err = "Error while fetching load status from $endpoint: ${response.statusCode}";
throw Exception(err);
}

final json = jsonDecode(response.body);
final backendStatus = BackendStatus.fromJson(json);

if (json["warning"]) {
// Load status is updated every minute.
// If the timestamp of the status is older than 5 minutes, we assume the backend is not usable.
if (DateTime.now().difference(backendStatus.timestamp).inMinutes > 5) {
hasWarning = true;
text = json["response_text"];
useFallback = true;
log.w("Load status is older than 5 minutes");
} else {
hasWarning = false;
text = null;
hasWarning = backendStatus.warning;
useFallback = backendStatus.recommendOtherBackend;
}

isLoading = false;
notifyListeners();
} catch (e, stacktrace) {
isLoading = false;
notifyListeners();
final hint = "Error while fetching load status: $e $stacktrace";
log.e(hint);
}
}

/// Sends an app start notification to the load service in the backend.
Future<void> sendAppStartNotification() async {
try {
final settings = getIt<Settings>();
final baseUrl = settings.backend.path;

final url = "https://$baseUrl/load-service/app/start";
final endpoint = Uri.parse(url);

final response = await Http.post(endpoint).timeout(const Duration(seconds: 4));
if (response.statusCode != 200) {
final err = "Error while sending app start to load service $endpoint: ${response.statusCode}";
throw Exception(err);
if (getIt<Feature>().canEnableInternalFeatures) {
// Don't switch the backend if the internal version is used. We want to keep the possibility
// to manually set the backend.
if (useFallback) {
ToastMessage.showError(
"Fallback müsste benutzt werden. Aufgrund der internen Version wird das Fallback jedoch nicht benutzt.");
}
} catch (e, stacktrace) {
final hint = "Error while sending app start to load service: $e $stacktrace";
log.e(hint);
useFallback = false;
}

notifyListeners();
}

/// Reset the status.
Future<void> reset() async {
hasWarning = false;
text = null;
isLoading = false;
notifyListeners();
}
Expand Down
2 changes: 1 addition & 1 deletion lib/home/services/poi.dart
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ class POI with ChangeNotifier {
/// A method which is used to fetch the POI data from the backend.
Future<dynamic> _fetchData(String relativeUrl) async {
final settings = getIt<Settings>();
final baseUrl = settings.backend.path;
final baseUrl = settings.city.selectedBackend(true).path;
final dataUrl = "https://$baseUrl$relativeUrl";
final dataEndpoint = Uri.parse(dataUrl);

Expand Down
10 changes: 5 additions & 5 deletions lib/home/services/shortcuts.dart
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ class Shortcuts with ChangeNotifier {
if (shortcuts == null) return;
final storage = await SharedPreferences.getInstance();

final backend = getIt<Settings>().backend;
final city = getIt<Settings>().city;
final jsonStr = jsonEncode(shortcuts!.map((e) => e.toJson()).toList());
storage.setString("priobike.home.shortcuts.${backend.regionName}", jsonStr);
storage.setString("priobike.home.shortcuts.${city.nameDE}", jsonStr);

// Activates the tutorial if more then 3 (+2 default shortcuts) shortcuts were stored.
if (shortcuts!.length >= 5) {
Expand All @@ -137,11 +137,11 @@ class Shortcuts with ChangeNotifier {
if (shortcuts != null) return;
final storage = await SharedPreferences.getInstance();

final backend = getIt<Settings>().backend;
final jsonStr = storage.getString("priobike.home.shortcuts.${backend.regionName}");
final city = getIt<Settings>().city;
final jsonStr = storage.getString("priobike.home.shortcuts.${city.nameDE}");

if (jsonStr == null) {
shortcuts = backend.defaultShortcuts;
shortcuts = city.defaultShortcuts;
await storeShortcuts();
} else {
// Init shortcuts.
Expand Down
6 changes: 3 additions & 3 deletions lib/home/views/load_status.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class LoadStatusViewState extends State<LoadStatusView> {
context: context,
builder: (BuildContext context) {
return DialogLayout(
title: "Mehr Nutzende als normalerweise",
text: loadStatus.text ?? "",
title: "Starke Auslastung",
text: "Aktuell sind unsere Server außergewöhnlich stark ausgelastet.",
actions: [
BigButtonTertiary(
label: "Schließen",
Expand All @@ -58,7 +58,7 @@ class LoadStatusViewState extends State<LoadStatusView> {
children: [
Flexible(
child: Content(
text: "Mehr Nutzende als normalerweise",
text: "Starke Auslastung",
context: context,
),
),
Expand Down
3 changes: 0 additions & 3 deletions lib/home/views/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -155,15 +155,13 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver, RouteAw
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.resumed) {
predictionStatusSummary.fetch();
loadStatus.fetch();
news.getArticles();
}
}

@override
void didPopNext() {
predictionStatusSummary.fetch();
loadStatus.fetch();
news.getArticles();
}

Expand Down Expand Up @@ -272,7 +270,6 @@ class HomeViewState extends State<HomeView> with WidgetsBindingObserver, RouteAw
onRefresh: () async {
HapticFeedback.lightImpact();
await predictionStatusSummary.fetch();
await loadStatus.fetch();
await news.getArticles();
await getIt<Weather>().fetch();
// Wait for one more second, otherwise the user will get impatient.
Expand Down
18 changes: 9 additions & 9 deletions lib/home/views/nav.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,18 +52,18 @@ class NavBarView extends StatelessWidget {
context: context,
),
Content(
text: settings.backend == Backend.staging ? " DD" : " HH",
text: settings.city == City.dresden ? " DD" : " HH",
color: Colors.white,
context: context,
),
Flexible(
fit: FlexFit.tight,
child: Small(
text: settings.backend == Backend.production ? " beta" : "",
color: Colors.white,
context: context,
),
),
settings.city.selectedBackend(true) == Backend.production
? Content(
text: ".",
color: Colors.white,
context: context,
)
: Container(),
Expanded(child: Container()),
BoldContent(text: "Moin!", color: Colors.white, context: context),
],
),
Expand Down
Loading