Skip to content

Commit

Permalink
Merge branch 'dev' into fix-continue-without-and-other-bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
Sainaamr authored Jan 28, 2024
2 parents 7eb8076 + 9ae60a0 commit 5589b44
Show file tree
Hide file tree
Showing 35 changed files with 425 additions and 374 deletions.
82 changes: 23 additions & 59 deletions lib/base/networking/api/handler/api_handler.dart
Original file line number Diff line number Diff line change
@@ -1,37 +1,29 @@
import 'dart:convert';

import 'package:dio/dio.dart';
import 'package:gocast_mobile/models/error/error_model.dart';
import 'package:logger/logger.dart';

/// Handles HTTP communication for the application.
class ApiHandler {
static final Logger _logger = Logger();

/// Handles an HTTP response.
/// Handles an HTTP error response.
///
/// This method checks the status code of the HTTP response and throws an [AppError] if necessary.
/// It uses the `handleHttpStatus` method to check the status code and throw the appropriate error.
/// This method takes a [response] and handles the error based on the status code.
///
/// The `apiMessage` from the response is passed to the `handleHttpStatus` method and used for the error message
/// of the [AppError.argumentError] thrown for a 400 status code.
/// Throws an [AppError] if the status code is not in the 2xx range.
static void handleHttpResponse(Response response) {
_logger
.i('Received HTTP response with status code: ${response.statusCode}');

if (response.data != null && response.data != '') {
try {
// Attempt to decode the response body
var responseBody = jsonDecode(response.data);
String? apiMessage = responseBody['message'];

// Log the extracted message
if (apiMessage != null) {
_logger.i('API message: $apiMessage');
}

handleHttpStatus(response.statusCode, apiMessage);
} catch (e) {
// Log any JSON decoding errors
_logger.e('Error decoding response data: $e');
}
} else {
Expand All @@ -40,58 +32,30 @@ class ApiHandler {
}
}

/// Handles the HTTP status code of a response and throws an [AppError] if necessary.
///
/// This method checks the HTTP status code and throws an [AppError] for certain status codes.
/// If the status code is null or not in the range of 100 to 399, it throws an [AppError].
/// For status codes 400, 401, 403, 404, and 500, it throws specific [AppError]s.
/// For all other status codes, it throws an [AppError.unknownError].
///
/// The method also accepts an optional `apiMessage` parameter. If provided, this message
/// is used for the error message of the [AppError.argumentError] thrown for a 400 status code.
/// Handles an HTTP status code.
///
/// - 1xx-3xx: No error is thrown
/// - 400: [AppError.argumentError] is thrown with `apiMessage` as the error message
/// - 401: [AppError.authenticationError] is thrown
/// - 403: [AppError.forbidden] is thrown
/// - 404: [AppError.notFound] is thrown
/// - 500: [AppError.internalServerError] is thrown
/// - Other: [AppError.unknownError] is thrown
/// This method takes a [statusCode] and [apiMessage] and throws an [AppError]
/// based on the status code.
static void handleHttpStatus(int? statusCode, String? apiMessage) {
// Log the received status code and API message
_logger
.d('Handling HTTP status code: $statusCode, API message: $apiMessage');

_logger.i('Handling HTTP status code: $statusCode, API message: $apiMessage');
if (statusCode == null) {
_logger.e('Status code is null');
throw AppError.unknownError("Status code is null");
}

if (statusCode >= 100 && statusCode < 400) {
// Log successful response
_logger.i('Successful HTTP response with status code: $statusCode');
return;
}
// Handle error status codes
switch (statusCode) {
case 400:
_logger.w('HTTP 400 Bad Request: $apiMessage');
throw AppError.argumentError(apiMessage ?? "Bad Request");
case 401:
_logger.w('HTTP 401 Unauthorized');
throw AppError.authenticationError();
case 403:
_logger.w('HTTP 403 Forbidden');
throw AppError.forbidden();
case 404:
_logger.w('HTTP 404 Not Found');
throw AppError.notFound();
case 500:
_logger.e('HTTP 500 Internal Server Error');
throw AppError.internalServerError();
default:
_logger.e('Unknown error with status code: $statusCode');
throw AppError.unknownError("Status code: $statusCode");
if (statusCode < 100 || statusCode >= 400) {
switch (statusCode) {
case 400:
throw AppError.argumentError(apiMessage ?? "Bad Request");
case 401:
throw AppError.authenticationError();
case 403:
throw AppError.forbidden();
case 404:
throw AppError.notFound();
case 500:
throw AppError.internalServerError();
default:
throw AppError.unknownError("Status code: $statusCode");
}
}
}
}
50 changes: 29 additions & 21 deletions lib/base/networking/api/handler/auth_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,51 +12,52 @@ import 'package:gocast_mobile/providers.dart';
import 'package:gocast_mobile/utils/globals.dart';
import 'package:logger/logger.dart';

/// Handles authentication for the application.
///Authentication handler for the application.
class AuthHandler {
static final Logger _logger = Logger();
static bool isLoginSuccessful = false;

/// Performs basic authentication.
/// the basic authentication method for internal Users
///
/// This method sends a POST request to the basic login URL with the given
/// username and password. If the request is successful, it saves the JWT token.
/// This method takes a [username] and [password] and sends a POST request to the server.
/// The server will respond with a JWT token that will be saved in the cookie jar.
///
/// Throws an [AppError] if a network error occurs or if no JWT-cookie is set.
/// Throws an [AppError] if a network error occurs or if the gRPC call fails.
static Future<void> basicAuth(
String username,
String password,
) async {
var url = Routes.basicLogin;
_logger.i('Starting basic authentication for user: $username');
var url = Routes.basicLogin;
final cookieJar = CookieJar();
final dio = Dio(
BaseOptions(
followRedirects: false,
validateStatus: (status) {
return status! < 500;
},
receiveTimeout: const Duration(seconds: 5),
receiveTimeout: const Duration(seconds: 3),
),
);
dio.interceptors.add(CookieManager(cookieJar));

final formData = FormData.fromMap({
'username': username,
'password': password,
});

Response response;
try {
response = await dio.post(url, data: formData);
_logger.i('Received HTTP response with status code: ${response.statusCode}');
} catch (e) {
_logger.e('Error during basic authentication for user: $username, Error: $e');
throw AppError.userError();
}
try {
final response = await dio.post(url, data: formData);
ApiHandler.handleHttpResponse(response);
_logger.i('Authentication successful for user: $username');
} catch (e) {
_logger.e('Authentication failed for user: $username, Error: $e');
// Throw the error so it can be caught and handled by the caller of basicAuth
throw AppError.networkError(e);
}

// Save jwt token
try {
List<Cookie> cookies = await cookieJar.loadForRequest(Uri.parse(url));
await TokenHandler.saveTokenFromCookies('jwt', cookies);
Expand All @@ -67,13 +68,16 @@ class AuthHandler {
}
}

/// the SSO authentication method for all Users
///
/// This method takes a [context] and [ref] and sends a POST request to the server.
/// The server will respond with a JWT token that will be saved in the cookie jar.
///
/// Throws an [AppError] if a network error occurs or if the gRPC call fails.
static Future<void> ssoAuth(BuildContext context, WidgetRef ref) async {
final viewModel = ref.read(userViewModelProvider.notifier);

_logger.i('Starting SSO authentication');
viewModel.setLoading(true); // Set loading state
_logger.i('Loading SSO login page');

try {
await navigatorKey.currentState?.push(
MaterialPageRoute(
Expand Down Expand Up @@ -101,6 +105,7 @@ class AuthHandler {
}
}

///Helper method to build the web view for SSO authentication
static Widget _buildWebView() {
_logger.i('Building web view');
return webview.InAppWebView(
Expand All @@ -109,6 +114,7 @@ class AuthHandler {
);
}

///Helper method to handle the web view load stop event
static Future<void> _onWebViewLoadStop(
webview.InAppWebViewController controller,
Uri? url,
Expand All @@ -124,21 +130,23 @@ class AuthHandler {
navigatorKey.currentState?.pushReplacementNamed('/welcome');
_logger.i('SSO authentication completed, redirected to app');
} else if (url != null) {
_logger.d('Web view loaded URL: $url');
_logger.i('Web view loaded URL: $url');
} else {
_logger.w('URL is null in web view onLoadStop');
_logger.e('URL is null in web view onLoadStop');
}
} catch (e) {
_logger.e('Error during SSO authentication: $e');
throw AppError.networkError(e);
throw AppError.userError();
}
}

///Helper method to handle the cookie retrieval
///
/// This method takes a [url] and retrieves the JWT token from the cookies.
static Future<void> _handleCookieRetrieval(Uri url) async {
try {
final cookieManager = webview.CookieManager.instance();
List<webview.Cookie> cookies = await cookieManager.getCookies(url: url);
_logger.d('Retrieved cookies from URL: $url');
webview.Cookie? jwtCookie;
for (var cookie in cookies) {
if (cookie.name == 'jwt') {
Expand Down
31 changes: 7 additions & 24 deletions lib/base/networking/api/handler/bookmarks_handler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,44 +3,37 @@ import 'package:logger/logger.dart';

import 'grpc_handler.dart';

/// Handles bookmark-related data operations.
///
/// This class is responsible for fetching and posting bookmark-related data, such as fetching user bookmarks and adding a bookmark.
class BooKMarkHandler {
static final Logger _logger = Logger();
final GrpcHandler _grpcHandler;

/// Creates a new instance of the `BookmarkHandler` class.
///
/// The [GrpcHandler] is required.
BooKMarkHandler(this._grpcHandler);

/// Fetches the current user's bookmarks.
/// Fetches user bookmarks.
///
/// This method sends a `getUserBookmarks` gRPC call to fetch the user's
/// bookmarks.
/// This method sends a `getUserBookmarks` gRPC call to fetch the user's bookmarks.
///
/// Returns a [List<Bookmark>] instance that represents the user's bookmarks.
/// returns a [List<Bookmark>] instance that represents the user's bookmarks.
Future<List<Bookmark>> fetchUserBookmarks() async {
_logger.i('Fetching user bookmarks');
return _grpcHandler.callGrpcMethod(
(client) async {
final response = await client.getUserBookmarks(GetBookmarksRequest());
_logger.i('User bookmarks fetched successfully');
_logger.d('User bookmarks: ${response.bookmarks}');
return response.bookmarks;
},
);
}


/// Adds a bookmark for the current user.
///
/// Sends a `putUserBookmark` gRPC call with the given [bookmarkData] to add a new bookmark.
/// Logs the action of saving and provides details of the saved bookmark.
/// Sends a `putUserBookmark` gRPC call with the given [bookmarkData] to add a bookmark.
/// Logs the action of adding and provides details of the added bookmark.
///
/// [bookmarkData]: The data of the bookmark to be added, encapsulated in a `BookmarkData` object.
///
/// Returns [Bookmark]: The `Bookmark` instance representing the newly added bookmark.
/// returns [Bookmark]: The `Bookmark` instance representing the added bookmark.
Future<Bookmark> addToBookmark(BookmarkData bookmarkData) async {
var request = PutBookmarkRequest(
description: bookmarkData.description,
Expand All @@ -52,21 +45,12 @@ class BooKMarkHandler {
return _grpcHandler.callGrpcMethod(
(client) async {
final response = await client.putUserBookmark(request);
_logger.i('User bookmark saved successfully');
_logger.d('User bookmark: ${response.bookmark}');
return response.bookmark;
},
);
}

/// Removes a bookmark for the current user.
///
/// Sends a `deleteUserBookmark` gRPC call to remove a bookmark identified by [bookmarkID].
/// Logs the successful removal of the bookmark.
///
/// [bookmarkID]: The unique identifier of the bookmark to be removed.
///
/// Returns [bool]: `true` if the bookmark was removed successfully, `false` otherwise.
Future<bool> removeFromBookmarks(int bookmarkID) async {
var request = DeleteBookmarkRequest(bookmarkID: bookmarkID);
try {
Expand Down Expand Up @@ -105,7 +89,6 @@ class BooKMarkHandler {
return _grpcHandler.callGrpcMethod(
(client) async {
final response = await client.patchUserBookmark(request);
_logger.i('User bookmark updated successfully');
_logger.d('User bookmark: ${response.bookmark}');
return response.bookmark;
},
Expand Down
Loading

0 comments on commit 5589b44

Please sign in to comment.