Skip to content

Commit 6a9f14c

Browse files
feat: data steward monitor config [ENG-2267] (#7217)
1 parent 495a3ea commit 6a9f14c

File tree

15 files changed

+130
-56
lines changed

15 files changed

+130
-56
lines changed

clients/admin-ui/src/features/data-discovery-and-detection/action-center/action-center.slice.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import {
88
ConsentStatus,
99
DiffStatus,
1010
MonitorConfig,
11-
MonitorTaskInProgressResponse,
1211
Page_ConsentBreakdown_,
1312
Page_StagedResourceAPIResponse_,
1413
Page_SystemStagedResourcesAggregateRecord_,
@@ -23,6 +22,7 @@ import { DatastoreMonitorResourcesDynamicFilters } from "~/types/api/models/Data
2322
import { DatastoreStagedResourceTreeAPIResponse } from "~/types/api/models/DatastoreStagedResourceTreeAPIResponse";
2423
import { ExecutionLogStatus } from "~/types/api/models/ExecutionLogStatus";
2524
import { MonitorActionResponse } from "~/types/api/models/MonitorActionResponse";
25+
import { MonitorTaskInProgressResponse } from "~/types/api/models/MonitorTaskInProgressResponse";
2626
import { Page_DatastoreStagedResourceTreeAPIResponse_ } from "~/types/api/models/Page_DatastoreStagedResourceTreeAPIResponse_";
2727
import {
2828
PaginatedResponse,

clients/admin-ui/src/features/data-discovery-and-detection/discovery-detection.slice.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import type { RootState } from "~/app/store";
55
import { baseApi } from "~/features/common/api.slice";
66
import {
77
DiffStatus,
8+
EditableMonitorConfig,
89
MonitorConfig,
910
MonitorFrequency,
1011
Page_MonitorStatusResponse_,
@@ -116,7 +117,7 @@ const discoveryDetectionApi = baseApi.injectEndpoints({
116117
}),
117118
providesTags: ["Discovery Monitor Configs"],
118119
}),
119-
putDiscoveryMonitor: build.mutation<MonitorConfig, MonitorConfig>({
120+
putDiscoveryMonitor: build.mutation<MonitorConfig, EditableMonitorConfig>({
120121
query: (body) => ({
121122
method: "PUT",
122123
url: `/plus/discovery-monitor`,

clients/admin-ui/src/features/integrations/configure-monitor/ConfigureMonitorDatabasesForm.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { InfoTooltip } from "~/features/common/InfoTooltip";
1212
import { DEFAULT_TOAST_PARAMS } from "~/features/common/toast";
1313
import MonitorDatabasePicker from "~/features/integrations/configure-monitor/MonitorDatabasePicker";
1414
import useCumulativeGetDatabases from "~/features/integrations/configure-monitor/useCumulativeGetDatabases";
15-
import { MonitorConfig } from "~/types/api";
15+
import { EditableMonitorConfig } from "~/types/api";
1616

1717
const TOOLTIP_COPY =
1818
"Selecting a project will monitor all current and future datasets within that project.";
@@ -27,11 +27,11 @@ const ConfigureMonitorDatabasesForm = ({
2727
onSubmit,
2828
onClose,
2929
}: {
30-
monitor: MonitorConfig;
30+
monitor: EditableMonitorConfig;
3131
isEditing?: boolean;
3232
isSubmitting?: boolean;
3333
integrationKey: string;
34-
onSubmit: (monitor: MonitorConfig) => void;
34+
onSubmit: (monitor: EditableMonitorConfig) => void;
3535
onClose: () => void;
3636
}) => {
3737
const toast = useToast();

clients/admin-ui/src/features/integrations/configure-monitor/ConfigureMonitorForm.tsx

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { skipToken } from "@reduxjs/toolkit/query";
12
import type { Dayjs } from "dayjs";
23
import dayjs from "dayjs";
34
import utc from "dayjs/plugin/utc";
@@ -8,12 +9,13 @@ import { useEffect, useState } from "react";
89
import { useFeatures } from "~/features/common/features/features.slice";
910
import { enumToOptions } from "~/features/common/helpers";
1011
import { useGetConfigurationSettingsQuery } from "~/features/config-settings/config-settings.slice";
12+
import { useGetSystemByFidesKeyQuery } from "~/features/system";
13+
import { useGetAllUsersQuery } from "~/features/user-management";
1114
import {
1215
ClassifyLlmPromptTemplateOptions,
1316
ConnectionSystemTypeMap,
1417
ConnectionType,
1518
EditableMonitorConfig,
16-
MonitorConfig,
1719
MonitorFrequency,
1820
} from "~/types/api";
1921

@@ -31,6 +33,7 @@ interface MonitorConfigFormValues {
3133
llm_model_override?: string;
3234
prompt_template?: ClassifyLlmPromptTemplateOptions;
3335
content_classification_enabled?: boolean;
36+
stewards?: string[];
3437
}
3538

3639
const DEFAULT_CLASSIFIER_PARAMS = {
@@ -40,7 +43,7 @@ const DEFAULT_CLASSIFIER_PARAMS = {
4043

4144
const getClassifyParams = (
4245
isEditing: boolean,
43-
monitor: MonitorConfig | undefined,
46+
monitor: EditableMonitorConfig | undefined,
4447
values: MonitorConfigFormValues,
4548
) => {
4649
const baseParams = isEditing
@@ -75,19 +78,21 @@ const getClassifyParams = (
7578
const ConfigureMonitorForm = ({
7679
monitor,
7780
integrationOption,
81+
integrationSystem,
7882
isSubmitting,
7983
databasesAvailable,
8084
onClose,
8185
onAdvance,
8286
onSubmit,
8387
}: {
84-
monitor?: MonitorConfig;
88+
monitor?: EditableMonitorConfig;
8589
integrationOption: ConnectionSystemTypeMap;
90+
integrationSystem?: string | null;
8691
isSubmitting?: boolean;
8792
databasesAvailable?: boolean;
8893
onClose: () => void;
89-
onAdvance: (monitor: MonitorConfig) => void;
90-
onSubmit: (monitor: MonitorConfig) => void;
94+
onAdvance: (monitor: EditableMonitorConfig) => void;
95+
onSubmit: (monitor: EditableMonitorConfig) => void;
9196
}) => {
9297
const isEditing = !!monitor;
9398
const { flags } = useFeatures();
@@ -106,6 +111,10 @@ const ConfigureMonitorForm = ({
106111
{ skip: !llmClassifierFeatureEnabled },
107112
);
108113

114+
const [form] = Form.useForm<MonitorConfigFormValues>();
115+
const { data: systemData, isLoading: isLoadingSystem } =
116+
useGetSystemByFidesKeyQuery(integrationSystem || skipToken);
117+
109118
/**
110119
* Server-side LLM classifier capability.
111120
* This determines if the backend supports LLM-based classification for monitors.
@@ -133,21 +142,22 @@ const ConfigureMonitorForm = ({
133142
};
134143

135144
const classifyParams = getClassifyParams(isEditing, monitor, values);
136-
137145
const payload: EditableMonitorConfig = isEditing
138146
? {
139147
...monitor,
140148
...executionInfo,
141149
name: values.name,
142150
shared_config_id: values.shared_config_id,
143151
classify_params: classifyParams,
152+
stewards: values.stewards,
144153
}
145154
: {
146155
...executionInfo,
147156
name: values.name,
148157
shared_config_id: values.shared_config_id,
149158
connection_config_key: integrationId!,
150159
classify_params: classifyParams,
160+
stewards: values.stewards,
151161
};
152162

153163
if (integrationOption.identifier === ConnectionType.DYNAMODB) {
@@ -169,10 +179,27 @@ const ConfigureMonitorForm = ({
169179
const monitorUsesLlmClassifier =
170180
monitor?.classify_params?.context_classifier === "llm";
171181

172-
const [form] = Form.useForm<MonitorConfigFormValues>();
173182
const [submittable, setSubmittable] = useState(false);
174183
const formValues = Form.useWatch([], form);
175184

185+
const { data: eligibleUsersData } = useGetAllUsersQuery({
186+
page: 1,
187+
size: 100,
188+
include_external: false,
189+
exclude_approvers: true,
190+
});
191+
192+
const dataStewardOptions = (eligibleUsersData?.items || []).map((user) => ({
193+
label: user.username,
194+
value: user.id,
195+
}));
196+
197+
// TODO: build better pattern for async form initialization
198+
useEffect(() => {
199+
form.resetFields();
200+
// eslint-disable-next-line react-hooks/exhaustive-deps
201+
}, [isLoadingSystem]);
202+
176203
useEffect(() => {
177204
form
178205
.validateFields({ validateOnly: true })
@@ -196,6 +223,8 @@ const ConfigureMonitorForm = ({
196223
content_classification_enabled: !monitorUsesLlmClassifier
197224
? monitor?.classify_params?.content_classification_enabled
198225
: undefined, // for now, content classification is always disabled for LLM classification
226+
stewards:
227+
monitor?.stewards || systemData?.data_stewards?.map(({ id }) => id),
199228
} as const;
200229

201230
return (
@@ -206,6 +235,7 @@ const ConfigureMonitorForm = ({
206235
layout="vertical"
207236
validateTrigger="onChange"
208237
initialValues={initialValues}
238+
disabled={isLoadingSystem} // TODO: establish better pattern and styles for async form initialization
209239
>
210240
<Form.Item
211241
label="Name"
@@ -214,13 +244,21 @@ const ConfigureMonitorForm = ({
214244
>
215245
<Input data-testid="input-name" />
216246
</Form.Item>
247+
<Form.Item label="Stewards" name="stewards">
248+
<Select
249+
mode="multiple"
250+
aria-label="Select stewards"
251+
data-testid="controlled-select"
252+
options={dataStewardOptions}
253+
/>
254+
</Form.Item>
217255
<Form.Item
218256
label="Automatic execution frequency"
219257
name="execution_frequency"
220258
tooltip="Interval to run the monitor automatically after the start date"
221259
>
222260
<Select
223-
aria-label="Select Automatic execution frequency"
261+
aria-label="Select automatic execution frequency"
224262
data-testid="controlled-select-execution_frequency"
225263
options={enumToOptions(MonitorFrequency)}
226264
/>
@@ -262,7 +300,6 @@ const ConfigureMonitorForm = ({
262300
checked={form.getFieldValue("use_llm_classifier")}
263301
/>
264302
</Form.Item>
265-
266303
{form.getFieldValue("use_llm_classifier") && (
267304
<Form.Item
268305
name="llm_model_override"

clients/admin-ui/src/features/integrations/configure-monitor/ConfigureMonitorModal.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ import ConfigureMonitorDatabasesForm from "~/features/integrations/configure-mon
1818
import ConfigureMonitorForm from "~/features/integrations/configure-monitor/ConfigureMonitorForm";
1919
import ConfigureWebsiteMonitorForm from "~/features/integrations/configure-monitor/ConfigureWebsiteMonitorForm";
2020
import {
21-
ConnectionConfigurationResponse,
21+
ConnectionConfigurationResponseWithSystemKey,
2222
ConnectionSystemTypeMap,
2323
ConnectionType,
24-
MonitorConfig,
24+
EditableMonitorConfig,
2525
MonitorFrequency,
2626
} from "~/types/api";
2727
import { isErrorResult, RTKResult } from "~/types/errors";
@@ -47,15 +47,14 @@ const ConfigureMonitorModal = ({
4747
isWebsiteMonitor,
4848
}: Pick<UseDisclosureReturn, "isOpen" | "onClose"> & {
4949
formStep: number;
50-
monitor?: MonitorConfig;
50+
monitor?: EditableMonitorConfig;
5151
isEditing?: boolean;
52-
onAdvance: (m: MonitorConfig) => void;
53-
integration: ConnectionConfigurationResponse;
52+
onAdvance: (m: EditableMonitorConfig) => void;
53+
integration: ConnectionConfigurationResponseWithSystemKey;
5454
integrationOption: ConnectionSystemTypeMap;
5555
isWebsiteMonitor?: boolean;
5656
}) => {
5757
const isOktaIntegration = integration.connection_type === ConnectionType.OKTA;
58-
5958
const [putMonitorMutationTrigger, { isLoading: isSubmittingRegular }] =
6059
usePutDiscoveryMonitorMutation();
6160

@@ -88,7 +87,7 @@ const ConfigureMonitorModal = ({
8887

8988
const { successAlert, errorAlert } = useAlert();
9089

91-
const handleSubmit = async (values: MonitorConfig) => {
90+
const handleSubmit = async (values: EditableMonitorConfig) => {
9291
let result: RTKResult | undefined;
9392
const timeout = setTimeout(() => {
9493
if (!result) {
@@ -188,6 +187,7 @@ const ConfigureMonitorModal = ({
188187
onSubmit={handleSubmit}
189188
isSubmitting={isSubmitting}
190189
databasesAvailable={databasesAvailable}
190+
integrationSystem={integration?.system_key}
191191
integrationOption={integrationOption}
192192
/>
193193
)}

clients/admin-ui/src/features/integrations/configure-monitor/ConfigureWebsiteMonitorForm.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,15 @@ import { PRIVACY_NOTICE_REGION_RECORD } from "~/features/common/privacy-notice-r
2222
import { useGetOnlyCountryLocationsQuery } from "~/features/locations/locations.slice";
2323
import { getSelectedRegionIds } from "~/features/privacy-experience/form/helpers";
2424
import {
25-
MonitorConfig,
25+
EditableMonitorConfig,
2626
MonitorFrequency,
2727
WebsiteMonitorParams,
2828
} from "~/types/api";
2929

3030
import { FormikSharedConfigSelect } from "./FormikSharedConfigSelect";
3131

3232
interface WebsiteMonitorConfig
33-
extends Omit<MonitorConfig, "datasource_params"> {
33+
extends Omit<EditableMonitorConfig, "datasource_params"> {
3434
datasource_params?: WebsiteMonitorParams;
3535
url: string;
3636
}
@@ -58,10 +58,10 @@ const ConfigureWebsiteMonitorForm = ({
5858
onClose,
5959
onSubmit,
6060
}: {
61-
monitor?: MonitorConfig;
61+
monitor?: EditableMonitorConfig;
6262
url: string;
6363
onClose: () => void;
64-
onSubmit: (values: MonitorConfig) => Promise<void>;
64+
onSubmit: (values: EditableMonitorConfig) => Promise<void>;
6565
}) => {
6666
const [isSubmitting, setIsSubmitting] = useState(false);
6767
const router = useRouter();

clients/admin-ui/src/features/integrations/configure-monitor/MonitorConfigEnableCell.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@ const MODAL_TEXT =
77
"You are about to disable this monitor. If you continue, it will no longer scan automatically.";
88

99
export const MonitorConfigEnableCell = ({
10-
record,
10+
record: { stewards, enabled, ...data },
1111
}: {
1212
record: MonitorConfig;
1313
}) => {
1414
const [putMonitor] = usePutDiscoveryMonitorMutation();
1515
const handleToggle = async (toggleValue: boolean) =>
1616
putMonitor({
17-
...record,
17+
...data,
18+
stewards: stewards?.map(({ id }) => id),
1819
enabled: toggleValue,
1920
});
20-
const { enabled } = record;
2121

2222
return (
2323
<EnableCell

clients/admin-ui/src/features/integrations/configure-monitor/MonitorConfigTab.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ import { MonitorIcon } from "~/features/common/Icon/MonitorIcon";
1212
import ConfigureMonitorModal from "~/features/integrations/configure-monitor/ConfigureMonitorModal";
1313
import { useMonitorConfigTable } from "~/features/integrations/hooks/useMonitorConfigTable";
1414
import {
15-
ConnectionConfigurationResponse,
15+
ConnectionConfigurationResponseWithSystemKey,
1616
ConnectionSystemTypeMap,
1717
ConnectionType,
18-
MonitorConfig,
18+
EditableMonitorConfig,
1919
SystemType,
2020
} from "~/types/api";
2121

@@ -36,7 +36,7 @@ const MonitorConfigTab = ({
3636
integration,
3737
integrationOption,
3838
}: {
39-
integration: ConnectionConfigurationResponse;
39+
integration: ConnectionConfigurationResponseWithSystemKey;
4040
integrationOption?: ConnectionSystemTypeMap;
4141
}) => {
4242
const isWebsiteMonitor =
@@ -47,12 +47,12 @@ const MonitorConfigTab = ({
4747

4848
const modal = useDisclosure();
4949
const [workingMonitor, setWorkingMonitor] = useState<
50-
MonitorConfig | undefined
50+
EditableMonitorConfig | undefined
5151
>(undefined);
5252
const [isEditing, setIsEditing] = useState<boolean>(false);
5353
const [formStep, setFormStep] = useState(0);
5454

55-
const handleEditMonitor = (monitor: MonitorConfig) => {
55+
const handleEditMonitor = (monitor: EditableMonitorConfig) => {
5656
setWorkingMonitor(monitor);
5757
setIsEditing(true);
5858
modal.onOpen();
@@ -65,7 +65,7 @@ const MonitorConfigTab = ({
6565
modal.onClose();
6666
};
6767

68-
const handleAdvanceForm = (monitor: MonitorConfig) => {
68+
const handleAdvanceForm = (monitor: EditableMonitorConfig) => {
6969
setWorkingMonitor(monitor);
7070
setFormStep(1);
7171
};

clients/admin-ui/src/features/integrations/hooks/useFeatureBasedTabs.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ export const useFeatureBasedTabs = ({
4848
supportsConnectionTest,
4949
}: UseFeatureBasedTabsProps) => {
5050
const { onOpen, isOpen, onClose } = useDisclosure();
51-
5251
const tabs = useMemo(() => {
5352
// Don't show tabs until enabledFeatures is loaded
5453
if (!enabledFeatures || !enabledFeatures.length) {

0 commit comments

Comments
 (0)