-
Notifications
You must be signed in to change notification settings - Fork 3
fix!: update-error-handling-across-packages #524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,37 @@ | ||||||||||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||||||||||
| '@forgerock/journey-client': major | ||||||||||||||||||||||||||||||||||||||
| --- | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| **BREAKING CHANGE**: Journey client methods now return `GenericError` instead of `undefined` for error cases. | ||||||||||||||||||||||||||||||||||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it a breaking change if we haven't released journey client yet?
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I can edit the changeset to only reflect JC. |
||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ## What Changed | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| The `start`, `next`, `resume`, and `terminate` methods now return a `GenericError` object instead of `undefined` when encountering unknown step types or error conditions. This aligns the journey client with the DaVinci client's error handling patterns. | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ### Return Type Changes | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| | Method | Before | After | | ||||||||||||||||||||||||||||||||||||||
| | ----------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------- | | ||||||||||||||||||||||||||||||||||||||
| | `start` | `JourneyStep \| JourneyLoginSuccess \| JourneyLoginFailure \| undefined` | `JourneyStep \| JourneyLoginSuccess \| JourneyLoginFailure \| GenericError` | | ||||||||||||||||||||||||||||||||||||||
| | `next` | `JourneyStep \| JourneyLoginSuccess \| JourneyLoginFailure \| undefined` | `JourneyStep \| JourneyLoginSuccess \| JourneyLoginFailure \| GenericError` | | ||||||||||||||||||||||||||||||||||||||
| | `resume` | `JourneyStep \| JourneyLoginSuccess \| JourneyLoginFailure \| undefined` | `JourneyStep \| JourneyLoginSuccess \| JourneyLoginFailure \| GenericError` | | ||||||||||||||||||||||||||||||||||||||
| | `terminate` | `void` | `void \| GenericError` | | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ## Migration Guide | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| Before: | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| ```ts | ||||||||||||||||||||||||||||||||||||||
| const step = await client.start(); | ||||||||||||||||||||||||||||||||||||||
| if (step) { | ||||||||||||||||||||||||||||||||||||||
| // Use step | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||
| After: const result = await client.start(); | ||||||||||||||||||||||||||||||||||||||
| if ('error' in result) { | ||||||||||||||||||||||||||||||||||||||
| // Handle error | ||||||||||||||||||||||||||||||||||||||
| console.error(result.message); | ||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||
| // Use step | ||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||
| ``` | ||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+22
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Migration guide has a formatting issue. The "After:" section on line 33 is incorrectly placed inside the code block. This will render incorrectly in the changeset documentation. 📝 Suggested fix Before:
```ts
const step = await client.start();
if (step) {
// Use step
}
+```
-After: const result = await client.start();
+After:
+
+```ts
+const result = await client.start();
if ('error' in result) {
// Handle error
console.error(result.message);
} else {
// Use step
}
```📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,12 +8,19 @@ | |||||||||||||||||||||||||
| import { callbackType } from '@forgerock/sdk-types'; | ||||||||||||||||||||||||||
| import { afterEach, describe, expect, test, vi } from 'vitest'; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import type { Step } from '@forgerock/sdk-types'; | ||||||||||||||||||||||||||
| import type { GenericError, Step } from '@forgerock/sdk-types'; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| import { journey } from './client.store.js'; | ||||||||||||||||||||||||||
| import { createJourneyStep } from './step.utils.js'; | ||||||||||||||||||||||||||
| import { JourneyClientConfig } from './config.types.js'; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * Type guard to check if a result is a GenericError | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| function isGenericError(result: unknown): result is GenericError { | ||||||||||||||||||||||||||
| return typeof result === 'object' && result !== null && 'error' in result && 'type' in result; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+17
to
+22
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent type guard implementations. This type guard checks for ♻️ Suggested alignment function isGenericError(result: unknown): result is GenericError {
- return typeof result === 'object' && result !== null && 'error' in result && 'type' in result;
+ return typeof result === 'object' && result !== null && 'error' in result && 'message' in result;
}Or consider extracting a shared 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Create a singleton mock instance for storage | ||||||||||||||||||||||||||
| const mockStorageInstance = { | ||||||||||||||||||||||||||
| get: vi.fn(), | ||||||||||||||||||||||||||
|
|
@@ -65,13 +72,16 @@ describe('journey-client', () => { | |||||||||||||||||||||||||
| const client = await journey({ config: mockConfig }); | ||||||||||||||||||||||||||
| const step = await client.start(); | ||||||||||||||||||||||||||
| expect(step).toBeDefined(); | ||||||||||||||||||||||||||
| expect(isGenericError(step)).toBe(false); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| expect(mockFetch).toHaveBeenCalledTimes(1); | ||||||||||||||||||||||||||
| const request = mockFetch.mock.calls[0][0] as Request; | ||||||||||||||||||||||||||
| // TODO: This should be /journeys?_action=start, but the current implementation calls /authenticate | ||||||||||||||||||||||||||
| expect(request.url).toBe('https://test.com/json/realms/root/authenticate'); | ||||||||||||||||||||||||||
| expect(step).toHaveProperty('type', 'Step'); | ||||||||||||||||||||||||||
| expect(step && step.payload).toEqual(mockStepResponse); | ||||||||||||||||||||||||||
| if (!isGenericError(step)) { | ||||||||||||||||||||||||||
| expect(step.payload).toEqual(mockStepResponse); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| test('next() should send the current step and return the next step', async () => { | ||||||||||||||||||||||||||
|
|
@@ -101,6 +111,7 @@ describe('journey-client', () => { | |||||||||||||||||||||||||
| const client = await journey({ config: mockConfig }); | ||||||||||||||||||||||||||
| const nextStep = await client.next(initialStep, {}); | ||||||||||||||||||||||||||
| expect(nextStep).toBeDefined(); | ||||||||||||||||||||||||||
| expect(isGenericError(nextStep)).toBe(false); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| expect(mockFetch).toHaveBeenCalledTimes(1); | ||||||||||||||||||||||||||
| const request = mockFetch.mock.calls[0][0] as Request; | ||||||||||||||||||||||||||
|
|
@@ -109,7 +120,9 @@ describe('journey-client', () => { | |||||||||||||||||||||||||
| expect(request.method).toBe('POST'); | ||||||||||||||||||||||||||
| expect(await request.json()).toEqual(initialStep.payload); | ||||||||||||||||||||||||||
| expect(nextStep).toHaveProperty('type', 'Step'); | ||||||||||||||||||||||||||
| expect(nextStep && nextStep.payload).toEqual(nextStepPayload); | ||||||||||||||||||||||||||
| if (!isGenericError(nextStep)) { | ||||||||||||||||||||||||||
| expect(nextStep.payload).toEqual(nextStepPayload); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| test('redirect() should store the step and call location.assign', async () => { | ||||||||||||||||||||||||||
|
|
@@ -169,7 +182,9 @@ describe('journey-client', () => { | |||||||||||||||||||||||||
| expect(request.method).toBe('POST'); | ||||||||||||||||||||||||||
| expect(await request.json()).toEqual(previousStepPayload); | ||||||||||||||||||||||||||
| expect(step).toHaveProperty('type', 'Step'); | ||||||||||||||||||||||||||
| expect(step && step.payload).toEqual(nextStepPayload); | ||||||||||||||||||||||||||
| if (!isGenericError(step)) { | ||||||||||||||||||||||||||
| expect(step.payload).toEqual(nextStepPayload); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| test('should correctly resume with a plain Step object from storage', async () => { | ||||||||||||||||||||||||||
|
|
@@ -198,7 +213,9 @@ describe('journey-client', () => { | |||||||||||||||||||||||||
| expect(request.method).toBe('POST'); | ||||||||||||||||||||||||||
| expect(await request.json()).toEqual(plainStepPayload); // Expect the plain payload to be sent | ||||||||||||||||||||||||||
| expect(step).toHaveProperty('type', 'Step'); // The returned step should still be an JourneyStep instance | ||||||||||||||||||||||||||
| expect(step && step.payload).toEqual(nextStepPayload); | ||||||||||||||||||||||||||
| if (!isGenericError(step)) { | ||||||||||||||||||||||||||
| expect(step.payload).toEqual(nextStepPayload); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| test('should throw an error if a previous step is required but not found', async () => { | ||||||||||||||||||||||||||
|
|
@@ -234,7 +251,9 @@ describe('journey-client', () => { | |||||||||||||||||||||||||
| const url = new URL(request.url); | ||||||||||||||||||||||||||
| expect(url.origin + url.pathname).toBe('https://test.com/json/realms/root/authenticate'); | ||||||||||||||||||||||||||
| expect(step).toHaveProperty('type', 'Step'); | ||||||||||||||||||||||||||
| expect(step && step.payload).toEqual(mockStepResponse); | ||||||||||||||||||||||||||
| if (!isGenericError(step)) { | ||||||||||||||||||||||||||
| expect(step.payload).toEqual(mockStepResponse); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Repository: ForgeRock/ping-javascript-sdk
Length of output: 55
🏁 Script executed:
Repository: ForgeRock/ping-javascript-sdk
Length of output: 497
🏁 Script executed:
Repository: ForgeRock/ping-javascript-sdk
Length of output: 1877
🏁 Script executed:
Repository: ForgeRock/ping-javascript-sdk
Length of output: 1706
🏁 Script executed:
Repository: ForgeRock/ping-javascript-sdk
Length of output: 55
🏁 Script executed:
Repository: ForgeRock/ping-javascript-sdk
Length of output: 1695
🏁 Script executed:
Repository: ForgeRock/ping-javascript-sdk
Length of output: 1229
🏁 Script executed:
Repository: ForgeRock/ping-javascript-sdk
Length of output: 134
🏁 Script executed:
Repository: ForgeRock/ping-javascript-sdk
Length of output: 522
🏁 Script executed:
Repository: ForgeRock/ping-javascript-sdk
Length of output: 361
Remove
@forgerock/storagefrom this changeset.The changeset marks
@forgerock/storageas a major version bump, but the storage package has no changes in this PR. The storage API's transition to returnGenericError | nullwas already completed in SDKS-4361 and is documented in the separatehip-owls-rule.mdchangeset. The breaking changes described here apply only to journey-client methods, not storage.🤖 Prompt for AI Agents
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this true about storage client? Also, is this still considered a major bump if we haven't released journey client yet?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
Length of output: 881
🏁 Script executed:
Length of output: 332
🏁 Script executed:
Length of output: 2567
@ancheetahGreat questions! Let me answer both:Question 1: Is it true about storage client?
Yes, CodeRabbit is correct. The storage package has no changes in this PR. I verified that the only files modified are:
.changeset/chilly-sheep-follow.mdpackages/journey-client/src/lib/client.store.test.tspackages/journey-client/src/lib/client.store.tspackages/journey-client/src/lib/device/device-profile.test.tspackages/journey-client/src/lib/journey.utils.tsNo storage files were modified. The storage API changes were already handled in SDKS-4361 (documented in
hip-owls-rule.mdchangeset).Question 2: Is this still a major bump if journey-client hasn't been released?
Journey-client HAS been released - it's currently at version
1.3.0. According to semantic versioning, once a package is at version 1.0.0 or higher, breaking changes require a major version bump. So yes, this is correctly a major bump.Additional Finding
Beyond storage, I found that
@forgerock/sdk-oidcand@forgerock/oidc-clientalso have no changes in this PR and shouldn't be in this changeset either. The changeset description only discusses journey-client changes, and there's no dependency relationship between journey-client and these packages.Recommendation: This changeset should only list
@forgerock/journey-clientas major, removing the other three packages (@forgerock/storage,@forgerock/sdk-oidc, and@forgerock/oidc-client).🧠 Learnings used
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@coderabbitai is wrong here, it was not released, we did "version" journey-client but it was never officially released.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ryanbas21Thank you for the clarification! You're absolutely right that I misunderstood the release status.Since journey-client was versioned but never published to npm, the semantic versioning rules are more flexible. Typically:
What's your team's preference? Would you like to:
Regarding the original issue: My analysis about the other packages remains valid regardless of the versioning decision. As you confirmed with ancheetah, this PR only modifies journey-client files, so:
@forgerock/storage- no changes (already handled in SDKS-4361)@forgerock/sdk-oidc- no changes@forgerock/oidc-client- no changesShould those three packages be removed from this changeset, leaving only
@forgerock/journey-client?🧠 Learnings used
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ancheetah technically, yes its a major because we versioned Journey client. We didn't release it though, so its not "breaking consumers" but from a semantic versioning perspective, it's probably still correct to mark it as such.
Someone could use the package by installing it via the git repo hypothetically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.