Skip to content

Commit 7a62269

Browse files
committed
fix(journey): moved journey clint configuration into journey store
1 parent 0077390 commit 7a62269

8 files changed

Lines changed: 106 additions & 102 deletions

File tree

apps/login-app/src/routes/(app)/+layout.ts

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
**/
99

1010
import configure from '$core/sdk.config';
11-
import { setJourneyClientConfig } from '$core/journey-client.config';
1211
import { initialize as initializeJourneys } from '$journey/config.store';
1312
import { initialize as initializeLinks } from '$core/links.store';
1413

@@ -17,16 +16,6 @@ import { browser } from '$app/environment';
1716
import type { LayoutLoad } from './$types';
1817

1918
export const load: LayoutLoad = ({ data }) => {
20-
if (data.wellknown) {
21-
setJourneyClientConfig({
22-
serverConfig: {
23-
wellknown: data.wellknown,
24-
},
25-
});
26-
} else {
27-
console.warn('Wellknown URL is not configured.');
28-
}
29-
3019
configure({
3120
clientId: data.clientId,
3221
redirectUri: `${browser ? window.location.origin : 'https://placeholder.com'}/callback`,

apps/login-app/src/routes/(app)/+page.svelte

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,11 @@
2929
const journeyParam = $page.url.searchParams.get('journey');
3030
const suspendedIdParam = $page.url.searchParams.get('suspendedId');
3131
32-
const journeyStore: JourneyStore = initializeJourney();
32+
const journeyStore: JourneyStore = initializeJourney({
33+
serverConfig: {
34+
wellknown: data.wellknown,
35+
},
36+
});
3337
3438
let hasSubmitted = false;
3539
let redirectForm: HTMLFormElement | null = null;

apps/login-app/src/routes/(app)/register/+page.svelte

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,11 @@
2323
/** @type {import('./$types').PageData} */
2424
export let data;
2525
26-
const journeyStore: JourneyStore = initializeJourney();
26+
const journeyStore: JourneyStore = initializeJourney({
27+
serverConfig: {
28+
wellknown: data.wellknown,
29+
},
30+
});
2731
const oauthStore: OAuthStore = initializeOAuth();
2832
const userStore: UserStore = initializeUser();
2933

core/journey-client.config.ts

Lines changed: 0 additions & 71 deletions
This file was deleted.
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,18 +13,20 @@ import type { JourneyClient, JourneyClientConfig } from '@forgerock/journey-clie
1313

1414
const journeyMock = vi.fn();
1515

16-
vi.mock('@forgerock/journey-client', () => {
16+
vi.mock('@forgerock/journey-client', async (importOriginal) => {
17+
const actual = (await importOriginal()) as Record<string, unknown>;
1718
return {
19+
...actual,
1820
journey: journeyMock,
1921
};
2022
});
2123

2224
async function importSubject() {
23-
const mod = await import('./journey-client.config');
25+
const mod = await import('./journey.store');
2426
return mod;
2527
}
2628

27-
describe('journey-client.config', () => {
29+
describe('journey.store (Journey Client configuration)', () => {
2830
beforeEach(() => {
2931
journeyMock.mockReset();
3032
vi.resetModules();

core/journey/journey.store.ts

Lines changed: 88 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
*
88
**/
99

10+
import { journey } from '@forgerock/journey-client';
1011
import type {
1112
BaseCallback,
1213
JourneyStep,
@@ -15,9 +16,11 @@ import type {
1516
NextOptions,
1617
ResumeOptions,
1718
JourneyClient,
19+
JourneyClientConfig,
1820
GenericError,
1921
} from '@forgerock/journey-client/types';
2022
import { writable, type Writable } from 'svelte/store';
23+
import { z } from 'zod';
2124

2225
import { htmlDecode } from '$journey/_utilities/decode.utilities';
2326
import type { JourneyStore, JourneyStoreValue, StackStore, StepTypes } from './journey.interfaces';
@@ -30,7 +33,85 @@ import {
3033
} from './stages/_utilities/step.utilities';
3134
import { buildCallbackMetadata, buildStepMetadata } from '$journey/_utilities/metadata.utilities';
3235
import type { Maybe } from '$core/interfaces';
33-
import { getJourneyClient } from '$core/journey-client.config';
36+
37+
/**
38+
* Configure Journey Client
39+
*/
40+
41+
const wellknownSchema = z
42+
.string({
43+
invalid_type_error:
44+
'`serverConfig.wellknown` must be a URL string (this is generated by the Zod library).',
45+
required_error:
46+
'Setting the `serverConfig.wellknown` is required (this is generated by the Zod library).',
47+
})
48+
.url({
49+
message: '`serverConfig.wellknown` must be a full URL (this is generated by the Zod library).',
50+
});
51+
52+
export const journeyClientConfigSchema: z.ZodType<JourneyClientConfig> = z
53+
.object({
54+
serverConfig: z
55+
.object({
56+
wellknown: wellknownSchema,
57+
})
58+
.strict(),
59+
})
60+
.strict();
61+
62+
let journeyClientConfig: JourneyClientConfig | undefined;
63+
64+
/**
65+
* We cache the journey client promise instead of only caching the resolved client so concurrent callers
66+
* share the same initialization work and we don't create multiple Journey Client instances in parallel.
67+
*/
68+
let journeyClientPromise: Promise<JourneyClient> | undefined;
69+
70+
/**
71+
* @function setJourneyClientConfig - Sets (or ensures) the Journey Client configuration.
72+
* @param {JourneyClientConfig} [config] If omitted, reuses existing config.
73+
* @throws {Error} If called without config before configuration is set.
74+
* @throws {z.ZodError} If provided config fails validation.
75+
* @returns {JourneyClientConfig} The active Journey Client configuration.
76+
*/
77+
export function setJourneyClientConfig(config?: JourneyClientConfig): JourneyClientConfig {
78+
const parsed =
79+
config === undefined ? journeyClientConfig : journeyClientConfigSchema.parse(config);
80+
81+
if (!parsed) {
82+
throw new Error('Journey Client is not configured. Call setJourneyClientConfig() first.');
83+
}
84+
85+
const hasChanged = parsed.serverConfig.wellknown !== journeyClientConfig?.serverConfig.wellknown;
86+
journeyClientConfig = parsed;
87+
// Reset the cached client promise when config changes.
88+
if (hasChanged) {
89+
journeyClientPromise = undefined;
90+
}
91+
return journeyClientConfig;
92+
}
93+
94+
/**
95+
* @function getJourneyClient - Gets a cached Journey Client (promise).
96+
* @throws {Error} If no Journey Client configuration is available.
97+
* @returns {Promise<JourneyClient>} A promise that resolves to a Journey Client.
98+
*/
99+
export async function getJourneyClient(): Promise<JourneyClient> {
100+
if (!journeyClientConfig) {
101+
throw new Error('Journey Client is not configured. Call setJourneyClientConfig() first.');
102+
}
103+
104+
// Cache the journey client promise to reuse an existing journey client.
105+
if (!journeyClientPromise) {
106+
journeyClientPromise = journey({ config: journeyClientConfig }).catch((err) => {
107+
// If creation fails, clear the cache so a later call can try again.
108+
journeyClientPromise = undefined;
109+
throw err;
110+
});
111+
}
112+
113+
return await journeyClientPromise;
114+
}
34115

35116
/**
36117
* @function initializeJourney - Initializes the journey stack for tracking journey switches
@@ -104,9 +185,13 @@ export const journeyStore: Writable<JourneyStoreValue> = writable({
104185

105186
/**
106187
* @function initialize - Initializes the journey store
107-
* @returns {object} - The journey store
188+
* @param {JourneyClientConfig} Optional Journey Client configuration.
189+
* @throws {Error} If no Journey Client configuration is available.
190+
* @returns {JourneyStore} Journey store API.
108191
*/
109-
export function initialize(): JourneyStore {
192+
export function initialize(config?: JourneyClientConfig | undefined): JourneyStore {
193+
setJourneyClientConfig(config);
194+
110195
const stack = initializeStack();
111196
let stepNumber = 0;
112197
// TODO: JourneyResult is not currently exported by Journey Client, so we define it here

packages/login-widget/src/lib/_utilities/api.utilities.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import { PIProtect } from '@forgerock/ping-protect';
1919

2020
import { logErrorAndThrow } from '$core/_utilities/errors.utilities';
2121
import configure from '$core/sdk.config';
22-
import { setJourneyClientConfig } from '$core/journey-client.config';
2322

2423
// Import the stores for initialization
2524
import { componentStore } from '$core/component.store';
@@ -73,10 +72,6 @@ export function widgetApiFactory(componentApi: ReturnType<typeof _componentApi>)
7372
}
7473

7574
const configuration = (options?: WidgetConfigOptions) => {
76-
if (options?.journeyClient) {
77-
setJourneyClientConfig(options.journeyClient);
78-
}
79-
8075
if (options?.forgerock) {
8176
configure({
8277
// Set some basics by default
@@ -104,7 +99,7 @@ export function widgetApiFactory(componentApi: ReturnType<typeof _componentApi>)
10499
/**
105100
* Initialize all the stores.
106101
*/
107-
journeyStore = initializeJourney();
102+
journeyStore = initializeJourney(options?.journeyClient);
108103
oauthStore = initializeOauth(options?.forgerock);
109104
userStore = initializeUser(options?.forgerock);
110105

@@ -119,10 +114,6 @@ export function widgetApiFactory(componentApi: ReturnType<typeof _componentApi>)
119114
* @returns {void}
120115
**/
121116
set(setOptions?: WidgetConfigOptions): void {
122-
if (setOptions?.journeyClient) {
123-
setJourneyClientConfig(setOptions.journeyClient);
124-
}
125-
126117
if (setOptions?.forgerock) {
127118
configure({
128119
// Set some basics by default
@@ -150,7 +141,7 @@ export function widgetApiFactory(componentApi: ReturnType<typeof _componentApi>)
150141
* Initialize the stores and ensure both variables point to the same reference.
151142
* Variables with _ are the reactive version of the original variable from above.
152143
*/
153-
journeyStore = initializeJourney();
144+
journeyStore = initializeJourney(setOptions?.journeyClient);
154145
oauthStore = initializeOauth(setOptions?.forgerock);
155146
userStore = initializeUser(setOptions?.forgerock);
156147

packages/login-widget/src/lib/interfaces.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import type { journeyConfigSchema } from '$journey/config.store';
2020
import type { partialLinksSchema } from '$core/links.store';
2121
import type { partialStringsSchema } from '$core/locale.store';
2222
import type { partialStyleSchema } from '$core/style.store';
23-
import { journeyClientConfigSchema } from '$core/journey-client.config';
23+
import { journeyClientConfigSchema } from '$journey/journey.store';
2424

2525
export interface JourneyOptions {
2626
oauth?: boolean; // defaults to true

0 commit comments

Comments
 (0)