From 15073be1edb36a9737ce2ed3128b6c9d4f1b1d30 Mon Sep 17 00:00:00 2001 From: Joshua Jones Date: Fri, 6 Jan 2023 12:08:59 -0800 Subject: [PATCH 1/4] Add application edit dialog (#678) * Add application edit dialog * Fix GraphQL ID type --- .../httplocalhost3000admingraphiql-19e539.gql | 2 +- .../httplocalhost3000admingraphiql-8b0644.gql | 6 + src/mocks/handlers.js | 7 +- src/mocks/resolvers/applications.js | 49 +++++++ .../application-services.tsx | 2 +- .../edit-application/edit-application.tsx | 131 ++++++++++++++++++ .../components/edit-application/index.ts | 1 + .../pages/devportal/applications/index.tsx | 20 +++ 8 files changed, 214 insertions(+), 4 deletions(-) create mode 100644 src/authz/graphql-whitelist/httplocalhost3000admingraphiql-8b0644.gql create mode 100644 src/nextapp/components/edit-application/edit-application.tsx create mode 100644 src/nextapp/components/edit-application/index.ts diff --git a/src/authz/graphql-whitelist/httplocalhost3000admingraphiql-19e539.gql b/src/authz/graphql-whitelist/httplocalhost3000admingraphiql-19e539.gql index b569691ff..ee58d8df5 100644 --- a/src/authz/graphql-whitelist/httplocalhost3000admingraphiql-19e539.gql +++ b/src/authz/graphql-whitelist/httplocalhost3000admingraphiql-19e539.gql @@ -1,5 +1,5 @@ - query GET_APPLICATION_SERVICES($appId: String!) { + query GetApplicationServices($appId: String!) { myServiceAccesses(where: { application: { appId: $appId } }) { id name diff --git a/src/authz/graphql-whitelist/httplocalhost3000admingraphiql-8b0644.gql b/src/authz/graphql-whitelist/httplocalhost3000admingraphiql-8b0644.gql new file mode 100644 index 000000000..fb7bd7c15 --- /dev/null +++ b/src/authz/graphql-whitelist/httplocalhost3000admingraphiql-8b0644.gql @@ -0,0 +1,6 @@ + + mutation UpdateApplication($id: ID!, $data: ApplicationUpdateInput) { + updateApplication(id: $id, data: $data) { + id + } + } diff --git a/src/mocks/handlers.js b/src/mocks/handlers.js index 8bfe5fdd3..ec38d7cca 100644 --- a/src/mocks/handlers.js +++ b/src/mocks/handlers.js @@ -9,7 +9,9 @@ import { import { allApplicationsHandler, createApplicationHandler, + getApplicationServicesHandler, removeApplicationHandler, + updateApplicationHandler, } from './resolvers/applications'; import { allProductsByNamespaceHandler, @@ -168,8 +170,7 @@ export const handlers = [ return res( ctx.status(200), ctx.json({ - user: null, - // user: { ...mark, namespace }, + user: { ...personas.mark, namespace }, }) ); }), @@ -238,7 +239,9 @@ export const handlers = [ keystone.mutation('UpdateEnvironment', updateEnvironmentHandler), // Applications keystone.query('MyApplications', allApplicationsHandler), + keystone.query('GetApplicationServices', getApplicationServicesHandler), keystone.mutation('AddApplication', createApplicationHandler), + keystone.mutation('UpdateApplication', updateApplicationHandler), keystone.mutation('RemoveApplication', removeApplicationHandler), // Services keystone.query('GetServices', allServicesHandler), diff --git a/src/mocks/resolvers/applications.js b/src/mocks/resolvers/applications.js index 07f048226..71996daee 100644 --- a/src/mocks/resolvers/applications.js +++ b/src/mocks/resolvers/applications.js @@ -53,6 +53,37 @@ export const allApplicationsHandler = (req, res, ctx) => { ); }; +export const getApplicationServicesHandler = (req, res, ctx) => { + if (req.variables.appId !== 'ABC123') { + return res( + ctx.data({ + myServiceAccesses: [], + }) + ); + } + return res( + ctx.data({ + myServiceAccesses: [ + { + id: 'sa1', + name: '123123-123ADSFSD', + active: true, + application: { + name: 'Demo App', + }, + productEnvironment: { + id: 'pe1', + name: 'dev', + product: { + name: 'My Pharma API', + }, + }, + }, + ], + }) + ); +}; + export const createApplicationHandler = (req, res, ctx) => { if (req.variables.name === 'error') { return res( @@ -75,6 +106,24 @@ export const createApplicationHandler = (req, res, ctx) => { ); }; +export const updateApplicationHandler = (req, res, ctx) => { + const { id, data } = req.variables; + const cached = applications.get(id); + + applications.set(id, { + ...cached, + ...data, + }); + + return res( + ctx.data({ + updateApplication: { + id, + }, + }) + ); +}; + export const removeApplicationHandler = (req, res, ctx) => { const { id } = req.variables; diff --git a/src/nextapp/components/application-services/application-services.tsx b/src/nextapp/components/application-services/application-services.tsx index 5b892a6c9..1fe9d377c 100644 --- a/src/nextapp/components/application-services/application-services.tsx +++ b/src/nextapp/components/application-services/application-services.tsx @@ -35,7 +35,7 @@ const ApplicationServices: React.FC = ({ appId }) => { export default ApplicationServices; const query = gql` - query GET_APPLICATION_SERVICES($appId: String!) { + query GetApplicationServices($appId: String!) { myServiceAccesses(where: { application: { appId: $appId } }) { id name diff --git a/src/nextapp/components/edit-application/edit-application.tsx b/src/nextapp/components/edit-application/edit-application.tsx new file mode 100644 index 000000000..86e46e0ad --- /dev/null +++ b/src/nextapp/components/edit-application/edit-application.tsx @@ -0,0 +1,131 @@ +import * as React from 'react'; +import { + Button, + ButtonGroup, + FormControl, + FormHelperText, + FormLabel, + Input, + Modal, + ModalBody, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Textarea, + useToast, +} from '@chakra-ui/react'; +import { useQueryClient } from 'react-query'; +import { useApiMutation } from '@/shared/services/api'; +import { gql } from 'graphql-request'; +import { Application, Mutation } from '@/shared/types/query.types'; + +interface EditApplicationProps { + data?: Application; + open: boolean; + onClose: () => void; + refreshQueryKey: string; +} + +const EditApplication: React.FC = ({ + data, + open, + onClose, + refreshQueryKey, +}) => { + const toast = useToast(); + const queryClient = useQueryClient(); + const applicationMutation = useApiMutation(mutation); + const form = React.useRef(); + const onSubmit = (event: React.FormEvent) => { + event.preventDefault(); + createApplication(); + }; + const createApplication = async () => { + if (form.current) { + try { + const formData = new FormData(form.current); + + if (form.current.checkValidity()) { + const name = formData.get('name') as string; + const description = formData.get('description') as string; + await applicationMutation.mutateAsync({ + id: data?.id, + data: { name, description }, + }); + toast({ + title: `${name} updated`, + status: 'success', + isClosable: true, + }); + queryClient.invalidateQueries(refreshQueryKey); + onClose(); + } + } catch (err) { + toast({ + title: 'Application update failed', + description: err, + status: 'error', + isClosable: true, + }); + } + } + }; + const submitForm = React.useCallback(() => form.current?.requestSubmit(), []); + + return ( + + + + Edit Application + +
+ + Application Name + + + + Description (Optional) + What does your application do? +