LENS-1207 : Fix gaps in lens ui
Project: http://git-wip-us.apache.org/repos/asf/lens/repo Commit: http://git-wip-us.apache.org/repos/asf/lens/commit/2de1b521 Tree: http://git-wip-us.apache.org/repos/asf/lens/tree/2de1b521 Diff: http://git-wip-us.apache.org/repos/asf/lens/diff/2de1b521 Branch: refs/heads/current-release-line Commit: 2de1b5218aa4f1d21606e067c229752e9f2ffbdc Parents: 77e7cc6 Author: Rajat Khandelwal <pro...@apache.org> Authored: Tue Jul 19 17:23:29 2016 +0530 Committer: Amareshwari Sriramadasu <amareshw...@apache.org> Committed: Tue Jul 19 17:23:29 2016 +0530 ---------------------------------------------------------------------- lens-ui/app/actions/LoginActions.js | 5 +- lens-ui/app/adapters/AdhocQueryAdapter.js | 9 +- lens-ui/app/adapters/AuthenticationAdapter.js | 3 +- lens-ui/app/app.js | 2 +- lens-ui/app/components/AdhocQueryComponent.js | 4 +- lens-ui/app/components/CubeSchemaComponent.js | 159 +++++++++++++++------ lens-ui/app/components/CubeTreeComponent.js | 13 +- lens-ui/app/components/DatabaseComponent.js | 14 +- lens-ui/app/components/LoginComponent.js | 12 +- lens-ui/app/components/QueryBoxComponent.js | 2 +- lens-ui/app/components/TableTreeComponent.js | 17 ++- lens-ui/app/stores/CubeStore.js | 5 +- lens-ui/app/stores/DatabaseStore.js | 10 -- lens-ui/app/stores/UserStore.js | 11 ++ lens-ui/app/utils/ErrorParser.js | 31 +--- lens-ui/config.json | 4 +- lens-ui/package.json | 6 +- lens-ui/server.js | 14 +- 18 files changed, 191 insertions(+), 130 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/actions/LoginActions.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/actions/LoginActions.js b/lens-ui/app/actions/LoginActions.js index 3cb39d0..5c57087 100644 --- a/lens-ui/app/actions/LoginActions.js +++ b/lens-ui/app/actions/LoginActions.js @@ -22,8 +22,8 @@ import AppConstants from '../constants/AppConstants'; import AuthenticationAdapter from '../adapters/AuthenticationAdapter'; let LoginActions = { - authenticate (email, password) { - AuthenticationAdapter.authenticate(email, password) + authenticate (email, password, database) { + AuthenticationAdapter.authenticate(email, password, database) .then(function (response) { // authenticating user right away @@ -31,6 +31,7 @@ let LoginActions = { actionType: AppConstants.AUTHENTICATION_SUCCESS, payload: { email: email, + database: database, secretToken: response } }); http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/adapters/AdhocQueryAdapter.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/adapters/AdhocQueryAdapter.js b/lens-ui/app/adapters/AdhocQueryAdapter.js index 9d4b416..a54274f 100644 --- a/lens-ui/app/adapters/AdhocQueryAdapter.js +++ b/lens-ui/app/adapters/AdhocQueryAdapter.js @@ -52,7 +52,12 @@ let AdhocQueryAdapter = { getCubes (secretToken) { let url = baseUrl + urls.getCubes; - return BaseAdapter.get(url + '?sessionid=' + secretToken); + let postURL = "?"; + if (Config.cubes_type) { + postURL += "type=" + Config.cubes_type + "&" + } + postURL += "sessionid=" + secretToken; + return BaseAdapter.get(url + postURL); }, getCubeDetails (secretToken, cubeName) { @@ -212,7 +217,7 @@ let AdhocQueryAdapter = { }, getParams (secretToken, query) { - let url = baseUrl + urls.parameters; + let url = baseUrl + urls.parameters + '?sessionid=' + secretToken; let formData = new FormData(); formData.append('query', query); http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/adapters/AuthenticationAdapter.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/adapters/AuthenticationAdapter.js b/lens-ui/app/adapters/AuthenticationAdapter.js index 15c196b..1effb14 100644 --- a/lens-ui/app/adapters/AuthenticationAdapter.js +++ b/lens-ui/app/adapters/AuthenticationAdapter.js @@ -38,12 +38,13 @@ let sessionconf = `<?xml version="1.0" encoding="UTF-8"?> </conf>`; let AuthenticationAdapter = { - authenticate (email, password) { + authenticate (email, password, database) { // preparing data as API accepts multipart/form-data :( var formData = new FormData(); formData.append('username', email); formData.append('password', password || ""); + formData.append('database', database|| "default"); formData.append('sessionconf', sessionconf); return BaseAdapter.post(authUrl, formData, { http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/app.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/app.js b/lens-ui/app/app.js index 07d9acf..18237d2 100644 --- a/lens-ui/app/app.js +++ b/lens-ui/app/app.js @@ -45,7 +45,7 @@ let routes = ( <Route name='savedqueries' handler={SavedQueries}/> <Route name='result' path='/results/:handle' handler={QueryDetailResult}/> </Route> - <Route name='schema' path='schema/:databaseName/' handler={AdhocQuery} > + <Route name='schema' path='schema/' handler={AdhocQuery} > <Route name='cubeschema' path='cube/:cubeName' handler={CubeSchema}/> <Route name='tableschema' path='table/:tableName' handler={TableSchema}/> http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/components/AdhocQueryComponent.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/components/AdhocQueryComponent.js b/lens-ui/app/components/AdhocQueryComponent.js index aae823c..ff68beb 100644 --- a/lens-ui/app/components/AdhocQueryComponent.js +++ b/lens-ui/app/components/AdhocQueryComponent.js @@ -28,11 +28,11 @@ class AdhocQuery extends React.Component { render () { return ( <section className='row'> - <div className='col-md-4'> + <div className='col-md-2'> <Sidebar {...this.props}/> </div> - <div className='col-md-8'> + <div className='col-md-10'> <QueryBox {...this.props}/> <RouteHandler/> http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/components/CubeSchemaComponent.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/components/CubeSchemaComponent.js b/lens-ui/app/components/CubeSchemaComponent.js index dbb4cbc..9c23b9f 100644 --- a/lens-ui/app/components/CubeSchemaComponent.js +++ b/lens-ui/app/components/CubeSchemaComponent.js @@ -1,21 +1,21 @@ /** -* Licensed to the Apache Software Foundation (ASF) under one -* or more contributor license agreements. See the NOTICE file -* distributed with this work for additional information -* regarding copyright ownership. The ASF licenses this file -* to you under the Apache License, Version 2.0 (the -* "License"); you may not use this file except in compliance -* with the License. You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ import React from 'react'; @@ -24,11 +24,11 @@ import UserStore from '../stores/UserStore'; import AdhocQueryActions from '../actions/AdhocQueryActions'; import Loader from '../components/LoaderComponent'; -function getCubes (database) { +function getCubes(database) { return CubeStore.getCubes(database); } -function constructMeasureTable (cubeName, measures) { +function constructMeasureTable(cubeName, measures) { let table = measures.map((measure) => { if (typeof(measure) == "string") { return ( @@ -40,9 +40,8 @@ function constructMeasureTable (cubeName, measures) { return ( <tr key={cubeName + '|' + measure.name}> <td>{ measure.name }</td> - <td>{ measure._type }</td> - <td>{ measure.default_aggr }</td> <td>{ measure.display_string }</td> + <td>{ measure.description }</td> </tr> ); } @@ -57,11 +56,9 @@ function constructMeasureTable (cubeName, measures) { header = ( <tr> <th>Name</th> - <th>Type</th> - <th>Default Aggr</th> + <th>Display String</th> <th>Description</th> </tr> - ); } @@ -76,9 +73,9 @@ function constructMeasureTable (cubeName, measures) { ); } -function constructDimensionTable (cubeName, dimensions) { +function constructDimensionTable(cubeName, dimensions) { let table = dimensions.map((dimension) => { - if (typeof(dimension) =="string") { + if (typeof(dimension) == "string") { return ( <tr key={cubeName + '|' + dimension}> <td>{ dimension}</td> @@ -88,12 +85,11 @@ function constructDimensionTable (cubeName, dimensions) { return ( <tr key={cubeName + '|' + dimension.name}> <td>{ dimension.name }</td> - <td>{ dimension._type }</td> - <td>{ dimension.ref_spec && dimension.ref_spec.chain_ref_column && - dimension.ref_spec.chain_ref_column.dest_table }</td> - <td>{ dimension.ref_spec && dimension.ref_spec.chain_ref_column && - dimension.ref_spec.chain_ref_column.ref_col }</td> + <td>{ dimension.display_string }</td> <td>{ dimension.description }</td> + <td>{ dimension.chain_ref_column ? dimension.chain_ref_column.map((ref) => { + return ref.chain_name + "." + ref.ref_col + }).join(" ") : ""}</td> </tr> ); } @@ -104,20 +100,89 @@ function constructDimensionTable (cubeName, dimensions) { </tr> ); if (typeof(dimensions[0]) != "string") { - header = ( + header = ( <tr> <th>Name</th> - <th>Type</th> - <th>Destination Table</th> - <th>Column</th> + <th>Display String</th> <th>Description</th> + <th>Source</th> </tr> ); } return ( <div className='table-responsive'> <table className='table table-striped'> - <caption className='bg-primary text-center'>Dimensions</caption> + <caption className='bg-primary text-center'>Dim-Attributes</caption> + <thead>{header}</thead> + <tbody>{table}</tbody> + </table> + </div> + ); +} + +function constructJoinChainTable(cubeName, join_chains) { + let table = join_chains.map((join_chain) => { + return ( + <tr key={cubeName + '|' + join_chain.name}> + <td>{ join_chain.name }</td> + <td>{ <pre> { + join_chain.paths.path.map((path) => { + return path.edges.edge.map((edge) => { + return edge.from.table + "." + edge.from.column + "=" + edge.to.table + "." + edge.to.column + }).join("->") + }).join("\n ") + } + </pre> + }</td> + </tr> + ); + }); + let header = ( + <tr> + <th>Name</th> + <th>Paths</th> + </tr> + ); + return ( + <div className='table-responsive'> + <table className='table table-striped'> + <caption className='bg-primary text-center'>Join Chains</caption> + <thead>{header}</thead> + <tbody>{table}</tbody> + </table> + </div> + ); +} + +function constructExpressionTable(cubeName, expressions) { + let table = expressions.map((expression) => { + return ( + <tr key={cubeName + '|' + expression.name}> + <td>{ expression.name }</td> + <td>{ expression.display_string }</td> + <td>{ expression.description }</td> + <td>{ <pre> { + expression.expr_spec.map((expr_spec) => { + return expr_spec.expr; + }).join("\n ") + } + </pre> + }</td> + </tr> + ); + }); + let header = ( + <tr> + <th>Name</th> + <th>Display String</th> + <th>Description</th> + <th>Expressions</th> + </tr> + ); + return ( + <div className='table-responsive'> + <table className='table table-striped'> + <caption className='bg-primary text-center'>Join Chains</caption> <thead>{header}</thead> <tbody>{table}</tbody> </table> @@ -127,7 +192,7 @@ function constructDimensionTable (cubeName, dimensions) { // TODO add prop checking. class CubeSchema extends React.Component { - constructor (props) { + constructor(props) { super(props); this.state = {cube: {}, database: props.params.databaseName}; this._onChange = this._onChange.bind(this); @@ -136,21 +201,21 @@ class CubeSchema extends React.Component { .getCubeDetails(UserStore.getUserDetails().secretToken, props.params.databaseName, props.params.cubeName); } - componentDidMount () { + componentDidMount() { CubeStore.addChangeListener(this._onChange); } - componentWillUnmount () { + componentWillUnmount() { CubeStore.removeChangeListener(this._onChange); } - componentWillReceiveProps (props) { + componentWillReceiveProps(props) { // TODO are props updated automatically, unlike state? let cubeName = props.params.cubeName; let cube = getCubes(props.params.databaseName)[cubeName]; if (cube.isLoaded) { - this.setState({ cube: cube, database: props.params.database }); + this.setState({cube: cube, database: props.params.database}); return; } @@ -158,15 +223,15 @@ class CubeSchema extends React.Component { .getCubeDetails(UserStore.getUserDetails().secretToken, props.params.databaseName, cubeName); // empty the previous state - this.setState({ cube: {}, database: props.params.databaseName }); + this.setState({cube: {}, database: props.params.databaseName}); } - render () { + render() { let schemaSection; // this will be empty if it's the first time so show a loader if (!this.state.cube.isLoaded) { - schemaSection = <Loader size='8px' margin='2px' />; + schemaSection = <Loader size='8px' margin='2px'/>; } else { // if we have cube state let cube = this.state.cube; @@ -182,6 +247,8 @@ class CubeSchema extends React.Component { <div> { constructMeasureTable(cube.name, cube.measures) } { constructDimensionTable(cube.name, cube.dimensions) } + { cube.join_chains && constructJoinChainTable(cube.name, cube.join_chains) } + { cube.expressions && constructExpressionTable(cube.name, cube.expressions) } </div> ); } @@ -196,7 +263,7 @@ class CubeSchema extends React.Component { <div className='panel-heading'> <h3 className='panel-title'>Schema Details: <strong className='text-primary'> - {this.props.params.cubeName} + {this.props.params.cubeName} </strong> </h3> </div> @@ -209,7 +276,7 @@ class CubeSchema extends React.Component { ); } - _onChange () { + _onChange() { this.setState({cube: getCubes(this.props.params.databaseName)[this.props.params.cubeName]}); } } http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/components/CubeTreeComponent.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/components/CubeTreeComponent.js b/lens-ui/app/components/CubeTreeComponent.js index 702fa36..e106a4c 100644 --- a/lens-ui/app/components/CubeTreeComponent.js +++ b/lens-ui/app/components/CubeTreeComponent.js @@ -33,7 +33,7 @@ import '../styles/css/tree.css'; function getCubeData () { return { - cubes: CubeStore.getCubes(DatabaseStore.currentDatabase()) + cubes: CubeStore.getCubes(UserStore.currentDatabase()) }; } @@ -45,12 +45,17 @@ class CubeTree extends React.Component { // comes with React.createClass, using constructor is the new // idiomatic way // https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html - this.state = {database: props.database, cubes: [], loading: true, isCollapsed: false }; + this.state = {cubes: CubeStore.getCubes(UserStore.currentDatabase()), loading: false, isCollapsed: false }; // no autobinding with ES6 so doing it manually, see link below // https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding this._onChange = this._onChange.bind(this); this.toggle = this.toggle.bind(this); + if (!this.state.cubes) { + this.state.cubes = this.state.cubes || []; + this.state.loading = true; + AdhocQueryActions.getCubes(UserStore.getUserDetails().secretToken, UserStore.currentDatabase()); + } } componentDidMount () { @@ -84,7 +89,7 @@ class CubeTree extends React.Component { let dimensionLabel = <Link to='cubeschema' params={{databaseName: this.state.database, cubeName: cubeName}} query={{type: 'dimensions'}}> - <span className='quiet'>Dimensions</span> + <span className='quiet'>Dim-Attributes</span> </Link>; return ( <TreeView key={cube.name + '|' + i} nodeLabel={label} @@ -101,7 +106,7 @@ class CubeTree extends React.Component { }) : null } </TreeView > - <TreeView key={cube.name + '|dimensions'} nodeLabel={dimensionLabel} + <TreeView key={cube.name + '|dim attributes'} nodeLabel={dimensionLabel} defaultCollapsed={!cube.isLoaded}> { cube.dimensions ? cube.dimensions.map(dimension => { return ( http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/components/DatabaseComponent.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/components/DatabaseComponent.js b/lens-ui/app/components/DatabaseComponent.js index 15a4e46..362a018 100644 --- a/lens-ui/app/components/DatabaseComponent.js +++ b/lens-ui/app/components/DatabaseComponent.js @@ -26,6 +26,7 @@ import UserStore from '../stores/UserStore'; import Loader from '../components/LoaderComponent'; import CubeTree from './CubeTreeComponent'; import TableTree from './TableTreeComponent'; +import Config from 'config.json'; function getDatabases () { return DatabaseStore.getDatabases(); @@ -38,24 +39,23 @@ class DatabaseComponent extends React.Component { databases: [], loading: true, isCollapsed: false, - selectedDatabase: props.params.databaseName, + selectedDatabase: UserStore.getUserDetails().database }; this._onChange = this._onChange.bind(this); this.toggle = this.toggle.bind(this); this.setDatabase = this.setDatabase.bind(this); AdhocQueryActions.getDatabases(UserStore.getUserDetails().secretToken); - if (this.state.selectedDatabase) { - this.setDatabase(this.state.selectedDatabase); - } } componentDidMount () { DatabaseStore.addChangeListener(this._onChange); + UserStore.addChangeListener(this._onChange); } componentWillUnmount () { DatabaseStore.removeChangeListener(this._onChange); + UserStore.removeChangeListener(this._onChange); } render () { @@ -92,7 +92,6 @@ class DatabaseComponent extends React.Component { <strong>Sorry, we couldn't find any databases.</strong> </div>); } - return (<div> {databaseComponent} { @@ -104,7 +103,7 @@ class DatabaseComponent extends React.Component { </div> } { - this.state.selectedDatabase && + this.state.selectedDatabase && Config.display_tables && <div> <hr style={{marginTop: '10px', marginBottom: '10px'}}/> <TableTree key={this.state.selectedDatabase} @@ -117,7 +116,7 @@ class DatabaseComponent extends React.Component { } _onChange () { - this.setState({ databases: getDatabases(), loading: false }); + this.setState({ databases: getDatabases(), loading: false, selectedDatabase: UserStore.currentDatabase() }); } toggle () { @@ -132,7 +131,6 @@ class DatabaseComponent extends React.Component { dbName = event.target.value; } AdhocQueryActions.setDatabase(UserStore.getUserDetails().secretToken, dbName); - this.setState({databases: getDatabases(), selectedDatabase: dbName, loading: false}); } } http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/components/LoginComponent.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/components/LoginComponent.js b/lens-ui/app/components/LoginComponent.js index 23b3bb9..0a81414 100644 --- a/lens-ui/app/components/LoginComponent.js +++ b/lens-ui/app/components/LoginComponent.js @@ -32,8 +32,8 @@ class Login extends React.Component { this._onChange = this._onChange.bind(this); this.state = { error: UserStore.isUserLoggedIn(), - password_required: Config.password_required }; + this.props.db = Config.default_database || "default"; } componentDidMount () { @@ -48,11 +48,13 @@ class Login extends React.Component { event.preventDefault(); var email = this.refs.email.getDOMNode().value; var pass = this.refs.pass.getDOMNode().value; - - LoginActions.authenticate(email, pass); + var db = this.refs.db.getDOMNode().value || this.refs.db.getDOMNode().placeholder; + LoginActions.authenticate(email, pass, db); } render () { + var { router } = this.context; + var db = router.getCurrentQuery().db || router.getCurrentQuery().database || this.props.db; return ( <section className='row' style={{margin: 'auto'}}> <form className='form-signin' onSubmit={this.handleSubmit}> @@ -63,7 +65,9 @@ class Login extends React.Component { <label htmlFor='inputPassword' className='sr-only'>Password</label> <input ref='pass' type='password' id='inputPassword' className='form-control' placeholder='Password' - required={this.state.password_required} disabled={!this.state.password_required}/> + required={Config.password_required} disabled={!Config.password_required}/> + <input ref='db' id='inputDatabase' + className='form-control' placeholder={db}/> <button className='btn btn-primary btn-block' type='submit'>Sign in</button> {this.state.error && ( http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/components/QueryBoxComponent.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/components/QueryBoxComponent.js b/lens-ui/app/components/QueryBoxComponent.js index 30eb643..49b999f 100644 --- a/lens-ui/app/components/QueryBoxComponent.js +++ b/lens-ui/app/components/QueryBoxComponent.js @@ -393,7 +393,7 @@ class QueryBox extends React.Component { _onChangeCubeStore () { // cubes - let cubes = CubeStore.getCubes(DatabaseStore.currentDatabase()); // hashmap + let cubes = CubeStore.getCubes(UserStore.currentDatabase()); // hashmap Object.keys(cubes).forEach((cubeName) => { let cube = cubes[cubeName]; codeMirrorHints[cubeName] = []; http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/components/TableTreeComponent.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/components/TableTreeComponent.js b/lens-ui/app/components/TableTreeComponent.js index 9a8351e..fb04912 100644 --- a/lens-ui/app/components/TableTreeComponent.js +++ b/lens-ui/app/components/TableTreeComponent.js @@ -80,7 +80,6 @@ class TableTree extends React.Component { page: 0, loading: true, isCollapsed: false, - database: props.database }; this._onChange = this._onChange.bind(this); this.prevPage = this.prevPage.bind(this); @@ -88,18 +87,18 @@ class TableTree extends React.Component { this.toggle = this.toggle.bind(this); this.validateClickEvent = this.validateClickEvent.bind(this); - if (!TableStore.getTables(props.database)) { + if (!TableStore.getTables(UserStore.currentDatabase())) { AdhocQueryActions - .getTables(UserStore.getUserDetails().secretToken, props.database); + .getTables(UserStore.getUserDetails().secretToken, UserStore.currentDatabase()); } else { - let state = getState(1, '', props.database); + let state = getState(1, '', UserStore.currentDatabase()); this.state = state; // on page refresh only a single table is fetched, and hence we need to // fetch others too. - if (!TableStore.areTablesCompletelyFetched(props.database)) { + if (!TableStore.areTablesCompletelyFetched(UserStore.currentDatabase())) { AdhocQueryActions - .getTables(UserStore.getUserDetails().secretToken, props.database); + .getTables(UserStore.getUserDetails().secretToken, UserStore.currentDatabase()); } } } @@ -122,7 +121,7 @@ class TableTree extends React.Component { // construct tree let tableTreeInternal = this.state.tables.map(table => { let label = (<Link to='tableschema' params={{databaseName: this.state.database, tableName: table.name}} - title={table.name} query={{database: this.props.database}}> + title={table.name} query={{database: UserStore.currentDatabase()}}> {table.name}</Link>); return ( <TreeView key={table.name} nodeLabel={label} @@ -209,7 +208,7 @@ class TableTree extends React.Component { _onChange (page) { // so that page doesn't reset to beginning page = page || this.state.page || 1; - this.setState(getState(page, filterString, this.props.database)); + this.setState(getState(page, filterString, UserStore.currentDatabase())); } getDetails (tableName, database) { @@ -247,7 +246,7 @@ class TableTree extends React.Component { validateClickEvent (e) { if (e.target && e.target.nodeName === 'DIV' && e.target.nextElementSibling.nodeName === 'A') { - this.getDetails(e.target.nextElementSibling.textContent, this.props.database); + this.getDetails(e.target.nextElementSibling.textContent, UserStore.currentDatabase()); } } http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/stores/CubeStore.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/stores/CubeStore.js b/lens-ui/app/stores/CubeStore.js index 4441e54..2648311 100644 --- a/lens-ui/app/stores/CubeStore.js +++ b/lens-ui/app/stores/CubeStore.js @@ -52,12 +52,15 @@ function receiveCubeDetails (payload) { cubes[payload.database][cubeDetails.name] = cubes[payload.database][cubeDetails.name] || { name: cubeDetails.name, isLoaded: false }; cubes[payload.database][cubeDetails.name].measures = measures; cubes[payload.database][cubeDetails.name].dimensions = dimensions; + if (cubeDetails.type == 'x_base_cube') { + cubes[payload.database][cubeDetails.name].join_chains = cubeDetails.join_chains.join_chain; + cubes[payload.database][cubeDetails.name].expressions = cubeDetails.expressions.expression; + } cubes[payload.database][cubeDetails.name].isLoaded = true; } let CHANGE_EVENT = 'change'; var cubes = {}; -var currentDatabase = null; let CubeStore = assign({}, EventEmitter.prototype, { getCubes (currentDatabase) { return cubes[currentDatabase]; http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/stores/DatabaseStore.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/stores/DatabaseStore.js b/lens-ui/app/stores/DatabaseStore.js index a18e986..4feef5e 100644 --- a/lens-ui/app/stores/DatabaseStore.js +++ b/lens-ui/app/stores/DatabaseStore.js @@ -23,8 +23,6 @@ import assign from 'object-assign'; import { EventEmitter } from 'events'; function receiveDatabases (payload) { - databases = []; - databases = payload.databases.stringList && payload.databases.stringList.elements && payload.databases.stringList.elements.slice(); @@ -32,14 +30,10 @@ function receiveDatabases (payload) { let CHANGE_EVENT = 'change'; var databases = []; -var currentDatabase = null; let DatabaseStore = assign({}, EventEmitter.prototype, { getDatabases () { return databases; }, - currentDatabase() { - return currentDatabase; - }, emitChange () { this.emit(CHANGE_EVENT); }, @@ -59,10 +53,6 @@ AppDispatcher.register((action) => { receiveDatabases(action.payload); DatabaseStore.emitChange(); break; - case AdhocQueryConstants.SELECT_DATABASE: - currentDatabase = action.payload.database; - DatabaseStore.emitChange(); - break; } }); http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/stores/UserStore.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/stores/UserStore.js b/lens-ui/app/stores/UserStore.js index 1d7e5d4..bd53f91 100644 --- a/lens-ui/app/stores/UserStore.js +++ b/lens-ui/app/stores/UserStore.js @@ -19,6 +19,8 @@ import AppDispatcher from '../dispatcher/AppDispatcher'; import AppConstants from '../constants/AppConstants'; +import AdhocQueryConstants from '../constants/AdhocQueryConstants'; + import assign from 'object-assign'; import { EventEmitter } from 'events'; @@ -35,6 +37,7 @@ function authenticateUser (details) { userDetails = { isUserLoggedIn: true, email: details.email, + database: details.database || "default", // creating the session string which is passed with every request secretToken: `<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <lensSessionHandle> @@ -88,6 +91,9 @@ var UserStore = assign({}, EventEmitter.prototype, { getUserDetails () { return userDetails; }, + currentDatabase() { + return userDetails.database || "default"; + }, logout () { unauthenticateUser(); @@ -115,6 +121,11 @@ AppDispatcher.register((action) => { UserStore.emitChange(); break; + case AdhocQueryConstants.SELECT_DATABASE: + userDetails.database = action.payload.database; + UserStore.emitChange(); + break; + case AppConstants.AUTHENTICATION_FAILED: unauthenticateUser(action.payload); http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/app/utils/ErrorParser.js ---------------------------------------------------------------------- diff --git a/lens-ui/app/utils/ErrorParser.js b/lens-ui/app/utils/ErrorParser.js index 8bd0d90..a7fa5b2 100644 --- a/lens-ui/app/utils/ErrorParser.js +++ b/lens-ui/app/utils/ErrorParser.js @@ -18,35 +18,10 @@ */ let ErrorParser = { - getMessage (errorXML) { - let errors = []; - - errors = Array.prototype.slice.call(errorXML.getElementsByTagName('error')) - .map(error => { - return { - code: error.getElementsByTagName('code')[0].textContent, - message: error.getElementsByTagName('message')[0].textContent - }; - }) - .sort((a, b) => { - return parseInt(a.code, 10) - parseInt(b.code, 10); - }) - // removes duplicate error messages - .filter((item, pos, array) => { - return !pos || (item.code != (array[pos - 1] && array[pos - 1].code)); - }) - // removes not so helpful `Internal Server Error` - .filter(error => { - return error.code != 1001; - }); - - if (errors && errors.length == 0) { - errors[0] = {}; - errors[0].code = 500; - errors[0].message = 'Oh snap! Something went wrong. Please try again later.'; + getMessage (error) { + if (error && error.lensAPIResult && error.lensAPIResult.error) { + return [error.lensAPIResult.error] } - - return errors; } }; http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/config.json ---------------------------------------------------------------------- diff --git a/lens-ui/config.json b/lens-ui/config.json index bebc7e5..d067e08 100644 --- a/lens-ui/config.json +++ b/lens-ui/config.json @@ -1,5 +1,7 @@ { "isPersistent": true, "baseURL": "/serverproxy/", - "password_required" : true + "password_required" : false, + "display_tables":false, + "cubes_type":"base" } http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/package.json ---------------------------------------------------------------------- diff --git a/lens-ui/package.json b/lens-ui/package.json index 0ed3db1..c90c91e 100644 --- a/lens-ui/package.json +++ b/lens-ui/package.json @@ -3,8 +3,9 @@ "description": "An exemplary front end solution for Apache LENS.", "main": "app/app.js", "scripts": { - "start": "NODE_ENV=production node_modules/webpack/bin/webpack.js -p && lensserver='http://0.0.0.0:9999/lensapi/' port=8082 node server.js", - "dev": "lensserver='http://0.0.0.0:9999/lensapi/' port=8082 node server.js & node_modules/webpack/bin/webpack.js --watch", + "start": "NODE_ENV=production node_modules/webpack/bin/webpack.js -p && nohup node server.js &", + "stop": "killall -15 lens-ui-server", + "dev": "node server.js lens-ui-server & node_modules/webpack/bin/webpack.js --watch", "deploy": "NODE_ENV=production node_modules/webpack/bin/webpack.js -p" }, "dependencies": { @@ -30,6 +31,7 @@ "react": "^0.13.3", "react-bootstrap": "0.25.1", "react-router": "^0.13.3", + "react-sidebar": "~1.1.0", "react-treeview": "^0.3.12", "react-widgets": "^2.8.0", "serve-favicon": "~2.2.1" http://git-wip-us.apache.org/repos/asf/lens/blob/2de1b521/lens-ui/server.js ---------------------------------------------------------------------- diff --git a/lens-ui/server.js b/lens-ui/server.js index c8920f3..0cff513 100644 --- a/lens-ui/server.js +++ b/lens-ui/server.js @@ -27,18 +27,17 @@ var app = express(); var httpProxy = require('http-proxy'); var proxy = httpProxy.createProxyServer(); -var port = process.env['port'] || 8082; +var port = process.env.npm_config_port || 8082; app.use(logger('dev')); app.use(cookieParser()); - process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; -process.env['lensserver'] = process.env['lensserver'] || 'http://0.0.0.0:9999/lensapi/'; -if (!process.env['lensserver']) { +process.env.npm_config_lensserver = process.env.npm_config_lensserver || 'http://0.0.0.0:9999/lensapi/'; +if (!process.env.npm_config_lensserver) { throw new Error('Specify LENS Server address in `lensserver` argument'); } - -console.log('Using this as your LENS Server Address: ', process.env['lensserver']); +process.title = "lens-ui-server"; +console.log('Using this as your LENS Server Address: ', process.env.npm_config_lensserver); console.log('If this seems wrong, please edit `lensserver` argument in package.json. Do not forget to append http://\n'); app.use(session({ @@ -62,7 +61,7 @@ app.get('/target/assets/*', function (req, res) { app.all('/serverproxy/*', function (req, res) { req.url = req.url.replace('serverproxy', ''); proxy.web(req, res, { - target: process.env['lensserver'] + target: process.env.npm_config_lensserver }, function (e) { console.error('Proxy Error: ', e); }); @@ -71,7 +70,6 @@ app.all('/serverproxy/*', function (req, res) { app.get('*', function (req, res) { res.end(fs.readFileSync(__dirname + '/index.html')); }); - var server = app.listen(port, function (err) { if (err) throw err;