Skip to content

Commit fc287da

Browse files
Feat: API Playground (twentyhq#10376)
/claim twentyhq#10283 --------- Co-authored-by: Félix Malfait <[email protected]> Co-authored-by: Félix Malfait <[email protected]>
1 parent d151876 commit fc287da

File tree

55 files changed

+2914
-162
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+2914
-162
lines changed

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,8 @@
360360
"graphql": "16.8.0",
361361
"type-fest": "4.10.1",
362362
"typescript": "5.3.3",
363-
"prosemirror-model": "1.23.0"
363+
"prosemirror-model": "1.23.0",
364+
"yjs": "13.6.18"
364365
},
365366
"version": "0.2.1",
366367
"nx": {},

packages/twenty-front/.storybook/main.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const config: StorybookConfig = {
3737
'@storybook/addon-docs',
3838
'@storybook/addon-essentials/docs',
3939
],
40-
}
40+
},
4141
},
4242
addons: [
4343
'@storybook/addon-links',

packages/twenty-front/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"@nivo/core": "^0.87.0",
3939
"@nivo/line": "^0.87.0",
4040
"@react-pdf/renderer": "^4.1.6",
41+
"@scalar/api-reference-react": "^0.4.36",
4142
"@tiptap/core": "^2.10.4",
4243
"@tiptap/extension-document": "^2.10.4",
4344
"@tiptap/extension-hard-break": "^2.10.4",

packages/twenty-front/src/modules/app/components/SettingsRoutes.tsx

+40-8
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,38 @@ import { SettingsPath } from '@/types/SettingsPath';
77
import { FeatureFlagKey } from '~/generated-metadata/graphql';
88
import { SettingsPermissions } from '~/generated/graphql';
99

10+
const SettingsApiKeys = lazy(() =>
11+
import('~/pages/settings/developers/api-keys/SettingsApiKeys').then(
12+
(module) => ({
13+
default: module.SettingsApiKeys,
14+
}),
15+
),
16+
);
17+
18+
const SettingsGraphQLPlayground = lazy(() =>
19+
import(
20+
'~/pages/settings/developers/playground/SettingsGraphQLPlayground'
21+
).then((module) => ({
22+
default: module.SettingsGraphQLPlayground,
23+
})),
24+
);
25+
26+
const SettingsRestPlayground = lazy(() =>
27+
import('~/pages/settings/developers/playground/SettingsRestPlayground').then(
28+
(module) => ({
29+
default: module.SettingsRestPlayground,
30+
}),
31+
),
32+
);
33+
34+
const SettingsWebhooks = lazy(() =>
35+
import(
36+
'~/pages/settings/developers/webhooks/components/SettingsWebhooks'
37+
).then((module) => ({
38+
default: module.SettingsWebhooks,
39+
})),
40+
);
41+
1042
const SettingsAccountsCalendars = lazy(() =>
1143
import('~/pages/settings/accounts/SettingsAccountsCalendars').then(
1244
(module) => ({
@@ -137,12 +169,6 @@ const SettingsBilling = lazy(() =>
137169
})),
138170
);
139171

140-
const SettingsDevelopers = lazy(() =>
141-
import('~/pages/settings/developers/SettingsDevelopers').then((module) => ({
142-
default: module.SettingsDevelopers,
143-
})),
144-
);
145-
146172
const SettingsIntegrations = lazy(() =>
147173
import('~/pages/settings/integrations/SettingsIntegrations').then(
148174
(module) => ({
@@ -376,9 +402,15 @@ export const SettingsRoutes = ({
376402
/>
377403
}
378404
>
405+
<Route path={SettingsPath.APIs} element={<SettingsApiKeys />} />
406+
<Route path={SettingsPath.Webhooks} element={<SettingsWebhooks />} />
407+
<Route
408+
path={`${SettingsPath.GraphQLPlayground}`}
409+
element={<SettingsGraphQLPlayground />}
410+
/>
379411
<Route
380-
path={SettingsPath.Developers}
381-
element={<SettingsDevelopers />}
412+
path={`${SettingsPath.RestPlayground}/*`}
413+
element={<SettingsRestPlayground />}
382414
/>
383415
<Route
384416
path={SettingsPath.DevelopersNewApiKey}

packages/twenty-front/src/modules/settings/components/SettingsNavigationDrawerItems.tsx

+5-3
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ export const SettingsNavigationDrawerItems = () => {
5757
getSelectedIndexForSubItems(subItems);
5858

5959
return (
60-
<NavigationDrawerItemGroup key={item.path}>
60+
<NavigationDrawerItemGroup
61+
key={item.path || `group-${index}`}
62+
>
6163
<SettingsNavigationDrawerItem
6264
item={item}
6365
subItemState={
@@ -72,7 +74,7 @@ export const SettingsNavigationDrawerItems = () => {
7274
/>
7375
{subItems.map((subItem, subIndex) => (
7476
<SettingsNavigationDrawerItem
75-
key={subItem.path}
77+
key={subItem.path || `subitem-${subIndex}`}
7678
item={subItem}
7779
subItemState={
7880
subItem.indentationLevel
@@ -90,7 +92,7 @@ export const SettingsNavigationDrawerItems = () => {
9092
}
9193
return (
9294
<SettingsNavigationDrawerItem
93-
key={item.path}
95+
key={item.path || `item-${index}`}
9496
item={item}
9597
subItemState={
9698
item.indentationLevel

packages/twenty-front/src/modules/settings/developers/components/SettingsApiKeysTable.tsx

+6-2
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@ import { SettingsApiKeysFieldItemTableRow } from '@/settings/developers/componen
44
import { ApiFieldItem } from '@/settings/developers/types/api-key/ApiFieldItem';
55
import { ApiKey } from '@/settings/developers/types/api-key/ApiKey';
66
import { formatExpirations } from '@/settings/developers/utils/formatExpiration';
7+
import { SettingsPath } from '@/types/SettingsPath';
78
import { Table } from '@/ui/layout/table/components/Table';
89
import { TableBody } from '@/ui/layout/table/components/TableBody';
910
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
1011
import { TableRow } from '@/ui/layout/table/components/TableRow';
1112
import styled from '@emotion/styled';
12-
import { MOBILE_VIEWPORT } from 'twenty-ui';
1313
import { Trans } from '@lingui/react/macro';
14+
import { MOBILE_VIEWPORT } from 'twenty-ui';
15+
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
1416

1517
const StyledTableBody = styled(TableBody)`
1618
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
@@ -53,7 +55,9 @@ export const SettingsApiKeysTable = () => {
5355
<SettingsApiKeysFieldItemTableRow
5456
key={fieldItem.id}
5557
fieldItem={fieldItem as ApiFieldItem}
56-
to={`/settings/developers/api-keys/${fieldItem.id}`}
58+
to={getSettingsPath(SettingsPath.DevelopersApiKeyDetail, {
59+
apiKeyId: fieldItem.id,
60+
})}
5761
/>
5862
))}
5963
</StyledTableBody>

packages/twenty-front/src/modules/settings/developers/components/SettingsReadDocumentationButton.tsx

-18
This file was deleted.

packages/twenty-front/src/modules/settings/developers/hooks/useWebhookUpdateForm.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ export const useWebhookUpdateForm = ({
145145

146146
const deleteWebhook = async () => {
147147
await deleteOneWebhook(webhookId);
148-
navigate(SettingsPath.Developers);
148+
navigate(SettingsPath.Webhooks);
149149
};
150150

151151
useFindOneRecord({

packages/twenty-front/src/modules/settings/hooks/useSettingsNavigationItems.tsx

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import {
2+
IconApi,
23
IconApps,
34
IconAt,
45
IconCalendarEvent,
5-
IconCode,
66
IconColorSwatch,
77
IconComponent,
88
IconCurrencyDollar,
@@ -18,6 +18,7 @@ import {
1818
IconSettings,
1919
IconUserCircle,
2020
IconUsers,
21+
IconWebhook,
2122
} from 'twenty-ui';
2223

2324
import { SettingsPath } from '@/types/SettingsPath';
@@ -159,9 +160,16 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
159160
isAdvanced: true,
160161
items: [
161162
{
162-
label: t`API & Webhooks`,
163-
path: SettingsPath.Developers,
164-
Icon: IconCode,
163+
label: t`APIs`,
164+
path: SettingsPath.APIs,
165+
Icon: IconApi,
166+
isAdvanced: true,
167+
isHidden: !permissionMap[SettingsPermissions.API_KEYS_AND_WEBHOOKS],
168+
},
169+
{
170+
label: t`Webhooks`,
171+
path: SettingsPath.Webhooks,
172+
Icon: IconWebhook,
165173
isAdvanced: true,
166174
isHidden: !permissionMap[SettingsPermissions.API_KEYS_AND_WEBHOOKS],
167175
},
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { playgroundApiKeyState } from '@/settings/playground/states/playgroundApiKeyState';
2+
import { PlaygroundSchemas } from '@/settings/playground/types/PlaygroundSchemas';
3+
import { explorerPlugin } from '@graphiql/plugin-explorer';
4+
import '@graphiql/plugin-explorer/dist/style.css';
5+
import { createGraphiQLFetcher } from '@graphiql/toolkit';
6+
import { GraphiQL } from 'graphiql';
7+
import 'graphiql/graphiql.css';
8+
import { useContext } from 'react';
9+
import { useRecoilValue } from 'recoil';
10+
import { ThemeContext } from 'twenty-ui';
11+
import { REACT_APP_SERVER_BASE_URL } from '~/config';
12+
13+
type GraphQLPlaygroundProps = {
14+
onError(): void;
15+
schema: PlaygroundSchemas;
16+
};
17+
18+
export const schemaToPath = {
19+
[PlaygroundSchemas.CORE]: 'graphql',
20+
[PlaygroundSchemas.METADATA]: 'metadata',
21+
};
22+
23+
export const GraphQLPlayground = ({
24+
onError,
25+
schema,
26+
}: GraphQLPlaygroundProps) => {
27+
const playgroundApiKey = useRecoilValue(playgroundApiKeyState);
28+
const baseUrl = REACT_APP_SERVER_BASE_URL + '/' + schemaToPath[schema];
29+
30+
const { theme } = useContext(ThemeContext);
31+
32+
if (!playgroundApiKey) {
33+
onError();
34+
return null;
35+
}
36+
37+
const explorer = explorerPlugin({
38+
showAttribution: true,
39+
});
40+
41+
const fetcher = createGraphiQLFetcher({
42+
url: baseUrl,
43+
});
44+
45+
return (
46+
<GraphiQL
47+
forcedTheme={theme.name as 'light' | 'dark'}
48+
plugins={[explorer]}
49+
fetcher={fetcher}
50+
defaultHeaders={JSON.stringify({
51+
Authorization: `Bearer ${playgroundApiKey}`,
52+
})}
53+
/>
54+
);
55+
};

0 commit comments

Comments
 (0)