Skip to content

Commit 3c63979

Browse files
committed
chore: pr-comments
1 parent 86c00eb commit 3c63979

5 files changed

Lines changed: 90 additions & 36 deletions

File tree

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
'@forgerock/davinci-client': patch
3+
'@forgerock/sdk-utilities': patch
4+
'@forgerock/storage': patch
5+
---
6+
7+
Fix error handling in storage client and davinci-client
8+
9+
- Add `isGenericError` type guard to sdk-utilities for runtime error validation
10+
- Fix storage client to properly catch errors from custom storage implementations, honoring the errors-as-values contract
11+
- Improve davinci-client error handling to use explicit error checks instead of try-catch

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

Lines changed: 44 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -206,45 +206,59 @@ export async function davinci<ActionType extends ActionTypes = ActionTypes>({
206206
}: {
207207
continueToken: string;
208208
}): Promise<InternalErrorResponse | NodeStates> => {
209-
const storedServerInfo = await serverInfo.get();
209+
try {
210+
const storedServerInfo = await serverInfo.get();
210211

211-
if (storedServerInfo === null) {
212-
log.error('No server info found in storage for resume operation');
213-
return {
214-
error: {
215-
message:
216-
'No server info found in storage. Social login needs server info which is saved in local storage. You may have cleared your browser data.',
217-
type: 'state_error',
218-
},
219-
type: 'internal_error',
220-
};
221-
}
212+
if (storedServerInfo === null) {
213+
log.error('No server info found in storage for resume operation');
214+
return {
215+
error: {
216+
message:
217+
'No server info found in storage. Social login needs server info which is saved in local storage. You may have cleared your browser data.',
218+
type: 'state_error',
219+
},
220+
type: 'internal_error',
221+
};
222+
}
223+
224+
if (isGenericError(storedServerInfo)) {
225+
log.error(storedServerInfo.message ?? storedServerInfo.error);
226+
return {
227+
error: {
228+
message:
229+
storedServerInfo.message ??
230+
'Failed to retrieve server info from storage for resume operation',
231+
type: 'internal_error',
232+
},
233+
type: 'internal_error',
234+
};
235+
}
222236

223-
if (isGenericError(storedServerInfo)) {
224-
log.error(storedServerInfo.message ?? storedServerInfo.error);
237+
await store.dispatch(
238+
davinciApi.endpoints.resume.initiate({ continueToken, serverInfo: storedServerInfo }),
239+
);
240+
241+
const removeResult = await serverInfo.remove();
242+
if (isGenericError(removeResult)) {
243+
log.warn(
244+
removeResult.message ?? 'Failed to remove server info from storage after resume',
245+
);
246+
}
247+
248+
const node = nodeSlice.selectSlice(store.getState());
249+
250+
return node;
251+
} catch (err) {
252+
const error = err as Error;
253+
log.error(error.message);
225254
return {
226255
error: {
227-
message:
228-
storedServerInfo.message ??
229-
'Failed to retrieve server info from storage for resume operation',
256+
message: error.message ?? 'An unexpected error occurred during resume operation',
230257
type: 'internal_error',
231258
},
232259
type: 'internal_error',
233260
};
234261
}
235-
236-
await store.dispatch(
237-
davinciApi.endpoints.resume.initiate({ continueToken, serverInfo: storedServerInfo }),
238-
);
239-
240-
const removeResult = await serverInfo.remove();
241-
if (isGenericError(removeResult)) {
242-
log.warn(removeResult.message ?? 'Failed to remove server info from storage after resume');
243-
}
244-
245-
const node = nodeSlice.selectSlice(store.getState());
246-
247-
return node;
248262
},
249263

250264
/**

packages/sdk-effects/storage/src/lib/storage.effects.ts

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,13 @@ export function createStorage<Value>(config: StorageConfig): StorageClient<Value
6868
return {
6969
get: async function storageGet(): Promise<Value | GenericError | null> {
7070
if (storeType === 'custom') {
71-
const value = await config.custom.get(key);
71+
let value: Awaited<ReturnType<typeof config.custom.get>>;
72+
try {
73+
value = await config.custom.get(key);
74+
} catch {
75+
return createStorageError(storeType, 'Retrieving');
76+
}
77+
7278
if (value === null || (typeof value === 'object' && 'error' in value)) {
7379
return value;
7480
}
@@ -101,8 +107,12 @@ export function createStorage<Value>(config: StorageConfig): StorageClient<Value
101107
set: async function storageSet(value: Value): Promise<GenericError | null> {
102108
const valueToStore = JSON.stringify(value);
103109
if (storeType === 'custom') {
104-
const value = await config.custom.set(key, valueToStore);
105-
return Promise.resolve(value ?? null);
110+
try {
111+
const result = await config.custom.set(key, valueToStore);
112+
return result ?? null;
113+
} catch {
114+
return createStorageError(storeType, 'Storing');
115+
}
106116
}
107117

108118
try {
@@ -114,8 +124,12 @@ export function createStorage<Value>(config: StorageConfig): StorageClient<Value
114124
},
115125
remove: async function storageRemove(): Promise<GenericError | null> {
116126
if (storeType === 'custom') {
117-
const value = await config.custom.remove(key);
118-
return Promise.resolve(value ?? null);
127+
try {
128+
const result = await config.custom.remove(key);
129+
return result ?? null;
130+
} catch {
131+
return createStorageError(storeType, 'Removing');
132+
}
119133
}
120134

121135
try {

packages/sdk-utilities/src/lib/error/error.utils.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,20 @@ describe('isGenericError', () => {
156156
expect(result).toBe(false);
157157
});
158158

159+
it('isGenericError_ObjectWithNonStringType_ReturnsFalse', () => {
160+
// Arrange
161+
const value = {
162+
error: 'storage_error',
163+
type: 123,
164+
};
165+
166+
// Act
167+
const result = isGenericError(value);
168+
169+
// Assert
170+
expect(result).toBe(false);
171+
});
172+
159173
it('isGenericError_ArrayValue_ReturnsFalse', () => {
160174
// Arrange
161175
const value = ['error', 'type'];

packages/sdk-utilities/src/lib/error/error.utils.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export function isGenericError(value: unknown): value is GenericError {
1919
value !== null &&
2020
'error' in value &&
2121
typeof (value as GenericError).error === 'string' &&
22-
'type' in value
22+
'type' in value &&
23+
typeof (value as GenericError).type === 'string'
2324
);
2425
}

0 commit comments

Comments
 (0)