Skip to content

Commit

Permalink
feat(cli-terminal): Create cli-terminal
Browse files Browse the repository at this point in the history
  • Loading branch information
abhinandan13jan authored and root committed Mar 27, 2020
1 parent a5bc8fc commit 9881603
Show file tree
Hide file tree
Showing 20 changed files with 394 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import * as React from 'react';
import { Button } from '@patternfly/react-core';
import CloudShellBody from './CloudShellBody';
import { connect } from 'react-redux';
import { RootState } from '@console/internal/redux';
import * as UIActions from '@console/internal/actions/ui';
import CloudShellDrawer from './CloudShellDrawer';
import CloudShellTerminal from './CloudshellTerminal';

const CloudShell: React.FC = () => {
const [open, setOpen] = React.useState(false);
return (
<>
{/* Remove this button once actual terminal is in place */}
<Button variant="control" onClick={() => setOpen(!open)}>
Open Drawer
</Button>
<CloudShellDrawer open={open} onClose={() => setOpen(false)}>
<CloudShellBody />
</CloudShellDrawer>
</>
);
type CloudshellContentProps = {
isTerminalExpanded: boolean;
toggleTerminal: any;
};

export default CloudShell;
const CloudshellContent: React.FC<CloudshellContentProps> = ({
isTerminalExpanded,
toggleTerminal,
}) => (
<CloudShellDrawer open={isTerminalExpanded} onClose={() => toggleTerminal()}>
<CloudShellTerminal />
</CloudShellDrawer>
);

const cloudshellStateToProps = ({ UI }: RootState) => ({
isTerminalExpanded: UI.getIn(['terminal', 'isExpanded']),
});

const cloudlPropsToState = {
toggleTerminal: UIActions.terminalDrawerToggleExpanded,
};
export default connect(cloudshellStateToProps, cloudlPropsToState)(CloudshellContent);

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,18 @@ const CloudShellDrawer: React.FC<CloudShellDrawerProps> = ({ open, children, onC
</FlexItem>
</Flex>
);
return (
open && (
<Drawer
open={expanded}
height={height}
header={header}
maxHeight={`calc(100vh - ${getMastheadHeight()}px)`}
onChange={handleChange}
resizable
>
{children}
</Drawer>
)
);
return open ? (
<Drawer
open={expanded}
height={height}
header={header}
maxHeight={`calc(100vh - ${getMastheadHeight()}px)`}
onChange={handleChange}
resizable
>
{children}
</Drawer>
) : null;
};

export default CloudShellDrawer;
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import * as React from 'react';
import { TerminalIcon } from '@patternfly/react-icons';
import { Button, ToolbarItem } from '@patternfly/react-core';
import { connectToFlags } from '@console/internal/reducers/features';
import { FLAG_DEVWORKSPACE } from '../../consts';

type terminalToggle = () => void;

type TerminalTooliconProps = {
flags: Record<string, any>;
terminalToggle: terminalToggle;
};

const TerminalToolicon: React.FC<TerminalTooliconProps> = ({ flags, terminalToggle }) => {
if (!flags[FLAG_DEVWORKSPACE]) return null;
return (
<ToolbarItem>
<Button variant="plain" aria-label="Terminal" onClick={terminalToggle}>
<TerminalIcon className="co-masthead-icon" />
</Button>
</ToolbarItem>
);
};

export default connectToFlags(FLAG_DEVWORKSPACE)(TerminalToolicon);
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.co-cloud-shell-resource {
&__iframe {
height: 100%;
width: 100%;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import * as React from 'react';
import * as _ from 'lodash';
import { LoadingBox } from '@console/internal/components/utils';
import { CloudShellResource } from './utils/cloudshell-resource';
import './CloudshellResource.scss';

type CloudshellResourceProps = {
resources?: { cloudShell: { data: CloudShellResource } };
};

const CloudshellResource: React.FC<CloudshellResourceProps> = (props) => {
const cloudShell = _.get(props, ['resources', 'cloudShell', 'data']);
const phase = _.get(cloudShell, ['status', 'phase']);
const ideUrl = _.get(cloudShell, ['status', 'ideUrl']);
if (phase === 'Running' && ideUrl)
return <iframe title="cloudshell" className="co-cloud-shell-resource__iframe" src={ideUrl} />;
return <LoadingBox />;
};

export default CloudshellResource;
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as React from 'react';
import { referenceForModel, k8sCreate, k8sList } from '@console/internal/module/k8s';
import { LoadingBox, Firehose, FirehoseResource } from '@console/internal/components/utils';

import { WorkspaceModel } from '../../models';
import CloudshellResource from './CloudshellResource';
import { newCloudShellWorkSpace, CloudShellResource } from './utils/cloudshell-resource';

type CloudShellTerminalProps = {};

const CloudShellTerminal: React.FC<CloudShellTerminalProps> = () => {
/* Change dedicated name and namespace as per update */
const namespace = 'che-workspace-controller';
const name = 'cloudshell-userID';
const [cloudShell, setCloudShell] = React.useState<CloudShellResource>(null);

const createCloudShell = () => {
k8sCreate(WorkspaceModel, newCloudShellWorkSpace(name, namespace))
.then((newShell) => {
setCloudShell(newShell);
})
.catch((error) => {
// eslint-disable-next-line no-console
console.warn('Cloudshell creation Error', error);
});
};

React.useEffect(() => {
k8sList(WorkspaceModel, { ns: namespace })
.then((existingShells) => {
if (!existingShells || existingShells.length === 0) {
createCloudShell();
} else {
setCloudShell(existingShells[0]);
}
})
.catch((error) => {
// eslint-disable-next-line no-console
console.warn('Cloud Shell resuming error', error);
});
}, []);

if (cloudShell && cloudShell.metadata) {
const resources: FirehoseResource[] = [
{
kind: referenceForModel(WorkspaceModel),
name: cloudShell.metadata.name,
namespace,
prop: `cloudShell`,
isList: false,
},
];

return (
<Firehose resources={resources}>
<CloudshellResource />
</Firehose>
);
}
return <LoadingBox />;
};

export default CloudShellTerminal;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import * as React from 'react';
import { shallow, ShallowWrapper } from 'enzyme';
import { LoadingBox } from '@console/internal/components/utils';
import CloudShellResource from '../CloudshellResource';

let CloudShellWrapper: ShallowWrapper;
type CloudShellResourceProps = React.ComponentProps<typeof CloudShellResource>;
const cloudShellResourceProps: CloudShellResourceProps = {
resources: {
cloudShell: {
data: {
metadata: {
name: 'cloudshell-userid',
namespace: 'default',
},
kind: 'Workspace',
status: {
phase: 'Starting',
ideUrl: 'dummy',
},
},
},
},
};

describe('CloudShellResourceComponent', () => {
beforeAll(() => {
CloudShellWrapper = shallow(<CloudShellResource {...cloudShellResourceProps} />);
});

it('should render loading box', () => {
expect(CloudShellWrapper.find(LoadingBox).exists()).toBe(true);
});
});

describe('CloudShellResourceComponent', () => {
beforeAll(() => {
cloudShellResourceProps.resources.cloudShell.data.status.phase = 'Running';
CloudShellWrapper = shallow(<CloudShellResource {...cloudShellResourceProps} />);
});

it('should render loading box', () => {
expect(CloudShellWrapper.find('iframe').exists()).toBe(true);
expect(CloudShellWrapper.find('iframe').prop('src')).toBe(
cloudShellResourceProps.resources.cloudShell.data.status.ideUrl,
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { newCloudShellWorkSpace } from '../utils/cloudshell-resource';

describe('CloudShell Resource Util', () => {
const expectedData = {
name: 'cloud-shell',
namespace: 'default',
kind: 'Workspace',
};

const newResource = newCloudShellWorkSpace(expectedData.name, expectedData.namespace);

it('creates new cloudshell Resource with correct name, kind, metadata', () => {
expect(newResource.kind).toEqual(expectedData.kind);
expect(newResource.metadata.name).toEqual(expectedData.name);
expect(newResource.metadata.namespace).toEqual(expectedData.namespace);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { action, ActionType } from 'typesafe-actions';

export enum Actions {
TerminalDrawerToggleExpanded = 'terminalDrawerExpanded',
}

export const terminalDrawerToggleExpanded = () => action(Actions.TerminalDrawerToggleExpanded);

const actions = {
terminalDrawerToggleExpanded,
};

export type TerminalAction = ActionType<typeof actions>;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Map } from 'immutable';
import { TerminalAction, Actions } from './action';

export type State = Map<string, any>;

export default (state: State, action: TerminalAction) => {
if (!state) {
return Map({
terminal: { isExpanded: false },
});
}
if (action.type === Actions.TerminalDrawerToggleExpanded)
return state.setIn(['terminal', 'isExpanded'], !state.getIn(['terminal', 'isExpanded']));

return state;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
type environment = {
value: string;
name: string;
};
type Component = {
type?: string;
id?: string;
memoryLimit?: string;
alias?: string;
image?: string;
args?: string[];
env?: environment[];
};

interface Devfile {
metadata: {
name: string;
};
components: Component[];
apiVersion?: string;
}

export interface CloudShellResource {
metadata: {
name: string;
namespace: string;
};
status?: {
phase: string;
ideUrl: string;
};
spec?: {
started?: boolean;
devfile?: Devfile;
};
apiVersion?: string;
kind: string;
}

export const newCloudShellWorkSpace = (name: string, namespace: string): CloudShellResource => ({
apiVersion: 'workspace.che.eclipse.org/v1alpha1',
kind: 'Workspace',
metadata: {
name,
namespace,
},
spec: {
started: true,
devfile: {
apiVersion: '0.0.1',
metadata: {
name: 'cloud-shell',
},
components: [
{
alias: 'cloud-shell',
type: 'cheEditor',
id: 'eclipse/cloud-shell/nightly',
},
{
type: 'dockerimage',
memoryLimit: '256Mi',
alias: 'dev',
image: 'quay.io/eclipse/che-sidecar-openshift-connector:0.1.2-2601509',
args: ['tail', '-f', '/dev/null'],
env: [
{
value: '\\[\\e[34m\\]>\\[\\e[m\\]\\[\\e[33m\\]>\\[\\e[m\\]',
name: 'PS1',
},
],
},
],
},
},
});
1 change: 1 addition & 0 deletions frontend/packages/console-app/src/consts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const FLAG_DEVWORKSPACE = 'DEVWORKSPACE';

0 comments on commit 9881603

Please sign in to comment.