From b5e50485b923c04b4170184d7e81240fe1449126 Mon Sep 17 00:00:00 2001 From: hoogom88 Date: Thu, 6 Jun 2024 10:44:20 +0900 Subject: [PATCH 01/32] =?UTF-8?q?Feat:=20CustomException=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=EB=A5=BC=20=EC=9C=84=ED=95=9C=20ExceptionCode=20enum?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=9E=91=EC=84=B1=20=EB=B0=8F?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/exception/custom_business_exception.dart | 4 +++- lib/data/common/http/status_code_handler.dart | 5 +++-- lib/data/feed/data_source/remote_feed_data_source_impl.dart | 3 ++- lib/presentation/my_page/view_model/my_page_view_model.dart | 3 ++- .../user_page/view_model/user_page_view_model.dart | 3 ++- 5 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/core/exception/custom_business_exception.dart b/lib/core/exception/custom_business_exception.dart index 6a233266..6f18ed98 100644 --- a/lib/core/exception/custom_business_exception.dart +++ b/lib/core/exception/custom_business_exception.dart @@ -1,6 +1,8 @@ +import 'package:weaco/core/enum/exception_code.dart'; + /// 어플리케이션 비지니스상 의도한 예외를 정의한 클래스 class CustomBusinessException implements Exception { - final int code; + final ExceptionCode code; final String message; CustomBusinessException({required this.code, required this.message}); diff --git a/lib/data/common/http/status_code_handler.dart b/lib/data/common/http/status_code_handler.dart index 4e569403..35e65f75 100644 --- a/lib/data/common/http/status_code_handler.dart +++ b/lib/data/common/http/status_code_handler.dart @@ -1,3 +1,4 @@ +import 'package:weaco/core/enum/exception_code.dart'; import 'package:weaco/core/exception/custom_business_exception.dart'; import 'package:weaco/core/exception/internal_server_exception.dart'; @@ -7,8 +8,8 @@ import '../../../core/exception/not_found_exception.dart'; CustomBusinessException statusCodeHandler({int? code, String? message}) { return switch (code) { 404 => NotFoundException( - code: code!, message: message ?? 'Data Not Found Error'), + code: ExceptionCode.unknownException, message: message ?? 'Data Not Found Error'), _ => InternalServerException( - code: code ?? 500, message: message ?? 'Internal Server Error'), + code: ExceptionCode.internalServerException, message: message ?? 'Internal Server Error'), }; } diff --git a/lib/data/feed/data_source/remote_feed_data_source_impl.dart b/lib/data/feed/data_source/remote_feed_data_source_impl.dart index c2b8d9a9..76dbd985 100644 --- a/lib/data/feed/data_source/remote_feed_data_source_impl.dart +++ b/lib/data/feed/data_source/remote_feed_data_source_impl.dart @@ -1,4 +1,5 @@ import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:weaco/core/enum/exception_code.dart'; import 'package:weaco/core/exception/not_found_exception.dart'; import 'package:weaco/core/firebase/firestore_dto_mapper.dart'; import 'package:weaco/data/feed/data_source/remote_feed_data_source.dart'; @@ -41,7 +42,7 @@ class RemoteFeedDataSourceImpl implements RemoteFeedDataSource { if (docSnapshot.data() == null) { throw NotFoundException( - code: 500, + code: ExceptionCode.notFoundException, message: '피드가 존재하지 않습니다.', ); } diff --git a/lib/presentation/my_page/view_model/my_page_view_model.dart b/lib/presentation/my_page/view_model/my_page_view_model.dart index 09425195..0741d708 100644 --- a/lib/presentation/my_page/view_model/my_page_view_model.dart +++ b/lib/presentation/my_page/view_model/my_page_view_model.dart @@ -1,6 +1,7 @@ import 'dart:developer'; import 'package:flutter/widgets.dart'; +import 'package:weaco/core/enum/exception_code.dart'; import 'package:weaco/core/exception/not_found_exception.dart'; import 'package:weaco/domain/feed/model/feed.dart'; import 'package:weaco/domain/feed/use_case/get_my_page_feeds_use_case.dart'; @@ -83,7 +84,7 @@ class MyPageViewModel with ChangeNotifier { (result) { if (result == null) { throw NotFoundException( - code: 404, + code: ExceptionCode.notFoundException, message: '이메일과 일치하는 사용자가 없습니다.', ); } diff --git a/lib/presentation/user_page/view_model/user_page_view_model.dart b/lib/presentation/user_page/view_model/user_page_view_model.dart index baa1e0d9..ff5ea7ae 100644 --- a/lib/presentation/user_page/view_model/user_page_view_model.dart +++ b/lib/presentation/user_page/view_model/user_page_view_model.dart @@ -1,6 +1,7 @@ import 'dart:developer'; import 'package:flutter/widgets.dart'; +import 'package:weaco/core/enum/exception_code.dart'; import 'package:weaco/core/exception/not_found_exception.dart'; import 'package:weaco/domain/feed/model/feed.dart'; import 'package:weaco/domain/feed/use_case/get_user_page_feeds_use_case.dart'; @@ -87,7 +88,7 @@ class UserPageViewModel with ChangeNotifier { if (result == null) { throw NotFoundException( - code: 404, + code: ExceptionCode.notFoundException, message: '이메일과 일치하는 사용자가 없습니다.', ); } From a7483cbc4a237cc34313a9df6ad2899651100f38 Mon Sep 17 00:00:00 2001 From: hoogom88 Date: Thu, 6 Jun 2024 10:54:59 +0900 Subject: [PATCH 02/32] =?UTF-8?q?Feat:=20ExceptionCode=20enum=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/enum/exception_code.dart | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 lib/core/enum/exception_code.dart diff --git a/lib/core/enum/exception_code.dart b/lib/core/enum/exception_code.dart new file mode 100644 index 00000000..b231be1f --- /dev/null +++ b/lib/core/enum/exception_code.dart @@ -0,0 +1,5 @@ +enum ExceptionCode{ + unknownException, + internalServerException, + notFoundException +} \ No newline at end of file From a01503e41455d656fca45964034cf03238fc763a Mon Sep 17 00:00:00 2001 From: hoogom88 Date: Thu, 6 Jun 2024 13:13:59 +0900 Subject: [PATCH 03/32] =?UTF-8?q?Feat:=20ExceptionCode=20=EC=9C=84?= =?UTF-8?q?=EC=B9=98=EA=B4=80=EB=A0=A8=20=EC=98=A4=EB=A5=98=20=EC=BD=94?= =?UTF-8?q?=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/enum/exception_code.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/enum/exception_code.dart b/lib/core/enum/exception_code.dart index b231be1f..80d008c0 100644 --- a/lib/core/enum/exception_code.dart +++ b/lib/core/enum/exception_code.dart @@ -1,5 +1,6 @@ enum ExceptionCode{ unknownException, internalServerException, - notFoundException + notFoundException, + locationException, } \ No newline at end of file From c2796202c076056a6f7e262540efc356f33b6895 Mon Sep 17 00:00:00 2001 From: hoogom88 Date: Thu, 6 Jun 2024 13:14:26 +0900 Subject: [PATCH 04/32] =?UTF-8?q?Feat:=20GPS=20=EC=9A=94=EC=B2=AD=20?= =?UTF-8?q?=EC=8B=A4=ED=8C=A8=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/exception/location_exception.dart | 6 ++++++ lib/core/gps/gps_helper.dart | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) create mode 100644 lib/core/exception/location_exception.dart diff --git a/lib/core/exception/location_exception.dart b/lib/core/exception/location_exception.dart new file mode 100644 index 00000000..6c6aa85a --- /dev/null +++ b/lib/core/exception/location_exception.dart @@ -0,0 +1,6 @@ +import 'package:weaco/core/exception/custom_business_exception.dart'; + +/// GPS 정보에 접근할 수 없음 예외 +class LocationException extends CustomBusinessException { + LocationException({required super.code, required super.message}); +} \ No newline at end of file diff --git a/lib/core/gps/gps_helper.dart b/lib/core/gps/gps_helper.dart index badc92b8..896e6992 100644 --- a/lib/core/gps/gps_helper.dart +++ b/lib/core/gps/gps_helper.dart @@ -1,5 +1,7 @@ import 'dart:developer'; import 'package:geolocator/geolocator.dart'; +import 'package:weaco/core/enum/exception_code.dart'; +import 'package:weaco/core/exception/location_exception.dart'; import 'package:weaco/core/gps/gps_permission_status.dart'; import 'package:weaco/core/gps/gps_position.dart'; @@ -43,7 +45,6 @@ class GpsHelper { Position position = await Geolocator.getCurrentPosition(); return GpsPosition(lat: position.latitude, lng: position.longitude); } - // Todo: 커스텀 Exception으로 리팩토링 필요 - throw Exception('권한 없음'); + throw LocationException(code: ExceptionCode.locationException, message: '위치 정보를 가져올 수 없습니다.'); } } From e8b5b759ea4bce36d7fa9dcb4a060ce55256e8ff Mon Sep 17 00:00:00 2001 From: hoogom88 Date: Thu, 6 Jun 2024 15:05:16 +0900 Subject: [PATCH 05/32] =?UTF-8?q?Feat:=20NetworkException=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/exception/network_exception.dart | 29 +++-------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/lib/core/exception/network_exception.dart b/lib/core/exception/network_exception.dart index ec30daff..3a5869d2 100644 --- a/lib/core/exception/network_exception.dart +++ b/lib/core/exception/network_exception.dart @@ -1,28 +1,5 @@ -sealed class NetworkException implements Exception { - final String code; - final String message; +import 'package:weaco/core/exception/custom_business_exception.dart'; - NetworkException({required this.code, required this.message}); - - factory NetworkException.noData( - {required String code, required String message}) = NoDataException; - - factory NetworkException.errorCode( - {required String code, - required String message}) = ErrorResponseCodeException; - - factory NetworkException.unknown( - {required String code, required String message}) = UnknownException; -} - -class NoDataException extends NetworkException { - NoDataException({required super.code, required super.message}); -} - -class ErrorResponseCodeException extends NetworkException { - ErrorResponseCodeException({required super.code, required super.message}); -} - -class UnknownException extends NetworkException { - UnknownException({required super.code, required super.message}); +class NetworkException extends CustomBusinessException { + NetworkException({required super.code, required super.message}); } From 851601f56fed8d827521a0075c71f52f40f7dbbf Mon Sep 17 00:00:00 2001 From: hoogom88 Date: Thu, 6 Jun 2024 15:35:30 +0900 Subject: [PATCH 06/32] =?UTF-8?q?Feat:=20Kakao=20API=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/enum/exception_code.dart | 1 + .../kakao_reverse_geo_coder_api.dart | 27 ++++++++++--------- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/lib/core/enum/exception_code.dart b/lib/core/enum/exception_code.dart index 80d008c0..378b1610 100644 --- a/lib/core/enum/exception_code.dart +++ b/lib/core/enum/exception_code.dart @@ -3,4 +3,5 @@ enum ExceptionCode{ internalServerException, notFoundException, locationException, + networkException, } \ No newline at end of file diff --git a/lib/data/location/data_source/remote_data_source/kakao_reverse_geo_coder_api.dart b/lib/data/location/data_source/remote_data_source/kakao_reverse_geo_coder_api.dart index 7756b034..5415af00 100644 --- a/lib/data/location/data_source/remote_data_source/kakao_reverse_geo_coder_api.dart +++ b/lib/data/location/data_source/remote_data_source/kakao_reverse_geo_coder_api.dart @@ -1,8 +1,11 @@ -import 'dart:developer'; +import 'package:dio/dio.dart'; import 'package:weaco/core/config/kakao_config.dart'; import 'package:weaco/core/dio/base_dio.dart'; import 'package:weaco/core/dio/base_response.dart'; +import 'package:weaco/core/enum/exception_code.dart'; +import 'package:weaco/core/exception/internal_server_exception.dart'; import 'package:weaco/core/exception/network_exception.dart'; +import 'package:weaco/core/exception/not_found_exception.dart'; import 'package:weaco/data/location/data_source/remote_data_source/remote_location_data_source.dart'; class KakaoReverseGeoCoderApi implements RemoteLocationDataSource { @@ -28,23 +31,21 @@ class KakaoReverseGeoCoderApi implements RemoteLocationDataSource { receiveTimeout: const Duration(seconds: 2)); if (result.statusCode == 200) { if (result.body['meta']['total_count'] == 0) { - throw NetworkException.noData( - code: 'EmptyData', message: '응답 데이터 없음'); + throw NotFoundException( + code: ExceptionCode.notFoundException, message: '데이터 조회 결과 없음'); } else { return '${result.body['documents'][0]['region_1depth_name']} ${result.body['documents'][0]['region_2depth_name']}'; } } - throw NetworkException.errorCode( - code: result.statusCode.toString(), message: '네트워크 응답 에러'); + throw InternalServerException( + code: ExceptionCode.internalServerException, + message: 'Kakao Api 처리 오류'); } catch (e) { - log(e.toString(), name: 'ReverseGeoCoderHelper.getDong()'); - switch (e) { - case NoDataException _ || ErrorResponseCodeException _: - rethrow; - default: - throw NetworkException.unknown( - code: 'Unknown', message: e.toString()); - } + throw switch (e) { + DioException _ => NetworkException( + code: ExceptionCode.networkException, message: '네트워크 요청 오류: $e'), + _ => e, + }; } } } From 4708466dbe53e347f62eb2ac500868b4c27619df Mon Sep 17 00:00:00 2001 From: hoogom88 Date: Thu, 6 Jun 2024 16:59:50 +0900 Subject: [PATCH 07/32] =?UTF-8?q?Feat:=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EC=98=88?= =?UTF-8?q?=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/enum/exception_code.dart | 1 + .../exception/authentication_exception.dart | 6 +++ lib/core/exception/location_exception.dart | 2 +- .../remote/remote_file_data_source_impl.dart | 54 +++++++++++++------ 4 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 lib/core/exception/authentication_exception.dart diff --git a/lib/core/enum/exception_code.dart b/lib/core/enum/exception_code.dart index 378b1610..a2a19ffa 100644 --- a/lib/core/enum/exception_code.dart +++ b/lib/core/enum/exception_code.dart @@ -3,5 +3,6 @@ enum ExceptionCode{ internalServerException, notFoundException, locationException, + authenticationNotExistException, networkException, } \ No newline at end of file diff --git a/lib/core/exception/authentication_exception.dart b/lib/core/exception/authentication_exception.dart new file mode 100644 index 00000000..a23fd0e1 --- /dev/null +++ b/lib/core/exception/authentication_exception.dart @@ -0,0 +1,6 @@ +import 'package:weaco/core/exception/custom_business_exception.dart'; + +class AuthenticationException extends CustomBusinessException { + AuthenticationException( + {required super.code, required super.message}); +} diff --git a/lib/core/exception/location_exception.dart b/lib/core/exception/location_exception.dart index 6c6aa85a..32635949 100644 --- a/lib/core/exception/location_exception.dart +++ b/lib/core/exception/location_exception.dart @@ -3,4 +3,4 @@ import 'package:weaco/core/exception/custom_business_exception.dart'; /// GPS 정보에 접근할 수 없음 예외 class LocationException extends CustomBusinessException { LocationException({required super.code, required super.message}); -} \ No newline at end of file +} diff --git a/lib/data/file/data_source/remote/remote_file_data_source_impl.dart b/lib/data/file/data_source/remote/remote_file_data_source_impl.dart index a1ad027f..cb382609 100644 --- a/lib/data/file/data_source/remote/remote_file_data_source_impl.dart +++ b/lib/data/file/data_source/remote/remote_file_data_source_impl.dart @@ -1,6 +1,10 @@ import 'dart:io'; - +import 'package:dio/dio.dart'; import 'package:firebase_storage/firebase_storage.dart'; +import 'package:weaco/core/enum/exception_code.dart'; +import 'package:weaco/core/exception/authentication_exception.dart'; +import 'package:weaco/core/exception/internal_server_exception.dart'; +import 'package:weaco/core/exception/network_exception.dart'; import 'package:weaco/core/firebase/firebase_auth_service.dart'; import 'package:weaco/data/file/data_source/remote/remote_file_data_source.dart'; @@ -9,25 +13,41 @@ class RemoteFileDataSourceImpl implements RemoteFileDataSource { final FirebaseAuthService _firebaseAuthService; RemoteFileDataSourceImpl( - {required FirebaseStorage firebaseStorage, required FirebaseAuthService firebaseAuthService}) + {required FirebaseStorage firebaseStorage, + required FirebaseAuthService firebaseAuthService}) : _firebaseStorage = firebaseStorage, _firebaseAuthService = firebaseAuthService; @override - Future> saveImage({required File croppedImage, required File compressedImage}) async { - final String? email = _firebaseAuthService.firebaseAuth.currentUser?.email; - if (email == null) throw Exception(); - final feedOriginImageRef = _firebaseStorage.ref().child( - 'feed_origin_images/${email}_${DateTime - .now() - .microsecondsSinceEpoch}.png'); - await feedOriginImageRef.putFile(croppedImage); - final feedThumbnailImageRef = _firebaseStorage.ref().child( - 'feed_thumbnail_images/${email}_${DateTime - .now() - .microsecondsSinceEpoch}.png'); - await feedThumbnailImageRef.putFile(compressedImage); - - return [await feedOriginImageRef.getDownloadURL(), await feedThumbnailImageRef.getDownloadURL()]; + Future> saveImage( + {required File croppedImage, required File compressedImage}) async { + try { + final String? email = + _firebaseAuthService.firebaseAuth.currentUser?.email; + if (email == null) { + throw AuthenticationException( + code: ExceptionCode.authenticationNotExistException, + message: 'FirebaseAuth 로그인 정보 없음'); + } + final feedOriginImageRef = _firebaseStorage.ref().child( + 'feed_origin_images/${email}_${DateTime.now().microsecondsSinceEpoch}.png'); + await feedOriginImageRef.putFile(croppedImage); + final feedThumbnailImageRef = _firebaseStorage.ref().child( + 'feed_thumbnail_images/${email}_${DateTime.now().microsecondsSinceEpoch}.png'); + await feedThumbnailImageRef.putFile(compressedImage); + return [ + await feedOriginImageRef.getDownloadURL(), + await feedThumbnailImageRef.getDownloadURL() + ]; + } catch (e) { + throw switch (e) { + FirebaseException _ => InternalServerException( + code: ExceptionCode.internalServerException, + message: 'Firebase Storage 저장 실패'), + DioException _ => NetworkException( + code: ExceptionCode.networkException, message: '네트워크 요청 오류: $e'), + _ => e, + }; + } } } From d728ba61c988e473920a2e8e45d5f7f4a39b5389 Mon Sep 17 00:00:00 2001 From: "nala0897@naver.com" Date: Thu, 6 Jun 2024 17:09:10 +0900 Subject: [PATCH 08/32] =?UTF-8?q?Feat:=20LocalDailyLocationWeatherDataSour?= =?UTF-8?q?ceImpl.getLocalDailyLocationWeather()=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...l_daily_location_weather_data_source_impl.dart | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/data/weather/data_source/local_data_source/local_daily_location_weather_data_source_impl.dart b/lib/data/weather/data_source/local_data_source/local_daily_location_weather_data_source_impl.dart index 32b92204..a6be3a45 100644 --- a/lib/data/weather/data_source/local_data_source/local_daily_location_weather_data_source_impl.dart +++ b/lib/data/weather/data_source/local_data_source/local_daily_location_weather_data_source_impl.dart @@ -1,5 +1,7 @@ import 'dart:convert'; +import 'package:weaco/core/enum/exception_code.dart'; +import 'package:weaco/core/exception/not_found_exception.dart'; import 'package:weaco/core/hive/hive_wrapper.dart'; import 'package:weaco/data/weather/data_source/local_data_source/local_daily_location_weather_data_source.dart'; import 'package:weaco/domain/weather/model/daily_location_weather.dart'; @@ -26,9 +28,14 @@ class LocalDailyLocationWeatherDataSourceImpl /// 로컬의 DailyLocationWeather 데이터를 가져와서 반환한다. @override - Future getLocalDailyLocationWeather() async { - final data = await _hiveWrapper.readData(dailyLocationWeatherKey); - if (data == null) return null; - return DailyLocationWeather.fromJson(jsonDecode(data)); + Future getLocalDailyLocationWeather() async { + try { + final data = await _hiveWrapper.readData(dailyLocationWeatherKey); + + return DailyLocationWeather.fromJson(jsonDecode(data!)); + } catch (e) { + throw NotFoundException( + code: ExceptionCode.notFoundException, message: '데이터 없음'); + } } } From 8784f03d7ddb26b6a8e953dc6d9d50743e68e275 Mon Sep 17 00:00:00 2001 From: "nala0897@naver.com" Date: Thu, 6 Jun 2024 17:09:40 +0900 Subject: [PATCH 09/32] =?UTF-8?q?Feat:=20getDailyLocationWeather()=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=B6=84?= =?UTF-8?q?=EA=B8=B0=20=EC=B2=98=EB=A6=AC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/daily_location_weather_repository_impl.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/data/weather/repository/daily_location_weather_repository_impl.dart b/lib/data/weather/repository/daily_location_weather_repository_impl.dart index 253d35b5..746be497 100644 --- a/lib/data/weather/repository/daily_location_weather_repository_impl.dart +++ b/lib/data/weather/repository/daily_location_weather_repository_impl.dart @@ -1,5 +1,6 @@ import 'dart:developer'; +import 'package:weaco/core/exception/not_found_exception.dart'; import 'package:weaco/data/weather/data_source/remote_data_source/remote_weather_data_source.dart'; import 'package:weaco/data/weather/dto/weather_dto.dart'; import 'package:weaco/data/weather/mapper/daily_location_weather_mapper.dart'; @@ -33,6 +34,10 @@ class DailyLocationWeatherRepositoryImpl await _localDailyLocationWeatherDataSource .getLocalDailyLocationWeather(); + if (localData is NotFoundException) { + await _fetchAndCacheDailyLocationWeather(); + } + return _isOldDataOrExpired(localData) ? await _fetchAndCacheDailyLocationWeather() : localData!; From 8770f92f49b2d45b039129e0930209ec5ce90938 Mon Sep 17 00:00:00 2001 From: "nala0897@naver.com" Date: Thu, 6 Jun 2024 17:11:36 +0900 Subject: [PATCH 10/32] =?UTF-8?q?Feat:=20LocalFileDataSourceImpl=20?= =?UTF-8?q?=EC=97=90=20=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../local/local_file_data_source.dart | 6 +-- .../local/local_file_data_source_impl.dart | 54 ++++++++++++------- .../file/repository/file_repository_impl.dart | 6 +-- .../file/repository/file_repository.dart | 4 +- 4 files changed, 42 insertions(+), 28 deletions(-) diff --git a/lib/data/file/data_source/local/local_file_data_source.dart b/lib/data/file/data_source/local/local_file_data_source.dart index f58a75bf..8e19bcde 100644 --- a/lib/data/file/data_source/local/local_file_data_source.dart +++ b/lib/data/file/data_source/local/local_file_data_source.dart @@ -3,11 +3,11 @@ import 'dart:io'; import 'package:weaco/core/enum/image_type.dart'; abstract interface class LocalFileDataSource { - Future getImage({required ImageType imageType}); + Future getImage({required ImageType imageType}); - Future saveImage({required bool isOrigin, required File file}); + Future saveImage({required bool isOrigin, required File file}); - Future getCompressedImage(); + Future getCompressedImage(); Future saveCompressedImage({required List image}); diff --git a/lib/data/file/data_source/local/local_file_data_source_impl.dart b/lib/data/file/data_source/local/local_file_data_source_impl.dart index d5471963..bd26f4bf 100644 --- a/lib/data/file/data_source/local/local_file_data_source_impl.dart +++ b/lib/data/file/data_source/local/local_file_data_source_impl.dart @@ -1,6 +1,10 @@ -import 'dart:developer'; import 'dart:io'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:dio/dio.dart'; +import 'package:weaco/core/enum/exception_code.dart'; import 'package:weaco/core/enum/image_type.dart'; +import 'package:weaco/core/exception/internal_server_exception.dart'; +import 'package:weaco/core/exception/network_exception.dart'; import 'package:weaco/core/path_provider/path_provider_service.dart'; import 'local_file_data_source.dart'; @@ -14,25 +18,24 @@ class LocalFileDataSourceImpl implements LocalFileDataSource { : _pathProvider = pathProvider; @override - Future getImage({required ImageType imageType}) async { + Future getImage({required ImageType imageType}) async { try { final directory = await _pathProvider.getCacheDirectory(); - String fileName = switch(imageType) { - ImageType.origin => _originImageFileName , + String fileName = switch (imageType) { + ImageType.origin => _originImageFileName, ImageType.cropped => _croppedImageFileName, ImageType.compressed => _compressedImageFileName, }; - return (await File('$directory/$fileName').exists()) - ? File('$directory/$fileName') - : null; + await File('$directory/$fileName').exists(); + + return File('$directory/$fileName'); } catch (e) { - log(e.toString(), name: 'LocalFileDataSourceImpl.getImagePath()'); - return null; + throw _exceptionHandling(e); } } @override - Future saveImage({required bool isOrigin, required File file}) async { + Future saveImage({required bool isOrigin, required File file}) async { try { final directory = await _pathProvider.getCacheDirectory(); String fileName = isOrigin ? _originImageFileName : _croppedImageFileName; @@ -40,23 +43,20 @@ class LocalFileDataSourceImpl implements LocalFileDataSource { await File('$directory/$fileName').delete(); } await File('$directory/$fileName').writeAsBytes(await file.readAsBytes()); - return true; } catch (e) { - log(e.toString(), name: 'LocalFileDataSourceImpl.saveImage()'); - return false; + _exceptionHandling(e); } } @override - Future getCompressedImage() async { + Future getCompressedImage() async { try { final directory = await _pathProvider.getCacheDirectory(); - return (await File('$directory/$_compressedImageFileName').exists()) - ? File('$directory/$_compressedImageFileName') - : null; + await File('$directory/$_compressedImageFileName').exists(); + + return File('$directory/$_compressedImageFileName'); } catch (e) { - log(e.toString(), name: 'LocalFileDataSourceImpl.getCompressedImage()'); - return null; + throw _exceptionHandling(e); } } @@ -69,7 +69,21 @@ class LocalFileDataSourceImpl implements LocalFileDataSource { } await File('$directory/$_compressedImageFileName').writeAsBytes(image); } catch (e) { - log(e.toString(), name: 'LocalFileDataSourceImpl.saveCompressedImage()'); + _exceptionHandling(e); + } + } + + Exception _exceptionHandling(Object e) { + switch (e.runtimeType) { + case FirebaseException _: + return InternalServerException( + code: ExceptionCode.internalServerException, message: '서버 내부 오류'); + case DioException _: + return NetworkException( + code: ExceptionCode.internalServerException, + message: '네트워크 오류 : $e'); + default: + return e as Exception; } } } diff --git a/lib/data/file/repository/file_repository_impl.dart b/lib/data/file/repository/file_repository_impl.dart index 69e9c71c..a5b57e4d 100644 --- a/lib/data/file/repository/file_repository_impl.dart +++ b/lib/data/file/repository/file_repository_impl.dart @@ -15,14 +15,14 @@ class FileRepositoryImpl implements FileRepository { _remoteFileDataSource = remoteFileDataSource; @override - Future getImage({required ImageType imageType}) async { + Future getImage({required ImageType imageType}) async { return await _localFileDataSource.getImage(imageType: imageType); } @override - Future saveImage({required bool isOrigin, required File file, required List compressedImage}) async { + Future saveImage({required bool isOrigin, required File file, required List compressedImage}) async { await _localFileDataSource.saveCompressedImage(image: compressedImage); - return await _localFileDataSource.saveImage(isOrigin: isOrigin, file: file); + await _localFileDataSource.saveImage(isOrigin: isOrigin, file: file); } diff --git a/lib/domain/file/repository/file_repository.dart b/lib/domain/file/repository/file_repository.dart index 1e6537c2..d33f53cf 100644 --- a/lib/domain/file/repository/file_repository.dart +++ b/lib/domain/file/repository/file_repository.dart @@ -3,9 +3,9 @@ import 'dart:io'; import 'package:weaco/core/enum/image_type.dart'; abstract interface class FileRepository { - Future getImage({required ImageType imageType}); + Future getImage({required ImageType imageType}); - Future saveImage({required bool isOrigin, required File file, required List compressedImage}); + Future saveImage({required bool isOrigin, required File file, required List compressedImage}); Future> saveOotdImage(); } From 231283cb751a92703ede6fe767015f6d94138f70 Mon Sep 17 00:00:00 2001 From: "nala0897@naver.com" Date: Thu, 6 Jun 2024 17:14:46 +0900 Subject: [PATCH 11/32] =?UTF-8?q?Feat:=20ExceptionCode=20=EC=97=90=20notFo?= =?UTF-8?q?undException=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/enum/exception_code.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/enum/exception_code.dart b/lib/core/enum/exception_code.dart index b231be1f..a83482b4 100644 --- a/lib/core/enum/exception_code.dart +++ b/lib/core/enum/exception_code.dart @@ -1,5 +1,5 @@ enum ExceptionCode{ unknownException, internalServerException, - notFoundException + notFoundException, } \ No newline at end of file From 794679c827b1ee9df85bdcb8d66eeb1cf1d4e4af Mon Sep 17 00:00:00 2001 From: "nala0897@naver.com" Date: Thu, 6 Jun 2024 17:47:23 +0900 Subject: [PATCH 12/32] =?UTF-8?q?Feat:=20RemoteFeedDataSourceImpl=20?= =?UTF-8?q?=EC=BB=A4=EC=8A=A4=ED=85=80=20=EC=98=88=EC=99=B8=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data_source/remote_feed_data_source.dart | 4 +- .../remote_feed_data_source_impl.dart | 228 +++++++++++------- 2 files changed, 139 insertions(+), 93 deletions(-) diff --git a/lib/data/feed/data_source/remote_feed_data_source.dart b/lib/data/feed/data_source/remote_feed_data_source.dart index f6e8bf2b..d1f8c5be 100644 --- a/lib/data/feed/data_source/remote_feed_data_source.dart +++ b/lib/data/feed/data_source/remote_feed_data_source.dart @@ -5,7 +5,7 @@ abstract interface class RemoteFeedDataSource { /// OOTD 피드 작성 성공 시 : 피드 업로드 요청(Feed) -> / 업로드 완료(bool) ← 파베 /// OOTD 편집 완료 후 [상세 페이지]: 위와 동일. /// OOTD 편집 완료 후 [마이 페이지]: 위와 동일.*피드 업데이트 - Future saveFeed({required Feed feed}); + Future saveFeed({required Feed feed}); /// OOTD 피드 [상세 페이지] : 피드 데이터 요청 (id) -> 파베 / 피드 데이터 반환(json) ← 파베 Future getFeed({required String id}); @@ -18,7 +18,7 @@ abstract interface class RemoteFeedDataSource { }); /// [마이페이지] 피드 삭제: 피드 삭제 요청(id) -> 파베/ 삭제 완료 (bool) from FB - Future deleteFeed({required String id}); + Future deleteFeed({required String id}); /// [홈 화면] 하단 OOTD 추천 목록: /// diff --git a/lib/data/feed/data_source/remote_feed_data_source_impl.dart b/lib/data/feed/data_source/remote_feed_data_source_impl.dart index 76dbd985..c3fd0e2a 100644 --- a/lib/data/feed/data_source/remote_feed_data_source_impl.dart +++ b/lib/data/feed/data_source/remote_feed_data_source_impl.dart @@ -1,5 +1,8 @@ import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:dio/dio.dart'; import 'package:weaco/core/enum/exception_code.dart'; +import 'package:weaco/core/exception/internal_server_exception.dart'; +import 'package:weaco/core/exception/network_exception.dart'; import 'package:weaco/core/exception/not_found_exception.dart'; import 'package:weaco/core/firebase/firestore_dto_mapper.dart'; import 'package:weaco/data/feed/data_source/remote_feed_data_source.dart'; @@ -15,39 +18,44 @@ class RemoteFeedDataSourceImpl implements RemoteFeedDataSource { /// OOTD 피드 작성 또는 편집 후 저장 @override - Future saveFeed({required Feed feed}) async { + Future saveFeed({required Feed feed}) async { final feedDto = toFeedDto(feed: feed); - // 피드를 수정 할 경우 - if (feed.id != null) { - return await _fireStore - .collection('feeds') - .doc(feed.id) - .set(feedDto) - .then((value) => true); + try { + // 피드를 수정 할 경우 + if (feed.id != null) { + await _fireStore + .collection('feeds') + .doc(feed.id) + .set(feedDto) + .then((value) => true); + } + + // 새 피드를 저장 할 경우 + await _fireStore.collection('feeds').add(feedDto).then((value) => true); + } catch (e) { + _exceptionHandling(e); } - - // 새 피드를 저장 할 경우 - return await _fireStore - .collection('feeds') - .add(feedDto) - .then((value) => true); } /// [OOTD 피드 상세 페이지]: /// 피드 데이터 요청 (id) -> 파베 / 피드 데이터 반환(json) ← 파베 @override Future getFeed({required String id}) async { - final docSnapshot = await _fireStore.collection('feeds').doc(id).get(); - - if (docSnapshot.data() == null) { - throw NotFoundException( - code: ExceptionCode.notFoundException, - message: '피드가 존재하지 않습니다.', - ); + try { + final docSnapshot = await _fireStore.collection('feeds').doc(id).get(); + + if (docSnapshot.data() == null) { + throw NotFoundException( + code: ExceptionCode.notFoundException, + message: '피드가 존재하지 않습니다.', + ); + } + + return toFeed(json: docSnapshot.data()!, id: docSnapshot.id); + } catch (e) { + throw _exceptionHandling(e); } - - return toFeed(json: docSnapshot.data()!, id: docSnapshot.id); } /// [유저 페이지/마이 페이지]: @@ -58,30 +66,44 @@ class RemoteFeedDataSourceImpl implements RemoteFeedDataSource { DateTime? createdAt, required int limit, }) async { - final querySnapshot = await _fireStore - .collection('feeds') - .where('user_email', isEqualTo: email) - .where('deleted_at', isNull: true) - .where('created_at', - isLessThan: createdAt ?? Timestamp.fromDate(DateTime.now())) - .orderBy('created_at', descending: true) - .limit(limit) - .get(); - - return querySnapshot.docs - .map((doc) => toFeed(json: doc.data(), id: doc.id)) - .toList(); + try { + final querySnapshot = await _fireStore + .collection('feeds') + .where('user_email', isEqualTo: email) + .where('deleted_at', isNull: true) + .where('created_at', + isLessThan: createdAt ?? Timestamp.fromDate(DateTime.now())) + .orderBy('created_at', descending: true) + .limit(limit) + .get(); + + if (querySnapshot.docs.isEmpty) { + throw NotFoundException( + code: ExceptionCode.notFoundException, + message: '피드가 존재하지 않습니다.', + ); + } + + return querySnapshot.docs + .map((doc) => toFeed(json: doc.data(), id: doc.id)) + .toList(); + } catch (e) { + throw _exceptionHandling(e); + } } /// [마이페이지] 피드 삭제 /// soft delete 처리 @override - Future deleteFeed({required String id}) async { - await _fireStore - .collection('feeds') - .doc(id) - .update({'deleted_at': Timestamp.fromDate(DateTime.now())}); - return true; + Future deleteFeed({required String id}) async { + try { + await _fireStore + .collection('feeds') + .doc(id) + .update({'deleted_at': Timestamp.fromDate(DateTime.now())}); + } catch (e) { + _exceptionHandling(e); + } } /// [홈 페이지] 하단 OOTD 추천: @@ -93,25 +115,30 @@ class RemoteFeedDataSourceImpl implements RemoteFeedDataSource { DateTime? createdAt}) async { final index = DateTime.now().hour; final weather = dailyLocationWeather.weatherList[index]; - final querySnapshot = await _fireStore - .collection('feeds') - .where('weather.code', isEqualTo: weather.code) - // .where('season_code', isEqualTo: dailyLocationWeather.seasonCode) - .where( - 'weather.temperature', - isLessThanOrEqualTo: dailyLocationWeather.highTemperature, - isGreaterThanOrEqualTo: dailyLocationWeather.lowTemperature, - ) - .where('created_at', - isLessThan: createdAt ?? Timestamp.fromDate(DateTime.now())) - .where('deleted_at', isNull: true) - .orderBy('created_at', descending: true) - .limit(10) - .get(); - - return querySnapshot.docs - .map((doc) => toFeed(json: doc.data(), id: doc.id)) - .toList(); + + try { + final querySnapshot = await _fireStore + .collection('feeds') + .where('weather.code', isEqualTo: weather.code) + // .where('season_code', isEqualTo: dailyLocationWeather.seasonCode) + .where( + 'weather.temperature', + isLessThanOrEqualTo: dailyLocationWeather.highTemperature, + isGreaterThanOrEqualTo: dailyLocationWeather.lowTemperature, + ) + .where('created_at', + isLessThan: createdAt ?? Timestamp.fromDate(DateTime.now())) + .where('deleted_at', isNull: true) + .orderBy('created_at', descending: true) + .limit(10) + .get(); + + return querySnapshot.docs + .map((doc) => toFeed(json: doc.data(), id: doc.id)) + .toList(); + } catch (e) { + throw _exceptionHandling(e); + } } /// [검색 페이지] 피드 검색: @@ -127,38 +154,57 @@ class RemoteFeedDataSourceImpl implements RemoteFeedDataSource { int? minTemperature, int? maxTemperature, }) async { - Query> query = _fireStore.collection('feeds'); - // 날씨 코드 필터링 - if (weatherCode != null) { - query = query.where('weather.code', isEqualTo: weatherCode); - } - - // 온도 범위 필터링 - if (minTemperature != null && maxTemperature != null) { - query = query - .where('weather.temperature', isLessThanOrEqualTo: maxTemperature) - .where('weather.temperature', isGreaterThanOrEqualTo: minTemperature); + try { + Query> query = _fireStore.collection('feeds'); + // 날씨 코드 필터링 + if (weatherCode != null) { + query = query.where('weather.code', isEqualTo: weatherCode); + } + + // 온도 범위 필터링 + if (minTemperature != null && maxTemperature != null) { + query = query + .where('weather.temperature', isLessThanOrEqualTo: maxTemperature) + .where('weather.temperature', + isGreaterThanOrEqualTo: minTemperature); + } + + // 계절 코드 필터링 + if (seasonCode != null) { + query = query.where('season_code', isEqualTo: seasonCode); + } + + // 생성일 기준으로 정렬하여 제한된 수의 문서 가져오기 + final QuerySnapshot> querySnapshot = await query + .where('created_at', + isLessThan: createdAt ?? Timestamp.fromDate(DateTime.now())) + .where('deleted_at', isNull: true) + .orderBy( + 'created_at', + descending: true, + ) + .limit(limit) + .get(); + + return querySnapshot.docs + .map((doc) => toFeed(json: doc.data(), id: doc.id)) + .toList(); + } catch (e) { + throw _exceptionHandling(e); } + } - // 계절 코드 필터링 - if (seasonCode != null) { - query = query.where('season_code', isEqualTo: seasonCode); + Exception _exceptionHandling(Object e) { + switch (e.runtimeType) { + case FirebaseException _: + return InternalServerException( + code: ExceptionCode.internalServerException, message: '서버 내부 오류'); + case DioException _: + return NetworkException( + code: ExceptionCode.internalServerException, + message: '네트워크 오류 : $e'); + default: + return e as Exception; } - - // 생성일 기준으로 정렬하여 제한된 수의 문서 가져오기 - final QuerySnapshot> querySnapshot = await query - .where('created_at', - isLessThan: createdAt ?? Timestamp.fromDate(DateTime.now())) - .where('deleted_at', isNull: true) - .orderBy( - 'created_at', - descending: true, - ) - .limit(limit) - .get(); - - return querySnapshot.docs - .map((doc) => toFeed(json: doc.data(), id: doc.id)) - .toList(); } } From 1eaf05e8661c5eb1d06a9873ec2adb868299dfa0 Mon Sep 17 00:00:00 2001 From: "nala0897@naver.com" Date: Thu, 6 Jun 2024 17:49:03 +0900 Subject: [PATCH 13/32] =?UTF-8?q?Feat:=20=EC=BB=A4=EC=8A=A4=ED=85=80=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=A0=81=EC=9A=A9?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD=EB=90=9C=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../feed/repository/feed_repository_impl.dart | 10 ++++---- .../repository/ootd_feed_repository_impl.dart | 25 +++++++------------ .../feed/repository/feed_repository.dart | 6 ++--- .../feed/repository/ootd_feed_repository.dart | 4 +-- .../remove_my_page_feed_use_case.dart | 4 +-- .../use_case/save_edit_feed_use_case.dart | 4 +-- .../file/use_case/save_image_use_case.dart | 4 +-- 7 files changed, 25 insertions(+), 32 deletions(-) diff --git a/lib/data/feed/repository/feed_repository_impl.dart b/lib/data/feed/repository/feed_repository_impl.dart index 0c7b0f9a..9d41f4a2 100644 --- a/lib/data/feed/repository/feed_repository_impl.dart +++ b/lib/data/feed/repository/feed_repository_impl.dart @@ -11,13 +11,13 @@ class FeedRepositoryImpl implements FeedRepository { }); @override - Future saveFeed({required Feed editedFeed}) { - return remoteFeedDataSource.saveFeed(feed: editedFeed); + Future saveFeed({required Feed editedFeed}) async { + remoteFeedDataSource.saveFeed(feed: editedFeed); } @override - Future deleteFeed({required String id}) async { - return await remoteFeedDataSource.deleteFeed(id: id); + Future deleteFeed({required String id}) async { + await remoteFeedDataSource.deleteFeed(id: id); } /// 피드의 id 값을 전달 하여 Firebase 내의 해당 피드를 가져 와야 한다. @@ -25,7 +25,7 @@ class FeedRepositoryImpl implements FeedRepository { /// @param id: 피드 id /// @return Feed: 피드 @override - Future getFeed({required String id}) async { + Future getFeed({required String id}) async { return await remoteFeedDataSource.getFeed(id: id); } diff --git a/lib/data/feed/repository/ootd_feed_repository_impl.dart b/lib/data/feed/repository/ootd_feed_repository_impl.dart index d1403482..1d389fb2 100644 --- a/lib/data/feed/repository/ootd_feed_repository_impl.dart +++ b/lib/data/feed/repository/ootd_feed_repository_impl.dart @@ -19,39 +19,32 @@ class OotdFeedRepositoryImpl implements OotdFeedRepository { /// 피드 저장 및 수정 @override - Future saveOotdFeed({required Feed feed}) async { - return feed.id == null - ? await _save(feed: feed) - : await _update(feed: feed); + Future saveOotdFeed({required Feed feed}) async { + feed.id == null ? await _save(feed: feed) : await _update(feed: feed); } /// 피드 저장 - Future _save({required Feed feed}) async { + Future _save({required Feed feed}) async { final List path = await _fileRepository.saveOotdImage(); - final saveResult = await _feedRepository.saveFeed( - editedFeed: feed.copyWith(imagePath: path[0], thumbnailImagePath: path[1])); + await _feedRepository.saveFeed( + editedFeed: + feed.copyWith(imagePath: path[0], thumbnailImagePath: path[1])); await _updateMyFeedCount(1); - - return saveResult; } /// 피드 수정 - Future _update({required Feed feed}) async { - final updateResult = await _feedRepository.saveFeed(editedFeed: feed); - - return updateResult; + Future _update({required Feed feed}) async { + await _feedRepository.saveFeed(editedFeed: feed); } /// 피드 삭제 @override - Future removeOotdFeed({required String id}) async { + Future removeOotdFeed({required String id}) async { await _feedRepository.deleteFeed(id: id); await _updateMyFeedCount(-1); - - return true; } /// 유저 피드 카운트 업데이트 diff --git a/lib/domain/feed/repository/feed_repository.dart b/lib/domain/feed/repository/feed_repository.dart index 9181701f..9a541e9b 100644 --- a/lib/domain/feed/repository/feed_repository.dart +++ b/lib/domain/feed/repository/feed_repository.dart @@ -11,13 +11,13 @@ abstract interface class FeedRepository { }); /// 피드의 상세 정보를 가져옵니다. - Future getFeed({required String id}); + Future getFeed({required String id}); /// 피드를 삭제합니다. - Future deleteFeed({required String id}); + Future deleteFeed({required String id}); /// 새 피드를 저장하거나 편집된 피드를 업데이트 합니다. - Future saveFeed({required Feed editedFeed}); + Future saveFeed({required Feed editedFeed}); /// [홈 하단] /// 추천 OOTD 목록을 불러옵니다. diff --git a/lib/domain/feed/repository/ootd_feed_repository.dart b/lib/domain/feed/repository/ootd_feed_repository.dart index bec94879..5024b3da 100644 --- a/lib/domain/feed/repository/ootd_feed_repository.dart +++ b/lib/domain/feed/repository/ootd_feed_repository.dart @@ -1,7 +1,7 @@ import 'package:weaco/domain/feed/model/feed.dart'; abstract interface class OotdFeedRepository { - Future saveOotdFeed({required Feed feed}); + Future saveOotdFeed({required Feed feed}); - Future removeOotdFeed({required String id}); + Future removeOotdFeed({required String id}); } \ No newline at end of file diff --git a/lib/domain/feed/use_case/remove_my_page_feed_use_case.dart b/lib/domain/feed/use_case/remove_my_page_feed_use_case.dart index ae5b1c85..6c5b23df 100644 --- a/lib/domain/feed/use_case/remove_my_page_feed_use_case.dart +++ b/lib/domain/feed/use_case/remove_my_page_feed_use_case.dart @@ -9,7 +9,7 @@ class RemoveMyPageFeedUseCase { /// id를 통해 특정 피드를 삭제 /// @param id: 삭제할 피드의 id /// @return: null - Future execute({required String id}) async { - return await _ootdFeedRepository.removeOotdFeed(id: id); + Future execute({required String id}) async { + await _ootdFeedRepository.removeOotdFeed(id: id); } } diff --git a/lib/domain/feed/use_case/save_edit_feed_use_case.dart b/lib/domain/feed/use_case/save_edit_feed_use_case.dart index df409d02..fa6ea300 100644 --- a/lib/domain/feed/use_case/save_edit_feed_use_case.dart +++ b/lib/domain/feed/use_case/save_edit_feed_use_case.dart @@ -10,7 +10,7 @@ class SaveEditFeedUseCase { /// 특정 피드 저장 또는 업데이트 /// @param feed: 저장할 피드 /// @return: 서버 응답 메세지. response 모델로 변경 예정. - Future execute({required Feed feed}) async { - return await _ootdFeedRepository.saveOotdFeed(feed: feed); + Future execute({required Feed feed}) async { + await _ootdFeedRepository.saveOotdFeed(feed: feed); } } diff --git a/lib/domain/file/use_case/save_image_use_case.dart b/lib/domain/file/use_case/save_image_use_case.dart index 0c6c3a6a..eecd11e6 100644 --- a/lib/domain/file/use_case/save_image_use_case.dart +++ b/lib/domain/file/use_case/save_image_use_case.dart @@ -16,8 +16,8 @@ class SaveImageUseCase { /// @param isOrigin: 원본 이미지 = true, 크롭된 이미지 = false /// @param file: 저장할 이미지 데이터 /// @return: 데이터 저장 성공 여부 반환 - Future execute({required bool isOrigin, required File file}) async { + Future execute({required bool isOrigin, required File file}) async { final List compressedImage = await _imageCompressor.compressImage(file: file); - return await _fileRepository.saveImage(isOrigin: isOrigin, file: file, compressedImage: compressedImage); + await _fileRepository.saveImage(isOrigin: isOrigin, file: file, compressedImage: compressedImage); } } From 74162b9353cd7200504bf6d5443b4bdd8490087e Mon Sep 17 00:00:00 2001 From: "YunZi.Song" Date: Thu, 6 Jun 2024 19:56:04 +0900 Subject: [PATCH 14/32] =?UTF-8?q?Feat:=20exception=20code=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/enum/exception_code.dart | 90 ++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 8 deletions(-) diff --git a/lib/core/enum/exception_code.dart b/lib/core/enum/exception_code.dart index a2a19ffa..b745163b 100644 --- a/lib/core/enum/exception_code.dart +++ b/lib/core/enum/exception_code.dart @@ -1,8 +1,82 @@ -enum ExceptionCode{ - unknownException, - internalServerException, - notFoundException, - locationException, - authenticationNotExistException, - networkException, -} \ No newline at end of file +enum ExceptionCode { + unknownException( + code: '500', + errorMessage: '', + alertMessage: '', + ), + internalServerException( + code: '400', + errorMessage: '', + alertMessage: '', + ), + notFoundException( + code: '404', + errorMessage: '', + alertMessage: '', + ), + userNotFoundException( + code: 'user-not-found', + errorMessage: '존재하지 않는 유저입니다.', + alertMessage: '', + ), + wrongPasswordException( + code: 'wrong-password', + errorMessage: '비밀번호가 맞지 않습니다.', + alertMessage: '', + ), + emailAlreadyInUseException( + code: 'email-already-in-use', + errorMessage: '이미 가입된 이메일입니다.', + alertMessage: '', + ), + invalidEmailException( + code: 'invalid-email', + errorMessage: '이메일 형식이 올바르지 못합니다.', + alertMessage: '', + ), + weakPasswordException( + code: 'weak-password', + errorMessage: '비밀번호 형식이 안전하지 않습니다.', + alertMessage: '', + ), + networkRequestFailedException( + code: 'network-request-failed', + errorMessage: '네트워크가 불안정 합니다.', + alertMessage: '', + ), + userDisabledException( + code: 'user-disabled', + errorMessage: '이미 탈퇴한 회원입니다.', + alertMessage: '', + ), + invalidCredentialException( + code: 'invalid-credential', + errorMessage: '이메일과 비밀번호를 확인해주세요.', + alertMessage: ''); + + final String code; + final String errorMessage; + final String alertMessage; + + const ExceptionCode( + {required this.code, + required this.alertMessage, + required this.errorMessage}); + + static ExceptionCode fromStatus(String status) { + return switch (status) { + '500' => ExceptionCode.unknownException, + '400' => ExceptionCode.internalServerException, + '404' => ExceptionCode.notFoundException, + 'user-not-found' => ExceptionCode.userNotFoundException, + 'wrong-password' => ExceptionCode.wrongPasswordException, + 'email-already-in-use' => ExceptionCode.emailAlreadyInUseException, + 'invalid-email' => ExceptionCode.invalidEmailException, + 'weak-password' => ExceptionCode.weakPasswordException, + 'network-request-failed' => ExceptionCode.networkRequestFailedException, + 'user-disabled' => ExceptionCode.userDisabledException, + 'invaild-credential' => ExceptionCode.invalidCredentialException, + _ => ExceptionCode.unknownException, + }; + } +} From be50aa057b9ee8ab00fb6123a02d7ee6bf8035f2 Mon Sep 17 00:00:00 2001 From: "YunZi.Song" Date: Thu, 6 Jun 2024 21:33:57 +0900 Subject: [PATCH 15/32] =?UTF-8?q?Feat:=20exceptionCode=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/enum/exception_code.dart | 45 ++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/core/enum/exception_code.dart b/lib/core/enum/exception_code.dart index b745163b..ff5b3633 100644 --- a/lib/core/enum/exception_code.dart +++ b/lib/core/enum/exception_code.dart @@ -1,19 +1,24 @@ enum ExceptionCode { unknownException( code: '500', - errorMessage: '', - alertMessage: '', + errorMessage: '알 수 없는 오류 발생', + alertMessage: '알 수 없는 오류가 발생했습니다.', ), internalServerException( code: '400', - errorMessage: '', + errorMessage: '서버 내부 오류', alertMessage: '', ), notFoundException( code: '404', - errorMessage: '', + errorMessage: '데이터 조회 실패', alertMessage: '', ), + networkException( + code: '400', + errorMessage: '네트워크 요청 오류', + alertMessage: '네트워크 연결을 확인해주세요.', + ), userNotFoundException( code: 'user-not-found', errorMessage: '존재하지 않는 유저입니다.', @@ -52,16 +57,35 @@ enum ExceptionCode { invalidCredentialException( code: 'invalid-credential', errorMessage: '이메일과 비밀번호를 확인해주세요.', - alertMessage: ''); + alertMessage: ''), + + authenticationNotExistException( + code: 'authentication-not-exist', + errorMessage: 'FirebaseAuth 로그인 정보 없음', + alertMessage: notShowingMessage), + + locationException( + code: 'no-location', + errorMessage: '위치 정보를 가져올 수 없습니다.', + alertMessage: notShowingMessage, + ), + + kakaoGeoCoderApiException( + code: 'kakao-geo-coder-api-error', + errorMessage: 'Kakao Api 처리 오류', + alertMessage: notShowingMessage, + ); final String code; final String errorMessage; final String alertMessage; + static const String notShowingMessage = ''; - const ExceptionCode( - {required this.code, - required this.alertMessage, - required this.errorMessage}); + const ExceptionCode({ + required this.code, + required this.alertMessage, + required this.errorMessage, + }); static ExceptionCode fromStatus(String status) { return switch (status) { @@ -76,6 +100,9 @@ enum ExceptionCode { 'network-request-failed' => ExceptionCode.networkRequestFailedException, 'user-disabled' => ExceptionCode.userDisabledException, 'invaild-credential' => ExceptionCode.invalidCredentialException, + 'authentication-not-exist' => ExceptionCode.authenticationNotExistException, + 'no-location' => ExceptionCode.locationException, + 'kakao-geo-coder-api-error' => ExceptionCode.kakaoGeoCoderApiException, _ => ExceptionCode.unknownException, }; } From 59d67837d621f9d5d104d5a7fb9e954b9ebe62a8 Mon Sep 17 00:00:00 2001 From: "YunZi.Song" Date: Thu, 6 Jun 2024 21:37:25 +0900 Subject: [PATCH 16/32] =?UTF-8?q?Feat:=20dataSource=20=EB=8B=A8=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/gps/gps_helper.dart | 3 +- .../remote_feed_data_source_impl.dart | 74 +++++++++++-------- .../local/local_file_data_source_impl.dart | 20 ++--- .../remote/remote_file_data_source_impl.dart | 14 +--- .../file/repository/file_repository_impl.dart | 5 +- .../kakao_reverse_geo_coder_api.dart | 13 +--- .../firebase_user_auth_data_source.dart | 63 ++++++++-------- .../data_source/user_auth_data_source.dart | 8 +- .../repository/user_auth_repository_impl.dart | 23 ++---- ...ily_location_weather_data_source_impl.dart | 4 +- .../remote_data_source/meteo_weather_api.dart | 31 ++++---- ...her_background_image_data_source_impl.dart | 22 ++++-- .../file/use_case/save_image_use_case.dart | 8 +- .../user/use_case/log_out_use_case.dart | 4 +- .../user/use_case/sign_in_use_case.dart | 4 +- .../user/use_case/sign_out_use_case.dart | 4 +- .../user/use_case/sign_up_use_case.dart | 10 +-- 17 files changed, 147 insertions(+), 163 deletions(-) diff --git a/lib/core/gps/gps_helper.dart b/lib/core/gps/gps_helper.dart index 896e6992..ae47e0ab 100644 --- a/lib/core/gps/gps_helper.dart +++ b/lib/core/gps/gps_helper.dart @@ -1,7 +1,6 @@ import 'dart:developer'; import 'package:geolocator/geolocator.dart'; import 'package:weaco/core/enum/exception_code.dart'; -import 'package:weaco/core/exception/location_exception.dart'; import 'package:weaco/core/gps/gps_permission_status.dart'; import 'package:weaco/core/gps/gps_position.dart'; @@ -45,6 +44,6 @@ class GpsHelper { Position position = await Geolocator.getCurrentPosition(); return GpsPosition(lat: position.latitude, lng: position.longitude); } - throw LocationException(code: ExceptionCode.locationException, message: '위치 정보를 가져올 수 없습니다.'); + throw ExceptionCode.locationException; } } diff --git a/lib/data/feed/data_source/remote_feed_data_source_impl.dart b/lib/data/feed/data_source/remote_feed_data_source_impl.dart index 76dbd985..f5164109 100644 --- a/lib/data/feed/data_source/remote_feed_data_source_impl.dart +++ b/lib/data/feed/data_source/remote_feed_data_source_impl.dart @@ -1,4 +1,5 @@ import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:dio/dio.dart'; import 'package:weaco/core/enum/exception_code.dart'; import 'package:weaco/core/exception/not_found_exception.dart'; import 'package:weaco/core/firebase/firestore_dto_mapper.dart'; @@ -127,38 +128,47 @@ class RemoteFeedDataSourceImpl implements RemoteFeedDataSource { int? minTemperature, int? maxTemperature, }) async { - Query> query = _fireStore.collection('feeds'); - // 날씨 코드 필터링 - if (weatherCode != null) { - query = query.where('weather.code', isEqualTo: weatherCode); + try { + Query> query = _fireStore.collection('feeds'); + // 날씨 코드 필터링 + if (weatherCode != null) { + query = query.where('weather.code', isEqualTo: weatherCode); + } + + // 온도 범위 필터링 + if (minTemperature != null && maxTemperature != null) { + query = query + .where('weather.temperature', isLessThanOrEqualTo: maxTemperature) + .where('weather.temperature', + isGreaterThanOrEqualTo: minTemperature); + } + + // 계절 코드 필터링 + if (seasonCode != null) { + query = query.where('season_code', isEqualTo: seasonCode); + } + + // 생성일 기준으로 정렬하여 제한된 수의 문서 가져오기 + final QuerySnapshot> querySnapshot = await query + .where('created_at', + isLessThan: createdAt ?? Timestamp.fromDate(DateTime.now())) + .where('deleted_at', isNull: true) + .orderBy( + 'created_at', + descending: true, + ) + .limit(limit) + .get(); + + return querySnapshot.docs + .map((doc) => toFeed(json: doc.data(), id: doc.id)) + .toList(); + } catch (e) { + throw switch (e) { + FirebaseException _ => ExceptionCode.internalServerException, + DioException _ => ExceptionCode.networkException, + _ => ExceptionCode.unknownException, + }; } - - // 온도 범위 필터링 - if (minTemperature != null && maxTemperature != null) { - query = query - .where('weather.temperature', isLessThanOrEqualTo: maxTemperature) - .where('weather.temperature', isGreaterThanOrEqualTo: minTemperature); - } - - // 계절 코드 필터링 - if (seasonCode != null) { - query = query.where('season_code', isEqualTo: seasonCode); - } - - // 생성일 기준으로 정렬하여 제한된 수의 문서 가져오기 - final QuerySnapshot> querySnapshot = await query - .where('created_at', - isLessThan: createdAt ?? Timestamp.fromDate(DateTime.now())) - .where('deleted_at', isNull: true) - .orderBy( - 'created_at', - descending: true, - ) - .limit(limit) - .get(); - - return querySnapshot.docs - .map((doc) => toFeed(json: doc.data(), id: doc.id)) - .toList(); } } diff --git a/lib/data/file/data_source/local/local_file_data_source_impl.dart b/lib/data/file/data_source/local/local_file_data_source_impl.dart index bd26f4bf..060a95e4 100644 --- a/lib/data/file/data_source/local/local_file_data_source_impl.dart +++ b/lib/data/file/data_source/local/local_file_data_source_impl.dart @@ -3,8 +3,6 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:dio/dio.dart'; import 'package:weaco/core/enum/exception_code.dart'; import 'package:weaco/core/enum/image_type.dart'; -import 'package:weaco/core/exception/internal_server_exception.dart'; -import 'package:weaco/core/exception/network_exception.dart'; import 'package:weaco/core/path_provider/path_provider_service.dart'; import 'local_file_data_source.dart'; @@ -73,17 +71,11 @@ class LocalFileDataSourceImpl implements LocalFileDataSource { } } - Exception _exceptionHandling(Object e) { - switch (e.runtimeType) { - case FirebaseException _: - return InternalServerException( - code: ExceptionCode.internalServerException, message: '서버 내부 오류'); - case DioException _: - return NetworkException( - code: ExceptionCode.internalServerException, - message: '네트워크 오류 : $e'); - default: - return e as Exception; - } + ExceptionCode _exceptionHandling(Object e) { + return switch (e) { + FirebaseException _ => ExceptionCode.internalServerException, + DioException _ => ExceptionCode.internalServerException, + _ => ExceptionCode.unknownException, + }; } } diff --git a/lib/data/file/data_source/remote/remote_file_data_source_impl.dart b/lib/data/file/data_source/remote/remote_file_data_source_impl.dart index cb382609..9b8d20f9 100644 --- a/lib/data/file/data_source/remote/remote_file_data_source_impl.dart +++ b/lib/data/file/data_source/remote/remote_file_data_source_impl.dart @@ -2,9 +2,6 @@ import 'dart:io'; import 'package:dio/dio.dart'; import 'package:firebase_storage/firebase_storage.dart'; import 'package:weaco/core/enum/exception_code.dart'; -import 'package:weaco/core/exception/authentication_exception.dart'; -import 'package:weaco/core/exception/internal_server_exception.dart'; -import 'package:weaco/core/exception/network_exception.dart'; import 'package:weaco/core/firebase/firebase_auth_service.dart'; import 'package:weaco/data/file/data_source/remote/remote_file_data_source.dart'; @@ -25,9 +22,7 @@ class RemoteFileDataSourceImpl implements RemoteFileDataSource { final String? email = _firebaseAuthService.firebaseAuth.currentUser?.email; if (email == null) { - throw AuthenticationException( - code: ExceptionCode.authenticationNotExistException, - message: 'FirebaseAuth 로그인 정보 없음'); + throw ExceptionCode.authenticationNotExistException; } final feedOriginImageRef = _firebaseStorage.ref().child( 'feed_origin_images/${email}_${DateTime.now().microsecondsSinceEpoch}.png'); @@ -41,11 +36,8 @@ class RemoteFileDataSourceImpl implements RemoteFileDataSource { ]; } catch (e) { throw switch (e) { - FirebaseException _ => InternalServerException( - code: ExceptionCode.internalServerException, - message: 'Firebase Storage 저장 실패'), - DioException _ => NetworkException( - code: ExceptionCode.networkException, message: '네트워크 요청 오류: $e'), + FirebaseException _ => ExceptionCode.internalServerException, + DioException _ => ExceptionCode.networkException, _ => e, }; } diff --git a/lib/data/file/repository/file_repository_impl.dart b/lib/data/file/repository/file_repository_impl.dart index a5b57e4d..bfa19f69 100644 --- a/lib/data/file/repository/file_repository_impl.dart +++ b/lib/data/file/repository/file_repository_impl.dart @@ -28,9 +28,8 @@ class FileRepositoryImpl implements FileRepository { @override Future> saveOotdImage() async { - final File? croppedImage = await _localFileDataSource.getImage(imageType: ImageType.cropped); - final File? compressedImage = await _localFileDataSource.getImage(imageType: ImageType.compressed); - if (croppedImage == null || compressedImage == null) throw Exception(); + final File croppedImage = await _localFileDataSource.getImage(imageType: ImageType.cropped); + final File compressedImage = await _localFileDataSource.getImage(imageType: ImageType.compressed); return await _remoteFileDataSource.saveImage(croppedImage: croppedImage, compressedImage: compressedImage); } } diff --git a/lib/data/location/data_source/remote_data_source/kakao_reverse_geo_coder_api.dart b/lib/data/location/data_source/remote_data_source/kakao_reverse_geo_coder_api.dart index 5415af00..e447b065 100644 --- a/lib/data/location/data_source/remote_data_source/kakao_reverse_geo_coder_api.dart +++ b/lib/data/location/data_source/remote_data_source/kakao_reverse_geo_coder_api.dart @@ -3,9 +3,6 @@ import 'package:weaco/core/config/kakao_config.dart'; import 'package:weaco/core/dio/base_dio.dart'; import 'package:weaco/core/dio/base_response.dart'; import 'package:weaco/core/enum/exception_code.dart'; -import 'package:weaco/core/exception/internal_server_exception.dart'; -import 'package:weaco/core/exception/network_exception.dart'; -import 'package:weaco/core/exception/not_found_exception.dart'; import 'package:weaco/data/location/data_source/remote_data_source/remote_location_data_source.dart'; class KakaoReverseGeoCoderApi implements RemoteLocationDataSource { @@ -31,19 +28,15 @@ class KakaoReverseGeoCoderApi implements RemoteLocationDataSource { receiveTimeout: const Duration(seconds: 2)); if (result.statusCode == 200) { if (result.body['meta']['total_count'] == 0) { - throw NotFoundException( - code: ExceptionCode.notFoundException, message: '데이터 조회 결과 없음'); + throw ExceptionCode.notFoundException; } else { return '${result.body['documents'][0]['region_1depth_name']} ${result.body['documents'][0]['region_2depth_name']}'; } } - throw InternalServerException( - code: ExceptionCode.internalServerException, - message: 'Kakao Api 처리 오류'); + throw ExceptionCode.kakaoGeoCoderApiException; } catch (e) { throw switch (e) { - DioException _ => NetworkException( - code: ExceptionCode.networkException, message: '네트워크 요청 오류: $e'), + DioException _ => ExceptionCode.networkException, _ => e, }; } diff --git a/lib/data/user/data_source/firebase_user_auth_data_source.dart b/lib/data/user/data_source/firebase_user_auth_data_source.dart index e8609024..420bbde0 100644 --- a/lib/data/user/data_source/firebase_user_auth_data_source.dart +++ b/lib/data/user/data_source/firebase_user_auth_data_source.dart @@ -1,5 +1,8 @@ import 'dart:developer'; +import 'package:dio/dio.dart'; +import 'package:firebase_core/firebase_core.dart'; +import 'package:weaco/core/enum/exception_code.dart'; import 'package:weaco/core/firebase/firebase_auth_service.dart'; import 'package:weaco/data/user/data_source/user_auth_data_source.dart'; @@ -12,70 +15,62 @@ class FirebaseUserAuthDataSourceImpl implements UserAuthDataSource { // 회원가입 @override - Future signUp({required String email, required String password}) async { - bool isSignUpSuccess = false; - + Future signUp({required String email, required String password}) async { try { - final userCredential = - await _firebaseService.signUp(email: email, password: password); - - isSignUpSuccess = userCredential?.user != null; - } on Exception catch (e) { - isSignUpSuccess = false; + await _firebaseService.signUp(email: email, password: password); + } catch (e) { log(e.toString(), name: 'FirebaseUserAuthDataSource.signUp()'); - rethrow; + throw _exceptionHandling(e); } - - return isSignUpSuccess; } // 로그인 @override - Future signIn({required String email, required String password}) async { + Future signIn({required String email, required String password}) async { try { await _firebaseService.signIn(email: email, password: password); - } on Exception catch (e) { + } catch (e) { log(e.toString(), name: 'FirebaseUserAuthDataSource.signIn()'); - rethrow; + throw _exceptionHandling(e); } - - return true; } // 로그아웃 @override - Future logOut() async { - bool isLogOutSuccess = true; - + Future logOut() async { try { await _firebaseService.logOut(); - } on Exception catch (e) { - isLogOutSuccess = false; + } catch (e) { log(e.toString(), name: 'FirebaseUserAuthDataSource.logOut()'); - rethrow; + throw _exceptionHandling(e); } - - return isLogOutSuccess; } // 회원탈퇴 @override - Future signOut() async { - bool isSignOutSuccess = true; - + Future signOut() async { try { await _firebaseService.signOut(); - } on Exception catch (e) { - isSignOutSuccess = false; + } catch (e) { log(e.toString(), name: 'FirebaseUserAuthDataSource.signOut()'); - rethrow; + throw _exceptionHandling(e); } - - return isSignOutSuccess; } @override String? signInCheck() { - return _firebaseService.user?.email; + try { + return _firebaseService.user?.email; + } catch (e) { + throw _exceptionHandling(e); + } + } + + ExceptionCode _exceptionHandling(Object e) { + return switch (e) { + FirebaseException fb => ExceptionCode.fromStatus(fb.code), + DioException _ => ExceptionCode.internalServerException, + _ => ExceptionCode.unknownException, + }; } } diff --git a/lib/data/user/data_source/user_auth_data_source.dart b/lib/data/user/data_source/user_auth_data_source.dart index f3a65f59..869d3ce0 100644 --- a/lib/data/user/data_source/user_auth_data_source.dart +++ b/lib/data/user/data_source/user_auth_data_source.dart @@ -1,21 +1,21 @@ abstract interface class UserAuthDataSource { // 회원가입 - Future signUp({ + Future signUp({ required String email, required String password, }); // 로그인 - Future signIn({ + Future signIn({ required String email, required String password, }); // 로그아웃 - Future logOut(); + Future logOut(); // 회원탈퇴 - Future signOut(); + Future signOut(); // 로그인 체크 email 반환 String? signInCheck(); diff --git a/lib/data/user/repository/user_auth_repository_impl.dart b/lib/data/user/repository/user_auth_repository_impl.dart index c7fda9ca..9374bf0c 100644 --- a/lib/data/user/repository/user_auth_repository_impl.dart +++ b/lib/data/user/repository/user_auth_repository_impl.dart @@ -26,42 +26,35 @@ class UserAuthRepositoryImpl implements UserAuthRepository { /// /// 두 작업이 완료시 return true @override - Future signUp({ + Future signUp({ required UserAuth userAuth, required UserProfile userProfile, }) async { - final signUpResult = await _userAuthDataSource.signUp( + await _userAuthDataSource.signUp( email: userAuth.email, password: userAuth.password); - if (signUpResult == false) { - return false; - } - final saveUserProfileResult = await _remoteUserProfileDataSource .saveUserProfile(userProfile: userProfile); if (saveUserProfileResult == false) { await _userAuthDataSource.signOut(); - return false; } - - return true; } @override - Future signIn({required UserAuth userAuth}) async { - return await _userAuthDataSource.signIn( + Future signIn({required UserAuth userAuth}) async { + await _userAuthDataSource.signIn( email: userAuth.email, password: userAuth.password); } @override - Future logOut() async { - return await _userAuthDataSource.logOut(); + Future logOut() async { + await _userAuthDataSource.logOut(); } @override - Future signOut() async { - return await _userAuthDataSource.signOut(); + Future signOut() async { + await _userAuthDataSource.signOut(); } @override diff --git a/lib/data/weather/data_source/local_data_source/local_daily_location_weather_data_source_impl.dart b/lib/data/weather/data_source/local_data_source/local_daily_location_weather_data_source_impl.dart index a6be3a45..18695527 100644 --- a/lib/data/weather/data_source/local_data_source/local_daily_location_weather_data_source_impl.dart +++ b/lib/data/weather/data_source/local_data_source/local_daily_location_weather_data_source_impl.dart @@ -1,7 +1,6 @@ import 'dart:convert'; import 'package:weaco/core/enum/exception_code.dart'; -import 'package:weaco/core/exception/not_found_exception.dart'; import 'package:weaco/core/hive/hive_wrapper.dart'; import 'package:weaco/data/weather/data_source/local_data_source/local_daily_location_weather_data_source.dart'; import 'package:weaco/domain/weather/model/daily_location_weather.dart'; @@ -34,8 +33,7 @@ class LocalDailyLocationWeatherDataSourceImpl return DailyLocationWeather.fromJson(jsonDecode(data!)); } catch (e) { - throw NotFoundException( - code: ExceptionCode.notFoundException, message: '데이터 없음'); + throw ExceptionCode.notFoundException; } } } diff --git a/lib/data/weather/data_source/remote_data_source/meteo_weather_api.dart b/lib/data/weather/data_source/remote_data_source/meteo_weather_api.dart index d34cb4fc..d0ce064b 100644 --- a/lib/data/weather/data_source/remote_data_source/meteo_weather_api.dart +++ b/lib/data/weather/data_source/remote_data_source/meteo_weather_api.dart @@ -3,7 +3,7 @@ import 'dart:developer'; import 'package:weaco/core/config/meteo_config.dart'; import 'package:weaco/core/dio/base_dio.dart'; import 'package:weaco/core/dio/base_response.dart'; -import 'package:weaco/data/common/http/status_code_handler.dart'; +import 'package:weaco/core/enum/exception_code.dart'; import 'package:weaco/data/weather/data_source/remote_data_source/remote_weather_data_source.dart'; import 'package:weaco/data/weather/dto/weather_dto.dart'; @@ -16,19 +16,24 @@ class MeteoWeatherApi implements RemoteWeatherDataSource { /// @param lat: 위도 /// @param lng: 경도 @override - Future getWeather( - {required double lat, required double lng}) async { - final BaseResponse response = await _dio.get( - path: '${MeteoConfig.baseUrl}/v1/forecast?hourly=temperature_2m,' - 'weathercode&latitude=$lat&longitude=$lng&lang=ko&past_days=1&' - 'forecast_days=2&daily=temperature_2m_max,temperature_2m_min&' - 'timezone=Asia%2FTokyo'); + Future getWeather({ + required double lat, + required double lng, + }) async { + try { + final BaseResponse response = await _dio.get( + path: '${MeteoConfig.baseUrl}/v1/forecast?hourly=temperature_2m,' + 'weathercode&latitude=$lat&longitude=$lng&lang=ko&past_days=1&' + 'forecast_days=2&daily=temperature_2m_max,temperature_2m_min&' + 'timezone=Asia%2FTokyo'); - if (response.statusCode != 200) { - log('Code: ${response.statusCode}, Body: ${response.body}'); - - throw statusCodeHandler(code: response.statusCode); + if (response.statusCode != 200) { + log('Code: ${response.statusCode}, Body: ${response.body}'); + throw ExceptionCode.fromStatus(response.statusCode.toString()); + } + return WeatherDto.fromJson(json: response.body); + } catch (e) { + throw ExceptionCode.unknownException; } - return WeatherDto.fromJson(json: response.body); } } diff --git a/lib/data/weather/data_source/remote_data_source/remote_weather_background_image_data_source_impl.dart b/lib/data/weather/data_source/remote_data_source/remote_weather_background_image_data_source_impl.dart index f777137d..b46d52fd 100644 --- a/lib/data/weather/data_source/remote_data_source/remote_weather_background_image_data_source_impl.dart +++ b/lib/data/weather/data_source/remote_data_source/remote_weather_background_image_data_source_impl.dart @@ -1,4 +1,6 @@ import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:dio/dio.dart'; +import 'package:weaco/core/enum/exception_code.dart'; import 'package:weaco/data/weather/data_source/remote_data_source/remote_weather_background_image_data_source.dart'; import 'package:weaco/domain/weather/model/weather_background_image.dart'; @@ -12,11 +14,19 @@ class RemoteWeatherBackgroundImageDataSourceImpl @override Future> getWeatherBackgroundImageList() async { - QuerySnapshot> result = - await _firestore.collection('weather_background_images').get(); - return result.docs - .map((e) => WeatherBackgroundImage( - code: e.data()['code'], imagePath: e.data()['image_path'])) - .toList(); + try { + QuerySnapshot> result = + await _firestore.collection('weather_background_images').get(); + return result.docs + .map((e) => WeatherBackgroundImage( + code: e.data()['code'], imagePath: e.data()['image_path'])) + .toList(); + } catch (e) { + throw switch (e) { + FirebaseException _ => ExceptionCode.internalServerException, + DioException _ => ExceptionCode.internalServerException, + _ => ExceptionCode.unknownException, + }; + } } } diff --git a/lib/domain/file/use_case/save_image_use_case.dart b/lib/domain/file/use_case/save_image_use_case.dart index 0c6c3a6a..e534fd19 100644 --- a/lib/domain/file/use_case/save_image_use_case.dart +++ b/lib/domain/file/use_case/save_image_use_case.dart @@ -16,8 +16,10 @@ class SaveImageUseCase { /// @param isOrigin: 원본 이미지 = true, 크롭된 이미지 = false /// @param file: 저장할 이미지 데이터 /// @return: 데이터 저장 성공 여부 반환 - Future execute({required bool isOrigin, required File file}) async { - final List compressedImage = await _imageCompressor.compressImage(file: file); - return await _fileRepository.saveImage(isOrigin: isOrigin, file: file, compressedImage: compressedImage); + Future execute({required bool isOrigin, required File file}) async { + final List compressedImage = + await _imageCompressor.compressImage(file: file); + await _fileRepository.saveImage( + isOrigin: isOrigin, file: file, compressedImage: compressedImage); } } diff --git a/lib/domain/user/use_case/log_out_use_case.dart b/lib/domain/user/use_case/log_out_use_case.dart index 7c411dcc..3fa89b0f 100644 --- a/lib/domain/user/use_case/log_out_use_case.dart +++ b/lib/domain/user/use_case/log_out_use_case.dart @@ -7,7 +7,7 @@ class LogOutUseCase { required UserAuthRepository userAuthRepository, }) : _userAuthRepository = userAuthRepository; - Future execute() async { - return await _userAuthRepository.logOut(); + Future execute() async { + await _userAuthRepository.logOut(); } } diff --git a/lib/domain/user/use_case/sign_in_use_case.dart b/lib/domain/user/use_case/sign_in_use_case.dart index 49e6b658..0283201d 100644 --- a/lib/domain/user/use_case/sign_in_use_case.dart +++ b/lib/domain/user/use_case/sign_in_use_case.dart @@ -9,7 +9,7 @@ class SignInUseCase { }) : _userAuthRepository = userAuthRepository; /// 로그인 - Future execute({required UserAuth userAuth}) async { - return await _userAuthRepository.signIn(userAuth: userAuth); + Future execute({required UserAuth userAuth}) async { + await _userAuthRepository.signIn(userAuth: userAuth); } } diff --git a/lib/domain/user/use_case/sign_out_use_case.dart b/lib/domain/user/use_case/sign_out_use_case.dart index 07fd7a4e..08c45e97 100644 --- a/lib/domain/user/use_case/sign_out_use_case.dart +++ b/lib/domain/user/use_case/sign_out_use_case.dart @@ -8,7 +8,7 @@ class SignOutUseCase { }) : _userAuthRepository = userAuthRepository; /// 설정 화면의 회월 탈퇴를 위한 Use Case - Future execute() async { - return await _userAuthRepository.signOut(); + Future execute() async { + await _userAuthRepository.signOut(); } } diff --git a/lib/domain/user/use_case/sign_up_use_case.dart b/lib/domain/user/use_case/sign_up_use_case.dart index 1d8770a6..7966b07a 100644 --- a/lib/domain/user/use_case/sign_up_use_case.dart +++ b/lib/domain/user/use_case/sign_up_use_case.dart @@ -13,14 +13,10 @@ class SignUpUseCase { /// [userAuth] : 회원 가입을 위한 이메일, 비밀번호 정보 /// [userProfile] : 회원 가입을 위한 프로필 정보 /// 회원 가입 성공시 true 반환, 실패시 false 반환 - Future execute( + Future execute( {required UserAuth userAuth, required UserProfile userProfile}) async { - final bool result = await _userAuthRepository.signUp( + await _userAuthRepository.signUp( userAuth: userAuth, userProfile: userProfile); - if (result) { - await _userAuthRepository.signIn(userAuth: userAuth); - } - - return result; + await _userAuthRepository.signIn(userAuth: userAuth); } } From b673c7163ab596d4e85d23277dd63ca9066d51e0 Mon Sep 17 00:00:00 2001 From: "YunZi.Song" Date: Thu, 6 Jun 2024 21:38:19 +0900 Subject: [PATCH 17/32] =?UTF-8?q?Feat:=20data=20source=20=EB=8B=A8=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20user=20auth=20repository=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/domain/user/repository/user_auth_repository.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/domain/user/repository/user_auth_repository.dart b/lib/domain/user/repository/user_auth_repository.dart index 212391ab..c485438d 100644 --- a/lib/domain/user/repository/user_auth_repository.dart +++ b/lib/domain/user/repository/user_auth_repository.dart @@ -2,16 +2,16 @@ import 'package:weaco/domain/user/model/user_auth.dart'; import 'package:weaco/domain/user/model/user_profile.dart'; abstract interface class UserAuthRepository { - Future signIn({required UserAuth userAuth}); + Future signIn({required UserAuth userAuth}); - Future signUp({ + Future signUp({ required UserAuth userAuth, required UserProfile userProfile, }); - Future signOut(); + Future signOut(); - Future logOut(); + Future logOut(); String? signInCheck(); } From c942aef73ea550b77a862caed6d4a62db5db4077 Mon Sep 17 00:00:00 2001 From: "YunZi.Song" Date: Thu, 6 Jun 2024 21:46:03 +0900 Subject: [PATCH 18/32] =?UTF-8?q?Feat:=20data=20source=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=9D=BC=EB=B6=80=20?= =?UTF-8?q?view=20model=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../view_model/picutre_crop_view_model.dart | 10 ++-------- .../view_model/app_setting_view_model.dart | 16 ++++------------ 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/lib/presentation/ootd_post/view_model/picutre_crop_view_model.dart b/lib/presentation/ootd_post/view_model/picutre_crop_view_model.dart index c1814a00..385ff025 100644 --- a/lib/presentation/ootd_post/view_model/picutre_crop_view_model.dart +++ b/lib/presentation/ootd_post/view_model/picutre_crop_view_model.dart @@ -30,14 +30,8 @@ class PictureCropViewModel with ChangeNotifier { /// 크롭 이미지 저장 Future saveCroppedImage({required File file}) async { - final bool result = - await _saveImageUseCase.execute(isOrigin: false, file: file); - - if (result) { - _status = PictureCropSaveStatus.success; - } else { - _status = PictureCropSaveStatus.error; - } + await _saveImageUseCase.execute(isOrigin: false, file: file); + notifyListeners(); } diff --git a/lib/presentation/settings/view_model/app_setting_view_model.dart b/lib/presentation/settings/view_model/app_setting_view_model.dart index b32dd8b3..7ebb8098 100644 --- a/lib/presentation/settings/view_model/app_setting_view_model.dart +++ b/lib/presentation/settings/view_model/app_setting_view_model.dart @@ -37,20 +37,12 @@ class AppSettingViewModel with ChangeNotifier { } /// 로그아웃 성공 시, true 반환 - Future logOut() async { - try { - return await _logOutUseCase.execute(); - } catch (e) { - return false; - } + Future logOut() async { + return await _logOutUseCase.execute(); } /// 회원탈퇴 성공 시, true 반환 - Future signOut() async { - try { - return await _signOutUseCase.execute(); - } catch (e) { - return false; - } + Future signOut() async { + return await _signOutUseCase.execute(); } } From 3b41f08cbdd09b712085534b5d0d97266f3de689 Mon Sep 17 00:00:00 2001 From: "YunZi.Song" Date: Thu, 6 Jun 2024 21:46:29 +0900 Subject: [PATCH 19/32] =?UTF-8?q?Feat:=20data=20source=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EC=9D=BC=EB=B6=80=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data_source/local_file_data_source_test.dart | 4 ++-- .../data_source/remote_file_data_source_test.dart | 14 ++++++-------- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/test/data/file/data_source/local_file_data_source_test.dart b/test/data/file/data_source/local_file_data_source_test.dart index 779a5472..e543db38 100644 --- a/test/data/file/data_source/local_file_data_source_test.dart +++ b/test/data/file/data_source/local_file_data_source_test.dart @@ -35,7 +35,7 @@ void main() { File? file = await dataSource.getImage(imageType: imageType); // Then - expect(file?.readAsBytesSync(), + expect(file.readAsBytesSync(), File('test/mock/assets/origin.png').readAsBytesSync()); }); @@ -49,7 +49,7 @@ void main() { File? file = await dataSource.getImage(imageType: imageType); // Then - expect(file?.readAsBytesSync(), + expect(file.readAsBytesSync(), File('test/mock/assets/cropped.png').readAsBytesSync()); }); diff --git a/test/data/file/data_source/remote_file_data_source_test.dart b/test/data/file/data_source/remote_file_data_source_test.dart index 56c3b561..abc921a7 100644 --- a/test/data/file/data_source/remote_file_data_source_test.dart +++ b/test/data/file/data_source/remote_file_data_source_test.dart @@ -45,14 +45,12 @@ void main() { File? croppedImage = await localFileDataSource.getImage(imageType: ImageType.cropped); File? compressedImage = await localFileDataSource.getImage(imageType: ImageType.compressed); - if (croppedImage != null && compressedImage != null) { - final path = - await remoteFileDataSource.saveImage(croppedImage: croppedImage, compressedImage: compressedImage); - - expect(path[0].startsWith('${bucketPath}feed_origin_images/$email'), true); - expect(path[1].startsWith('${bucketPath}feed_thumbnail_images/$email'), true); - } - }); + final path = + await remoteFileDataSource.saveImage(croppedImage: croppedImage, compressedImage: compressedImage); + + expect(path[0].startsWith('${bucketPath}feed_origin_images/$email'), true); + expect(path[1].startsWith('${bucketPath}feed_thumbnail_images/$email'), true); + }); }); }); } From 227472353cc5d2ed0b75dea43d0868bc33d6158d Mon Sep 17 00:00:00 2001 From: "YunZi.Song" Date: Thu, 6 Jun 2024 22:07:23 +0900 Subject: [PATCH 20/32] =?UTF-8?q?Feat:=20remote=20user=20profile=20data=20?= =?UTF-8?q?source=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=EC=88=98=EC=A0=95=20=EC=9E=91=EC=97=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data_source/remote_user_profile_data_source.dart | 6 +++--- .../remote_user_profile_date_source_impl.dart | 12 ++++++------ .../user/repository/user_auth_repository_impl.dart | 10 +++++----- .../repository/user_profile_repository_impl.dart | 5 +++-- .../user/repository/user_profile_repository.dart | 4 ++-- 5 files changed, 19 insertions(+), 18 deletions(-) diff --git a/lib/data/user/data_source/remote_user_profile_data_source.dart b/lib/data/user/data_source/remote_user_profile_data_source.dart index dad87273..b4bd19d4 100644 --- a/lib/data/user/data_source/remote_user_profile_data_source.dart +++ b/lib/data/user/data_source/remote_user_profile_data_source.dart @@ -6,11 +6,11 @@ abstract interface class RemoteUserProfileDataSource { Future getUserProfile({String email}); /// 유저 프로필 업데이트 (피드 갯수 수정 할 때 주로 쓰임) - Future updateUserProfile({required UserProfile userProfile}); + Future updateUserProfile({required UserProfile userProfile}); /// 유저 프로필 저장 - Future saveUserProfile({required UserProfile userProfile}); + Future saveUserProfile({required UserProfile userProfile}); /// 유저 프로필 삭제 - Future removeUserProfile(); + Future removeUserProfile(); } diff --git a/lib/data/user/data_source/remote_user_profile_date_source_impl.dart b/lib/data/user/data_source/remote_user_profile_date_source_impl.dart index 7b342f3a..e0bac837 100644 --- a/lib/data/user/data_source/remote_user_profile_date_source_impl.dart +++ b/lib/data/user/data_source/remote_user_profile_date_source_impl.dart @@ -15,9 +15,9 @@ class RemoteUserProfileDataSourceImpl implements RemoteUserProfileDataSource { _firebaseService = firebaseService; @override - Future saveUserProfile({required UserProfile userProfile}) async { + Future saveUserProfile({required UserProfile userProfile}) async { try { - return await _firestore + await _firestore .collection('user_profiles') .add(toUserProfileDto(userProfile: userProfile)) .then((value) => true) @@ -46,14 +46,14 @@ class RemoteUserProfileDataSourceImpl implements RemoteUserProfileDataSource { } @override - Future updateUserProfile({required UserProfile userProfile}) async { + Future updateUserProfile({required UserProfile userProfile}) async { try { final originProfileDocument = await _firestore .collection('user_profiles') .where('email', isEqualTo: userProfile.email) .get(); - return await _firestore + await _firestore .collection('user_profiles') .doc(originProfileDocument.docs[0].reference.id) .set(toUserProfileDto(userProfile: userProfile)) @@ -67,7 +67,7 @@ class RemoteUserProfileDataSourceImpl implements RemoteUserProfileDataSource { } @override - Future removeUserProfile({String? email}) async { + Future removeUserProfile({String? email}) async { try { email = email ?? _firebaseService.firebaseAuth.currentUser!.email; @@ -76,7 +76,7 @@ class RemoteUserProfileDataSourceImpl implements RemoteUserProfileDataSource { .where('email', isEqualTo: email) .get(); - return await _firestore + await _firestore .collection('user_profiles') .doc(originProfileDocument.docs[0].reference.id) .delete() diff --git a/lib/data/user/repository/user_auth_repository_impl.dart b/lib/data/user/repository/user_auth_repository_impl.dart index 9374bf0c..c98a90d8 100644 --- a/lib/data/user/repository/user_auth_repository_impl.dart +++ b/lib/data/user/repository/user_auth_repository_impl.dart @@ -32,12 +32,12 @@ class UserAuthRepositoryImpl implements UserAuthRepository { }) async { await _userAuthDataSource.signUp( email: userAuth.email, password: userAuth.password); - - final saveUserProfileResult = await _remoteUserProfileDataSource - .saveUserProfile(userProfile: userProfile); - - if (saveUserProfileResult == false) { + try { + await _remoteUserProfileDataSource.saveUserProfile( + userProfile: userProfile); + } catch (e) { await _userAuthDataSource.signOut(); + rethrow; } } diff --git a/lib/data/user/repository/user_profile_repository_impl.dart b/lib/data/user/repository/user_profile_repository_impl.dart index b3e3d1d6..3742b260 100644 --- a/lib/data/user/repository/user_profile_repository_impl.dart +++ b/lib/data/user/repository/user_profile_repository_impl.dart @@ -20,7 +20,8 @@ class UserProfileRepositoryImpl implements UserProfileRepository { } @override - Future updateUserProfile({required UserProfile userProfile}) async { - return await _remoteUserProfileDataSource.updateUserProfile(userProfile: userProfile); + Future updateUserProfile({required UserProfile userProfile}) async { + await _remoteUserProfileDataSource.updateUserProfile( + userProfile: userProfile); } } diff --git a/lib/domain/user/repository/user_profile_repository.dart b/lib/domain/user/repository/user_profile_repository.dart index 25644f6f..e1ca7ebe 100644 --- a/lib/domain/user/repository/user_profile_repository.dart +++ b/lib/domain/user/repository/user_profile_repository.dart @@ -5,5 +5,5 @@ abstract interface class UserProfileRepository { Future getMyProfile(); - Future updateUserProfile({required UserProfile userProfile}); -} \ No newline at end of file + Future updateUserProfile({required UserProfile userProfile}); +} From 7aa79a550108b20300e9ef9628b804073eca089c Mon Sep 17 00:00:00 2001 From: "YunZi.Song" Date: Thu, 6 Jun 2024 22:31:46 +0900 Subject: [PATCH 21/32] =?UTF-8?q?Feat:=20my=20page=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=EC=86=8C=EC=8A=A4=EB=8B=A8=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=B6=84=EA=B8=B0=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../my_page/screen/my_page_screen.dart | 18 +++++------------- .../my_page/view_model/my_page_view_model.dart | 13 ++++--------- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/lib/presentation/my_page/screen/my_page_screen.dart b/lib/presentation/my_page/screen/my_page_screen.dart index 22955ed6..d1908734 100644 --- a/lib/presentation/my_page/screen/my_page_screen.dart +++ b/lib/presentation/my_page/screen/my_page_screen.dart @@ -201,19 +201,11 @@ class _MyPageScreenState extends State { myPageViewModel .removeSelectedFeed(currentFeed.id!) .then((result) { - if (result) { - AlertUtil.showAlert( - context: context, - exceptionAlert: ExceptionAlert.snackBar, - message: '피드가 삭제 되었습니다.', - ); - } else { - AlertUtil.showAlert( - context: context, - exceptionAlert: ExceptionAlert.snackBar, - message: '피드가 삭제에 실패했습니다.', - ); - } + AlertUtil.showAlert( + context: context, + exceptionAlert: ExceptionAlert.snackBar, + message: '피드가 삭제 되었습니다.', + ); context.pop(); context.pop(); diff --git a/lib/presentation/my_page/view_model/my_page_view_model.dart b/lib/presentation/my_page/view_model/my_page_view_model.dart index 0741d708..55d80cc6 100644 --- a/lib/presentation/my_page/view_model/my_page_view_model.dart +++ b/lib/presentation/my_page/view_model/my_page_view_model.dart @@ -151,21 +151,16 @@ class MyPageViewModel with ChangeNotifier { } // Feed 리스트에서 특정 피드 삭제 - Future removeSelectedFeed(String feedId) async { + Future removeSelectedFeed(String feedId) async { try { - final result = await _removeMyPageFeedUseCase.execute(id: feedId); + await _removeMyPageFeedUseCase.execute(id: feedId); - if (result) { - _decreaseFeedCount(); - _removeFeedFromList(feedId); - } + _decreaseFeedCount(); + _removeFeedFromList(feedId); notifyListeners(); - - return result; } on Exception catch (e) { log(e.toString(), name: 'MyPageViewModel.removeSelectedFeed()'); - return false; } } From 933ed4e5ffc351009234167476fae35be9bdf7b3 Mon Sep 17 00:00:00 2001 From: "YunZi.Song" Date: Thu, 6 Jun 2024 22:32:20 +0900 Subject: [PATCH 22/32] =?UTF-8?q?Feat:=20ootd=20post=20screen=20&=20view?= =?UTF-8?q?=20model=20=EB=8D=B0=EC=9D=B4=ED=84=B0=EC=86=8C=EC=8A=A4?= =?UTF-8?q?=EB=8B=A8=20=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=EB=B6=84=EA=B8=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ootd_post/screen/ootd_post_screen.dart | 10 +--------- .../ootd_post/view_model/ootd_post_view_model.dart | 10 ++++------ 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/lib/presentation/ootd_post/screen/ootd_post_screen.dart b/lib/presentation/ootd_post/screen/ootd_post_screen.dart index 2e1bb442..c40a6f3e 100644 --- a/lib/presentation/ootd_post/screen/ootd_post_screen.dart +++ b/lib/presentation/ootd_post/screen/ootd_post_screen.dart @@ -228,15 +228,7 @@ class _OotdPostScreenState extends State { } if (mounted) { - if (viewModel.saveStatus) { - RouterStatic.goToDefault(context); - } else { - AlertUtil.showAlert( - context: context, - exceptionAlert: ExceptionAlert.snackBar, - message: '다시 시도해 주세요.', - ); - } + RouterStatic.goToDefault(context); } }, child: viewModel.showSpinner diff --git a/lib/presentation/ootd_post/view_model/ootd_post_view_model.dart b/lib/presentation/ootd_post/view_model/ootd_post_view_model.dart index 1064a3eb..3ad9bd8e 100644 --- a/lib/presentation/ootd_post/view_model/ootd_post_view_model.dart +++ b/lib/presentation/ootd_post/view_model/ootd_post_view_model.dart @@ -16,7 +16,6 @@ class OotdPostViewModel with ChangeNotifier { final GetDailyLocationWeatherUseCase _getDailyLocationWeatherUseCase; final SaveEditFeedUseCase _saveEditFeedUseCase; bool _showSpinner = false; - bool _saveStatus = false; File? _originImage; File? _croppedImage; Weather? _weather; @@ -38,7 +37,8 @@ class OotdPostViewModel with ChangeNotifier { notifyListeners(); try { - _croppedImage = await _getImageUseCase.execute(imageType: ImageType.cropped); + _croppedImage = + await _getImageUseCase.execute(imageType: ImageType.cropped); _dailyLocationWeather = await _getDailyLocationWeatherUseCase.execute(); // 현재 시간 날씨 _weather = _dailyLocationWeather!.weatherList.firstWhere((element) { @@ -79,7 +79,7 @@ class OotdPostViewModel with ChangeNotifier { createdAt: now, ); - _saveStatus = await _saveEditFeedUseCase.execute(feed: feed); + await _saveEditFeedUseCase.execute(feed: feed); _showSpinner = false; notifyListeners(); @@ -101,7 +101,7 @@ class OotdPostViewModel with ChangeNotifier { createdAt: feed.createdAt, ); - _saveStatus = await _saveEditFeedUseCase.execute(feed: editedFeed); + await _saveEditFeedUseCase.execute(feed: editedFeed); _showSpinner = false; notifyListeners(); @@ -120,7 +120,5 @@ class OotdPostViewModel with ChangeNotifier { DailyLocationWeather? get dailyLocationWeather => _dailyLocationWeather; - bool get saveStatus => _saveStatus; - bool get showSpinner => _showSpinner; } From 725049b51c442fe8f5c649636adf3cc64b5740e6 Mon Sep 17 00:00:00 2001 From: "YunZi.Song" Date: Thu, 6 Jun 2024 22:32:43 +0900 Subject: [PATCH 23/32] =?UTF-8?q?Feat:=20app=20setting=20screen=20=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=EC=86=8C=EC=8A=A4=EB=8B=A8=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=EB=B6=84?= =?UTF-8?q?=EA=B8=B0=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/screen/app_setting_screen.dart | 38 ++++++------------- 1 file changed, 12 insertions(+), 26 deletions(-) diff --git a/lib/presentation/settings/screen/app_setting_screen.dart b/lib/presentation/settings/screen/app_setting_screen.dart index b1ada6bf..a128d337 100644 --- a/lib/presentation/settings/screen/app_setting_screen.dart +++ b/lib/presentation/settings/screen/app_setting_screen.dart @@ -113,21 +113,14 @@ class _AppSettingScreenState extends State { context.pop(); context.read().logOut().then((value) { - if (value) { - context.read().signOut(); + context.read().signOut(); - AlertUtil.showAlert( - context: context, - exceptionAlert: ExceptionAlert.snackBar, - message: '로그아웃에 성공했습니다.'); + AlertUtil.showAlert( + context: context, + exceptionAlert: ExceptionAlert.snackBar, + message: '로그아웃에 성공했습니다.'); - RouterStatic.clearAndNavigate(context, RouterPath.defaultPage.path); - } else { - AlertUtil.showAlert( - context: context, - exceptionAlert: ExceptionAlert.snackBar, - message: '로그아웃에 실패했습니다.'); - } + RouterStatic.clearAndNavigate(context, RouterPath.defaultPage.path); }); } @@ -135,21 +128,14 @@ class _AppSettingScreenState extends State { context.pop(); context.read().signOut().then((value) { - if (value) { - context.read().signOut(); + context.read().signOut(); - AlertUtil.showAlert( - context: context, - exceptionAlert: ExceptionAlert.snackBar, - message: '회원탈퇴에 성공했습니다.'); + AlertUtil.showAlert( + context: context, + exceptionAlert: ExceptionAlert.snackBar, + message: '회원탈퇴에 성공했습니다.'); - RouterStatic.clearAndNavigate(context, RouterPath.defaultPage.path); - } else { - AlertUtil.showAlert( - context: context, - exceptionAlert: ExceptionAlert.snackBar, - message: '회원탈퇴에 실패했습니다.'); - } + RouterStatic.clearAndNavigate(context, RouterPath.defaultPage.path); }); } } From fda37f163f3c0b874f3644366782451abd12a713 Mon Sep 17 00:00:00 2001 From: "YunZi.Song" Date: Thu, 6 Jun 2024 23:46:18 +0900 Subject: [PATCH 24/32] =?UTF-8?q?Feat:=20exception=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../remote_feed_data_source_impl_test.dart | 16 +- .../repository/feed_repository_impl_test.dart | 8 +- .../ootd_feed_repository_impl_test.dart | 34 +-- .../user_auth_repository_impl_test.dart | 194 ++++-------------- .../remove_my_page_feed_use_case_test.dart | 9 +- .../save_edit_feed_use_case_test.dart | 35 ---- .../remote_user_profile_data_source_test.dart | 41 +++- .../repository/mock_feed_repository_impl.dart | 4 +- 8 files changed, 113 insertions(+), 228 deletions(-) diff --git a/test/data/feed/data_source/remote_feed_data_source_impl_test.dart b/test/data/feed/data_source/remote_feed_data_source_impl_test.dart index df36b14f..8e63566a 100644 --- a/test/data/feed/data_source/remote_feed_data_source_impl_test.dart +++ b/test/data/feed/data_source/remote_feed_data_source_impl_test.dart @@ -24,7 +24,7 @@ void main() { group( 'saveFeed()는', () { - test('Firestore에 Feed를 저장하고, true를 반환해야 한다', () async { + test('Firestore에 Feed를 저장 요청을 보낸다.', () async { // Given Weather mockWeather = Weather( temperature: 31, @@ -51,10 +51,13 @@ void main() { ); // When - final result = await dataSource.saveFeed(feed: mockFeed); + await dataSource.saveFeed(feed: mockFeed); + + final snapshot = + fakeFirestore.collection('feeds').doc(mockFeed.id).get(); // Then - expect(result, true); + expect(mockFeed, snapshot); }); test( 'Firestore에 데이터를 추가해야 한다.', @@ -185,7 +188,7 @@ void main() { }); }); group('deletedFeed()는', () { - test('id값을 전달하면, 특정 Feed를 삭제하고 true를 반환해야 한다', () async { + test('id값을 전달하면, 특정 Feed를 삭제 요청을 한다.', () async { // Given const testId = 'testId'; @@ -216,7 +219,8 @@ void main() { }); // When - final result = await dataSource.deleteFeed(id: testId); + await dataSource.deleteFeed(id: testId); + final docResult = await fakeFirestore.collection('feeds').doc(testId).get(); @@ -224,7 +228,7 @@ void main() { toFeed(json: docResult.data()!, id: docResult.id).deletedAt; // Then - expect(result, feedDeletedAt != null); + expect(feedDeletedAt != null, true); }); }); group('getSearchFeedList()는', () { diff --git a/test/data/feed/repository/feed_repository_impl_test.dart b/test/data/feed/repository/feed_repository_impl_test.dart index 7376ea82..12213689 100644 --- a/test/data/feed/repository/feed_repository_impl_test.dart +++ b/test/data/feed/repository/feed_repository_impl_test.dart @@ -42,7 +42,7 @@ void main() { tearDown(() => mockFeedDataSource.cleanUpMockData()); - test('saveFeed는', () async { + test('saveFeed는 ', () async { // given final mockFeed = Feed( id: 'id', @@ -69,10 +69,9 @@ void main() { mockFeedDataSource.saveFeedReturnValue = true; // when - final actual = await feedRepository.saveFeed(editedFeed: mockFeed); + await feedRepository.saveFeed(editedFeed: mockFeed); // then - expect(actual, true); expect(mockFeedDataSource.feedList.last, mockFeed); }); test('deleteFeed는', () async { @@ -83,11 +82,10 @@ void main() { mockFeedDataSource.deleteFeedReturnValue = expectedBool; // When - final actual = await feedRepository.deleteFeed(id: expectedId); + await feedRepository.deleteFeed(id: expectedId); // Then expect(mockFeedDataSource.deleteFeedParamId, expectedId); - expect(actual, expectedBool); }); test('getUserFeedList는', () async { // Given diff --git a/test/data/feed/repository/ootd_feed_repository_impl_test.dart b/test/data/feed/repository/ootd_feed_repository_impl_test.dart index 45b84a19..5399c49f 100644 --- a/test/data/feed/repository/ootd_feed_repository_impl_test.dart +++ b/test/data/feed/repository/ootd_feed_repository_impl_test.dart @@ -145,19 +145,22 @@ void main() { }); test( - 'FeedRepository.saveFeed, UserProfileRepository.updateMyFeedCount()를 ' - '정상적으로 호출한 뒤, true 를 반환한다.', () async { + 'FeedRepository.saveFeed(), UserProfileRepository.updateMyFeedCount()를 ' + '정상적으로 한번 호출했는지 확인한다.', () async { // Given - const expected = true; + const int expected = 1; // When - final actual = await ootdFeedRepository.saveOotdFeed(feed: mockFeed); + await ootdFeedRepository.saveOotdFeed(feed: mockFeed); // Then - expect(actual, expected); + expect(feedRepository.saveFeedCallCount, expected); + expect(userProfileRepository.updateUserProfileCallCount, expected); }); - test('파라미터로 전달받은 Feed의 id값이 있으면 수정하는 Feed로써 FeedRepository.saveFeed를 한번 호출한다.', () async { + test( + '파라미터로 전달받은 Feed의 id값이 있으면 수정하는 Feed로써 FeedRepository.saveFeed를 한번 호출한다.', + () async { // Given const int expected = 1; @@ -168,7 +171,9 @@ void main() { expect(feedRepository.saveFeedCallCount, expected); }); - test('파라미터로 전달받은 Feed의 id값이 있으면 수정하는 Feed로써 FileRepository.saveOotdImage()를 호출하지 않는다.', () async { + test( + '파라미터로 전달받은 Feed의 id값이 있으면 수정하는 Feed로써 FileRepository.saveOotdImage()를 호출하지 않는다.', + () async { // Given const int expected = 0; @@ -179,7 +184,9 @@ void main() { expect(fileRepository.saveImageCallCount, expected); }); - test('파라미터로 전달받은 Feed의 id값이 있으면 수정하는 Feed로써 UserProfileRepository.getMyProfile()를 호출하지 않는다.', () async { + test( + '파라미터로 전달받은 Feed의 id값이 있으면 수정하는 Feed로써 UserProfileRepository.getMyProfile()를 호출하지 않는다.', + () async { // Given const int expected = 0; @@ -246,16 +253,17 @@ void main() { }); test( - 'FeedRepository.deleteFeed(), OotdFeedRepositoryImpl.updateMyFeedCount()를 ' - '정상적으로 호출한 뒤, true 를 반환한다.', () async { + 'FeedRepository.deleteFeed(), UserProfileRepository.updateUserProfile()를 ' + '정상적으로 한번 호출했는지 확인한다.', () async { // Given - const expected = true; + const expected = 1; // When - final actual = await ootdFeedRepository.removeOotdFeed(id: feedId); + await ootdFeedRepository.removeOotdFeed(id: feedId); // Then - expect(actual, expected); + expect(feedRepository.getDeleteFeedCallCount, expected); + expect(userProfileRepository.updateUserProfileCallCount, expected); }); }); }); diff --git a/test/data/user/repository/user_auth_repository_impl_test.dart b/test/data/user/repository/user_auth_repository_impl_test.dart index fa7ab7c3..c144ae75 100644 --- a/test/data/user/repository/user_auth_repository_impl_test.dart +++ b/test/data/user/repository/user_auth_repository_impl_test.dart @@ -119,171 +119,63 @@ void main() { expectUserProfile); }); - test('회원가입이 성공했을 때 true 를 반환한다.', () async { - // given - final expectUserAuth = userAuth.copyWith(); - final expectUserProfile = userProfile.copyWith(); + group('signIn() 메소드는', () { + test('UserAuthDataSource.signIn() 메소드를 1회 호출한다.', () async { + // given + int expectCallCount = 1; - userAuthDataSource.returnValue = true; - userProfileDataSource.isSaved = true; + // when + await userAuthRepository.signIn( + userAuth: userAuth, + ); - // when - final actualResult = await userAuthRepository.signUp( - userAuth: expectUserAuth, - userProfile: expectUserProfile, - ); + // then + expect(userAuthDataSource.signInCallCount, expectCallCount); + }); - // then - expect(actualResult, true); - }); - test('회원가입이 성공했을 때 false 를 반환한다.', () async { - // given - final expectUserAuth = userAuth.copyWith(); - final expectUserProfile = userProfile.copyWith(); + test('인자로 받은 값을 UserAuthDataSource.signIn() 메소드에 그대로 전달한다.', () async { + // given + final expectUserAuth = userAuth.copyWith(); - userAuthDataSource.returnValue = false; + final expectUserAuthParameter = { + 'email': expectUserAuth.email, + 'password': expectUserAuth.password, + }; - // when - final actualResult = await userAuthRepository.signUp( - userAuth: expectUserAuth, - userProfile: expectUserProfile, - ); + // when + await userAuthRepository.signIn( + userAuth: expectUserAuth, + ); - // then - expect(actualResult, false); + // then + expect(userAuthDataSource.methodParameter, expectUserAuthParameter); + }); }); - }); - group('signIn() 메소드는', () { - test('UserAuthDataSource.signIn() 메소드를 1회 호출한다.', () async { - // given - int expectCallCount = 1; + group('logOut() 메소드는', () { + test('UserAuthDataSource.logOut() 메소드를 1회 호출한다.', () async { + // given + int expectCallCount = 1; - // when - await userAuthRepository.signIn( - userAuth: userAuth, - ); + // when + await userAuthRepository.logOut(); - // then - expect(userAuthDataSource.signInCallCount, expectCallCount); + // then + expect(userAuthDataSource.logOutCallCount, expectCallCount); + }); }); - test('인자로 받은 값을 UserAuthDataSource.signIn() 메소드에 그대로 전달한다.', () async { - // given - final expectUserAuth = userAuth.copyWith(); + group('signOut() 메소드는', () { + test('UserAuthDataSource.signOut() 메소드를 1회 호출한다.', () async { + // given + int expectCallCount = 1; - final expectUserAuthParameter = { - 'email': expectUserAuth.email, - 'password': expectUserAuth.password, - }; - - // when - await userAuthRepository.signIn( - userAuth: expectUserAuth, - ); - - // then - expect(userAuthDataSource.methodParameter, expectUserAuthParameter); - }); - - test('로그인이 성공했을 때 true 를 반환한다.', () async { - // given - final expectUserAuth = userAuth.copyWith(); - - userAuthDataSource.returnValue = true; - - // when - final actualResult = await userAuthRepository.signIn( - userAuth: expectUserAuth, - ); + // when + await userAuthRepository.signOut(); - // then - expect(actualResult, true); - }); - - test('로그인이 성공했을 때 false 를 반환한다.', () async { - // given - final expectUserAuth = userAuth.copyWith(); - - userAuthDataSource.returnValue = false; - - // when - final actualResult = await userAuthRepository.signIn( - userAuth: expectUserAuth, - ); - - // then - expect(actualResult, false); - }); - }); - - group('logOut() 메소드는', () { - test('UserAuthDataSource.logOut() 메소드를 1회 호출한다.', () async { - // given - int expectCallCount = 1; - - // when - await userAuthRepository.logOut(); - - // then - expect(userAuthDataSource.logOutCallCount, expectCallCount); - }); - - test('로그아웃이 성공했을 때 true 를 반환한다.', () async { - // given - userAuthDataSource.returnValue = true; - - // when - final actualResult = await userAuthRepository.logOut(); - - // then - expect(actualResult, true); - }); - - test('로그아웃이 성공했을 때 false 를 반환한다.', () async { - // given - userAuthDataSource.returnValue = false; - - // when - final actualResult = await userAuthRepository.logOut(); - - // then - expect(actualResult, false); - }); - }); - - group('signOut() 메소드는', () { - test('UserAuthDataSource.signOut() 메소드를 1회 호출한다.', () async { - // given - int expectCallCount = 1; - - // when - await userAuthRepository.signOut(); - - // then - expect(userAuthDataSource.signOutCallCount, expectCallCount); - }); - - test('회원탈퇴가 성공했을 때 true 를 반환한다.', () async { - // given - userAuthDataSource.returnValue = true; - - // when - final actualResult = await userAuthRepository.signOut(); - - // then - expect(actualResult, true); - }); - - test('회원탈퇴가 성공했을 때 false 를 반환한다.', () async { - // given - userAuthDataSource.returnValue = false; - - // when - final actualResult = await userAuthRepository.signOut(); - - // then - expect(actualResult, false); + // then + expect(userAuthDataSource.signOutCallCount, expectCallCount); + }); }); }); }); diff --git a/test/domain/feed/use_case/remove_my_page_feed_use_case_test.dart b/test/domain/feed/use_case/remove_my_page_feed_use_case_test.dart index 4df7b726..1c283716 100644 --- a/test/domain/feed/use_case/remove_my_page_feed_use_case_test.dart +++ b/test/domain/feed/use_case/remove_my_page_feed_use_case_test.dart @@ -20,16 +20,15 @@ void main() { expect(mockOotdFeedRepository.removeOotdFeedParamId, expected); }); - test('OotdFeedRepository.removeOotdFeed를 호출하고 반환받은 값을 그대로 반환한다.', () async { + test('OotdFeedRepository.removeOotdFeed를 한번 호출한다.', () async { // Given - const expected = true; - mockOotdFeedRepository.removeOotdFeedReturnValue = expected; + const expected = 1; // When - final result = await useCase.execute(id: 'id'); + await useCase.execute(id: 'id'); // Then - expect(result, expected); + expect(mockOotdFeedRepository.removeOotdFeedCallCount, expected); }); }); }); diff --git a/test/domain/feed/use_case/save_edit_feed_use_case_test.dart b/test/domain/feed/use_case/save_edit_feed_use_case_test.dart index bfdcea11..280bfa4f 100644 --- a/test/domain/feed/use_case/save_edit_feed_use_case_test.dart +++ b/test/domain/feed/use_case/save_edit_feed_use_case_test.dart @@ -47,41 +47,6 @@ void main() { // Then expect(ootdFeedRepository.saveOotdFeedCallCount, expectedCallCount); }); - - test('OotdFeedRepository.saveFeed를 호출하고 반환받은 값을 그대로 반환한다.', () async { - // Given - const bool expected = true; - final editedFeed = Feed( - id: 'id', - imagePath: 'imagePath', - thumbnailImagePath: 'thumbnailImagePath', - userEmail: 'userEmail', - description: 'description', - weather: Weather( - temperature: 1, - timeTemperature: DateTime.now(), - code: 1, - createdAt: DateTime.now(), - ), - seasonCode: 1, - location: Location( - lat: 1, - lng: 1, - city: 'city', - createdAt: DateTime.now(), - ), - createdAt: DateTime.now(), - deletedAt: null, - ); - ootdFeedRepository.saveOotdFeedParamFeed = editedFeed; - ootdFeedRepository.saveOotdFeedReturnValue = expected; - - // When - final actual = await saveEditFeedUseCase.execute(feed: editedFeed); - - // Then - expect(actual, expected); - }); }); }); } diff --git a/test/domain/user/data_source/remote_user_profile_data_source_test.dart b/test/domain/user/data_source/remote_user_profile_data_source_test.dart index ed62ad44..1d3e0c79 100644 --- a/test/domain/user/data_source/remote_user_profile_data_source_test.dart +++ b/test/domain/user/data_source/remote_user_profile_data_source_test.dart @@ -33,11 +33,12 @@ void main() async { ); // When - final bool res = - await dataSource.saveUserProfile(userProfile: expectedUserProfile); + await dataSource.saveUserProfile(userProfile: expectedUserProfile); + + final actual = instance.collection('user_profiles').doc('email').get(); // Then - expect(res, true); + expect(actual, expectedUserProfile.email); }); test( 'getUserProfile()은 firebase storage에서 파라미터로 받은 이메일과 동일한 유저 프로필 정보를 반환한다.', @@ -123,7 +124,7 @@ void main() async { }); final editedUserProfile = UserProfile( - email: 'test@gmail.com', + email: 'test123@gmail.com', nickname: '테스트123', gender: 1, profileImagePath: @@ -133,11 +134,17 @@ void main() async { ); // When - final bool res = await dataSource.updateUserProfile( - userProfile: editedUserProfile); + await dataSource.updateUserProfile(userProfile: editedUserProfile); + + final actual = await instance + .collection('user_profiles') + .where('email', isEqualTo: 'test123@gmail.com') + .get(); + + final data = actual.docs.first.data()['feedCount']; // Then - expect(res, true); + expect(data, 1); }, ); test( @@ -156,10 +163,16 @@ void main() async { }); // When - final bool res = await dataSource.removeUserProfile(); + await dataSource.removeUserProfile(); + final actual = await instance + .collection('user_profiles') + .where('email', isEqualTo: 'test@gmail.com') + .get(); + + final data = actual.docs.first.data()['deleted_at']; // Then - expect(res, true); + expect(data != null, true); }, ); @@ -179,10 +192,16 @@ void main() async { }); // When - final bool res = await dataSource.removeUserProfile(); + await dataSource.removeUserProfile(); + final actual = await instance + .collection('user_profiles') + .where('email', isEqualTo: ' ${firebaseService.user?.email}') + .get(); + + final data = actual.docs.first.data()['deleted_at']; // Then - expect(res, true); + expect(data != null, true); }, ); }, diff --git a/test/mock/data/feed/repository/mock_feed_repository_impl.dart b/test/mock/data/feed/repository/mock_feed_repository_impl.dart index 6ce42160..89f80692 100644 --- a/test/mock/data/feed/repository/mock_feed_repository_impl.dart +++ b/test/mock/data/feed/repository/mock_feed_repository_impl.dart @@ -57,10 +57,10 @@ class MockFeedRepositoryImpl implements FeedRepository { /// [getFeedParamId]에 [id] 저장 /// [getFeedResult] 반환 @override - Future getFeed({required String id}) async { + Future getFeed({required String id}) async { getFeedCallCount++; methodParameterMap['id'] = id; - return getFeedResult; + return getFeedResult!; } /// [_fakeFeedList]에서 조건에 맞는 피드 데이터를 찾아서 리스트로 반환 From b879d227939328773631ed1e06a2e7b9d0e1cf33 Mon Sep 17 00:00:00 2001 From: "YunZi.Song" Date: Fri, 7 Jun 2024 10:54:42 +0900 Subject: [PATCH 25/32] =?UTF-8?q?Feat:=20data=20source=20=EB=8B=A8=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../user/use_case/log_out_use_case_test.dart | 22 +++--------- .../user/use_case/sign_in_use_case_test.dart | 36 ------------------- .../user/use_case/sign_out_use_case_test.dart | 24 ++++--------- .../mock_local_file_data_source_impl.dart | 4 +-- .../repository/mock_file_repository_impl.dart | 4 +-- 5 files changed, 15 insertions(+), 75 deletions(-) diff --git a/test/domain/user/use_case/log_out_use_case_test.dart b/test/domain/user/use_case/log_out_use_case_test.dart index e16cca62..f9850f1a 100644 --- a/test/domain/user/use_case/log_out_use_case_test.dart +++ b/test/domain/user/use_case/log_out_use_case_test.dart @@ -3,7 +3,6 @@ import 'package:weaco/domain/user/use_case/log_out_use_case.dart'; import '../../../mock/data/user/repository/mock_user_auth_repository_impl.dart'; - void main() { group('LogOutUseCase 클래스', () { final userAuthRepository = MockUserAuthRepositoryImpl(); @@ -13,25 +12,14 @@ void main() { setUp(() => userAuthRepository.initMockData()); group('logOut 메서드는', () { - test('로그아웃 실패를 반환한다.', () async { - // Given - userAuthRepository.isLogOut = false; - final expectedResult = await userAuthRepository.logOut(); - - // When - final actual = await useCase.execute(); - - // Then - expect(actual, expectedResult); - }); - - test('로그아웃 성공을 반환한다.', () async { + test('한 번 호출 된다.', () async { // Given - userAuthRepository.isLogOut = true; - final expectedResult = await userAuthRepository.logOut(); + int expectedResult = 1; + await userAuthRepository.logOut(); // When - final actual = await useCase.execute(); + await useCase.execute(); + final actual = userAuthRepository.logOutCallCount; // Then expect(actual, expectedResult); diff --git a/test/domain/user/use_case/sign_in_use_case_test.dart b/test/domain/user/use_case/sign_in_use_case_test.dart index bc9de1b8..af3a56dc 100644 --- a/test/domain/user/use_case/sign_in_use_case_test.dart +++ b/test/domain/user/use_case/sign_in_use_case_test.dart @@ -38,42 +38,6 @@ void main() { expect(mockUserAuthRepositoryImpl.methodParameter, expectParameter); }); - test('일치하는 유저가 없을시 false를 반환한다.', () async { - final expectParameter = UserAuth( - email: 'qoophon@gmail.com', - password: 'password', - ); - - mockUserAuthRepositoryImpl.addUserAuth( - UserAuth( - email: 'qoophon@gmail.com', - password: 'password1', - ), - ); - - final actualReturnValue = - await signInUseCase.execute(userAuth: expectParameter); - - expect(actualReturnValue, false); - }); - test('일치하는 유저가 있을 시 true를 반환한다.', () async { - final expectParameter = UserAuth( - email: 'qoophon@gmail.com', - password: 'password', - ); - - mockUserAuthRepositoryImpl.addUserAuth( - UserAuth( - email: 'qoophon@gmail.com', - password: 'password', - ), - ); - - final actualReturnValue = - await signInUseCase.execute(userAuth: expectParameter); - - expect(actualReturnValue, true); - }); }); }); } diff --git a/test/domain/user/use_case/sign_out_use_case_test.dart b/test/domain/user/use_case/sign_out_use_case_test.dart index c1d684da..35fe8376 100644 --- a/test/domain/user/use_case/sign_out_use_case_test.dart +++ b/test/domain/user/use_case/sign_out_use_case_test.dart @@ -9,31 +9,19 @@ void main() { final SignOutUseCase useCase = SignOutUseCase(userAuthRepository: userAuthRepository); - group('signOut 메서드는', () { + group('execute() 메서드는', () { setUp(() => userAuthRepository.initMockData()); - test('회원 탈퇴에 실패한다.', () async { + test('userAuthRepository.signOut() 메서드를 1회 호출한다.', () async { // Given - const bool expectedResult = false; - userAuthRepository.isSignOut = expectedResult; + const int expectedResult = 1; // When - final result = await useCase.execute(); + await useCase.execute(); + final actual = userAuthRepository.signOutCallCount; // Then - expect(result, expectedResult); - }); - - test('회원 탈퇴에 성공한다.', () async { - // Given - const bool expectedResult = true; - userAuthRepository.isSignOut = expectedResult; - - // When - final result = await useCase.execute(); - - // Then - expect(result, expectedResult); + expect(actual, expectedResult); }); }); }); diff --git a/test/mock/data/file/data_source/local/mock_local_file_data_source_impl.dart b/test/mock/data/file/data_source/local/mock_local_file_data_source_impl.dart index f883b1c5..36d318e3 100644 --- a/test/mock/data/file/data_source/local/mock_local_file_data_source_impl.dart +++ b/test/mock/data/file/data_source/local/mock_local_file_data_source_impl.dart @@ -14,7 +14,7 @@ class MockLocalFileDataSourceImpl implements LocalFileDataSource { } @override - Future getImage({required ImageType imageType}) async { + Future getImage({required ImageType imageType}) async { methodCallCount['getImage'] = (methodCallCount['getImage'] ?? 0) + 1; methodParameter['isOrigin'] = imageType; return methodResult['getImage']; @@ -29,7 +29,7 @@ class MockLocalFileDataSourceImpl implements LocalFileDataSource { } @override - Future getCompressedImage() { + Future getCompressedImage() { methodCallCount['getImage'] = (methodCallCount['getImage'] ?? 0) + 1; return methodResult['getImage']; } diff --git a/test/mock/data/file/repository/mock_file_repository_impl.dart b/test/mock/data/file/repository/mock_file_repository_impl.dart index bcfb508a..775ba6ce 100644 --- a/test/mock/data/file/repository/mock_file_repository_impl.dart +++ b/test/mock/data/file/repository/mock_file_repository_impl.dart @@ -20,9 +20,9 @@ class MockFileRepositoryImpl implements FileRepository { } @override - Future getImage({required ImageType imageType}) async { + Future getImage({required ImageType imageType}) async { getImageCallCount++; - return getImageResult; + return getImageResult!; } @override From d436aae45cbbc5e8e6133bfaa4cb7f8f8f4db172 Mon Sep 17 00:00:00 2001 From: hoogom88 Date: Fri, 7 Jun 2024 17:26:36 +0900 Subject: [PATCH 26/32] =?UTF-8?q?Feat:=20exception=20=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=EC=97=90=20=EB=94=B0=EB=A5=B8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BD=94=EB=93=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/db/transaction_service.dart | 2 +- lib/core/enum/exception_code.dart | 8 +-- .../local/local_file_data_source_impl.dart | 3 +- .../remote_user_profile_date_source_impl.dart | 58 +++++++++++-------- .../remote_data_source/meteo_weather_api.dart | 3 + .../remote_feed_data_source_impl_test.dart | 5 +- .../ootd_feed_repository_impl_test.dart | 2 +- .../local_file_data_source_test.dart | 14 ++--- .../repository/file_repository_impl_test.dart | 8 --- .../user_auth_repository_impl_test.dart | 20 ------- .../meteo_weather_api_test.dart | 9 ++- .../get_detail_feed_detail_use_case_test.dart | 22 +++++++ .../remove_my_page_feed_use_case_test.dart | 3 +- .../use_case/save_image_use_case_test.dart | 14 ----- .../remote_user_profile_data_source_test.dart | 3 +- .../user/use_case/log_out_use_case_test.dart | 1 - .../firebase/mock_firestore_service_impl.dart | 4 +- .../mock_remote_feed_data_source.dart | 6 +- .../mock_local_file_data_source_impl.dart | 1 + .../repository/mock_file_repository_impl.dart | 5 +- .../mock_remote_user_profile_data_source.dart | 9 +-- 21 files changed, 94 insertions(+), 106 deletions(-) diff --git a/lib/core/db/transaction_service.dart b/lib/core/db/transaction_service.dart index 42f92096..deadbdd4 100644 --- a/lib/core/db/transaction_service.dart +++ b/lib/core/db/transaction_service.dart @@ -1,3 +1,3 @@ abstract interface class TransactionService { - Future run(Function callBack); + Future run(Function callBack); } diff --git a/lib/core/enum/exception_code.dart b/lib/core/enum/exception_code.dart index ff5b3633..4fc45acd 100644 --- a/lib/core/enum/exception_code.dart +++ b/lib/core/enum/exception_code.dart @@ -1,4 +1,6 @@ -enum ExceptionCode { +import 'dart:io'; + +enum ExceptionCode implements Exception { unknownException( code: '500', errorMessage: '알 수 없는 오류 발생', @@ -88,10 +90,8 @@ enum ExceptionCode { }); static ExceptionCode fromStatus(String status) { + if (int.tryParse(status) != null) return ExceptionCode.internalServerException; return switch (status) { - '500' => ExceptionCode.unknownException, - '400' => ExceptionCode.internalServerException, - '404' => ExceptionCode.notFoundException, 'user-not-found' => ExceptionCode.userNotFoundException, 'wrong-password' => ExceptionCode.wrongPasswordException, 'email-already-in-use' => ExceptionCode.emailAlreadyInUseException, diff --git a/lib/data/file/data_source/local/local_file_data_source_impl.dart b/lib/data/file/data_source/local/local_file_data_source_impl.dart index 060a95e4..c7240887 100644 --- a/lib/data/file/data_source/local/local_file_data_source_impl.dart +++ b/lib/data/file/data_source/local/local_file_data_source_impl.dart @@ -24,7 +24,7 @@ class LocalFileDataSourceImpl implements LocalFileDataSource { ImageType.cropped => _croppedImageFileName, ImageType.compressed => _compressedImageFileName, }; - await File('$directory/$fileName').exists(); + if (!await File('$directory/$fileName').exists()) throw ExceptionCode.notFoundException; return File('$directory/$fileName'); } catch (e) { @@ -75,6 +75,7 @@ class LocalFileDataSourceImpl implements LocalFileDataSource { return switch (e) { FirebaseException _ => ExceptionCode.internalServerException, DioException _ => ExceptionCode.internalServerException, + ExceptionCode _ => e, _ => ExceptionCode.unknownException, }; } diff --git a/lib/data/user/data_source/remote_user_profile_date_source_impl.dart b/lib/data/user/data_source/remote_user_profile_date_source_impl.dart index f8b336be..e234deca 100644 --- a/lib/data/user/data_source/remote_user_profile_date_source_impl.dart +++ b/lib/data/user/data_source/remote_user_profile_date_source_impl.dart @@ -1,4 +1,8 @@ import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:dio/dio.dart'; +import 'package:weaco/core/enum/exception_code.dart'; +import 'package:weaco/core/exception/internal_server_exception.dart'; +import 'package:weaco/core/exception/network_exception.dart'; import 'package:weaco/core/firebase/firebase_auth_service.dart'; import 'package:weaco/core/firebase/firestore_dto_mapper.dart'; import 'package:weaco/data/user/data_source/remote_user_profile_data_source.dart'; @@ -11,7 +15,7 @@ class RemoteUserProfileDataSourceImpl implements RemoteUserProfileDataSource { const RemoteUserProfileDataSourceImpl({ required FirebaseFirestore firestore, required FirebaseAuthService firebaseService, - }) : _firestore = firestore, + }) : _firestore = firestore, _firebaseService = firebaseService; @override @@ -19,30 +23,26 @@ class RemoteUserProfileDataSourceImpl implements RemoteUserProfileDataSource { try { await _firestore .collection('user_profiles') - .add(toUserProfileDto(userProfile: userProfile)) - .then((value) => true) - .catchError( - (e) => false, - ); + .add(toUserProfileDto(userProfile: userProfile)); } catch (e) { - throw Exception(e); + _exceptionHandling(e); } } @override Future getUserProfile({String? email}) async { - email = email ?? _firebaseService.firebaseAuth.currentUser!.email; + try { + email = email ?? _firebaseService.firebaseAuth.currentUser!.email; - QuerySnapshot> snapshot = await _firestore - .collection('user_profiles') - .where('email', isEqualTo: email) - .get(); + QuerySnapshot> snapshot = await _firestore + .collection('user_profiles') + .where('email', isEqualTo: email) + .get(); - if (snapshot.docs.isEmpty) { - throw Exception('유저 프로필이 존재하지 않습니다.'); + return toUserProfile(json: snapshot.docs[0].data()); + } catch (e) { + throw _exceptionHandling(e); } - - return toUserProfile(json: snapshot.docs[0].data()); } @override @@ -65,7 +65,7 @@ class RemoteUserProfileDataSourceImpl implements RemoteUserProfileDataSource { transaction.set( originProfileDocRef, toUserProfileDto(userProfile: userProfile)); } catch (e) { - throw Exception(e); + _exceptionHandling(e); } } @@ -82,13 +82,23 @@ class RemoteUserProfileDataSourceImpl implements RemoteUserProfileDataSource { await _firestore .collection('user_profiles') .doc(originProfileDocument.docs[0].reference.id) - .delete() - .then((value) => true) - .catchError( - (e) => false, - ); + .set(toUserProfileDto(userProfile: toUserProfile(json: originProfileDocument.docs.first.data()).copyWith(deletedAt: DateTime.now()))); } catch (e) { - throw Exception(e); + _exceptionHandling(e); + } + } + + Exception _exceptionHandling(Object e) { + switch (e.runtimeType) { + case FirebaseException _: + return InternalServerException( + code: ExceptionCode.internalServerException, message: '서버 내부 오류'); + case DioException _: + return NetworkException( + code: ExceptionCode.internalServerException, + message: '네트워크 오류 : $e'); + default: + return Exception(e); } } -} +} \ No newline at end of file diff --git a/lib/data/weather/data_source/remote_data_source/meteo_weather_api.dart b/lib/data/weather/data_source/remote_data_source/meteo_weather_api.dart index d0ce064b..610deec5 100644 --- a/lib/data/weather/data_source/remote_data_source/meteo_weather_api.dart +++ b/lib/data/weather/data_source/remote_data_source/meteo_weather_api.dart @@ -30,8 +30,11 @@ class MeteoWeatherApi implements RemoteWeatherDataSource { if (response.statusCode != 200) { log('Code: ${response.statusCode}, Body: ${response.body}'); throw ExceptionCode.fromStatus(response.statusCode.toString()); + // throw InternalServerException(code: ExceptionCode.internalServerException, message: 'sdf'); } return WeatherDto.fromJson(json: response.body); + } on ExceptionCode catch (_) { + rethrow; } catch (e) { throw ExceptionCode.unknownException; } diff --git a/test/data/feed/data_source/remote_feed_data_source_impl_test.dart b/test/data/feed/data_source/remote_feed_data_source_impl_test.dart index 213cab50..60ee1b69 100644 --- a/test/data/feed/data_source/remote_feed_data_source_impl_test.dart +++ b/test/data/feed/data_source/remote_feed_data_source_impl_test.dart @@ -58,10 +58,11 @@ void main() { ); }); final snapshot = - fakeFirestore.collection('feeds').doc(mockFeed.id).get(); + await fakeFirestore.collection('feeds').doc(mockFeed.id).get(); // Then - expect(mockFeed, snapshot); + expect(mockFeed.userEmail, snapshot['user_email']); }); + test( 'Firestore에 데이터를 추가해야 한다.', () async { diff --git a/test/data/feed/repository/ootd_feed_repository_impl_test.dart b/test/data/feed/repository/ootd_feed_repository_impl_test.dart index 52786c9e..8aed18bb 100644 --- a/test/data/feed/repository/ootd_feed_repository_impl_test.dart +++ b/test/data/feed/repository/ootd_feed_repository_impl_test.dart @@ -151,7 +151,7 @@ void main() { expectedUseProfile); }); - + test( '파라미터로 전달받은 Feed의 id값이 있으면 수정하는 Feed로써' 'RemoteFeedDataSourceImpl.saveFeed()를 한번 호출한다.', () async { diff --git a/test/data/file/data_source/local_file_data_source_test.dart b/test/data/file/data_source/local_file_data_source_test.dart index e543db38..87426d2a 100644 --- a/test/data/file/data_source/local_file_data_source_test.dart +++ b/test/data/file/data_source/local_file_data_source_test.dart @@ -1,5 +1,6 @@ import 'dart:io'; import 'package:flutter_test/flutter_test.dart'; +import 'package:weaco/core/enum/exception_code.dart'; import 'package:weaco/core/enum/image_type.dart'; import 'package:weaco/core/path_provider/path_provider_service.dart'; import 'package:weaco/data/file/data_source/local/local_file_data_source.dart'; @@ -14,8 +15,8 @@ void main() { final LocalFileDataSource dataSource = LocalFileDataSourceImpl(pathProvider: mockPathProvider); - group('getImagePath 메서드는', () { - tearDown(() { + group('getImage 메서드는', () { + setUp(() { if (File('test/mock/assets/origin.png').existsSync()) { File('test/mock/assets/origin.png').deleteSync(); } @@ -53,15 +54,12 @@ void main() { File('test/mock/assets/cropped.png').readAsBytesSync()); }); - test('찾으려는 파일이 없는 경우, null을 반환한다.', () async { + test('찾으려는 파일이 없는 경우, ExceptionCode.notFoundException 예외를 던진다.', () async { // Given const imageType = ImageType.cropped; - // When - File? file = await dataSource.getImage(imageType: imageType); - - // Then - expect(file, null); + // When, Then + expect(dataSource.getImage(imageType: imageType), throwsA(ExceptionCode.notFoundException)); }); }); diff --git a/test/data/file/repository/file_repository_impl_test.dart b/test/data/file/repository/file_repository_impl_test.dart index d5ed4b18..bc3aed20 100644 --- a/test/data/file/repository/file_repository_impl_test.dart +++ b/test/data/file/repository/file_repository_impl_test.dart @@ -129,14 +129,6 @@ void main() { expect(mockLocalFileDataSource.methodCallCount['getImage'], 2); }); - test('LocalFileDataSource.getImage()의 반환 값이 null이라면 Exception을 발생시킨다.', () async { - // Given - mockLocalFileDataSource.methodResult['getImage'] = null; - - // When // Then - expect(fileRepository.saveOotdImage(),throwsA(isA())); - }); - test('RemoteFileDataSource.saveImage()의 반환 값을 그대로 반환한다.', () async { // Given const List expectResult = ['test/mock/assets/test_image.png', 'test/mock/assets/test_image.png']; diff --git a/test/data/user/repository/user_auth_repository_impl_test.dart b/test/data/user/repository/user_auth_repository_impl_test.dart index 749cd1d2..524628b9 100644 --- a/test/data/user/repository/user_auth_repository_impl_test.dart +++ b/test/data/user/repository/user_auth_repository_impl_test.dart @@ -52,26 +52,6 @@ void main() { expect(userAuthDataSource.signUpCallCount, expectCallCount); }); - test( - 'UserAuthDataSource.signUp() 이 실패하면' - 'RemoteUserProfileDataSource.saveUserProfile() 메소드를' - '호출하지 않는다.', () async { - // given - int expectCallCount = 0; - - userAuthDataSource.returnValue = false; - - // when - await userAuthRepository.signUp( - userAuth: userAuth, - userProfile: userProfile, - ); - - // then - expect(userProfileDataSource.saveUserProfileMethodCallCount, - expectCallCount); - }); - test( 'UserAuthDataSource.signUp() 이 성공하면' 'RemoteUserProfileDataSource.saveUserProfile() 메소드를' diff --git a/test/data/weather/data_source/remote_data_source/meteo_weather_api_test.dart b/test/data/weather/data_source/remote_data_source/meteo_weather_api_test.dart index 797a4639..e08d1c98 100644 --- a/test/data/weather/data_source/remote_data_source/meteo_weather_api_test.dart +++ b/test/data/weather/data_source/remote_data_source/meteo_weather_api_test.dart @@ -2,8 +2,7 @@ import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:weaco/core/config/meteo_config.dart'; import 'package:weaco/core/dio/base_response.dart'; -import 'package:weaco/core/exception/internal_server_exception.dart'; -import 'package:weaco/core/exception/not_found_exception.dart'; +import 'package:weaco/core/enum/exception_code.dart'; import 'package:weaco/data/weather/data_source/remote_data_source/meteo_weather_api.dart'; import 'package:weaco/data/weather/dto/daily_dto.dart'; import 'package:weaco/data/weather/dto/hourly_dto.dart'; @@ -39,14 +38,14 @@ Future main() async { expect(mockMeteoDio.getPathParameter, expectedUrl); }); - test('반환받은 status code가 404라면 NotFoundException예외를 던진다.', () async { + test('반환받은 status code가 200이 아니라면 ExceptionCode예외를 던진다.', () async { // Given mockMeteoDio.getResponseReturnData = BaseResponse(statusCode: 404, body: {}); // When Then expect(meteoWeatherApi.getWeather(lat: lat, lng: lng), - throwsA(isA())); + throwsA(ExceptionCode.internalServerException)); }); test('반환받은 status code가 500이라면 InternalServerException예외를 던진다.', @@ -57,7 +56,7 @@ Future main() async { // When Then expect(meteoWeatherApi.getWeather(lat: lat, lng: lng), - throwsA(isA())); + throwsA(ExceptionCode.internalServerException)); }); test('반환받은 status code가 200이라면 BaseResponse를 반환한다.', () async { diff --git a/test/domain/feed/use_case/get_detail_feed_detail_use_case_test.dart b/test/domain/feed/use_case/get_detail_feed_detail_use_case_test.dart index 2795b48d..48739a57 100644 --- a/test/domain/feed/use_case/get_detail_feed_detail_use_case_test.dart +++ b/test/domain/feed/use_case/get_detail_feed_detail_use_case_test.dart @@ -20,6 +20,28 @@ void main() { test('파라미터로 받은 id를 FeedRepository.getFeed에 넘긴다.', () async { // Given const String expectedId = 'id'; + feedRepository.getFeedResult = Feed( + id: 'id', + imagePath: 'imagePath', + thumbnailImagePath: 'thumbnailImagePath', + userEmail: 'userEmail', + description: 'description', + weather: Weather( + temperature: 1, + timeTemperature: DateTime.now(), + code: 1, + createdAt: DateTime.now(), + ), + seasonCode: 1, + location: Location( + lat: 1, + lng: 1, + city: 'city', + createdAt: DateTime.now(), + ), + createdAt: DateTime.now(), + deletedAt: null, + ); // When await getDetailFeedDetailUseCase.execute(id: expectedId); diff --git a/test/domain/feed/use_case/remove_my_page_feed_use_case_test.dart b/test/domain/feed/use_case/remove_my_page_feed_use_case_test.dart index 1c283716..8fd6881d 100644 --- a/test/domain/feed/use_case/remove_my_page_feed_use_case_test.dart +++ b/test/domain/feed/use_case/remove_my_page_feed_use_case_test.dart @@ -7,11 +7,12 @@ void main() { final mockOotdFeedRepository = MockOotdFeedRepositoryImpl(); final RemoveMyPageFeedUseCase useCase = RemoveMyPageFeedUseCase(ootdFeedRepository: mockOotdFeedRepository); - + setUp(() => mockOotdFeedRepository.initMockData()); group('execute 메소드는', () { test('파라미터로 전달받은 id를 OotdFeedRepository에 그대로 전달한다.', () async { // Given const expected = 'id'; + // mockOotdFeedRepository. // When await useCase.execute(id: expected); diff --git a/test/domain/file/use_case/save_image_use_case_test.dart b/test/domain/file/use_case/save_image_use_case_test.dart index 29dd0c19..9e993daf 100644 --- a/test/domain/file/use_case/save_image_use_case_test.dart +++ b/test/domain/file/use_case/save_image_use_case_test.dart @@ -37,20 +37,6 @@ void main() { // Then expect(mockFileRepository.methodParameterMap['data'], data); }); - - test('FileRepository.saveImage()을 호출하고 반환 받은 값을 그대로 반환한다.', () async { - // Given - File data = File('flutter-wea-co\\test\\mock\\assets\\test_file.txt'); - bool expectResult = false; - mockFileRepository.saveImageResult = expectResult; - - // When - final result = - await mockFileRepository.saveImage(isOrigin: true, file: data); - - // Then - expect(result, expectResult); - }); }); }); } diff --git a/test/domain/user/data_source/remote_user_profile_data_source_test.dart b/test/domain/user/data_source/remote_user_profile_data_source_test.dart index 160abbab..2bd99b0e 100644 --- a/test/domain/user/data_source/remote_user_profile_data_source_test.dart +++ b/test/domain/user/data_source/remote_user_profile_data_source_test.dart @@ -189,6 +189,7 @@ void main() async { 'removeUserProfile()은 userProfile 이 null 일 경우, 현재 유저 프로필 정보를 삭제한다.', () async { // Given + final tmp = firebaseService.user?.email; await instance.collection('user_profiles').add({ 'created_at': '2024-05-08 02:27:00', 'deleted_at': null, @@ -204,7 +205,7 @@ void main() async { await dataSource.removeUserProfile(); final actual = await instance .collection('user_profiles') - .where('email', isEqualTo: ' ${firebaseService.user?.email}') + .where('email', isEqualTo: firebaseService.user?.email) .get(); final data = actual.docs.first.data()['deleted_at']; diff --git a/test/domain/user/use_case/log_out_use_case_test.dart b/test/domain/user/use_case/log_out_use_case_test.dart index f9850f1a..d0690745 100644 --- a/test/domain/user/use_case/log_out_use_case_test.dart +++ b/test/domain/user/use_case/log_out_use_case_test.dart @@ -15,7 +15,6 @@ void main() { test('한 번 호출 된다.', () async { // Given int expectedResult = 1; - await userAuthRepository.logOut(); // When await useCase.execute(); diff --git a/test/mock/core/firebase/mock_firestore_service_impl.dart b/test/mock/core/firebase/mock_firestore_service_impl.dart index 4c06ace9..b3178603 100644 --- a/test/mock/core/firebase/mock_firestore_service_impl.dart +++ b/test/mock/core/firebase/mock_firestore_service_impl.dart @@ -5,8 +5,8 @@ class MockFirestoreServiceImpl implements TransactionService { final _fakeFirestore = FakeFirebaseFirestore(); @override - Future run(Function callBack) async { - return await _fakeFirestore.runTransaction((transaction) async { + Future run(Function callBack) async { + return await _fakeFirestore.runTransaction((transaction) async { return await callBack(transaction); }).then( (value) => true, diff --git a/test/mock/data/feed/data_source/mock_remote_feed_data_source.dart b/test/mock/data/feed/data_source/mock_remote_feed_data_source.dart index 766dce0f..cd4402af 100644 --- a/test/mock/data/feed/data_source/mock_remote_feed_data_source.dart +++ b/test/mock/data/feed/data_source/mock_remote_feed_data_source.dart @@ -26,13 +26,12 @@ class MockRemoteFeedDataSource implements RemoteFeedDataSource { } @override - Future deleteFeed({ + Future deleteFeed({ required Transaction transaction, required String id, }) async { deleteFeedParamId = id; deleteFeedMethodCallCount++; - return deleteFeedReturnValue; } @override @@ -76,7 +75,7 @@ class MockRemoteFeedDataSource implements RemoteFeedDataSource { } @override - Future saveFeed({ + Future saveFeed({ required Transaction transaction, required Feed feed, }) async { @@ -87,6 +86,5 @@ class MockRemoteFeedDataSource implements RemoteFeedDataSource { if (saveFeedReturnValue) { feedList.add(feed); } - return saveFeedReturnValue; } } diff --git a/test/mock/data/file/data_source/local/mock_local_file_data_source_impl.dart b/test/mock/data/file/data_source/local/mock_local_file_data_source_impl.dart index 36d318e3..ea1e736b 100644 --- a/test/mock/data/file/data_source/local/mock_local_file_data_source_impl.dart +++ b/test/mock/data/file/data_source/local/mock_local_file_data_source_impl.dart @@ -11,6 +11,7 @@ class MockLocalFileDataSourceImpl implements LocalFileDataSource { methodResult.clear(); methodParameter.clear(); methodCallCount.clear(); + methodResult['getImage'] = File('test/mock/assets/origin.png'); } @override diff --git a/test/mock/data/file/repository/mock_file_repository_impl.dart b/test/mock/data/file/repository/mock_file_repository_impl.dart index 775ba6ce..fafdeca0 100644 --- a/test/mock/data/file/repository/mock_file_repository_impl.dart +++ b/test/mock/data/file/repository/mock_file_repository_impl.dart @@ -7,7 +7,7 @@ class MockFileRepositoryImpl implements FileRepository { int saveImageCallCount = 0; int saveOotdImageCallCount = 0; final Map methodParameterMap = {}; - File? getImageResult; + File getImageResult = File('test/mock/assets/cropped.png'); List saveOotdImageResult = ['', '']; bool saveImageResult = false; @@ -26,10 +26,9 @@ class MockFileRepositoryImpl implements FileRepository { } @override - Future saveImage({required bool isOrigin, required File file, List? compressedImage}) async { + Future saveImage({required bool isOrigin, required File file, List? compressedImage}) async { saveImageCallCount++; methodParameterMap['data'] = file; - return saveImageResult; } @override diff --git a/test/mock/data/user/data_source/mock_remote_user_profile_data_source.dart b/test/mock/data/user/data_source/mock_remote_user_profile_data_source.dart index 43c9b390..0f76b216 100644 --- a/test/mock/data/user/data_source/mock_remote_user_profile_data_source.dart +++ b/test/mock/data/user/data_source/mock_remote_user_profile_data_source.dart @@ -37,26 +37,23 @@ class MockRemoteUserProfileDataSourceImpl } @override - Future updateUserProfile({ + Future updateUserProfile({ required Transaction transaction, UserProfile? userProfile, }) async { updateUserProfileMethodCallCount++; methodUserProfileParameter = userProfile; - return isUpdated; } @override - Future saveUserProfile({required UserProfile userProfile}) async { + Future saveUserProfile({required UserProfile userProfile}) async { saveUserProfileMethodCallCount++; methodUserProfileParameter = userProfile; - return isSaved; } @override - Future removeUserProfile({String? email}) async { + Future removeUserProfile({String? email}) async { removeUserProfileMethodCallCount++; methodEmailParameter = email; - return isRemoved; } } From cd11d3c5de76a3b2a976799f1bc44e0a5f15c32a Mon Sep 17 00:00:00 2001 From: SeongJongHo Date: Sat, 8 Jun 2024 15:25:05 +0900 Subject: [PATCH 27/32] =?UTF-8?q?Modify:=20=EC=82=AC=EC=9A=A9=20=EC=95=88?= =?UTF-8?q?=ED=95=98=EB=8A=94=20import=EB=AC=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/enum/exception_code.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/core/enum/exception_code.dart b/lib/core/enum/exception_code.dart index 4fc45acd..fb07d475 100644 --- a/lib/core/enum/exception_code.dart +++ b/lib/core/enum/exception_code.dart @@ -1,5 +1,3 @@ -import 'dart:io'; - enum ExceptionCode implements Exception { unknownException( code: '500', From ddf410ab972e6c12d22095d92a24e51e0a808396 Mon Sep 17 00:00:00 2001 From: SeongJongHo Date: Sat, 8 Jun 2024 15:25:45 +0900 Subject: [PATCH 28/32] =?UTF-8?q?Refactor:=20=EC=9E=AC=ED=95=A0=EB=8B=B9?= =?UTF-8?q?=20=EC=95=88=EB=90=98=EB=8A=94=20=EB=B3=80=EC=88=98=20final?= =?UTF-8?q?=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ootd_post/view_model/picutre_crop_view_model.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/presentation/ootd_post/view_model/picutre_crop_view_model.dart b/lib/presentation/ootd_post/view_model/picutre_crop_view_model.dart index 385ff025..203dcae6 100644 --- a/lib/presentation/ootd_post/view_model/picutre_crop_view_model.dart +++ b/lib/presentation/ootd_post/view_model/picutre_crop_view_model.dart @@ -17,7 +17,7 @@ enum PictureCropSaveStatus { class PictureCropViewModel with ChangeNotifier { final SaveImageUseCase _saveImageUseCase; - PictureCropSaveStatus _status = PictureCropSaveStatus.idle; + final PictureCropSaveStatus _status = PictureCropSaveStatus.idle; PictureCropViewModel({ required SaveImageUseCase saveImageUseCase, From 35e23dfe3ac3024052845ac5ed3f1878aeb03967 Mon Sep 17 00:00:00 2001 From: SeongJongHo Date: Sat, 8 Jun 2024 15:26:14 +0900 Subject: [PATCH 29/32] =?UTF-8?q?Refactor:=20firestore=20set=20=3D>=20upda?= =?UTF-8?q?te=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data_source/remote_user_profile_date_source_impl.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/data/user/data_source/remote_user_profile_date_source_impl.dart b/lib/data/user/data_source/remote_user_profile_date_source_impl.dart index e234deca..8046d964 100644 --- a/lib/data/user/data_source/remote_user_profile_date_source_impl.dart +++ b/lib/data/user/data_source/remote_user_profile_date_source_impl.dart @@ -15,7 +15,7 @@ class RemoteUserProfileDataSourceImpl implements RemoteUserProfileDataSource { const RemoteUserProfileDataSourceImpl({ required FirebaseFirestore firestore, required FirebaseAuthService firebaseService, - }) : _firestore = firestore, + }) : _firestore = firestore, _firebaseService = firebaseService; @override @@ -82,7 +82,7 @@ class RemoteUserProfileDataSourceImpl implements RemoteUserProfileDataSource { await _firestore .collection('user_profiles') .doc(originProfileDocument.docs[0].reference.id) - .set(toUserProfileDto(userProfile: toUserProfile(json: originProfileDocument.docs.first.data()).copyWith(deletedAt: DateTime.now()))); + .update({'deleted_at': Timestamp.now()}); } catch (e) { _exceptionHandling(e); } @@ -101,4 +101,4 @@ class RemoteUserProfileDataSourceImpl implements RemoteUserProfileDataSource { return Exception(e); } } -} \ No newline at end of file +} From 9d2970c95c2f91417f6134c83ef2550c4b2f6d5e Mon Sep 17 00:00:00 2001 From: SeongJongHo Date: Sat, 8 Jun 2024 15:26:33 +0900 Subject: [PATCH 30/32] =?UTF-8?q?Refactor:=20emial=20=ED=8C=8C=EB=9D=BC?= =?UTF-8?q?=EB=AF=B8=ED=84=B0=20=EC=9D=B8=ED=84=B0=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=8A=A4=EC=97=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/user/data_source/remote_user_profile_data_source.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/data/user/data_source/remote_user_profile_data_source.dart b/lib/data/user/data_source/remote_user_profile_data_source.dart index 5832d226..3ebdf2ff 100644 --- a/lib/data/user/data_source/remote_user_profile_data_source.dart +++ b/lib/data/user/data_source/remote_user_profile_data_source.dart @@ -16,5 +16,5 @@ abstract interface class RemoteUserProfileDataSource { Future saveUserProfile({required UserProfile userProfile}); /// 유저 프로필 삭제 - Future removeUserProfile(); + Future removeUserProfile({String? email}); } From f902caa03fad8f715f41ba50a515df0187a5495a Mon Sep 17 00:00:00 2001 From: SeongJongHo Date: Sat, 8 Jun 2024 15:27:20 +0900 Subject: [PATCH 31/32] =?UTF-8?q?Feat:=20transaction=EC=97=90=20=EB=B9=84?= =?UTF-8?q?=EB=8F=99=EA=B8=B0=EC=B2=98=EB=A6=AC=20await=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/feed/repository/ootd_feed_repository_impl.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/data/feed/repository/ootd_feed_repository_impl.dart b/lib/data/feed/repository/ootd_feed_repository_impl.dart index 048e42ee..3d8dccb9 100644 --- a/lib/data/feed/repository/ootd_feed_repository_impl.dart +++ b/lib/data/feed/repository/ootd_feed_repository_impl.dart @@ -27,7 +27,7 @@ class OotdFeedRepositoryImpl implements OotdFeedRepository { /// 피드 저장 및 수정 @override Future saveOotdFeed({required Feed feed}) async { - _firestoreService.run((Transaction transaction) async { + await _firestoreService.run((Transaction transaction) async { feed.id == null ? await _save(transaction: transaction, feed: feed) : await _update(transaction: transaction, feed: feed); @@ -66,7 +66,7 @@ class OotdFeedRepositoryImpl implements OotdFeedRepository { /// 피드 삭제 @override Future removeOotdFeed({required String id}) async { - _firestoreService.run((Transaction transaction) async { + await _firestoreService.run((Transaction transaction) async { await _remoteFeedDataSource.deleteFeed( transaction: transaction, id: id, From b31ced6e9b55209105b8ee4c003d37da997ae68d Mon Sep 17 00:00:00 2001 From: SeongJongHo Date: Sat, 8 Jun 2024 15:28:35 +0900 Subject: [PATCH 32/32] =?UTF-8?q?Test:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F=20?= =?UTF-8?q?Mock=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ootd_feed_repository_impl_test.dart | 5 +- .../remote_user_profile_data_source_test.dart | 50 ++----------------- .../firebase/mock_firestore_service_impl.dart | 10 ++-- .../repository/mock_file_repository_impl.dart | 2 +- .../mock_remote_user_profile_data_source.dart | 4 +- 5 files changed, 14 insertions(+), 57 deletions(-) diff --git a/test/data/feed/repository/ootd_feed_repository_impl_test.dart b/test/data/feed/repository/ootd_feed_repository_impl_test.dart index 8aed18bb..21f70812 100644 --- a/test/data/feed/repository/ootd_feed_repository_impl_test.dart +++ b/test/data/feed/repository/ootd_feed_repository_impl_test.dart @@ -50,7 +50,7 @@ void main() { ); final mockUserProfile = UserProfile( - email: 'email', + email: 'test@email.com', nickname: 'nickname', gender: 1, profileImagePath: 'profileImagePath', @@ -62,7 +62,6 @@ void main() { fileRepository.initMockData(); remoteFeedDataSource.cleanUpMockData(); remoteUserProfileDatSource.initMockData(); - remoteUserProfileDatSource.getUserProfileResult = mockUserProfile; }); @@ -151,7 +150,6 @@ void main() { expectedUseProfile); }); - test( '파라미터로 전달받은 Feed의 id값이 있으면 수정하는 Feed로써' 'RemoteFeedDataSourceImpl.saveFeed()를 한번 호출한다.', () async { @@ -243,6 +241,7 @@ void main() { mockUserProfile.copyWith(feedCount: mockUserProfile.feedCount - 1); // When + // remove 메소드에 run transaction에 await 없었음 await ootdFeedRepository.removeOotdFeed(id: feedId); // Then diff --git a/test/domain/user/data_source/remote_user_profile_data_source_test.dart b/test/domain/user/data_source/remote_user_profile_data_source_test.dart index 2bd99b0e..06293c8e 100644 --- a/test/domain/user/data_source/remote_user_profile_data_source_test.dart +++ b/test/domain/user/data_source/remote_user_profile_data_source_test.dart @@ -112,58 +112,15 @@ void main() async { expect(result, expectProfile); }, ); - - test( - 'updateUserProfile()은 userProfile 이 null 일 경우, 현재 유저 프로필 정보를 업데이트한다.', - () async { - // Given - await instance.collection('user_profiles').add({ - 'created_at': '2024-05-01 13:27:00', - 'deleted_at': null, - 'email': 'test@gmail.com', - 'feed_count': 0, - 'gender': 1, - 'nickname': '호구몬', - 'profile_image_path': - 'https://health.chosun.com/site/data/img_dir/2024/01/22/2024012201607_0.jpg' - }); - - final editedUserProfile = UserProfile( - email: 'test123@gmail.com', - nickname: '테스트123', - gender: 1, - profileImagePath: - 'https://health.chosun.com/site/data/img_dir/2024/01/22/2024012201607_0.jpg', - feedCount: 0, - createdAt: DateTime.parse('2024-05-01 13:27:00'), - ); - - // When - await instance.runTransaction((transaction) async { - await dataSource.updateUserProfile( - transaction: transaction, - userProfile: editedUserProfile, - ); - }); - - final actual = await instance - .collection('user_profiles') - .where('email', isEqualTo: 'test123@gmail.com') - .get(); - - final data = actual.docs.first.data()['feedCount']; - // Then - expect(data, 1); - }, - ); test( 'removeUserProfile()은 firebase storage에서 파라미터로 받은 이메일과 동일한 유저 프로필 정보를 삭제한다.', () async { // Given + const email = 'test@gmail.com'; await instance.collection('user_profiles').add({ 'created_at': '2024-05-01 13:27:00', 'deleted_at': null, - 'email': 'test@gmail.com', + 'email': email, 'feed_count': 0, 'gender': 1, 'nickname': '호구몬', @@ -172,7 +129,7 @@ void main() async { }); // When - await dataSource.removeUserProfile(); + await dataSource.removeUserProfile(email: email); final actual = await instance .collection('user_profiles') .where('email', isEqualTo: 'test@gmail.com') @@ -189,7 +146,6 @@ void main() async { 'removeUserProfile()은 userProfile 이 null 일 경우, 현재 유저 프로필 정보를 삭제한다.', () async { // Given - final tmp = firebaseService.user?.email; await instance.collection('user_profiles').add({ 'created_at': '2024-05-08 02:27:00', 'deleted_at': null, diff --git a/test/mock/core/firebase/mock_firestore_service_impl.dart b/test/mock/core/firebase/mock_firestore_service_impl.dart index b3178603..a4df879c 100644 --- a/test/mock/core/firebase/mock_firestore_service_impl.dart +++ b/test/mock/core/firebase/mock_firestore_service_impl.dart @@ -6,13 +6,15 @@ class MockFirestoreServiceImpl implements TransactionService { @override Future run(Function callBack) async { - return await _fakeFirestore.runTransaction((transaction) async { - return await callBack(transaction); + await _fakeFirestore.runTransaction((transaction) async { + await callBack(transaction); }).then( - (value) => true, + (value) { + return; + }, onError: (e) { // throw Exception('피드 업로드에 실패 하였습니다.'); - throw Exception(e); + throw Exception('mockFireStore: $e'); }, ); } diff --git a/test/mock/data/file/repository/mock_file_repository_impl.dart b/test/mock/data/file/repository/mock_file_repository_impl.dart index fafdeca0..bd8e7fd5 100644 --- a/test/mock/data/file/repository/mock_file_repository_impl.dart +++ b/test/mock/data/file/repository/mock_file_repository_impl.dart @@ -22,7 +22,7 @@ class MockFileRepositoryImpl implements FileRepository { @override Future getImage({required ImageType imageType}) async { getImageCallCount++; - return getImageResult!; + return getImageResult; } @override diff --git a/test/mock/data/user/data_source/mock_remote_user_profile_data_source.dart b/test/mock/data/user/data_source/mock_remote_user_profile_data_source.dart index 0f76b216..42b3b0cb 100644 --- a/test/mock/data/user/data_source/mock_remote_user_profile_data_source.dart +++ b/test/mock/data/user/data_source/mock_remote_user_profile_data_source.dart @@ -30,10 +30,10 @@ class MockRemoteUserProfileDataSourceImpl } @override - Future getUserProfile({String? email}) { + Future getUserProfile({String? email}) async { getUserProfileMethodCallCount++; methodEmailParameter = email; - return Future.value(getUserProfileResult); + return getUserProfileResult!; } @override