Skip to content

Commit 9213bb9

Browse files
committed
fix(davinci-client): add validation props to collectors
1 parent 4fbb22c commit 9213bb9

File tree

6 files changed

+152
-36
lines changed

6 files changed

+152
-36
lines changed

.changeset/fine-windows-search.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@forgerock/davinci-client': patch
3+
---
4+
5+
Exposes `required` and `validatePhoneNumber` properties on collectors

packages/davinci-client/src/lib/client.store.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import type {
3333
ObjectValueCollectors,
3434
PhoneNumberInputValue,
3535
AutoCollectors,
36+
MultiValueCollectors,
3637
} from './collector.types.js';
3738
import type {
3839
InitFlow,
@@ -290,11 +291,13 @@ export async function davinci<ActionType extends ActionTypes = ActionTypes>({
290291

291292
/**
292293
* @method validate - Method for validating the value against validation rules
293-
* @param {SingleValueCollector} collector - the collector to validate
294+
* @param {SingleValueCollectors | ObjectValueCollectors | MultiValueCollectors} collector - the collector to validate
294295
* @returns {function} - a function to call for validating collector value
295-
* @throws {Error} - if the collector is not a SingleValueCollector
296+
* @throws {Error} - if the collector cannot be validated
296297
*/
297-
validate: (collector: SingleValueCollectors): Validator => {
298+
validate: (
299+
collector: SingleValueCollectors | ObjectValueCollectors | MultiValueCollectors,
300+
): Validator => {
298301
if (!collector.id) {
299302
return handleUpdateValidateError(
300303
'Argument for `collector` has no ID',
@@ -317,9 +320,13 @@ export async function davinci<ActionType extends ActionTypes = ActionTypes>({
317320
return handleUpdateValidateError('Collector not found', 'state_error', log.error);
318321
}
319322

320-
if (collectorToUpdate.category !== 'ValidatedSingleValueCollector') {
323+
if (
324+
collectorToUpdate.category !== 'ValidatedSingleValueCollector' &&
325+
collectorToUpdate.category !== 'ObjectValueCollector' &&
326+
collectorToUpdate.category !== 'MultiValueCollector'
327+
) {
321328
return handleUpdateValidateError(
322-
'Collector is not a SingleValueCollector and cannot be validated',
329+
'Collector is not a SingleValueCollector, ObjectValueCollector, or MultiValueCollector and cannot be validated',
323330
'state_error',
324331
log.error,
325332
);

packages/davinci-client/src/lib/collector.types.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ interface ValidationRegex {
3737
rule: string;
3838
}
3939

40+
interface ValidationPhoneNumber {
41+
type: 'validatePhoneNumber';
42+
message: string;
43+
rule: boolean;
44+
}
45+
4046
export interface SingleValueCollectorWithValue<T extends SingleValueCollectorTypes> {
4147
category: 'SingleValueCollector';
4248
error: string | null;
@@ -196,6 +202,7 @@ export interface MultiValueCollectorWithValue<T extends MultiValueCollectorTypes
196202
key: string;
197203
value: string[];
198204
type: string;
205+
validation?: ValidationRequired[];
199206
};
200207
output: {
201208
key: string;
@@ -216,12 +223,12 @@ export interface MultiValueCollectorNoValue<T extends MultiValueCollectorTypes>
216223
key: string;
217224
value: string[];
218225
type: string;
226+
validation?: ValidationRequired[];
219227
};
220228
output: {
221229
key: string;
222230
label: string;
223231
type: string;
224-
value: string[];
225232
options: SelectorOption[];
226233
};
227234
}
@@ -308,6 +315,7 @@ export interface ObjectOptionsCollectorWithStringValue<
308315
key: string;
309316
value: V;
310317
type: string;
318+
validation?: ValidationRequired[];
311319
};
312320
output: {
313321
key: string;
@@ -331,6 +339,7 @@ export interface ObjectOptionsCollectorWithObjectValue<
331339
key: string;
332340
value: V;
333341
type: string;
342+
validation?: ValidationRequired[];
334343
};
335344
output: {
336345
key: string;
@@ -355,6 +364,7 @@ export interface ObjectValueCollectorWithObjectValue<
355364
key: string;
356365
value: IV;
357366
type: string;
367+
validation?: (ValidationRequired | ValidationPhoneNumber)[];
358368
};
359369
output: {
360370
key: string;

packages/davinci-client/src/lib/collector.utils.test.ts

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,12 @@ import type {
3030
RedirectField,
3131
StandardField,
3232
} from './davinci.types.js';
33-
import { PhoneNumberOutputValue, ValidatedTextCollector } from './collector.types.js';
33+
import {
34+
MultiSelectCollector,
35+
PhoneNumberCollector,
36+
PhoneNumberOutputValue,
37+
ValidatedTextCollector,
38+
} from './collector.types.js';
3439

3540
describe('Action Collectors', () => {
3641
describe('returnFlowCollector', () => {
@@ -410,6 +415,7 @@ describe('Multi-Value Collectors', () => {
410415
const result = returnMultiSelectCollector(comboField, 1, []);
411416
expect(result.type).toBe('MultiSelectCollector');
412417
expect(result.output).toHaveProperty('value', []);
418+
expect(result.input).toHaveProperty('validation');
413419
});
414420
});
415421
});
@@ -438,7 +444,7 @@ describe('Object value collectors', () => {
438444
description: 'device2-value',
439445
},
440446
],
441-
required: true,
447+
required: false,
442448
};
443449

444450
const transformedDevices = mockField.options.map((device) => ({
@@ -524,6 +530,13 @@ describe('Object value collectors', () => {
524530
key: mockField.key,
525531
value: '',
526532
type: mockField.type,
533+
validation: [
534+
{
535+
message: 'Value cannot be empty',
536+
rule: true,
537+
type: 'required',
538+
},
539+
],
527540
},
528541
output: {
529542
key: mockField.key,
@@ -561,6 +574,18 @@ describe('returnPhoneNumberCollector', () => {
561574
phoneNumber: '',
562575
},
563576
type: mockField.type,
577+
validation: [
578+
{
579+
message: 'Value cannot be empty',
580+
rule: true,
581+
type: 'required',
582+
},
583+
{
584+
message: 'Phone number should be validated',
585+
rule: true,
586+
type: 'validatePhoneNumber',
587+
},
588+
],
564589
},
565590
output: {
566591
key: mockField.key,
@@ -580,8 +605,8 @@ describe('returnPhoneNumberCollector', () => {
580605
defaultCountryCode: 'US',
581606
label: 'Phone Number',
582607
type: 'PHONE_NUMBER',
583-
required: true,
584-
validatePhoneNumber: true,
608+
required: false,
609+
validatePhoneNumber: false,
585610
};
586611
const result = returnObjectValueCollector(mockField, 1, {});
587612
expect(result).toEqual({
@@ -616,8 +641,8 @@ describe('returnPhoneNumberCollector', () => {
616641
defaultCountryCode: 'US',
617642
label: 'Phone Number',
618643
type: 'PHONE_NUMBER',
619-
required: true,
620-
validatePhoneNumber: true,
644+
required: false,
645+
validatePhoneNumber: false,
621646
};
622647
const prefillMock: PhoneNumberOutputValue = {
623648
countryCode: 'CA',
@@ -657,8 +682,8 @@ describe('returnPhoneNumberCollector', () => {
657682
defaultCountryCode: null,
658683
label: 'Phone Number',
659684
type: 'PHONE_NUMBER',
660-
required: true,
661-
validatePhoneNumber: true,
685+
required: false,
686+
validatePhoneNumber: false,
662687
};
663688
const prefillMock: PhoneNumberOutputValue = {
664689
phoneNumber: '1234567890',
@@ -696,8 +721,8 @@ describe('returnPhoneNumberCollector', () => {
696721
defaultCountryCode: 'US',
697722
label: 'Phone Number',
698723
type: 'PHONE_NUMBER',
699-
required: true,
700-
validatePhoneNumber: true,
724+
required: false,
725+
validatePhoneNumber: false,
701726
};
702727
const prefillMock: PhoneNumberOutputValue = {
703728
countryCode: 'CA',
@@ -778,17 +803,40 @@ describe('Return collector validator', () => {
778803
const validatedTextCollector = {
779804
input: {
780805
validation: [
781-
{ type: 'required', message: 'This field is required' },
806+
{ type: 'required', message: 'This field is required', rule: true },
782807
{ type: 'regex', message: 'Invalid format', rule: '^[a-zA-Z0-9]+$' },
783808
],
784809
},
785810
} as ValidatedTextCollector;
786811

812+
const objectValueCollector = {
813+
input: {
814+
validation: [
815+
{ type: 'required', message: 'This field is required', rule: true },
816+
{ type: 'validatePhoneNumber', message: 'Phone number should be validated', rule: true },
817+
],
818+
},
819+
} as PhoneNumberCollector;
820+
821+
const multiValueCollector = {
822+
input: {
823+
validation: [{ type: 'required', message: 'This field is required', rule: true }],
824+
},
825+
} as MultiSelectCollector;
826+
787827
const validator = returnValidator(validatedTextCollector);
828+
const objValidator = returnValidator(objectValueCollector);
829+
const multiValueValidator = returnValidator(multiValueCollector);
788830

789831
it('should return an error message for required validation when value is empty', () => {
790832
const result = validator('');
791833
expect(result).toContain('This field is required');
834+
835+
const objResult = objValidator({});
836+
expect(objResult).toContain('This field is required');
837+
838+
const multiValueResult = multiValueValidator({});
839+
expect(multiValueResult).toContain('This field is required');
792840
});
793841

794842
it('should return an error message for regex validation when value does not match the pattern', () => {
@@ -799,6 +847,12 @@ describe('Return collector validator', () => {
799847
it('should return no error messages when value passes all validations', () => {
800848
const result = validator('validValue123');
801849
expect(result).toEqual([]);
850+
851+
const objResult = objValidator({ countryCode: 'US', phoneNumber: '1234567890' });
852+
expect(objResult).toEqual([]);
853+
854+
const multiValueResult = multiValueValidator(['a', 'b', 'c']);
855+
expect(multiValueResult).toEqual([]);
802856
});
803857

804858
it('should handle invalid regex patterns gracefully', () => {

0 commit comments

Comments
 (0)