Skip to content

Commit

Permalink
Merge pull request #86 from autentia/feature/#177848857-sign-in-with-…
Browse files Browse the repository at this point in the history
…google-and-micronaut

Feature/#177848857 sign in with google and micronaut
  • Loading branch information
achamorro-dev authored Feb 9, 2023
2 parents 75b4b66 + f76370a commit 6a0c542
Show file tree
Hide file tree
Showing 37 changed files with 243 additions and 726 deletions.
38 changes: 9 additions & 29 deletions src/modules/login/components/LoginForm/LoginForm.tsx
Original file line number Diff line number Diff line change
@@ -1,45 +1,25 @@
import { Flex, Heading, Stack, useColorModeValue } from '@chakra-ui/react'
import { Flex, Heading, useColorModeValue } from '@chakra-ui/react'
import { AppVersion } from 'modules/login/components/AppVersion'
import { useLoginForm } from 'modules/login/components/LoginForm/useLoginForm'
import { PasswordField } from 'modules/login/components/PasswordField/PasswordField'
import { useTranslation } from 'react-i18next'
import SubmitButton from 'shared/components/FormFields/SubmitButton'
import { TextField } from 'shared/components/FormFields/TextField'
import { LogoAutentia } from 'shared/components/LogoAutentia'
import { SignInWithGoogleButton } from '../SignInWithGoogle/SignInWithGoogleButton'

export const LoginForm = () => {
const { t } = useTranslation()
const form = useLoginForm()

const bgColor = useColorModeValue('white', undefined)

return (
<Flex direction="column" height="100%" bgColor={bgColor}>
<Flex direction="column" m="auto" minWidth="300px">
<LogoAutentia size="lg" />
<form data-testid="login-form" onSubmit={form.onSubmit}>
<Heading as="h1" size="xl">
{t('login_page.welcome_title')}
</Heading>
<Heading as="h2" size="lg">
{t('login_page.welcome_message')}
</Heading>
<Stack spacing={6} mt={8}>
<TextField
label={t('login_page.username_field')}
autoComplete="username"
autoFocus={true}
{...form.register('username', { required: true })}
error={form.errors.username && t('form_errors.field_required')}
/>
<PasswordField
label={t('login_page.password_field')}
{...form.register('password', { required: true })}
error={form.errors.password && t('form_errors.field_required')}
/>
<SubmitButton isLoading={form.isLoading}>LOGIN</SubmitButton>
</Stack>
</form>
<Heading as="h1" size="xl">
{t('login_page.welcome_title')}
</Heading>
<Heading as="h2" size="md" mt={2} mb={8}>
{t('login_page.welcome_message')}
</Heading>
<SignInWithGoogleButton />
</Flex>
<AppVersion />
</Flex>
Expand Down
43 changes: 0 additions & 43 deletions src/modules/login/components/LoginForm/useLoginForm.ts

This file was deleted.

31 changes: 0 additions & 31 deletions src/modules/login/components/PasswordField/PasswordField.tsx

This file was deleted.

27 changes: 0 additions & 27 deletions src/modules/login/components/PasswordField/PasswordInput.test.tsx

This file was deleted.

39 changes: 0 additions & 39 deletions src/modules/login/components/PasswordField/PasswordInput.tsx

This file was deleted.

10 changes: 10 additions & 0 deletions src/modules/login/components/SignInWithGoogle/GoogleIcon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { IconProps } from '@chakra-ui/icons'
import { Icon } from '@chakra-ui/react'

export const GoogleIcon: React.FC<IconProps> = (props) => {
return (
<Icon viewBox="0 0 488 512" {...props}>
<path d="M488 261.8C488 403.3 391.1 504 248 504 110.8 504 0 393.2 0 256S110.8 8 248 8c66.8 0 123 24.5 166.3 64.9l-67.5 64.9C258.5 52.6 94.3 116.6 94.3 256c0 86.5 69.1 156.6 153.7 156.6 98.2 0 135-70.4 140.8-106.9H248v-85.3h236.1c2.3 12.7 3.9 24.9 3.9 41.4z" />
</Icon>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Button } from '@chakra-ui/react'
import { useTranslation } from 'react-i18next'
import endpoints from 'shared/api/endpoints'
import { GoogleIcon } from './GoogleIcon'

export const SignInWithGoogleButton: React.FC = () => {
const { t } = useTranslation()

const onClick = () => {
window.location.assign(endpoints.googleLogin)
}

return (
<Button
type="button"
colorScheme="brand"
variant="solid"
onClick={onClick}
leftIcon={<GoogleIcon fill="white" boxSize={4} mr={4} />}
>
{t('login_page.sign_in_with_google')}
</Button>
)
}
54 changes: 54 additions & 0 deletions src/modules/login/data-access/actions/auto-login-action.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { AppState } from 'shared/data-access/state/app-state'
import { container } from 'tsyringe'
import { mock } from 'jest-mock-extended'
import { UserRepository } from '../repositories/user-repository'
import { AutoLoginAction } from './auto-login-action'
import { buildUser } from 'test-utils/generateTestMocks'
import { AnonymousUserError } from '../errors/anonymous-user-error'

describe('AutoLoginAction', () => {
test('should set loggedUser state when repository return the user', async () => {
const { appState, userRepository, autoLoginAction } = setup()
userRepository.getUser.mockResolvedValue(buildUser())
appState.loggedUser = undefined

await autoLoginAction.execute()

expect(appState.loggedUser).toEqual(buildUser())
})

test('should set isAuthenticated state when repository return the user', async () => {
const { appState, userRepository, autoLoginAction } = setup()
userRepository.getUser.mockResolvedValue(buildUser())
appState.isAuthenticated = false

await autoLoginAction.execute()

expect(appState.isAuthenticated).toBeTruthy()
})

test('should throw error when error is not AnonymousUserError', async () => {
const { userRepository, autoLoginAction } = setup()
userRepository.getUser.mockRejectedValue(new Error())

expect(autoLoginAction.execute()).rejects.toThrowError()
})

test('should not throw error when error is AnonymousUserError', async () => {
const { userRepository, autoLoginAction } = setup()
userRepository.getUser.mockRejectedValue(new AnonymousUserError())

expect(autoLoginAction.execute()).resolves.not.toThrowError()
})
})

function setup() {
const userRepository = mock<UserRepository>()
const appState = container.resolve(AppState)

return {
userRepository,
appState,
autoLoginAction: new AutoLoginAction(appState, userRepository)
}
}
35 changes: 15 additions & 20 deletions src/modules/login/data-access/actions/auto-login-action.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,32 @@
import { action, makeObservable, runInAction } from 'mobx'
import { inject, singleton } from 'tsyringe'
import { singleton } from 'tsyringe'
import type { IAction } from 'shared/arch/interfaces/IAction'
import type { TokenStorage } from 'shared/api/oauth/token-storage/token-storage'
import { OAuthRepository } from 'shared/api/oauth/oauth-repository'
import { AppState } from 'shared/data-access/state/app-state'
import { UserRepository } from 'modules/login/data-access/repositories/user-repository'
import { AnonymousUserError } from '../errors/anonymous-user-error'

@singleton()
export class AutoLoginAction implements IAction<{ username: string; password: string }> {
constructor(
@inject('TokenStorage') private tokenStorage: TokenStorage,
private oauthRepository: OAuthRepository,
private appState: AppState,
private userRepository: UserRepository
) {
constructor(private appState: AppState, private userRepository: UserRepository) {
makeObservable(this)
}

@action
async execute(): Promise<void> {
const refreshToken = await this.tokenStorage.getRefreshToken()

if (refreshToken) {
const oauthResponse = await this.oauthRepository.renewOAuthByRefreshToken(refreshToken)
this.tokenStorage.setAccessToken(oauthResponse.access_token)
await this.tokenStorage.setRefreshToken(oauthResponse.refresh_token)

try {
const user = await this.userRepository.getUser()

runInAction(() => {
this.appState.isAuthenticated = true
this.appState.loggedUser = user
})
if (user) {
runInAction(() => {
this.appState.isAuthenticated = true
this.appState.loggedUser = user
})
}
} catch (error) {
const isAnonymousUser = error instanceof AnonymousUserError
if (!isAnonymousUser) {
throw error
}
}
}
}
44 changes: 0 additions & 44 deletions src/modules/login/data-access/actions/login-action.test.ts

This file was deleted.

Loading

0 comments on commit 6a0c542

Please sign in to comment.