Skip to content

Commit

Permalink
Merge pull request #35 from moxious/v0.4.3
Browse files Browse the repository at this point in the history
V0.4.3
  • Loading branch information
moxious authored Nov 7, 2018
2 parents 05fee6e + 91066bb commit a9f955f
Show file tree
Hide file tree
Showing 13 changed files with 1,688 additions and 358 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
19 changes: 19 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# base image
FROM kkarczmarczyk/node-yarn:latest

# set working directory
RUN mkdir /app
WORKDIR /app

ENV PORT 3000

# install and cache app dependencies
COPY . /app
RUN npm config set registry https://neo.jfrog.io/neo/api/npm/npm/
RUN yarn install
EXPOSE 3000

ENV PATH /app/node_modules/.bin:$PATH

# start app
CMD ["/usr/local/bin/yarn", "start"]
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ Primary features:

Browse to http://localhost:3000/ and you're ready to go.

### Running in Docker

```
docker run -d -p 127.0.0.1:3000:3000 --rm -t mdavidallen/halin:latest
```

Open a browser to http://localhost:3000/

### Running as a GraphApp

4. Inside of Neo4j Desktop, go to application settings, scroll all the way to the bottom, enable development mode
Expand All @@ -30,6 +38,14 @@ Browse to http://localhost:3000/ and you're ready to go.
7. Finally, inside of desktop you'll see a special tile labeled "Development App 9.9.9". This will
point to your running copy of Halin

### Docker Support

To build the container:

```
docker build -t halin:latest -f Dockerfile .
```

## FAQ

1. Does it support Neo4j Enterprise or Community?
Expand Down
14 changes: 14 additions & 0 deletions deploy/build-docker.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#!/bin/bash
export HALIN_VERSION=$(jq -r '.version' < package.json)

if [ -z $HALIN_VERSION ] ; then
echo "Check path; can't find package.json."
exit 1 ;
else
echo "HALIN_VERSION=$HALIN_VERSION"
fi

IMAGE=mdavidallen/halin:$HALIN_VERSION
docker build -t $IMAGE -f Dockerfile .
docker push $IMAGE

6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "halin",
"description": "Halin helps you monitor and improve your Neo4j graph",
"version": "0.4.2",
"version": "0.4.3",
"neo4jDesktop": {
"apiVersion": "^1.2.0"
},
"license": "apache-2.0",
"license": "Apache-2.0",
"icons": [
{
"src": "./public/img/halin-icon.png",
Expand All @@ -21,7 +21,7 @@
"dependencies": {
"@sentry/browser": "^4.0.6",
"bluebird": "^3.5.2",
"graph-app-kit": "^0.7.0",
"graph-app-kit": "^1.0.2",
"lodash": "^4.17.11",
"moment": "^2.22.2",
"neo4j-driver": "^1.6.3",
Expand Down
10 changes: 10 additions & 0 deletions release-notes.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Halin Release Notes

## 0.4.3

- Added Docker support. Halin can be built and run inside of docker.
- Fixed Neo4j Desktop bug that occurred when no database was active (#31)
- Added several suggestions from Dave Shiposh; heap size checking, page cache size checking (#29, #28)
- Disabled User Management tab when Neo4j cluster does not support native authentication (#27)
- Improved freshness calculations for degrading connections
- Improved connection troubleshooting
- Key dependency updates

## 0.4.2

- Certain cluster lifecycle events (like leader re-elections) can be detected and seen in
Expand Down
15 changes: 4 additions & 11 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import React, { Component } from 'react';
import * as Sentry from '@sentry/browser';
import {
GraphAppBase,
ConnectModal,
CONNECTED
} from 'graph-app-kit/components/GraphAppBase';
import { Render } from 'graph-app-kit/components/Render';
Expand Down Expand Up @@ -144,7 +143,7 @@ class Halin extends Component {

// The user management tab is only available in enterprise, unfortunately,
// because it relies on stored procedures that don't exist in community.
if (window.halinContext.isEnterprise()) {
if (window.halinContext.isEnterprise() && window.halinContext.supportsNativeAuth()) {
allPanesInOrder.push(userMgmtPane);
}
allPanesInOrder.push(diagnosticPane);
Expand All @@ -160,7 +159,7 @@ class Halin extends Component {
const err = (this.state.error ? status.formatStatusMessage(this) : null);

if (err) {
return (
return (
<div className='MainBodyError'>
{ err }

Expand Down Expand Up @@ -217,15 +216,9 @@ const App = () => {
driverFactory={neo4j}
integrationPoint={window.neo4jDesktopApi}
render={({ connectionState, connectionDetails, setCredentials }) => {
return [
<ConnectModal
key="modal"
errorMsg={connectionDetails ? connectionDetails.message : ""}
onSubmit={setCredentials}
show={connectionState !== CONNECTED}
/>,
return (
<Halin key="app" connected={connectionState === CONNECTED} />
];
);
}}
/>
);
Expand Down
33 changes: 28 additions & 5 deletions src/data/DataFeed.js
Original file line number Diff line number Diff line change
Expand Up @@ -243,13 +243,35 @@ export default class DataFeed extends Metric {
}

isFresh() {
const lastDataPoint = this.state.lastDataArrived.getTime();
// this.sampleStart is when we last asked for data.
// this.state.lastDataArrived is when we last got some.
const lastAskedForData = (this.sampleStart || new Date()).getTime();
const lastReceivedData = this.state.lastDataArrived.getTime();
const now = new Date().getTime();
const elapsed = now - lastDataPoint;

// Encountered errors mean we aren't fresh.
if (this.state.error) {
return false;
}

// How much time has gone by since we asked for data?
let elapsed = 0;
if (lastReceivedData >= lastAskedForData) {
// Haven't started polling yet for next go-around.
// So do it on the basis of last request experience.
elapsed = lastReceivedData - lastAskedForData;
} else {
// We have a pending request out.
elapsed = now - lastAskedForData;
}

// How long/extra we'll wait before we're not fresh.
const freshnessThreshold = this.windowWidth * 2;
if (elapsed > freshnessThreshold) {
return false;
}

// We are fresh if we've received data within 2x our window, and there is no present
// error in data received.
return (this.windowWidth * 2) > elapsed && !this.state.error;
return true;
}

/**
Expand All @@ -264,6 +286,7 @@ export default class DataFeed extends Metric {

const session = this.driver.session();
const startTime = new Date().getTime();
this.sampleStart = new Date();

return session.run(this.query, this.params)
.then(results => {
Expand Down
64 changes: 52 additions & 12 deletions src/data/HalinContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export default class HalinContext {
connectionTimeout: 10000,
trust: 'TRUST_CUSTOM_CA_SIGNED_CERTIFICATES',
};
this.dbms = {};
this.debug = true;
this.mgr = new ClusterManager(this);
}
Expand Down Expand Up @@ -108,6 +109,13 @@ export default class HalinContext {
return this.dbms.edition === 'enterprise';
}

/**
* Returns true if the context provides for native auth management, false otherwise.
*/
supportsNativeAuth() {
return this.dbms.nativeAuth;
}

/**
* Starts a slow data feed for the node's cluster role. In this way, if the leader
* changes, we can detect it.
Expand Down Expand Up @@ -198,7 +206,6 @@ export default class HalinContext {

_.each(object, (val, key) => {
if (key === keyToClean) {
console.log('found target key');
found = true;
} else if(_.isArray(val)) {
object[key] = val.map(v => deepReplace(keyToClean, newVal, v));
Expand Down Expand Up @@ -228,24 +235,49 @@ export default class HalinContext {
const q = 'call dbms.components()';
const session = driver.session();

return session.run(q, {})
const componentsPromise = session.run(q, {})
.then(results => {
const rec = results.records[0];
this.dbms = {
name: rec.get('name'),
versions: rec.get('versions'),
edition: rec.get('edition'),
};
this.dbms.name = rec.get('name')
this.dbms.versions = rec.get('versions');
this.dbms.edition = rec.get('edition');
})
.catch(err => {
Sentry.captureException(err);
console.error('Failed to get DBMS components');
this.dbms = {
name: 'UNKNOWN',
versions: [],
edition: 'UNKNOWN',
};
this.dbms.name = 'UNKNOWN';
this.dbms.versions = [];
this.dbms.edition = 'UNKNOWN';
});

// See issue #27 for what's going on here. DB must support native auth
// in order for us to expose some features, such as user management.
const authQ = `
CALL dbms.listConfig() YIELD name, value
WHERE name =~ 'dbms.security.auth_provider.*'
RETURN value;`;
const authPromise = session.run(authQ, {})
.then(results => {
let nativeAuth = false;

results.records.forEach(rec => {
const val = rec.get('value');
const valAsStr = `${val}`; // Coerce ['foo','bar']=>'foo,bar' if present

if (valAsStr.indexOf('native') > -1) {
nativeAuth = true;
}
});

this.dbms.nativeAuth = nativeAuth;
})
.catch(err => {
Sentry.captureException(err);
console.error('Failed to get DBMS auth implementation type');
this.dbms.nativeAuth = false;
});

return Promise.all([componentsPromise, authPromise]);
}

checkUser(driver) {
Expand Down Expand Up @@ -290,6 +322,14 @@ export default class HalinContext {
try {
return nd.getFirstActive()
.then(active => {
if (_.isNil(active)) {
// In the web version, this will never happen because the
// shim will fake an active DB. In Neo4j Desktop this
// **will** happen if the user launches Halin without an
// activated database.
throw new Error('In order to launch Halin, you must have an active database connection');
}

// console.log('FIRST ACTIVE', active);
this.project = active.project;
this.graph = active.graph;
Expand Down
Loading

0 comments on commit a9f955f

Please sign in to comment.