From a6f4c3e61d12246df7b13bd4d5e8a8f3906dad1c Mon Sep 17 00:00:00 2001 From: Steffen Kleinle Date: Thu, 26 Dec 2024 11:59:42 +0100 Subject: [PATCH] Add local api setup and docs --- .env.dev | 6 ++- .env.prod | 5 +- .env.staging | 9 ++++ README.md | 53 +++++++++++++++---- android/app/src/debug/AndroidManifest.xml | 2 + lib/app/auth/repository/auth_repository.dart | 3 ++ lib/app/constants/config.dart | 17 +++--- lib/app/router.dart | 3 +- lib/app/services/gruene_api_core.dart | 6 +-- .../settings/screens/settings_screen.dart | 3 +- 10 files changed, 79 insertions(+), 28 deletions(-) create mode 100644 .env.staging diff --git a/.env.dev b/.env.dev index 6d56c4cd..3660fcd6 100644 --- a/.env.dev +++ b/.env.dev @@ -1,6 +1,8 @@ +DEVELOPMENT=true +GRUENE_API_URL=http://127.0.0.1:5000 + OIDC_CLIENT_ID=gruene_app -OIDC_ISSUER=https://saml.gruene.de/realms/gruene-app-test -USE_LOGIN=true +OIDC_ISSUER=http://127.0.0.1:8080/realms/dev MAP_MAPLIBRE_URL=assets/maps/gruene_map.json MAP_ADDRESSSEARCH_URL=https://maps.gruene.verdigado.net/nominatim diff --git a/.env.prod b/.env.prod index b3d0e725..5bf7b86a 100644 --- a/.env.prod +++ b/.env.prod @@ -1,7 +1,8 @@ +DEVELOPMENT=false +GRUENE_API_URL=https://api.gruene.de + OIDC_CLIENT_ID=gruene_app OIDC_ISSUER=https://saml.gruene.de/realms/gruenes-netz -USE_LOGIN=true MAP_MAPLIBRE_URL=assets/maps/gruene_map.json MAP_ADDRESSSEARCH_URL=https://maps.gruene.verdigado.net/nominatim -GRUENES_NETZ_API_URL=https://api.gruene.de diff --git a/.env.staging b/.env.staging new file mode 100644 index 00000000..ca3d838f --- /dev/null +++ b/.env.staging @@ -0,0 +1,9 @@ +DEVELOPMENT=false +GRUENE_API_URL=https://api.gruene.de +GRUENE_API_ACCESS_TOKEN= + +OIDC_CLIENT_ID=gruene_app +OIDC_ISSUER=https://saml.gruene.de/realms/gruene-app-test + +MAP_MAPLIBRE_URL=https://maps.gruene.verdigado.net/styles/wkapp/style.json +MAP_ADDRESSSEARCH_URL=https://nominatim.maps.tuerantuer.org/nominatim diff --git a/README.md b/README.md index 82d15ce5..ae69dc22 100644 --- a/README.md +++ b/README.md @@ -12,34 +12,67 @@ ### Initial Setup -1. Install the Android SDK via the [Android plugin](https://www.jetbrains.com/help/idea/create-your-first-android-application.html#754fd) or Android Studio +1. Install the Android SDK via + the [Android plugin](https://www.jetbrains.com/help/idea/create-your-first-android-application.html#754fd) or Android + Studio 2. Install [fvm](https://fvm.app/documentation/getting-started/installation) (flutter version manager) 3. Install flutter + ``` shell fvm install ``` -4. Configure local environment -``` shell -cp .env.dev .env -``` -5. [Optional] Adjust the environment variables in `.env` as needed -6. [Optional] Open IntelliJ settings and + +4. [Optional] Open IntelliJ settings and - Install the Android plugin and set the Android SDK path - Install the Dart plugin and set the Dart SDK path - Install the Flutter plugin and set the Flutter SDK path +#### API Setup + +There are two options to connect your app to the Grüne API for development: + +- [Connect to the staging Grüne API using an access token](#staging-grüne-api-setup) +- [Connect to the locally running Grüne API](#local-grüne-api-setup) + +##### Staging Grüne API Setup + +1. Generate an access token for the staging Grüne API +2. Copy staging environment + +``` shell +cp .env.staging .env +``` + +3. Add your `GRUENE_API_ACCESS_TOKEN` to `.env` + +##### Local Grüne API Setup + +0. Make sure the Grüne API is setup and running. For documentation on the necessary steps, refer to + the [Grüne API README](https://github.com/verdigado/gruene-api). +1. Configure local environment + +``` shell +cp .env.dev .env +``` + ### Run the App 1. Update translations + ``` shell fvm dart run slang ``` + 2. Run build runner to update API definitions + ``` shell fvm dart run build_runner build ``` -3. Run the app (`development`) -### Connecting to local Grüne API +3. [Optional] If you are running the app on a real device and use a local Grüne API, you need to expose the ports: + +``` shell +adb reverse tcp:8080 tcp:8080 && adb reverse tcp:5000 tcp:5000 +``` -TODO +4. Run the app (`development`) diff --git a/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml index 399f6981..55d6d32e 100644 --- a/android/app/src/debug/AndroidManifest.xml +++ b/android/app/src/debug/AndroidManifest.xml @@ -4,4 +4,6 @@ to allow setting breakpoints, to provide hot reload, etc. --> + + diff --git a/lib/app/auth/repository/auth_repository.dart b/lib/app/auth/repository/auth_repository.dart index 251ab2a3..9061ee8d 100644 --- a/lib/app/auth/repository/auth_repository.dart +++ b/lib/app/auth/repository/auth_repository.dart @@ -18,6 +18,7 @@ class AuthRepository { AuthorizationTokenRequest( Config.oidcClientId, Config.oidcCallbackPath, + allowInsecureConnections: Config.development, issuer: Config.oidcIssuer, scopes: ['openid', 'profile', 'email', 'offline_access'], ), @@ -43,6 +44,7 @@ class AuthRepository { idTokenHint: idToken, postLogoutRedirectUrl: Config.oidcCallbackPath, discoveryUrl: '${Config.oidcIssuer}/.well-known/openid-configuration', + allowInsecureConnections: Config.development, ), ); @@ -69,6 +71,7 @@ class AuthRepository { TokenRequest( Config.oidcClientId, Config.oidcCallbackPath, + allowInsecureConnections: Config.development, refreshToken: refreshToken, issuer: Config.oidcIssuer, ), diff --git a/lib/app/constants/config.dart b/lib/app/constants/config.dart index 46ce5b10..4912bd11 100644 --- a/lib/app/constants/config.dart +++ b/lib/app/constants/config.dart @@ -2,14 +2,17 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; class Config { static String get appId => 'de.gruene.wkapp'; + static bool get development => bool.parse(dotenv.env['DEVELOPMENT']!); + + static String get grueneApiUrl => dotenv.env['GRUENE_API_URL']!; + static String get grueneApiAccessToken => dotenv.env['GRUENE_API_ACCESS_TOKEN'] ?? ''; + static String get oidcCallbackPath => '${Config.appId}://oauthredirect'; - static String get oidcClientId => dotenv.env['OIDC_CLIENT_ID'] ?? ''; - static String get oidcIssuer => dotenv.env['OIDC_ISSUER'] ?? ''; - static bool get useLogin => dotenv.env['USE_LOGIN'] == 'true'; - static String get maplibreUrl => dotenv.env['MAP_MAPLIBRE_URL'] ?? ''; - static String get addressSearchUrl => dotenv.env['MAP_ADDRESSSEARCH_URL'] ?? ''; - static String get gruenesNetzApiUrl => dotenv.env['GRUENES_NETZ_API_URL'] ?? 'http://localhost:5000'; - static String get gruenesNetzAccessToken => dotenv.env['GRUENES_NETZ_ACCESS_TOKEN'] ?? ''; + static String get oidcClientId => dotenv.env['OIDC_CLIENT_ID']!; + static String get oidcIssuer => dotenv.env['OIDC_ISSUER']!; + + static String get maplibreUrl => dotenv.env['MAP_MAPLIBRE_URL']!; + static String get addressSearchUrl => dotenv.env['MAP_ADDRESSSEARCH_URL']!; static bool get androidFloss { // may be needed when building for f-droid store diff --git a/lib/app/router.dart b/lib/app/router.dart index bcc594be..b94caceb 100644 --- a/lib/app/router.dart +++ b/lib/app/router.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:gruene_app/app/auth/bloc/auth_bloc.dart'; -import 'package:gruene_app/app/constants/config.dart'; import 'package:gruene_app/app/constants/routes.dart'; GoRouter createAppRouter(BuildContext context) { @@ -19,7 +18,7 @@ GoRouter createAppRouter(BuildContext context) { ], redirect: (context, state) { final authBloc = context.read(); - final isLoggedIn = !Config.useLogin || authBloc.state is Authenticated; + final isLoggedIn = authBloc.state is Authenticated; final isLoggingIn = state.uri.toString() == Routes.login.path; final isMfa = [ Routes.mfa.path, diff --git a/lib/app/services/gruene_api_core.dart b/lib/app/services/gruene_api_core.dart index b9dcc611..1ce8a07a 100644 --- a/lib/app/services/gruene_api_core.dart +++ b/lib/app/services/gruene_api_core.dart @@ -13,8 +13,8 @@ Future createGrueneApiClient() async { List interceptors = [UserAgentInterceptor(userAgentHeaderValue)]; chopper.Authenticator? authenticator; - if (Config.gruenesNetzAccessToken.isNotEmpty) { - interceptors.add(AuthInterceptor.withFixedAccessToken(Config.gruenesNetzAccessToken)); + if (Config.grueneApiAccessToken.isNotEmpty) { + interceptors.add(AuthInterceptor.withFixedAccessToken(Config.grueneApiAccessToken)); } else { AuthRepository repo = AuthRepository(); authenticator = AccessTokenAuthenticator(repo); @@ -22,7 +22,7 @@ Future createGrueneApiClient() async { } return GrueneApi.create( - baseUrl: Uri.parse(Config.gruenesNetzApiUrl), + baseUrl: Uri.parse(Config.grueneApiUrl), authenticator: authenticator, interceptors: interceptors, ); diff --git a/lib/features/settings/screens/settings_screen.dart b/lib/features/settings/screens/settings_screen.dart index 50daa894..5306f8e9 100644 --- a/lib/features/settings/screens/settings_screen.dart +++ b/lib/features/settings/screens/settings_screen.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; import 'package:gruene_app/app/auth/bloc/auth_bloc.dart'; -import 'package:gruene_app/app/constants/config.dart'; import 'package:gruene_app/app/constants/routes.dart'; import 'package:gruene_app/app/constants/urls.dart'; import 'package:gruene_app/app/theme/theme.dart'; @@ -19,7 +18,7 @@ class SettingsScreen extends StatelessWidget { Widget build(BuildContext context) { final theme = Theme.of(context); final authBloc = context.read(); - final isLoggedIn = !Config.useLogin || authBloc.state is Authenticated; + final isLoggedIn = authBloc.state is Authenticated; return ListView( padding: const EdgeInsets.only(top: 32), children: [