Skip to content

Commit

Permalink
Merge pull request #19 from alexandr7035/develop
Browse files Browse the repository at this point in the history
Release v0.2-alpha

- Add password reset feature
  • Loading branch information
alexandr7035 authored Dec 28, 2021
2 parents 3944f57 + da055e9 commit dc3588e
Show file tree
Hide file tree
Showing 40 changed files with 1,276 additions and 212 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/launch_unit_tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This is a basic workflow to help you get started with Actions

name: Unit tests

# Controls when the workflow will run
on:
# Triggers the workflow on pull request events for the master branch
pull_request:
branches: [ master ]

# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
# This workflow contains a single job called "build"
build:
# The type of runner that the job will run on
runs-on: ubuntu-latest

# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v2

- name: Run Unit Tests
run: ./gradlew test
10 changes: 10 additions & 0 deletions app/src/main/java/by/alexandr7035/affinidi_id/data/ApiService.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package by.alexandr7035.affinidi_id.data

import by.alexandr7035.affinidi_id.data.model.reset_password.ConfirmResetPasswordRequest
import by.alexandr7035.affinidi_id.data.model.reset_password.InitializeResetPasswordRequest
import by.alexandr7035.affinidi_id.data.model.sign_in.SignInRequest
import by.alexandr7035.affinidi_id.data.model.sign_in.SignInResponse
import by.alexandr7035.affinidi_id.data.model.sign_up.ConfirmSignUpRequest
Expand All @@ -25,4 +27,12 @@ interface ApiService {

@POST("api/v1/users/logout")
suspend fun logOut(@Header("Authorization") accessToken: String): Response<Unit>

// This request doesn't return anything but sends OTP to user's email
@POST("api/v1/users/forgot-password")
suspend fun initializePasswordReset(@Body body: InitializeResetPasswordRequest): Response<Unit>

// This request doesn't return anything. 204 code for success
@POST("api/v1/users/forgot-password/confirm")
suspend fun confirmPasswordReset(@Body body: ConfirmResetPasswordRequest): Response<Unit>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package by.alexandr7035.affinidi_id.data

import by.alexandr7035.affinidi_id.data.model.log_out.LogOutModel
import by.alexandr7035.affinidi_id.data.model.sign_in.SignInModel

interface LoginRepository {
suspend fun signIn(
userName: String,
password: String,
): SignInModel

fun checkIfAuthorized(): Boolean

fun saveUserName(userName: String)

suspend fun logOut(): LogOutModel
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
package by.alexandr7035.affinidi_id.data

import by.alexandr7035.affinidi_id.data.model.log_out.LogOutModel
import by.alexandr7035.affinidi_id.data.model.sign_in.SignInModel
import by.alexandr7035.affinidi_id.data.model.sign_up.SignUpConfirmationModel
import by.alexandr7035.affinidi_id.data.model.sign_up.SignUpModel

interface AuthRepository {
interface RegistrationRepository {
suspend fun signUp(
userName: String,
password: String,
Expand All @@ -16,14 +14,6 @@ interface AuthRepository {
confirmationCode: String
): SignUpConfirmationModel

suspend fun signIn(
userName: String,
password: String,
): SignInModel

fun checkIfAuthorized(): Boolean

// FIXME refactoring
fun saveUserName(userName: String)

suspend fun logOut(): LogOutModel
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package by.alexandr7035.affinidi_id.data

import by.alexandr7035.affinidi_id.data.model.reset_password.ConfirmPasswordResetModel
import by.alexandr7035.affinidi_id.data.model.reset_password.InitializePasswordResetModel

interface ResetPasswordRepository {
suspend fun initializePasswordReset(userName: String): InitializePasswordResetModel

suspend fun confirmResetPassword(userName: String, newPassword: String, confirmationCode: String): ConfirmPasswordResetModel
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package by.alexandr7035.affinidi_id.data.helpers.validation

interface InputValidationHelper {
fun validateUserName(userName: String): InputValidationResult

fun validatePassword(password: String): InputValidationResult

fun validateConfirmationCode(confirmationCode: String): InputValidationResult
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package by.alexandr7035.affinidi_id.data.helpers.validation

import android.util.Patterns

class InputValidationHelperImpl(minPasswordLength: Int): InputValidationHelper {

private val passwordPattern = "^(?=.*[A-Z])(?=.*[a-z])(?=.*\\d).{$minPasswordLength,}\$".toPattern()

override fun validateUserName(userName: String): InputValidationResult {
return when {
userName.isBlank() -> InputValidationResult.EMPTY_FIELD
! Patterns.EMAIL_ADDRESS.matcher(userName).matches() -> InputValidationResult.WRONG_FORMAT
else -> InputValidationResult.NO_ERRORS
}
}

override fun validatePassword(password: String): InputValidationResult {
return when {
password.isBlank() -> InputValidationResult.EMPTY_FIELD
! passwordPattern.matcher(password).matches() -> InputValidationResult.WRONG_FORMAT
else -> InputValidationResult.NO_ERRORS
}
}

override fun validateConfirmationCode(confirmationCode: String): InputValidationResult {
return when {
confirmationCode.isBlank() -> InputValidationResult.EMPTY_FIELD
else -> InputValidationResult.NO_ERRORS
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package by.alexandr7035.affinidi_id.data.helpers.validation

enum class InputValidationResult {
EMPTY_FIELD,
WRONG_FORMAT,
NO_ERRORS
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,102 +5,19 @@ import by.alexandr7035.affinidi_id.core.ErrorType
import by.alexandr7035.affinidi_id.core.extensions.debug
import by.alexandr7035.affinidi_id.data.ApiService
import by.alexandr7035.affinidi_id.data.AuthDataStorage
import by.alexandr7035.affinidi_id.data.AuthRepository
import by.alexandr7035.affinidi_id.data.LoginRepository
import by.alexandr7035.affinidi_id.data.model.log_out.LogOutModel
import by.alexandr7035.affinidi_id.data.model.sign_in.SignInModel
import by.alexandr7035.affinidi_id.data.model.sign_in.SignInRequest
import by.alexandr7035.affinidi_id.data.model.sign_in.SignInResponse
import by.alexandr7035.affinidi_id.data.model.sign_up.*
import timber.log.Timber
import java.lang.Exception
import javax.inject.Inject

class AuthRepositoryImpl @Inject constructor(
class LoginRepositoryImpl @Inject constructor(
private val apiService: ApiService,
private val authDataStorage: AuthDataStorage
): AuthRepository {
override suspend fun signUp(userName: String, password: String): SignUpModel {

try {

val res = apiService.signUp(
SignUpRequest(
userName = userName,
password = password,
// Use with default params
signUpOptions = SignUpOptions(),
signUpMessageParams = SignUpMessageParams()
)
)

return if (res.isSuccessful) {
// Get token for signup confirmation and return
SignUpModel.Success(res.body() as String)
} else {
Timber.debug("RES FAILED")
when (res.code()) {
409 -> {
SignUpModel.Fail(ErrorType.USER_ALREADY_EXISTS)
}
else -> {
SignUpModel.Fail(ErrorType.UNKNOWN_ERROR)
}
}
}
}
// Handled in ErrorInterceptor
catch (appError: AppError) {
Timber.debug("RES FAILED ${appError.errorType.name}")
return SignUpModel.Fail(appError.errorType)
}
// Unknown exception
catch (e: Exception) {
e.printStackTrace()
return SignUpModel.Fail(ErrorType.UNKNOWN_ERROR)
}
}

override suspend fun confirmSignUp(confirmationToken: String, confirmationCode: String): SignUpConfirmationModel {
try {
val res = apiService.confirmSignUp(ConfirmSignUpRequest(
token = confirmationToken,
confirmationCode = confirmationCode
))

if (res.isSuccessful) {
val data = res.body() as ConfirmSignUpResponse

// TODO think if should do this through fragment - viewmodel - repo chain
saveAuthData(userDid = data.userDid, accessToken = data.accessToken)

return SignUpConfirmationModel.Success(
accessToken = data.accessToken,
userDid = data.userDid
)
}
else {
return when (res.code()) {
400 -> {
SignUpConfirmationModel.Fail(ErrorType.WRONG_CONFIRMATION_CODE)
}
else -> {
SignUpConfirmationModel.Fail(ErrorType.UNKNOWN_ERROR)
}
}
}
}
// Handled in ErrorInterceptor
catch (appError: AppError) {
return SignUpConfirmationModel.Fail(appError.errorType)
}
// Unknown exception
catch (e: Exception) {
return SignUpConfirmationModel.Fail(ErrorType.UNKNOWN_ERROR)
}

}


): LoginRepository {
override suspend fun signIn(userName: String, password: String): SignInModel {

try {
Expand Down Expand Up @@ -130,7 +47,7 @@ class AuthRepositoryImpl @Inject constructor(
}
// Handled in ErrorInterceptor
catch (appError: AppError) {
return SignInModel.Fail(appError.errorType)
return SignInModel.Fail(appError.errorType)
}
// Unknown exception
catch (e: Exception) {
Expand All @@ -146,11 +63,6 @@ class AuthRepositoryImpl @Inject constructor(
authDataStorage.saveUserName(userName)
}

private fun saveAuthData(userDid: String, accessToken: String) {
authDataStorage.saveDid(userDid)
authDataStorage.saveAccessToken(accessToken)
}

override suspend fun logOut(): LogOutModel {
try {
val res = apiService.logOut(authDataStorage.getAccessToken() ?: "")
Expand Down Expand Up @@ -193,4 +105,9 @@ class AuthRepositoryImpl @Inject constructor(
return LogOutModel.Fail(ErrorType.UNKNOWN_ERROR)
}
}

private fun saveAuthData(userDid: String, accessToken: String) {
authDataStorage.saveDid(userDid)
authDataStorage.saveAccessToken(accessToken)
}
}
Loading

0 comments on commit dc3588e

Please sign in to comment.