-
Notifications
You must be signed in to change notification settings - Fork 61
Expand file tree
/
Copy pathuseUserIntegrations.ts
More file actions
141 lines (129 loc) · 4.68 KB
/
Copy pathuseUserIntegrations.ts
File metadata and controls
141 lines (129 loc) · 4.68 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import {
buildUserRepositoryOptions,
normalizeRepositoryNames,
type RepositoryOption,
repositoryLoadWarning,
} from "@posthog/core/integrations/repositories";
import { useQuery } from "@tanstack/react-query";
import { useCallback, useMemo } from "react";
import { useAuthStore } from "@/features/auth";
import { getPostHogApiClient } from "@/lib/posthogApiClient";
/**
* User-scoped sibling of {@link useIntegrations}. Reads the authenticated
* user's personal GitHub integrations (`/api/users/@me/integrations/`) rather
* than the team-level ones, matching how the desktop app links GitHub per user.
*
* Used by the interactive task-creation flow (new task screen, task list empty
* state, connect prompt). Automations stay on {@link useIntegrations} because
* they run server-side without a user and need the team integration.
*
* Repos are keyed by the numeric GitHub `installation_id` so the existing
* number-based picker/`RepositoryOption` keep working; `getUserIntegrationId`
* maps that back to the `UserIntegration` UUID for task creation. No persisted
* cache here (unlike the team hook) so it can't clobber the automations cache.
*/
export const userIntegrationKeys = {
all: ["user-integrations"] as const,
github: () => [...userIntegrationKeys.all, "github"] as const,
repos: (installationIds: string[]) =>
[...userIntegrationKeys.all, "repos", installationIds] as const,
};
interface UseUserIntegrationsOptions {
enabled?: boolean;
}
export function useUserIntegrations(options: UseUserIntegrationsOptions = {}) {
const { enabled = true } = options;
const { oauthAccessToken } = useAuthStore();
const integrationsQuery = useQuery({
queryKey: userIntegrationKeys.github(),
queryFn: async () => {
const integrations =
await getPostHogApiClient().getGithubUserIntegrations();
return integrations.map(({ account, ...integration }) => ({
...integration,
account: account
? {
name: account.name ?? undefined,
type: account.type ?? undefined,
}
: undefined,
}));
},
enabled: enabled && !!oauthAccessToken,
});
const integrations = enabled ? (integrationsQuery.data ?? []) : [];
const repositoriesQuery = useQuery({
queryKey: userIntegrationKeys.repos(
integrations.map((i) => i.installation_id),
),
queryFn: async () => {
const byInstallation: Record<string, string[]> = {};
const results = await Promise.allSettled(
integrations.map(async (integration) => ({
installationId: integration.installation_id,
repositories: normalizeRepositoryNames(
await getPostHogApiClient().getGithubUserRepositories(
integration.installation_id,
),
),
})),
);
let failedCount = 0;
for (const result of results) {
if (result.status === "fulfilled") {
byInstallation[result.value.installationId] =
result.value.repositories;
} else {
failedCount += 1;
}
}
return {
byInstallation,
partialError: repositoryLoadWarning(failedCount, integrations.length),
};
},
enabled: enabled && integrations.length > 0,
});
const repositoryOptions = useMemo<RepositoryOption[]>(() => {
return buildUserRepositoryOptions(
integrations,
repositoriesQuery.data?.byInstallation ?? {},
);
}, [integrations, repositoriesQuery.data]);
/** Resolve the `UserIntegration` UUID for a selected installation id, to send
* as `github_user_integration` on task creation. */
const getUserIntegrationId = useCallback(
(installationId: number | null): string | undefined => {
if (installationId == null) return undefined;
return integrations.find(
(i) => Number(i.installation_id) === installationId,
)?.id;
},
[integrations],
);
const refetch = useCallback(async () => {
if (!enabled) return;
await integrationsQuery.refetch();
await repositoriesQuery.refetch();
}, [enabled, integrationsQuery, repositoriesQuery]);
return {
hasGithubIntegration: !enabled
? null
: integrationsQuery.isFetched
? integrations.length > 0
: null,
integrations,
repositoryOptions,
getUserIntegrationId,
// No persisted cache, so there is no "cached list while refreshing" state.
isRefreshingInBackground: false,
isLoading: enabled
? integrationsQuery.isLoading || repositoriesQuery.isLoading
: false,
error: enabled ? (integrationsQuery.error?.message ?? null) : null,
repositoryWarning: enabled
? (repositoriesQuery.data?.partialError ?? null)
: null,
refetch,
};
}