diff --git a/front/lib/core/config/custom_interceptor.dart b/front/lib/core/config/custom_interceptor.dart index 593efee..adfcdaa 100644 --- a/front/lib/core/config/custom_interceptor.dart +++ b/front/lib/core/config/custom_interceptor.dart @@ -1,7 +1,12 @@ +import 'dart:io'; + +import 'package:dartz/dartz.dart'; import 'package:dio/dio.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart' hide Options; import 'package:front/core/const/const.dart'; +import 'package:front/core/utils/exception.dart'; +import 'package:front/core/utils/failure.dart'; ///토큰을 헤더에 추가, 에러를 다루기 위한 인터셉터 class CustomInterceptor extends Interceptor { @@ -92,8 +97,6 @@ class CustomInterceptor extends Interceptor { // } on DioException catch (e) { // // 새로운 Access Token임에도 에러가 발생한다면, Refresh Token마저도 만료된 것임 - // await ref.read(userMeNotifierProvider.notifier).logout(); - // return handler.reject(e); // } // } @@ -101,3 +104,42 @@ class CustomInterceptor extends Interceptor { // return handler.reject(err); // } } + +mixin ErrorHandler { + Future> catchError(Future Function() f) async { + try { + return Future.value(Right(await f())); + } on DioException catch (err) { + switch (err.type) { + case DioExceptionType.badCertificate: + return Future.value(Left(ResponseFailure.unknown.getFailure())); + case DioExceptionType.badResponse: + return Future.value(Left(ResponseFailure.values + .firstWhere((value) => value.code == err.response?.statusCode, + orElse: () => ResponseFailure.unknown) + .failure + .copyWith(message: err.response?.data['message']))); + case DioExceptionType.cancel: + return Future.value(Left(ResponseFailure.cancel.getFailure())); + case DioExceptionType.connectionError: + return Future.value( + Left(ResponseFailure.connectionError.getFailure())); + case DioExceptionType.connectionTimeout: + return Future.value( + Left(ResponseFailure.connectionTimeout.getFailure())); + case DioExceptionType.receiveTimeout: + return Future.value( + Left(ResponseFailure.receiveTimeout.getFailure())); + case DioExceptionType.sendTimeout: + return Future.value(Left(ResponseFailure.sendTimeout.getFailure())); + case DioExceptionType.unknown: + return Future.value(Left(ResponseFailure.unknown.getFailure())); + } + } on ServerException { + return Future.value(const Left(ServerFailure('An error has occurred'))); + } on SocketException { + return Future.value( + const Left(ServerFailure('Failed to connect to the network'))); + } + } +} diff --git a/front/lib/core/project/data/data_sources/remote_data_source.dart b/front/lib/core/project/data/data_sources/remote_data_source.dart index 927b2e3..9ae2483 100644 --- a/front/lib/core/project/data/data_sources/remote_data_source.dart +++ b/front/lib/core/project/data/data_sources/remote_data_source.dart @@ -31,7 +31,7 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource { throw ServerException(); } } on DioException { - throw ServerException(); + rethrow; } } @@ -46,7 +46,7 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource { throw ServerException(); } } on DioException { - throw ServerException(); + rethrow; } } @@ -62,7 +62,7 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource { throw ServerException(); } } on DioException { - throw ServerException(); + rethrow; } } @@ -77,7 +77,7 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource { throw ServerException(); } } on DioException { - throw ServerException(); + rethrow; } } @@ -93,8 +93,8 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource { } else { throw ServerException(); } - } on DioException { - throw ServerException(); + } on DioException { + rethrow; } } } diff --git a/front/lib/core/project/data/repositories/project_repository_impl.dart b/front/lib/core/project/data/repositories/project_repository_impl.dart index abe2e72..487b850 100644 --- a/front/lib/core/project/data/repositories/project_repository_impl.dart +++ b/front/lib/core/project/data/repositories/project_repository_impl.dart @@ -1,15 +1,13 @@ -import 'dart:io'; - import 'package:dartz/dartz.dart'; +import 'package:front/core/config/custom_interceptor.dart'; import 'package:front/core/project/data/data_sources/remote_data_source.dart'; import 'package:front/core/project/data/data_sources/temp_data_source.dart'; import 'package:front/core/project/data/models/project_request.dart'; import 'package:front/core/project/domain/entities/project.dart'; import 'package:front/core/project/domain/repositories/project_repository.dart'; -import 'package:front/core/utils/exception.dart'; import 'package:front/core/utils/failure.dart'; -class ProjectRepositoryImpl implements ProjectRepository { +class ProjectRepositoryImpl with ErrorHandler implements ProjectRepository { ProjectRepositoryImpl( {required this.projectRemoteDataSource, required this.projectTempDataSource}); @@ -20,77 +18,53 @@ class ProjectRepositoryImpl implements ProjectRepository { @override Future>> getProjectsByTeamId(int teamId) async { - try { - var result = await projectRemoteDataSource.getProjectsByTeamId(teamId); - for (var element in result) { - projects.addAll({element.projectId: element.toEntity()}); - } - return Right(result.map((e) => e.toEntity()).toList()); - } on ServerException { - return const Left(ServerFailure('An error has occurred')); - } on SocketException { - return const Left(ServerFailure('Failed to connect to the network')); - } on UnimplementedError { - var result = await projectTempDataSource.getProjectsByTeamId(teamId); - return Right(result.map((e) => e.toEntity()).toList()); - } + return catchError>( + () async { + var result = await projectRemoteDataSource.getProjectsByTeamId(teamId); + for (var element in result) { + projects.addAll({element.projectId: element.toEntity()}); + } + return result.map((e) => e.toEntity()).toList(); + }, + ); } @override Future> getProjectById(int projectId) async { - try { + return catchError(() async { var result = await projectRemoteDataSource.getProjectById(projectId); projects.addAll({projectId: result.toEntity()}); - return Right(projects[projectId]!); - } on ServerException { - return const Left(ServerFailure('An error has occurred')); - } on SocketException { - return const Left(ServerFailure('Failed to connect to the network')); - } + return projects[projectId]!; + }); } @override Future> deleteProjectById(int projectId) async { - try { + return catchError(() async { await projectRemoteDataSource.deleteProjectById(projectId); projects.remove(projectId); - return const Right(null); - } on ServerException { - return const Left(ServerFailure('An error has occurred')); - } on SocketException { - return const Left(ServerFailure('Failed to connect to the network')); - } + return; + }); } @override Future> updateProjectById( ProjectRequestModel project, int projectId) async { - try { + return catchError(() async { var result = await projectRemoteDataSource.updateProjectById(project, projectId); projects.addAll({result.projectId: result.toEntity()}); - return Right(result.toEntity()); - } on ServerException { - return const Left(ServerFailure('An error has occurred')); - } on SocketException { - return const Left(ServerFailure('Failed to connect to the network')); - } + return result.toEntity(); + }); } @override Future> createProject( ProjectRequestModel project, int teamId) async { - try { + return catchError(() async { var result = await projectRemoteDataSource.createProject(project, teamId); projects.addAll({result.projectId: result.toEntity()}); - return Right(result.toEntity()); - } on ServerException { - return const Left(ServerFailure('An error has occurred')); - } on SocketException { - return const Left(ServerFailure('Failed to connect to the network')); - } on UnimplementedError { - var result = await projectTempDataSource.createProject(project); - return Right(result.toEntity()); - } + return result.toEntity(); + }); } } diff --git a/front/lib/core/utils/failure.dart b/front/lib/core/utils/failure.dart index 6dc2dd2..8ba0c43 100644 --- a/front/lib/core/utils/failure.dart +++ b/front/lib/core/utils/failure.dart @@ -1,9 +1,13 @@ import 'package:equatable/equatable.dart'; -abstract class Failure extends Equatable { +class Failure extends Equatable { const Failure(this.message); final String message; + Failure copyWith({String? message}) { + return Failure(message ?? this.message); + } + @override List get props => [message]; } @@ -18,4 +22,56 @@ class ConnectionFailure extends Failure { class DatabaseFailure extends Failure { const DatabaseFailure(String message) : super(message); -} \ No newline at end of file +} + +enum ResponseFailure { + badRequest( + code: 400, message: 'badRequest', failure: ServerFailure('badRequest')), + forbidden( + code: 403, message: 'forbidden', failure: ServerFailure('forbidden')), + unauthorized( + code: 401, + message: 'unauthorized', + failure: ServerFailure('unauthorized')), + notFound(code: 404, message: 'notFound', failure: ServerFailure('notFound')), + + cancel(code: 701, message: 'cancel', failure: ServerFailure('cancel')), + connectionError( + code: 702, + message: 'connectionError', + failure: ServerFailure('connectionError')), + connectionTimeout( + code: 703, + message: 'connectionTimeout', + failure: ServerFailure('connectionTimeout')), + receiveTimeout( + code: 704, + message: 'receiveTimeout', + failure: ServerFailure('receiveTimeout')), + sendTimeout( + code: 705, message: 'sendTimeout', failure: ServerFailure('sendTimeout')), + unknown(code: 706, message: 'unknown', failure: ServerFailure('unknown')); + + factory ResponseFailure.getByCode(int code) { + return ResponseFailure.values.firstWhere((value) => value.code == code, + orElse: () => ResponseFailure.unknown); + } + + const ResponseFailure( + {required this.code, required this.message, required this.failure}); + + final int code; + final String message; + final Failure failure; + Failure getFailure() { + return ServerFailure(message); + } +} + +enum ResponseSuccess { + success(200), + created(201); + + const ResponseSuccess(this.code); + final int code; +} diff --git a/front/macos/Flutter/GeneratedPluginRegistrant.swift b/front/macos/Flutter/GeneratedPluginRegistrant.swift index cccf817..724bb2a 100644 --- a/front/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/front/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/front/test/core/project/data/data_source/remote_data_source_test.dart b/front/test/core/project/data/data_source/remote_data_source_test.dart index 69055ba..dd7b1bf 100644 --- a/front/test/core/project/data/data_source/remote_data_source_test.dart +++ b/front/test/core/project/data/data_source/remote_data_source_test.dart @@ -4,7 +4,6 @@ import 'package:front/core/config/providers/dio.dart'; import 'package:front/core/project/data/data_sources/remote_data_source.dart'; import 'package:front/core/project/data/models/project.dart'; import 'package:front/core/project/data/models/project_request.dart'; -import 'package:front/core/utils/exception.dart'; import 'package:http_mock_adapter/http_mock_adapter.dart'; import '../../../../helpers/dummy_data/project/api_response.dart'; import '../../../../helpers/provider_container.dart'; @@ -63,7 +62,7 @@ void main() { '/v1/projects/$teamId', (server) => server.reply(500, null)); expect(projectRemoteDataSource.getProjectsByTeamId(teamId), - throwsA(isA())); + throwsA(isA())); }); }); @@ -86,7 +85,7 @@ void main() { '/v1/project/$projectId', (server) => server.reply(500, null)); expect(projectRemoteDataSource.getProjectsByTeamId(projectId), - throwsA(isA())); + throwsA(isA())); }); }); @@ -115,7 +114,7 @@ void main() { expect( projectRemoteDataSource.updateProjectById( projectRequestModel, projectId), - throwsA(isA())); + throwsA(isA())); }); }); @@ -139,7 +138,7 @@ void main() { '/v1/project/$projectId', (server) => server.reply(500, null)); expect(projectRemoteDataSource.deleteProjectById(projectId), - throwsA(isA())); + throwsA(isA())); }); }); }