Skip to content

Commit

Permalink
Merge pull request #56 from moxious/v0.7.1
Browse files Browse the repository at this point in the history
V0.7.1
  • Loading branch information
moxious authored Jan 11, 2019
2 parents 2742bdb + c033412 commit 3211196
Show file tree
Hide file tree
Showing 12 changed files with 182 additions and 61 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "halin",
"description": "Halin helps you monitor and improve your Neo4j graph",
"version": "0.7.0",
"version": "0.7.1",
"neo4jDesktop": {
"apiVersion": "^1.2.0"
},
Expand Down
8 changes: 8 additions & 0 deletions release-notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
# Halin Release Notes

## 0.7.1

- Fixes #55; removes DBcount component and institutes a new plugins / CypherSurface
component to consolidate view of functions and procedures
- Fixes #54; adds an "Other" column to store file size tracking, to indicate when
users have extraneous files in their graph.db folders that are taking up space. Thanks
to @dfgitn4j for the suggestion.

## 0.7.0

- (Optional opt-in) you may report diagnostic information to help improve Halin & Neo4j
Expand Down
6 changes: 3 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Render } from 'graph-app-kit/components/Render';
import Neo4jConfiguration from './configuration/Neo4jConfiguration';
import PerformancePane from './performance/PerformancePane';
import OSPane from './performance/OSPane';
import DatabasePane from './db/DatabasePane';
import PluginPane from './db/PluginPane';
import PermissionsPane from './configuration/PermissionsPane';
import ClusterOverviewPane from './overview/ClusterOverviewPane';
import ClusterNodeTabHeader from './ClusterNodeTabHeader';
Expand Down Expand Up @@ -54,9 +54,9 @@ class Halin extends Component {
<OSPane key={key} node={node} driver={driver} />),
},
{
menuItem: 'Data',
menuItem: 'Plugins',
render: () => this.paneWrapper(
<DatabasePane key={key} node={node} driver={driver} />),
<PluginPane key={key} node={node} driver={driver} />),
},
]),
};
Expand Down
20 changes: 20 additions & 0 deletions src/data/ClusterNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,26 @@ export default class ClusterNode {
return this.dbms.authEnabled === 'true';
}

getCypherSurface() {
const session = this.driver.session();

const extractRecordsWithType = (results, t) => results.records.map(rec => ({
name: rec.get('name'),
signature: rec.get('signature'),
description: rec.get('description'),
roles: rec.get('roles'),
type: t,
}));

const functionsPromise = session.run('CALL dbms.functions()', {})
.then(results => extractRecordsWithType(results, 'function'));
const procsPromise = session.run('CALL dbms.procedures()', {})
.then(results => extractRecordsWithType(results, 'procedure'));

return Promise.all([functionsPromise, procsPromise])
.then(results => _.flatten(results));
}

checkComponents() {
if (!this.driver) {
throw new Error('ClusterNode has no driver');
Expand Down
14 changes: 12 additions & 2 deletions src/data/query-library.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export default {
},

JMX_STORE_SIZES: {
// otherStore is a calculated value that catches all other files which may
// be in the store directory which don't belong to Neo4j.
query: `
CALL dbms.queryJmx('org.neo4j:instance=kernel#0,name=Store sizes')
YIELD attributes
Expand All @@ -36,11 +38,18 @@ export default {
attributes.TotalStoreSize.value as total,
attributes.TransactionLogsSize.value as txLogs,
attributes.ArrayStoreSize.value as arrayStore
WITH countStore, indexStore, labelStore, nodeStore,
propStore, relStore, schemaStore, stringStore, total,
txLogs, arrayStore,
(total - (countStore + indexStore + labelStore + nodeStore +
propStore + relStore + schemaStore + stringStore +
txLogs + arrayStore)) as otherStore
RETURN
countStore, indexStore, labelStore,
schemaStore, txLogs,
stringStore, arrayStore,
relStore, propStore, total, nodeStore;
relStore, propStore, total, nodeStore,
otherStore;
`,

columns: [
Expand All @@ -52,7 +61,8 @@ export default {
{ Header: 'Schema', accessor: 'schemaStore' },
{ Header: 'TXs', accessor: 'txLogs' },
{ Header: 'Strings', accessor: 'stringStore' },
{ Header: 'Arrays', accessor: 'arrayStore' },
{ Header: 'Arrays', accessor: 'arrayStore' },
{ Header: 'Other', accessor: 'otherStore' },
],
rate: 1000,
},
Expand Down
104 changes: 104 additions & 0 deletions src/db/CypherSurface.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, { Component } from 'react';
import fields from '../data/fields';
import Spinner from '../Spinner';
import ReactTable from 'react-table';
import 'react-table/react-table.css';

class CypherSurface extends Component {
state = {
surface: null,
displayColumns: [
{
Header: 'Type',
accessor: 'type',
filterMethod: (filter, row) => {
if (filter.value === "all") {
return true;
}

return row[filter.id] === filter.value;
},
Filter: ({ filter, onChange }) =>
<select
onChange={event => onChange(event.target.value)}
style={{ width: "100%" }}
value={filter ? filter.value : "all"}
>
<option value="all">All</option>
<option value="function">Functions</option>
<option value="procedure">Procedures</option>
</select>,
},
{
Header: 'Name',
accessor: 'name',
style: { whiteSpace: 'unset', textAlign: 'left' }
},
{
Header: 'Signature',
accessor: 'signature',
style: { whiteSpace: 'unset', textAlign: 'left' }
},
{
Header: 'Description',
accessor: 'description',
style: { whiteSpace: 'unset', textAlign: 'left' }
},
{
Header: 'Roles',
accessor: 'roles',
Cell: fields.jsonField,
style: { whiteSpace: 'unset', textAlign: 'left' },
show: false,
},
],
};

constructor(props, context) {
super(props, context);
this.driver = props.driver || context.driver;
}

componentWillMount() {
return this.props.node.getCypherSurface()
.then(surface => {
this.setState({ surface });
})
.catch(err => {
console.error('Failed to get surface', err);
});
}

render() {
if (!this.state.surface) {
return (
<div className='CypherSurface'>
<Spinner/>
</div>
);
}

return (
<div className='CypherSurface'>
<h3>Installed Functions &amp; Procedures</h3>

<ReactTable
// By default, filter only catches data if the value STARTS WITH
// the entered string. This makes it less picky.
defaultFilterMethod={(filter, row, column) => {
const id = filter.pivotId || filter.id
return row[id] !== undefined ? String(row[id]).indexOf(filter.value) > -1 : true
}}
data={this.state.surface}
sortable={true}
filterable={true}
defaultPageSize={10}
showPagination={true}
columns={this.state.displayColumns}
/>
</div>
);
}
}

export default CypherSurface;
5 changes: 5 additions & 0 deletions src/db/DBSize.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ import React, { Component } from 'react';
import CypherTimeseries from '../timeseries/CypherTimeseries';
import uuid from 'uuid';

/*
* @deprecated by issue https://github.com/moxious/halin/issues/55
* This component is no longer in use because Kees Vegter's dbAnalyzer graphapp is a
* far superior method of doing the same thing.
*/
class DBSize extends Component {
state = {
key: uuid.v4(),
Expand Down
39 changes: 0 additions & 39 deletions src/db/DatabasePane.js

This file was deleted.

3 changes: 3 additions & 0 deletions src/db/Functions.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import fields from '../data/fields';
import CypherDataTable from '../data/CypherDataTable';
import './Functions.css';

/**
* @deprecated by the CypherSurface component
*/
class Functions extends Component {
state = {
query: 'CALL dbms.functions()',
Expand Down
19 changes: 19 additions & 0 deletions src/db/PluginPane.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { Component } from 'react';
import CypherSurface from './CypherSurface';
import uuid from 'uuid';

export default class PluginPane extends Component {
state = {
key: uuid.v4(),
};

render() {
return (
<div className='PluginPane'>
<CypherSurface
key={this.state.key} node={this.props.node} driver={this.props.driver}
/>
</div>
)
}
}
3 changes: 3 additions & 0 deletions src/db/Procedures.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import fields from '../data/fields';
import CypherDataTable from '../data/CypherDataTable';
import './Procedures.css';

/**
* @deprecated by the CypherSurface component
*/
class Procedures extends Component {
state = {
query: 'CALL dbms.procedures()',
Expand Down
20 changes: 4 additions & 16 deletions src/diagnostic/StoreFiles.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,22 @@
import React, { Component } from 'react';
import fields from '../data/fields';
import CypherDataTable from '../data/CypherDataTable';
import queryLibrary from '../data/query-library';

class StoreFiles extends Component {
state = {
rate: (1000 * 60), // Once per minute
query: `
CALL dbms.queryJmx('org.neo4j:instance=kernel#0,name=Store file sizes')
YIELD attributes
WITH
attributes.LogicalLogSize.value as logicalLog,
attributes.StringStoreSize.value as stringStore,
attributes.ArrayStoreSize.value as arrayStore,
attributes.RelationshipStoreSize.value as relStore,
attributes.PropertyStoreSize.value as propStore,
attributes.TotalStoreSize.value as total,
attributes.NodeStoreSize.value as nodeStore
RETURN
logicalLog, stringStore, arrayStore,
relStore, propStore, total, nodeStore;
`,
query: queryLibrary.JMX_STORE_SIZES.query,
displayColumns: [
{ Header: 'Total', accessor: 'total', Cell: fields.dataSizeField },
{ Header: 'Nodes', accessor: 'nodeStore', Cell: fields.dataSizeField },
{ Header: 'Relationships', accessor: 'relStore', Cell: fields.dataSizeField },
{ Header: 'Properties', accessor: 'propStore', Cell: fields.dataSizeField },

{ Header: 'Logical Log', accessor: 'logicalLog', Cell: fields.dataSizeField, show: false },
{ Header: 'TX Logs', accessor: 'txLogs', Cell: fields.dataSizeField, show: false },
{ Header: 'Strings', accessor: 'stringStore', Cell: fields.dataSizeField, show: false },
{ Header: 'Arrays', accessor: 'arrayStore', Cell: fields.dataSizeField, show: false },
{ Header: 'Other', accessor: 'otherStore', Cell: fields.dataSizeField, show: true },
],
};

Expand Down

0 comments on commit 3211196

Please sign in to comment.