Skip to content

Commit 8693a1d

Browse files
committed
feat(davinci-client): add metadata collector (SDKS-5100)
1 parent 493f264 commit 8693a1d

18 files changed

Lines changed: 429 additions & 88 deletions

‎.changeset/real-swans-leave.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@forgerock/davinci-client': minor
3+
---
4+
5+
Add MetadataCollector and error helper on client
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Copyright (c) 2026 Ping Identity Corporation. All rights reserved.
3+
*
4+
* This software may be modified and distributed under the terms
5+
* of the MIT license. See the LICENSE file for details.
6+
*/
7+
import type { MetadataCollector, MetadataError, Updater } from '@forgerock/davinci-client/types';
8+
9+
export default function metadataComponent(
10+
formEl: HTMLFormElement,
11+
updater: Updater<MetadataCollector>,
12+
getMetadataError: (errorDetails: MetadataError) => MetadataError,
13+
submitForm: () => Promise<void>,
14+
) {
15+
const successBtn = document.createElement('button');
16+
successBtn.type = 'button';
17+
successBtn.innerHTML = 'Metadata Success';
18+
19+
const errorBtn = document.createElement('button');
20+
errorBtn.type = 'button';
21+
errorBtn.innerHTML = 'Metadata Error';
22+
23+
formEl.appendChild(successBtn);
24+
formEl.appendChild(errorBtn);
25+
26+
successBtn.onclick = async () => {
27+
updater({ status: 'succeeded' });
28+
await submitForm();
29+
};
30+
31+
errorBtn.onclick = async () => {
32+
const metadataError = getMetadataError({ code: 'ERROR_CODE', message: 'Operation cancelled' });
33+
updater(metadataError);
34+
await submitForm();
35+
};
36+
}

‎e2e/davinci-app/main.ts‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import qrCodeComponent from './components/qr-code.js';
3838
import formImageComponent from './components/form-image.js';
3939
import pollingComponent from './components/polling.js';
4040
import booleanComponent from './components/boolean.js';
41+
import metadataComponent from './components/metadata.js';
4142

4243
const loggerFn = {
4344
error: () => {
@@ -289,6 +290,13 @@ const urlParams = new URLSearchParams(window.location.search);
289290
davinciClient.update(collector), // Returns an update function for this collector
290291
submitForm,
291292
);
293+
} else if (collector.type === 'MetadataCollector') {
294+
metadataComponent(
295+
formEl, // You can ignore this; it's just for rendering
296+
davinciClient.update(collector), // Returns an update function for this collector
297+
davinciClient.getMetadataError,
298+
submitForm,
299+
);
292300
} else if (collector.type === 'BooleanCollector') {
293301
booleanComponent(formEl, collector, davinciClient.update(collector));
294302
} else if (collector.type === 'ValidatedBooleanCollector') {

‎packages/davinci-client/api-report/davinci-client.api.md‎

Lines changed: 44 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ export interface AutoCollector<C extends AutoCollectorCategories, T extends Auto
140140
export type AutoCollectorCategories = 'SingleValueAutoCollector' | 'ObjectValueAutoCollector';
141141

142142
// @public (undocumented)
143-
export type AutoCollectors = ProtectCollector | FidoRegistrationCollector | FidoAuthenticationCollector | PollingCollector | SingleValueAutoCollector | ObjectValueAutoCollector;
143+
export type AutoCollectors = ProtectCollector | FidoRegistrationCollector | FidoAuthenticationCollector | MetadataCollector | PollingCollector | SingleValueAutoCollector | ObjectValueAutoCollector;
144144

145145
// @public (undocumented)
146146
export type AutoCollectorTypes = SingleValueAutoCollectorTypes | ObjectValueAutoCollectorTypes;
@@ -173,7 +173,7 @@ export interface CollectorRichContent {
173173
}
174174

175175
// @public (undocumented)
176-
export type Collectors = FlowCollector | PasswordCollector | ValidatedPasswordCollector | TextCollector | BooleanCollector | ValidatedBooleanCollector | SingleSelectCollector | IdpCollector | SubmitCollector | ActionCollector<'ActionCollector'> | SingleValueCollector<'SingleValueCollector'> | MultiSelectCollector | DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | PhoneNumberExtensionCollector | ReadOnlyCollector | RichTextCollector | ValidatedTextCollector | ProtectCollector | PollingCollector | FidoRegistrationCollector | FidoAuthenticationCollector | QrCodeCollector | ImageCollector | UnknownCollector;
176+
export type Collectors = FlowCollector | MetadataCollector | PasswordCollector | ValidatedPasswordCollector | TextCollector | BooleanCollector | ValidatedBooleanCollector | SingleSelectCollector | IdpCollector | SubmitCollector | ActionCollector<'ActionCollector'> | SingleValueCollector<'SingleValueCollector'> | MultiSelectCollector | DeviceAuthenticationCollector | DeviceRegistrationCollector | PhoneNumberCollector | PhoneNumberExtensionCollector | ReadOnlyCollector | RichTextCollector | ValidatedTextCollector | ProtectCollector | PollingCollector | FidoRegistrationCollector | FidoAuthenticationCollector | QrCodeCollector | ImageCollector | UnknownCollector;
177177

178178
// @public
179179
export type CollectorValueType<T> = T extends {
@@ -211,6 +211,8 @@ export type CollectorValueType<T> = T extends {
211211
} ? FidoRegistrationInputValue : T extends {
212212
type: 'FidoAuthenticationCollector';
213213
} ? FidoAuthenticationInputValue : T extends {
214+
type: 'MetadataCollector';
215+
} ? Record<string, unknown> | MetadataError : T extends {
214216
category: 'SingleValueCollector';
215217
} ? string : T extends {
216218
category: 'ValidatedSingleValueCollector';
@@ -225,10 +227,10 @@ export type CollectorValueType<T> = T extends {
225227
} ? never : CollectorValueTypes;
226228

227229
// @public
228-
export type CollectorValueTypes = string | string[] | boolean | PhoneNumberInputValue | PhoneNumberExtensionInputValue | FidoRegistrationInputValue | FidoAuthenticationInputValue;
230+
export type CollectorValueTypes = string | string[] | boolean | PhoneNumberInputValue | PhoneNumberExtensionInputValue | FidoRegistrationInputValue | FidoAuthenticationInputValue | MetadataError;
229231

230232
// @public (undocumented)
231-
export type ComplexValueFields = DeviceAuthenticationField | DeviceRegistrationField | PhoneNumberField | PhoneNumberExtensionField | FidoRegistrationField | FidoAuthenticationField | PollingField;
233+
export type ComplexValueFields = DeviceAuthenticationField | DeviceRegistrationField | PhoneNumberField | PhoneNumberExtensionField | FidoRegistrationField | FidoAuthenticationField | PollingField | MetadataField;
232234

233235
// @public (undocumented)
234236
export interface ContinueNode {
@@ -283,16 +285,19 @@ export function davinci<ActionType extends ActionTypes = ActionTypes>(input: {
283285
resume: (input: {
284286
continueToken: string;
285287
}) => Promise<InternalErrorResponse | NodeStates>;
286-
start: <QueryParams extends OutgoingQueryParams = OutgoingQueryParams>(options?: StartOptions<QueryParams> | undefined) => Promise<ContinueNode | ErrorNode | FailureNode | StartNode | SuccessNode>;
288+
start: <QueryParams extends OutgoingQueryParams = OutgoingQueryParams>(options?: StartOptions<QueryParams> | undefined) => Promise<ContinueNode | StartNode | ErrorNode | FailureNode | SuccessNode>;
287289
update: <T extends SingleValueCollectors | MultiSelectCollector | ObjectValueCollectors | AutoCollectors>(collector: T) => Updater<T>;
288290
validate: (collector: SingleValueCollectors | ObjectValueCollectors | MultiValueCollectors | AutoCollectors) => Validator;
289291
pollStatus: (collector: PollingCollector) => Poller;
292+
getMetadataError: (errorDetails: MetadataError) => MetadataError;
290293
getClient: () => {
291294
action: string;
292295
collectors: Collectors[];
293296
description?: string;
294297
name?: string;
295298
status: "continue";
299+
} | {
300+
status: "start";
296301
} | {
297302
action: string;
298303
collectors: Collectors[];
@@ -301,8 +306,6 @@ export function davinci<ActionType extends ActionTypes = ActionTypes>(input: {
301306
status: "error";
302307
} | {
303308
status: "failure";
304-
} | {
305-
status: "start";
306309
} | {
307310
authorization?: {
308311
code?: string;
@@ -313,7 +316,7 @@ export function davinci<ActionType extends ActionTypes = ActionTypes>(input: {
313316
getCollectors: () => Collectors[];
314317
getError: () => DaVinciError | null;
315318
getErrorCollectors: () => CollectorErrors[];
316-
getNode: () => ContinueNode | ErrorNode | FailureNode | StartNode | SuccessNode;
319+
getNode: () => ContinueNode | StartNode | ErrorNode | FailureNode | SuccessNode;
317320
getServer: () => {
318321
_links?: Links;
319322
id?: string;
@@ -322,6 +325,8 @@ export function davinci<ActionType extends ActionTypes = ActionTypes>(input: {
322325
href?: string;
323326
eventName?: string;
324327
status: "continue";
328+
} | {
329+
status: "start";
325330
} | {
326331
_links?: Links;
327332
eventName?: string;
@@ -337,8 +342,6 @@ export function davinci<ActionType extends ActionTypes = ActionTypes>(input: {
337342
interactionId?: string;
338343
interactionToken?: string;
339344
status: "failure";
340-
} | {
341-
status: "start";
342345
} | {
343346
_links?: Links;
344347
eventName?: string;
@@ -355,14 +358,14 @@ export function davinci<ActionType extends ActionTypes = ActionTypes>(input: {
355358
} & Omit<{
356359
requestId: string;
357360
data?: unknown;
358-
error?: FetchBaseQueryError | SerializedError | undefined;
361+
error?: SerializedError | FetchBaseQueryError | undefined;
359362
endpointName: string;
360363
startedTimeStamp: number;
361364
fulfilledTimeStamp?: number;
362365
}, "data" | "fulfilledTimeStamp"> & Required<Pick<{
363366
requestId: string;
364367
data?: unknown;
365-
error?: FetchBaseQueryError | SerializedError | undefined;
368+
error?: SerializedError | FetchBaseQueryError | undefined;
366369
endpointName: string;
367370
startedTimeStamp: number;
368371
fulfilledTimeStamp?: number;
@@ -379,14 +382,14 @@ export function davinci<ActionType extends ActionTypes = ActionTypes>(input: {
379382
} & Omit<{
380383
requestId: string;
381384
data?: unknown;
382-
error?: FetchBaseQueryError | SerializedError | undefined;
385+
error?: SerializedError | FetchBaseQueryError | undefined;
383386
endpointName: string;
384387
startedTimeStamp: number;
385388
fulfilledTimeStamp?: number;
386389
}, "error"> & Required<Pick<{
387390
requestId: string;
388391
data?: unknown;
389-
error?: FetchBaseQueryError | SerializedError | undefined;
392+
error?: SerializedError | FetchBaseQueryError | undefined;
390393
endpointName: string;
391394
startedTimeStamp: number;
392395
fulfilledTimeStamp?: number;
@@ -407,14 +410,14 @@ export function davinci<ActionType extends ActionTypes = ActionTypes>(input: {
407410
} & Omit<{
408411
requestId: string;
409412
data?: unknown;
410-
error?: FetchBaseQueryError | SerializedError | undefined;
413+
error?: SerializedError | FetchBaseQueryError | undefined;
411414
endpointName: string;
412415
startedTimeStamp: number;
413416
fulfilledTimeStamp?: number;
414417
}, "data" | "fulfilledTimeStamp"> & Required<Pick<{
415418
requestId: string;
416419
data?: unknown;
417-
error?: FetchBaseQueryError | SerializedError | undefined;
420+
error?: SerializedError | FetchBaseQueryError | undefined;
418421
endpointName: string;
419422
startedTimeStamp: number;
420423
fulfilledTimeStamp?: number;
@@ -431,14 +434,14 @@ export function davinci<ActionType extends ActionTypes = ActionTypes>(input: {
431434
} & Omit<{
432435
requestId: string;
433436
data?: unknown;
434-
error?: FetchBaseQueryError | SerializedError | undefined;
437+
error?: SerializedError | FetchBaseQueryError | undefined;
435438
endpointName: string;
436439
startedTimeStamp: number;
437440
fulfilledTimeStamp?: number;
438441
}, "error"> & Required<Pick<{
439442
requestId: string;
440443
data?: unknown;
441-
error?: FetchBaseQueryError | SerializedError | undefined;
444+
error?: SerializedError | FetchBaseQueryError | undefined;
442445
endpointName: string;
443446
startedTimeStamp: number;
444447
fulfilledTimeStamp?: number;
@@ -636,6 +639,9 @@ export interface DaVinciRequest {
636639
};
637640
}
638641

642+
// @public
643+
export type DaVinciRequestValueTypes = string | number | boolean | (string | number | boolean)[] | DeviceValue | PhoneNumberInputValue | FidoRegistrationInputValue | FidoAuthenticationInputValue | MetadataError;
644+
639645
// @public (undocumented)
640646
export interface DaVinciSuccessResponse extends DaVinciBaseResponse {
641647
// (undocumented)
@@ -968,7 +974,7 @@ export type ImageField = {
968974
export type InferActionCollectorType<T extends ActionCollectorTypes> = T extends 'IdpCollector' ? IdpCollector : T extends 'SubmitCollector' ? SubmitCollector : T extends 'FlowCollector' ? FlowCollector : ActionCollectorWithUrl<'ActionCollector'> | ActionCollectorNoUrl<'ActionCollector'>;
969975

970976
// @public
971-
export type InferAutoCollectorType<T extends AutoCollectorTypes> = T extends 'ProtectCollector' ? ProtectCollector : T extends 'PollingCollector' ? PollingCollector : T extends 'FidoRegistrationCollector' ? FidoRegistrationCollector : T extends 'FidoAuthenticationCollector' ? FidoAuthenticationCollector : T extends 'ObjectValueAutoCollector' ? ObjectValueAutoCollector : SingleValueAutoCollector;
977+
export type InferAutoCollectorType<T extends AutoCollectorTypes> = T extends 'ProtectCollector' ? ProtectCollector : T extends 'PollingCollector' ? PollingCollector : T extends 'FidoRegistrationCollector' ? FidoRegistrationCollector : T extends 'FidoAuthenticationCollector' ? FidoAuthenticationCollector : T extends 'MetadataCollector' ? MetadataCollector : T extends 'ObjectValueAutoCollector' ? ObjectValueAutoCollector : SingleValueAutoCollector;
972978

973979
// @public
974980
export type InferMultiValueCollectorType<T extends MultiValueCollectorTypes> = T extends 'MultiSelectCollector' ? MultiValueCollectorWithValue<'MultiSelectCollector'> : MultiValueCollectorWithValue<'MultiValueCollector'> | MultiValueCollectorNoValue<'MultiValueCollector'>;
@@ -1005,6 +1011,24 @@ export interface Links {
10051011

10061012
export { LogLevel }
10071013

1014+
// @public (undocumented)
1015+
export type MetadataCollector = AutoCollector<'ObjectValueAutoCollector', 'MetadataCollector', Record<string, unknown>, Record<string, unknown>>;
1016+
1017+
// @public (undocumented)
1018+
export interface MetadataError {
1019+
// (undocumented)
1020+
code: string;
1021+
// (undocumented)
1022+
message: string;
1023+
}
1024+
1025+
// @public (undocumented)
1026+
export type MetadataField = {
1027+
type: 'METADATA';
1028+
key: string;
1029+
payload: Record<string, unknown>;
1030+
};
1031+
10081032
// @public (undocumented)
10091033
export type MultiSelectCollector = MultiValueCollectorWithValue<'MultiSelectCollector'>;
10101034

@@ -1222,7 +1246,7 @@ export interface ObjectOptionsCollectorWithStringValue<T extends ObjectValueColl
12221246
export type ObjectValueAutoCollector = AutoCollector<'ObjectValueAutoCollector', 'ObjectValueAutoCollector', Record<string, unknown>>;
12231247

12241248
// @public (undocumented)
1225-
export type ObjectValueAutoCollectorTypes = 'ObjectValueAutoCollector' | 'FidoRegistrationCollector' | 'FidoAuthenticationCollector';
1249+
export type ObjectValueAutoCollectorTypes = 'ObjectValueAutoCollector' | 'FidoRegistrationCollector' | 'FidoAuthenticationCollector' | 'MetadataCollector';
12261250

12271251
// @public (undocumented)
12281252
export type ObjectValueCollector<T extends ObjectValueCollectorTypes> = ObjectOptionsCollectorWithObjectValue<T> | ObjectOptionsCollectorWithStringValue<T> | ObjectValueCollectorWithObjectValue<T>;

0 commit comments

Comments
 (0)