From 6f6f78fbe5c105af87e07b5e5f9185d466471c76 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Tue, 1 Feb 2022 19:38:45 +0200 Subject: [PATCH] refactor(azure/aci): migrate create view to react [EE-2188] (#6371) --- .../CreateContainerInstanceForm.test.tsx | 43 ++++ .../CreateContainerInstanceForm.tsx | 219 ++++++++++++++++++ .../CreateContainerInstanceForm.validation.ts | 21 ++ .../PortsMappingField.module.css | 13 ++ .../PortsMappingField.tsx | 90 +++++++ .../PortsMappingField.validation.ts | 11 + .../CreateContainerInstanceForm/index.ts | 1 + .../useCreateInstanceMutation.tsx | 61 +++++ .../useLoadFormState.ts | 171 ++++++++++++++ .../CreateContainerInstanceForm/utils.ts | 35 +++ .../CreateContainerInstanceView.tsx | 34 +++ app/azure/ContainerInstances/index.ts | 11 + app/azure/_module.js | 9 +- app/azure/models/container_group.js | 45 ---- app/azure/models/provider.js | 9 - app/azure/models/provider.ts | 21 ++ app/azure/rest/subscription.js | 1 - app/azure/services/azureService.js | 119 +++++----- .../services/container-groups.service.ts | 78 +++++++ app/azure/services/containerGroupService.js | 14 +- app/azure/services/provider.service.ts | 29 +++ app/azure/services/providerService.js | 27 --- app/azure/services/resource-groups.service.ts | 42 ++++ app/azure/services/resourceGroupService.js | 17 -- app/azure/services/subscription.service.ts | 30 +++ app/azure/services/subscriptionService.js | 23 +- app/azure/services/utils.ts | 12 + app/azure/types.ts | 83 +++++++ .../createContainerInstanceController.js | 122 ---------- .../create/createcontainerinstance.html | 173 -------------- app/global.d.ts | 10 + .../TeamsSelector/TeamsSelector.tsx | 3 + .../UsersSelector/UsersSelector.tsx | 3 + .../accessControlForm/AccessControlForm.tsx | 31 ++- .../AccessControlForm.validation.test.ts | 45 +++- .../AccessControlForm.validation.ts | 74 +++--- .../accessControlForm/TeamsField.tsx | 13 +- .../accessControlForm/UsersField.tsx | 6 +- .../AccessControlForm.validation.test.ts.snap | 6 +- .../FormControl/FormControl.tsx | 10 +- .../components/form-components/FormError.tsx | 13 ++ .../form-components/Input/Select.tsx | 2 +- .../InputList/InputList.module.css | 4 + .../form-components/InputList/InputList.tsx | 30 ++- app/portainer/hooks/useUser.tsx | 2 +- app/portainer/resource-control/helper.ts | 51 ++++ .../resource-control.service.ts | 48 ++++ app/portainer/resource-control/types.ts | 13 ++ app/portainer/services/axios.ts | 34 ++- app/setup-tests/server-handlers.ts | 3 + app/setup-tests/setup-handlers/azure.ts | 76 ++++++ package.json | 1 + yarn.lock | 5 + 53 files changed, 1476 insertions(+), 571 deletions(-) create mode 100644 app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.test.tsx create mode 100644 app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.tsx create mode 100644 app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.validation.ts create mode 100644 app/azure/ContainerInstances/CreateContainerInstanceForm/PortsMappingField.module.css create mode 100644 app/azure/ContainerInstances/CreateContainerInstanceForm/PortsMappingField.tsx create mode 100644 app/azure/ContainerInstances/CreateContainerInstanceForm/PortsMappingField.validation.ts create mode 100644 app/azure/ContainerInstances/CreateContainerInstanceForm/index.ts create mode 100644 app/azure/ContainerInstances/CreateContainerInstanceForm/useCreateInstanceMutation.tsx create mode 100644 app/azure/ContainerInstances/CreateContainerInstanceForm/useLoadFormState.ts create mode 100644 app/azure/ContainerInstances/CreateContainerInstanceForm/utils.ts create mode 100644 app/azure/ContainerInstances/CreateContainerInstanceView.tsx create mode 100644 app/azure/ContainerInstances/index.ts delete mode 100644 app/azure/models/provider.js create mode 100644 app/azure/models/provider.ts create mode 100644 app/azure/services/container-groups.service.ts create mode 100644 app/azure/services/provider.service.ts delete mode 100644 app/azure/services/providerService.js create mode 100644 app/azure/services/resource-groups.service.ts create mode 100644 app/azure/services/subscription.service.ts create mode 100644 app/azure/services/utils.ts create mode 100644 app/azure/types.ts delete mode 100644 app/azure/views/containerinstances/create/createContainerInstanceController.js delete mode 100644 app/azure/views/containerinstances/create/createcontainerinstance.html create mode 100644 app/portainer/components/form-components/FormError.tsx create mode 100644 app/portainer/resource-control/helper.ts create mode 100644 app/portainer/resource-control/resource-control.service.ts create mode 100644 app/portainer/resource-control/types.ts create mode 100644 app/setup-tests/setup-handlers/azure.ts diff --git a/app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.test.tsx b/app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.test.tsx new file mode 100644 index 000000000..2d01601a2 --- /dev/null +++ b/app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.test.tsx @@ -0,0 +1,43 @@ +import userEvent from '@testing-library/user-event'; + +import { UserContext } from '@/portainer/hooks/useUser'; +import { UserViewModel } from '@/portainer/models/user'; +import { renderWithQueryClient } from '@/react-tools/test-utils'; + +import { CreateContainerInstanceForm } from './CreateContainerInstanceForm'; + +jest.mock('@uirouter/react', () => ({ + ...jest.requireActual('@uirouter/react'), + useCurrentStateAndParams: jest.fn(() => ({ + params: { endpointId: 5 }, + })), +})); + +test('submit button should be disabled when name or image is missing', async () => { + const user = new UserViewModel({ Username: 'user' }); + + const { findByText, getByText, getByLabelText } = renderWithQueryClient( + + + + ); + + await expect(findByText(/Azure settings/)).resolves.toBeVisible(); + + const button = getByText(/Deploy the container/); + expect(button).toBeVisible(); + expect(button).toBeDisabled(); + + const nameInput = getByLabelText(/name/i); + userEvent.type(nameInput, 'name'); + + const imageInput = getByLabelText(/image/i); + userEvent.type(imageInput, 'image'); + + await expect(findByText(/Deploy the container/)).resolves.toBeEnabled(); + + expect(nameInput).toHaveValue('name'); + userEvent.clear(nameInput); + + await expect(findByText(/Deploy the container/)).resolves.toBeDisabled(); +}); diff --git a/app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.tsx b/app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.tsx new file mode 100644 index 000000000..0aa9aa0e2 --- /dev/null +++ b/app/azure/ContainerInstances/CreateContainerInstanceForm/CreateContainerInstanceForm.tsx @@ -0,0 +1,219 @@ +import { Field, Form, Formik } from 'formik'; +import { useCurrentStateAndParams, useRouter } from '@uirouter/react'; + +import { FormControl } from '@/portainer/components/form-components/FormControl'; +import { Input, Select } from '@/portainer/components/form-components/Input'; +import { FormSectionTitle } from '@/portainer/components/form-components/FormSectionTitle'; +import { LoadingButton } from '@/portainer/components/Button/LoadingButton'; +import { InputListError } from '@/portainer/components/form-components/InputList/InputList'; +import { AccessControlForm } from '@/portainer/components/accessControlForm'; +import { ContainerInstanceFormValues } from '@/azure/types'; +import * as notifications from '@/portainer/services/notifications'; +import { isAdmin, useUser } from '@/portainer/hooks/useUser'; + +import { validationSchema } from './CreateContainerInstanceForm.validation'; +import { PortMapping, PortsMappingField } from './PortsMappingField'; +import { useLoadFormState } from './useLoadFormState'; +import { + getSubscriptionLocations, + getSubscriptionResourceGroups, +} from './utils'; +import { useCreateInstance } from './useCreateInstanceMutation'; + +export function CreateContainerInstanceForm() { + const { + params: { endpointId: environmentId }, + } = useCurrentStateAndParams(); + + if (!environmentId) { + throw new Error('endpointId url param is required'); + } + + const { user } = useUser(); + const isUserAdmin = isAdmin(user); + + const { initialValues, isLoading, providers, subscriptions, resourceGroups } = + useLoadFormState(environmentId, isUserAdmin); + + const router = useRouter(); + + const { mutateAsync } = useCreateInstance( + resourceGroups, + environmentId, + user?.Id + ); + + if (isLoading) { + return null; + } + + return ( + + initialValues={initialValues} + validationSchema={() => validationSchema(isUserAdmin)} + onSubmit={onSubmit} + validateOnMount + validateOnChange + enableReinitialize + > + {({ + errors, + handleSubmit, + isSubmitting, + isValid, + values, + setFieldValue, + }) => ( +
+ Azure settings + + + + + + + + + + + + + Container configuration + + + + + + + + + + + + + + setFieldValue('ports', value)} + errors={errors.ports as InputListError[]} + /> + +
+
+ This will automatically deploy a container with a public IP + address +
+
+ + Container Resources + + + + + + + + + + setFieldValue('accessControl', values)} + values={values.accessControl} + errors={errors.accessControl} + /> + +
+
+ +