enabling multi dashboards API+client

This commit is contained in:
morsh 2017-04-27 18:49:07 -07:00
Родитель 4a95285161
Коммит a83a7076e8
8 изменённых файлов: 229 добавлений и 24 удалений

Просмотреть файл

@ -44,7 +44,132 @@ router.post('/dashboard.js', (req, res) => {
}) })
}); });
function isAuthoeizedToSetup(req) {
router.get('/dashboards', (req, res) => {
let privateDashboard = path.join(__dirname, '..', 'dashboards');
let preconfDashboard = path.join(__dirname, '..', 'dashboards', 'preconfigured');
let script = '';
let files = fs.readdirSync(privateDashboard);
if (files && files.length) {
files.forEach((fileName) => {
let filePath = path.join(privateDashboard, fileName);
let stats = fs.statSync(filePath);
if (stats.isFile()) {
let content = fs.readFileSync(filePath, 'utf8');
// Ensuing this dashboard is loaded into the dashboards array on the page
script += `
(function (window) {
var dashboard = (function () {
${content}
})();
window.dashboardDefinitions = window.dashboardDefinitions || [];
window.dashboardDefinitions.push(dashboard);
})(window);
`;
}
});
}
let templates = fs.readdirSync(preconfDashboard);
if (templates && templates.length) {
templates.forEach((fileName) => {
let filePath = path.join(preconfDashboard, fileName);
let stats = fs.statSync(filePath);
if (stats.isFile()) {
let content = fs.readFileSync(filePath, 'utf8');
// Ensuing this dashboard is loaded into the dashboards array on the page
script += `
(function (window) {
var dashboardTemplate = (function () {
${content}
})();
window.dashboardTemplates = window.dashboardTemplates || [];
window.dashboardTemplates.push(dashboardTemplate);
})(window);
`;
}
});
}
res.send(script);
});
router.get('/dashboards/:id', (req, res) => {
let dashboardId = req.params.id;
let privateDashboard = path.join(__dirname, '..', 'dashboards');
let preconfDashboard = path.join(__dirname, '..', 'dashboards', 'preconfigured');
let script = '';
let files = fs.readdirSync(privateDashboard) || [];
// Make sure the array only contains files
files = files.filter(fileName => fs.statSync(path.join(privateDashboard, fileName)).isFile());
if (files.length) {
let dashboardFile = null;
let dashboardIndex = parseInt(dashboardId);
if (!isNaN(dashboardIndex) && files.length > dashboardIndex) {
dashboardFile = files[dashboardIndex];
}
if (!dashboardFile) {
files.forEach(fileName => {
let filePath = path.join(privateDashboard, fileName);
let stats = fs.statSync(filePath);
if (stats.isFile()) {
let dashboard = getJSONFromScript(filePath);
if (dashboard.url && dashboard.url.toLowerCase() === dashboardId.toLowerCase()) {
dashboardFile = fileName;
}
}
})
}
if (dashboardFile) {
let filePath = path.join(privateDashboard, dashboardFile);
let stats = fs.statSync(filePath);
if (stats.isFile()) {
let content = fs.readFileSync(filePath, 'utf8');
// Ensuing this dashboard is loaded into the dashboards array on the page
script += `
(function (window) {
var dashboard = (function () {
${content}
})();
window.dashboard = dashboard || null;
})(window);
`;
}
}
}
res.send(script);
});
router.post('/dashboards/:id', (req, res) => {
let dashboardName = req.params.dashboard;
var content = (req.body && req.body.script) || '';
console.dir(content);
fs.writeFile(path.join(__dirname, '..', 'dashboards', 'dashboard.private.js'), content, err => {
if (err) {
console.error(err);
return res.end(err);
}
res.end(content);
})
});
function isAuthorizedToSetup(req) {
if (!fs.existsSync(privateSetupPath)) { return true; } if (!fs.existsSync(privateSetupPath)) { return true; }
let configString = fs.readFileSync(privateSetupPath, 'utf8'); let configString = fs.readFileSync(privateSetupPath, 'utf8');
@ -61,7 +186,7 @@ function isAuthoeizedToSetup(req) {
router.get('/setup', (req, res) => { router.get('/setup', (req, res) => {
if (!isAuthoeizedToSetup(req)) { if (!isAuthorizedToSetup(req)) {
return res.send({ error: new Error('User is not authorized to setup') }); return res.send({ error: new Error('User is not authorized to setup') });
} }
@ -76,7 +201,7 @@ router.get('/setup', (req, res) => {
router.post('/setup', (req, res) => { router.post('/setup', (req, res) => {
if (!isAuthoeizedToSetup(req)) { if (!isAuthorizedToSetup(req)) {
return res.send({ error: new Error('User is not authorized to setup') }); return res.send({ error: new Error('User is not authorized to setup') });
} }
@ -95,4 +220,17 @@ router.post('/setup', (req, res) => {
module.exports = { module.exports = {
router router
}
function getJSONFromScript(filePath) {
if (!fs.existsSync(filePath)) { return {}; }
let jsonScript = {};
let stats = fs.statSync(filePath);
if (stats.isFile()) {
let content = fs.readFileSync(filePath, 'utf8');
eval('jsonScript = (function () { ' + content + ' })();');
}
return jsonScript;
} }

Просмотреть файл

@ -3,6 +3,7 @@ import * as request from 'xhr-request';
interface IConfigurationsActions { interface IConfigurationsActions {
loadConfiguration(): any; loadConfiguration(): any;
loadDashboard(id: string): any;
saveConfiguration(dashboard: IDashboardConfig): any; saveConfiguration(dashboard: IDashboardConfig): any;
failure(error: any): void; failure(error: any): void;
} }
@ -14,17 +15,33 @@ class ConfigurationsActions extends AbstractActions implements IConfigurationsAc
loadConfiguration() { loadConfiguration() {
return (dispatcher: (dashboard: IDashboardConfig) => void) => { return (dispatcher: (result: { dashboards: IDashboardConfig[], templates: IDashboardConfig[] }) => void) => {
this.getScript('/api/dashboard.js', () => { this.getScript('/api/dashboards', () => {
let dashboards: IDashboardConfig[] = (window as any)['dashboards']; let dashboards: IDashboardConfig[] = (window as any)['dashboardDefinitions'];
let templates: IDashboardConfig[] = (window as any)['dashboardTemplates'];
if (!dashboards || !dashboards.length) { if (!dashboards || !dashboards.length) {
return this.failure(new Error('Could not load configuration')); return this.failure(new Error('Could not load configuration'));
} }
let dashboard = dashboards[0]; return dispatcher({ dashboards, templates });
return dispatcher(dashboard); });
};
}
loadDashboard(id: string) {
return (dispatcher: (result: { dashboard: IDashboardConfig }) => void) => {
this.getScript('/api/dashboards/' + id, () => {
let dashboard: IDashboardConfig = (window as any)['dashboard'];
if (!dashboard) {
return this.failure(new Error('Could not load configuration for dashboard ' + id));
}
return dispatcher({ dashboard });
}); });
}; };
} }

Просмотреть файл

@ -35,7 +35,7 @@ export default class ConfigDashboard extends React.Component<IConfigDashboardPro
this.onSave = this.onSave.bind(this); this.onSave = this.onSave.bind(this);
this.onSaveGoToDashboard = this.onSaveGoToDashboard.bind(this); this.onSaveGoToDashboard = this.onSaveGoToDashboard.bind(this);
ConfigurationsActions.loadConfiguration(); //ConfigurationsActions.loadConfiguration();
} }
onParamChange(connectionKey, paramKey, value) { onParamChange(connectionKey, paramKey, value) {

Просмотреть файл

@ -12,6 +12,8 @@ import Chip from 'react-md/lib/Chips';
import AccountStore from '../../stores/AccountStore'; import AccountStore from '../../stores/AccountStore';
import AccountActions from '../../actions/AccountActions'; import AccountActions from '../../actions/AccountActions';
import ConfigurationsStore from '../../stores/ConfigurationsStore';
import './style.css'; import './style.css';
const avatarSrc = 'https://cloud.githubusercontent.com/assets/13041/19686250/971bf7f8-9ac0-11e6-975c-188defd82df1.png'; const avatarSrc = 'https://cloud.githubusercontent.com/assets/13041/19686250/971bf7f8-9ac0-11e6-975c-188defd82df1.png';
@ -47,9 +49,16 @@ export default class Navbar extends React.Component<any, any> {
this.setState(state); this.setState(state);
}); });
AccountActions.updateAccount(); AccountActions.updateAccount();
ConfigurationsStore.listen((state) => {
this.setState({
dashboards: state.dashboards
});
});
} }
render() { render() {
let { dashboards } = this.state;
let { children, title } = this.props; let { children, title } = this.props;
let pathname = '/'; let pathname = '/';
try { pathname = window.location.pathname; } catch (e) { } try { pathname = window.location.pathname; } catch (e) { }
@ -81,17 +90,6 @@ export default class Navbar extends React.Component<any, any> {
<ListItem <ListItem
key="2" key="2"
component={Link} component={Link}
href="/dashboard"
active={pathname === '/dashboard'}
leftIcon={<FontIcon>dashboard</FontIcon>}
tileClassName="md-list-tile--mini"
primaryText={'Dashboard'}
/>
),
(
<ListItem
key="3"
component={Link}
href="/setup" href="/setup"
active={pathname === '/setup'} active={pathname === '/setup'}
leftIcon={<FontIcon>settings</FontIcon>} leftIcon={<FontIcon>settings</FontIcon>}
@ -101,6 +99,30 @@ export default class Navbar extends React.Component<any, any> {
) )
]; ];
(dashboards || []).forEach((dashboard, index) => {
let name = dashboard.name || null;
let url = '/dashboard/' + (dashboard.url || index.toString());
let active = pathname === url;
if (!title && active && name) {
title = name;
}
navigationItems.push(
(
<ListItem
key={index + 4}
component={Link}
href={url}
active={active}
leftIcon={<FontIcon>{dashboard.icon || 'dashboard'}</FontIcon>}
tileClassName="md-list-tile--mini"
primaryText={name || 'Dashboard'}
/>
)
)
});
let toolbarActions = let toolbarActions =
this.state.account ? this.state.account ?
<Chip style={{ marginRight: 30 }} label={'Hello, ' + this.state.account.displayName} /> : <Chip style={{ marginRight: 30 }} label={'Hello, ' + this.state.account.displayName} /> :
@ -129,6 +151,7 @@ export default class Navbar extends React.Component<any, any> {
break; break;
default: default:
title = 'Ibex Dashboard'; title = 'Ibex Dashboard';
break; break;
} }

Просмотреть файл

@ -22,7 +22,7 @@ export default class Config extends React.Component<any, IDashboardState> {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
ConfigurationsActions.loadConfiguration(); //ConfigurationsActions.loadConfiguration();
} }
componentDidMount() { componentDidMount() {

Просмотреть файл

@ -23,7 +23,7 @@ export default class Dashboard extends React.Component<any, IDashboardState> {
constructor(props: any) { constructor(props: any) {
super(props); super(props);
ConfigurationsActions.loadConfiguration(); // ConfigurationsActions.loadConfiguration();
} }
componentDidMount() { componentDidMount() {

Просмотреть файл

@ -16,6 +16,7 @@ export default (
<Route path="/about" component={About} /> <Route path="/about" component={About} />
<Route path="/dashboard" component={Dashboard} /> <Route path="/dashboard" component={Dashboard} />
<Route path="/dashboard/config" component={Config} /> <Route path="/dashboard/config" component={Config} />
<Route path="/dashboard/:id" component={Dashboard}/>
<Route path="/setup" component={Setup} /> <Route path="/setup" component={Setup} />
<Route path="*" component={NotFound} /> <Route path="*" component={NotFound} />
</Route> </Route>

Просмотреть файл

@ -7,6 +7,8 @@ import configurationActions from '../actions/ConfigurationsActions';
interface IConfigurationsStoreState { interface IConfigurationsStoreState {
dashboard: IDashboardConfig; dashboard: IDashboardConfig;
dashboards: IDashboardConfig[];
templates: IDashboardConfig[];
connections: IDictionary; connections: IDictionary;
connectionsMissing: boolean; connectionsMissing: boolean;
loaded: boolean; loaded: boolean;
@ -15,6 +17,8 @@ interface IConfigurationsStoreState {
class ConfigurationsStore extends AbstractStoreModel<IConfigurationsStoreState> implements IConfigurationsStoreState { class ConfigurationsStore extends AbstractStoreModel<IConfigurationsStoreState> implements IConfigurationsStoreState {
dashboard: IDashboardConfig; dashboard: IDashboardConfig;
dashboards: IDashboardConfig[];
templates: IDashboardConfig[];
connections: IDictionary; connections: IDictionary;
connectionsMissing: boolean; connectionsMissing: boolean;
loaded: boolean; loaded: boolean;
@ -23,16 +27,38 @@ class ConfigurationsStore extends AbstractStoreModel<IConfigurationsStoreState>
super(); super();
this.dashboard = null; this.dashboard = null;
this.dashboards = null;
this.templates = null;
this.connections = {}; this.connections = {};
this.connectionsMissing = false; this.connectionsMissing = false;
this.loaded = false; this.loaded = false;
this.bindListeners({ this.bindListeners({
loadConfiguration: configurationActions.loadConfiguration loadConfiguration: configurationActions.loadConfiguration,
loadDashboard: configurationActions.loadDashboard
}); });
configurationActions.loadConfiguration();
let pathname = window.location.pathname;
if (pathname === '/dashboard') {
configurationActions.loadDashboard("0");
}
if (pathname.startsWith('/dashboard/')) {
let dashboardId = pathname.substring('/dashboard/'.length);
configurationActions.loadDashboard(dashboardId);
}
} }
loadConfiguration(dashboard: IDashboardConfig) { loadConfiguration(result: { dashboards: IDashboardConfig[], templates: IDashboardConfig[] }) {
let { dashboards,templates } = result;
this.dashboards = dashboards;
this.templates = templates;
}
loadDashboard(result: { dashboard: IDashboardConfig }) {
let { dashboard } = result;
this.dashboard = dashboard; this.dashboard = dashboard;
if (this.dashboard && !this.loaded) { if (this.dashboard && !this.loaded) {