Skip to content

Commit 5cbda1a

Browse files
authored
refactor: migrate organizations apis (Kong#9615)
* refactor: migrate organizations apis * fix: ts check * fix: comment
1 parent 1a9ca96 commit 5cbda1a

33 files changed

+331
-241
lines changed

packages/insomnia-api/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@ export * from './trial';
55
export * from './project';
66
export * from './collaborators';
77
export * from './invite';
8+
export * from './organizations';
89

910
export { configureFetch, type FetchConfig, ResponseFailError, isApiError } from './fetch';
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
import { fetch } from './fetch';
2+
3+
interface Branding {
4+
logo_url: string;
5+
}
6+
7+
export interface Metadata {
8+
organizationType: string;
9+
ownerAccountId: string;
10+
}
11+
12+
export interface Organization {
13+
id: string;
14+
name: string;
15+
display_name: string;
16+
branding?: Branding;
17+
metadata: Metadata;
18+
}
19+
20+
export interface OrganizationsResponse {
21+
start: number;
22+
limit: number;
23+
length: number;
24+
total: number;
25+
next: string;
26+
organizations: Organization[];
27+
}
28+
29+
export const getOrganizations = ({ sessionId }: { sessionId: string }) => {
30+
return fetch<OrganizationsResponse>({
31+
method: 'GET',
32+
path: '/v1/organizations',
33+
sessionId,
34+
});
35+
};
36+
37+
export const needsToUpgrade = 'NEEDS_TO_UPGRADE';
38+
export const needsToIncreaseSeats = 'NEEDS_TO_INCREASE_SEATS';
39+
40+
export interface CheckSeatsResponse {
41+
isAllowed: boolean;
42+
code?: typeof needsToUpgrade | typeof needsToIncreaseSeats;
43+
}
44+
45+
export const checkSeats = ({
46+
organizationId,
47+
sessionId,
48+
emails,
49+
}: {
50+
organizationId: string;
51+
sessionId: string;
52+
emails: string[];
53+
}) => {
54+
return fetch<CheckSeatsResponse>({
55+
method: 'POST',
56+
path: `/v1/organizations/${organizationId}/check-seats`,
57+
data: { emails },
58+
sessionId,
59+
});
60+
};
61+
62+
export interface Role {
63+
id: string;
64+
name: string;
65+
description?: string;
66+
}
67+
68+
export const getOrganizationRoles = ({ sessionId }: { sessionId: string }) => {
69+
return fetch<Role[]>({
70+
method: 'GET',
71+
path: `/v1/organizations/roles`,
72+
sessionId,
73+
});
74+
};
75+
76+
export interface FeatureStatus {
77+
enabled: boolean;
78+
reason?: string;
79+
}
80+
81+
export interface FeatureList {
82+
bulkImport: FeatureStatus;
83+
gitSync: FeatureStatus;
84+
orgBasicRbac: FeatureStatus;
85+
aiMockServers: FeatureStatus;
86+
aiCommitMessages: FeatureStatus;
87+
aiMcpClient: FeatureStatus;
88+
}
89+
90+
export interface Billing {
91+
// If true, the user has paid for the current period
92+
isActive: boolean;
93+
expirationWarningMessage: string;
94+
expirationErrorMessage: string;
95+
accessDenied: boolean;
96+
}
97+
98+
export const getOrganizationFeatures = ({
99+
organizationId,
100+
sessionId,
101+
}: {
102+
organizationId: string;
103+
sessionId: string;
104+
}) => {
105+
return fetch<{ features: FeatureList; billing: Billing }>({
106+
method: 'GET',
107+
path: `/v1/organizations/${organizationId}/features`,
108+
sessionId,
109+
});
110+
};
111+
112+
export interface StorageRules {
113+
enableCloudSync: boolean;
114+
enableLocalVault: boolean;
115+
enableGitSync: boolean;
116+
isOverridden: boolean;
117+
}
118+
119+
export const getOrganizationStorageRule = ({
120+
organizationId,
121+
sessionId,
122+
}: {
123+
organizationId: string;
124+
sessionId: string;
125+
}) => {
126+
return fetch<StorageRules>({
127+
method: 'GET',
128+
path: `/v1/organizations/${organizationId}/storage-rule`,
129+
sessionId,
130+
});
131+
};
132+
133+
export type Permission =
134+
| 'own:organization'
135+
| 'read:organization'
136+
| 'delete:organization'
137+
| 'update:organization'
138+
| 'read:membership'
139+
| 'delete:membership'
140+
| 'update:membership'
141+
| 'read:invitation'
142+
| 'create:invitation'
143+
| 'delete:invitation'
144+
| 'create:enterprise_connection'
145+
| 'read:enterprise_connection'
146+
| 'delete:enterprise_connection'
147+
| 'update:enterprise_connection'
148+
| 'leave:organization';
149+
150+
export const getOrgUserPermissions = ({ organizationId, sessionId }: { organizationId: string; sessionId: string }) => {
151+
return fetch<Record<Permission, boolean>>({
152+
method: 'GET',
153+
path: `/v1/organizations/${organizationId}/user-permissions`,
154+
sessionId,
155+
});
156+
};
157+
158+
export const deleteOrganizationMember = ({
159+
organizationId,
160+
userId,
161+
sessionId,
162+
}: {
163+
organizationId: string;
164+
userId: string;
165+
sessionId: string;
166+
}) => {
167+
return fetch({
168+
method: 'DELETE',
169+
path: `/v1/organizations/${organizationId}/members/${userId}`,
170+
sessionId,
171+
});
172+
};
173+
174+
export const updateUserRoles = ({
175+
organizationId,
176+
userId,
177+
roleId,
178+
sessionId,
179+
}: {
180+
organizationId: string;
181+
userId: string;
182+
roleId: string;
183+
sessionId: string;
184+
}) => {
185+
return fetch({
186+
method: 'PATCH',
187+
path: `/v1/organizations/${organizationId}/members/${userId}/roles`,
188+
data: {
189+
roles: [roleId],
190+
},
191+
sessionId,
192+
});
193+
};
194+
195+
export const getOrganizationMemberRoles = ({
196+
organizationId,
197+
userId,
198+
sessionId,
199+
}: {
200+
organizationId: string;
201+
userId: string;
202+
sessionId: string;
203+
}) => {
204+
return fetch<Role>({
205+
method: 'GET',
206+
path: `/v1/organizations/${organizationId}/members/${userId}/roles`,
207+
sessionId,
208+
});
209+
};

packages/insomnia/src/models/organization.ts

Lines changed: 1 addition & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,4 @@
1-
import { type PersonalPlanType } from 'insomnia-api';
2-
3-
interface Branding {
4-
logo_url: string;
5-
}
6-
7-
export interface Metadata {
8-
organizationType: string;
9-
ownerAccountId: string;
10-
}
11-
12-
export interface Organization {
13-
id: string;
14-
name: string;
15-
display_name: string;
16-
branding?: Branding;
17-
metadata: Metadata;
18-
}
19-
20-
export interface StorageRules {
21-
enableCloudSync: boolean;
22-
enableLocalVault: boolean;
23-
enableGitSync: boolean;
24-
isOverridden: boolean;
25-
}
1+
import { type Organization, type PersonalPlanType } from 'insomnia-api';
262

273
export const SCRATCHPAD_ORGANIZATION_ID = 'org_scratchpad';
284
export const isScratchpadOrganizationId = (organizationId: string) => organizationId === SCRATCHPAD_ORGANIZATION_ID;
@@ -40,14 +16,6 @@ export const findPersonalOrganization = (organizations: Organization[], accountI
4016
}),
4117
);
4218
};
43-
export interface OrganizationsResponse {
44-
start: number;
45-
limit: number;
46-
length: number;
47-
total: number;
48-
next: string;
49-
organizations: Organization[];
50-
}
5119

5220
export const formatCurrentPlanType = (type: PersonalPlanType) => {
5321
switch (type) {

packages/insomnia/src/models/project.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { StorageRules } from '~/models/organization';
1+
import type { StorageRules } from 'insomnia-api';
22

33
import { database as db } from '../common/database';
44
import { generateId } from '../common/misc';

packages/insomnia/src/routes/commands.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import type { Organization } from 'insomnia-api';
2+
13
import { database } from '~/common/database';
24
import { fuzzyMatch } from '~/common/misc';
35
import {
@@ -12,7 +14,7 @@ import {
1214
} from '~/models';
1315
import type { Environment } from '~/models/environment';
1416
import type { GrpcRequest } from '~/models/grpc-request';
15-
import { isScratchpadOrganizationId, type Organization } from '~/models/organization';
17+
import { isScratchpadOrganizationId } from '~/models/organization';
1618
import { isRemoteProject, type Project } from '~/models/project';
1719
import type { Request } from '~/models/request';
1820
import type { RequestGroup } from '~/models/request-group';

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

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,12 @@
1+
import { checkSeats } from 'insomnia-api';
12
import { href } from 'react-router';
23
import { v4 as uuidv4 } from 'uuid';
34

45
import { userSession } from '~/models';
5-
import { insomniaFetch } from '~/ui/insomnia-fetch';
66
import { createFetcherLoadHook } from '~/utils/router';
77

88
import type { Route } from './+types/organization.$organizationId.collaborators-check-seats';
99

10-
export const needsToUpgrade = 'NEEDS_TO_UPGRADE';
11-
export const needsToIncreaseSeats = 'NEEDS_TO_INCREASE_SEATS';
12-
13-
export interface CheckSeatsResponse {
14-
isAllowed: boolean;
15-
code?: typeof needsToUpgrade | typeof needsToIncreaseSeats;
16-
}
17-
1810
export async function clientLoader({ params }: Route.ClientLoaderArgs) {
1911
const { id: sessionId } = await userSession.get();
2012

@@ -23,12 +15,10 @@ export async function clientLoader({ params }: Route.ClientLoaderArgs) {
2315
try {
2416
// Check whether the user can add a new collaborator
2517
// Use a random email to avoid hitting any existing member emails
26-
const checkResponseData = await insomniaFetch<CheckSeatsResponse>({
27-
method: 'POST',
28-
path: `/v1/organizations/${organizationId}/check-seats`,
29-
data: { emails: [`insomnia-mock-check-seats-${uuidv4()}@example.net`] },
18+
const checkResponseData = await checkSeats({
19+
organizationId,
3020
sessionId,
31-
onlyResolveOnSuccess: true,
21+
emails: [`insomnia-mock-check-seats-${uuidv4()}@example.net`],
3222
});
3323
return checkResponseData;
3424
} catch {

packages/insomnia/src/routes/organization.$organizationId.members.$userId.roles.tsx

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

34
import * as models from '~/models';
4-
import { insomniaFetch } from '~/ui/insomnia-fetch';
55
import { invariant } from '~/utils/invariant';
66
import { createFetcherSubmitHook } from '~/utils/router';
77

@@ -18,11 +18,10 @@ export async function clientAction({ request, params }: Route.ClientActionArgs)
1818
try {
1919
const user = await models.userSession.getOrCreate();
2020
const sessionId = user.id;
21-
22-
const response = await insomniaFetch<{ enabled: boolean }>({
23-
method: 'PATCH',
24-
path: `/v1/organizations/${organizationId}/members/${userId}/roles`,
25-
data: { roles: [roleId] },
21+
const response = await updateUserRoles({
22+
organizationId,
23+
userId,
24+
roleId,
2625
sessionId,
2726
});
2827

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

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1+
import { type Billing, type FeatureList, getOrganizationFeatures, type Organization } from 'insomnia-api';
12
import { href, redirect, type ShouldRevalidateFunctionArgs } from 'react-router';
23

34
import { userSession } from '~/models';
4-
import { isScratchpadOrganizationId, type Organization } from '~/models/organization';
5-
import { insomniaFetch } from '~/ui/insomnia-fetch';
5+
import { isScratchpadOrganizationId } from '~/models/organization';
66
import { createFetcherLoadHook } from '~/utils/router';
77

88
import type { Route } from './+types/organization.$organizationId.permissions';
9-
import type { Billing, FeatureList } from './organization';
109

1110
export const fallbackFeatures = Object.freeze<FeatureList>({
1211
bulkImport: { enabled: false, reason: 'Insomnia API unreachable' },
@@ -44,11 +43,7 @@ export async function clientLoader({ params }: Route.ClientLoaderArgs) {
4443
}
4544

4645
try {
47-
const featuresResponse = insomniaFetch<{ features: FeatureList; billing: Billing } | undefined>({
48-
method: 'GET',
49-
path: `/v1/organizations/${organizationId}/features`,
50-
sessionId,
51-
});
46+
const featuresResponse = getOrganizationFeatures({ organizationId, sessionId });
5247

5348
return {
5449
featuresPromise: featuresResponse.then(res => res?.features || fallbackFeatures),

packages/insomnia/src/routes/organization.$organizationId.project.$projectId._index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { IconName, IconProp } from '@fortawesome/fontawesome-svg-core';
2+
import type { Organization } from 'insomnia-api';
23
import { Fragment, useEffect, useMemo, useState } from 'react';
34
import {
45
Button,
@@ -42,7 +43,6 @@ import type { ApiSpec } from '~/models/api-spec';
4243
import type { GitRepository } from '~/models/git-repository';
4344
import { sortProjects } from '~/models/helpers/project';
4445
import type { MockServer } from '~/models/mock-server';
45-
import type { Organization } from '~/models/organization';
4646
import { isOwnerOfOrganization, isPersonalOrganization, isScratchpadOrganizationId } from '~/models/organization';
4747
import {
4848
getProjectStorageTypeLabel,

0 commit comments

Comments
 (0)