Skip to content

Commit

Permalink
Merge pull request #70 from TaskWeaver/61-서버-api요청-시-에러-처리
Browse files Browse the repository at this point in the history
61 서버 api요청 시 에러 처리
  • Loading branch information
brownglasses authored Mar 24, 2024
2 parents 0337f06 + 203ba4f commit 91d256b
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 64 deletions.
46 changes: 44 additions & 2 deletions front/lib/core/config/custom_interceptor.dart
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -92,12 +97,49 @@ class CustomInterceptor extends Interceptor {
// } on DioException catch (e) {
// // 새로운 Access Token임에도 에러가 발생한다면, Refresh Token마저도 만료된 것임

// await ref.read(userMeNotifierProvider.notifier).logout();

// return handler.reject(e);
// }
// }

// return handler.reject(err);
// }
}

mixin ErrorHandler {
Future<Either<Failure, T>> catchError<T>(Future<T> 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')));
}
}
}
12 changes: 6 additions & 6 deletions front/lib/core/project/data/data_sources/remote_data_source.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource {
throw ServerException();
}
} on DioException {
throw ServerException();
rethrow;
}
}

Expand All @@ -46,7 +46,7 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource {
throw ServerException();
}
} on DioException {
throw ServerException();
rethrow;
}
}

Expand All @@ -62,7 +62,7 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource {
throw ServerException();
}
} on DioException {
throw ServerException();
rethrow;
}
}

Expand All @@ -77,7 +77,7 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource {
throw ServerException();
}
} on DioException {
throw ServerException();
rethrow;
}
}

Expand All @@ -93,8 +93,8 @@ class ProjectRemoteDataSourceImpl implements ProjectRemoteDataSource {
} else {
throw ServerException();
}
} on DioException {
throw ServerException();
} on DioException {
rethrow;
}
}
}
Original file line number Diff line number Diff line change
@@ -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});
Expand All @@ -20,77 +18,53 @@ class ProjectRepositoryImpl implements ProjectRepository {

@override
Future<Either<Failure, List<Project>>> 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<List<Project>>(
() 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<Either<Failure, Project>> 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<Either<Failure, void>> 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<Either<Failure, Project>> 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<Either<Failure, Project>> 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();
});
}
}
60 changes: 58 additions & 2 deletions front/lib/core/utils/failure.dart
Original file line number Diff line number Diff line change
@@ -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<Object> get props => [message];
}
Expand All @@ -18,4 +22,56 @@ class ConnectionFailure extends Failure {

class DatabaseFailure extends Failure {
const DatabaseFailure(String message) : super(message);
}
}

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;
}
2 changes: 2 additions & 0 deletions front/macos/Flutter/GeneratedPluginRegistrant.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
import FlutterMacOS
import Foundation

import shared_preferences_foundation

func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
}
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -63,7 +62,7 @@ void main() {
'/v1/projects/$teamId', (server) => server.reply(500, null));

expect(projectRemoteDataSource.getProjectsByTeamId(teamId),
throwsA(isA<ServerException>()));
throwsA(isA<DioException>()));
});
});

Expand All @@ -86,7 +85,7 @@ void main() {
'/v1/project/$projectId', (server) => server.reply(500, null));

expect(projectRemoteDataSource.getProjectsByTeamId(projectId),
throwsA(isA<ServerException>()));
throwsA(isA<DioException>()));
});
});

Expand Down Expand Up @@ -115,7 +114,7 @@ void main() {
expect(
projectRemoteDataSource.updateProjectById(
projectRequestModel, projectId),
throwsA(isA<ServerException>()));
throwsA(isA<DioException>()));
});
});

Expand All @@ -139,7 +138,7 @@ void main() {
'/v1/project/$projectId', (server) => server.reply(500, null));

expect(projectRemoteDataSource.deleteProjectById(projectId),
throwsA(isA<ServerException>()));
throwsA(isA<DioException>()));
});
});
}

0 comments on commit 91d256b

Please sign in to comment.