Skip to content

Commit 405b3c0

Browse files
authored
refactor: migrate collaborator api (Kong#9605)
* refactor: migrate collaborator api * fix: search collaborators query params undefined * fix: comment
1 parent 9e29c40 commit 405b3c0

File tree

7 files changed

+231
-114
lines changed

7 files changed

+231
-114
lines changed
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import { fetch } from './fetch';
2+
3+
type CollaboratorType = 'invite' | 'member' | 'group';
4+
interface CollaboratorMetadata {
5+
groupId?: string;
6+
invitationId?: string;
7+
roleId?: string;
8+
email?: string;
9+
userId?: string;
10+
expiresAt?: string;
11+
groupTotal?: number;
12+
}
13+
14+
export interface Collaborator {
15+
id: string;
16+
picture: string;
17+
type: CollaboratorType;
18+
name: string;
19+
createdAt?: string;
20+
metadata: CollaboratorMetadata;
21+
}
22+
23+
interface PaginatedList {
24+
start: number;
25+
limit: number;
26+
length: number;
27+
total: number;
28+
next: string;
29+
}
30+
31+
type CollaboratorsListResult = PaginatedList & {
32+
collaborators: Collaborator[];
33+
};
34+
35+
export const getCollaborators = ({
36+
sessionId,
37+
organizationId,
38+
pageLimit = 25,
39+
page,
40+
filter,
41+
}: {
42+
sessionId: string;
43+
organizationId: string;
44+
filter?: string;
45+
pageLimit?: number;
46+
page?: number;
47+
}) => {
48+
const params = new URLSearchParams();
49+
params.set('per_page', String(pageLimit));
50+
if (page !== undefined) {
51+
params.set('page', String(page));
52+
}
53+
if (filter !== undefined) {
54+
params.set('filter', filter);
55+
}
56+
return fetch<CollaboratorsListResult>({
57+
method: 'GET',
58+
path: `/v1/desktop/organizations/${organizationId}/collaborators?${params.toString()}`,
59+
sessionId,
60+
});
61+
};
62+
63+
interface CollaboratorSearchResultItem {
64+
id: string;
65+
picture: string;
66+
type: CollaboratorType;
67+
name: string;
68+
}
69+
70+
export const searchCollaborators = ({
71+
sessionId,
72+
organizationId,
73+
keyword,
74+
}: {
75+
sessionId: string;
76+
organizationId: string;
77+
keyword: string;
78+
}) => {
79+
return fetch<CollaboratorSearchResultItem[]>({
80+
method: 'GET',
81+
path: `/v1/desktop/organizations/${organizationId}/collaborators/search/${keyword}`,
82+
sessionId,
83+
});
84+
};
85+
86+
interface CollaboratorInstructionItem {
87+
accountId: string;
88+
publicKey: string; // stringified JSON WEB KEY
89+
autoLinked: boolean;
90+
}
91+
92+
type CollaboratorInstruction = Record<string, CollaboratorInstructionItem>;
93+
94+
export const startAddingCollaborators = ({
95+
sessionId,
96+
organizationId,
97+
emails,
98+
teamIds,
99+
}: {
100+
sessionId: string;
101+
organizationId: string;
102+
emails: string[];
103+
teamIds: string[];
104+
}) => {
105+
return fetch<CollaboratorInstruction>({
106+
method: 'POST',
107+
path: `/v1/desktop/organizations/${organizationId}/collaborators/start-adding`,
108+
data: { teamIds, emails },
109+
sessionId,
110+
});
111+
};
112+
113+
interface CollaboratorInviteKey {
114+
accountId: string;
115+
projectId: string;
116+
encKey: string;
117+
}
118+
119+
export const finishAddingCollaborators = ({
120+
sessionId,
121+
organizationId,
122+
teamIds,
123+
keys,
124+
accountIds,
125+
roleId,
126+
}: {
127+
sessionId: string;
128+
organizationId: string;
129+
teamIds: string[];
130+
keys: Record<string, Record<string, CollaboratorInviteKey>>;
131+
accountIds: string[];
132+
roleId?: string;
133+
}) => {
134+
return fetch({
135+
method: 'POST',
136+
path: `/v1/desktop/organizations/${organizationId}/collaborators/finish-adding`,
137+
data: { teamIds, keys, accountIds, roleId },
138+
sessionId,
139+
});
140+
};
141+
142+
export const unlinkCollaborator = ({
143+
sessionId,
144+
organizationId,
145+
collaboratorId,
146+
}: {
147+
sessionId: string;
148+
organizationId: string;
149+
collaboratorId: string;
150+
}) => {
151+
return fetch({
152+
method: 'DELETE',
153+
path: `/v1/desktop/organizations/${organizationId}/collaborators/${collaboratorId}/unlink`,
154+
sessionId,
155+
});
156+
};
157+
158+
export interface UserPresence {
159+
acct: string;
160+
avatar: string;
161+
branch: string;
162+
file: string;
163+
firstName: string;
164+
lastName: string;
165+
project: string;
166+
team: string;
167+
}
168+
169+
export const getRealTimeCollaborators = ({
170+
sessionId,
171+
// this is sanitized organization id
172+
organizationId,
173+
projectRemoteId,
174+
workspaceId,
175+
}: {
176+
sessionId: string;
177+
organizationId: string;
178+
projectRemoteId: string;
179+
workspaceId: string;
180+
}) => {
181+
return fetch<{
182+
data?: UserPresence[];
183+
}>({
184+
path: `/v1/organizations/${organizationId}/collaborators`,
185+
method: 'POST',
186+
sessionId,
187+
data: {
188+
project: projectRemoteId,
189+
file: workspaceId,
190+
},
191+
});
192+
};

packages/insomnia-api/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ export * from './vault';
33
export * from './enterprise';
44
export * from './trial';
55
export * from './project';
6+
export * from './collaborators';
67
export * from './invite';
78

89
export { configureFetch, type FetchConfig, ResponseFailError, isApiError } from './fetch';

packages/insomnia/src/routes/organization.$organizationId.collaborators-search.tsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,11 @@
1+
import { searchCollaborators } from 'insomnia-api';
12
import { href } from 'react-router';
23

34
import { userSession } from '~/models';
4-
import { insomniaFetch } from '~/ui/insomnia-fetch';
55
import { createFetcherLoadHook } from '~/utils/router';
66

77
import type { Route } from './+types/organization.$organizationId.collaborators-search';
88

9-
type CollaboratorType = 'invite' | 'member' | 'group';
10-
11-
interface CollaboratorSearchResultItem {
12-
id: string;
13-
picture: string;
14-
type: CollaboratorType;
15-
name: string;
16-
}
17-
189
export async function clientLoader({ params, request }: Route.ClientLoaderArgs) {
1910
const { id: sessionId } = await userSession.get();
2011

@@ -24,10 +15,10 @@ export async function clientLoader({ params, request }: Route.ClientLoaderArgs)
2415
const requestUrl = new URL(request.url);
2516
const searchParams = Object.fromEntries(requestUrl.searchParams.entries());
2617

27-
const collaboratorsSearchList = await insomniaFetch<CollaboratorSearchResultItem[]>({
28-
method: 'GET',
29-
path: `/v1/desktop/organizations/${organizationId}/collaborators/search/${searchParams.query}`,
18+
const collaboratorsSearchList = await searchCollaborators({
3019
sessionId,
20+
organizationId,
21+
keyword: searchParams.query || '',
3122
});
3223

3324
return collaboratorsSearchList;
@@ -40,7 +31,7 @@ export const useCollaboratorsSearchLoaderFetcher = createFetcherLoadHook(
4031
load =>
4132
({ organizationId, query }: { organizationId: string; query?: string }) => {
4233
return load(
43-
`${href(`/organization/:organizationId/collaborators-search`, { organizationId })}?${encodeURIComponent(query || '')}`,
34+
`${href(`/organization/:organizationId/collaborators-search`, { organizationId })}?query=${encodeURIComponent(query || '')}`,
4435
);
4536
},
4637
clientLoader,

packages/insomnia/src/routes/organization.$organizationId.collaborators.tsx

Lines changed: 6 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,11 @@
1+
import { getCollaborators } from 'insomnia-api';
12
import { href } from 'react-router';
23

34
import { userSession } from '~/models';
4-
import { insomniaFetch } from '~/ui/insomnia-fetch';
55
import { createFetcherLoadHook } from '~/utils/router';
66

77
import type { Route } from './+types/organization.$organizationId.collaborators';
88

9-
interface PaginatedList {
10-
start: number;
11-
limit: number;
12-
length: number;
13-
total: number;
14-
next: string;
15-
}
16-
17-
export type CollaboratorType = 'invite' | 'member' | 'group';
18-
19-
interface CollaboratorMetadata {
20-
groupId?: string;
21-
invitationId?: string;
22-
roleId?: string;
23-
email?: string;
24-
userId?: string;
25-
expiresAt?: string;
26-
groupTotal?: number;
27-
}
28-
29-
export interface Collaborator {
30-
id: string;
31-
picture: string;
32-
type: CollaboratorType;
33-
name: string;
34-
createdAt?: string;
35-
metadata: CollaboratorMetadata;
36-
}
37-
38-
type CollaboratorsListResult =
39-
| (PaginatedList & {
40-
collaborators: Collaborator[];
41-
})
42-
| Error;
43-
449
export async function clientLoader({ params, request }: Route.ClientLoaderArgs) {
4510
const { id: sessionId } = await userSession.get();
4611

@@ -50,22 +15,12 @@ export async function clientLoader({ params, request }: Route.ClientLoaderArgs)
5015
const requestUrl = new URL(request.url);
5116
const searchParams = Object.fromEntries(requestUrl.searchParams.entries());
5217

53-
// Construct the base path
54-
let path = `/v1/desktop/organizations/${organizationId}/collaborators?per_page=${searchParams.per_page || 25}`;
55-
56-
// Append query parameters conditionally
57-
if (searchParams.page) {
58-
path += `&page=${searchParams.page}`;
59-
}
60-
61-
if (searchParams.filter) {
62-
path += `&filter=${searchParams.filter}`;
63-
}
64-
65-
const collaboratorsList = await insomniaFetch<CollaboratorsListResult>({
66-
method: 'GET',
67-
path,
18+
const collaboratorsList = await getCollaborators({
6819
sessionId,
20+
organizationId,
21+
pageLimit: Number(searchParams.per_page) || 25,
22+
page: Number(searchParams.page) || 0,
23+
filter: searchParams.filter,
6924
});
7025

7126
return collaboratorsList;

packages/insomnia/src/ui/components/modals/invite-modal/encryption.ts

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { finishAddingCollaborators, startAddingCollaborators } from 'insomnia-api';
2+
13
import { decryptRSAWithJWK, encryptRSAWithJWK } from '../../../../account/crypt';
24
import { getCurrentSessionId, getPrivateKey } from '../../../../account/session';
35
import { invariant } from '../../../../utils/invariant';
@@ -124,26 +126,17 @@ interface MemberProjectKey {
124126
encSymmetricKey: string;
125127
}
126128

127-
interface CollaboratorInstructionItem {
128-
accountId: string;
129-
publicKey: string; // stringified JSON WEB KEY
130-
autoLinked: boolean;
131-
}
132-
133-
type CollaboratorInstruction = Record<string, CollaboratorInstructionItem>;
134-
135129
export async function startInvite({ emails, teamIds, organizationId, roleId }: StartInviteParams) {
136130
const sessionId = await getCurrentSessionId();
137131
invariant(sessionId, 'Session ID is required');
138132

139133
// we are merging these endpoints into one as it has grown onto several types over time.
140134
// this way, we can also offload the complex logic to the API
141-
const instruction = await insomniaFetch<CollaboratorInstruction>({
142-
method: 'POST',
143-
path: `/v1/desktop/organizations/${organizationId}/collaborators/start-adding`,
144-
data: { teamIds, emails },
135+
const instruction = await startAddingCollaborators({
145136
sessionId,
146-
onlyResolveOnSuccess: true,
137+
organizationId,
138+
emails,
139+
teamIds,
147140
});
148141

149142
const myKeysInfo = await insomniaFetch<ResponseGetMyProjectKeys>({
@@ -206,12 +199,12 @@ export async function startInvite({ emails, teamIds, organizationId, roleId }: S
206199
});
207200
}
208201
}
209-
210-
await insomniaFetch({
211-
method: 'POST',
212-
path: `/v1/desktop/organizations/${organizationId}/collaborators/finish-adding`,
213-
data: { teamIds, keys, accountIds, roleId },
202+
await finishAddingCollaborators({
214203
sessionId,
215-
onlyResolveOnSuccess: true,
204+
organizationId,
205+
teamIds,
206+
keys,
207+
accountIds,
208+
roleId,
216209
});
217210
}

0 commit comments

Comments
 (0)