Skip to content

Commit

Permalink
fix(ui): improve cache for session, integrations (#1904)
Browse files Browse the repository at this point in the history
## Describe your changes

Fix NAN-639

- Completely empty SWR cache request when we logout
This is an edge case as most user don't have multiple account, but that
can avoid some surprises.

- Create hook for integration list and expose correct type

- Correctly mutate cache when we create, update, delete integrations


## Demo


https://github.com/NangoHQ/nango/assets/1637651/41771184-b458-43b0-9d25-a07a2736dab8
  • Loading branch information
bodinsamuel committed Mar 26, 2024
1 parent e065491 commit 452a806
Show file tree
Hide file tree
Showing 7 changed files with 47 additions and 17 deletions.
8 changes: 6 additions & 2 deletions packages/server/lib/controllers/config.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
} from '@nangohq/shared';
import { getUserAccountAndEnvironmentFromSession, parseConnectionConfigParamsFromTemplate } from '../utils/utils.js';

interface Integration {
export interface Integration {
authMode: AuthModes;
uniqueKey: string;
provider: string;
Expand All @@ -39,12 +39,16 @@ interface Integration {
connectionConfigParams?: string[];
}

export interface ListIntegration {
integrations: Integration[];
}

class ConfigController {
/**
* Webapp
*/

async listProviderConfigsWeb(req: Request, res: Response, next: NextFunction) {
async listProviderConfigsWeb(req: Request, res: Response<ListIntegration>, next: NextFunction) {
try {
const { success, error, response } = await getUserAccountAndEnvironmentFromSession(req);
if (!success || response === null) {
Expand Down
1 change: 1 addition & 0 deletions packages/server/lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export type { GetMeta } from './controllers/environment.controller.js';
export type { GetUser } from './controllers/user.controller.js';
export type { ListIntegration, Integration } from './controllers/config.controller.js';
16 changes: 16 additions & 0 deletions packages/webapp/src/hooks/useIntegration.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import useSWR from 'swr';
import type { ListIntegration } from '@nangohq/server';
import { swrFetcher } from '../utils/api';

export function useListIntegration(env: string) {
const { data, error, mutate } = useSWR<ListIntegration>(`/api/v1/integration?env=${env}`, swrFetcher);

const loading = !data && !error;

return {
loading,
error,
list: data,
mutate
};
}
19 changes: 14 additions & 5 deletions packages/webapp/src/pages/Integration/AuthSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,28 @@ import { useNavigate } from 'react-router-dom';
import { toast } from 'react-toastify';
import { HelpCircle } from '@geist-ui/icons';
import { PencilSquareIcon, XCircleIcon } from '@heroicons/react/24/outline';
import { Tooltip } from '@geist-ui/core';
import { useModal } from '@geist-ui/core';
import { AuthModes, IntegrationConfig, Account } from '../../types';
import { Tooltip, useModal } from '@geist-ui/core';
import type { IntegrationConfig, Account } from '../../types';
import { AuthModes } from '../../types';
import { useDeleteIntegrationAPI, useCreateIntegrationAPI, useEditIntegrationAPI, useEditIntegrationNameAPI } from '../../utils/api';
import Info from '../../components/ui/Info';
import ActionModal from '../../components/ui/ActionModal';
import SecretInput from '../../components/ui/input/SecretInput';
import SecretTextArea from '../../components/ui/input/SecretTextArea';
import { formatDateToShortUSFormat } from '../../utils/utils';
import { formatDateToShortUSFormat, defaultCallback } from '../../utils/utils';
import CopyButton from '../../components/ui/button/CopyButton';
import TagsInput from '../../components/ui/input/TagsInput';
import { defaultCallback } from '../../utils/utils';

import { useStore } from '../../store';
import { useSWRConfig } from 'swr';

interface AuthSettingsProps {
integration: IntegrationConfig | null;
account: Account;
}

export default function AuthSettings(props: AuthSettingsProps) {
const { mutate } = useSWRConfig();
const { integration, account } = props;

const [serverErrorMessage, setServerErrorMessage] = useState('');
Expand Down Expand Up @@ -56,6 +57,7 @@ export default function AuthSettings(props: AuthSettingsProps) {

if (res?.status === 204) {
toast.success('Integration deleted!', { position: toast.POSITION.BOTTOM_CENTER });
clearCache();
navigate(`/${env}/integrations`, { replace: true });
}
setModalShowSpinner(false);
Expand All @@ -70,6 +72,10 @@ export default function AuthSettings(props: AuthSettingsProps) {
setVisible(true);
};

const clearCache = () => {
void mutate((key) => typeof key === 'string' && key.startsWith('/api/v1/integration'), undefined);
};

const handleSave = async (e: React.SyntheticEvent) => {
e.preventDefault();
setServerErrorMessage('');
Expand Down Expand Up @@ -114,6 +120,7 @@ export default function AuthSettings(props: AuthSettingsProps) {

if (res?.status === 200) {
toast.success('Integration updated!', { position: toast.POSITION.BOTTOM_CENTER });
clearCache();
}
} else {
const target = e.target as typeof e.target & {
Expand Down Expand Up @@ -150,6 +157,7 @@ export default function AuthSettings(props: AuthSettingsProps) {

if (res?.status === 200) {
toast.success('Integration created!', { position: toast.POSITION.BOTTOM_CENTER });
clearCache();
navigate(`/${env}/integrations`, { replace: true });
} else if (res != null) {
const payload = await res.json();
Expand Down Expand Up @@ -181,6 +189,7 @@ export default function AuthSettings(props: AuthSettingsProps) {
toast.success('Integration ID updated!', { position: toast.POSITION.BOTTOM_CENTER });
setIntegrationId(integrationIdEdit);
navigate(`/${env}/integration/${integrationIdEdit}`, { replace: true });
clearCache();
} else if (res != null) {
const payload = await res.json();
toast.error(payload.error, {
Expand Down
3 changes: 3 additions & 0 deletions packages/webapp/src/pages/Integration/Create.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import DashboardLayout from '../../layout/DashboardLayout';
import type { AuthModes } from '../../types';
import IntegrationLogo from '../../components/ui/IntegrationLogo';
import { useStore } from '../../store';
import { useSWRConfig } from 'swr';

interface Provider {
name: string;
Expand All @@ -20,6 +21,7 @@ interface Provider {
}

export default function Create() {
const { mutate } = useSWRConfig();
const [loaded, setLoaded] = useState(false);
const [initialProviders, setInitialProviders] = useState<Provider[] | null>(null);
const [providers, setProviders] = useState<Provider[] | null>(null);
Expand Down Expand Up @@ -52,6 +54,7 @@ export default function Create() {
if (res?.status === 200) {
toast.success('Integration created!', { position: toast.POSITION.BOTTOM_CENTER });
const data = await res.json();
void mutate((key) => typeof key === 'string' && key.startsWith('/api/v1/integration'), undefined);
navigate(`/${env}/integration/${data.config.unique_key}#auth`);
}
};
Expand Down
11 changes: 2 additions & 9 deletions packages/webapp/src/pages/Integration/List.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useNavigate, Link } from 'react-router-dom';
import useSWR from 'swr';
import { Loading } from '@geist-ui/core';
import { PlusIcon } from '@heroicons/react/24/outline';

Expand All @@ -9,20 +8,14 @@ import IntegrationLogo from '../../components/ui/IntegrationLogo';
import { requestErrorToast } from '../../utils/api';

import { useStore } from '../../store';

interface Integration {
uniqueKey: string;
provider: string;
connection_count: number;
scripts: number;
}
import { useListIntegration } from '../../hooks/useIntegration';

export default function IntegrationList() {
const navigate = useNavigate();

const env = useStore((state) => state.cookieValue);

const { data, error } = useSWR<{ integrations: Integration[] }>(`/api/v1/integration?env=${env}`);
const { list: data, error } = useListIntegration(env);

if (error) {
requestErrorToast();
Expand Down
6 changes: 5 additions & 1 deletion packages/webapp/src/utils/user.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import storage, { LocalStorageKeys } from '../utils/local-storage';
import { useLogoutAPI } from '../utils/api';
import { useNavigate } from 'react-router-dom';
import { useAnalyticsIdentify, useAnalyticsReset } from './analytics';
import { useSWRConfig } from 'swr';

export interface User {
id: number;
Expand All @@ -26,12 +27,15 @@ export function useSignin() {
export function useSignout() {
const analyticsReset = useAnalyticsReset();
const nav = useNavigate();
const { mutate } = useSWRConfig();
const logoutAPI = useLogoutAPI();

return () => {
storage.clear();
analyticsReset();
logoutAPI(); // Destroy server session.
void logoutAPI(); // Destroy server session.

void mutate(() => true, undefined, { revalidate: false }); // clean all cache
nav('/signin', { replace: true });
};
}
Expand Down

0 comments on commit 452a806

Please sign in to comment.