Skip to content

Commit 163d638

Browse files
committed
Remove LoginAction dependency on AlertStream
1 parent 6b0e1ad commit 163d638

File tree

9 files changed

+72
-68
lines changed

9 files changed

+72
-68
lines changed
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import type { UserInterface } from './models/userInterface'
2+
3+
/**
4+
* API Interfaces - What the API expects and what it returns
5+
*
6+
* This interface is tied to the `LoginAction` API, accessed at the
7+
* POST `/account/login` endpoint.
8+
*/
9+
export interface LoginRequest {
10+
user_name: string
11+
password: string
12+
}
13+
14+
export interface LoginResponse {
15+
user: UserInterface
16+
message: string
17+
redirect: string
18+
}

packages/sprinkle-account/app/assets/interfaces/forms/loginForm.ts

Lines changed: 0 additions & 10 deletions
This file was deleted.

packages/sprinkle-account/app/assets/interfaces/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
export type { LoginForm } from './forms/loginForm'
21
export type { UserInterface } from './models/userInterface'
32
export type { GroupInterface } from './models/groupInterface'
43
export type { RoleInterface } from './models/roleInterface'
@@ -8,3 +7,4 @@ export type { ProfileEditRequest } from './ProfileEditApi'
87
export type { PasswordEditRequest } from './PasswordEditApi'
98
export type { EmailEditRequest } from './EmailEditApi'
109
export type { RegisterRequest, RegisterResponse } from './RegisterApi'
10+
export type { LoginRequest, LoginResponse } from './LoginApi'

packages/sprinkle-account/app/assets/stores/auth.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { defineStore } from 'pinia'
22
import axios from 'axios'
3-
import type { UserInterface, LoginForm } from '../interfaces'
3+
import type { UserInterface, LoginRequest, LoginResponse } from '../interfaces'
44
import { type AlertInterface, Severity } from '@userfrosting/sprinkle-core/interfaces'
55
import { useTranslator } from '@userfrosting/sprinkle-core/stores'
66

@@ -26,16 +26,16 @@ export const useAuthStore = defineStore('auth', {
2626
unsetUser(): void {
2727
this.user = null
2828
},
29-
async login(form: LoginForm) {
29+
async login(form: LoginRequest) {
3030
return axios
31-
.post<UserInterface>('/account/login', form)
31+
.post<LoginResponse>('/account/login', form)
3232
.then((response) => {
33-
this.setUser(response.data)
33+
this.setUser(response.data.user)
3434

3535
// Reload the translator dictionary to reflect the user's language
3636
useTranslator().load()
3737

38-
return this.user
38+
return response.data
3939
})
4040
.catch((err) => {
4141
const error: AlertInterface = {

packages/sprinkle-account/app/assets/tests/stores/auth.test.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { setActivePinia, createPinia } from 'pinia'
22
import { beforeEach, describe, expect, test, vi } from 'vitest'
33
import { useAuthStore } from '../../stores/auth'
44
import axios from 'axios'
5-
import type { LoginForm, UserInterface } from 'app/assets/interfaces'
5+
import type { LoginRequest, LoginResponse, UserInterface } from 'app/assets/interfaces'
66
import { Severity } from '@userfrosting/sprinkle-core/interfaces'
77

88
const testUser: UserInterface = {
@@ -22,7 +22,7 @@ const testUser: UserInterface = {
2222
deleted_at: null
2323
}
2424

25-
const form: LoginForm = {
25+
const form: LoginRequest = {
2626
user_name: 'john',
2727
password: 'password'
2828
}
@@ -62,8 +62,12 @@ describe('authStore', () => {
6262
test('should login successfully', async () => {
6363
// Arrange
6464
const authStore = useAuthStore()
65-
const response = { data: testUser }
66-
vi.spyOn(axios, 'post').mockResolvedValue(response as any)
65+
const response: LoginResponse = {
66+
user: testUser,
67+
message: 'Welcome back John Doe!',
68+
redirect: '/dashboard'
69+
}
70+
vi.spyOn(axios, 'post').mockResolvedValue({ data: response })
6771

6872
// Assert initial state
6973
expect(authStore.user).toBeNull()
@@ -73,7 +77,7 @@ describe('authStore', () => {
7377

7478
// Assert
7579
expect(axios.post).toHaveBeenCalledWith('/account/login', form)
76-
expect(result).toStrictEqual(testUser)
80+
expect(result).toStrictEqual(response)
7781
expect(authStore.user).toStrictEqual(testUser)
7882
expect(loadTranslator).toHaveBeenCalled()
7983
})

packages/sprinkle-account/app/src/Controller/LoginAction.php

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,14 @@
1515
use Psr\EventDispatcher\EventDispatcherInterface;
1616
use Psr\Http\Message\ResponseInterface as Response;
1717
use Psr\Http\Message\ServerRequestInterface as Request;
18-
use UserFrosting\Alert\AlertStream;
1918
use UserFrosting\Config\Config;
2019
use UserFrosting\Fortress\RequestSchema;
2120
use UserFrosting\Fortress\RequestSchema\RequestSchemaInterface;
2221
use UserFrosting\Fortress\Transformer\RequestDataTransformer;
2322
use UserFrosting\Fortress\Validator\ServerSideValidator;
23+
use UserFrosting\I18n\Translator;
2424
use UserFrosting\Sprinkle\Account\Authenticate\Authenticator;
25+
use UserFrosting\Sprinkle\Account\Database\Models\Interfaces\UserInterface;
2526
use UserFrosting\Sprinkle\Account\Event\UserRedirectedAfterLoginEvent;
2627
use UserFrosting\Sprinkle\Account\Exceptions\AccountException;
2728
use UserFrosting\Sprinkle\Account\Exceptions\InvalidCredentialsException;
@@ -57,7 +58,7 @@ class LoginAction
5758
* @param \UserFrosting\Event\EventDispatcher $eventDispatcher
5859
*/
5960
public function __construct(
60-
protected AlertStream $alert,
61+
protected Translator $translator,
6162
protected Authenticator $authenticator,
6263
protected Config $config,
6364
protected EventDispatcherInterface $eventDispatcher,
@@ -76,29 +77,37 @@ public function __construct(
7677
*/
7778
public function __invoke(Request $request, Response $response): Response
7879
{
79-
$this->handle($request);
80-
$response = $this->writeResponse($response);
80+
$user = $this->handle($request);
81+
$response = $this->writeResponse($response, $user);
8182

8283
return $response->withHeader('Content-Type', 'application/json');
8384
}
8485

8586
/**
8687
* Write to the response object.
8788
*
88-
* @param Response $response
89+
* @param Response $response
90+
* @param UserInterface $user
8991
*
9092
* @return Response
9193
*/
92-
protected function writeResponse(Response $response): Response
94+
protected function writeResponse(Response $response, UserInterface $user): Response
9395
{
9496
// Get redirect target and add Header
9597
$event = $this->eventDispatcher->dispatch(new UserRedirectedAfterLoginEvent());
9698
if ($event->getRedirect() !== null) {
9799
$response = $response->withHeader('UF-Redirect', $event->getRedirect());
98100
}
99101

102+
// Define payload
103+
$data = [
104+
'user' => $user,
105+
'message' => $this->translator->translate('WELCOME', $user->toArray()),
106+
'redirect' => $event->getRedirect() ?? '',
107+
];
108+
100109
// Write response with the user info in it
101-
$payload = json_encode($this->authenticator->user(), JSON_THROW_ON_ERROR);
110+
$payload = json_encode($data, JSON_THROW_ON_ERROR);
102111
$response->getBody()->write($payload);
103112

104113
return $response;
@@ -108,8 +117,10 @@ protected function writeResponse(Response $response): Response
108117
* Handle the request and return the payload.
109118
*
110119
* @param Request $request
120+
*
121+
* @return UserInterface
111122
*/
112-
protected function handle(Request $request): void
123+
protected function handle(Request $request): UserInterface
113124
{
114125
// Get POST parameters
115126
$params = (array) $request->getParsedBody();
@@ -156,8 +167,7 @@ protected function handle(Request $request): void
156167
throw new InvalidCredentialsException();
157168
}
158169

159-
// Add success message
160-
$this->alert->addMessage('success', 'WELCOME', $currentUser->toArray());
170+
return $currentUser;
161171
}
162172

163173
/**

packages/sprinkle-account/app/tests/Controller/LoginActionTest.php

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
use Mockery;
1616
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
17-
use UserFrosting\Alert\AlertStream;
1817
use UserFrosting\Config\Config;
1918
use UserFrosting\Sprinkle\Account\Account;
2019
use UserFrosting\Sprinkle\Account\Authenticate\Authenticator;
@@ -59,18 +58,14 @@ public function testLogin(): void
5958
$response = $this->handleRequest($request);
6059

6160
// Assert response status & body
62-
$this->assertJsonResponse($user->toArray(), $response);
61+
$this->assertJsonResponse($user->toArray(), $response, 'user');
62+
$this->assertJsonResponse('Welcome back, ' . $user->full_name . '!', $response, 'message');
63+
$this->assertJsonResponse('/home', $response, 'redirect');
6364
$this->assertResponseStatus(200, $response);
6465

6566
// Assert Event Redirect
6667
$this->assertSame('/home', $response->getHeaderLine('UF-Redirect'));
6768

68-
// Test message
69-
/** @var AlertStream */
70-
$ms = $this->ci->get(AlertStream::class);
71-
$messages = $ms->getAndClearMessages();
72-
$this->assertSame('success', array_reverse($messages)[0]['type']);
73-
7469
// We have to logout the user to avoid problem
7570
/** @var Authenticator */
7671
$authenticator = $this->ci->get(Authenticator::class);
@@ -93,7 +88,7 @@ public function testLoginWithEmail(): void
9388
$response = $this->handleRequest($request);
9489

9590
// Assert response status & body
96-
$this->assertJsonResponse($user->toArray(), $response);
91+
$this->assertJsonResponse($user->toArray(), $response, 'user');
9792
$this->assertResponseStatus(200, $response);
9893

9994
// We have to logout the user to avoid problem
@@ -215,7 +210,7 @@ public function testLoginThrottlerDoesNotCountSuccessfulLogins(): void
215210
$response = $this->handleRequest($request);
216211

217212
// Assert response status & body
218-
$this->assertJsonResponse($user->toArray(), $response);
213+
$this->assertJsonResponse($user->toArray(), $response, 'user');
219214
$this->assertResponseStatus(200, $response);
220215

221216
// We have to logout the user to avoid problem

packages/theme-pink-cupcake/src/components/Pages/Account/FormLogin.vue

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
import { ref } from 'vue'
33
import UIkit from 'uikit'
44
import type { AlertInterface } from '@userfrosting/sprinkle-core/interfaces'
5-
import type { LoginForm } from '@userfrosting/sprinkle-account/interfaces'
5+
import type { LoginRequest } from '@userfrosting/sprinkle-account/interfaces'
66
import { useAuthStore } from '@userfrosting/sprinkle-account/stores'
7-
import { useTranslator } from '@userfrosting/sprinkle-core/stores'
87
98
// Variables
109
const loading = ref(false)
1110
const error = ref<AlertInterface | null>()
12-
let form: LoginForm = {
11+
let form: LoginRequest = {
1312
user_name: '',
1413
password: ''
1514
}
@@ -21,17 +20,19 @@ async function sendLogin() {
2120
const auth = useAuthStore()
2221
await auth
2322
.login(form)
24-
.then((user) => {
25-
// TODO : If the notification can be defined in sprinkle-core, and
26-
// implemented in the theme, this notification could be moved to the
27-
// API directly.
28-
const { translate } = useTranslator()
23+
.then((response) => {
2924
UIkit.notification({
30-
message: translate('WELCOME', user ?? {}), // TODO : Same error as before, the locale is not updated yet and this is not a reactive variable.
25+
message: response.message,
3126
status: 'primary',
3227
pos: 'top-right',
3328
timeout: 4000
3429
})
30+
31+
// If a redirect is set, redirect to it
32+
// TODO : Dilemma, should we redirect using VueRouter or not?
33+
// if (response.redirect) {
34+
// window.location.href = response.redirect
35+
// }
3536
})
3637
.catch((err: AlertInterface) => {
3738
error.value = err

packages/theme-pink-cupcake/src/tests/components/Content/FormLogin.test.ts

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import FormLogin from '../../../components/Pages/Account/FormLogin.vue'
44
import { useAuthStore } from '@userfrosting/sprinkle-account/stores'
55
import UFAlert from '../../../components/UFAlert.vue'
66
import UIkit from 'uikit'
7-
import type { LoginForm } from '@userfrosting/sprinkle-account/interfaces'
7+
import type { LoginRequest } from '@userfrosting/sprinkle-account/interfaces'
88
import type { AlertInterface } from '@userfrosting/sprinkle-core/interfaces'
99

1010
// Register the UFAlert component stub globally
@@ -18,7 +18,7 @@ const mockUseAuthStore = {
1818
}
1919

2020
// Login form data
21-
const form: LoginForm = {
21+
const form: LoginRequest = {
2222
user_name: 'john.doe',
2323
password: 'password'
2424
}
@@ -42,20 +42,13 @@ describe('FormLogin.vue', () => {
4242
})
4343

4444
test('handles successful login', async () => {
45-
mockUseAuthStore.login.mockResolvedValueOnce({ full_name: 'John Doe' })
45+
mockUseAuthStore.login.mockResolvedValueOnce({ message: 'Welcome back John Doe!' })
4646
vi.mocked(useAuthStore).mockReturnValue(mockUseAuthStore as any)
4747
vi.spyOn(UIkit, 'notification')
4848

4949
const wrapper = mount(FormLogin)
5050
await (wrapper.vm as any).sendLogin(form)
5151

52-
// Mock the useTranslator store
53-
vi.mock('@userfrosting/sprinkle-core/stores', () => ({
54-
useTranslator: () => ({
55-
translate: vi.fn(() => 'Welcome back John Doe!')
56-
})
57-
}))
58-
5952
// Spy on the authStore & UIkit notification method
6053
expect(useAuthStore).toHaveBeenCalled()
6154
expect(mockUseAuthStore.login).toHaveBeenCalledTimes(1)
@@ -93,17 +86,10 @@ describe('FormLogin.vue', () => {
9386
})
9487

9588
test('Handle login using the v-model', async () => {
96-
mockUseAuthStore.login.mockResolvedValueOnce({ full_name: 'John Doe' })
89+
mockUseAuthStore.login.mockResolvedValueOnce({ message: 'Welcome back John Doe!' })
9790
vi.mocked(useAuthStore).mockReturnValue(mockUseAuthStore as any)
9891
vi.spyOn(UIkit, 'notification')
9992

100-
// Mock the useTranslator store
101-
vi.mock('@userfrosting/sprinkle-core/stores', () => ({
102-
useTranslator: () => ({
103-
translate: vi.fn(() => 'Welcome back John Doe!')
104-
})
105-
}))
106-
10793
const wrapper = mount(FormLogin)
10894
wrapper.find('[data-test="username"]').setValue('john.doe')
10995
wrapper.find('[data-test="password"]').setValue('password')

0 commit comments

Comments
 (0)