Skip to content

Commit

Permalink
Feature/sprint 56 UI fixes (#596)
Browse files Browse the repository at this point in the history
* Add fixes for deleting application optimistically and preventing saves in controls dialog

* Tighten up applications mocks, errors
  • Loading branch information
Jonesy committed Oct 12, 2022
1 parent 466d7e9 commit 63c5e67
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 41 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

mutation Remove($id: ID!) {
mutation RemoveApplication($id: ID!) {
deleteApplication(id: $id) {
name
id
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@

mutation Add($name: String!, $description: String) {
mutation AddApplication($name: String!, $description: String) {
createApplication(data: { name: $name, description: $description }) {
id
appId
Expand Down
9 changes: 9 additions & 0 deletions src/mocks/handlers.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { graphql, rest } from 'msw';

import { harley, mark } from './resolvers/personas';
import {
allApplicationsHandler,
createApplicationHandler,
removeApplicationHandler,
} from './resolvers/applications';
import {
allProductsByNamespaceHandler,
accessRequestAuthHandler,
Expand Down Expand Up @@ -116,6 +121,10 @@ export const handlers = [
'GetConsumerProductsAndEnvironments',
allProductsByNamespaceHandler
),
// Applications
keystone.query('MyApplications', allApplicationsHandler),
keystone.mutation('AddApplication', createApplicationHandler),
keystone.mutation('RemoveApplication', removeApplicationHandler),
// Service accounts
keystone.query('GetAllServiceAccounts', getAllServiceAccountsHandler),
keystone.mutation('CreateServiceAccount', createServiceAccountHandler),
Expand Down
98 changes: 98 additions & 0 deletions src/mocks/resolvers/applications.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import casual from 'casual-browserify';

const applications = new Map([
[
'app1',
{
id: 'app1',
appId: 'ABC123',
description: '',
name: 'Demo App',
owner: {
name: 'Mark Smith',
},
},
],
[
'app2',
{
id: 'app2',
appId: 'ABC123',
description: '',
name: 'Shoppers Drug Mart 112',
owner: {
name: 'Mark Smith',
},
},
],
[
'app3error',
{
id: 'app3error',
appId: 'ABC123',
description: '',
name: 'Easy Mart Store 123',
owner: {
name: 'Carter Schleifer',
},
},
],
]);

export const allApplicationsHandler = (req, res, ctx) => {
return res(
ctx.data({
myApplications: Array.from(applications.values()),
allTemporaryIdentities: [
{
id: 'u1',
userId: '1',
},
],
})
);
};

export const createApplicationHandler = (req, res, ctx) => {
if (req.variables.name === 'error') {
return res(
ctx.data({
errors: [{ message: 'You do not have permission' }],
})
);
}

const record = {
id: `app1${applications.size + 1}`,
appId: casual.uuid,
...req.variables,
};
applications.set(record.id, record);
return res(
ctx.data({
createApplication: record,
})
);
};

export const removeApplicationHandler = (req, res, ctx) => {
const { id } = req.variables;

if (id === 'app3error') {
return res(
ctx.data({
errors: [
{ message: 'You do not have permission to delete this application' },
],
})
);
}

const application = applications.get(id);
applications.delete(id);
return res(
ctx.data({
deleteApplication: application,
})
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -71,11 +71,11 @@ const AccessRequestDialog: React.FC<AccessRequestDialogProps> = ({
const buttonProps = !isInline
? {}
: {
fontWeight: 'normal',
fontSize: 'inherit',
color: 'bc-link',
textDecor: 'underline',
};
fontWeight: 'normal',
fontSize: 'inherit',
color: 'bc-link',
textDecor: 'underline',
};

// Events
const handleAccessSubmit = React.useCallback(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ const query = gql`
`;

const mutation = gql`
mutation Add($name: String!, $description: String) {
mutation AddApplication($name: String!, $description: String) {
createApplication(data: { name: $name, description: $description }) {
id
appId
Expand Down
22 changes: 19 additions & 3 deletions src/nextapp/components/access-request/edit-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,12 @@ import {
Grid,
useToast,
Flex,
Alert,
AlertDescription,
AlertIcon,
} from '@chakra-ui/react';
import {
ConsumerPlugin,
GatewayPlugin,
GatewayPluginCreateInput,
} from '@/shared/types/query.types';
import EnvironmentTag from '@/components/environment-tag';
import format from 'date-fns/format';
Expand Down Expand Up @@ -56,6 +57,7 @@ const ConsumerEditDialog: React.FC<ConsumerEditDialogProps> = ({
{ suspense: false, enabled: isOpen }
);
const [tabIndex, setTabIndex] = React.useState(0);
const [hasUnsavedChanges, setHasUnsavedChanges] = React.useState(false);
const restrictions = React.useState<ConsumerPlugin[]>(() => {
return (
data?.getConsumerProdEnvAccess.plugins
Expand Down Expand Up @@ -93,6 +95,7 @@ const ConsumerEditDialog: React.FC<ConsumerEditDialogProps> = ({
const handleClose = () => {
const [, setRestrictions] = restrictions;
const [, setRateLimits] = rateLimits;
setHasUnsavedChanges(false);
setRestrictions(
data?.getConsumerProdEnvAccess.plugins
.filter((p) => p.name === 'ip-restriction')
Expand Down Expand Up @@ -124,10 +127,16 @@ const ConsumerEditDialog: React.FC<ConsumerEditDialogProps> = ({
const data = {
plugins: [...restrictsionsData, ...rateLimitsData],
};

const authorizationForm = ref?.current.querySelector(
'form[name="authorizationForm"]'
);
const ipRestrictionForm = new FormData(ref?.current.querySelector('form[name="ipRestrictionsForm"]'));

if (ipRestrictionForm.get('allow') !== '[]') {
setHasUnsavedChanges(true);
return false;
}

if (authorizationForm) {
const authorizationFormData = new FormData(authorizationForm);
const defaultClientScopes = authorizationFormData.getAll(
Expand All @@ -153,6 +162,7 @@ const ConsumerEditDialog: React.FC<ConsumerEditDialogProps> = ({
client.invalidateQueries(['consumerEdit', prodEnvId, consumerId]);
onClose();
setTabIndex(0);
setHasUnsavedChanges(false);
} catch (err) {
toast({
title: 'Request save failed',
Expand Down Expand Up @@ -249,6 +259,12 @@ const ConsumerEditDialog: React.FC<ConsumerEditDialogProps> = ({
<ModalCloseButton data-testid="consumer-edit-close-btn" />
{isSuccess && (
<ModalBody ref={ref}>
{hasUnsavedChanges && (
<Alert status="error" variant="solid" mb={8}>
<AlertIcon />
<AlertDescription>You have an unapplied IP Restrictions control.</AlertDescription>
</Alert>
)}
<Box
hidden={tabIndex !== 0}
display={tabIndex === 0 ? 'block' : 'none'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,10 @@ const NewApplicationDialog: React.FC<NewApplicationDialog> = ({
handleAfterCreate(newApplication?.createApplication);
}
}
} catch {
} catch (err) {
toast({
title: 'Create application failed',
description: err,
status: 'error',
isClosable: true,
});
Expand Down Expand Up @@ -133,7 +134,7 @@ const NewApplicationDialog: React.FC<NewApplicationDialog> = ({
export default NewApplicationDialog;

const mutation = gql`
mutation Add($name: String!, $description: String) {
mutation AddApplication($name: String!, $description: String) {
createApplication(data: { name: $name, description: $description }) {
id
appId
Expand Down
74 changes: 46 additions & 28 deletions src/nextapp/pages/devportal/applications/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ import EmptyPane from '@/components/empty-pane';
import ApplicationServices from '@/components/application-services';
import { ErrorBoundary } from 'react-error-boundary';

const queryKey = 'allApplications';

export const getServerSideProps: GetServerSideProps = async (context) => {
const queryKey = 'allApplications';
const queryClient = new QueryClient();

await queryClient.prefetchQuery(
Expand All @@ -50,13 +49,14 @@ export const getServerSideProps: GetServerSideProps = async (context) => {
return {
props: {
dehydratedState: dehydrate(queryClient),
queryKey,
},
};
};

const ApplicationsPage: React.FC<
InferGetServerSidePropsType<typeof getServerSideProps>
> = () => {
> = ({ queryKey }) => {
const toast = useToast();
const [openId, setOpenId] = React.useState<string | null>();
const queryClient = useQueryClient();
Expand All @@ -75,30 +75,44 @@ const ApplicationsPage: React.FC<
},
[setOpenId]
);
const deleteMutation = useApiMutation<{ id: string }>(mutation);
const handleDelete = React.useCallback(
(id: string) => async () => {
try {
if (openId === id) {
setOpenId(null);
}
await deleteMutation.mutateAsync({ id });
queryClient.invalidateQueries('allApplications');
toast({
title: 'Application deleted',
status: 'success',
isClosable: true,
});
} catch {
toast({
title: 'Application delete failed',
status: 'error',
isClosable: true,
});
}
const deleteMutation = useApiMutation<{ id: string }>(mutation, {
onMutate: async ({ id }) => {
const previousApps = queryClient.getQueryData(queryKey);
queryClient.setQueryData(queryKey, (old: any) => ({
...old,
myApplications: old.myApplications.filter(
(a: Application) => a.id !== id
),
}));
return { previousApps };
},
[deleteMutation, openId, queryClient, setOpenId, toast]
);
onError: (_, __, context) => {
queryClient.setQueryData(queryKey, context.previousApps);
},
onSettled() {
queryClient.refetchQueries(queryKey);
},
});
const handleDelete = (id: string) => async () => {
try {
if (openId === id) {
setOpenId(null);
}
await deleteMutation.mutateAsync({ id });
toast({
title: 'Application deleted',
status: 'success',
isClosable: true,
});
} catch (err) {
toast({
title: 'Application delete failed',
description: err,
status: 'error',
isClosable: true,
});
}
};

// Table props
const columns = [
Expand Down Expand Up @@ -151,7 +165,11 @@ const ApplicationsPage: React.FC<
aria-label={`${d.name} actions menu button`}
placement="bottom-end"
>
<MenuItem color="red.500" data-testid="delete-application-btn" onClick={handleDelete(d.id)}>
<MenuItem
color="red.500"
data-testid="delete-application-btn"
onClick={handleDelete(d.id)}
>
Delete Application
</MenuItem>
</ActionsMenu>
Expand Down Expand Up @@ -237,7 +255,7 @@ const query = gql`
`;

const mutation = gql`
mutation Remove($id: ID!) {
mutation RemoveApplication($id: ID!) {
deleteApplication(id: $id) {
name
id
Expand Down

0 comments on commit 63c5e67

Please sign in to comment.