Skip to content

Commit ebfda26

Browse files
author
Yehudit Kerido
committed
feat(ws): Notebooks 2.0 // Frontend // Fetch workspaces
Signed-off-by: Yehudit Kerido <[email protected]>
1 parent e89096b commit ebfda26

File tree

7 files changed

+154
-88
lines changed

7 files changed

+154
-88
lines changed

workspaces/backend/internal/models/workspaces/funcs.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ func NewWorkspaceModelFromWorkspace(ws *kubefloworgv1beta1.Workspace, wsk *kubef
120120
ImageConfig: buildImageConfig(ws, wsk),
121121
PodConfig: buildPodConfig(ws, wsk),
122122
},
123+
EndPoints: buildEndpointsList(ws, wsk),
123124
},
124125
Activity: Activity{
125126
LastActivity: ws.Status.Activity.LastActivity,
@@ -132,6 +133,37 @@ func NewWorkspaceModelFromWorkspace(ws *kubefloworgv1beta1.Workspace, wsk *kubef
132133
return workspaceModel
133134
}
134135

136+
func buildEndpointsList(ws *kubefloworgv1beta1.Workspace, wsk *kubefloworgv1beta1.WorkspaceKind) []EndPoints {
137+
var endpoints []EndPoints
138+
139+
// Return an empty list if wsk is nil.
140+
if wsk == nil {
141+
return endpoints
142+
}
143+
144+
// Get the image configuration from the WorkspaceKind's PodTemplate options.
145+
imageConfig := wsk.Spec.PodTemplate.Options.ImageConfig
146+
147+
for _, val := range imageConfig.Values {
148+
if len(val.Spec.Ports) == 0 {
149+
continue
150+
}
151+
firstPort := val.Spec.Ports[0]
152+
portStr := fmt.Sprintf("%d", firstPort.Port)
153+
displayName := firstPort.DisplayName
154+
if displayName == "" {
155+
displayName = val.Id
156+
}
157+
ep := EndPoints{
158+
DisplayName: displayName,
159+
Port: portStr,
160+
}
161+
endpoints = append(endpoints, ep)
162+
}
163+
164+
return endpoints
165+
}
166+
135167
func wskExists(wsk *kubefloworgv1beta1.WorkspaceKind) bool {
136168
return wsk != nil && wsk.UID != ""
137169
}

workspaces/backend/internal/models/workspaces/types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ type PodTemplate struct {
5555
PodMetadata PodMetadata `json:"pod_metadata"`
5656
Volumes PodVolumes `json:"volumes"`
5757
Options PodTemplateOptions `json:"options"`
58+
EndPoints []EndPoints `json:"end_points"`
59+
}
60+
61+
type EndPoints struct {
62+
DisplayName string `json:"display_name"`
63+
Port string `json:"port"`
5864
}
5965

6066
type PodMetadata struct {
Binary file not shown.

workspaces/frontend/src/__tests__/cypress/cypress/tests/mocked/workspace.mock.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import type { Workspace } from '~/shared/types';
21
import { WorkspaceState } from '~/shared/types';
32

43
const generateMockWorkspace = (
@@ -21,7 +20,7 @@ const generateMockWorkspace = (
2120
state: WorkspaceState;
2221
state_message: string;
2322
pod_template: {
24-
pod_metadata: { labels: {}; annotations: {} };
23+
pod_metadata: { labels: object; annotations: object };
2524
volumes: {
2625
home: { pvc_name: string; mount_path: string; readOnly: boolean };
2726
data: { pvc_name: string; mount_path: string; readOnly: boolean }[];
@@ -44,8 +43,9 @@ const generateMockWorkspace = (
4443
};
4544
};
4645
};
47-
image_config: { current: string; desired: string; redirect_chain: any[] };
48-
pod_config: { current: string; desired: string; redirect_chain: any[] };
46+
image_config: { current: string; desired: string; redirect_chain: never[] };
47+
pod_config: { current: string; desired: string; redirect_chain: never[] };
48+
end_points: { display_name: string; port: string }[];
4949
};
5050
activity: { last_activity: number; last_update: number };
5151
} => {
@@ -68,6 +68,12 @@ const generateMockWorkspace = (
6868
? 'Workspace is paused.'
6969
: 'Workspace is operational.',
7070
pod_template: {
71+
end_points: [
72+
{
73+
display_name: 'Jupyter-lab',
74+
port: '8888',
75+
},
76+
],
7177
pod_metadata: {
7278
labels: {},
7379
annotations: {},

workspaces/frontend/src/app/pages/Workspaces/WorkspaceConnectAction.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ export const WorkspaceConnectAction: React.FunctionComponent<WorkspaceConnectAct
3333
};
3434

3535
const onClickConnect = () => {
36-
openEndpoint(workspace.podTemplate.endpoints[0].port);
36+
openEndpoint(workspace.pod_template.end_points[0].port);
3737
};
3838

3939
const openEndpoint = (port: string) => {
@@ -51,7 +51,7 @@ export const WorkspaceConnectAction: React.FunctionComponent<WorkspaceConnectAct
5151
onClick={onToggleClick}
5252
isExpanded={open}
5353
isFullWidth
54-
isDisabled={workspace.status.state !== WorkspaceState.Running}
54+
isDisabled={workspace.state !== WorkspaceState.Running}
5555
splitButtonItems={[
5656
<MenuToggleAction
5757
id="connect-endpoint-button"
@@ -67,9 +67,9 @@ export const WorkspaceConnectAction: React.FunctionComponent<WorkspaceConnectAct
6767
shouldFocusToggleOnSelect
6868
>
6969
<DropdownList>
70-
{workspace.podTemplate.endpoints.map((endpoint) => (
70+
{workspace.pod_template.end_points.map((endpoint) => (
7171
<DropdownItem value={endpoint.port} key={`${workspace.name}-${endpoint.port}`}>
72-
{endpoint.displayName}
72+
{endpoint.display_name}
7373
</DropdownItem>
7474
))}
7575
</DropdownList>

workspaces/frontend/src/app/pages/Workspaces/Workspaces.tsx

Lines changed: 98 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ export const Workspaces: React.FunctionComponent = () => {
258258
};
259259

260260
const workspaceDefaultActions = (workspace: Workspace): IActions => {
261-
const workspaceState = workspace.status.state;
261+
const workspaceState = workspace.state;
262262
const workspaceActions = [
263263
{
264264
title: 'View Details',
@@ -400,86 +400,104 @@ export const Workspaces: React.FunctionComponent = () => {
400400
Create Workspace
401401
</Button>
402402
</Content>
403-
<Table aria-label="Sortable table" ouiaId="SortableTable">
404-
<Thead>
405-
<Tr>
406-
<Th />
407-
{Object.values(columnNames).map((columnName, index) => (
408-
<Th key={`${columnName}-col-name`} sort={getSortParams(index)}>
409-
{columnName}
410-
</Th>
411-
))}
412-
<Th screenReaderText="Primary action" />
413-
</Tr>
414-
</Thead>
415-
{sortedWorkspaces.map((workspace, rowIndex) => (
416-
<Tbody
417-
id="workspaces-table-content"
418-
key={rowIndex}
419-
isExpanded={isWorkspaceExpanded(workspace)}
420-
data-testid="table-body"
421-
>
422-
<Tr id={`workspaces-table-row-${rowIndex + 1}`}>
423-
<Td
424-
expand={{
425-
rowIndex,
426-
isExpanded: isWorkspaceExpanded(workspace),
427-
onToggle: () =>
428-
setWorkspaceExpanded(workspace, !isWorkspaceExpanded(workspace)),
429-
}}
430-
/>
431-
<Td dataLabel={columnNames.name}>{workspace.name}</Td>
432-
<Td dataLabel={columnNames.kind}>
433-
{kindLogoDict[workspace.kind] ? (
434-
<Tooltip content={workspace.kind}>
435-
<Brand
436-
src={kindLogoDict[workspace.kind]}
437-
alt={workspace.kind}
438-
style={{ width: '20px', height: '20px', cursor: 'pointer' }}
439-
/>
440-
</Tooltip>
441-
) : (
442-
<Tooltip content={workspace.kind}>
443-
<CodeIcon />
444-
</Tooltip>
445-
)}
446-
</Td>
447-
<Td dataLabel={columnNames.image}>{workspace.options.imageConfig}</Td>
448-
<Td dataLabel={columnNames.podConfig}>{workspace.options.podConfig}</Td>
449-
<Td dataLabel={columnNames.state}>
450-
<Label color={stateColors[workspace.status.state]}>
451-
{WorkspaceState[workspace.status.state]}
452-
</Label>
453-
</Td>
454-
<Td dataLabel={columnNames.homeVol}>{workspace.podTemplate.volumes.home}</Td>
455-
<Td dataLabel={columnNames.cpu}>{`${workspace.cpu}%`}</Td>
456-
<Td dataLabel={columnNames.ram}>{formatRam(workspace.ram)}</Td>
457-
<Td dataLabel={columnNames.lastActivity}>
458-
<Timestamp
459-
date={new Date(workspace.status.activity.lastActivity)}
460-
tooltip={{ variant: TimestampTooltipVariant.default }}
461-
>
462-
1 hour ago
463-
</Timestamp>
464-
</Td>
465-
<Td>
466-
<WorkspaceConnectAction workspace={workspace} />
467-
</Td>
468-
<Td isActionCell data-testid="action-column">
469-
<ActionsColumn
470-
items={workspaceDefaultActions(workspace).map((action) => ({
471-
...action,
472-
'data-testid': `action-${typeof action.title === 'string' ? action.title.toLowerCase() : ''}`,
473-
}))}
474-
/>
475-
</Td>
403+
{/* Show a loading spinner if data is still loading */}
404+
{!loaded ? (
405+
<Spinner size="xl" />
406+
) : (
407+
<Table
408+
data-testid="workspaces-table"
409+
aria-label="Sortable table"
410+
ouiaId="SortableTable"
411+
>
412+
<Thead>
413+
<Tr>
414+
<Th />
415+
{Object.values(columnNames).map((columnName, index) => (
416+
<Th key={`${columnName}-col-name`} sort={getSortParams(index)}>
417+
{columnName}
418+
</Th>
419+
))}
420+
<Th screenReaderText="Primary action" />
476421
</Tr>
477-
{isWorkspaceExpanded(workspace) && (
478-
<ExpandedWorkspaceRow workspace={workspace} columnNames={columnNames} />
479-
)}
480-
</Tbody>
481-
))}
482-
</Table>
422+
</Thead>
423+
{sortedWorkspaces.map((workspace, rowIndex) => (
424+
<Tbody
425+
id="workspaces-table-content"
426+
key={rowIndex}
427+
isExpanded={isWorkspaceExpanded(workspace)}
428+
data-testid="table-body"
429+
>
430+
<Tr
431+
id={`workspaces-table-row-${rowIndex + 1}`}
432+
data-testid={`workspace-row-${rowIndex}`}
433+
>
434+
<Td
435+
expand={{
436+
rowIndex,
437+
isExpanded: isWorkspaceExpanded(workspace),
438+
onToggle: () =>
439+
setWorkspaceExpanded(workspace, !isWorkspaceExpanded(workspace)),
440+
}}
441+
/>
442+
<Td data-testid="workspace-name" dataLabel={columnNames.name}>
443+
{workspace.name}
444+
</Td>
445+
<Td dataLabel={columnNames.kind}>
446+
{kindLogoDict[workspace.workspace_kind.name] ? (
447+
<Tooltip content={workspace.workspace_kind.name}>
448+
<Brand
449+
src={kindLogoDict[workspace.workspace_kind.name]}
450+
alt={workspace.workspace_kind.name}
451+
style={{ width: '20px', height: '20px', cursor: 'pointer' }}
452+
/>
453+
</Tooltip>
454+
) : (
455+
<Tooltip content={workspace.workspace_kind.name}>
456+
<CodeIcon />
457+
</Tooltip>
458+
)}
459+
</Td>
460+
<Td dataLabel={columnNames.image}>
461+
{workspace.pod_template.options.image_config.current.display_name}
462+
</Td>
463+
<Td data-testid="pod-config" dataLabel={columnNames.podConfig}>
464+
{workspace.pod_template.options.pod_config.current.display_name}
465+
</Td>
466+
<Td data-testid="state-label" dataLabel={columnNames.state}>
467+
<Label color={stateColors[workspace.state]}>{workspace.state}</Label>{' '}
468+
</Td>
469+
<Td dataLabel={columnNames.homeVol}>
470+
{workspace.pod_template.volumes.home.pvc_name}
471+
</Td>
472+
<Td dataLabel={columnNames.cpu}>{formatCPU(getCpuValue(workspace))}</Td>
473+
<Td dataLabel={columnNames.ram}>{formatRam(getRamValue(workspace))}</Td>
474+
<Td dataLabel={columnNames.lastActivity}>
475+
<Timestamp
476+
date={new Date(workspace.activity.last_activity)}
477+
tooltip={{ variant: TimestampTooltipVariant.default }}
478+
>
479+
1 hour ago
480+
</Timestamp>
481+
</Td>
482+
<Td>
483+
<WorkspaceConnectAction workspace={workspace} />
484+
</Td>
485+
<Td isActionCell data-testid="action-column">
486+
<ActionsColumn
487+
items={workspaceDefaultActions(workspace).map((action) => ({
488+
...action,
489+
'data-testid': `action-${typeof action.title === 'string' ? action.title.toLowerCase() : ''}`,
490+
}))}
491+
/>
492+
</Td>
493+
</Tr>
494+
{isWorkspaceExpanded(workspace) && (
495+
<ExpandedWorkspaceRow workspace={workspace} columnNames={columnNames} />
496+
)}
497+
</Tbody>
498+
))}
499+
</Table>
500+
)}
483501
{isActionAlertModalOpen && chooseAlertModal()}
484502
<DeleteModal
485503
isOpen={workspaceToDelete != null}

workspaces/frontend/src/shared/types.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ export interface Workspace {
3232
state: WorkspaceState;
3333
state_message: string;
3434
pod_template: {
35+
end_points: {
36+
display_name: string;
37+
port: string;
38+
}[];
3539
pod_metadata: {
3640
labels: Record<string, string>;
3741
annotations: Record<string, string>;

0 commit comments

Comments
 (0)