Skip to content

Commit

Permalink
show/hide connector values (#3775)
Browse files Browse the repository at this point in the history
  • Loading branch information
TheAndrewJackson authored Jul 18, 2023
1 parent e9aeaf0 commit aa76b4b
Show file tree
Hide file tree
Showing 28 changed files with 522 additions and 193 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ The types of changes are:
- Bumped supported Python versions to `3.10.12`, `3.9.17`, and `3.8.17` [#3733](https://github.com/ethyca/fides/pull/3733)
- Logging Updates [#3758](https://github.com/ethyca/fides/pull/3758)
- Add polyfill service to fides-js route [#3759](https://github.com/ethyca/fides/pull/3759)
- Show/hide integration values [#3775](https://github.com/ethyca/fides/pull/3775)
- Sort system cards alphabetically by name on "View systems" page [#3781](https://github.com/ethyca/fides/pull/3781)

### Removed
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,14 @@ import {
useCreateSassConnectionConfigMutation,
useDeleteDatastoreConnectionMutation,
useGetConnectionConfigDatasetConfigsQuery,
useUpdateDatastoreConnectionSecretsMutation,
} from "datastore-connections/datastore-connection.slice";
import { useDatasetConfigField } from "datastore-connections/system_portal_config/forms/fields/DatasetConfigField/DatasetConfigField";
import {
CreateSaasConnectionConfigRequest,
CreateSaasConnectionConfigResponse,
DatastoreConnectionSecretsRequest,
DatastoreConnectionSecretsResponse,
} from "datastore-connections/types";
import { useState } from "react";
import { useMemo, useState } from "react";

import { useAppDispatch, useAppSelector } from "~/app/hooks";
import { DEFAULT_TOAST_PARAMS } from "~/features/common/toast";
Expand All @@ -25,9 +23,11 @@ import { formatKey } from "~/features/datastore-connections/system_portal_config
import TestConnectionMessage from "~/features/datastore-connections/system_portal_config/TestConnectionMessage";
import TestData from "~/features/datastore-connections/TestData";
import {
ConnectionConfigSecretsRequest,
selectActiveSystem,
setActiveSystem,
usePatchSystemConnectionConfigsMutation,
usePatchSystemConnectionSecretsMutation,
} from "~/features/system/system.slice";
import {
AccessLevel,
Expand Down Expand Up @@ -69,7 +69,7 @@ const createSaasConnector = async (
};

Object.entries(secretsSchema!.properties).forEach((key) => {
params.connectionConfig.secrets[key[0]] = values[key[0]];
params.connectionConfig.secrets[key[0]] = values.secrets[key[0]];
});
return (await createSaasConnectorFunc(
params
Expand Down Expand Up @@ -124,18 +124,32 @@ export const patchConnectionConfig = async (
const upsertConnectionConfigSecrets = async (
values: ConnectionConfigFormValues,
secretsSchema: ConnectionTypeSecretSchemaReponse,
connectionConfigFidesKey: string,
upsertFunc: any
systemFidesKey: string,
originalSecrets: Record<string, string>,
patchFunc: any
) => {
const params2: DatastoreConnectionSecretsRequest = {
connection_key: connectionConfigFidesKey,
const params2: ConnectionConfigSecretsRequest = {
systemFidesKey,
secrets: {},
};
Object.entries(secretsSchema!.properties).forEach((key) => {
params2.secrets[key[0]] = values[key[0]];
/*
* Only patch secrets that have changed. Otherwise, sensitive secrets
* would get overwritten with "**********" strings
*/
if (
!(key[0] in originalSecrets) ||
values.secrets[key[0]] !== originalSecrets[key[0]]
) {
params2.secrets[key[0]] = values.secrets[key[0]];
}
});

return (await upsertFunc(
if (Object.keys(params2.secrets).length === 0) {
return Promise.resolve();
}

return (await patchFunc(
params2
).unwrap()) as DatastoreConnectionSecretsResponse;
};
Expand Down Expand Up @@ -180,15 +194,19 @@ export const useConnectorForm = ({
});

const [createSassConnectionConfig] = useCreateSassConnectionConfigMutation();
const [updateDatastoreConnectionSecrets] =
useUpdateDatastoreConnectionSecretsMutation();
const [updateSystemConnectionSecrets] =
usePatchSystemConnectionSecretsMutation();
const [patchDatastoreConnection] = usePatchSystemConnectionConfigsMutation();
const [deleteDatastoreConnection, deleteDatastoreConnectionResult] =
useDeleteDatastoreConnectionMutation();
const { data: allDatasetConfigs } = useGetConnectionConfigDatasetConfigsQuery(
connectionConfig?.key || ""
);

const originalSecrets = useMemo(
() => (connectionConfig ? { ...connectionConfig.secrets } : {}),
[connectionConfig]
);
const activeSystem = useAppSelector(selectActiveSystem) as SystemResponse;

const handleDelete = async (id: string) => {
Expand Down Expand Up @@ -248,8 +266,9 @@ export const useConnectorForm = ({
await upsertConnectionConfigSecrets(
secretsPayload,
secretsSchema!,
payload.succeeded[0].key,
updateDatastoreConnectionSecrets
systemFidesKey,
originalSecrets,
updateSystemConnectionSecrets
);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ const ConnectorParametersForm: React.FC<ConnectorParametersFormProps> = ({

const validateField = (label: string, value: string, type?: string) => {
let error;
if (typeof value === "undefined" || value === "") {
if (typeof value === "undefined" || value === "" || value === undefined) {
error = `${label} is required`;
}
if (type === FIDES_DATASET_REFERENCE) {
Expand Down Expand Up @@ -146,67 +146,78 @@ const ConnectorParametersForm: React.FC<ConnectorParametersFormProps> = ({
item: ConnectionTypeSecretSchemaProperty
): JSX.Element => (
<Field
id={key}
name={key}
key={key}
id={`secrets.${key}`}
name={`secrets.${key}`}
key={`secrets.${key}`}
validate={
isRequiredSecretValue(key) || item.type === "integer"
? (value: string) =>
validateField(item.title, value, item.allOf?.[0].$ref)
: false
}
>
{({ field, form }: { field: FieldInputProps<string>; form: any }) => (
<FormControl
display="flex"
isRequired={isRequiredSecretValue(key)}
isInvalid={form.errors[key] && form.touched[key]}
>
{getFormLabel(key, item.title)}
<VStack align="flex-start" w="inherit">
{item.type !== "integer" && (
<Input
{...field}
placeholder={getPlaceholder(item)}
autoComplete="off"
color="gray.700"
size="sm"
/>
)}
{item.type === "integer" && (
<NumberInput
allowMouseWheel
color="gray.700"
defaultValue={0}
min={0}
size="sm"
>
<NumberInputField {...field} autoComplete="off" />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
)}
<FormErrorMessage>{form.errors[key]}</FormErrorMessage>
</VStack>
<Tooltip
aria-label={item.description}
hasArrow
label={item.description}
placement="right-start"
openDelay={500}
{({ field, form }: { field: FieldInputProps<string>; form: any }) => {
const error = form.errors.secrets && form.errors.secrets[key];
const touch = form.touched.secrets ? form.touched.secrets[key] : false;

return (
<FormControl
display="flex"
isRequired={isRequiredSecretValue(key)}
isInvalid={error && touch}
>
<Flex
alignItems="center"
h="32px"
visibility={item.description ? "visible" : "hidden"}
{getFormLabel(key, item.title)}
<VStack align="flex-start" w="inherit">
{item.type !== "integer" && (
<Input
{...field}
placeholder={getPlaceholder(item)}
autoComplete="off"
color="gray.700"
size="sm"
/>
)}
{item.type === "integer" && (
<NumberInput
allowMouseWheel
color="gray.700"
onChange={(value) => {
form.setFieldValue(field.name, value);
}}
defaultValue={field.value ?? 0}
min={0}
size="sm"
>
<NumberInputField {...field} autoComplete="off" />
<NumberInputStepper>
<NumberIncrementStepper />
<NumberDecrementStepper />
</NumberInputStepper>
</NumberInput>
)}
<FormErrorMessage>{error}</FormErrorMessage>
</VStack>
<Tooltip
aria-label={item.description}
hasArrow
label={item.description}
placement="right-start"
openDelay={500}
>
<CircleHelpIcon marginLeft="8px" _hover={{ cursor: "pointer" }} />
</Flex>
</Tooltip>
</FormControl>
)}
<Flex
alignItems="center"
h="32px"
visibility={item.description ? "visible" : "hidden"}
>
<CircleHelpIcon
marginLeft="8px"
_hover={{ cursor: "pointer" }}
/>
</Flex>
</Tooltip>
</FormControl>
);
}}
</Field>
);

Expand All @@ -219,6 +230,9 @@ const ConnectorParametersForm: React.FC<ConnectorParametersFormProps> = ({
connectionConfig.connection_type === ConnectionType.SAAS
? (connectionConfig.saas_config?.fides_key as string)
: connectionConfig.key;
// @ts-ignore
initialValues.secrets = connectionConfig.secrets;
return initialValues;
}
return fillInDefaults(initialValues, secretsSchema);
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,16 @@ export const fillInDefaults = (
Object.entries(connectionSchema.properties).forEach((key) => {
const [name, schema] = key;

if (!("secrets" in filledInValues)) {
filledInValues.secrets = {};
}

if (schema.type === "integer") {
filledInValues[name] = schema.default ? Number(schema.default) : 0;
filledInValues.secrets[name] = schema.default
? Number(schema.default)
: 0;
} else {
filledInValues[name] = schema.default ?? "";
filledInValues.secrets[name] = schema.default ?? "";
}
});
}
Expand Down
21 changes: 20 additions & 1 deletion clients/admin-ui/src/features/system/system.slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
ConnectionConfigurationResponse,
System,
SystemResponse,
TestStatusMessage,
} from "~/types/api";

interface SystemDeleteResponse {
Expand All @@ -20,6 +21,13 @@ interface UpsertResponse {
updated: number;
}

export type ConnectionConfigSecretsRequest = {
systemFidesKey: string;
secrets: {
[key: string]: any;
};
};

const systemApi = baseApi.injectEndpoints({
endpoints: (build) => ({
getAllSystems: build.query<SystemResponse[], void>({
Expand Down Expand Up @@ -98,7 +106,17 @@ const systemApi = baseApi.injectEndpoints({
}),
invalidatesTags: ["Datamap", "System", "Datastore Connection"],
}),

patchSystemConnectionSecrets: build.mutation<
TestStatusMessage,
ConnectionConfigSecretsRequest
>({
query: ({ secrets, systemFidesKey }) => ({
url: `/system/${systemFidesKey}/connection/secrets?verify=false`,
method: "PATCH",
body: secrets,
}),
invalidatesTags: () => ["Datastore Connection"],
}),
getSystemConnectionConfigs: build.query<
ConnectionConfigurationResponse[],
string
Expand All @@ -120,6 +138,7 @@ export const {
useUpsertSystemsMutation,
usePatchSystemConnectionConfigsMutation,
useGetSystemConnectionConfigsQuery,
usePatchSystemConnectionSecretsMutation,
} = systemApi;

export interface State {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ export type ConnectionConfigurationResponse = {
last_test_timestamp?: string;
last_test_succeeded?: boolean;
saas_config?: SaaSConfigBase;
secrets?: object;
};
Loading

0 comments on commit aa76b4b

Please sign in to comment.