Skip to content

Commit

Permalink
[RHOAIENG-10312] Connection type table view
Browse files Browse the repository at this point in the history
  • Loading branch information
jeff-phillips-18 committed Aug 6, 2024
1 parent e7239c5 commit 9e9c333
Show file tree
Hide file tree
Showing 20 changed files with 786 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,9 @@ export const patchConnectionType = async (
const { dashboardNamespace } = getNamespaces(fastify);

if (
(partialConfigMap.metadata.labels?.[KnownLabels.DASHBOARD_RESOURCE] &&
(partialConfigMap.metadata?.labels?.[KnownLabels.DASHBOARD_RESOURCE] &&
partialConfigMap.metadata.labels[KnownLabels.DASHBOARD_RESOURCE] !== 'true') ||
(partialConfigMap.metadata.labels?.[KnownLabels.CONNECTION_TYPE] &&
(partialConfigMap.metadata?.labels?.[KnownLabels.CONNECTION_TYPE] &&
partialConfigMap.metadata.labels[KnownLabels.CONNECTION_TYPE] !== 'true')
) {
const error = 'Unable to update connection type, incorrect labels.';
Expand Down
47 changes: 47 additions & 0 deletions frontend/src/__mocks__/mockConnectionTypeResource.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { KnownLabels } from '~/k8sTypes';
import { genUID } from '~/__mocks__/mockUtils';
import { ConnectionTypeConfigMap } from '~/concepts/connectionTypes/types';

type MockResourceConfigType = {
name?: string;
displayName?: string;
description?: string;
enabled?: 'true' | 'false';
username?: string;
creationTimestamp?: string;
data?: {
fields?: string;
};
uid?: string;
};

export const mockConnectionTypeResource = ({
name = 'test-connection-type',
displayName = 'Test connection type',
description = 'Test connection description',
enabled = 'true',
username = 'testuser',
creationTimestamp = '2024-07-31T15:40:24.000Z',
uid = genUID('connection-type'),
data = {},
}: MockResourceConfigType): ConnectionTypeConfigMap => ({
kind: 'ConfigMap',
apiVersion: 'v1',
metadata: {
annotations: {
'openshift.io/display-name': displayName,
'openshift.io/description': description,
'opendatahub.io/enabled': enabled,
'opendatahub.io/username': username,
},
name,
namespace: 'redhat-ods-applications',
creationTimestamp,
labels: {
[KnownLabels.DASHBOARD_RESOURCE]: 'true',
'opendatahub.io/connection-type': 'true',
},
uid,
},
data,
});
114 changes: 114 additions & 0 deletions frontend/src/__tests__/cypress/cypress/pages/connectionTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { appChrome } from '~/__tests__/cypress/cypress/pages/appChrome';
import { TableRow } from './components/table';
import { TableToolbar } from './components/TableToolbar';

class ConnectionTypesTableToolbar extends TableToolbar {}
class ConnectionTypeRow extends TableRow {
findConnectionTypeName() {
return this.find().findByTestId('connection-type-name');
}

shouldHaveName(name: string) {
return this.findConnectionTypeName().should('have.text', name);
}

findConnectionTypeDescription() {
return this.find().findByTestId('connection-type-description');
}

findConnectionTypeCreator() {
return this.find().findByTestId('connection-type-creator');
}

shouldHaveDescription(description: string) {
return this.findConnectionTypeDescription().should('have.text', description);
}

shouldHaveCreator(creator: string) {
return this.findConnectionTypeCreator().should('have.text', creator);
}

shouldShowPreInstalledLabel() {
return this.find().findByTestId('connection-type-user-label').should('exist');
}

findEnabled() {
return this.find().pfSwitchValue('connection-type-enable-switch');
}

findEnableSwitch() {
return this.find().pfSwitch('connection-type-enable-switch');
}

shouldBeEnabled() {
this.findEnabled().should('be.checked');
}

shouldBeDisabled() {
this.findEnabled().should('not.be.checked');
}

findEnableStatus() {
return this.find().findByTestId('connection-type-enable-status');
}
}

class ConnectionTypesPage {
visit() {
cy.visitWithLogin('/connectionTypes');
this.wait();
}

private wait() {
cy.findByTestId('app-page-title');
cy.testA11y();
}

findNavItem() {
return appChrome.findNavItem('Connection types');
}

navigate() {
this.findNavItem().click();
this.wait();
}

shouldHaveConnectionTypes() {
this.findTable().should('exist');
return this;
}

shouldReturnNotFound() {
cy.findByTestId('not-found-page').should('exist');
return this;
}

shouldBeEmpty() {
cy.findByTestId('connection-types-empty-state').should('exist');
return this;
}

findTable() {
return cy.findByTestId('connection-types-table');
}

getConnectionTypeRow(name: string) {
return new ConnectionTypeRow(() =>
this.findTable().findAllByTestId(`connection-type-name`).contains(name).parents('tr'),
);
}

findEmptyFilterResults() {
return cy.findByTestId('no-result-found-title');
}

findSortButton(name: string) {
return this.findTable().find('thead').findByRole('button', { name });
}

getTableToolbar() {
return new ConnectionTypesTableToolbar(() => cy.findByTestId('connection-types-table-toolbar'));
}
}

export const connectionTypesPage = new ConnectionTypesPage();
12 changes: 12 additions & 0 deletions frontend/src/__tests__/cypress/cypress/support/commands/odh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
RegisteredModel,
RegisteredModelList,
} from '~/concepts/modelRegistry/types';
import type { ConnectionTypeConfigMap } from '~/concepts/connectionTypes/types';
import type {
DashboardConfigKind,
DataScienceClusterInitializationKindStatus,
Expand Down Expand Up @@ -565,6 +566,17 @@ declare global {
path: { namespace: string };
},
response: OdhResponse<number>,
) => Cypress.Chainable<null>) &
((
type: 'GET /api/connection-types',
response: ConnectionTypeConfigMap[],
) => Cypress.Chainable<null>) &
((
type: 'PATCH /api/connection-types/:name',
options: {
path: { name: string };
},
response: { success: boolean; error: string },
) => Cypress.Chainable<null>);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { pageNotfound } from '~/__tests__/cypress/cypress/pages/pageNotFound';
import {
asProductAdminUser,
asProjectAdminUser,
} from '~/__tests__/cypress/cypress/utils/mockUsers';
import { connectionTypesPage } from '~/__tests__/cypress/cypress/pages/connectionTypes';
import { mockDashboardConfig } from '~/__mocks__';
import { mockConnectionTypeResource } from '~/__mocks__/mockConnectionTypeResource';

it('Connection types should not be available for non product admins', () => {
asProjectAdminUser();
cy.visitWithLogin('/connectionTypes');
pageNotfound.findPage().should('exist');
connectionTypesPage.findNavItem().should('not.exist');
});

it('Connection types should be hidden by feature flag', () => {
asProductAdminUser();

cy.visitWithLogin('/connectionTypes');
connectionTypesPage.shouldReturnNotFound();

cy.interceptOdh(
'GET /api/config',
mockDashboardConfig({
disableConnectionTypes: false,
}),
);

connectionTypesPage.visit();
});

describe('Connection types', () => {
beforeEach(() => {
asProductAdminUser();

cy.interceptOdh(
'GET /api/config',
mockDashboardConfig({
disableConnectionTypes: false,
}),
);
cy.interceptOdh('GET /api/connection-types', [
mockConnectionTypeResource({}),
mockConnectionTypeResource({
name: 'no-display-name',
displayName: '',
description: 'description 2',
username: 'Pre-installed',
enabled: 'false',
}),
mockConnectionTypeResource({
name: 'test-2',
displayName: 'Test display name',
description: 'Test description',
}),
]);
});

it('should show the connections type table', () => {
connectionTypesPage.visit();
connectionTypesPage.shouldHaveConnectionTypes();
});

it('should show the empty state when there are no results', () => {
cy.interceptOdh('GET /api/connection-types', []);
connectionTypesPage.visit();
connectionTypesPage.shouldBeEmpty();
});

it('should show the correct column values', () => {
connectionTypesPage.visit();

const row = connectionTypesPage.getConnectionTypeRow('Test display name');
row.shouldHaveDescription('Test description');
row.shouldHaveCreator('testuser');
row.shouldBeEnabled();

const row2 = connectionTypesPage.getConnectionTypeRow('no-display-name');
row2.shouldHaveDescription('description 2');
row2.shouldShowPreInstalledLabel();
row2.shouldBeDisabled();
});

it('should show status text when switching enabled state', () => {
connectionTypesPage.visit();
cy.interceptOdh(
'PATCH /api/connection-types/:name',
{ path: { name: 'test-2' } },
{ success: true, error: '' },
);
cy.interceptOdh(
'PATCH /api/connection-types/:name',
{ path: { name: 'no-display-name' } },
{ success: true, error: '' },
);

const row = connectionTypesPage.getConnectionTypeRow('Test display name');
row.findEnableSwitch().click();
row.findEnableStatus().should('have.text', 'Disabling...');

const row2 = connectionTypesPage.getConnectionTypeRow('no-display-name');
row2.findEnableSwitch().click();
row2.findEnableStatus().should('have.text', 'Enabling...');
});
});
5 changes: 5 additions & 0 deletions frontend/src/app/AppRoutes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ const ClusterSettingsPage = React.lazy(() => import('../pages/clusterSettings/Cl
const CustomServingRuntimeRoutes = React.lazy(
() => import('../pages/modelServing/customServingRuntimes/CustomServingRuntimeRoutes'),
);
const ConnectionTypesPage = React.lazy(() => import('../pages/connectionTypes/ConnectionTypes'));
const GroupSettingsPage = React.lazy(() => import('../pages/groupSettings/GroupSettings'));
const LearningCenterPage = React.lazy(() => import('../pages/learningCenter/LearningCenter'));
const BYONImagesPage = React.lazy(() => import('../pages/BYONImages/BYONImages'));
Expand All @@ -69,6 +70,7 @@ const AppRoutes: React.FC = () => {
const { isAdmin, isAllowed } = useUser();
const isJupyterEnabled = useCheckJupyterEnabled();
const isHomeAvailable = useIsAreaAvailable(SupportedArea.HOME).status;
const isConnectionTypesAvailable = useIsAreaAvailable(SupportedArea.CONNECTION_TYPES).status;

if (!isAllowed) {
return (
Expand Down Expand Up @@ -123,6 +125,9 @@ const AppRoutes: React.FC = () => {
<Route path="/clusterSettings" element={<ClusterSettingsPage />} />
<Route path="/acceleratorProfiles/*" element={<AcceleratorProfileRoutes />} />
<Route path="/servingRuntimes/*" element={<CustomServingRuntimeRoutes />} />
{isConnectionTypesAvailable ? (
<Route path="/connectionTypes" element={<ConnectionTypesPage />} />
) : null}
<Route path="/modelRegistrySettings/*" element={<ModelRegistrySettingsRoutes />} />
<Route path="/groupSettings" element={<GroupSettingsPage />} />
</>
Expand Down
6 changes: 3 additions & 3 deletions frontend/src/concepts/areas/const.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ export const SupportedAreasStateMap: SupportedAreasState = {
featureFlags: ['disableCustomServingRuntimes'],
reliantAreas: [SupportedArea.MODEL_SERVING],
},
[SupportedArea.CONNECTION_TYPES]: {
featureFlags: ['disableConnectionTypes'],
},
[SupportedArea.DS_PIPELINES]: {
featureFlags: ['disablePipelines'],
requiredComponents: [StackComponent.DS_PIPELINES],
Expand Down Expand Up @@ -124,7 +127,4 @@ export const SupportedAreasStateMap: SupportedAreasState = {
requiredComponents: [StackComponent.MODEL_REGISTRY],
requiredCapabilities: [StackCapability.SERVICE_MESH, StackCapability.SERVICE_MESH_AUTHZ],
},
[SupportedArea.DATA_CONNECTIONS_TYPES]: {
featureFlags: ['disableConnectionTypes'],
},
};
3 changes: 1 addition & 2 deletions frontend/src/concepts/areas/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export enum SupportedArea {
CLUSTER_SETTINGS = 'cluster-settings',
USER_MANAGEMENT = 'user-management',
ACCELERATOR_PROFILES = 'accelerator-profiles',
CONNECTION_TYPES = 'connections-types',

/* DS Projects specific areas */
DS_PROJECTS_PERMISSIONS = 'ds-projects-permission',
Expand All @@ -61,8 +62,6 @@ export enum SupportedArea {

/* Model Registry areas */
MODEL_REGISTRY = 'model-registry',

DATA_CONNECTIONS_TYPES = 'data-connections-types',
}

/** Components deployed by the Operator. Part of the DSC Status. */
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/concepts/connectionTypes/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ export type ConnectionTypeField =
export type ConnectionTypeConfigMap = K8sResourceCommon & {
metadata: {
name: string;
annotations: DisplayNameAnnotations & {
annotations?: DisplayNameAnnotations & {
'opendatahub.io/enabled'?: 'true' | 'false';
'opendatahub.io/username'?: string;
};
labels: DashboardLabels & {
labels?: DashboardLabels & {
'opendatahub.io/connection-type': 'true';
};
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SupportedArea, useIsAreaAvailable } from '~/concepts/areas';

const useConnectionTypesEnabled = (): boolean =>
useIsAreaAvailable(SupportedArea.DATA_CONNECTIONS_TYPES).status;
useIsAreaAvailable(SupportedArea.CONNECTION_TYPES).status;

export default useConnectionTypesEnabled;
Loading

0 comments on commit 9e9c333

Please sign in to comment.