@shipfox/client-integrations 5.0.0 → 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +64 -0
- package/dist/application/complete-integration-callback.d.ts +13 -0
- package/dist/application/complete-integration-callback.d.ts.map +1 -0
- package/dist/application/complete-integration-callback.js +26 -0
- package/dist/application/complete-integration-callback.js.map +1 -0
- package/dist/components/connection-picker.d.ts +2 -2
- package/dist/components/connection-picker.d.ts.map +1 -1
- package/dist/components/connection-picker.js +4 -4
- package/dist/components/connection-picker.js.map +1 -1
- package/dist/components/installed-integrations-section.d.ts +8 -6
- package/dist/components/installed-integrations-section.d.ts.map +1 -1
- package/dist/components/installed-integrations-section.js +22 -15
- package/dist/components/installed-integrations-section.js.map +1 -1
- package/dist/components/integration-gallery-for-workspace.d.ts +2 -2
- package/dist/components/integration-gallery-for-workspace.d.ts.map +1 -1
- package/dist/components/integration-gallery-for-workspace.js +14 -7
- package/dist/components/integration-gallery-for-workspace.js.map +1 -1
- package/dist/components/integration-gallery.d.ts +2 -2
- package/dist/components/integration-gallery.d.ts.map +1 -1
- package/dist/components/integration-gallery.js.map +1 -1
- package/dist/components/integration-usage-events.d.ts +1 -3
- package/dist/components/integration-usage-events.d.ts.map +1 -1
- package/dist/components/integration-usage-events.js +1 -125
- package/dist/components/integration-usage-events.js.map +1 -1
- package/dist/components/integration-usage-modal.d.ts +2 -2
- package/dist/components/integration-usage-modal.d.ts.map +1 -1
- package/dist/components/integration-usage-modal.js +2 -2
- package/dist/components/integration-usage-modal.js.map +1 -1
- package/dist/components/provider-grid.d.ts +7 -4
- package/dist/components/provider-grid.d.ts.map +1 -1
- package/dist/components/provider-grid.js +15 -10
- package/dist/components/provider-grid.js.map +1 -1
- package/dist/components/redirect-install-page.d.ts +2 -3
- package/dist/components/redirect-install-page.d.ts.map +1 -1
- package/dist/components/redirect-install-page.js +1 -1
- package/dist/components/redirect-install-page.js.map +1 -1
- package/dist/components/repository-picker.d.ts +2 -2
- package/dist/components/repository-picker.d.ts.map +1 -1
- package/dist/components/repository-picker.js +4 -4
- package/dist/components/repository-picker.js.map +1 -1
- package/dist/components/webhook/webhook-create-modal.d.ts +2 -2
- package/dist/components/webhook/webhook-create-modal.d.ts.map +1 -1
- package/dist/components/webhook/webhook-create-modal.js +8 -8
- package/dist/components/webhook/webhook-create-modal.js.map +1 -1
- package/dist/components/webhook/webhook-modals.stories.d.ts.map +1 -1
- package/dist/components/webhook/webhook-modals.stories.js +7 -7
- package/dist/components/webhook/webhook-modals.stories.js.map +1 -1
- package/dist/components/webhook/webhook-usage-details.d.ts.map +1 -1
- package/dist/components/webhook/webhook-usage-details.js +2 -2
- package/dist/components/webhook/webhook-usage-details.js.map +1 -1
- package/dist/connection-status-badge.d.ts +2 -2
- package/dist/connection-status-badge.d.ts.map +1 -1
- package/dist/connection-status-badge.js +11 -15
- package/dist/connection-status-badge.js.map +1 -1
- package/dist/core/models.d.ts +66 -0
- package/dist/core/models.d.ts.map +1 -0
- package/dist/core/models.js +156 -0
- package/dist/core/models.js.map +1 -0
- package/dist/feature.d.ts +7 -0
- package/dist/feature.d.ts.map +1 -1
- package/dist/feature.js +10 -9
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/integration-mapper.d.ts +11 -0
- package/dist/hooks/api/integration-mapper.d.ts.map +1 -0
- package/dist/hooks/api/integration-mapper.js +54 -0
- package/dist/hooks/api/integration-mapper.js.map +1 -0
- package/dist/hooks/api/integrations.d.ts +45 -240
- package/dist/hooks/api/integrations.d.ts.map +1 -1
- package/dist/hooks/api/integrations.js +78 -31
- package/dist/hooks/api/integrations.js.map +1 -1
- package/dist/hooks/api/webhook-connections.d.ts +12 -64
- package/dist/hooks/api/webhook-connections.d.ts.map +1 -1
- package/dist/hooks/api/webhook-connections.js +17 -9
- package/dist/hooks/api/webhook-connections.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/linear-callback.d.ts +2 -1
- package/dist/linear-callback.d.ts.map +1 -1
- package/dist/linear-callback.js +14 -15
- package/dist/linear-callback.js.map +1 -1
- package/dist/pages/gitea-install-page.d.ts.map +1 -1
- package/dist/pages/gitea-install-page.js +1 -10
- package/dist/pages/gitea-install-page.js.map +1 -1
- package/dist/pages/linear-callback-page.d.ts.map +1 -1
- package/dist/pages/linear-callback-page.js +19 -32
- package/dist/pages/linear-callback-page.js.map +1 -1
- package/dist/pages/linear-install-page.d.ts.map +1 -1
- package/dist/pages/linear-install-page.js +2 -1
- package/dist/pages/linear-install-page.js.map +1 -1
- package/dist/pages/sentry-callback-page.d.ts.map +1 -1
- package/dist/pages/sentry-callback-page.js +22 -25
- package/dist/pages/sentry-callback-page.js.map +1 -1
- package/dist/pages/sentry-install-page.d.ts.map +1 -1
- package/dist/pages/sentry-install-page.js +2 -1
- package/dist/pages/sentry-install-page.js.map +1 -1
- package/dist/pages/slack-callback-page.d.ts.map +1 -1
- package/dist/pages/slack-callback-page.js +21 -28
- package/dist/pages/slack-callback-page.js.map +1 -1
- package/dist/pages/slack-install-page.d.ts.map +1 -1
- package/dist/pages/slack-install-page.js +2 -1
- package/dist/pages/slack-install-page.js.map +1 -1
- package/dist/pages/source-control-onboarding-page.d.ts.map +1 -1
- package/dist/pages/source-control-onboarding-page.js +5 -1
- package/dist/pages/source-control-onboarding-page.js.map +1 -1
- package/dist/provider-catalog.d.ts.map +1 -1
- package/dist/provider-catalog.js +7 -6
- package/dist/provider-catalog.js.map +1 -1
- package/dist/routes/github-callback.d.ts +16 -0
- package/dist/routes/github-callback.d.ts.map +1 -1
- package/dist/routes/github-callback.js +43 -21
- package/dist/routes/github-callback.js.map +1 -1
- package/dist/routes/linear-callback.d.ts +2 -0
- package/dist/routes/linear-callback.d.ts.map +1 -1
- package/dist/routes/linear-callback.js +2 -0
- package/dist/routes/linear-callback.js.map +1 -1
- package/dist/routes/sentry-callback.d.ts +2 -0
- package/dist/routes/sentry-callback.d.ts.map +1 -1
- package/dist/routes/sentry-callback.js +2 -0
- package/dist/routes/sentry-callback.js.map +1 -1
- package/dist/routes/slack-callback.d.ts +2 -0
- package/dist/routes/slack-callback.d.ts.map +1 -1
- package/dist/routes/slack-callback.js +2 -0
- package/dist/routes/slack-callback.js.map +1 -1
- package/dist/sentry-callback.d.ts +2 -1
- package/dist/sentry-callback.d.ts.map +1 -1
- package/dist/sentry-callback.js +16 -19
- package/dist/sentry-callback.js.map +1 -1
- package/dist/slack-callback.d.ts +2 -1
- package/dist/slack-callback.d.ts.map +1 -1
- package/dist/slack-callback.js +14 -15
- package/dist/slack-callback.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +17 -17
- package/src/application/complete-integration-callback.test.ts +41 -0
- package/src/application/complete-integration-callback.ts +39 -0
- package/src/components/connection-picker.tsx +7 -7
- package/src/components/installed-integrations-section.tsx +29 -24
- package/src/components/integration-gallery-for-workspace.tsx +23 -16
- package/src/components/integration-gallery.tsx +3 -3
- package/src/components/integration-usage-events.ts +1 -116
- package/src/components/integration-usage-modal.tsx +4 -4
- package/src/components/provider-grid.tsx +30 -18
- package/src/components/redirect-install-page.test.tsx +2 -6
- package/src/components/redirect-install-page.tsx +3 -2
- package/src/components/repository-picker.tsx +6 -6
- package/src/components/webhook/webhook-create-modal.tsx +12 -11
- package/src/components/webhook/webhook-modals.stories.tsx +10 -10
- package/src/components/webhook/webhook-usage-details.tsx +2 -4
- package/src/connection-status-badge.tsx +8 -14
- package/src/core/models.ts +200 -0
- package/src/feature.ts +12 -10
- package/src/hooks/api/integration-mapper.ts +64 -0
- package/src/hooks/api/integrations.test.ts +44 -22
- package/src/hooks/api/integrations.ts +229 -84
- package/src/hooks/api/webhook-connections.ts +58 -17
- package/src/index.ts +2 -1
- package/src/linear-callback.ts +21 -16
- package/src/pages/gitea-install-page.tsx +1 -10
- package/src/pages/linear-callback-page.tsx +18 -30
- package/src/pages/linear-install-page.tsx +2 -1
- package/src/pages/sentry-callback-page.test.tsx +5 -1
- package/src/pages/sentry-callback-page.tsx +28 -25
- package/src/pages/sentry-install-page.tsx +2 -1
- package/src/pages/slack-callback-page.tsx +20 -26
- package/src/pages/slack-install-page.tsx +2 -1
- package/src/pages/source-control-onboarding-page.tsx +5 -1
- package/src/provider-catalog.ts +7 -6
- package/src/routes/github-callback.test.ts +35 -1
- package/src/routes/github-callback.tsx +47 -21
- package/src/routes/linear-callback.tsx +5 -1
- package/src/routes/sentry-callback.tsx +5 -1
- package/src/routes/slack-callback.tsx +6 -1
- package/src/sentry-callback.test.ts +2 -1
- package/src/sentry-callback.ts +28 -20
- package/src/slack-callback.ts +21 -16
- package/tsconfig.build.tsbuildinfo +1 -1
- package/vercel.json +5 -0
- package/dist/integration-icon.d.ts +0 -17
- package/dist/integration-icon.d.ts.map +0 -1
- package/dist/integration-icon.js +0 -22
- package/dist/integration-icon.js.map +0 -1
- package/src/integration-icon.react.test.tsx +0 -40
- package/src/integration-icon.test.ts +0 -19
- package/src/integration-icon.tsx +0 -26
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import {ApiError} from '@shipfox/client-api';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
githubCallbackErrorMessage,
|
|
4
|
+
githubCallbackParams,
|
|
5
|
+
missingCallbackParams,
|
|
6
|
+
validateGithubCallbackSearch,
|
|
7
|
+
} from './github-callback.js';
|
|
3
8
|
|
|
4
9
|
describe('githubCallbackErrorMessage', () => {
|
|
5
10
|
it('does not expose an API error message or request URL', () => {
|
|
@@ -15,3 +20,32 @@ describe('githubCallbackErrorMessage', () => {
|
|
|
15
20
|
expect(message).not.toContain('https://');
|
|
16
21
|
});
|
|
17
22
|
});
|
|
23
|
+
|
|
24
|
+
describe('github callback route inputs', () => {
|
|
25
|
+
it('normalizes valid values and reports missing or malformed inputs', () => {
|
|
26
|
+
const search = validateGithubCallbackSearch({
|
|
27
|
+
code: 'grant-code',
|
|
28
|
+
installation_id: '42',
|
|
29
|
+
state: 'signed-state',
|
|
30
|
+
setup_action: 'install',
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
expect(search).toEqual({
|
|
34
|
+
code: 'grant-code',
|
|
35
|
+
installationId: 42,
|
|
36
|
+
state: 'signed-state',
|
|
37
|
+
setupAction: 'install',
|
|
38
|
+
});
|
|
39
|
+
expect(githubCallbackParams(search)).toEqual({
|
|
40
|
+
code: 'grant-code',
|
|
41
|
+
installationId: 42,
|
|
42
|
+
state: 'signed-state',
|
|
43
|
+
setupAction: 'install',
|
|
44
|
+
});
|
|
45
|
+
expect(missingCallbackParams(validateGithubCallbackSearch({code: ['bad'], state: 7}))).toEqual([
|
|
46
|
+
'code',
|
|
47
|
+
'installation_id',
|
|
48
|
+
'state',
|
|
49
|
+
]);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -1,30 +1,43 @@
|
|
|
1
|
-
import {ApiError
|
|
1
|
+
import {ApiError} from '@shipfox/client-api';
|
|
2
2
|
import {useRefreshAuth} from '@shipfox/client-auth';
|
|
3
|
-
import {defineRoute} from '@shipfox/client-shell/runtime';
|
|
3
|
+
import {defineRoute, useRouteSearch} from '@shipfox/client-shell/runtime';
|
|
4
4
|
import {createSingleFlight} from '@shipfox/client-ui';
|
|
5
5
|
import {FullPageLoader} from '@shipfox/react-ui/loader';
|
|
6
6
|
import {toast} from '@shipfox/react-ui/toast';
|
|
7
|
-
import {useNavigate
|
|
7
|
+
import {useNavigate} from '@tanstack/react-router';
|
|
8
8
|
import {useEffect} from 'react';
|
|
9
|
+
import {useCompleteIntegrationCallback} from '#application/complete-integration-callback.js';
|
|
10
|
+
import {completeGithubCallback} from '#hooks/api/integrations.js';
|
|
9
11
|
|
|
10
|
-
interface GithubCallbackParams {
|
|
12
|
+
export interface GithubCallbackParams {
|
|
11
13
|
code: string;
|
|
12
14
|
installationId: number;
|
|
13
15
|
state: string;
|
|
14
16
|
setupAction?: string;
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
export interface GithubCallbackSearch {
|
|
20
|
+
code?: string;
|
|
21
|
+
installationId?: number;
|
|
22
|
+
state?: string;
|
|
23
|
+
setupAction?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
// Retain only recent completions: this bounds long-lived callback pages while
|
|
18
27
|
// still covering StrictMode and immediate Back/Forward remounts.
|
|
19
28
|
const callbackRequests = createSingleFlight<string, void>({maxTerminalResults: 32});
|
|
20
29
|
const toastedCallbacks = new Set<string>();
|
|
21
30
|
|
|
22
|
-
export default defineRoute({
|
|
31
|
+
export default defineRoute({
|
|
32
|
+
validateSearch: validateGithubCallbackSearch,
|
|
33
|
+
component: GithubCallbackRoute,
|
|
34
|
+
});
|
|
23
35
|
|
|
24
36
|
function GithubCallbackRoute() {
|
|
25
|
-
const search =
|
|
37
|
+
const search = useRouteSearch(validateGithubCallbackSearch);
|
|
26
38
|
const navigate = useNavigate();
|
|
27
39
|
const refreshAuth = useRefreshAuth();
|
|
40
|
+
const completeIntegrationCallback = useCompleteIntegrationCallback();
|
|
28
41
|
|
|
29
42
|
useEffect(() => {
|
|
30
43
|
const missing = missingCallbackParams(search);
|
|
@@ -46,11 +59,11 @@ function GithubCallbackRoute() {
|
|
|
46
59
|
const request = callbackRequests.run(
|
|
47
60
|
key,
|
|
48
61
|
async () =>
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
})
|
|
53
|
-
}),
|
|
62
|
+
void (await completeIntegrationCallback({
|
|
63
|
+
input: params,
|
|
64
|
+
refreshAuth,
|
|
65
|
+
complete: async (input, token) => await completeGithubCallback({...input, token}),
|
|
66
|
+
})),
|
|
54
67
|
);
|
|
55
68
|
request
|
|
56
69
|
.then(() => {
|
|
@@ -72,24 +85,37 @@ function GithubCallbackRoute() {
|
|
|
72
85
|
return () => {
|
|
73
86
|
disposed = true;
|
|
74
87
|
};
|
|
75
|
-
}, [navigate, refreshAuth, search]);
|
|
88
|
+
}, [completeIntegrationCallback, navigate, refreshAuth, search]);
|
|
76
89
|
|
|
77
90
|
return <FullPageLoader />;
|
|
78
91
|
}
|
|
79
92
|
|
|
80
|
-
function
|
|
93
|
+
export function validateGithubCallbackSearch(input: Record<string, unknown>): GithubCallbackSearch {
|
|
94
|
+
const code = stringParam(input.code);
|
|
95
|
+
const installationId = numberParam(input.installation_id);
|
|
96
|
+
const state = stringParam(input.state);
|
|
97
|
+
const setupAction = stringParam(input.setup_action);
|
|
98
|
+
return {
|
|
99
|
+
...(code ? {code} : {}),
|
|
100
|
+
...(installationId === undefined ? {} : {installationId}),
|
|
101
|
+
...(state ? {state} : {}),
|
|
102
|
+
...(setupAction ? {setupAction} : {}),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function missingCallbackParams(search: GithubCallbackSearch): string[] {
|
|
81
107
|
const missing: string[] = [];
|
|
82
|
-
if (!
|
|
83
|
-
if (
|
|
84
|
-
if (!
|
|
108
|
+
if (!search.code) missing.push('code');
|
|
109
|
+
if (search.installationId === undefined) missing.push('installation_id');
|
|
110
|
+
if (!search.state) missing.push('state');
|
|
85
111
|
return missing;
|
|
86
112
|
}
|
|
87
|
-
function githubCallbackParams(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const state =
|
|
113
|
+
export function githubCallbackParams(
|
|
114
|
+
search: GithubCallbackSearch,
|
|
115
|
+
): GithubCallbackParams | undefined {
|
|
116
|
+
const {code, installationId, state} = search;
|
|
91
117
|
if (!code || installationId === undefined || !state) return undefined;
|
|
92
|
-
const setupAction =
|
|
118
|
+
const {setupAction} = search;
|
|
93
119
|
return setupAction ? {code, installationId, state, setupAction} : {code, installationId, state};
|
|
94
120
|
}
|
|
95
121
|
function encodeCallbackQuery(params: GithubCallbackParams): string {
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import {defineRoute} from '@shipfox/client-shell/runtime';
|
|
2
2
|
import {LinearCallbackPage} from '#pages/linear-callback-page.js';
|
|
3
|
+
import {parseLinearCallbackQuery} from '../linear-callback.js';
|
|
3
4
|
|
|
4
|
-
export default defineRoute({
|
|
5
|
+
export default defineRoute({
|
|
6
|
+
validateSearch: parseLinearCallbackQuery,
|
|
7
|
+
component: LinearCallbackPage,
|
|
8
|
+
});
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import {defineRoute} from '@shipfox/client-shell/runtime';
|
|
2
2
|
import {SentryCallbackPage} from '#pages/sentry-callback-page.js';
|
|
3
|
+
import {parseSentryCallbackParams} from '../sentry-callback.js';
|
|
3
4
|
|
|
4
|
-
export default defineRoute({
|
|
5
|
+
export default defineRoute({
|
|
6
|
+
validateSearch: parseSentryCallbackParams,
|
|
7
|
+
component: SentryCallbackPage,
|
|
8
|
+
});
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
import {defineRoute} from '@shipfox/client-shell/runtime';
|
|
2
2
|
import {SlackCallbackPage} from '#pages/slack-callback-page.js';
|
|
3
|
-
|
|
3
|
+
import {parseSlackCallbackQuery} from '../slack-callback.js';
|
|
4
|
+
|
|
5
|
+
export default defineRoute({
|
|
6
|
+
validateSearch: parseSlackCallbackQuery,
|
|
7
|
+
component: SlackCallbackPage,
|
|
8
|
+
});
|
|
@@ -181,7 +181,8 @@ describe('classifySentryConnectError', () => {
|
|
|
181
181
|
|
|
182
182
|
expect(result).toEqual({
|
|
183
183
|
kind: 'terminal',
|
|
184
|
-
message:
|
|
184
|
+
message:
|
|
185
|
+
'Sentry did not accept this install. Start the install again from workspace settings.',
|
|
185
186
|
startOver: true,
|
|
186
187
|
});
|
|
187
188
|
});
|
package/src/sentry-callback.ts
CHANGED
|
@@ -1,34 +1,39 @@
|
|
|
1
1
|
import {ApiError} from '@shipfox/client-api';
|
|
2
|
+
import {
|
|
3
|
+
type BrowserStorage,
|
|
4
|
+
type BrowserStorageKey,
|
|
5
|
+
createTypedBrowserStorage,
|
|
6
|
+
} from '@shipfox/client-ui';
|
|
2
7
|
|
|
3
8
|
export const SENTRY_INSTALL_WORKSPACE_KEY = 'shipfox.sentry-install.workspace-id';
|
|
4
9
|
|
|
5
|
-
type WorkspaceStorage =
|
|
10
|
+
type WorkspaceStorage = BrowserStorage | undefined;
|
|
11
|
+
|
|
12
|
+
const sentryInstallWorkspaceStorageKey = {
|
|
13
|
+
key: SENTRY_INSTALL_WORKSPACE_KEY,
|
|
14
|
+
lifetime: 'session',
|
|
15
|
+
principalScope: 'workspace',
|
|
16
|
+
serialize: (workspaceId: string) => workspaceId,
|
|
17
|
+
parse: (value: string) => value || undefined,
|
|
18
|
+
} satisfies BrowserStorageKey<string>;
|
|
6
19
|
|
|
7
20
|
// Storage helpers swallow failures (quota, private mode, disabled storage):
|
|
8
21
|
// the handoff is an optimization, never a requirement — the callback always
|
|
9
22
|
// asks for explicit confirmation and only uses the stored id to pre-select.
|
|
10
23
|
export function saveSentryInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void {
|
|
11
|
-
|
|
12
|
-
storage.setItem(SENTRY_INSTALL_WORKSPACE_KEY, workspaceId);
|
|
13
|
-
} catch {
|
|
14
|
-
// The callback falls back to its sole-workspace pre-selection.
|
|
15
|
-
}
|
|
24
|
+
installWorkspaceStorage(storage).write(workspaceId);
|
|
16
25
|
}
|
|
17
26
|
|
|
18
27
|
export function readSentryInstallWorkspace(storage: WorkspaceStorage): string | undefined {
|
|
19
|
-
|
|
20
|
-
return storage.getItem(SENTRY_INSTALL_WORKSPACE_KEY) ?? undefined;
|
|
21
|
-
} catch {
|
|
22
|
-
return undefined;
|
|
23
|
-
}
|
|
28
|
+
return installWorkspaceStorage(storage).read();
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
export function clearSentryInstallWorkspace(storage: WorkspaceStorage): void {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
installWorkspaceStorage(storage).remove();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function installWorkspaceStorage(storage: WorkspaceStorage) {
|
|
36
|
+
return createTypedBrowserStorage(() => storage, sentryInstallWorkspaceStorageKey);
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
export interface SentryCallbackParams {
|
|
@@ -108,7 +113,7 @@ export function classifySentryConnectError(error: unknown): SentryConnectFailure
|
|
|
108
113
|
return {kind: 'retryable', message: 'Sentry is unreachable. Try again.'};
|
|
109
114
|
}
|
|
110
115
|
if (error.status === 0 || error.code === 'network-error') {
|
|
111
|
-
// A network failure (
|
|
116
|
+
// A network failure (the client transport reports status 0) never reached the
|
|
112
117
|
// server, so the grant code is untouched and a plain Retry can recover.
|
|
113
118
|
// Must precede the status < 500 branch, which would otherwise treat it as
|
|
114
119
|
// a spent code and force a full restart.
|
|
@@ -118,9 +123,12 @@ export function classifySentryConnectError(error: unknown): SentryConnectFailure
|
|
|
118
123
|
};
|
|
119
124
|
}
|
|
120
125
|
if (error.status < 500) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
126
|
+
return {
|
|
127
|
+
kind: 'terminal',
|
|
128
|
+
message:
|
|
129
|
+
'Sentry did not accept this install. Start the install again from workspace settings.',
|
|
130
|
+
startOver: true,
|
|
131
|
+
};
|
|
124
132
|
}
|
|
125
133
|
}
|
|
126
134
|
return {kind: 'retryable', message: 'Could not install Sentry. Try again.'};
|
package/src/slack-callback.ts
CHANGED
|
@@ -1,29 +1,34 @@
|
|
|
1
1
|
import type {SlackCallbackQueryDto} from '@shipfox/api-integration-slack-dto';
|
|
2
2
|
import {ApiError} from '@shipfox/client-api';
|
|
3
|
+
import {
|
|
4
|
+
type BrowserStorage,
|
|
5
|
+
type BrowserStorageKey,
|
|
6
|
+
createTypedBrowserStorage,
|
|
7
|
+
} from '@shipfox/client-ui';
|
|
3
8
|
|
|
4
9
|
export const SLACK_INSTALL_WORKSPACE_KEY = 'shipfox.slack-install.workspace-id';
|
|
5
|
-
type WorkspaceStorage =
|
|
10
|
+
type WorkspaceStorage = BrowserStorage | undefined;
|
|
11
|
+
|
|
12
|
+
const slackInstallWorkspaceStorageKey = {
|
|
13
|
+
key: SLACK_INSTALL_WORKSPACE_KEY,
|
|
14
|
+
lifetime: 'session',
|
|
15
|
+
principalScope: 'workspace',
|
|
16
|
+
serialize: (workspaceId: string) => workspaceId,
|
|
17
|
+
parse: (value: string) => value || undefined,
|
|
18
|
+
} satisfies BrowserStorageKey<string>;
|
|
6
19
|
|
|
7
20
|
export function saveSlackInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void {
|
|
8
|
-
|
|
9
|
-
storage.setItem(SLACK_INSTALL_WORKSPACE_KEY, workspaceId);
|
|
10
|
-
} catch {
|
|
11
|
-
// Storage only supports recovery navigation.
|
|
12
|
-
}
|
|
21
|
+
installWorkspaceStorage(storage).write(workspaceId);
|
|
13
22
|
}
|
|
14
23
|
export function readSlackInstallWorkspace(storage: WorkspaceStorage): string | undefined {
|
|
15
|
-
|
|
16
|
-
return storage.getItem(SLACK_INSTALL_WORKSPACE_KEY) ?? undefined;
|
|
17
|
-
} catch {
|
|
18
|
-
return undefined;
|
|
19
|
-
}
|
|
24
|
+
return installWorkspaceStorage(storage).read();
|
|
20
25
|
}
|
|
21
26
|
export function clearSlackInstallWorkspace(storage: WorkspaceStorage): void {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
installWorkspaceStorage(storage).remove();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function installWorkspaceStorage(storage: WorkspaceStorage) {
|
|
31
|
+
return createTypedBrowserStorage(() => storage, slackInstallWorkspaceStorageKey);
|
|
27
32
|
}
|
|
28
33
|
|
|
29
34
|
export function parseSlackCallbackQuery(
|