Skip to content

Commit

Permalink
notebook connections
Browse files Browse the repository at this point in the history
  • Loading branch information
emilys314 committed Oct 9, 2024
1 parent d857dec commit cc49fd6
Show file tree
Hide file tree
Showing 16 changed files with 795 additions and 69 deletions.
24 changes: 15 additions & 9 deletions frontend/src/components/MultiSelection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,11 @@ import {
HelperTextItem,
SelectGroup,
Divider,
SelectOptionProps,
} from '@patternfly/react-core';
import { TimesIcon } from '@patternfly/react-icons/dist/esm/icons/times-icon';

export type SelectionOptions = {
export type SelectionOptions = Omit<SelectOptionProps, 'id'> & {
id: number | string;
name: string;
selected?: boolean;
Expand Down Expand Up @@ -49,9 +50,12 @@ type MultiSelectionProps = {
isCreateOptionOnTop?: boolean;
/** Message to display to create a new option */
createOptionMessage?: string | ((newValue: string) => string);
filterFunction?: (filterText: string, options: SelectionOptions[]) => SelectionOptions[];
};

const defaultCreateOptionMessage = (newValue: string) => `Create "${newValue}"`;
const defaultFilterFunction = (filterText: string, options: SelectionOptions[]) =>
options.filter((o) => !filterText || o.name.toLowerCase().includes(filterText.toLowerCase()));

export const MultiSelection: React.FC<MultiSelectionProps> = ({
value = [],
Expand All @@ -69,6 +73,7 @@ export const MultiSelection: React.FC<MultiSelectionProps> = ({
isCreatable = false,
isCreateOptionOnTop = false,
createOptionMessage = defaultCreateOptionMessage,
filterFunction = defaultFilterFunction,
}) => {
const [isOpen, setIsOpen] = React.useState(false);
const [inputValue, setInputValue] = React.useState<string>('');
Expand All @@ -80,16 +85,14 @@ export const MultiSelection: React.FC<MultiSelectionProps> = ({
let counter = 0;
return groupedValues
.map((g) => {
const values = g.values.filter(
(v) => !inputValue || v.name.toLowerCase().includes(inputValue.toLowerCase()),
);
const values = filterFunction(inputValue, g.values);
return {
...g,
values: values.map((v) => ({ ...v, index: counter++ })),
};
})
.filter((g) => g.values.length);
}, [inputValue, groupedValues]);
}, [filterFunction, groupedValues, inputValue]);

const setOpen = (open: boolean) => {
setIsOpen(open);
Expand All @@ -104,10 +107,11 @@ export const MultiSelection: React.FC<MultiSelectionProps> = ({

const selectOptions = React.useMemo(
() =>
value
.filter((v) => !inputValue || v.name.toLowerCase().includes(inputValue.toLowerCase()))
.map((v, index) => ({ ...v, index: groupOptions.length + index })),
[groupOptions, inputValue, value],
filterFunction(inputValue, value).map((v, index) => ({
...v,
index: groupOptions.length + index,
})),
[filterFunction, groupOptions, inputValue, value],
);

const allValues = React.useMemo(() => {
Expand Down Expand Up @@ -340,6 +344,7 @@ export const MultiSelection: React.FC<MultiSelectionProps> = ({
value={option.id}
ref={null}
isSelected={option.selected}
description={option.description}
>
{option.name}
</SelectOption>
Expand All @@ -363,6 +368,7 @@ export const MultiSelection: React.FC<MultiSelectionProps> = ({
value={option.id}
ref={null}
isSelected={option.selected}
description={option.description}
>
{option.name}
</SelectOption>
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/components/table/TableRowTitleDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import TruncatedText from '~/components/TruncatedText';
type TableRowTitleDescriptionProps = {
title: React.ReactNode;
boldTitle?: boolean;
titleIcon?: React.ReactNode;
resource?: K8sResourceCommon;
subtitle?: React.ReactNode;
description?: React.ReactNode;
Expand All @@ -19,6 +20,7 @@ type TableRowTitleDescriptionProps = {
const TableRowTitleDescription: React.FC<TableRowTitleDescriptionProps> = ({
title,
boldTitle = true,
titleIcon,
description,
resource,
subtitle,
Expand Down Expand Up @@ -56,6 +58,7 @@ const TableRowTitleDescription: React.FC<TableRowTitleDescriptionProps> = ({
) : (
title
)}
{titleIcon}
</div>
{subtitle}
{descriptionNode}
Expand Down
16 changes: 11 additions & 5 deletions frontend/src/pages/projects/notebook/useNotebooksStates.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react';
import useFetchState, { FetchState } from '~/utilities/useFetchState';
import useFetchState, { FetchState, NotReadyError } from '~/utilities/useFetchState';
import { NotebookKind } from '~/k8sTypes';
import { POLL_INTERVAL } from '~/utilities/const';
import { getNotebooksStates } from '~/pages/projects/notebook/useProjectNotebookStates';
Expand All @@ -8,11 +8,17 @@ import { NotebookState } from './types';
export const useNotebooksStates = (
notebooks: NotebookKind[],
namespace: string,
checkStatus = true,
): FetchState<NotebookState[]> => {
const fetchNotebooksStatus = React.useCallback(
() => getNotebooksStates(notebooks, namespace),
[namespace, notebooks],
);
const fetchNotebooksStatus = React.useCallback(() => {
if (!namespace) {
return Promise.reject(new NotReadyError('No namespace'));

Check warning on line 15 in frontend/src/pages/projects/notebook/useNotebooksStates.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/projects/notebook/useNotebooksStates.ts#L15

Added line #L15 was not covered by tests
}
if (!checkStatus) {
return Promise.reject(new NotReadyError('Not running'));

Check warning on line 18 in frontend/src/pages/projects/notebook/useNotebooksStates.ts

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/projects/notebook/useNotebooksStates.ts#L18

Added line #L18 was not covered by tests
}
return getNotebooksStates(notebooks, namespace);
}, [namespace, notebooks, checkStatus]);

return useFetchState<NotebookState[]>(fetchNotebooksStatus, [], {
refreshRate: POLL_INTERVAL,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,20 @@ const ConnectionsTable: React.FC<ConnectionsTableProps> = ({
key={connection.metadata.name}
obj={connection}
connectionTypes={connectionTypes}
onEditConnection={() => setManageConnectionModal(connection)}
onDeleteConnection={() => setDeleteConnection(connection)}
kebabActions={[
{
title: 'Edit',
onClick: () => {
setManageConnectionModal(connection);
},
},
{
title: 'Delete',
onClick: () => {
setDeleteConnection(connection);
},
},
]}
/>
)}
isStriped
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as React from 'react';
import { ActionsColumn, Td, Tr } from '@patternfly/react-table';
import { LabelGroup, Truncate } from '@patternfly/react-core';
import { ActionsColumn, IAction, Td, Tr } from '@patternfly/react-table';
import { Icon, LabelGroup, Truncate } from '@patternfly/react-core';
import { ExclamationTriangleIcon } from '@patternfly/react-icons';
import { Connection, ConnectionTypeConfigMapObj } from '~/concepts/connectionTypes/types';
import { TableRowTitleDescription } from '~/components/table';
import { getDescriptionFromK8sResource, getDisplayNameFromK8sResource } from '~/concepts/k8s/utils';
Expand All @@ -11,15 +12,19 @@ import ConnectedResources from '~/pages/projects/screens/detail/connections/Conn
type ConnectionsTableRowProps = {
obj: Connection;
connectionTypes?: ConnectionTypeConfigMapObj[];
onEditConnection: (pvc: Connection) => void;
onDeleteConnection: (dataConnection: Connection) => void;
kebabActions: IAction[];
showCompatibilityCell?: boolean;
showConnectedResourcesCell?: boolean;
showWarningIcon?: boolean;
};

const ConnectionsTableRow: React.FC<ConnectionsTableRowProps> = ({
obj,
connectionTypes,
onEditConnection,
onDeleteConnection,
kebabActions,
showCompatibilityCell = true,
showConnectedResourcesCell = true,
showWarningIcon = false,
}) => {
const connectionTypeDisplayName = React.useMemo(() => {
const matchingType = connectionTypes?.find(
Expand All @@ -45,44 +50,40 @@ const ConnectionsTableRow: React.FC<ConnectionsTableRowProps> = ({
<TableRowTitleDescription
title={<Truncate content={getDisplayNameFromK8sResource(obj)} />}
boldTitle={false}
titleIcon={
showWarningIcon ? (
<Icon status="warning" className="pf-v5-u-pl-lg">
<ExclamationTriangleIcon />
</Icon>
) : undefined
}
resource={obj}
description={getDescriptionFromK8sResource(obj)}
truncateDescriptionLines={2}
wrapResourceTitle={false}
/>
</Td>
<Td dataLabel="Type">{connectionTypeDisplayName}</Td>
<Td dataLabel="Compatibility">
{compatibleTypes.length ? (
<LabelGroup>
{compatibleTypes.map((compatibleType) => (
<CompatibilityLabel key={compatibleType} type={compatibleType} />
))}
</LabelGroup>
) : (
'-'
)}
</Td>
<Td dataLabel="Connected resources">
<ConnectedResources connection={obj} />
</Td>
{showCompatibilityCell && (
<Td dataLabel="Compatibility">
{compatibleTypes.length ? (
<LabelGroup>
{compatibleTypes.map((compatibleType) => (
<CompatibilityLabel key={compatibleType} type={compatibleType} />
))}
</LabelGroup>
) : (
'-'
)}
</Td>
)}
{showConnectedResourcesCell && (
<Td dataLabel="Connected resources">
<ConnectedResources connection={obj} />
</Td>
)}
<Td isActionCell>
<ActionsColumn
items={[
{
title: 'Edit',
onClick: () => {
onEditConnection(obj);
},
},
{
title: 'Delete',
onClick: () => {
onDeleteConnection(obj);
},
},
]}
/>
<ActionsColumn items={kebabActions} />
</Td>
</Tr>
);
Expand Down
18 changes: 16 additions & 2 deletions frontend/src/pages/projects/screens/spawner/SpawnerFooter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { useUser } from '~/redux/selectors';
import { ProjectDetailsContext } from '~/pages/projects/ProjectDetailsContext';
import { AppContext } from '~/app/AppContext';
import { ProjectSectionID } from '~/pages/projects/screens/detail/types';
import { Connection } from '~/concepts/connectionTypes/types';
import { fireFormTrackingEvent } from '~/concepts/analyticsTracking/segmentIOUtils';
import {
FormTrackingEventProperties,
Expand All @@ -40,12 +41,15 @@ import {
} from './service';
import { checkRequiredFieldsForNotebookStart } from './spawnerUtils';
import { getNotebookDataConnection } from './dataConnection/useNotebookDataConnection';
import { setConnectionsOnEnvFrom } from './connections/utils';

type SpawnerFooterProps = {
startNotebookData: StartNotebookData;
storageData: StorageData;
envVariables: EnvVariable[];
dataConnection: DataConnectionData;
isConnectionTypesEnabled: boolean;
connections: Connection[];
canEnablePipelines: boolean;
};

Expand All @@ -54,6 +58,8 @@ const SpawnerFooter: React.FC<SpawnerFooterProps> = ({
storageData,
envVariables,
dataConnection,
isConnectionTypesEnabled,
connections,
canEnablePipelines,
}) => {
const [error, setError] = React.useState<K8sStatusError>();
Expand All @@ -67,6 +73,7 @@ const SpawnerFooter: React.FC<SpawnerFooterProps> = ({
const {
notebooks: { data },
dataConnections: { data: existingDataConnections },
connections: { data: projectConnections },
refreshAllProjectData,
} = React.useContext(ProjectDetailsContext);
const { notebookName } = useParams();
Expand Down Expand Up @@ -148,7 +155,7 @@ const SpawnerFooter: React.FC<SpawnerFooterProps> = ({
dryRun,
).catch(handleError);

const envFrom = await updateConfigMapsAndSecretsForNotebook(
let envFrom = await updateConfigMapsAndSecretsForNotebook(
projectName,
editNotebook,
envVariables,
Expand All @@ -157,6 +164,10 @@ const SpawnerFooter: React.FC<SpawnerFooterProps> = ({
dryRun,
).catch(handleError);

if (isConnectionTypesEnabled && envFrom) {
envFrom = setConnectionsOnEnvFrom(connections, envFrom, projectConnections);

Check warning on line 168 in frontend/src/pages/projects/screens/spawner/SpawnerFooter.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/projects/screens/spawner/SpawnerFooter.tsx#L168

Added line #L168 was not covered by tests
}

if (!pvcDetails || !envFrom) {
return;
}
Expand Down Expand Up @@ -248,7 +259,7 @@ const SpawnerFooter: React.FC<SpawnerFooterProps> = ({
: [];

const pvcDetails = await createPvcDataForNotebook(projectName, storageData).catch(handleError);
const envFrom = await createConfigMapsAndSecretsForNotebook(projectName, [
let envFrom = await createConfigMapsAndSecretsForNotebook(projectName, [
...envVariables,
...newDataConnection,
]).catch(handleError);
Expand All @@ -259,6 +270,9 @@ const SpawnerFooter: React.FC<SpawnerFooterProps> = ({
}

const { volumes, volumeMounts } = pvcDetails;
if (isConnectionTypesEnabled) {
envFrom = setConnectionsOnEnvFrom(connections, envFrom, projectConnections);

Check warning on line 274 in frontend/src/pages/projects/screens/spawner/SpawnerFooter.tsx

View check run for this annotation

Codecov / codecov/patch

frontend/src/pages/projects/screens/spawner/SpawnerFooter.tsx#L274

Added line #L274 was not covered by tests
}
const newStartData: StartNotebookData = {
...startNotebookData,
volumes,
Expand Down
Loading

0 comments on commit cc49fd6

Please sign in to comment.