-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #51 from nimblehq/feature/9-backend-sign-in
[#9] [Backend] As a user, I can sign in with email and password
- Loading branch information
Showing
19 changed files
with
353 additions
and
99 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
SECRET= | ||
REST_API_ENDPOINT= | ||
CLIENT_ID= | ||
CLIENT_SECRET= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -37,4 +37,4 @@ SPEC CHECKSUMS: | |
|
||
PODFILE CHECKSUM: 632d6ac0b577d6e268ff7a13a105bbc4f7941989 | ||
|
||
COCOAPODS: 1.12.0 | ||
COCOAPODS: 1.12.1 |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import 'package:dio/dio.dart'; | ||
import 'package:survey_flutter/model/request/login_request.dart'; | ||
import 'package:survey_flutter/model/response/login_response.dart'; | ||
import 'package:retrofit/retrofit.dart'; | ||
|
||
part 'authentication_api_service.g.dart'; | ||
|
||
@RestApi() | ||
abstract class AuthenticationApiService { | ||
factory AuthenticationApiService(Dio dio, {String baseUrl}) = | ||
_AuthenticationApiService; | ||
|
||
@POST('/oauth/token') | ||
Future<LoginResponse> login( | ||
@Body() LoginRequest body, | ||
); | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import 'package:japx/japx.dart'; | ||
|
||
class ResponseDecoder { | ||
static Map<String, dynamic> decode(Map<String, dynamic> json) { | ||
return Japx.decode(json)['data']; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import 'package:equatable/equatable.dart'; | ||
|
||
class LoginModel extends Equatable { | ||
final String id; | ||
final String accessToken; | ||
final double expiresIn; | ||
final String refreshToken; | ||
|
||
const LoginModel({ | ||
required this.id, | ||
required this.accessToken, | ||
required this.expiresIn, | ||
required this.refreshToken, | ||
}); | ||
|
||
@override | ||
List<Object?> get props => [ | ||
id, | ||
accessToken, | ||
expiresIn, | ||
refreshToken, | ||
]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import 'package:json_annotation/json_annotation.dart'; | ||
|
||
part 'login_request.g.dart'; | ||
|
||
@JsonSerializable() | ||
class LoginRequest { | ||
final String grantType; | ||
final String email; | ||
final String password; | ||
final String clientId; | ||
final String clientSecret; | ||
|
||
LoginRequest({ | ||
required this.grantType, | ||
required this.email, | ||
required this.password, | ||
required this.clientId, | ||
required this.clientSecret, | ||
}); | ||
|
||
Map<String, dynamic> toJson() => _$LoginRequestToJson(this); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import 'package:json_annotation/json_annotation.dart'; | ||
import 'package:survey_flutter/api/response_decoder.dart'; | ||
import 'package:survey_flutter/model/login_model.dart'; | ||
|
||
part 'login_response.g.dart'; | ||
|
||
@JsonSerializable() | ||
class LoginResponse { | ||
final String id; | ||
final String accessToken; | ||
final String tokenType; | ||
final double expiresIn; | ||
final String refreshToken; | ||
final int createdAt; | ||
|
||
LoginResponse({ | ||
required this.id, | ||
required this.accessToken, | ||
required this.tokenType, | ||
required this.expiresIn, | ||
required this.refreshToken, | ||
required this.createdAt, | ||
}); | ||
|
||
factory LoginResponse.fromJson(Map<String, dynamic> json) => | ||
_$LoginResponseFromJson(ResponseDecoder.decode(json)); | ||
|
||
LoginModel toLoginModel() => LoginModel( | ||
id: id, | ||
accessToken: accessToken, | ||
expiresIn: expiresIn, | ||
refreshToken: refreshToken, | ||
); | ||
|
||
static LoginResponse dummy() { | ||
return LoginResponse( | ||
id: "", | ||
accessToken: "", | ||
tokenType: "", | ||
expiresIn: 0, | ||
refreshToken: "", | ||
createdAt: 0, | ||
); | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import 'package:survey_flutter/api/authentication_api_service.dart'; | ||
import 'package:survey_flutter/api/exception/network_exceptions.dart'; | ||
import 'package:survey_flutter/env.dart'; | ||
import 'package:survey_flutter/model/login_model.dart'; | ||
import 'package:survey_flutter/model/request/login_request.dart'; | ||
import 'package:injectable/injectable.dart'; | ||
|
||
const String _grantType = "password"; | ||
|
||
abstract class AuthenticationRepository { | ||
Future<LoginModel> login({ | ||
required String email, | ||
required String password, | ||
}); | ||
} | ||
|
||
@Singleton(as: AuthenticationRepository) | ||
class AuthenticationRepositoryImpl extends AuthenticationRepository { | ||
final AuthenticationApiService _authenticationApiService; | ||
|
||
AuthenticationRepositoryImpl(this._authenticationApiService); | ||
|
||
@override | ||
Future<LoginModel> login({ | ||
required String email, | ||
required String password, | ||
}) async { | ||
try { | ||
final response = await _authenticationApiService.login(LoginRequest( | ||
email: email, | ||
password: password, | ||
clientId: Env.clientId, | ||
clientSecret: Env.clientSecret, | ||
grantType: _grantType, | ||
)); | ||
return response.toLoginModel(); | ||
} catch (exception) { | ||
throw NetworkExceptions.fromDioException(exception); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import 'dart:async'; | ||
import 'package:survey_flutter/model/login_model.dart'; | ||
import 'package:survey_flutter/repositories/authentication_repository.dart'; | ||
import 'package:survey_flutter/usecases/base/base_use_case.dart'; | ||
import 'package:injectable/injectable.dart'; | ||
|
||
class LoginParams { | ||
final String email; | ||
final String password; | ||
|
||
LoginParams({ | ||
required this.email, | ||
required this.password, | ||
}); | ||
} | ||
|
||
@Injectable() | ||
class LoginUseCase extends UseCase<LoginModel, LoginParams> { | ||
final AuthenticationRepository _repository; | ||
|
||
const LoginUseCase(this._repository); | ||
|
||
@override | ||
Future<Result<LoginModel>> call(LoginParams params) async { | ||
try { | ||
final result = await _repository.login( | ||
email: params.email, | ||
password: params.password, | ||
); | ||
return Success(result); | ||
} catch (exception) { | ||
return Failed(UseCaseException(exception)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
import 'package:flutter_config/flutter_config.dart'; | ||
import 'package:flutter_test/flutter_test.dart'; | ||
import 'package:mockito/mockito.dart'; | ||
import 'package:survey_flutter/repositories/authentication_repository.dart'; | ||
import 'package:survey_flutter/api/exception/network_exceptions.dart'; | ||
import 'package:survey_flutter/model/response/login_response.dart'; | ||
|
||
import '../../mocks/generate_mocks.mocks.dart'; | ||
|
||
void main() { | ||
group('AuthenticationRepositoryTest', () { | ||
late MockAuthenticationApiService mockAuthApiService; | ||
late AuthenticationRepositoryImpl authRepository; | ||
|
||
const email = "email"; | ||
const password = "password"; | ||
|
||
setUpAll(() { | ||
FlutterConfig.loadValueForTesting({ | ||
'CLIENT_ID': 'CLIENT_ID', | ||
'CLIENT_SECRET': 'CLIENT_SECRET', | ||
}); | ||
}); | ||
|
||
setUp(() { | ||
mockAuthApiService = MockAuthenticationApiService(); | ||
authRepository = AuthenticationRepositoryImpl(mockAuthApiService); | ||
}); | ||
|
||
test('When login successfully, it returns correct model', () async { | ||
final loginResponse = LoginResponse.dummy(); | ||
|
||
when(mockAuthApiService.login(any)) | ||
.thenAnswer((_) async => loginResponse); | ||
|
||
final result = | ||
await authRepository.login(email: email, password: password); | ||
|
||
expect(result, loginResponse.toLoginModel()); | ||
}); | ||
|
||
test('When login fail, it returns failed exception', () async { | ||
when(mockAuthApiService.login(any)).thenThrow(Exception()); | ||
|
||
final result = authRepository.login(email: email, password: password); | ||
|
||
expect(result, throwsA(isA<NetworkExceptions>())); | ||
}); | ||
}); | ||
} |
Oops, something went wrong.