richie-education 3.3.1-dev15 → 3.3.1-dev17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/js/api/auth/keycloak.spec.ts +1 -0
- package/js/api/auth/keycloak.ts +5 -1
- package/js/api/lms/openedx-fonzie-keycloak.spec.ts +26 -2
- package/js/api/lms/openedx-fonzie-keycloak.ts +22 -0
- package/js/components/SaleTunnel/SaleTunnelInformation/SaleTunnelInformationSingular.tsx +2 -1
- package/js/components/SaleTunnel/index.spec.tsx +1 -0
- package/js/pages/DashboardKeycloakProfile/index.spec.tsx +6 -6
- package/js/pages/DashboardKeycloakProfile/index.tsx +2 -1
- package/package.json +1 -1
package/js/api/auth/keycloak.ts
CHANGED
|
@@ -59,8 +59,12 @@ const API = (APIConf: AuthenticationBackend): { user: APIAuthentication } => {
|
|
|
59
59
|
.loadUserProfile()
|
|
60
60
|
.then((userProfile) => {
|
|
61
61
|
sessionStorage.setItem(RICHIE_USER_TOKEN, keycloak.idToken!);
|
|
62
|
+
const fullName = [userProfile.firstName, userProfile.lastName]
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.join(' ');
|
|
62
65
|
return {
|
|
63
|
-
username:
|
|
66
|
+
username: fullName,
|
|
67
|
+
full_name: fullName,
|
|
64
68
|
email: userProfile.email,
|
|
65
69
|
access_token: keycloak.idToken,
|
|
66
70
|
};
|
|
@@ -31,12 +31,36 @@ describe('Fonzie Keycloak API', () => {
|
|
|
31
31
|
jest.clearAllMocks();
|
|
32
32
|
});
|
|
33
33
|
|
|
34
|
-
it('uses its own route to get user information', async () => {
|
|
34
|
+
it('uses its own route to get user information and enriches with keycloak account', async () => {
|
|
35
35
|
const user = {
|
|
36
|
-
username:
|
|
36
|
+
username: 'test-richie-ncl',
|
|
37
|
+
full_name: 'n c',
|
|
38
|
+
};
|
|
39
|
+
const keycloakAccount = {
|
|
40
|
+
firstName: 'John',
|
|
41
|
+
lastName: 'Doe',
|
|
42
|
+
email: 'john.doe@example.com',
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
fetchMock.get('https://demo.endpoint.api/api/v1.0/user/me', user);
|
|
46
|
+
fetchMock.get('https://keycloak.test/auth/realms/richie-realm/account/', keycloakAccount);
|
|
47
|
+
|
|
48
|
+
const api = FonzieKeycloakAPIInterface(configuration);
|
|
49
|
+
await expect(api.user.me()).resolves.toEqual({
|
|
50
|
+
...user,
|
|
51
|
+
full_name: 'John Doe',
|
|
52
|
+
email: 'john.doe@example.com',
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('falls back to fonzie data when keycloak account call fails', async () => {
|
|
57
|
+
const user = {
|
|
58
|
+
username: 'test-richie-ncl',
|
|
59
|
+
full_name: 'n c',
|
|
37
60
|
};
|
|
38
61
|
|
|
39
62
|
fetchMock.get('https://demo.endpoint.api/api/v1.0/user/me', user);
|
|
63
|
+
fetchMock.get('https://keycloak.test/auth/realms/richie-realm/account/', 500);
|
|
40
64
|
|
|
41
65
|
const api = FonzieKeycloakAPIInterface(configuration);
|
|
42
66
|
await expect(api.user.me()).resolves.toEqual(user);
|
|
@@ -43,6 +43,28 @@ const API = (APIConf: AuthenticationBackend): APILms => {
|
|
|
43
43
|
...ApiInterface,
|
|
44
44
|
user: {
|
|
45
45
|
...ApiInterface.user,
|
|
46
|
+
me: async () => {
|
|
47
|
+
const user = await ApiInterface.user.me();
|
|
48
|
+
if (!user) return null;
|
|
49
|
+
try {
|
|
50
|
+
const keycloakAccount = await fetch(APIOptions.routes.user.account, {
|
|
51
|
+
credentials: 'include',
|
|
52
|
+
headers: { Accept: 'application/json' },
|
|
53
|
+
}).then((res) => {
|
|
54
|
+
if (!res.ok) throw new Error(`Keycloak account fetch failed: ${res.status}`);
|
|
55
|
+
return res.json();
|
|
56
|
+
});
|
|
57
|
+
return {
|
|
58
|
+
...user,
|
|
59
|
+
full_name:
|
|
60
|
+
[keycloakAccount.firstName, keycloakAccount.lastName].filter(Boolean).join(' ') ||
|
|
61
|
+
user.full_name,
|
|
62
|
+
email: keycloakAccount.email || user.email,
|
|
63
|
+
};
|
|
64
|
+
} catch {
|
|
65
|
+
return user;
|
|
66
|
+
}
|
|
67
|
+
},
|
|
46
68
|
login: () => {
|
|
47
69
|
const next = encodeURIComponent(location.href);
|
|
48
70
|
location.assign(`${APIOptions.routes.user.login}?next=${next}`);
|
|
@@ -14,6 +14,7 @@ import { HttpError } from 'utils/errors/HttpError';
|
|
|
14
14
|
import { APIBackend, KeycloakAccountApi } from 'types/api';
|
|
15
15
|
import context from 'utils/context';
|
|
16
16
|
import { AuthenticationApi } from 'api/authentication';
|
|
17
|
+
import { UserHelper } from 'utils/UserHelper';
|
|
17
18
|
|
|
18
19
|
const messages = defineMessages({
|
|
19
20
|
title: {
|
|
@@ -242,7 +243,7 @@ const KeycloakAccountEdit = () => {
|
|
|
242
243
|
<h4>
|
|
243
244
|
<FormattedMessage {...messages.keycloakUsernameLabel} />
|
|
244
245
|
</h4>
|
|
245
|
-
<div className="fw-bold">{user
|
|
246
|
+
<div className="fw-bold">{user ? UserHelper.getName(user) : ''}</div>
|
|
246
247
|
</div>
|
|
247
248
|
<div className="sale-tunnel__username__description">
|
|
248
249
|
<FormattedMessage {...messages.keycloakUsernameInfo} />
|
|
@@ -48,7 +48,7 @@ describe('pages.DashboardKeycloakProfile', () => {
|
|
|
48
48
|
AuthenticationApi!.account = originalAccount;
|
|
49
49
|
});
|
|
50
50
|
|
|
51
|
-
it('should render profile information', async () => {
|
|
51
|
+
it('should render profile information with full_name', async () => {
|
|
52
52
|
render(<DashboardKeycloakProfile />, {
|
|
53
53
|
queryOptions: { client: createTestQueryClient({ user: richieUser }) },
|
|
54
54
|
});
|
|
@@ -56,7 +56,7 @@ describe('pages.DashboardKeycloakProfile', () => {
|
|
|
56
56
|
expect(screen.getByText('Profile')).toBeInTheDocument();
|
|
57
57
|
expect(screen.getByText('Account information')).toBeInTheDocument();
|
|
58
58
|
|
|
59
|
-
expect(await screen.findByDisplayValue(richieUser.
|
|
59
|
+
expect(await screen.findByDisplayValue(richieUser.full_name!)).toBeInTheDocument();
|
|
60
60
|
expect(screen.getByDisplayValue(richieUser.email!)).toBeInTheDocument();
|
|
61
61
|
|
|
62
62
|
const editLink = screen.getByRole('link', { name: 'Edit your profile' });
|
|
@@ -64,14 +64,14 @@ describe('pages.DashboardKeycloakProfile', () => {
|
|
|
64
64
|
expect(editLink).toHaveAttribute('href', mockAccountUpdateUrl);
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
-
it('should
|
|
68
|
-
const
|
|
67
|
+
it('should fallback to username when full_name is empty', async () => {
|
|
68
|
+
const userWithoutFullName = UserFactory({ full_name: undefined, email: undefined }).one();
|
|
69
69
|
|
|
70
70
|
render(<DashboardKeycloakProfile />, {
|
|
71
|
-
queryOptions: { client: createTestQueryClient({ user:
|
|
71
|
+
queryOptions: { client: createTestQueryClient({ user: userWithoutFullName }) },
|
|
72
72
|
});
|
|
73
73
|
|
|
74
|
-
expect(await screen.findByDisplayValue(
|
|
74
|
+
expect(await screen.findByDisplayValue(userWithoutFullName.username)).toBeInTheDocument();
|
|
75
75
|
expect(screen.getByLabelText('Account email')).toHaveValue(DEFAULT_DISPLAYED_FORM_VALUE);
|
|
76
76
|
});
|
|
77
77
|
});
|
|
@@ -6,6 +6,7 @@ import { DashboardCard } from 'widgets/Dashboard/components/DashboardCard';
|
|
|
6
6
|
import Form from 'components/Form';
|
|
7
7
|
import { AuthenticationApi } from 'api/authentication';
|
|
8
8
|
import { KeycloakAccountApi } from 'types/api';
|
|
9
|
+
import { UserHelper } from 'utils/UserHelper';
|
|
9
10
|
|
|
10
11
|
const messages = defineMessages({
|
|
11
12
|
sectionHeader: {
|
|
@@ -63,7 +64,7 @@ const DashboardKeycloakProfile = () => {
|
|
|
63
64
|
name="username"
|
|
64
65
|
label={intl.formatMessage(messages.usernameInputLabel)}
|
|
65
66
|
disabled={true}
|
|
66
|
-
value={user
|
|
67
|
+
value={user ? UserHelper.getName(user) : DEFAULT_DISPLAYED_FORM_VALUE}
|
|
67
68
|
text={intl.formatMessage(messages.usernameInputDescription)}
|
|
68
69
|
/>
|
|
69
70
|
</Form.Row>
|