Skip to content

Commit 63927e5

Browse files
committed
fix(settings): extract shared response mappers to prevent server/client shape drift
Addresses PR review feedback — prefetch.ts duplicated response mapping logic from client hooks. Extracted mapGeneralSettingsResponse and mapUserProfileResponse as shared functions used by both client fetch and server prefetch.
1 parent ab61f51 commit 63927e5

File tree

3 files changed

+39
-42
lines changed

3 files changed

+39
-42
lines changed

apps/sim/app/workspace/[workspaceId]/settings/[section]/prefetch.ts

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import type { QueryClient } from '@tanstack/react-query'
22
import { headers } from 'next/headers'
33
import { getInternalApiBaseUrl } from '@/lib/core/utils/urls'
4-
import { generalSettingsKeys } from '@/hooks/queries/general-settings'
5-
import { userProfileKeys } from '@/hooks/queries/user-profile'
4+
import { generalSettingsKeys, mapGeneralSettingsResponse } from '@/hooks/queries/general-settings'
5+
import { mapUserProfileResponse, userProfileKeys } from '@/hooks/queries/user-profile'
66

77
/**
88
* Forwards incoming request cookies so server-side API fetches authenticate correctly.
@@ -29,17 +29,7 @@ export function prefetchGeneralSettings(queryClient: QueryClient) {
2929
})
3030
if (!response.ok) throw new Error(`Settings prefetch failed: ${response.status}`)
3131
const { data } = await response.json()
32-
return {
33-
autoConnect: data.autoConnect ?? true,
34-
showTrainingControls: data.showTrainingControls ?? false,
35-
superUserModeEnabled: data.superUserModeEnabled ?? true,
36-
theme: data.theme || 'system',
37-
telemetryEnabled: data.telemetryEnabled ?? true,
38-
billingUsageNotificationsEnabled: data.billingUsageNotificationsEnabled ?? true,
39-
errorNotificationsEnabled: data.errorNotificationsEnabled ?? true,
40-
snapToGridSize: data.snapToGridSize ?? 0,
41-
showActionBar: data.showActionBar ?? true,
42-
}
32+
return mapGeneralSettingsResponse(data)
4333
},
4434
staleTime: 60 * 60 * 1000,
4535
})
@@ -61,14 +51,7 @@ export function prefetchUserProfile(queryClient: QueryClient) {
6151
})
6252
if (!response.ok) throw new Error(`Profile prefetch failed: ${response.status}`)
6353
const { user } = await response.json()
64-
return {
65-
id: user.id,
66-
name: user.name || '',
67-
email: user.email || '',
68-
image: user.image || null,
69-
createdAt: user.createdAt,
70-
updatedAt: user.updatedAt,
71-
}
54+
return mapUserProfileResponse(user)
7255
},
7356
staleTime: 5 * 60 * 1000,
7457
})

apps/sim/hooks/queries/general-settings.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,24 @@ export interface GeneralSettings {
2828
showActionBar: boolean
2929
}
3030

31+
/**
32+
* Map raw API response data to GeneralSettings with defaults.
33+
* Shared by both client fetch and server prefetch to prevent shape drift.
34+
*/
35+
export function mapGeneralSettingsResponse(data: Record<string, unknown>): GeneralSettings {
36+
return {
37+
autoConnect: (data.autoConnect as boolean) ?? true,
38+
showTrainingControls: (data.showTrainingControls as boolean) ?? false,
39+
superUserModeEnabled: (data.superUserModeEnabled as boolean) ?? true,
40+
theme: (data.theme as GeneralSettings['theme']) || 'system',
41+
telemetryEnabled: (data.telemetryEnabled as boolean) ?? true,
42+
billingUsageNotificationsEnabled: (data.billingUsageNotificationsEnabled as boolean) ?? true,
43+
errorNotificationsEnabled: (data.errorNotificationsEnabled as boolean) ?? true,
44+
snapToGridSize: (data.snapToGridSize as number) ?? 0,
45+
showActionBar: (data.showActionBar as boolean) ?? true,
46+
}
47+
}
48+
3149
/**
3250
* Fetch general settings from API
3351
*/
@@ -39,18 +57,7 @@ async function fetchGeneralSettings(signal?: AbortSignal): Promise<GeneralSettin
3957
}
4058

4159
const { data } = await response.json()
42-
43-
return {
44-
autoConnect: data.autoConnect ?? true,
45-
showTrainingControls: data.showTrainingControls ?? false,
46-
superUserModeEnabled: data.superUserModeEnabled ?? true,
47-
theme: data.theme || 'system',
48-
telemetryEnabled: data.telemetryEnabled ?? true,
49-
billingUsageNotificationsEnabled: data.billingUsageNotificationsEnabled ?? true,
50-
errorNotificationsEnabled: data.errorNotificationsEnabled ?? true,
51-
snapToGridSize: data.snapToGridSize ?? 0,
52-
showActionBar: data.showActionBar ?? true,
53-
}
60+
return mapGeneralSettingsResponse(data)
5461
}
5562

5663
/**

apps/sim/hooks/queries/user-profile.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,21 @@ export interface UserProfile {
2424
updatedAt: string
2525
}
2626

27+
/**
28+
* Map raw API response user object to UserProfile.
29+
* Shared by both client fetch and server prefetch to prevent shape drift.
30+
*/
31+
export function mapUserProfileResponse(user: Record<string, unknown>): UserProfile {
32+
return {
33+
id: user.id as string,
34+
name: (user.name as string) || '',
35+
email: (user.email as string) || '',
36+
image: (user.image as string) || null,
37+
createdAt: user.createdAt as string,
38+
updatedAt: user.updatedAt as string,
39+
}
40+
}
41+
2742
/**
2843
* Fetch user profile from API
2944
*/
@@ -35,15 +50,7 @@ async function fetchUserProfile(signal?: AbortSignal): Promise<UserProfile> {
3550
}
3651

3752
const { user } = await response.json()
38-
39-
return {
40-
id: user.id,
41-
name: user.name || '',
42-
email: user.email || '',
43-
image: user.image || null,
44-
createdAt: user.createdAt,
45-
updatedAt: user.updatedAt,
46-
}
53+
return mapUserProfileResponse(user)
4754
}
4855

4956
/**

0 commit comments

Comments
 (0)