@shipfox/client-integrations 12.0.1 → 13.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 +41 -0
- package/dist/components/callback-status-shell.d.ts +4 -3
- package/dist/components/callback-status-shell.d.ts.map +1 -1
- package/dist/components/callback-status-shell.js +11 -11
- package/dist/components/callback-status-shell.js.map +1 -1
- package/dist/components/installed-integrations-section.d.ts +2 -1
- package/dist/components/installed-integrations-section.d.ts.map +1 -1
- package/dist/components/installed-integrations-section.js +7 -6
- package/dist/components/installed-integrations-section.js.map +1 -1
- package/dist/components/integration-gallery-for-workspace.d.ts.map +1 -1
- package/dist/components/integration-gallery-for-workspace.js +11 -2
- package/dist/components/integration-gallery-for-workspace.js.map +1 -1
- package/dist/components/integration-gallery.stories.js +10 -9
- package/dist/components/integration-gallery.stories.js.map +1 -1
- package/dist/components/provider-grid.d.ts +2 -2
- package/dist/components/provider-grid.d.ts.map +1 -1
- package/dist/components/provider-grid.js +4 -4
- package/dist/components/provider-grid.js.map +1 -1
- package/dist/components/redirect-install-page.d.ts +1 -1
- package/dist/components/redirect-install-page.js +2 -2
- package/dist/components/redirect-install-page.js.map +1 -1
- package/dist/components/webhook/webhook-modals.stories.js +2 -2
- package/dist/components/webhook/webhook-modals.stories.js.map +1 -1
- package/dist/feature.d.ts +7 -7
- package/dist/feature.js +7 -7
- package/dist/feature.js.map +1 -1
- package/dist/hooks/api/integrations.js +1 -1
- package/dist/hooks/api/integrations.js.map +1 -1
- package/dist/pages/gitea-install-page.js +4 -4
- 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 +58 -12
- package/dist/pages/linear-callback-page.js.map +1 -1
- package/dist/pages/sentry-callback-page.d.ts.map +1 -1
- package/dist/pages/sentry-callback-page.js +22 -9
- package/dist/pages/sentry-callback-page.js.map +1 -1
- package/dist/pages/slack-callback-page.d.ts.map +1 -1
- package/dist/pages/slack-callback-page.js +50 -18
- package/dist/pages/slack-callback-page.js.map +1 -1
- package/dist/pages/source-control-onboarding-page.js +1 -1
- package/dist/pages/source-control-onboarding-page.js.map +1 -1
- package/dist/provider-catalog.d.ts +1 -1
- package/dist/provider-catalog.d.ts.map +1 -1
- package/dist/provider-catalog.js +5 -5
- package/dist/provider-catalog.js.map +1 -1
- package/dist/sentry-callback.js +2 -2
- package/dist/sentry-callback.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/dist/workspace-navigation.d.ts +13 -0
- package/dist/workspace-navigation.d.ts.map +1 -0
- package/dist/workspace-navigation.js +17 -0
- package/dist/workspace-navigation.js.map +1 -0
- package/package.json +13 -13
- package/src/components/callback-status-shell.tsx +14 -10
- package/src/components/installed-integrations-section.tsx +16 -9
- package/src/components/integration-gallery-for-workspace.tsx +20 -10
- package/src/components/integration-gallery.stories.tsx +10 -10
- package/src/components/integration-gallery.test.tsx +30 -15
- package/src/components/provider-grid.tsx +6 -6
- package/src/components/redirect-install-page.test.tsx +3 -3
- package/src/components/redirect-install-page.tsx +2 -2
- package/src/components/webhook/webhook-modals.stories.tsx +2 -2
- package/src/feature.ts +7 -7
- package/src/hooks/api/integrations.ts +1 -1
- package/src/pages/gitea-install-page.tsx +2 -2
- package/src/pages/linear-callback-page.test.tsx +25 -12
- package/src/pages/linear-callback-page.tsx +73 -12
- package/src/pages/sentry-callback-page.test.tsx +8 -5
- package/src/pages/sentry-callback-page.tsx +32 -8
- package/src/pages/slack-callback-page.test.tsx +78 -0
- package/src/pages/slack-callback-page.tsx +56 -18
- package/src/pages/source-control-onboarding-page.test.tsx +4 -4
- package/src/pages/source-control-onboarding-page.tsx +1 -1
- package/src/provider-catalog.test.ts +4 -4
- package/src/provider-catalog.ts +10 -10
- package/src/sentry-callback.ts +2 -2
- package/src/workspace-navigation.test.ts +43 -0
- package/src/workspace-navigation.ts +38 -0
- package/test/render.tsx +2 -1
- package/tsconfig.build.tsbuildinfo +1 -1
|
@@ -4,20 +4,20 @@ describe('PROVIDER_CATALOG', () => {
|
|
|
4
4
|
test('contains route entries for github, sentry, and gitea', () => {
|
|
5
5
|
expect(PROVIDER_CATALOG.github).toMatchObject({
|
|
6
6
|
kind: 'redirect-install',
|
|
7
|
-
setupPath: '/
|
|
7
|
+
setupPath: '/w/$workspaceSlug/integrations/github',
|
|
8
8
|
});
|
|
9
9
|
expect(PROVIDER_CATALOG.sentry).toMatchObject({
|
|
10
10
|
kind: 'redirect-install',
|
|
11
|
-
setupPath: '/
|
|
11
|
+
setupPath: '/w/$workspaceSlug/integrations/sentry',
|
|
12
12
|
});
|
|
13
13
|
expect(PROVIDER_CATALOG.linear).toMatchObject({
|
|
14
14
|
kind: 'redirect-install',
|
|
15
15
|
iconName: 'linear',
|
|
16
|
-
setupPath: '/
|
|
16
|
+
setupPath: '/w/$workspaceSlug/integrations/linear',
|
|
17
17
|
});
|
|
18
18
|
expect(PROVIDER_CATALOG.gitea).toMatchObject({
|
|
19
19
|
kind: 'direct-connect',
|
|
20
|
-
setupPath: '/
|
|
20
|
+
setupPath: '/w/$workspaceSlug/integrations/gitea',
|
|
21
21
|
});
|
|
22
22
|
});
|
|
23
23
|
|
package/src/provider-catalog.ts
CHANGED
|
@@ -4,11 +4,11 @@ import type {IconName} from '@shipfox/react-ui/icon';
|
|
|
4
4
|
// Literal union (not string) so `<Link to={catalog.setupPath}>` stays typed
|
|
5
5
|
// against TanStack Router's route tree.
|
|
6
6
|
export type ProviderSetupPath =
|
|
7
|
-
| '/
|
|
8
|
-
| '/
|
|
9
|
-
| '/
|
|
10
|
-
| '/
|
|
11
|
-
| '/
|
|
7
|
+
| '/w/$workspaceSlug/integrations/github'
|
|
8
|
+
| '/w/$workspaceSlug/integrations/gitea'
|
|
9
|
+
| '/w/$workspaceSlug/integrations/sentry'
|
|
10
|
+
| '/w/$workspaceSlug/integrations/linear'
|
|
11
|
+
| '/w/$workspaceSlug/integrations/slack';
|
|
12
12
|
|
|
13
13
|
interface RouteProviderCatalogEntry {
|
|
14
14
|
kind: 'redirect-install' | 'direct-connect';
|
|
@@ -27,27 +27,27 @@ export const PROVIDER_CATALOG: Record<string, ProviderCatalogEntry> = {
|
|
|
27
27
|
github: {
|
|
28
28
|
kind: 'redirect-install',
|
|
29
29
|
iconName: PROVIDER_ICONS.github,
|
|
30
|
-
setupPath: '/
|
|
30
|
+
setupPath: '/w/$workspaceSlug/integrations/github',
|
|
31
31
|
},
|
|
32
32
|
sentry: {
|
|
33
33
|
kind: 'redirect-install',
|
|
34
34
|
iconName: PROVIDER_ICONS.sentry,
|
|
35
|
-
setupPath: '/
|
|
35
|
+
setupPath: '/w/$workspaceSlug/integrations/sentry',
|
|
36
36
|
},
|
|
37
37
|
linear: {
|
|
38
38
|
kind: 'redirect-install',
|
|
39
39
|
iconName: PROVIDER_ICONS.linear,
|
|
40
|
-
setupPath: '/
|
|
40
|
+
setupPath: '/w/$workspaceSlug/integrations/linear',
|
|
41
41
|
},
|
|
42
42
|
slack: {
|
|
43
43
|
kind: 'redirect-install',
|
|
44
44
|
iconName: PROVIDER_ICONS.slack,
|
|
45
|
-
setupPath: '/
|
|
45
|
+
setupPath: '/w/$workspaceSlug/integrations/slack',
|
|
46
46
|
},
|
|
47
47
|
gitea: {
|
|
48
48
|
kind: 'direct-connect',
|
|
49
49
|
iconName: PROVIDER_ICONS.gitea,
|
|
50
|
-
setupPath: '/
|
|
50
|
+
setupPath: '/w/$workspaceSlug/integrations/gitea',
|
|
51
51
|
},
|
|
52
52
|
webhook: {
|
|
53
53
|
kind: 'modal-connect',
|
package/src/sentry-callback.ts
CHANGED
|
@@ -20,7 +20,7 @@ const sentryInstallWorkspaceStorageKey = {
|
|
|
20
20
|
} satisfies BrowserStorageKey<string>;
|
|
21
21
|
|
|
22
22
|
// Storage helpers swallow failures (quota, private mode, disabled storage):
|
|
23
|
-
// the handoff is an optimization, never a requirement
|
|
23
|
+
// the handoff is an optimization, never a requirement: the callback always
|
|
24
24
|
// asks for explicit confirmation and only uses the stored id to pre-select.
|
|
25
25
|
export function saveSentryInstallWorkspace(storage: WorkspaceStorage, workspaceId: string): void {
|
|
26
26
|
installWorkspaceStorage(storage).write(workspaceId);
|
|
@@ -67,7 +67,7 @@ export type SentryWorkspacePreselection =
|
|
|
67
67
|
| {kind: 'none'};
|
|
68
68
|
|
|
69
69
|
// Sentry's redirect carries no state token, so the callback can never prove it
|
|
70
|
-
// belongs to an install the user started
|
|
70
|
+
// belongs to an install the user started. Installation always requires an
|
|
71
71
|
// explicit click. The stored id (and a sole workspace) only pre-select.
|
|
72
72
|
export function preselectSentryWorkspace(
|
|
73
73
|
storedId: string | undefined,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import {configureApiClient} from '@shipfox/client-api';
|
|
2
|
+
import {userWorkspacesQueryKey} from '@shipfox/client-shell/runtime';
|
|
3
|
+
import {QueryClient} from '@tanstack/react-query';
|
|
4
|
+
import {resolveWorkspaceSlug} from './workspace-navigation.js';
|
|
5
|
+
|
|
6
|
+
function jsonResponse(body: unknown, init: ResponseInit = {}): Response {
|
|
7
|
+
return new Response(JSON.stringify(body), {
|
|
8
|
+
headers: {'content-type': 'application/json'},
|
|
9
|
+
status: 200,
|
|
10
|
+
...init,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
describe('resolveWorkspaceSlug', () => {
|
|
15
|
+
beforeEach(() => {
|
|
16
|
+
configureApiClient({baseUrl: 'https://api.example.test', fetchImpl: undefined});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('uses refreshed cached memberships when the follow-up list request fails', async () => {
|
|
20
|
+
configureApiClient({
|
|
21
|
+
fetchImpl: vi
|
|
22
|
+
.fn()
|
|
23
|
+
.mockResolvedValue(
|
|
24
|
+
jsonResponse(
|
|
25
|
+
{code: 'server-error', message: 'Workspace list unavailable'},
|
|
26
|
+
{status: 500},
|
|
27
|
+
),
|
|
28
|
+
),
|
|
29
|
+
});
|
|
30
|
+
const queryClient = new QueryClient();
|
|
31
|
+
queryClient.setQueryData(userWorkspacesQueryKey, {
|
|
32
|
+
memberships: [{id: 'response-workspace', slug: 'response-workspace'}],
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
await expect(
|
|
36
|
+
resolveWorkspaceSlug({
|
|
37
|
+
workspaceId: 'response-workspace',
|
|
38
|
+
fallbackWorkspaces: [{id: 'response-workspace', slug: 'old-slug'}],
|
|
39
|
+
queryClient,
|
|
40
|
+
}),
|
|
41
|
+
).resolves.toBe('response-workspace');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import {listUserWorkspaces, userWorkspacesQueryKey} from '@shipfox/client-shell/runtime';
|
|
2
|
+
import type {QueryClient} from '@tanstack/react-query';
|
|
3
|
+
|
|
4
|
+
interface WorkspaceSlugCandidate {
|
|
5
|
+
id: string;
|
|
6
|
+
slug: string;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface UserWorkspacesQueryData {
|
|
10
|
+
memberships: WorkspaceSlugCandidate[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function rememberCallbackKey(keys: Set<string>, key: string, maxSize = 32): void {
|
|
14
|
+
keys.delete(key);
|
|
15
|
+
keys.add(key);
|
|
16
|
+
if (keys.size <= maxSize) return;
|
|
17
|
+
const oldest = keys.values().next().value;
|
|
18
|
+
if (typeof oldest === 'string') keys.delete(oldest);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function resolveWorkspaceSlug({
|
|
22
|
+
workspaceId,
|
|
23
|
+
fallbackWorkspaces,
|
|
24
|
+
queryClient,
|
|
25
|
+
}: {
|
|
26
|
+
workspaceId: string;
|
|
27
|
+
fallbackWorkspaces: readonly WorkspaceSlugCandidate[];
|
|
28
|
+
queryClient: QueryClient;
|
|
29
|
+
}): Promise<string | undefined> {
|
|
30
|
+
const currentWorkspaces = await listUserWorkspaces()
|
|
31
|
+
.then(({memberships}) => memberships)
|
|
32
|
+
.catch(() => {
|
|
33
|
+
const refreshedWorkspaces =
|
|
34
|
+
queryClient.getQueryData<UserWorkspacesQueryData>(userWorkspacesQueryKey);
|
|
35
|
+
return refreshedWorkspaces?.memberships ?? fallbackWorkspaces;
|
|
36
|
+
});
|
|
37
|
+
return currentWorkspaces.find(({id}) => id === workspaceId)?.slug;
|
|
38
|
+
}
|
package/test/render.tsx
CHANGED
|
@@ -19,11 +19,12 @@ export const INTEGRATIONS_TEST_WID = '11111111-1111-4111-8111-111111111111';
|
|
|
19
19
|
export interface TestWorkspace {
|
|
20
20
|
id: string;
|
|
21
21
|
name: string;
|
|
22
|
+
slug: string;
|
|
22
23
|
membershipId: string;
|
|
23
24
|
}
|
|
24
25
|
|
|
25
26
|
export function testWorkspace(overrides: Partial<TestWorkspace> = {}): TestWorkspace {
|
|
26
|
-
return {id: INTEGRATIONS_TEST_WID, name: 'Acme', membershipId: 'm-1', ...overrides};
|
|
27
|
+
return {id: INTEGRATIONS_TEST_WID, name: 'Acme', slug: 'acme', membershipId: 'm-1', ...overrides};
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export function jsonResponse(body: unknown, init: ResponseInit = {}) {
|