@shipfox/client-integrations 1.0.0 → 2.0.0
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 +27 -0
- package/dist/components/callback-status-shell.d.ts +9 -0
- package/dist/components/callback-status-shell.d.ts.map +1 -0
- package/dist/components/callback-status-shell.js +81 -0
- package/dist/components/callback-status-shell.js.map +1 -0
- package/dist/components/integration-gallery.stories.d.ts.map +1 -1
- package/dist/components/integration-gallery.stories.js +28 -0
- package/dist/components/integration-gallery.stories.js.map +1 -1
- package/dist/components/redirect-install-page.d.ts +2 -1
- package/dist/components/redirect-install-page.d.ts.map +1 -1
- package/dist/components/redirect-install-page.js +4 -2
- package/dist/components/redirect-install-page.js.map +1 -1
- package/dist/feature.d.ts +8 -0
- package/dist/feature.d.ts.map +1 -1
- package/dist/feature.js +10 -0
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/integrations.d.ts +41 -0
- package/dist/hooks/api/integrations.d.ts.map +1 -1
- package/dist/hooks/api/integrations.js +24 -0
- package/dist/hooks/api/integrations.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/pages/linear-callback-page.d.ts.map +1 -1
- package/dist/pages/linear-callback-page.js +11 -78
- 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 +1 -0
- package/dist/pages/linear-install-page.js.map +1 -1
- package/dist/pages/sentry-install-page.d.ts.map +1 -1
- package/dist/pages/sentry-install-page.js +1 -0
- package/dist/pages/sentry-install-page.js.map +1 -1
- package/dist/pages/slack-callback-page.d.ts +2 -0
- package/dist/pages/slack-callback-page.d.ts.map +1 -0
- package/dist/pages/slack-callback-page.js +114 -0
- package/dist/pages/slack-callback-page.js.map +1 -0
- package/dist/pages/slack-install-page.d.ts +2 -0
- package/dist/pages/slack-install-page.d.ts.map +1 -0
- package/dist/pages/slack-install-page.js +19 -0
- package/dist/pages/slack-install-page.js.map +1 -0
- package/dist/provider-catalog.d.ts +1 -1
- package/dist/provider-catalog.d.ts.map +1 -1
- package/dist/provider-catalog.js +5 -0
- package/dist/provider-catalog.js.map +1 -1
- package/dist/routes/slack-callback.d.ts +6 -0
- package/dist/routes/slack-callback.d.ts.map +1 -0
- package/dist/routes/slack-callback.js +7 -0
- package/dist/routes/slack-callback.js.map +1 -0
- package/dist/routes/slack.d.ts +6 -0
- package/dist/routes/slack.d.ts.map +1 -0
- package/dist/routes/slack.js +7 -0
- package/dist/routes/slack.js.map +1 -0
- package/dist/slack-callback.d.ts +17 -0
- package/dist/slack-callback.d.ts.map +1 -0
- package/dist/slack-callback.js +99 -0
- package/dist/slack-callback.js.map +1 -0
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +13 -12
- package/src/components/callback-status-shell.tsx +73 -0
- package/src/components/integration-gallery.stories.tsx +18 -0
- package/src/components/redirect-install-page.tsx +3 -1
- package/src/feature.ts +10 -0
- package/src/hooks/api/integrations.test.ts +30 -0
- package/src/hooks/api/integrations.ts +35 -0
- package/src/index.ts +3 -0
- package/src/pages/linear-callback-page.tsx +11 -52
- package/src/pages/linear-install-page.tsx +1 -0
- package/src/pages/sentry-install-page.tsx +1 -0
- package/src/pages/slack-callback-page.tsx +120 -0
- package/src/pages/slack-install-page.tsx +22 -0
- package/src/provider-catalog.ts +7 -1
- package/src/routes/slack-callback.tsx +3 -0
- package/src/routes/slack.tsx +3 -0
- package/src/slack-callback.test.ts +32 -0
- package/src/slack-callback.ts +141 -0
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type {SlackCallbackQueryDto} from '@shipfox/api-integration-slack-dto';
|
|
2
|
+
import {ApiError} from '@shipfox/client-api';
|
|
3
|
+
|
|
4
|
+
export const SLACK_INSTALL_WORKSPACE_KEY = 'shipfox.slack-install.workspace-id';
|
|
5
|
+
type WorkspaceStorage = Pick<Storage, 'getItem' | 'setItem' | 'removeItem'>;
|
|
6
|
+
|
|
7
|
+
export function saveSlackInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void {
|
|
8
|
+
try {
|
|
9
|
+
storage.setItem(SLACK_INSTALL_WORKSPACE_KEY, workspaceId);
|
|
10
|
+
} catch {
|
|
11
|
+
// Storage only supports recovery navigation.
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export function readSlackInstallWorkspace(storage: WorkspaceStorage): string | undefined {
|
|
15
|
+
try {
|
|
16
|
+
return storage.getItem(SLACK_INSTALL_WORKSPACE_KEY) ?? undefined;
|
|
17
|
+
} catch {
|
|
18
|
+
return undefined;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function clearSlackInstallWorkspace(storage: WorkspaceStorage): void {
|
|
22
|
+
try {
|
|
23
|
+
storage.removeItem(SLACK_INSTALL_WORKSPACE_KEY);
|
|
24
|
+
} catch {
|
|
25
|
+
// A stale recovery hint cannot affect the API callback.
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function parseSlackCallbackQuery(
|
|
30
|
+
search: Record<string, unknown>,
|
|
31
|
+
): SlackCallbackQueryDto | undefined {
|
|
32
|
+
const state = stringParam(search.state);
|
|
33
|
+
if (!state) return undefined;
|
|
34
|
+
const code = stringParam(search.code);
|
|
35
|
+
if (code) return {code, state};
|
|
36
|
+
const error = stringParam(search.error);
|
|
37
|
+
if (!error) return undefined;
|
|
38
|
+
const errorDescription = stringParam(search.error_description);
|
|
39
|
+
return errorDescription ? {error, error_description: errorDescription, state} : {error, state};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function serializeSlackCallbackQuery(query: SlackCallbackQueryDto): string {
|
|
43
|
+
const params = new URLSearchParams();
|
|
44
|
+
if ('code' in query) params.set('code', query.code);
|
|
45
|
+
else {
|
|
46
|
+
params.set('error', query.error);
|
|
47
|
+
if (query.error_description) params.set('error_description', query.error_description);
|
|
48
|
+
}
|
|
49
|
+
params.set('state', query.state);
|
|
50
|
+
return params.toString();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export type SlackCallbackFailure = {
|
|
54
|
+
title: string;
|
|
55
|
+
message: string;
|
|
56
|
+
startOver: boolean;
|
|
57
|
+
signIn: boolean;
|
|
58
|
+
};
|
|
59
|
+
export function classifySlackCallbackError(error: unknown): SlackCallbackFailure {
|
|
60
|
+
if (error instanceof ApiError) {
|
|
61
|
+
if (error.code === 'invalid-slack-install-state')
|
|
62
|
+
return failure(
|
|
63
|
+
'Slack install link expired',
|
|
64
|
+
'Slack install link expired. Start again from workspace settings.',
|
|
65
|
+
true,
|
|
66
|
+
);
|
|
67
|
+
if (error.code === 'slack-install-state-actor-mismatch' || error.code === 'unauthorized')
|
|
68
|
+
return {
|
|
69
|
+
...failure(
|
|
70
|
+
'Different Shipfox account',
|
|
71
|
+
'Different Shipfox account. Sign in with the account that started this install.',
|
|
72
|
+
true,
|
|
73
|
+
),
|
|
74
|
+
signIn: true,
|
|
75
|
+
};
|
|
76
|
+
if (['not-found', 'forbidden', 'workspace-inactive'].includes(error.code))
|
|
77
|
+
return failure(
|
|
78
|
+
'Workspace access changed',
|
|
79
|
+
'You no longer have access to this workspace. Return to Shipfox to continue.',
|
|
80
|
+
false,
|
|
81
|
+
);
|
|
82
|
+
if (
|
|
83
|
+
[
|
|
84
|
+
'slack-installation-already-linked',
|
|
85
|
+
'slack-connection-already-linked',
|
|
86
|
+
'slug-conflict',
|
|
87
|
+
].includes(error.code)
|
|
88
|
+
)
|
|
89
|
+
return failure(
|
|
90
|
+
'Slack already linked',
|
|
91
|
+
'This Slack workspace is already linked and cannot be installed again here.',
|
|
92
|
+
false,
|
|
93
|
+
);
|
|
94
|
+
if (
|
|
95
|
+
[
|
|
96
|
+
'slack-authorization-scope-mismatch',
|
|
97
|
+
'slack-oauth-callback-error',
|
|
98
|
+
'access-denied',
|
|
99
|
+
].includes(error.code)
|
|
100
|
+
)
|
|
101
|
+
return failure(
|
|
102
|
+
'Slack permissions needed',
|
|
103
|
+
'Slack did not authorize the permissions Shipfox needs. Review the consent and start again.',
|
|
104
|
+
true,
|
|
105
|
+
);
|
|
106
|
+
if (
|
|
107
|
+
error.code === 'slack-enterprise-install-unsupported' ||
|
|
108
|
+
error.code === 'slack-token-rotation-unsupported'
|
|
109
|
+
)
|
|
110
|
+
return failure(
|
|
111
|
+
'Slack install unsupported',
|
|
112
|
+
'This Slack installation is not supported. Return to Shipfox to continue.',
|
|
113
|
+
false,
|
|
114
|
+
);
|
|
115
|
+
if (error.status === 0 || error.code === 'network-error')
|
|
116
|
+
return failure('Could not reach Shipfox', 'Check your connection and start again.', true);
|
|
117
|
+
if (
|
|
118
|
+
error.status >= 500 ||
|
|
119
|
+
error.status === 429 ||
|
|
120
|
+
['rate-limited', 'timeout', 'provider-unavailable', 'malformed-provider-response'].includes(
|
|
121
|
+
error.code,
|
|
122
|
+
)
|
|
123
|
+
)
|
|
124
|
+
return failure(
|
|
125
|
+
'Slack is temporarily unavailable',
|
|
126
|
+
'Start a new install when Slack is available.',
|
|
127
|
+
true,
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
return failure(
|
|
131
|
+
'Slack install could not be completed',
|
|
132
|
+
'Could not complete the Slack install. Start again from workspace settings.',
|
|
133
|
+
true,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
function failure(title: string, message: string, startOver: boolean): SlackCallbackFailure {
|
|
137
|
+
return {title, message, startOver, signIn: false};
|
|
138
|
+
}
|
|
139
|
+
function stringParam(value: unknown): string | undefined {
|
|
140
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
141
|
+
}
|