diff --git a/lib/core/http/api_client/abstract_api_client.dart b/lib/core/http/api_client/abstract_api_client.dart index f41053cc..952ce0ad 100644 --- a/lib/core/http/api_client/abstract_api_client.dart +++ b/lib/core/http/api_client/abstract_api_client.dart @@ -23,4 +23,15 @@ abstract class AbstractApiClient { ProgressCallback onSendProgress, ProgressCallback onReceiveProgress, }); + + Future put( + String path, { + Map data, + Map queryParameters, + Map header, + Options options, + CancelToken cancelToken, + ProgressCallback onSendProgress, + ProgressCallback onReceiveProgress, + }); } diff --git a/lib/core/http/api_client/api_client.dart b/lib/core/http/api_client/api_client.dart index 714fc040..eaf5c6c5 100644 --- a/lib/core/http/api_client/api_client.dart +++ b/lib/core/http/api_client/api_client.dart @@ -58,20 +58,7 @@ class ApiClient implements AbstractApiClient { cancelToken: cancelToken, onReceiveProgress: onReceiveProgress, ); - final responseData = response.data; - final statusCode = response.statusCode; - if (responseData == null || statusCode == null) { - throw const ApiException(statusCode: 500); - } - if (statusCode >= 400 && statusCode < 600) { - final errorResponse = ErrorResponse.fromJson(responseData); - throw ApiException( - statusCode: statusCode, - errorCode: errorResponse.errorCode, - description: errorResponse.description, - ); - } - return ApiResponse.fromJson(responseData); + return _toApiResponse(response); } on DioError catch (e) { final exception = _handleDioError(e); throw exception; @@ -99,26 +86,58 @@ class ApiClient implements AbstractApiClient { onSendProgress: onSendProgress, onReceiveProgress: onReceiveProgress, ); - final responseData = response.data; - final statusCode = response.statusCode; - if (responseData == null || statusCode == null) { - throw const ApiException(statusCode: 500); - } - if (statusCode >= 400 && statusCode < 600) { - final errorResponse = ErrorResponse.fromJson(responseData); - throw ApiException( - statusCode: statusCode, - errorCode: errorResponse.errorCode, - description: errorResponse.description, - ); - } - return ApiResponse.fromJson(responseData); + return _toApiResponse(response); } on DioError catch (e) { final exception = _handleDioError(e); throw exception; } } + @override + Future put( + String path, { + Map? data, + Map? queryParameters, + Map? header, + Options? options, + CancelToken? cancelToken, + ProgressCallback? onSendProgress, + ProgressCallback? onReceiveProgress, + }) async { + try { + final response = await _dio.put( + path, + data: data, + queryParameters: queryParameters, + options: options ?? Options(headers: header), + cancelToken: cancelToken, + onSendProgress: onSendProgress, + onReceiveProgress: onReceiveProgress, + ); + return _toApiResponse(response); + } on DioError catch (e) { + final exception = _handleDioError(e); + throw exception; + } + } + + ApiResponse _toApiResponse(Response> res) { + final responseData = res.data; + final statusCode = res.statusCode; + if (responseData == null || statusCode == null) { + throw const ApiException(statusCode: 500); + } + if (statusCode >= 400 && statusCode < 600) { + final errorResponse = ErrorResponse.fromJson(responseData); + throw ApiException( + statusCode: statusCode, + errorCode: errorResponse.errorCode, + description: errorResponse.description, + ); + } + return ApiResponse.fromJson(responseData); + } + /// DioError を受けて [ApiException] もしくはそのサブクラスを返す。 /// デバッグモードの場合は [DioError] の原因が [SocketException] によるものかを判別して、 /// そうである場合は、 [SocketException] を返す。 diff --git a/lib/features/trips/controller/trip_belonging_controller.g.dart b/lib/features/trips/controller/trip_belonging_controller.g.dart index c8e2e35c..87b7e45f 100644 --- a/lib/features/trips/controller/trip_belonging_controller.g.dart +++ b/lib/features/trips/controller/trip_belonging_controller.g.dart @@ -9,7 +9,7 @@ part of 'trip_belonging_controller.dart'; // ************************************************************************** String _$tripBelongingsControllerHash() => - r'bb340cfbecab66b713ca8b66f610a42da36ef23b'; + r'a60704a08ad5243284142df6a152e13131eafb9e'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/features/trips/data/models/change_check_status_response.dart b/lib/features/trips/data/models/change_check_status_response.dart new file mode 100644 index 00000000..7a59872b --- /dev/null +++ b/lib/features/trips/data/models/change_check_status_response.dart @@ -0,0 +1,14 @@ +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'change_check_status_response.freezed.dart'; +part 'change_check_status_response.g.dart'; + +@freezed +class ChangeCheckStatusResponse with _$ChangeCheckStatusResponse { + const factory ChangeCheckStatusResponse({ + required bool isChecked, + }) = _ChangeCheckStatusResponse; + + factory ChangeCheckStatusResponse.fromJson(Map json) => + _$ChangeCheckStatusResponseFromJson(json); +} diff --git a/lib/features/trips/data/models/change_check_status_response.freezed.dart b/lib/features/trips/data/models/change_check_status_response.freezed.dart new file mode 100644 index 00000000..b3d722a6 --- /dev/null +++ b/lib/features/trips/data/models/change_check_status_response.freezed.dart @@ -0,0 +1,159 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'change_check_status_response.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + +ChangeCheckStatusResponse _$ChangeCheckStatusResponseFromJson( + Map json) { + return _ChangeCheckStatusResponse.fromJson(json); +} + +/// @nodoc +mixin _$ChangeCheckStatusResponse { + bool get isChecked => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $ChangeCheckStatusResponseCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $ChangeCheckStatusResponseCopyWith<$Res> { + factory $ChangeCheckStatusResponseCopyWith(ChangeCheckStatusResponse value, + $Res Function(ChangeCheckStatusResponse) then) = + _$ChangeCheckStatusResponseCopyWithImpl<$Res, ChangeCheckStatusResponse>; + @useResult + $Res call({bool isChecked}); +} + +/// @nodoc +class _$ChangeCheckStatusResponseCopyWithImpl<$Res, + $Val extends ChangeCheckStatusResponse> + implements $ChangeCheckStatusResponseCopyWith<$Res> { + _$ChangeCheckStatusResponseCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? isChecked = null, + }) { + return _then(_value.copyWith( + isChecked: null == isChecked + ? _value.isChecked + : isChecked // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$_ChangeCheckStatusResponseCopyWith<$Res> + implements $ChangeCheckStatusResponseCopyWith<$Res> { + factory _$$_ChangeCheckStatusResponseCopyWith( + _$_ChangeCheckStatusResponse value, + $Res Function(_$_ChangeCheckStatusResponse) then) = + __$$_ChangeCheckStatusResponseCopyWithImpl<$Res>; + @override + @useResult + $Res call({bool isChecked}); +} + +/// @nodoc +class __$$_ChangeCheckStatusResponseCopyWithImpl<$Res> + extends _$ChangeCheckStatusResponseCopyWithImpl<$Res, + _$_ChangeCheckStatusResponse> + implements _$$_ChangeCheckStatusResponseCopyWith<$Res> { + __$$_ChangeCheckStatusResponseCopyWithImpl( + _$_ChangeCheckStatusResponse _value, + $Res Function(_$_ChangeCheckStatusResponse) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? isChecked = null, + }) { + return _then(_$_ChangeCheckStatusResponse( + isChecked: null == isChecked + ? _value.isChecked + : isChecked // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$_ChangeCheckStatusResponse implements _ChangeCheckStatusResponse { + const _$_ChangeCheckStatusResponse({required this.isChecked}); + + factory _$_ChangeCheckStatusResponse.fromJson(Map json) => + _$$_ChangeCheckStatusResponseFromJson(json); + + @override + final bool isChecked; + + @override + String toString() { + return 'ChangeCheckStatusResponse(isChecked: $isChecked)'; + } + + @override + bool operator ==(dynamic other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$_ChangeCheckStatusResponse && + (identical(other.isChecked, isChecked) || + other.isChecked == isChecked)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, isChecked); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$_ChangeCheckStatusResponseCopyWith<_$_ChangeCheckStatusResponse> + get copyWith => __$$_ChangeCheckStatusResponseCopyWithImpl< + _$_ChangeCheckStatusResponse>(this, _$identity); + + @override + Map toJson() { + return _$$_ChangeCheckStatusResponseToJson( + this, + ); + } +} + +abstract class _ChangeCheckStatusResponse implements ChangeCheckStatusResponse { + const factory _ChangeCheckStatusResponse({required final bool isChecked}) = + _$_ChangeCheckStatusResponse; + + factory _ChangeCheckStatusResponse.fromJson(Map json) = + _$_ChangeCheckStatusResponse.fromJson; + + @override + bool get isChecked; + @override + @JsonKey(ignore: true) + _$$_ChangeCheckStatusResponseCopyWith<_$_ChangeCheckStatusResponse> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/lib/features/trips/data/models/change_check_status_response.g.dart b/lib/features/trips/data/models/change_check_status_response.g.dart new file mode 100644 index 00000000..c8b2fb51 --- /dev/null +++ b/lib/features/trips/data/models/change_check_status_response.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +// ignore_for_file: type=lint, implicit_dynamic_parameter, implicit_dynamic_type, implicit_dynamic_method, strict_raw_type + +part of 'change_check_status_response.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$_ChangeCheckStatusResponse _$$_ChangeCheckStatusResponseFromJson( + Map json) => + $checkedCreate( + r'_$_ChangeCheckStatusResponse', + json, + ($checkedConvert) { + final val = _$_ChangeCheckStatusResponse( + isChecked: $checkedConvert('is_checked', (v) => v as bool), + ); + return val; + }, + fieldKeyMap: const {'isChecked': 'is_checked'}, + ); + +Map _$$_ChangeCheckStatusResponseToJson( + _$_ChangeCheckStatusResponse instance) => + { + 'is_checked': instance.isChecked, + }; diff --git a/lib/features/trips/data/repositories/trip_repository.dart b/lib/features/trips/data/repositories/trip_repository.dart index 35a86992..7ebe97bb 100644 --- a/lib/features/trips/data/repositories/trip_repository.dart +++ b/lib/features/trips/data/repositories/trip_repository.dart @@ -2,6 +2,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:trip_app_nativeapp/core/extensions/datetime.dart'; import 'package:trip_app_nativeapp/core/http/api_client/abstract_api_client.dart'; import 'package:trip_app_nativeapp/core/http/api_client/api_client.dart'; +import 'package:trip_app_nativeapp/features/trips/data/models/change_check_status_response.dart'; import 'package:trip_app_nativeapp/features/trips/data/models/create_trip_response.dart'; import 'package:trip_app_nativeapp/features/trips/data/models/fetch_trip_belongings_response.dart'; import 'package:trip_app_nativeapp/features/trips/data/models/fetch_trips_response.dart'; @@ -38,6 +39,7 @@ class TripRepository implements TripRepositoryInterface { final AbstractApiClient privateV1Client; static const _basePath = '/trips'; static const _invitationBasePath = '/trip_invitations'; + static const _belongingBasePath = '/trip_belongings'; @override Future> fetchTripsByUserId(int userId) async { @@ -189,4 +191,20 @@ class TripRepository implements TripRepositoryInterface { ) as AddedTripBelonging; }).toList(); } + + @override + Future changeBelongingCheckStatus({ + required int belongingId, + required bool isChecked, + }) async { + final res = await privateV1Client.put( + '$_belongingBasePath/$belongingId/check_status', + data: { + 'is_checked': isChecked, + }, + ); + + final changeStatusRes = ChangeCheckStatusResponse.fromJson(res.data); + return changeStatusRes.isChecked; + } } diff --git a/lib/features/trips/domain/repositories/trip_repository_interface.dart b/lib/features/trips/domain/repositories/trip_repository_interface.dart index 10a0e980..2ec4296b 100644 --- a/lib/features/trips/domain/repositories/trip_repository_interface.dart +++ b/lib/features/trips/domain/repositories/trip_repository_interface.dart @@ -30,4 +30,10 @@ abstract class TripRepositoryInterface { Future> fetchTripBelongings( int tripId, ); + + /// 持ち物チェックステータスを変更 + Future changeBelongingCheckStatus({ + required int belongingId, + required bool isChecked, + }); } diff --git a/test/feature/trips/controller/trip_controller_test.mocks.dart b/test/feature/trips/controller/trip_controller_test.mocks.dart index cf7e0ced..8737d3bb 100644 --- a/test/feature/trips/controller/trip_controller_test.mocks.dart +++ b/test/feature/trips/controller/trip_controller_test.mocks.dart @@ -3,17 +3,17 @@ // Do not manually edit this file. // ignore_for_file: no_leading_underscores_for_library_prefixes -import 'dart:async' as _i5; +import 'dart:async' as _i6; import 'package:mockito/mockito.dart' as _i1; import 'package:trip_app_nativeapp/features/trips/domain/entity/trip/trip.dart' - as _i6; -import 'package:trip_app_nativeapp/features/trips/domain/entity/trip/trip_belonging.dart' as _i7; +import 'package:trip_app_nativeapp/features/trips/domain/entity/trip/trip_belonging.dart' + as _i4; import 'package:trip_app_nativeapp/features/trips/domain/entity/trip/trip_invitation.dart' as _i3; import 'package:trip_app_nativeapp/features/trips/domain/interactor/trip_interactor.dart' - as _i4; + as _i5; import 'package:trip_app_nativeapp/features/trips/domain/repositories/trip_repository_interface.dart' as _i2; @@ -50,10 +50,21 @@ class _FakeGeneratedTripInvitation_1 extends _i1.SmartFake ); } +class _FakeAddedTripBelonging_2 extends _i1.SmartFake + implements _i4.AddedTripBelonging { + _FakeAddedTripBelonging_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [TripInteractor]. /// /// See the documentation for Mockito's code generation for more information. -class MockTripInteractor extends _i1.Mock implements _i4.TripInteractor { +class MockTripInteractor extends _i1.Mock implements _i5.TripInteractor { MockTripInteractor() { _i1.throwOnMissingStub(this); } @@ -67,7 +78,7 @@ class MockTripInteractor extends _i1.Mock implements _i4.TripInteractor { ), ) as _i2.TripRepositoryInterface); @override - _i5.Future createTrip( + _i6.Future createTrip( String? title, DateTime? fromDate, DateTime? endDate, @@ -81,11 +92,11 @@ class MockTripInteractor extends _i1.Mock implements _i4.TripInteractor { endDate, ], ), - returnValue: _i5.Future.value(), - returnValueForMissingStub: _i5.Future.value(), - ) as _i5.Future); + returnValue: _i6.Future.value(), + returnValueForMissingStub: _i6.Future.value(), + ) as _i6.Future); @override - _i5.Future<_i3.GeneratedTripInvitation> invite({ + _i6.Future<_i3.GeneratedTripInvitation> invite({ required int? tripId, required int? invitationNum, }) => @@ -98,7 +109,7 @@ class MockTripInteractor extends _i1.Mock implements _i4.TripInteractor { #invitationNum: invitationNum, }, ), - returnValue: _i5.Future<_i3.GeneratedTripInvitation>.value( + returnValue: _i6.Future<_i3.GeneratedTripInvitation>.value( _FakeGeneratedTripInvitation_1( this, Invocation.method( @@ -110,25 +121,58 @@ class MockTripInteractor extends _i1.Mock implements _i4.TripInteractor { }, ), )), - ) as _i5.Future<_i3.GeneratedTripInvitation>); + ) as _i6.Future<_i3.GeneratedTripInvitation>); @override - _i5.Future> fetchTripsByUserId(int? userId) => + _i6.Future> fetchTripsByUserId(int? userId) => (super.noSuchMethod( Invocation.method( #fetchTripsByUserId, [userId], ), returnValue: - _i5.Future>.value(<_i6.ExistingTrip>[]), - ) as _i5.Future>); + _i6.Future>.value(<_i7.ExistingTrip>[]), + ) as _i6.Future>); + @override + _i6.Future<_i4.AddedTripBelonging> addTripBelonging({ + required int? tripId, + required String? name, + required int? numOf, + required bool? isShareAmongMember, + }) => + (super.noSuchMethod( + Invocation.method( + #addTripBelonging, + [], + { + #tripId: tripId, + #name: name, + #numOf: numOf, + #isShareAmongMember: isShareAmongMember, + }, + ), + returnValue: + _i6.Future<_i4.AddedTripBelonging>.value(_FakeAddedTripBelonging_2( + this, + Invocation.method( + #addTripBelonging, + [], + { + #tripId: tripId, + #name: name, + #numOf: numOf, + #isShareAmongMember: isShareAmongMember, + }, + ), + )), + ) as _i6.Future<_i4.AddedTripBelonging>); @override - _i5.Future> fetchTripBelongings(int? tripId) => + _i6.Future> fetchTripBelongings(int? tripId) => (super.noSuchMethod( Invocation.method( #fetchTripBelongings, [tripId], ), - returnValue: _i5.Future>.value( - <_i7.AddedTripBelonging>[]), - ) as _i5.Future>); + returnValue: _i6.Future>.value( + <_i4.AddedTripBelonging>[]), + ) as _i6.Future>); } diff --git a/test/feature/trips/data/repositories/trip_repository_test.dart b/test/feature/trips/data/repositories/trip_repository_test.dart index fcf62227..ca74f397 100644 --- a/test/feature/trips/data/repositories/trip_repository_test.dart +++ b/test/feature/trips/data/repositories/trip_repository_test.dart @@ -444,4 +444,53 @@ Future main() async { ); }); }); + + group('changeBelongingCheckStatus', () { + const validBelongingId = 99; + test('正常系', () async { + when( + mockApiClient.put( + '/trip_belongings/$validBelongingId/check_status', + data: { + 'is_checked': true, + }, + ), + ).thenAnswer((_) async { + return const ApiResponse( + data: {'is_checked': true}, + ); + }); + + final result = await providerContainer + .read(tripRepositoryProvider) + .changeBelongingCheckStatus( + belongingId: validBelongingId, + isChecked: true, + ); + expect(result, true); + }); + + test('正常系', () async { + when( + mockApiClient.put( + '/trip_belongings/$validBelongingId/check_status', + data: { + 'is_checked': true, + }, + ), + ).thenThrow(unexpectedException); + + await expectLater( + providerContainer + .read(tripRepositoryProvider) + .changeBelongingCheckStatus( + belongingId: validBelongingId, + isChecked: true, + ), + throwsA( + isA(), + ), + ); + }); + }); } diff --git a/test/feature/trips/data/repositories/trip_repository_test.mocks.dart b/test/feature/trips/data/repositories/trip_repository_test.mocks.dart index 9d0550f8..2f3a94d7 100644 --- a/test/feature/trips/data/repositories/trip_repository_test.mocks.dart +++ b/test/feature/trips/data/repositories/trip_repository_test.mocks.dart @@ -119,4 +119,46 @@ class MockAbstractApiClient extends _i1.Mock implements _i3.AbstractApiClient { ), )), ) as _i4.Future<_i2.ApiResponse>); + @override + _i4.Future<_i2.ApiResponse> put( + String? path, { + Map? data, + Map? queryParameters, + Map? header, + _i5.Options? options, + _i5.CancelToken? cancelToken, + _i5.ProgressCallback? onSendProgress, + _i5.ProgressCallback? onReceiveProgress, + }) => + (super.noSuchMethod( + Invocation.method( + #put, + [path], + { + #data: data, + #queryParameters: queryParameters, + #header: header, + #options: options, + #cancelToken: cancelToken, + #onSendProgress: onSendProgress, + #onReceiveProgress: onReceiveProgress, + }, + ), + returnValue: _i4.Future<_i2.ApiResponse>.value(_FakeApiResponse_0( + this, + Invocation.method( + #put, + [path], + { + #data: data, + #queryParameters: queryParameters, + #header: header, + #options: options, + #cancelToken: cancelToken, + #onSendProgress: onSendProgress, + #onReceiveProgress: onReceiveProgress, + }, + ), + )), + ) as _i4.Future<_i2.ApiResponse>); } diff --git a/test/feature/trips/domain/trip_interactor_test.mocks.dart b/test/feature/trips/domain/trip_interactor_test.mocks.dart index 46e52c76..cc9f328a 100644 --- a/test/feature/trips/domain/trip_interactor_test.mocks.dart +++ b/test/feature/trips/domain/trip_interactor_test.mocks.dart @@ -191,4 +191,20 @@ class MockTripRepository extends _i1.Mock implements _i6.TripRepository { returnValue: _i7.Future>.value( <_i5.AddedTripBelonging>[]), ) as _i7.Future>); + @override + _i7.Future changeBelongingCheckStatus({ + required int? belongingId, + required bool? isChecked, + }) => + (super.noSuchMethod( + Invocation.method( + #changeBelongingCheckStatus, + [], + { + #belongingId: belongingId, + #isChecked: isChecked, + }, + ), + returnValue: _i7.Future.value(false), + ) as _i7.Future); } diff --git a/test/feature/user/data/user_repository_test.mocks.dart b/test/feature/user/data/user_repository_test.mocks.dart index b1ab56a0..55a2e960 100644 --- a/test/feature/user/data/user_repository_test.mocks.dart +++ b/test/feature/user/data/user_repository_test.mocks.dart @@ -118,4 +118,46 @@ class MockApiClient extends _i1.Mock implements _i3.ApiClient { ), )), ) as _i4.Future<_i2.ApiResponse>); + @override + _i4.Future<_i2.ApiResponse> put( + String? path, { + Map? data, + Map? queryParameters, + Map? header, + _i5.Options? options, + _i5.CancelToken? cancelToken, + _i5.ProgressCallback? onSendProgress, + _i5.ProgressCallback? onReceiveProgress, + }) => + (super.noSuchMethod( + Invocation.method( + #put, + [path], + { + #data: data, + #queryParameters: queryParameters, + #header: header, + #options: options, + #cancelToken: cancelToken, + #onSendProgress: onSendProgress, + #onReceiveProgress: onReceiveProgress, + }, + ), + returnValue: _i4.Future<_i2.ApiResponse>.value(_FakeApiResponse_0( + this, + Invocation.method( + #put, + [path], + { + #data: data, + #queryParameters: queryParameters, + #header: header, + #options: options, + #cancelToken: cancelToken, + #onSendProgress: onSendProgress, + #onReceiveProgress: onReceiveProgress, + }, + ), + )), + ) as _i4.Future<_i2.ApiResponse>); }