Skip to content

Commit 51989be

Browse files
refactor(dashboard): use minimal fragment for search sandboxes and add tracking (#8834)
* refactor(dashboard): use minimal fragment for search sandboxes * refactor(dashboard): generated types for search fragment * feat(dashboard): track dashboard search * fix: add type guards for SearchTeamSandboxFragment missing properties
1 parent 8944671 commit 51989be

File tree

6 files changed

+197
-72
lines changed

6 files changed

+197
-72
lines changed

packages/app/src/app/graphql/types.ts

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5113,6 +5113,33 @@ export type AllTeamsQuery = {
51135113
} | null;
51145114
};
51155115

5116+
export type SearchTeamSandboxFragment = {
5117+
__typename?: 'Sandbox';
5118+
id: string;
5119+
alias: string | null;
5120+
title: string | null;
5121+
description: string | null;
5122+
updatedAt: string;
5123+
viewCount: number;
5124+
isV2: boolean;
5125+
draft: boolean;
5126+
restricted: boolean;
5127+
privacy: number;
5128+
screenshotUrl: string | null;
5129+
source: { __typename?: 'Source'; template: string | null };
5130+
customTemplate: {
5131+
__typename?: 'Template';
5132+
id: any | null;
5133+
iconUrl: string | null;
5134+
} | null;
5135+
author: { __typename?: 'User'; username: string } | null;
5136+
collection: {
5137+
__typename?: 'Collection';
5138+
path: string;
5139+
id: any | null;
5140+
} | null;
5141+
};
5142+
51165143
export type _SearchTeamSandboxesQueryVariables = Exact<{
51175144
teamId: Scalars['UUID4'];
51185145
}>;
@@ -5130,43 +5157,25 @@ export type _SearchTeamSandboxesQuery = {
51305157
alias: string | null;
51315158
title: string | null;
51325159
description: string | null;
5133-
lastAccessedAt: any;
5134-
insertedAt: string;
51355160
updatedAt: string;
5136-
removedAt: string | null;
5137-
privacy: number;
5138-
isFrozen: boolean;
5139-
screenshotUrl: string | null;
51405161
viewCount: number;
5141-
likeCount: number;
51425162
isV2: boolean;
51435163
draft: boolean;
51445164
restricted: boolean;
5145-
authorId: any | null;
5146-
teamId: any | null;
5165+
privacy: number;
5166+
screenshotUrl: string | null;
51475167
source: { __typename?: 'Source'; template: string | null };
51485168
customTemplate: {
51495169
__typename?: 'Template';
51505170
id: any | null;
51515171
iconUrl: string | null;
51525172
} | null;
5153-
forkedTemplate: {
5154-
__typename?: 'Template';
5155-
id: any | null;
5156-
color: string | null;
5157-
iconUrl: string | null;
5158-
} | null;
5173+
author: { __typename?: 'User'; username: string } | null;
51595174
collection: {
51605175
__typename?: 'Collection';
51615176
path: string;
51625177
id: any | null;
51635178
} | null;
5164-
author: { __typename?: 'User'; username: string } | null;
5165-
permissions: {
5166-
__typename?: 'SandboxProtectionSettings';
5167-
preventSandboxLeaving: boolean;
5168-
preventSandboxExport: boolean;
5169-
} | null;
51705179
}>;
51715180
} | null;
51725181
} | null;

packages/app/src/app/overmind/effects/gql/dashboard/queries.ts

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,40 @@ export const getTeams: Query<AllTeamsQuery, AllTeamsQueryVariables> = gql`
303303
${teamFragmentDashboard}
304304
`;
305305

306+
const SEARCH_TEAM_SANDBOX_FRAGMENT = gql`
307+
fragment searchTeamSandbox on Sandbox {
308+
id
309+
alias
310+
title
311+
description
312+
updatedAt
313+
viewCount
314+
isV2
315+
draft
316+
restricted
317+
privacy
318+
screenshotUrl
319+
320+
source {
321+
template
322+
}
323+
324+
customTemplate {
325+
id
326+
iconUrl
327+
}
328+
329+
author {
330+
username
331+
}
332+
333+
collection {
334+
path
335+
id
336+
}
337+
}
338+
`;
339+
306340
export const searchTeamSandboxes: Query<
307341
_SearchTeamSandboxesQuery,
308342
_SearchTeamSandboxesQueryVariables
@@ -313,12 +347,12 @@ export const searchTeamSandboxes: Query<
313347
314348
team(id: $teamId) {
315349
sandboxes(orderBy: { field: "updated_at", direction: DESC }) {
316-
...sandboxFragmentDashboard
350+
...searchTeamSandbox
317351
}
318352
}
319353
}
320354
}
321-
${sandboxFragmentDashboard}
355+
${SEARCH_TEAM_SANDBOX_FRAGMENT}
322356
`;
323357

324358
/**

packages/app/src/app/overmind/namespaces/dashboard/actions.ts

Lines changed: 94 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@ import { uniq } from 'lodash-es';
66
import {
77
TemplateFragmentDashboardFragment,
88
SandboxFragmentDashboardFragment,
9+
SandboxByPathFragment,
910
DraftSandboxFragment,
1011
RepoFragmentDashboardFragment,
1112
ProjectFragment,
13+
SearchTeamSandboxFragment,
1214
} from 'app/graphql/types';
1315
import {
1416
sandboxUrl,
@@ -29,6 +31,19 @@ import {
2931
import { OrderBy, PageTypes, sandboxesTypes } from './types';
3032
import * as internalActions from './internalActions';
3133

34+
// Type guards to check if sandbox has specific properties
35+
function hasIsFrozen(
36+
sandbox: SandboxFragmentDashboardFragment | SandboxByPathFragment | DraftSandboxFragment | SearchTeamSandboxFragment
37+
): sandbox is SandboxFragmentDashboardFragment | SandboxByPathFragment | DraftSandboxFragment {
38+
return 'isFrozen' in sandbox;
39+
}
40+
41+
function hasPermissions(
42+
sandbox: SandboxFragmentDashboardFragment | SandboxByPathFragment | DraftSandboxFragment | SearchTeamSandboxFragment
43+
): sandbox is SandboxFragmentDashboardFragment | SandboxByPathFragment | DraftSandboxFragment {
44+
return 'permissions' in sandbox;
45+
}
46+
3247
export const internal = internalActions;
3348

3449
export const dashboardMounted = withLoadApp();
@@ -751,7 +766,7 @@ export const getSearchSandboxes = async ({ state, effects }: Context) => {
751766
try {
752767
const activeTeam = state.activeTeam;
753768

754-
let sandboxes: SandboxFragmentDashboardFragment[] = [];
769+
let sandboxes: SearchTeamSandboxFragment[] = [];
755770
if (activeTeam) {
756771
const data = await effects.gql.queries.searchTeamSandboxes({
757772
teamId: activeTeam,
@@ -980,21 +995,35 @@ export const changeSandboxesFrozen = async (
980995
changedSandboxes,
981996
} = actions.dashboard.internal.changeSandboxesInState({
982997
sandboxIds,
983-
sandboxMutation: sandbox => ({ ...sandbox, isFrozen }),
998+
sandboxMutation: sandbox => {
999+
// Only update isFrozen if the property exists on the sandbox type
1000+
if (hasIsFrozen(sandbox)) {
1001+
return { ...sandbox, isFrozen };
1002+
}
1003+
return sandbox;
1004+
},
9841005
});
9851006

9861007
try {
9871008
await effects.gql.mutations.changeFrozen({ sandboxIds, isFrozen });
9881009
} catch (error) {
989-
changedSandboxes.forEach(oldSandbox =>
990-
actions.dashboard.internal.changeSandboxesInState({
991-
sandboxIds: [oldSandbox.id],
992-
sandboxMutation: sandbox => ({
993-
...sandbox,
994-
isFrozen: oldSandbox.isFrozen,
995-
}),
996-
})
997-
);
1010+
changedSandboxes.forEach(oldSandbox => {
1011+
// Only rollback if the sandbox has isFrozen property
1012+
if (hasIsFrozen(oldSandbox)) {
1013+
actions.dashboard.internal.changeSandboxesInState({
1014+
sandboxIds: [oldSandbox.id],
1015+
sandboxMutation: sandbox => {
1016+
if (hasIsFrozen(sandbox)) {
1017+
return {
1018+
...sandbox,
1019+
isFrozen: oldSandbox.isFrozen,
1020+
};
1021+
}
1022+
return sandbox;
1023+
},
1024+
});
1025+
}
1026+
});
9981027

9991028
actions.internal.handleError({
10001029
message:
@@ -1128,10 +1157,16 @@ export const setPreventSandboxesLeavingWorkspace = async (
11281157
changedSandboxes,
11291158
} = actions.dashboard.internal.changeSandboxesInState({
11301159
sandboxIds,
1131-
sandboxMutation: sandbox => ({
1132-
...sandbox,
1133-
permissions: { ...sandbox.permissions, preventSandboxLeaving },
1134-
}),
1160+
sandboxMutation: sandbox => {
1161+
// Only update permissions if the property exists on the sandbox type
1162+
if (hasPermissions(sandbox) && sandbox.permissions) {
1163+
return {
1164+
...sandbox,
1165+
permissions: { ...sandbox.permissions, preventSandboxLeaving },
1166+
};
1167+
}
1168+
return sandbox;
1169+
},
11351170
});
11361171

11371172
effects.analytics.track(`Dashboard - Change sandbox permissions`, {
@@ -1146,15 +1181,23 @@ export const setPreventSandboxesLeavingWorkspace = async (
11461181

11471182
effects.notificationToast.success('Sandbox permissions updated.');
11481183
} catch (error) {
1149-
changedSandboxes.forEach(oldSandbox =>
1150-
actions.dashboard.internal.changeSandboxesInState({
1151-
sandboxIds: [oldSandbox.id],
1152-
sandboxMutation: sandbox => ({
1153-
...sandbox,
1154-
permissions: { ...oldSandbox.permissions },
1155-
}),
1156-
})
1157-
);
1184+
changedSandboxes.forEach(oldSandbox => {
1185+
// Only rollback if the sandbox has permissions property
1186+
if (hasPermissions(oldSandbox) && oldSandbox.permissions) {
1187+
actions.dashboard.internal.changeSandboxesInState({
1188+
sandboxIds: [oldSandbox.id],
1189+
sandboxMutation: sandbox => {
1190+
if (hasPermissions(sandbox) && sandbox.permissions) {
1191+
return {
1192+
...sandbox,
1193+
permissions: { ...oldSandbox.permissions },
1194+
};
1195+
}
1196+
return sandbox;
1197+
},
1198+
});
1199+
}
1200+
});
11581201
effects.notificationToast.error(
11591202
'There was a problem updating your sandbox permissions'
11601203
);
@@ -1176,10 +1219,16 @@ export const setPreventSandboxesExport = async (
11761219
changedSandboxes,
11771220
} = actions.dashboard.internal.changeSandboxesInState({
11781221
sandboxIds,
1179-
sandboxMutation: sandbox => ({
1180-
...sandbox,
1181-
permissions: { ...sandbox.permissions, preventSandboxExport },
1182-
}),
1222+
sandboxMutation: sandbox => {
1223+
// Only update permissions if the property exists on the sandbox type
1224+
if (hasPermissions(sandbox) && sandbox.permissions) {
1225+
return {
1226+
...sandbox,
1227+
permissions: { ...sandbox.permissions, preventSandboxExport },
1228+
};
1229+
}
1230+
return sandbox;
1231+
},
11831232
});
11841233

11851234
effects.analytics.track(`Dashboard - Change sandbox permissions`, {
@@ -1194,15 +1243,23 @@ export const setPreventSandboxesExport = async (
11941243

11951244
effects.notificationToast.success('Sandbox permissions updated.');
11961245
} catch (error) {
1197-
changedSandboxes.forEach(oldSandbox =>
1198-
actions.dashboard.internal.changeSandboxesInState({
1199-
sandboxIds: [oldSandbox.id],
1200-
sandboxMutation: sandbox => ({
1201-
...sandbox,
1202-
permissions: { ...oldSandbox.permissions },
1203-
}),
1204-
})
1205-
);
1246+
changedSandboxes.forEach(oldSandbox => {
1247+
// Only rollback if the sandbox has permissions property
1248+
if (hasPermissions(oldSandbox) && oldSandbox.permissions) {
1249+
actions.dashboard.internal.changeSandboxesInState({
1250+
sandboxIds: [oldSandbox.id],
1251+
sandboxMutation: sandbox => {
1252+
if (hasPermissions(sandbox) && sandbox.permissions) {
1253+
return {
1254+
...sandbox,
1255+
permissions: { ...oldSandbox.permissions },
1256+
};
1257+
}
1258+
return sandbox;
1259+
},
1260+
});
1261+
}
1262+
});
12061263
effects.notificationToast.error(
12071264
'There was a problem updating your sandbox permissions'
12081265
);

packages/app/src/app/overmind/namespaces/dashboard/internalActions.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
SandboxFragmentDashboardFragment,
44
SandboxByPathFragment,
55
DraftSandboxFragment,
6+
SearchTeamSandboxFragment,
67
} from 'app/graphql/types';
78

89
/**
@@ -19,14 +20,14 @@ export const changeSandboxesInState = (
1920
* The mutation that happens on the sandbox, make sure to return a *new* sandbox here, to make sure
2021
* that we can still rollback easily in the future.
2122
*/
22-
sandboxMutation: <T extends SandboxFragmentDashboardFragment | SandboxByPathFragment | DraftSandboxFragment>(
23+
sandboxMutation: <T extends SandboxFragmentDashboardFragment | SandboxByPathFragment | DraftSandboxFragment | SearchTeamSandboxFragment>(
2324
sandbox: T
2425
) => T;
2526
}
2627
) => {
2728
const changedSandboxes: Set<ReturnType<typeof sandboxMutation>> = new Set();
2829

29-
const doMutateSandbox = <T extends SandboxFragmentDashboardFragment | SandboxByPathFragment | DraftSandboxFragment>(
30+
const doMutateSandbox = <T extends SandboxFragmentDashboardFragment | SandboxByPathFragment | DraftSandboxFragment | SearchTeamSandboxFragment>(
3031
sandbox: T
3132
): T => {
3233
changedSandboxes.add(sandbox);
@@ -110,7 +111,7 @@ export const deleteSandboxesFromState = (
110111
ids: string[];
111112
}
112113
) => {
113-
const sandboxFilter = <T extends SandboxFragmentDashboardFragment | SandboxByPathFragment | DraftSandboxFragment>(
114+
const sandboxFilter = <T extends SandboxFragmentDashboardFragment | SandboxByPathFragment | DraftSandboxFragment | SearchTeamSandboxFragment>(
114115
sandbox: T
115116
): boolean => !ids.includes(sandbox.id);
116117

@@ -165,7 +166,8 @@ export const deleteSandboxesFromState = (
165166
} else if (type !== 'RECENT_BRANCHES') {
166167
const newSandboxes = sandboxStructure[type].filter(sandboxFilter);
167168
if (newSandboxes.length !== sandboxStructure[type].length) {
168-
dashboard.sandboxes[type] = newSandboxes;
169+
// TypeScript can't narrow the union type based on the key, so we need to assert
170+
(dashboard.sandboxes as any)[type] = newSandboxes;
169171
}
170172
}
171173
});

0 commit comments

Comments
 (0)