Skip to content

Commit 8eae7ba

Browse files
authored
[CA-4747] Profile editor should update consents with key in snakecase (#280)
1 parent 0febd99 commit 8eae7ba

File tree

4 files changed

+77
-9
lines changed

4 files changed

+77
-9
lines changed

src/components/form/fields/consentField.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ import React from 'react';
22

33
import styled from 'styled-components';
44

5+
import { ConsentType } from '@reachfive/identity-core';
6+
57
import { Checkbox } from '../formControlsComponent';
68
import { createField, type FieldComponentProps, type FieldDefinition } from '../fieldCreator';
79
import { MarkdownContent } from '../../miscComponent';
810

11+
import { PathMapping } from '../../../core/mapping';
912
import { checked, empty, isValidatorError } from '../../../core/validation';
1013
import { isRichFormValue } from '../../../helpers/utils';
11-
import { ConsentType } from '@reachfive/identity-core';
14+
import { snakeCasePath } from '../../../helpers/transformObjectProperties';
1215

1316
const Description = styled.div`
1417
font-size: ${props => props.theme.smallTextFontSize}px;
@@ -80,6 +83,7 @@ export default function consentField({
8083
...props,
8184
required,
8285
defaultValue: { granted: props.defaultValue ?? false },
86+
mapping: new PathMapping(snakeCasePath(props.path ?? props.key)), // Consent key should be snake_case
8387
format: {
8488
bind: value => value,
8589
unbind: value => value !== undefined

src/main.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,19 @@ export function createClient(creationConfig: Config): Client {
3434

3535
return fetch(`https://${creationConfig.domain}/identity/v1/config/consents?${toQueryString({ lang: language })}`)
3636
.then(response => response.json())
37-
.then(consentsVersions => {
37+
.then((consentsVersions: Record<string, ConsentVersions>) => {
3838
return fetch(`${remoteConfig.resourceBaseUrl}/${language}.json`)
3939
.then(response => response.json())
4040
.then(defaultI18n => {
4141
const config = {
4242
...creationConfig,
4343
...remoteConfig,
44-
consentsVersions: camelCaseProperties(consentsVersions) as Record<string, ConsentVersions>
44+
consentsVersions: Object.fromEntries(
45+
Object.entries(consentsVersions).map(([key, value]) => [
46+
key, // consents keys should be snake_case
47+
camelCaseProperties(value) as ConsentVersions
48+
])
49+
)
4550
}
4651
return new UiClient(config, coreClient, defaultI18n)
4752
})

src/widgets/profileEditor/profileEditorWidget.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,23 @@ export default createWidget<ProfileEditorWidgetProps, ProfileEditorProps>({
135135
accessToken,
136136
fields: resolvedFields.map(({ path }) => path).join(',')
137137
})
138-
.then(profile => camelCaseProperties(profile) as Profile) /** @todo check api response key format in sdk core */
138+
/**
139+
* @todo this can be removed when https://github.com/ReachFive/identity-web-core-sdk/pull/260 will be merged
140+
* L'idéal serait que la clé d'un consentement soit en valeur d'une propriété "key" et non une clé de la structure Map utilisée.
141+
* le mieux serait même que `consents` soit un array et non un map.
142+
* Mais bon, difficile de changer ça maintenant sans créer un breaking change pour les utilisateurs de l'api ou du sdk core...
143+
*/
144+
.then(({ consents, ...profile }) => ({
145+
...profile,
146+
consents: consents
147+
? Object.fromEntries(
148+
Object.entries(consents).map(([key, value]) => [
149+
key,
150+
camelCaseProperties(value) as UserConsent
151+
])
152+
)
153+
: undefined
154+
}) as Profile) /** @todo check api response key format in sdk core */
139155
.then((profile: Profile) => {
140156
const filteredProfileConsents = (profile.consents && Object.keys(profile.consents).length > 0) ? filterProfileConsents(fields, config.consentsVersions, profile.consents) : undefined;
141157
const filteredOutConsentsProfile = { ...profile, consents: filteredProfileConsents };
@@ -156,7 +172,11 @@ export default createWidget<ProfileEditorWidgetProps, ProfileEditorProps>({
156172
});
157173

158174
// Filter out the profile consents with different version than the one the given consent field own
159-
const filterProfileConsents = (fields: (string | Field)[], consentsVersions: Record<string, ConsentVersions>, profileConsents: Record<string, UserConsent>) => {
175+
const filterProfileConsents = (
176+
fields: (string | Field)[],
177+
consentsVersions: Record<string, ConsentVersions>,
178+
profileConsents: Record<string, UserConsent>
179+
) => {
160180
return Object.keys(profileConsents)
161181
.filter(profileConsentKey => {
162182
const consentField = fields.map(f => typeof f === 'string' ? f : f.key).find(field => field.startsWith(`consents.${profileConsentKey}`));

tests/widgets/profileEditor/profileEditorWidget.test.ts

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,19 @@ const defaultConfig: Config = {
3030
mfaSmsEnabled: false,
3131
mfaEmailEnabled: false,
3232
rbaEnabled: false,
33-
consentsVersions: {},
33+
consentsVersions: {
34+
'optinTesting': {
35+
key: 'optinTesting',
36+
consentType: 'opt-in',
37+
status: 'active',
38+
versions: [{
39+
versionId: 1,
40+
title: 'Opt-in Testing v1',
41+
language: 'fr',
42+
description: 'This is just a test'
43+
}]
44+
}
45+
},
3446
passwordPolicy: {
3547
minLength: 8,
3648
minStrength: 2,
@@ -116,9 +128,21 @@ describe('DOM testing', () => {
116128
test('default', async () => {
117129
const user = userEvent.setup()
118130

119-
const profile = {
131+
// @ts-expect-error partial Profile
132+
const profile: Profile = {
120133
givenName: 'John',
121134
familyName: 'Do',
135+
consents: {
136+
optinTesting: {
137+
granted: false,
138+
consentType: 'opt-in',
139+
consentVersion: {
140+
versionId: 1,
141+
language: 'fr'
142+
},
143+
date: '2021-01-01T00:00:00.000Z',
144+
}
145+
}
122146
}
123147

124148
getUser.mockResolvedValue(profile as Profile)
@@ -128,14 +152,15 @@ describe('DOM testing', () => {
128152
await generateComponent({
129153
fields: [
130154
'given_name',
131-
'family_name'
155+
'family_name',
156+
'optinTesting'
132157
]
133158
})
134159

135160
expect(getUser).toBeCalledWith(
136161
expect.objectContaining({
137162
accessToken: 'azerty',
138-
fields: 'givenName,familyName',
163+
fields: 'givenName,familyName,consents.optinTesting',
139164
})
140165
)
141166

@@ -152,6 +177,10 @@ describe('DOM testing', () => {
152177
await userEvent.clear(familyNameInput)
153178
await userEvent.type(familyNameInput, 'reachfive')
154179

180+
// const consentCheckbox = screen.getByTestId('consents.optinTesting.1')
181+
const consentCheckbox = screen.getByLabelText(defaultConfig.consentsVersions['optinTesting'].versions[0].title)
182+
await user.click(consentCheckbox)
183+
155184
const submitBtn = screen.getByRole('button', { name: 'save'})
156185
expect(submitBtn).toBeInTheDocument()
157186

@@ -163,6 +192,16 @@ describe('DOM testing', () => {
163192
data: {
164193
givenName: 'alice',
165194
familyName: 'reachfive',
195+
consents: {
196+
optin_testing: { // consent key should be snakecase
197+
granted: true,
198+
consentType: 'opt-in',
199+
consentVersion: {
200+
versionId: 1,
201+
language: 'fr'
202+
}
203+
}
204+
}
166205
}
167206
})
168207
)

0 commit comments

Comments
 (0)