Skip to content

Commit

Permalink
feat: alternative solution for list existing secret
Browse files Browse the repository at this point in the history
  • Loading branch information
JoaoPedroPP committed Oct 28, 2024
1 parent bc6a3d4 commit 766ee2e
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 30 deletions.
18 changes: 13 additions & 5 deletions src/components/ImportForm/SecretSection/SecretSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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' }];
Expand All @@ -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(
Expand Down
36 changes: 27 additions & 9 deletions src/components/Secrets/SecretForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 & {
Expand All @@ -19,14 +19,29 @@ type SecretFormProps = RawComponentProps & {

const SecretForm: React.FC<React.PropsWithChildren<SecretFormProps>> = ({ existingSecrets }) => {
const { values, setFieldValue } = useFormikContext<SecretFormValues>();
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);
Expand Down Expand Up @@ -80,15 +95,18 @@ const SecretForm: React.FC<React.PropsWithChildren<SecretFormProps>> = ({ 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);
Expand Down
4 changes: 2 additions & 2 deletions src/components/Secrets/__tests___/SecretModal.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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(
<SecretModal
Expand All @@ -104,7 +104,7 @@ describe('SecretForm', () => {
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 () => {
Expand Down
2 changes: 1 addition & 1 deletion src/components/Secrets/__tests___/secret-utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 },
]);
});
});
Expand Down
22 changes: 11 additions & 11 deletions src/components/Secrets/utils/secret-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export type PartnerTask = {
key: string;
value: string;
readOnlyKey?: boolean;
readOnlyValue?: boolean;
}[];
};

Expand All @@ -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 }],
},
};

Expand All @@ -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 : [];
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`);
Expand Down
4 changes: 2 additions & 2 deletions src/utils/validation-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down

0 comments on commit 766ee2e

Please sign in to comment.