-
Notifications
You must be signed in to change notification settings - Fork 5
feat: Add resilience to provider API response handling #223
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
95d4499
4181c35
537a64a
ef5930c
9337314
b8c2f35
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 |
|---|---|---|
|
|
@@ -35,6 +35,11 @@ export function ConfigProvider(props: ParentProps) { | |
| let refreshSeq = 0 | ||
| let lastUpdateAt = 0 | ||
|
|
||
| // Validate that a response has the expected Config shape (must be a non-array object) | ||
| function isValidConfig(data: unknown): data is Config { | ||
| return !!data && typeof data === "object" && !Array.isArray(data) | ||
| } | ||
|
|
||
| async function refresh() { | ||
| const seq = ++refreshSeq | ||
| setLoading(true) | ||
|
|
@@ -45,7 +50,11 @@ export function ConfigProvider(props: ParentProps) { | |
| try { | ||
| const projRes = await sdk.client.config.get() | ||
| if (seq !== refreshSeq) return // superseded by newer refresh | ||
| setProject(reconcile((projRes?.data as Config) ?? {})) | ||
| const projData = projRes?.data | ||
| if (projData && !isValidConfig(projData)) { | ||
| console.error("[Config] Unexpected project config response shape:", projData) | ||
| } | ||
| setProject(reconcile(isValidConfig(projData) ? projData : {})) | ||
| } catch (e) { | ||
| console.error("[Config] Failed to fetch project config:", e) | ||
| if (seq !== refreshSeq) return | ||
|
|
@@ -58,7 +67,11 @@ export function ConfigProvider(props: ParentProps) { | |
| try { | ||
| const globalRes = await sdk.client.global.config.get() | ||
| if (seq !== refreshSeq) return | ||
| setGlobal(reconcile((globalRes?.data as Config) ?? {})) | ||
| const globalData = globalRes?.data | ||
| if (globalData && !isValidConfig(globalData)) { | ||
| console.error("[Config] Unexpected global config response shape:", globalData) | ||
| } | ||
| setGlobal(reconcile(isValidConfig(globalData) ? globalData : {})) | ||
| } catch (e) { | ||
| console.error("[Config] Failed to fetch global config:", e) | ||
| if (seq !== refreshSeq) return | ||
|
|
@@ -76,8 +89,13 @@ export function ConfigProvider(props: ParentProps) { | |
| setError(null) | ||
| try { | ||
| const res = await sdk.client.config.update({ config: patch }) | ||
| const data = res.data as Config | undefined | ||
| if (data) { | ||
| const data = res.data | ||
| if (data && !isValidConfig(data)) { | ||
| console.error("[Config] Unexpected project update response shape:", data) | ||
| setError("Failed to save project configuration") | ||
| return null | ||
| } | ||
| if (isValidConfig(data)) { | ||
| lastUpdateAt = Date.now() | ||
| setProject(reconcile(data)) | ||
| return data | ||
|
|
@@ -101,8 +119,13 @@ export function ConfigProvider(props: ParentProps) { | |
| ? { ...patch, disabled_providers: global.disabled_providers } | ||
| : patch | ||
| const res = await sdk.client.global.config.update({ config: safePatch }) | ||
| const data = res.data as Config | undefined | ||
| if (data) { | ||
| const data = res.data | ||
| if (data && !isValidConfig(data)) { | ||
| console.error("[Config] Unexpected global update response shape:", data) | ||
| setError("Failed to save global configuration") | ||
| return null | ||
| } | ||
|
Comment on lines
121
to
+127
|
||
| if (isValidConfig(data)) { | ||
| lastUpdateAt = Date.now() | ||
| setGlobal(reconcile(data)) | ||
| return data | ||
|
|
||
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.
When
config.updateProject/config.updateGlobalreceive a non-null response that failsisValidConfig, they currently justconsole.error(...)and returnnullwhile leavingerror()cleared. Call sites (e.g. Settings) only show UI feedback viaconfig.error()/saveError(), so this becomes a silent save failure. Consider settingsetError(...)(or throwing) on invalid response shapes so the user gets a visible error state.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.
Addressed in ef5930c: invalid non-object project update responses now set a user-visible config error before returning
null.