Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions build_scripts/webpack-rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,5 @@ module.exports = [
{
test: /\.html?$/,
use: ['html-loader']
},
{
test: /\.(graphql|gql)$/,
exclude: /node_modules/,
loader: 'graphql-tag/loader'
}
]
68 changes: 68 additions & 0 deletions e2e_tests/integration/desktop-env-url.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/* global Cypress, cy, test, expect, before */

import { getDesktopContext } from '../support/utils'
let appContextListener

// This file only esists to be able to test the auto connect using
// the host field.
// We can't load two times in the same file

describe('Neo4j Desktop environment using url field', () => {
before(() => {
cy.visit(Cypress.config('url'), {
onBeforeLoad: win => {
win.neo4jDesktopApi = {
getContext: () =>
Promise.resolve(getDesktopContext(Cypress.config, 'url')),
onContextUpdate: fn => (appContextListener = fn.bind(fn))
}
}
})
})
it('can auto connect using url field', () => {
const frames = cy.get('[data-testid="frameCommand"]', { timeout: 10000 })
frames.should('have.length', 2)

// Auto connected = :play start
frames.first().should('contain', ':play start')
cy.wait(1000)
})
it('switches connection when that event is triggered using url field', () => {
cy.executeCommand(':clear')
cy.wait(1000).then(() => {
appContextListener(
{ type: 'GRAPH_ACTIVE', id: 'test' },
getDesktopContext(Cypress.config, 'url')
)
})

const frames = cy.get('[data-testid="frameCommand"]', { timeout: 10000 })
frames.should('have.length', 1)

frames.first().should('contain', ':server switch success')

cy.get('[data-testid="frame"]', { timeout: 10000 })
.first()
.should('contain', 'Connection updated')
})
})
64 changes: 64 additions & 0 deletions e2e_tests/integration/desktop-env.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

/* global Cypress, cy, test, expect, before */
import { getDesktopContext } from '../support/utils'

let appContextListener

describe('Neo4j Desktop environment', () => {
before(() => {
cy.visit(Cypress.config('url'), {
onBeforeLoad: win => {
win.neo4jDesktopApi = {
getContext: () =>
Promise.resolve(getDesktopContext(Cypress.config, 'host')),
onContextUpdate: fn => (appContextListener = fn.bind(fn))
}
}
})
})
it('can auto connect using host + post fields', () => {
const frames = cy.get('[data-testid="frameCommand"]', { timeout: 10000 })
frames.should('have.length', 2)

// Auto connected = :play start
frames.first().should('contain', ':play start')
cy.wait(1000)
})
it('switches connection when that event is triggered using host + port fields', () => {
cy.executeCommand(':clear')
cy.wait(1000).then(() => {
appContextListener(
{ type: 'GRAPH_ACTIVE', id: 'test' },
getDesktopContext(Cypress.config, 'host')
)
})

const frames = cy.get('[data-testid="frameCommand"]', { timeout: 10000 })
frames.should('have.length', 1)

frames.first().should('contain', ':server switch success')

cy.get('[data-testid="frame"]', { timeout: 10000 })
.first()
.should('contain', 'Connection updated')
})
})
1 change: 0 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ module.exports = {
'\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|html)$':
'<rootDir>/test_utils/__mocks__/fileMock.js',
'\\.(css|less)$': '<rootDir>/test_utils/__mocks__/styleMock.js',
'\\.(graphql)$': '<rootDir>/test_utils/__mocks__/graphqlMock.js',
'^browser-styles(.*)$': '<rootDir>/src/browser/styles$1',
'^browser-components(.*)$': '<rootDir>/src/browser/components$1',
'^browser-hooks(.*)$': '<rootDir>/src/browser/hooks$1',
Expand Down
12 changes: 0 additions & 12 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,18 +123,9 @@
"xml2js": "^0.4.19"
},
"dependencies": {
"@apollo/react-hooks": "^3.0.1",
"@neo4j/browser-lambda-parser": "^1.0.3",
"@relate-by-ui/css": "^1.0.4",
"@relate-by-ui/saved-scripts": "^1.0.4",
"apollo-boost": "^0.4.4",
"apollo-cache-inmemory": "^1.6.3",
"apollo-link": "^1.2.12",
"apollo-link-context": "^1.0.18",
"apollo-link-error": "^1.1.11",
"apollo-link-http": "^1.5.15",
"apollo-link-ws": "^1.0.18",
"apollo-utilities": "^1.3.2",
"ascii-data-table": "^2.1.1",
"classnames": "^2.2.5",
"codemirror": "^5.29.0",
Expand All @@ -147,8 +138,6 @@
"dompurify": "^1.0.11",
"file-saver": "^1.3.8",
"firebase": "^5.8.3",
"graphql": "^14.4.2",
"graphql-tag": "^2.10.1",
"isomorphic-fetch": "^2.2.1",
"jsonic": "^0.3.0",
"jszip": "^3.2.2",
Expand All @@ -175,7 +164,6 @@
"styled-components": "^4.0.0",
"stylis": "^3.4.10",
"suber": "^5.0.1",
"subscriptions-transport-ws": "^0.9.16",
"swipe-js-iso": "^2.0.4",
"url-parse": "^1.1.9",
"uuid": "^3.0.1"
Expand Down
15 changes: 6 additions & 9 deletions src/browser/AppInit.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@ import {
createReduxMiddleware as createSuberReduxMiddleware
} from 'suber'
import { BusProvider } from 'react-suber'

import App from './modules/App/App'
import reducers from 'shared/rootReducer'
import epics from 'shared/rootEpic'
Expand All @@ -37,7 +36,6 @@ import { APP_START } from 'shared/modules/app/appDuck'
import { GlobalStyle } from './styles/global-styles.js'
import { detectRuntimeEnv } from 'services/utils.js'
import { NEO4J_CLOUD_DOMAINS } from 'shared/modules/settings/settingsDuck.js'
import RelateApiProvider from 'browser-components/relate-api/relate-api-provider'

// Configure localstorage sync
applyKeys(
Expand Down Expand Up @@ -85,21 +83,20 @@ bus.applyMiddleware((_, origin) => (channel, message, source) => {
// Introduce environment to be able to fork functionality
const env = detectRuntimeEnv(window, NEO4J_CLOUD_DOMAINS)

// URL we're on
const url = window.location.href

// Signal app upstart (for epics)
store.dispatch({ type: APP_START, url, env })
store.dispatch({ type: APP_START, url: window.location.href, env })

const AppInit = () => {
return (
<Provider store={store}>
<BusProvider bus={bus}>
<React.Fragment>
<GlobalStyle />
<RelateApiProvider urlString={url}>
<App />
</RelateApiProvider>
<App
desktopIntegrationPoint={
window && window.neo4jDesktopApi ? window.neo4jDesktopApi : null
}
/>
</React.Fragment>
</BusProvider>
</Provider>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`<DesktopIntegration> calls onMount with data on mounting 1`] = `<div />`;

exports[`<DesktopIntegration> calls onMount with data on mounting 2`] = `<div />`;

exports[`<DesktopIntegration> calls onXxx with data on event XXX 1`] = `<div />`;

exports[`<DesktopIntegration> does not render anything if no integration point 1`] = `<div />`;

exports[`<DesktopIntegration> does not render anything if there is an integration point 1`] = `<div />`;
131 changes: 131 additions & 0 deletions src/browser/components/DesktopIntegration/helpers.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
/*
* Copyright (c) 2002-2019 "Neo4j,"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/

import { NATIVE, KERBEROS } from 'services/bolt/boltHelpers'

export const getActiveGraph = (context = {}) => {
if (!context) return null
const { projects } = context
if (!Array.isArray(projects)) return null
const activeProject = projects.find(project => {
if (!project) return false
if (!(project.graphs && Array.isArray(project.graphs))) return false
return project.graphs.find(({ status }) => status === 'ACTIVE')
})
if (!activeProject) return null
return activeProject.graphs.find(({ status }) => status === 'ACTIVE')
}

export const getCredentials = (type, connection = null) => {
if (!connection) return null
const { configuration = null } = connection
if (!configuration) {
return null
}
if (!configuration.protocols) {
return null
}
if (typeof configuration.protocols[type] === 'undefined') {
return null
}
return configuration.protocols[type]
}

// XXX_YYY -> onXxxYyy
export const eventToHandler = type => {
if (typeof type !== 'string') return null
return (
'on' +
splitOnUnderscore(type)
.filter(notEmpty)
.map(toLower)
.map(upperFirst)
.join('')
)
}
const notEmpty = str => str.length > 0
const splitOnUnderscore = str => str.split('_')
const toLower = str => str.toLowerCase()
const upperFirst = str => str[0].toUpperCase() + str.substring(1)

export const didChangeActiveGraph = (newContext, oldContext) => {
const oldActive = getActiveGraph(oldContext)
const newActive = getActiveGraph(newContext)
if (!oldActive && !newActive) return false // If no active before and after = nu change
return !(oldActive && newActive && newActive.id === oldActive.id)
}

export const getActiveCredentials = (type, context) => {
const activeGraph = getActiveGraph(context)
if (!activeGraph || typeof activeGraph.connection === 'undefined') return null
const creds = getCredentials(type, activeGraph.connection)
return creds || null
}

const isKerberosEnabled = context => {
const activeGraph = getActiveGraph(context)
if (!activeGraph || typeof activeGraph.connection === 'undefined') {
return false
}
if (!activeGraph.connection) return null
const { configuration = null } = activeGraph.connection
if (!configuration) {
return false
}
if (
!configuration.authenticationMethods ||
!configuration.authenticationMethods.kerberos
) {
return false
}
if (!configuration.authenticationMethods.kerberos.enabled) {
return false
}
return configuration.authenticationMethods.kerberos
}

export const buildConnectionCredentialsObject = async (
context,
existingData = {},
getKerberosTicket = () => {}
) => {
const creds = getActiveCredentials('bolt', context)
if (!creds) return // No connection. Ignore and let browser show connection lost msgs.
const httpsCreds = getActiveCredentials('https', context)
const httpCreds = getActiveCredentials('http', context)
const kerberos = isKerberosEnabled(context)
if (kerberos !== false) {
creds.password = await getKerberosTicket(kerberos.servicePrincipal)
}
const restApi =
httpsCreds && httpsCreds.enabled
? `https://${httpsCreds.host}:${httpsCreds.port}`
: `http://${httpCreds.host}:${httpCreds.port}`
const connectionCreds = {
// Use current connections creds until we get new from API
...existingData,
...creds,
encrypted: creds.tlsLevel === 'REQUIRED',
host: creds.url || `bolt://${creds.host}:${creds.port}`,
restApi,
authenticationMethod: kerberos ? KERBEROS : NATIVE
}
return connectionCreds
}
Loading