From 766ee2ea8631b6006e874640b401ced2615f0849 Mon Sep 17 00:00:00 2001 From: Joao Pedro Poloni Ponce Date: Mon, 28 Oct 2024 14:53:19 -0300 Subject: [PATCH] feat: alternative solution for list existing secret --- .../SecretSection/SecretSection.tsx | 18 +++++++--- src/components/Secrets/SecretForm.tsx | 36 ++++++++++++++----- .../Secrets/__tests___/SecretModal.spec.tsx | 4 +-- .../Secrets/__tests___/secret-utils.spec.ts | 2 +- src/components/Secrets/utils/secret-utils.ts | 22 ++++++------ .../KeyValueFileInputField.tsx | 1 + src/utils/validation-utils.ts | 4 +-- 7 files changed, 57 insertions(+), 30 deletions(-) diff --git a/src/components/ImportForm/SecretSection/SecretSection.tsx b/src/components/ImportForm/SecretSection/SecretSection.tsx index a8988bd50..96f8325a4 100644 --- a/src/components/ImportForm/SecretSection/SecretSection.tsx +++ b/src/components/ImportForm/SecretSection/SecretSection.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { TextInputTypes, GridItem, Grid, FormSection } from '@patternfly/react-core'; import { PlusCircleIcon } from '@patternfly/react-icons/dist/js/icons/plus-circle-icon'; import { useFormikContext } from 'formik'; +import { Base64 } from 'js-base64'; import { useSecrets } from '../../../hooks/useSecrets'; import { SecretModel } from '../../../models'; import { InputField, TextColumnField } from '../../../shared'; @@ -11,7 +12,6 @@ import { useWorkspaceInfo } from '../../../utils/workspace-context-utils'; import { ButtonWithAccessTooltip } from '../../ButtonWithAccessTooltip'; import { useModalLauncher } from '../../modal/ModalProvider'; import { SecretModalLauncher } from '../../Secrets/SecretModalLauncher'; -import { getSupportedPartnerTaskSecrets } from '../../Secrets/utils/secret-utils'; import { ImportFormValues } from '../type'; const accessReviewResources: AccessReviewResources = [{ model: SecretModel, verb: 'create' }]; @@ -24,12 +24,20 @@ const SecretSection = () => { 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) || [] + ? secrets?.map((secret) => ({ + type: secret.type, + name: secret.metadata.name, + providerUrl: '', + tokenKeyName: secret.metadata.name, + keyValuePairs: Object.keys(secret.data).map((key) => ({ + key, + value: Base64.decode(secret.data[key]), + readOnlyKey: true, + readOnlyValue: true, + })), + })) || [] : []; const onSubmit = React.useCallback( diff --git a/src/components/Secrets/SecretForm.tsx b/src/components/Secrets/SecretForm.tsx index 1024f6e4f..66b2884a0 100644 --- a/src/components/Secrets/SecretForm.tsx +++ b/src/components/Secrets/SecretForm.tsx @@ -4,13 +4,13 @@ import { SelectVariant } from '@patternfly/react-core/deprecated'; import { useFormikContext } from 'formik'; import { DropdownItemObject, SelectInputField } from '../../shared'; import KeyValueFileInputField from '../../shared/components/formik-fields/key-value-file-input-field/KeyValueFileInputField'; -import { SecretFormValues, SecretTypeDropdownLabel } from '../../types'; +import { SecretFormValues, SecretTypeDropdownLabel, K8sSecretType } from '../../types'; import { RawComponentProps } from '../modal/createModalLauncher'; import SecretTypeSelector from './SecretTypeSelector'; import { + supportedPartnerTasksSecrets, getSupportedPartnerTaskKeyValuePairs, isPartnerTask, - getSupportedPartnerTaskSecrets, } from './utils/secret-utils'; type SecretFormProps = RawComponentProps & { @@ -19,14 +19,29 @@ type SecretFormProps = RawComponentProps & { const SecretForm: React.FC> = ({ existingSecrets }) => { const { values, setFieldValue } = useFormikContext(); + const currentTypeRef = React.useRef(values.type); const defaultKeyValues = [{ key: '', value: '', readOnlyKey: false }]; const defaultImageKeyValues = [{ key: '.dockerconfigjson', value: '', readOnlyKey: true }]; - const initialOptions = getSupportedPartnerTaskSecrets().filter( - (secret) => !existingSecrets.includes(secret.value), - ); + const initialOptions = existingSecrets + .filter((secret) => secret.type === K8sSecretType[currentTypeRef.current]) + .concat( + currentTypeRef.current === SecretTypeDropdownLabel.opaque && + existingSecrets.find((s) => s.name === 'snyk-secret') === undefined + ? [supportedPartnerTasksSecrets.snyk] + : [], + ) + .map((secret) => ({ value: secret.name, lable: secret.name })); + const initialOptionsValues = existingSecrets + .filter((secret) => secret.type === K8sSecretType[currentTypeRef.current]) + .reduce( + (dictOfSecrets, secret) => { + dictOfSecrets[secret.name] = secret; + return dictOfSecrets; + }, + { 'snyk-secret': supportedPartnerTasksSecrets.snyk }, + ); const [options, setOptions] = React.useState(initialOptions); - const currentTypeRef = React.useRef(values.type); const clearKeyValues = () => { const newKeyValues = values.keyValues.filter((kv) => !kv.readOnlyKey); @@ -80,15 +95,18 @@ const SecretForm: React.FC> = ({ existi toggleId="secret-name-toggle" toggleAriaLabel="secret-name-dropdown" onClear={() => { - if (currentTypeRef.current !== values.type || isPartnerTask(values.secretName)) { + if ( + currentTypeRef.current !== values.type || + isPartnerTask(values.secretName, initialOptionsValues) + ) { clearKeyValues(); } }} onSelect={(e, value) => { - if (isPartnerTask(value)) { + if (isPartnerTask(value, initialOptionsValues)) { setFieldValue('keyValues', [ ...values.keyValues.filter((kv) => !kv.readOnlyKey && (!!kv.key || !!kv.value)), - ...getSupportedPartnerTaskKeyValuePairs(value), + ...getSupportedPartnerTaskKeyValuePairs(value, initialOptionsValues), ]); } setFieldValue('secretName', value); diff --git a/src/components/Secrets/__tests___/SecretModal.spec.tsx b/src/components/Secrets/__tests___/SecretModal.spec.tsx index 7ddcf7587..5add76f45 100644 --- a/src/components/Secrets/__tests___/SecretModal.spec.tsx +++ b/src/components/Secrets/__tests___/SecretModal.spec.tsx @@ -88,7 +88,7 @@ describe('SecretForm', () => { }); }); - it('should not show the secrets in the select dropdown if it is already existing', async () => { + it('should show the secrets in the select dropdown if it is already existing', async () => { const onClose = jest.fn(); formikRenderer( { const modal = screen.queryByTestId('build-secret-modal'); fireEvent.click(modal.querySelector('#secret-name-toggle-select-typeahead')); }); - expect(screen.queryByText('snyk-secret')).not.toBeInTheDocument(); + expect(screen.queryByText('snyk-secret')).toBeInTheDocument(); }); it('should remove the selected value with clearn button is clicked', async () => { diff --git a/src/components/Secrets/__tests___/secret-utils.spec.ts b/src/components/Secrets/__tests___/secret-utils.spec.ts index b00146ed1..516036865 100644 --- a/src/components/Secrets/__tests___/secret-utils.spec.ts +++ b/src/components/Secrets/__tests___/secret-utils.spec.ts @@ -42,7 +42,7 @@ describe('getSupportedPartnerTaskKeyValuePairs', () => { it('should return snyk secret values ', () => { expect(getSupportedPartnerTaskKeyValuePairs('snyk-secret')).toEqual([ - { key: 'snyk_token', readOnlyKey: true, value: '' }, + { key: 'snyk_token', readOnlyKey: true, value: '', readOnlyValue: false }, ]); }); }); diff --git a/src/components/Secrets/utils/secret-utils.ts b/src/components/Secrets/utils/secret-utils.ts index be6e4b823..a0faa8c66 100644 --- a/src/components/Secrets/utils/secret-utils.ts +++ b/src/components/Secrets/utils/secret-utils.ts @@ -27,6 +27,7 @@ export type PartnerTask = { key: string; value: string; readOnlyKey?: boolean; + readOnlyValue?: boolean; }[]; }; @@ -36,7 +37,7 @@ export const supportedPartnerTasksSecrets: { [key: string]: PartnerTask } = { name: 'snyk-secret', providerUrl: 'https://snyk.io/', tokenKeyName: 'snyk_token', - keyValuePairs: [{ key: 'snyk_token', value: '', readOnlyKey: true }], + keyValuePairs: [{ key: 'snyk_token', value: '', readOnlyKey: true, readOnlyValue: false }], }, }; @@ -46,19 +47,18 @@ export const getSupportedPartnerTaskSecrets = () => { value: secret.name, })); }; -export const isPartnerTaskAvailable = (type: string) => - !!Object.values(supportedPartnerTasksSecrets).find( - (secret) => secret.type === K8sSecretType[type], - ); +export const isPartnerTaskAvailable = (type: string, arr = supportedPartnerTasksSecrets) => + !!Object.values(arr).find((secret) => secret.type === K8sSecretType[type]); -export const isPartnerTask = (secretName: string) => { - return !!Object.values(supportedPartnerTasksSecrets).find((secret) => secret.name === secretName); +export const isPartnerTask = (secretName: string, arr = supportedPartnerTasksSecrets) => { + return !!Object.values(arr).find((secret) => secret.name === secretName); }; -export const getSupportedPartnerTaskKeyValuePairs = (secretName?: string) => { - const partnerTask = Object.values(supportedPartnerTasksSecrets).find( - (secret) => secret.name === secretName, - ); +export const getSupportedPartnerTaskKeyValuePairs = ( + secretName?: string, + arr = supportedPartnerTasksSecrets, +) => { + const partnerTask = Object.values(arr).find((secret) => secret.name === secretName); return partnerTask ? partnerTask.keyValuePairs : []; }; diff --git a/src/shared/components/formik-fields/key-value-file-input-field/KeyValueFileInputField.tsx b/src/shared/components/formik-fields/key-value-file-input-field/KeyValueFileInputField.tsx index d79e84c3c..7ea9a53e3 100644 --- a/src/shared/components/formik-fields/key-value-file-input-field/KeyValueFileInputField.tsx +++ b/src/shared/components/formik-fields/key-value-file-input-field/KeyValueFileInputField.tsx @@ -94,6 +94,7 @@ const KeyValueFileInputField: React.FC< label="Value" name={`${name}.${idx.toString()}.value`} filenamePlaceholder="Drag a file here or upload one" + isDisabled={v.readOnlyValue ?? v.readOnlyKey} onDataChange={(ev, data: string) => { setFieldValue(`${name}.${idx.toString()}.value`, data); onChange && onChange(data, `${name}.${idx.toString()}.value`); diff --git a/src/utils/validation-utils.ts b/src/utils/validation-utils.ts index c8544fd5b..8d6ff2ee6 100644 --- a/src/utils/validation-utils.ts +++ b/src/utils/validation-utils.ts @@ -24,8 +24,8 @@ export const SecretFromSchema = yup.object({ secretName: resourceNameYupValidation.test( 'existing-secret-test', 'Secret already exists', - (value, { parent: { existingSecrets } }) => { - return !existingSecrets.includes(value); + (value) => { + return value !== undefined; }, ), keyValues: yup.array().of(