diff --git a/src/components/ImportForm_new/ApplicationSection/ApplicationSection.tsx b/src/components/ImportForm_new/ApplicationSection/ApplicationSection.tsx
index aa57ef9d8..a85888c99 100644
--- a/src/components/ImportForm_new/ApplicationSection/ApplicationSection.tsx
+++ b/src/components/ImportForm_new/ApplicationSection/ApplicationSection.tsx
@@ -1,7 +1,7 @@
import * as React from 'react';
import { Flex, FlexItem, FormSection } from '@patternfly/react-core';
import { useFormikContext } from 'formik';
-import { InputField } from 'formik-pf';
+import { InputField } from '../../../shared';
import { ApplicationThumbnail } from '../../ApplicationDetails/ApplicationThumbnail';
import { ImportFormValues } from '../type';
@@ -22,9 +22,8 @@ const ApplicationSection: React.FunctionComponent
diff --git a/src/components/ImportForm_new/ComponentSection/ComponentSection.tsx b/src/components/ImportForm_new/ComponentSection/ComponentSection.tsx
index f514e87a5..731f36b25 100644
--- a/src/components/ImportForm_new/ComponentSection/ComponentSection.tsx
+++ b/src/components/ImportForm_new/ComponentSection/ComponentSection.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
import { FormSection, Text, TextContent, TextVariants } from '@patternfly/react-core';
-import { InputField } from 'formik-pf';
+import { InputField } from '../../../shared';
import { SourceSection } from './SourceSection';
export const ComponentSection = () => {
@@ -13,7 +13,7 @@ export const ComponentSection = () => {
-
+
);
};
diff --git a/src/components/ImportForm_new/ComponentSection/GitOptions.tsx b/src/components/ImportForm_new/ComponentSection/GitOptions.tsx
index d76af5a7e..35cb32513 100644
--- a/src/components/ImportForm_new/ComponentSection/GitOptions.tsx
+++ b/src/components/ImportForm_new/ComponentSection/GitOptions.tsx
@@ -1,6 +1,6 @@
import * as React from 'react';
-import { ExpandableSection } from '@patternfly/react-core';
-import { InputField } from 'formik-pf';
+import { ExpandableSection, FormSection, PageSection } from '@patternfly/react-core';
+import { InputField } from '../../../shared';
import HelpPopover from '../../HelpPopover';
const GitOptions: React.FC> = () => {
@@ -9,22 +9,26 @@ const GitOptions: React.FC> = () => {
toggleTextExpanded="Hide advanced Git options"
toggleTextCollapsed="Show advanced Git options"
>
-
+
+
+
-
- }
- />
+
+ }
+ />
+
+
);
};
diff --git a/src/components/ImportForm_new/ComponentSection/SampleSection/SampleCard.tsx b/src/components/ImportForm_new/ComponentSection/SampleSection/SampleCard.tsx
deleted file mode 100644
index 2f280740b..000000000
--- a/src/components/ImportForm_new/ComponentSection/SampleSection/SampleCard.tsx
+++ /dev/null
@@ -1,80 +0,0 @@
-import * as React from 'react';
-import { CatalogItem } from '@openshift/dynamic-plugin-sdk-extensions';
-import {
- Card,
- CardHeader,
- CardTitle,
- CardBody,
- CardFooter,
- Badge,
- ButtonVariant,
- Button,
- Divider,
- TextContent,
-} from '@patternfly/react-core';
-import { ArrowRightIcon } from '@patternfly/react-icons/dist/js/icons/arrow-right-icon';
-import ExternalLink from '../../../../shared/components/links/ExternalLink';
-import { SampleAttrs } from './useDevfileSamples';
-
-type SampleCardProps = {
- sample: CatalogItem;
- onSampleImport: (url: string, name: string) => void;
-};
-
-const SampleCard: React.FC> = ({
- sample,
- onSampleImport,
-}) => {
- const { icon, name, tags, description, attributes } = sample;
-
- const sourceUrl = (attributes as SampleAttrs)?.git?.remotes?.origin;
-
- const badges = tags?.map((tag) => (
-
- {tag}
-
- ));
-
- const handleClick = React.useCallback(() => {
- onSampleImport(sourceUrl, name);
- }, [name, onSampleImport, sourceUrl]);
-
- return (
-
- 0 && {
- actions: { actions: <>{badges}>, hasNoOffset: false, className: undefined },
- })}
- >
- {icon?.url && (
-
- )}
-
- {name}
-
- {description}
-
-
-
-
-
-
-
-
-
- );
-};
-
-export default SampleCard;
diff --git a/src/components/ImportForm_new/ComponentSection/SampleSection/SampleInfoAlert.tsx b/src/components/ImportForm_new/ComponentSection/SampleSection/SampleInfoAlert.tsx
deleted file mode 100644
index 829811112..000000000
--- a/src/components/ImportForm_new/ComponentSection/SampleSection/SampleInfoAlert.tsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import * as React from 'react';
-import { Alert, AlertActionCloseButton } from '@patternfly/react-core';
-
-const SAMPLES_FLOW_INSTRUCTION_KEY = 'samples-flow-instruction';
-
-const SamplesInfoAlert: React.FC> = ({ children }) => {
- const [showAlertInfo, setShowAlertInfo] = React.useState(
- window.localStorage.getItem(SAMPLES_FLOW_INSTRUCTION_KEY) !== 'false',
- );
-
- return showAlertInfo ? (
- {
- setShowAlertInfo(false);
- window.localStorage.setItem(SAMPLES_FLOW_INSTRUCTION_KEY, 'false');
- }}
- />
- }
- >
- {children}
-
- ) : null;
-};
-
-export default SamplesInfoAlert;
diff --git a/src/components/ImportForm_new/ComponentSection/SampleSection/SampleSection.scss b/src/components/ImportForm_new/ComponentSection/SampleSection/SampleSection.scss
deleted file mode 100644
index 1b02dd734..000000000
--- a/src/components/ImportForm_new/ComponentSection/SampleSection/SampleSection.scss
+++ /dev/null
@@ -1,9 +0,0 @@
-.hac-catalog {
- margin: var(--pf-v5-global--spacer--lg);
- --pf-v5-l-gallery--m-gutter--GridGap: var(--pf-v5-global--gutter--md);
-
- &__tile {
- height: 100%;
- color: black;
- }
-}
diff --git a/src/components/ImportForm_new/ComponentSection/SampleSection/SampleSection.tsx b/src/components/ImportForm_new/ComponentSection/SampleSection/SampleSection.tsx
deleted file mode 100644
index e83bd769e..000000000
--- a/src/components/ImportForm_new/ComponentSection/SampleSection/SampleSection.tsx
+++ /dev/null
@@ -1,56 +0,0 @@
-import * as React from 'react';
-import { ExpandableSection, Gallery, GalleryItem } from '@patternfly/react-core';
-import { skeletonTileSelector } from '../../../../shared/components/catalog/utils/skeleton-catalog';
-import { StatusBox } from '../../../../shared/components/status-box/StatusBox';
-import SampleCard from './SampleCard';
-import { useDevfileSamples } from './useDevfileSamples';
-
-import './SampleSection.scss';
-
-type SampleSectionProp = {
- onSampleImport?: (url: string, name: string) => void;
-};
-
-const SampleSection: React.FunctionComponent> = ({
- onSampleImport,
-}) => {
- const [samples, loaded, loadError] = useDevfileSamples();
-
- const filteredSamples = React.useMemo(
- () => (loaded ? samples.filter((item) => !item.attributes.deprecated) : []),
- [samples, loaded],
- );
-
- return (
-
-
- {filteredSamples.length > 0 ? (
-
- {filteredSamples.map((sample) => (
-
-
-
- ))}
-
- ) : null}
-
-
- );
-};
-
-export default SampleSection;
diff --git a/src/components/ImportForm_new/ComponentSection/SampleSection/__tests__/SampleSection.spec.tsx b/src/components/ImportForm_new/ComponentSection/SampleSection/__tests__/SampleSection.spec.tsx
deleted file mode 100644
index f7f3754e5..000000000
--- a/src/components/ImportForm_new/ComponentSection/SampleSection/__tests__/SampleSection.spec.tsx
+++ /dev/null
@@ -1,162 +0,0 @@
-import * as React from 'react';
-import { configure, fireEvent, render, screen, waitFor } from '@testing-library/react';
-import { useFormikContext } from 'formik';
-import { mockCatalogItem } from '../../../../../utils/__data__/mock-devfile-data';
-import SampleSection from '../SampleSection';
-import { useDevfileSamples } from '../useDevfileSamples';
-
-import '@testing-library/jest-dom';
-
-configure({ testIdAttribute: 'data-test' });
-
-jest.mock('../../utils/useDevfileSamples', () => ({
- useDevfileSamples: jest.fn(),
-}));
-
-jest.mock('formik', () => ({
- useFormikContext: jest.fn(),
-}));
-
-jest.mock('react-i18next', () => ({
- useTranslation: jest.fn(() => ({ t: (x) => x })),
-}));
-
-const onSampleImportMock = jest.fn();
-
-const useFormikContextMock = useFormikContext as jest.Mock;
-
-const useDevfileSamplesMock = useDevfileSamples as jest.Mock;
-
-describe('SampleSection', () => {
- it('renders component samples page with a progressbar when samples are loading', async () => {
- useFormikContextMock.mockReturnValue({
- values: { source: { git: {} }, application: { name: '' } },
- setFieldValue: jest.fn(),
- });
- useDevfileSamplesMock.mockReturnValue([[], false, null]);
- render();
- await screen.getByRole('progressbar');
- });
-
- it('renders component samples page with an empty state when no samples are loaded', async () => {
- useFormikContextMock.mockReturnValue({
- values: { source: { git: {} }, application: { name: '' } },
- setFieldValue: jest.fn(),
- });
- useDevfileSamplesMock.mockReturnValue([[], true, null]);
- render();
- await waitFor(() => {
- screen.getByText('No Catalog items found');
- });
- });
-
- it('renders component samples page with nodejs sample tile', async () => {
- useFormikContextMock.mockReturnValue({
- values: { source: { git: {} }, application: { name: '' } },
- setFieldValue: jest.fn(),
- });
- useDevfileSamplesMock.mockReturnValue([[mockCatalogItem[0]], true, null]);
- render();
- await waitFor(() => {
- screen.getByText('Basic Node.js');
- });
- });
-
- it('should call onSampleImport when user clicks on CTA', async () => {
- const setFieldValue = jest.fn();
- useFormikContextMock.mockReturnValue({
- values: { source: { git: { url: 'https://github.com/repo' } }, application: 'test-app' },
- setFieldValue,
- setStatus: jest.fn(),
- });
- useDevfileSamplesMock.mockReturnValue([mockCatalogItem, true, null]);
-
- render();
-
- await waitFor(() => fireEvent.click(screen.getByTestId('import-sample-Basic Node.js')));
-
- await waitFor(() => {
- expect(onSampleImportMock).toHaveBeenLastCalledWith(
- 'https://github.com/nodeshift-starters/devfile-sample.git',
- 'Basic Node.js',
- );
- });
- });
-
- it('should show empty state for filtered samples', async () => {
- const setFieldValue = jest.fn();
- useFormikContextMock.mockReturnValue({
- values: { source: { git: { url: 'https://github.com/repo' } }, application: 'test-app' },
- setFieldValue,
- setStatus: jest.fn(),
- });
- useDevfileSamplesMock.mockReturnValue([mockCatalogItem, true, null]);
-
- render();
-
- await waitFor(() =>
- fireEvent.input(screen.getByPlaceholderText('Filter by keyword...'), {
- target: { value: 'asdf' },
- }),
- );
-
- await waitFor(() => screen.getByText('No results found'));
-
- await waitFor(() => fireEvent.click(screen.getByText('Clear all filters')));
-
- await waitFor(() => screen.getByText('Basic Node.js'));
- await waitFor(() => screen.getByText('Basic Quarkus'));
- });
-
- it('should filter sample items based on input value', async () => {
- const setFieldValue = jest.fn();
- useFormikContextMock.mockReturnValue({
- values: { source: { git: { url: 'https://github.com/repo' } }, application: 'test-app' },
- setFieldValue,
- setStatus: jest.fn(),
- });
- useDevfileSamplesMock.mockReturnValue([mockCatalogItem, true, null]);
-
- render();
-
- await waitFor(() => screen.getByText('Basic Node.js'));
- await waitFor(() => screen.getByText('Basic Quarkus'));
-
- await waitFor(() =>
- fireEvent.input(screen.getByPlaceholderText('Filter by keyword...'), {
- target: { value: 'node' },
- }),
- );
-
- await waitFor(() => screen.getByText('Basic Node.js'));
- await waitFor(() => expect(screen.queryByText('Basic Quarkus')).not.toBeInTheDocument());
- });
-
- it('should filter sample items based on tags', async () => {
- const setFieldValue = jest.fn();
- useFormikContextMock.mockReturnValue({
- values: { source: { git: { url: 'https://github.com/repo' } }, application: 'test-app' },
- setFieldValue,
- setStatus: jest.fn(),
- });
- useDevfileSamplesMock.mockReturnValue([mockCatalogItem, true, null]);
-
- render();
-
- await waitFor(() => screen.getByText('Basic Node.js'));
- await waitFor(() => screen.getByText('Basic Quarkus'));
- await waitFor(() => screen.getByText('Basic Python'));
- await waitFor(() => screen.getByText('Basic Spring Boot'));
-
- await waitFor(() =>
- fireEvent.input(screen.getByPlaceholderText('Filter by keyword...'), {
- target: { value: 'spring' },
- }),
- );
-
- await waitFor(() => screen.getByText('Basic Spring Boot'));
- await waitFor(() => expect(screen.queryByText('Basic Node.js')).not.toBeInTheDocument());
- await waitFor(() => expect(screen.queryByText('Basic Quarkus')).not.toBeInTheDocument());
- await waitFor(() => expect(screen.queryByText('Basic Python')).not.toBeInTheDocument());
- });
-});
diff --git a/src/components/ImportForm_new/ComponentSection/SampleSection/useDevfileSamples.tsx b/src/components/ImportForm_new/ComponentSection/SampleSection/useDevfileSamples.tsx
deleted file mode 100644
index e88b5e35e..000000000
--- a/src/components/ImportForm_new/ComponentSection/SampleSection/useDevfileSamples.tsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import * as React from 'react';
-import { CatalogItem } from '@openshift/dynamic-plugin-sdk-extensions';
-import { getDevfileSamples } from '../../../../utils/devfile-utils';
-
-export type SampleAttrs = {
- projectType: string;
- language: string;
- git: {
- remotes: {
- [remote: string]: string;
- };
- };
- deprecated?: boolean;
-};
-
-export const useDevfileSamples = (): [CatalogItem[], boolean, string] => {
- const [samples, setSamples] = React.useState[]>([]);
- const [loaded, setLoaded] = React.useState(false);
- const [loadError, setLoadError] = React.useState();
-
- React.useEffect(() => {
- let unmounted = false;
- const fetchDevfileSamples = async () => {
- if (unmounted) return;
-
- try {
- const devfileSamples = await getDevfileSamples();
-
- if (devfileSamples) {
- setSamples(devfileSamples);
- setLoaded(true);
- }
- } catch (e) {
- setLoadError(`Failed to load devfile samples: ${e.message}`);
- }
- };
-
- fetchDevfileSamples();
- return () => {
- unmounted = true;
- };
- }, []);
-
- return [samples, loaded, loadError];
-};
diff --git a/src/components/ImportForm_new/ComponentSection/SourceSection.tsx b/src/components/ImportForm_new/ComponentSection/SourceSection.tsx
index bf32dec30..7dd124b39 100644
--- a/src/components/ImportForm_new/ComponentSection/SourceSection.tsx
+++ b/src/components/ImportForm_new/ComponentSection/SourceSection.tsx
@@ -1,22 +1,27 @@
import * as React from 'react';
-import { InputField } from 'formik-pf';
+import { ValidatedOptions } from '@patternfly/react-core';
+import { useField } from 'formik';
+import { InputField } from '../../../shared';
import GitOptions from './GitOptions';
export const SourceSection = () => {
+ const [, { touched, error }] = useField('source.git.url');
+ const validated = touched
+ ? touched && !error
+ ? ValidatedOptions.success
+ : ValidatedOptions.error
+ : ValidatedOptions.default;
return (
<>
-
+ {validated === ValidatedOptions.success ? : null}
>
);
};
diff --git a/src/components/ImportForm_new/GitImportActions.scss b/src/components/ImportForm_new/GitImportActions.scss
index 08fec9596..809968f31 100644
--- a/src/components/ImportForm_new/GitImportActions.scss
+++ b/src/components/ImportForm_new/GitImportActions.scss
@@ -1,6 +1,6 @@
.git-import-actions {
&__sticky {
- position: fixed;
+ position: sticky;
bottom: 0;
width: 100%;
padding-top: var(--pf-v5-global--spacer--lg);
diff --git a/src/components/ImportForm_new/GitImportForm.tsx b/src/components/ImportForm_new/GitImportForm.tsx
index cc0fdc84c..9ffdb9eb6 100644
--- a/src/components/ImportForm_new/GitImportForm.tsx
+++ b/src/components/ImportForm_new/GitImportForm.tsx
@@ -5,7 +5,9 @@ import ApplicationSection from './ApplicationSection/ApplicationSection';
import { ComponentSection } from './ComponentSection/ComponentSection';
import GitImportActions from './GitImportActions';
import { PipelineSection } from './PipelineSection/PipelineSection';
+import SecretSection from './SecretSection/SecretSection';
import { ImportFormValues } from './type';
+import { formValidationSchema } from './validation.utils';
export const GitImportForm: React.FC<{ applicationName: string }> = ({ applicationName }) => {
const initialValues: ImportFormValues = {
@@ -21,6 +23,8 @@ export const GitImportForm: React.FC<{ applicationName: string }> = ({ applicati
pipeline: {
name: '',
},
+ importSecrets: [],
+ newSecrets: [],
};
const handleSubmit = React.useCallback(() => {}, []);
@@ -31,6 +35,7 @@ export const GitImportForm: React.FC<{ applicationName: string }> = ({ applicati
initialValues={initialValues}
onSubmit={handleSubmit}
onReset={handleReset}
+ validationSchema={formValidationSchema}
>
{(formikProps) => {
return (
@@ -45,6 +50,7 @@ export const GitImportForm: React.FC<{ applicationName: string }> = ({ applicati
<>
+
>
) : null}
diff --git a/src/components/ImportForm_new/PipelineSection/PipelineSection.tsx b/src/components/ImportForm_new/PipelineSection/PipelineSection.tsx
index aad0c1c6e..a8e804876 100644
--- a/src/components/ImportForm_new/PipelineSection/PipelineSection.tsx
+++ b/src/components/ImportForm_new/PipelineSection/PipelineSection.tsx
@@ -1,24 +1,14 @@
import * as React from 'react';
-import { useFormikContext } from 'formik';
import { DropdownField } from '../../../shared';
-import { ImportFormValues } from '../type';
import { usePipelineTemplates } from './usePipelineTemplate';
export const PipelineSection: React.FunctionComponent = () => {
const [template, loaded] = usePipelineTemplates();
- const { values, setFieldValue } = useFormikContext();
const dropdownItems = React.useMemo(() => {
return loaded ? template.pipelines.map((t) => ({ key: t.name, value: t.name })) : [];
}, [loaded, template?.pipelines]);
- React.useEffect(() => {
- if (loaded && values.pipeline.name) {
- const bundle = template.pipelines.find((t) => t.name === values.pipeline.name)?.bundle;
- setFieldValue('pipeline.bundle', bundle);
- }
- }, [loaded, setFieldValue, template?.pipelines, values.pipeline.name]);
-
return (
{
+ const [canCreateSecret] = useAccessReviewForModels(accessReviewResources);
+ const showModal = useModalLauncher();
+ const { values, setFieldValue } = useFormikContext();
+ const { namespace } = useWorkspaceInfo();
+
+ const [secrets, secretsLoaded] = useSecrets(namespace);
+
+ const partnerTaskNames = getSupportedPartnerTaskSecrets().map(({ label }) => label);
+ const partnerTaskSecrets: string[] =
+ secrets && secretsLoaded
+ ? secrets
+ ?.filter((rs) => partnerTaskNames.includes(rs.metadata.name))
+ ?.map((s) => s.metadata.name) || []
+ : [];
+
+ const onSubmit = React.useCallback(
+ (secretValue: any) => {
+ const allSecrets = [...values.importSecrets, secretValue];
+ const secretNames = [...values.newSecrets, secretValue.secretName];
+ setFieldValue('importSecrets', allSecrets);
+ setFieldValue('newSecrets', secretNames);
+ },
+ [values, setFieldValue],
+ );
+
+ return (
+
+
+ setFieldValue(
+ 'importSecrets',
+ values.importSecrets.filter((vs) => v.includes(vs.secretName)),
+ )
+ }
+ >
+ {(props) => {
+ return (
+
+
+
+
+ {props.removeButton}
+
+ );
+ }}
+
+ }
+ onClick={() =>
+ showModal(SecretModalLauncher([...partnerTaskSecrets, ...values.newSecrets], onSubmit))
+ }
+ isDisabled={!canCreateSecret}
+ tooltip="You don't have access to add a secret"
+ >
+ Add secret
+
+
+ );
+};
+export default SecretSection;
diff --git a/src/components/ImportForm_new/SecretSection/__tests__/SecretSection.spec.tsx b/src/components/ImportForm_new/SecretSection/__tests__/SecretSection.spec.tsx
new file mode 100644
index 000000000..4728cda72
--- /dev/null
+++ b/src/components/ImportForm_new/SecretSection/__tests__/SecretSection.spec.tsx
@@ -0,0 +1,66 @@
+import * as React from 'react';
+import '@testing-library/jest-dom';
+import { useK8sWatchResource } from '@openshift/dynamic-plugin-sdk-utils';
+import { screen, fireEvent, act, waitFor } from '@testing-library/react';
+import { useAccessReviewForModels } from '../../../../utils/rbac';
+import { formikRenderer } from '../../../../utils/test-utils';
+import SecretSection from '../SecretSection';
+
+jest.mock('@openshift/dynamic-plugin-sdk-utils', () => ({
+ useK8sWatchResource: jest.fn(),
+ getActiveWorkspace: jest.fn(() => 'test-ws'),
+}));
+
+jest.mock('../../../../utils/rbac', () => ({
+ useAccessReviewForModels: jest.fn(),
+}));
+
+const watchResourceMock = useK8sWatchResource as jest.Mock;
+const accessReviewMock = useAccessReviewForModels as jest.Mock;
+
+describe('SecretSection', () => {
+ beforeEach(() => {
+ watchResourceMock.mockReturnValue([[], true]);
+ accessReviewMock.mockReturnValue([true, true]);
+ });
+
+ it('should render secret section', () => {
+ formikRenderer(, {});
+
+ screen.getByText('Secrets');
+ screen.getByTestId('add-secret-button');
+ });
+
+ it('should render added secrets in removable lists', () => {
+ formikRenderer(, { newSecrets: ['secret-one', 'secret-two'] });
+
+ expect(screen.queryByDisplayValue('secret-one')).toBeInTheDocument();
+ expect(screen.queryByDisplayValue('secret-two')).toBeInTheDocument();
+ });
+
+ it('should be able to remove the newly added secrets from the list', async () => {
+ formikRenderer(, {
+ importSecrets: [],
+ newSecrets: ['secret-one', 'secret-two'],
+ });
+
+ expect(screen.queryByDisplayValue('secret-one')).toBeInTheDocument();
+ expect(screen.queryByDisplayValue('secret-two')).toBeInTheDocument();
+ act(() => {
+ fireEvent.click(screen.getByTestId('newSecrets-1-remove-button'));
+ });
+
+ await waitFor(() => {
+ expect(screen.queryByDisplayValue('secret-two')).not.toBeInTheDocument();
+ });
+ });
+
+ it('should not allow adding secrets if user does not have create access', () => {
+ accessReviewMock.mockReturnValue([false, true]);
+ formikRenderer(, {});
+ expect(screen.getByRole('button', { name: 'Add secret' })).toHaveAttribute(
+ 'aria-disabled',
+ 'true',
+ );
+ });
+});
diff --git a/src/components/ImportForm_new/type.ts b/src/components/ImportForm_new/type.ts
index d2dc52503..7a5d7047a 100644
--- a/src/components/ImportForm_new/type.ts
+++ b/src/components/ImportForm_new/type.ts
@@ -14,4 +14,16 @@ export type ImportFormValues = {
name: string;
bundle?: string;
};
+ importSecrets?: ImportSecret[];
+ newSecrets?: string[];
+};
+
+export type ImportSecret = {
+ secretName: string;
+ type: string;
+ keyValues: {
+ key: string;
+ value: string;
+ readOnlyKey?: boolean;
+ }[];
};
diff --git a/src/components/ImportForm_new/validation.utils.ts b/src/components/ImportForm_new/validation.utils.ts
index cb4a87c39..0cf4da537 100644
--- a/src/components/ImportForm_new/validation.utils.ts
+++ b/src/components/ImportForm_new/validation.utils.ts
@@ -1,4 +1,5 @@
import * as yup from 'yup';
+import { ImportFormValues } from './type';
const gitUrlRegex =
/^((((ssh|git|https?:?):\/\/:?)(([^\s@]+@|[^@]:?)[-\w.]+(:\d\d+:?)?(\/[-\w.~/?[\]!$&'()*+,;=:@%]*:?)?:?))|([^\s@]+@[-\w.]+:[-\w.~/?[\]!$&'()*+,;=:@%]*?:?))$/;
@@ -10,12 +11,7 @@ const RESOURCE_NAME_REGEX_MSG =
const MAX_RESOURCE_NAME_LENGTH = 63;
const RESOURCE_NAME_LENGTH_ERROR_MSG = `Must be no more than ${MAX_RESOURCE_NAME_LENGTH} characters.`;
-export const formValidationSchema = yup.object({
- application: yup
- .string()
- .matches(resourceNameRegex, RESOURCE_NAME_REGEX_MSG)
- .max(MAX_RESOURCE_NAME_LENGTH, RESOURCE_NAME_LENGTH_ERROR_MSG)
- .required('Required'),
+const componentSchema = yup.object({
source: yup.object({
git: yup.object({
url: yup
@@ -36,3 +32,17 @@ export const formValidationSchema = yup.object({
.required('Required'),
pipeline: yup.object({ name: yup.string().required('Required') }),
});
+
+export const formValidationSchema = yup.mixed().test(
+ (values: ImportFormValues) =>
+ yup
+ .object({
+ application: yup
+ .string()
+ .matches(resourceNameRegex, RESOURCE_NAME_REGEX_MSG)
+ .max(MAX_RESOURCE_NAME_LENGTH, RESOURCE_NAME_LENGTH_ERROR_MSG)
+ .required('Required'),
+ })
+ .concat(values.showComponent ? componentSchema : undefined)
+ .validate(values, { abortEarly: false }) as any,
+);
diff --git a/src/hooks/useSecrets.ts b/src/hooks/useSecrets.ts
new file mode 100644
index 000000000..031831e4c
--- /dev/null
+++ b/src/hooks/useSecrets.ts
@@ -0,0 +1,17 @@
+import React from 'react';
+import { useK8sWatchResource } from '@openshift/dynamic-plugin-sdk-utils';
+import { SecretGroupVersionKind } from '../models';
+import { SecretKind } from '../types';
+
+export const useSecrets = (namespace: string): [SecretKind[], boolean, unknown] => {
+ const [secrets, loaded, error] = useK8sWatchResource({
+ groupVersionKind: SecretGroupVersionKind,
+ namespace,
+ isList: true,
+ });
+
+ return React.useMemo(
+ () => [secrets.filter((rs) => !rs.metadata.deletionTimestamp), loaded, error],
+ [secrets, loaded, error],
+ );
+};