Skip to content

Commit

Permalink
chore: update mappingcontext to use streamdescriptor as key (#14844)
Browse files Browse the repository at this point in the history
Co-authored-by: josephkmh <[email protected]>
  • Loading branch information
teallarson and josephkmh committed Dec 19, 2024
1 parent eb9bf0a commit 3bb941b
Show file tree
Hide file tree
Showing 14 changed files with 141 additions and 99 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Tooltip } from "components/ui/Tooltip";
import { useConnectionFormService } from "hooks/services/ConnectionForm/ConnectionFormService";

import styles from "./AddStreamForMappingComboBox.module.scss";
import { useMappingContext } from "./MappingContext";
import { getKeyForStream, useMappingContext } from "./MappingContext";
import { useGetStreamsForNewMapping } from "./useGetStreamsForNewMappings";

export const AddStreamForMappingComboBox: React.FC<{ secondary?: boolean }> = ({ secondary = false }) => {
Expand All @@ -23,14 +23,14 @@ export const AddStreamForMappingComboBox: React.FC<{ secondary?: boolean }> = ({
? formatMessage({ id: "connections.mappings.addStream" })
: formatMessage({ id: "connections.mappings.selectAStream" });

const onChange = (streamName: string) => {
setSelectedStream(streamName);
addStreamToMappingsList(streamName);
const onChange = (streamDescriptorKey: string) => {
setSelectedStream(streamDescriptorKey);
addStreamToMappingsList(streamDescriptorKey);
};

const options = streamsToList?.map((stream) => ({
label: stream.stream?.name || "",
value: stream.stream?.name || "",
value: stream.stream ? getKeyForStream(stream.stream) : "",
}));
const disabled = !options || options.length === 0 || mode === "readonly";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const ConnectionMappingsList: React.FC = () => {

const handleValidations = async () => {
const validations = await Promise.allSettled(
Object.entries(streamsWithMappings).flatMap(([_streamName, mappers]) =>
Object.entries(streamsWithMappings).flatMap(([_streamDescriptorKey, mappers]) =>
mappers.map((mapper) => mapper.validationCallback())
)
);
Expand Down Expand Up @@ -50,12 +50,12 @@ export const ConnectionMappingsList: React.FC = () => {
</FlexContainer>
</FlexContainer>
<FlexContainer direction="column">
{Object.entries(streamsWithMappings).map(([streamName, mappers]) => {
{Object.entries(streamsWithMappings).map(([streamDescriptorKey, mappers]) => {
if (!mappers || mappers.length === 0) {
return null;
}

return <StreamMappingsCard key={`${streamName}-${key}`} streamName={streamName} />;
return <StreamMappingsCard key={`${streamDescriptorKey}-${key}`} streamDescriptorKey={streamDescriptorKey} />;
})}
<div>
<AddStreamForMappingComboBox secondary />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ const encryptionMapperSchema = yup
.required();

interface EncryptionFormProps {
streamName: string;
streamDescriptorKey: string;
mapping: StreamMapperWithId<EncryptionMapperConfiguration>;
targetFieldOptions: SelectFieldOption[];
}

export const EncryptionForm: React.FC<EncryptionFormProps> = ({ streamName, mapping, targetFieldOptions }) => {
export const EncryptionForm: React.FC<EncryptionFormProps> = ({ streamDescriptorKey, mapping, targetFieldOptions }) => {
const { updateLocalMapping, validateMappings } = useMappingContext();
const { formatMessage } = useIntl();
const [algorithm, setAlgorithm] = useState<EncryptionMapperAlgorithm>(mapping.mapperConfiguration.algorithm || "RSA");
Expand All @@ -71,23 +71,21 @@ export const EncryptionForm: React.FC<EncryptionFormProps> = ({ streamName, mapp
...(mapping.mapperConfiguration.algorithm === "RSA" && {
publicKey: mapping.mapperConfiguration.publicKey ?? "",
}),

...(mapping.mapperConfiguration.algorithm === "AES" && {
key: mapping.mapperConfiguration.key ?? "",
mode: mapping.mapperConfiguration.mode ?? "CBC",
padding: mapping.mapperConfiguration.padding ?? "PKCS5Padding",
targetField: mapping.mapperConfiguration.targetField ?? "",
fieldNameSuffix: mapping.mapperConfiguration.fieldNameSuffix ?? "_encrypted",
}),
},
resolver: autoSubmitResolver<EncryptionMapperConfiguration>(encryptionMapperSchema, (formValues) => {
updateLocalMapping(streamName, mapping.id, { mapperConfiguration: formValues });
updateLocalMapping(streamDescriptorKey, mapping.id, { mapperConfiguration: formValues });
validateMappings();
}),
mode: "onBlur",
});

useEffect(() => {
updateLocalMapping(streamName, mapping.id, { validationCallback: methods.trigger });
}, [methods.trigger, streamName, updateLocalMapping, mapping.id]);
updateLocalMapping(streamDescriptorKey, mapping.id, { validationCallback: methods.trigger });
}, [methods.trigger, streamDescriptorKey, updateLocalMapping, mapping.id]);

const values = methods.watch();

Expand All @@ -96,7 +94,7 @@ export const EncryptionForm: React.FC<EncryptionFormProps> = ({ streamName, mapp
<form className={styles.form}>
<MappingTypeListBox
selectedValue={StreamMapperType.encryption}
streamName={streamName}
streamDescriptorKey={streamDescriptorKey}
mappingId={mapping.id}
/>
<SelectTargetField<EncryptionMapperConfiguration> targetFieldOptions={targetFieldOptions} name="targetField" />
Expand Down Expand Up @@ -172,13 +170,15 @@ export const EncryptionForm: React.FC<EncryptionFormProps> = ({ streamName, mapp

export const EncryptionRow: React.FC<{
mapping: StreamMapperWithId<EncryptionMapperConfiguration>;
streamName: string;
}> = ({ mapping, streamName }) => {
const fieldsInStream = useGetFieldsInStream(streamName);
streamDescriptorKey: string;
}> = ({ mapping, streamDescriptorKey }) => {
const fieldsInStream = useGetFieldsInStream(streamDescriptorKey);

if (!mapping) {
return null;
}

return <EncryptionForm streamName={streamName} mapping={mapping} targetFieldOptions={fieldsInStream} />;
return (
<EncryptionForm streamDescriptorKey={streamDescriptorKey} mapping={mapping} targetFieldOptions={fieldsInStream} />
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,12 @@ export const fieldRenamingConfigSchema = yup.object().shape({

interface FieldRenamingRowProps {
mapping: StreamMapperWithId<FieldRenamingMapperConfiguration>;
streamName: string;
streamDescriptorKey: string;
}

export const FieldRenamingRow: React.FC<FieldRenamingRowProps> = ({ mapping, streamName }) => {
export const FieldRenamingRow: React.FC<FieldRenamingRowProps> = ({ mapping, streamDescriptorKey }) => {
const { updateLocalMapping, validateMappings } = useMappingContext();
const fieldsInStream = useGetFieldsInStream(streamName);
const fieldsInStream = useGetFieldsInStream(streamDescriptorKey);
const { formatMessage } = useIntl();

const defaultValues = useMemo(() => {
Expand All @@ -41,23 +41,23 @@ export const FieldRenamingRow: React.FC<FieldRenamingRowProps> = ({ mapping, str
const methods = useForm<FieldRenamingMapperConfiguration>({
defaultValues,
resolver: autoSubmitResolver<FieldRenamingMapperConfiguration>(fieldRenamingConfigSchema, (formValues) => {
updateLocalMapping(streamName, mapping.id, { mapperConfiguration: formValues });
updateLocalMapping(streamDescriptorKey, mapping.id, { mapperConfiguration: formValues });
validateMappings();
}),
mode: "onBlur",
});

useEffect(() => {
updateLocalMapping(streamName, mapping.id, { validationCallback: methods.trigger });
}, [methods.trigger, streamName, updateLocalMapping, mapping.id]);
updateLocalMapping(streamDescriptorKey, mapping.id, { validationCallback: methods.trigger });
}, [methods.trigger, streamDescriptorKey, updateLocalMapping, mapping.id]);

return (
<FormProvider {...methods}>
<form>
<MappingRowContent>
<MappingTypeListBox
selectedValue={StreamMapperType["field-renaming"]}
streamName={streamName}
streamDescriptorKey={streamDescriptorKey}
mappingId={mapping.id}
/>
<SelectTargetField<FieldRenamingMapperConfiguration>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,10 @@ const hashingMapperConfigSchema = yup.object().shape({

export const HashFieldRow: React.FC<{
mapping: StreamMapperWithId<HashingMapperConfiguration>;
streamName: string;
}> = ({ mapping, streamName }) => {
streamDescriptorKey: string;
}> = ({ mapping, streamDescriptorKey }) => {
const { updateLocalMapping, validateMappings } = useMappingContext();
const fieldsInStream = useGetFieldsInStream(streamName);
const fieldsInStream = useGetFieldsInStream(streamDescriptorKey);

const defaultValues = useMemo(() => {
return {
Expand All @@ -50,15 +50,15 @@ export const HashFieldRow: React.FC<{
const methods = useForm<HashingMapperConfiguration>({
defaultValues,
resolver: autoSubmitResolver<HashingMapperConfiguration>(hashingMapperConfigSchema, (formValues) => {
updateLocalMapping(streamName, mapping.id, { mapperConfiguration: formValues });
updateLocalMapping(streamDescriptorKey, mapping.id, { mapperConfiguration: formValues });
validateMappings();
}),
mode: "onBlur",
});

useEffect(() => {
updateLocalMapping(streamName, mapping.id, { validationCallback: methods.trigger });
}, [methods.trigger, streamName, updateLocalMapping, mapping.id]);
updateLocalMapping(streamDescriptorKey, mapping.id, { validationCallback: methods.trigger });
}, [methods.trigger, streamDescriptorKey, updateLocalMapping, mapping.id]);

if (!mapping) {
return null;
Expand All @@ -68,7 +68,11 @@ export const HashFieldRow: React.FC<{
<FormProvider {...methods}>
<form>
<MappingRowContent>
<MappingTypeListBox selectedValue={StreamMapperType.hashing} mappingId={mapping.id} streamName={streamName} />
<MappingTypeListBox
selectedValue={StreamMapperType.hashing}
mappingId={mapping.id}
streamDescriptorKey={streamDescriptorKey}
/>
<SelectTargetField<HashingMapperConfiguration> name="targetField" targetFieldOptions={fieldsInStream} />
<MappingRowItem>
<Text>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import React, { PropsWithChildren, createContext, useCallback, useContext, useSt
import { v4 as uuidv4 } from "uuid";

import { useCurrentConnection } from "core/api";
import { HashingMapperConfigurationMethod, StreamMapperType } from "core/api/types/AirbyteClient";
import {
AirbyteStream,
HashingMapperConfigurationMethod,
StreamDescriptor,
StreamMapperType,
} from "core/api/types/AirbyteClient";
import { useNotificationService } from "hooks/services/Notification";

import { StreamMapperWithId } from "./types";
Expand All @@ -12,20 +17,33 @@ import { useUpdateMappingsForCurrentConnection } from "./useUpdateMappingsForCur

interface MappingContextType {
streamsWithMappings: Record<string, StreamMapperWithId[]>;
updateLocalMapping: (streamName: string, mappingId: string, updatedMapping: Partial<StreamMapperWithId>) => void;
reorderMappings: (streamName: string, newOrder: StreamMapperWithId[]) => void;
updateLocalMapping: (
streamDescriptorKey: string,
mappingId: string,
updatedMapping: Partial<StreamMapperWithId>
) => void;
reorderMappings: (streamDescriptorKey: string, newOrder: StreamMapperWithId[]) => void;
clear: () => void;
submitMappings: () => Promise<void>;
removeMapping: (streamName: string, mappingId: string) => void;
addStreamToMappingsList: (streamName: string) => void;
addMappingForStream: (streamName: string) => void;
removeMapping: (streamDescriptorKey: string, mappingId: string) => void;
addStreamToMappingsList: (streamDescriptorKey: string) => void;
addMappingForStream: (streamDescriptorKey: string) => void;
validateMappings: () => void;
key: number;
}

export const MAPPING_VALIDATION_ERROR_KEY = "mapping-validation-error";

const MappingContext = createContext<MappingContextType | undefined>(undefined);
export const getKeyForStream = (stream: AirbyteStream) => `${stream.namespace}-${stream.name}`;
export const getStreamDescriptorForKey = (key: string): StreamDescriptor => {
const [namespace, name] = key.split("-");

if (namespace === "undefined") {
return { namespace: undefined, name };
}
return { namespace, name };
};

export const MappingContextProvider: React.FC<PropsWithChildren> = ({ children }) => {
const connection = useCurrentConnection();
Expand All @@ -43,12 +61,12 @@ export const MappingContextProvider: React.FC<PropsWithChildren> = ({ children }

// Updates a specific mapping in the local state
const updateLocalMapping = useCallback(
(streamName: string, mappingId: string, updatedMapping: Partial<StreamMapperWithId>) => {
console.log(`updating local mapping for stream ${streamName}`, updatedMapping);
(streamDescriptorKey: string, mappingId: string, updatedMapping: Partial<StreamMapperWithId>) => {
console.log(`updating local mapping for stream ${streamDescriptorKey}`, updatedMapping);

setStreamsWithMappings((prevMappings) => ({
...prevMappings,
[streamName]: prevMappings[streamName].map((mapping) => {
[streamDescriptorKey]: prevMappings[streamDescriptorKey].map((mapping) => {
if (mapping.id === mappingId) {
if (updatedMapping.type && updatedMapping.type !== mapping.type) {
return updatedMapping as StreamMapperWithId;
Expand All @@ -63,11 +81,11 @@ export const MappingContextProvider: React.FC<PropsWithChildren> = ({ children }
[]
);

const addMappingForStream = (streamName: string) => {
const addMappingForStream = (streamDescriptorKey: string) => {
setStreamsWithMappings((prevMappings) => ({
...prevMappings,
[streamName]: [
...prevMappings[streamName],
[streamDescriptorKey]: [
...prevMappings[streamDescriptorKey],
{
type: StreamMapperType.hashing,
id: uuidv4(),
Expand All @@ -83,10 +101,10 @@ export const MappingContextProvider: React.FC<PropsWithChildren> = ({ children }
};

// Reorders the mappings for a specific stream
const reorderMappings = (streamName: string, newOrder: StreamMapperWithId[]) => {
const reorderMappings = (streamDescriptorKey: string, newOrder: StreamMapperWithId[]) => {
setStreamsWithMappings((prevMappings) => ({
...prevMappings,
[streamName]: newOrder,
[streamDescriptorKey]: newOrder,
}));
};

Expand All @@ -97,17 +115,17 @@ export const MappingContextProvider: React.FC<PropsWithChildren> = ({ children }
unregisterNotificationById(MAPPING_VALIDATION_ERROR_KEY);
};

const removeMapping = (streamName: string, mappingId: string) => {
const mappingsForStream = streamsWithMappings[streamName].filter((mapping) => mapping.id !== mappingId);
const removeMapping = (streamDescriptorKey: string, mappingId: string) => {
const mappingsForStream = streamsWithMappings[streamDescriptorKey].filter((mapping) => mapping.id !== mappingId);

setStreamsWithMappings((prevMappings) => {
if (mappingsForStream.length === 0) {
const { [streamName]: removedStream, ...rest } = prevMappings;
const { [streamDescriptorKey]: removedStream, ...rest } = prevMappings;
return rest;
}
return {
...prevMappings,
[streamName]: mappingsForStream,
[streamDescriptorKey]: mappingsForStream,
};
});
};
Expand All @@ -118,9 +136,9 @@ export const MappingContextProvider: React.FC<PropsWithChildren> = ({ children }
return Promise.resolve();
};

const addStreamToMappingsList = (streamName: string) => {
const addStreamToMappingsList = (streamDescriptorKey: string) => {
const newMapping: Record<string, StreamMapperWithId[]> = {
[streamName]: [
[streamDescriptorKey]: [
{
type: StreamMapperType.hashing,
id: uuidv4(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import { RowFilteringMapperForm } from "./RowFilteringMapperForm";
import { isEncryptionMapping, isFieldRenamingMapping, isHashingMapping, isRowFilteringMapping } from "./typeHelpers";

export const MappingRow: React.FC<{
streamName: string;
streamDescriptorKey: string;
id: string;
}> = ({ streamName, id }) => {
}> = ({ streamDescriptorKey, id }) => {
const { removeMapping, streamsWithMappings } = useMappingContext();
const mapping = streamsWithMappings[streamName].find((m) => m.id === id);
const mapping = streamsWithMappings[streamDescriptorKey].find((m) => m.id === id);

const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });

Expand All @@ -37,20 +37,20 @@ export const MappingRow: React.FC<{
}

if (isHashingMapping(mapping)) {
return <HashFieldRow streamName={streamName} mapping={mapping} />;
return <HashFieldRow streamDescriptorKey={streamDescriptorKey} mapping={mapping} />;
}
if (isFieldRenamingMapping(mapping)) {
return <FieldRenamingRow streamName={streamName} mapping={mapping} />;
return <FieldRenamingRow streamDescriptorKey={streamDescriptorKey} mapping={mapping} />;
}
if (isRowFilteringMapping(mapping)) {
return <RowFilteringMapperForm streamName={streamName} mapping={mapping} />;
return <RowFilteringMapperForm streamDescriptorKey={streamDescriptorKey} mapping={mapping} />;
}
if (isEncryptionMapping(mapping)) {
return <EncryptionRow streamName={streamName} mapping={mapping} />;
return <EncryptionRow streamDescriptorKey={streamDescriptorKey} mapping={mapping} />;
}

return null;
}, [mapping, streamName]);
}, [mapping, streamDescriptorKey]);

if (!RowContent || !mapping) {
return null;
Expand All @@ -69,7 +69,7 @@ export const MappingRow: React.FC<{
key={`remove-${id}`}
variant="clear"
type="button"
onClick={() => removeMapping(streamName, mapping.id)}
onClick={() => removeMapping(streamDescriptorKey, mapping.id)}
>
<Icon color="disabled" type="trash" />
</Button>
Expand Down
Loading

0 comments on commit 3bb941b

Please sign in to comment.