stitch-ui
Version:
350 lines (336 loc) • 10.6 kB
JavaScript
/* global window */
import React from "react";
import PropTypes from "prop-types";
import { List } from "immutable";
import { connect } from "react-redux";
import qs from "query-string";
import PipelineEditor from "../../pipelineeditor/components/PipelineEditor";
import * as pipelineActions from "../../pipelineeditor/actions";
import * as actions from "../actions";
import { Pipeline, PipelineStage } from "../../models";
import { PIPELINE_OUTPUT_ARRAY } from "../../models/Pipeline";
import { User } from "../../user";
import UserModal from "./UserModal";
import OutputPanel from "./OutputPanel";
import { Button, Banner, Panel } from "../../core";
const savedPipelinesKey = "debugconsole/pipelines";
const pipelineKey = appId => `debugconsole/${appId}`;
const defaultNewPipeline = new Pipeline({
stages: List([
new PipelineStage({
action: "literal",
args: { items: [{ x: 1, y: "foo" }] }
})
]),
output: PIPELINE_OUTPUT_ARRAY
});
const keepPipeline = (appId, pipeline, userId) => {
if (typeof window === "undefined" || !window.localStorage) {
return;
}
const savedPipelines = JSON.parse(
window.localStorage.getItem(savedPipelinesKey) || "{}"
);
savedPipelines[appId] = { pipeline, userId };
window.localStorage.setItem(
savedPipelinesKey,
JSON.stringify(savedPipelines)
);
};
const restorePipeline = appId => {
if (typeof window === "undefined" || !window.localStorage) {
return null;
}
const savedPipelines = JSON.parse(
window.localStorage.getItem(savedPipelinesKey) || "{}"
);
return savedPipelines[appId];
};
class DebugConsole extends React.Component {
componentDidMount() {
const {
resetDebugConsole,
resetUsers,
loadUser,
initPipeline,
app
} = this.props;
const location = this.props.history.location;
location.query = qs.parse(location.search);
resetDebugConsole();
resetUsers();
let shouldRestoreUser = true;
let shouldRestorePipeline = true;
if (location && location.query.user_id) {
loadUser(location.query.user_id);
shouldRestoreUser = false;
}
let initialPipeline = defaultNewPipeline;
if (
typeof window !== "undefined" &&
window.localStorage &&
location &&
location.query.pipelineKey
) {
try {
const jsonStages = JSON.parse(
window.localStorage.getItem(location.query.pipelineKey)
);
const stages = List(jsonStages.map(s => new PipelineStage(s)));
initialPipeline = new Pipeline({
output: PIPELINE_OUTPUT_ARRAY,
stages
});
shouldRestorePipeline = false;
} catch (err) {
this.props.setError(err);
}
}
if (shouldRestoreUser || shouldRestorePipeline) {
const restoredPipeline = restorePipeline(app._id);
if (restoredPipeline && shouldRestoreUser) {
loadUser(restoredPipeline.userId);
}
if (restoredPipeline && shouldRestorePipeline) {
initialPipeline = new Pipeline({
output: restoredPipeline.pipeline.output,
stages: List(
restoredPipeline.pipeline.pipeline.map(s => new PipelineStage(s))
)
});
}
}
initPipeline(initialPipeline);
}
render() {
const {
app,
error,
adminError,
output,
users,
loadUsers,
setError,
services,
executePipeline,
clearOutput,
lastUserId,
selectUser,
selectedUser,
clearSelectedUser
} = this.props;
return (
<div>
<div className="section-header">
<div className="section-header-title">
<div className="section-header-title-text">Debug Console</div>
</div>
</div>
<div className="tabs-content">
<Banner
message="All stages of pipelines executed in the debug console will be fully run against services configured."
info
icon
clearable
/>
<UserModal
users={users}
loadUsers={loadUsers}
lastUserId={lastUserId}
selectUser={selectUser}
selectedUser={selectedUser}
open={(this.state || {}).showUserModal}
onClose={() => {
this.setState({ showUserModal: false });
}}
/>
<Panel>
<div className="panel-header">
<div className="panel-header-title">
<div className="steps-step">
<div className="steps-step-num">1</div>
<h5 className="steps-step-title">
Execute pipeline as user: {" "}
</h5>
</div>
{selectedUser
? <span>
<div className="users-list-item">
<div className="users-list-item-column users-list-item-pic">
{selectedUser.getImage()}
</div>
<div className="users-list-item-column users-list-item-name">
{selectedUser.getDisplayName()}
</div>
</div>
<Button
type="button"
className="button button-is-small"
onClick={clearSelectedUser}
>
Clear
</Button>
</span>
: <Button
type="button"
className="button button-is-small"
onClick={() => {
this.setState({ showUserModal: true });
}}
>
Select User
</Button>}
</div>
</div>
<div className="panel-content splitpanel-debug-content">
<div className="debugconsole-splitpanel-input">
<div className="steps-step">
<div className="steps-step-num">2</div>
<h5 className="steps-step-title">Pipeline to execute:</h5>
</div>
<div>
<Button
small
primary
onClick={() =>
this.pipelineEditor.getWrappedInstance().child.submit()}
>
Execute
</Button>
</div>
<PipelineEditor
noOutputType
ref={pipelineEditor => (this.pipelineEditor = pipelineEditor)}
pipelineKey={pipelineKey(app._id)}
onSubmit={pipeline => {
if (!selectedUser) {
setError("Must select a user first.");
return;
}
keepPipeline(
app._id,
pipeline.toRaw(),
selectedUser.getId()
);
executePipeline(
pipeline.toRaw().pipeline,
selectedUser.getId()
);
}}
saveButtonText="Execute"
services={services}
/>
</div>
<OutputPanel
error={error}
clearOutput={clearOutput}
adminError={adminError}
output={output}
/>
</div>
</Panel>
</div>
</div>
);
}
}
const mapStateToProps = state => {
const { app } = state.app.root;
const {
output,
error,
adminError,
selectedUser
} = state.debugconsole.debugconsole;
const { services } = state.app.root;
const {
users,
loadingUsers,
loadingUsersError,
lastUserId
} = state.debugconsole.users;
return {
output,
error,
adminError,
services,
app,
users,
loadingUsers,
loadingUsersError,
lastUserId,
selectedUser
};
};
const mapDispatchToProps = (dispatch, ownProps) => ({
resetDebugConsole: () => dispatch(actions.resetDebugConsole()),
resetUsers: () => dispatch(actions.resetUsersAction()),
resetError: () => dispatch(actions.resetError()),
setError: msg => dispatch(actions.setError(msg)),
selectUser: user => dispatch(actions.selectUser(user)),
clearOutput: () => dispatch(actions.clearOutput()),
clearSelectedUser: () => dispatch(actions.clearSelectedUser()),
loadUser: userId =>
dispatch(actions.loadUser(ownProps.app.groupId, ownProps.app._id, userId)),
loadUsers: filter =>
dispatch(actions.loadUsers(ownProps.app.groupId, ownProps.app._id, filter)),
initPipeline: pipelineObj =>
dispatch(
pipelineActions.initPipeline({
pipelineKey: pipelineKey(ownProps.app._id),
pipelineObj
})
),
executePipeline: (pipelineObj, userId) =>
dispatch(
actions.executePipeline(
ownProps.app.groupId,
ownProps.app._id,
userId,
pipelineObj
)
)
});
DebugConsole.propTypes = {
// Data
app: PropTypes.shape({ _id: PropTypes.string.isRequired }).isRequired,
error: PropTypes.string,
adminError: PropTypes.string,
output: PropTypes.object, // eslint-disable-line react/forbid-prop-types
users: PropTypes.instanceOf(List).isRequired,
services: PropTypes.objectOf(
PropTypes.shape({
type: PropTypes.string.isRequired
})
).isRequired,
lastUserId: PropTypes.string,
location: PropTypes.object, // eslint-disable-line react/forbid-prop-types
selectedUser: PropTypes.instanceOf(User),
history: PropTypes.objectOf(
PropTypes.shape({
location: PropTypes.any
})
),
// Functions
loadUsers: PropTypes.func.isRequired,
setError: PropTypes.func.isRequired,
executePipeline: PropTypes.func.isRequired,
selectUser: PropTypes.func.isRequired,
clearSelectedUser: PropTypes.func.isRequired,
resetDebugConsole: PropTypes.func.isRequired,
resetUsers: PropTypes.func.isRequired,
loadUser: PropTypes.func.isRequired,
initPipeline: PropTypes.func.isRequired,
clearOutput: PropTypes.func.isRequired
};
DebugConsole.defaultProps = {
error: null,
adminError: null,
output: undefined,
lastUserId: null,
selectedUser: null,
location: null,
match: null,
history: null
};
export default connect(mapStateToProps, mapDispatchToProps)(DebugConsole);