Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(webapp): Show integration issues in their settings #3044

Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
716cd13
More config validation
Nov 21, 2024
67efd89
More progress on showing missing config fields
Nov 21, 2024
4c83d9d
Merge branch 'master' into alan/nan-2168-surface-integrationsconnecti…
Nov 22, 2024
40e80d7
Merge branch 'master' into alan/nan-2168-surface-integrationsconnecti…
nalanj Nov 22, 2024
802a76a
Add missing fields column to db
Nov 22, 2024
ca8fb04
Move validation logic over and make it more functional per Thomas' fe…
Nov 22, 2024
1ef4ac4
Add missing_fields to _nango_config table and set them on update and …
Nov 22, 2024
796dd48
Merge branch 'master' into alan/nan-2168/integration-missing-fields
nalanj Nov 22, 2024
683e00b
Backfill missing fields on configs
Nov 22, 2024
40dbef7
Fix some type errors
Nov 25, 2024
5ddca13
Merge branch 'master' into alan/nan-2168/integration-missing-fields
nalanj Nov 25, 2024
a1c7f1e
Clean up setting missing fields on create
Nov 25, 2024
b137bfb
Merge branch 'master' into alan/nan-2168/integration-missing-fields
Nov 25, 2024
7dc7bd3
Merge branch 'master' into alan/nan-2168/integration-missing-fields
nalanj Nov 25, 2024
81ff9b8
One more typing fix
Nov 25, 2024
4b25005
Merge branch 'master' into alan/nan-2168/integration-missing-fields
nalanj Nov 25, 2024
95bc2ad
Add validation on CUSTOM auth mode
Nov 25, 2024
eaefdc9
Switch to flatMap
Nov 25, 2024
1f2cddc
Merge branch 'master' into alan/nan-2168/integration-missing-fields
nalanj Nov 25, 2024
a41f023
Merge branch 'master' into alan/nan-2168/integration-missing-fields
nalanj Nov 25, 2024
14ea864
Merge branch 'alan/nan-2168/integration-missing-fields' into alan/nan…
Nov 25, 2024
25fb4d6
Merge branch 'alan/nan-2168/missing-fields-backfill' into alan/nan-21…
Nov 25, 2024
57e563e
Show integration missing field warnings for all auto modes
Nov 25, 2024
2de006d
Add errors to integration list
Nov 25, 2024
6175ebe
Merge branch 'master' into alan/nan-2168-surface-integrationsconnecti…
Nov 27, 2024
2fca22a
Merge branch 'master' into alan/nan-2168-surface-integrationsconnecti…
nalanj Nov 27, 2024
c2f8497
Clean up field output based on feedback from Thomas
Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/server/lib/controllers/config.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export interface Integration {
creationDate: Date | undefined;
connectionConfigParams?: string[];
credentialParams?: string[];
missing_fields_count: number;
}

export interface ListIntegration {
Expand Down Expand Up @@ -131,7 +132,8 @@ class ConfigController {
provider: config.provider,
scripts: activeFlows.length,
connection_count: Number(config.connection_count),
creationDate: config.created_at
creationDate: config.created_at,
missing_fields_count: config.missing_fields.length
};

// Used by legacy connection create
Expand Down
5 changes: 3 additions & 2 deletions packages/webapp/src/components/ErrorCircle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { IconExclamationMark, IconLock, IconRefresh } from '@tabler/icons-react'
import type React from 'react';

export type ErrorCircleIcon = '!' | 'sync' | 'auth';
export const ErrorCircle: React.FC<{ icon?: ErrorCircleIcon }> = ({ icon = '!' }) => {
export type ErrorCircleVariant = 'error' | 'warning';
export const ErrorCircle: React.FC<{ icon?: ErrorCircleIcon; variant?: ErrorCircleVariant }> = ({ icon = '!', variant = 'error' }) => {
return (
<span className="mx-1 cursor-auto flex h-4 w-4 rounded-full ring-red-base/[.35] ring-4">
<span className="flex items-center rounded-full bg-red-base h-4 w-4">
<span className={`flex items-center rounded-full ${variant === 'warning' ? 'bg-yellow-base' : 'bg-red-base'} h-4 w-4`}>
{icon === '!' && <IconExclamationMark className="ml-[2px] h-3 w-3 text-pure-black" />}
{icon === 'sync' && <IconRefresh className="ml-[2px] h-3 w-3 text-pure-black" />}
{icon === 'auth' && <IconLock className="ml-[2px] h-3 w-3 text-pure-black" />}
Expand Down
9 changes: 8 additions & 1 deletion packages/webapp/src/pages/Integrations/List.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import IntegrationLogo from '../../components/ui/IntegrationLogo';

import { useStore } from '../../store';
import { useListIntegration } from '../../hooks/useIntegration';
import { ErrorCircle } from '../../components/ErrorCircle';
import { SimpleTooltip } from '../../components/SimpleTooltip';
import { Helmet } from 'react-helmet';
import { ErrorPageComponent } from '../../components/ErrorComponent';

Expand Down Expand Up @@ -61,7 +63,7 @@ export default function IntegrationList() {
<div className="w-1/3">Connections</div>
<div className="w-24">Active Scripts</div>
</div>
{integrations?.map(({ uniqueKey, provider, connection_count, scripts }) => (
{integrations?.map(({ uniqueKey, provider, connection_count, scripts, missing_fields_count }) => (
<div
key={`tr-${uniqueKey}`}
className={`flex gap-4 ${
Expand All @@ -76,6 +78,11 @@ export default function IntegrationList() {
<IntegrationLogo provider={provider} height={7} width={7} />
</div>
<p className="truncate">{uniqueKey}</p>
{missing_fields_count > 0 && (
<SimpleTooltip tooltipContent="Missing configuration">
<ErrorCircle icon="!" variant="warning" />
</SimpleTooltip>
)}
</div>
<div className="flex items-center w-1/3">
<p className="">{connection_count}</p>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import type { GetIntegration } from '@nangohq/types';
import { SettingsGeneral } from './components/General';
import { SettingsOAuth } from './components/OAuth';
import { SettingsOAuth, settingsMissingOAuth } from './components/OAuth';
import { useStore } from '../../../../store';
import { useEnvironment } from '../../../../hooks/useEnvironment';
import type { EnvironmentAndAccount } from '@nangohq/server';
import { SettingsApp } from './components/App';
import { SettingsCustom } from './components/Custom';
import { SettingsApp, settingsMissingApp } from './components/App';
import { SettingsCustom, settingsMissingCustom } from './components/Custom';
import { SettingsDefault } from './components/Default';

export const SettingsSwitch: React.FC<{ data: GetIntegration['Success']['data']; environment: EnvironmentAndAccount['environment'] }> = ({
Expand Down Expand Up @@ -41,6 +41,32 @@ export const SettingsSwitch: React.FC<{ data: GetIntegration['Success']['data'];
}
};

const missingFieldsMessage = (data: GetIntegration['Success']['data']): string | null => {
switch (data.template.auth_mode) {
case 'OAUTH1':
case 'OAUTH2':
case 'TBA':
return settingsMissingOAuth(data.integration.missing_fields);

case 'APP':
return settingsMissingApp(data.integration.missing_fields);

case 'CUSTOM':
return settingsMissingCustom(data.integration.missing_fields);

case 'BASIC':
case 'API_KEY':
case 'APP_STORE':
case 'TABLEAU':
case 'NONE':
case 'OAUTH2_CC':
return null;

default:
return null;
}
};

export const SettingsShow: React.FC<{ data: GetIntegration['Success']['data'] }> = ({ data }) => {
const env = useStore((state) => state.env);
const { environmentAndAccount, loading } = useEnvironment(env);
Expand All @@ -51,7 +77,7 @@ export const SettingsShow: React.FC<{ data: GetIntegration['Success']['data'] }>

return (
<div>
<SettingsGeneral data={data} environment={environmentAndAccount.environment} />
<SettingsGeneral data={data} environment={environmentAndAccount.environment} missingFieldsMessage={missingFieldsMessage(data)} />
<SettingsSwitch data={data} environment={environmentAndAccount.environment} />
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,20 @@ export const SettingsApp: React.FC<{ data: GetIntegration['Success']['data']; en
</div>
);
};

export const settingsMissingApp = (missingFields: string[]): string => {
return missingFields
.map((field) => {
switch (field) {
case 'oauth_client_id':
return 'App ID';
case 'oauth_client_secret':
return 'App Private Key';
case 'app_link':
return 'App Public Link';
default:
return field;
}
})
.join(', ');
};
Original file line number Diff line number Diff line change
Expand Up @@ -136,3 +136,24 @@ export const SettingsCustom: React.FC<{ data: GetIntegration['Success']['data'];
</div>
);
};

export const settingsMissingCustom = (missingFields: string[]): string => {
return missingFields
.map((field) => {
switch (field) {
case 'oauth_client_id':
return 'Client ID';
case 'oauth_client_secret':
return 'Client Secret';
case 'app_link':
return 'App Public Link';
case 'app_id':
return 'App ID';
case 'private_key':
return 'App Private Key';
default:
return field;
}
})
.join(', ');
};
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import { InfoBloc } from '../../../../../components/InfoBloc';
import { CopyButton } from '../../../../../components/ui/button/CopyButton';
import SecretInput from '../../../../../components/ui/input/SecretInput';
import type { EnvironmentAndAccount } from '@nangohq/server';
import { Info } from '../../../../../components/Info';

export const SettingsGeneral: React.FC<{ data: GetIntegration['Success']['data']; environment: EnvironmentAndAccount['environment'] }> = ({
data: { integration, meta, template },
environment
}) => {
export const SettingsGeneral: React.FC<{
data: GetIntegration['Success']['data'];
environment: EnvironmentAndAccount['environment'];
missingFieldsMessage: string | null;
}> = ({ data: { integration, meta, template }, environment, missingFieldsMessage }) => {
const { toast } = useToast();
const navigate = useNavigate();

Expand Down Expand Up @@ -59,6 +61,10 @@ export const SettingsGeneral: React.FC<{ data: GetIntegration['Success']['data']

return (
<div className="flex flex-col gap-8">
{missingFieldsMessage && (
<Info variant="warning">This integration cannot create connections until the following fields are configured: {missingFieldsMessage}</Info>
)}

<div className="grid grid-cols-2 gap-10">
<InfoBloc title="API Provider">{integration?.provider}</InfoBloc>
<InfoBloc title="Integration ID">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,3 +94,18 @@ export const SettingsOAuth: React.FC<{ data: GetIntegration['Success']['data'];
</div>
);
};

export const settingsMissingOAuth = (missingFields: string[]): string => {
return missingFields
.map((field) => {
switch (field) {
case 'oauth_client_id':
return 'Client ID';
case 'oauth_client_secret':
return 'Client Secret';
default:
return field;
}
})
.join(', ');
};
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,10 @@ export const ShowIntegration: React.FC = () => {
<Button variant={tab === 'home' ? 'active' : 'zombie'}>Endpoints</Button>
</Link>
<Link to="./settings">
<Button variant={tab === 'settings' ? 'active' : 'zombie'}>Settings</Button>
<Button variant={tab === 'settings' ? 'active' : 'zombie'}>
Settings
{data.integration.missing_fields.length > 0 && <span className="ml-2 bg-yellow-base h-1.5 w-1.5 rounded-full inline-block"></span>}
</Button>
</Link>
</nav>
<Routes>
Expand Down
Loading