Skip to content

Commit

Permalink
fix: ignore json errors on initial post
Browse files Browse the repository at this point in the history
  • Loading branch information
jrea committed Aug 16, 2023
1 parent 83364fe commit 3fd3206
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 49 deletions.
92 changes: 76 additions & 16 deletions packages/react/src/SSO/BaseSSOForm.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react';
import { useMutation } from '@tanstack/react-query';
import { UpdateProviderRequest } from '@theniledev/browser';
import { UpdateProviderRequest, SSOProvider } from '@theniledev/browser';
import Stack from '@mui/joy/Stack';
import Typography from '@mui/joy/Typography';
import { SSOProvider } from '@theniledev/browser';
import Alert from '@mui/joy/Alert';
import CheckCircleOutlined from '@mui/icons-material/CheckCircleOutlined';

import SimpleForm from '../lib/SimpleForm';
import { useApi } from '../context';
Expand All @@ -21,7 +22,6 @@ export default function BaseSSOForm(
config: SSOProvider;
}
) {
const api = useApi();
const {
config,
providerName,
Expand All @@ -30,13 +30,19 @@ export default function BaseSSOForm(
allowEdit = true,
configurationGuide,
} = props;

const api = useApi();
const [loading, setLoading] = React.useState(false);
const [success, setSuccess] = React.useState(false);
const [optimisticConfig, setConfig] = React.useState<SSOProvider>(config);
const timer = React.useRef<NodeJS.Timeout>();
const attributes = React.useMemo(() => {
const attributes: Attribute[] = [
{
name: 'enabled',
label: 'Allow Okta logins',
type: AttributeType.Switch,
defaultValue: config?.enabled === true,
defaultValue: optimisticConfig?.enabled === true,
options: [
{
label: 'Enabled',
Expand All @@ -51,15 +57,15 @@ export default function BaseSSOForm(
name: 'clientId',
label: 'Client id',
type: AttributeType.Text,
defaultValue: config?.clientId ?? '',
defaultValue: optimisticConfig?.clientId ?? '',
required: true,
disabled: !allowEdit,
},
{
name: 'configUrl',
label: 'Config url',
type: AttributeType.Text,
defaultValue: config?.configUrl ?? '',
defaultValue: optimisticConfig?.configUrl ?? '',
helpText:
'The URL of the .well-known/openid-configuration for the identity provider',
required: true,
Expand All @@ -69,14 +75,14 @@ export default function BaseSSOForm(
name: 'emailDomains',
label: 'Email domains',
type: AttributeType.Text,
defaultValue: config?.emailDomains?.join(', ') ?? '',
defaultValue: optimisticConfig?.emailDomains?.join(', ') ?? '',
required: true,
helpText:
'A comma seperated list of email domains (@yourDomain.com) to be used',
'A comma seperated list of email domains (yourDomain.com) to be used',
disabled: !allowEdit,
},
];
if (!config?.clientId) {
if (!optimisticConfig?.clientId) {
attributes.splice(2, 0, {
name: 'clientSecret',
label: 'Client secret',
Expand All @@ -89,42 +95,96 @@ export default function BaseSSOForm(
return attributes;
}, [
allowEdit,
config?.clientId,
config?.configUrl,
config?.emailDomains,
config?.enabled,
optimisticConfig?.clientId,
optimisticConfig?.configUrl,
optimisticConfig?.emailDomains,
optimisticConfig?.enabled,
]);

const handleTimer = () => {
if (timer.current) {
clearTimeout(timer.current);
}

timer.current = setTimeout(() => {
setSuccess(false);
}, 3000);
};

const mutation = useMutation(
(ssoRequest: SSOFormRequest) => {
setLoading(true);
const payload = {
providerName: providerName.toLowerCase(),
updateProviderRequest: {
...ssoRequest,
emailDomains: ssoRequest.emailDomains.split(','),
},
};
if (config != null) {
if (optimisticConfig != null) {
return api.auth.updateProvider(payload);
} else {
return api.auth.createProvider(payload);
}
},
{
onSuccess,
onSuccess: (data, vars) => {
setConfig(data);
setSuccess(true);
onSuccess && onSuccess(data, vars);
},
onError,
onSettled: (data, error, vars) => {
setLoading(false);
handleTimer();
if (!data) {
if (!error || error?.message.includes('Unterminated string')) {
// something unexpected happened on the BE, but it's non-fatal
setConfig({
enabled: vars.enabled,
clientId: vars.clientId,
configUrl: vars.configUrl,
emailDomains: vars.emailDomains.split(', '),
} as SSOProvider);
}
setSuccess(true);
onSuccess && onSuccess(data, vars);
}
},
}
);

React.useEffect(() => {
() => {
clearTimeout(timer.current);
};
});

return (
<Stack gap={2}>
<Stack gap={2} position="relative">
<Typography level="h4">Step 1</Typography>
{configurationGuide}
<Typography level="h4">Step 2</Typography>
<SimpleForm
mutation={mutation}
buttonText="Update"
attributes={attributes}
loading={loading}
successMessage={
<Alert
color="success"
sx={{
opacity: success ? 1 : 0,
transition: 'opacity 200ms',
height: '0.9rem',
}}
startDecorator={<CheckCircleOutlined />}
>
<Typography textAlign="center" fontSize="sm">
Provider updated
</Typography>
</Alert>
}
/>
</Stack>
);
Expand Down
40 changes: 25 additions & 15 deletions packages/react/src/SSO/Okta.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,22 +56,32 @@ function ConfigGuide({ callbackUrl }: { callbackUrl: string }) {
readOnly={true}
endDecorator={
<Tooltip title="Copy Okta redirect URL">
<Box position="relative" width="24px" height="24px">
<Box position="absolute" top="0" left="0">
<CopyAll
sx={{
opacity: copied ? 0 : 1,
transition: 'opacity 300ms',
}}
/>
<Box
position="relative"
width={copied ? '82px' : '24px'}
height="24px"
>
<Box
position="absolute"
top="0"
left="0"
sx={{
opacity: copied ? 0 : 1,
transition: 'opacity 300ms',
}}
>
<CopyAll />
</Box>
<Box position="absolute" top="0" left="0">
<CheckCircleOutlined
sx={{
opacity: !copied ? 0 : 1,
transition: 'opacity 300ms',
}}
/>
<Box
position="absolute"
top="0"
left="0"
sx={{ opacity: !copied ? 0 : 1, transition: 'opacity 300ms' }}
>
<Stack direction="row" gap={1}>
<CheckCircleOutlined />
<Typography color="primary">Copied!</Typography>
</Stack>
</Box>
</Box>
</Tooltip>
Expand Down
20 changes: 17 additions & 3 deletions packages/react/src/lib/SimpleForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ function Labler(props: { error?: string; attr: Attribute }) {
<Tooltip title={error} color="danger" sx={{ cursor: 'pointer' }}>
<FormLabel>
{attr.label ?? attr.name}
<Error color="error" sx={{ ml: 0.5 }} />
<Error sx={{ ml: 0.5, '--Icon-color': '#c41c1c' }} fontSize="small" />
</FormLabel>
</Tooltip>
);
Expand All @@ -59,8 +59,17 @@ export default function SimpleForm(props: {
attributes: Attribute[];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
mutation: any;
loading?: boolean;
successMessage?: JSX.Element;
}) {
const { mutation, buttonText, attributes, cancelButton } = props;
const {
mutation,
buttonText,
attributes,
cancelButton,
loading,
successMessage,
} = props;

const defaultValues = React.useMemo(
() =>
Expand Down Expand Up @@ -269,7 +278,12 @@ export default function SimpleForm(props: {
</Stack>
) : (
<Box>
<Button type="submit">{buttonText}</Button>
<Stack direction="row" gap={2}>
<Button type="submit" loading={loading}>
{buttonText}
</Button>
{successMessage}
</Stack>
</Box>
)}
</Stack>
Expand Down
15 changes: 0 additions & 15 deletions packages/server/src/utils/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,21 +87,6 @@ export async function _fetch(
});

if (response && response.status >= 200 && response.status < 300) {
// const _res = response.clone();
// const { headers: _, body: _b, ...everythingElse } = response;
// const headers = new Headers(response.headers);
// if (_res) {
// const length = Buffer.byteLength(
// await new Response(_res.body).text(),
// 'utf-8'
// );
// headers.set('content-length', String(length));
// }
// const res = new Response(_b, {
// headers,
// ...everythingElse,
// });
// return res;
return response;
}

Expand Down

0 comments on commit 3fd3206

Please sign in to comment.