diff --git a/build_scripts/webpack-rules.js b/build_scripts/webpack-rules.js
index bc22da1f4cd..e2587a2c188 100644
--- a/build_scripts/webpack-rules.js
+++ b/build_scripts/webpack-rules.js
@@ -118,10 +118,5 @@ module.exports = [
{
test: /\.html?$/,
use: ['html-loader']
- },
- {
- test: /\.(graphql|gql)$/,
- exclude: /node_modules/,
- loader: 'graphql-tag/loader'
}
]
diff --git a/e2e_tests/integration/desktop-env-url.spec.js b/e2e_tests/integration/desktop-env-url.spec.js
new file mode 100644
index 00000000000..2b4d164e8eb
--- /dev/null
+++ b/e2e_tests/integration/desktop-env-url.spec.js
@@ -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 .
+ */
+
+/* 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')
+ })
+})
diff --git a/e2e_tests/integration/desktop-env.spec.js b/e2e_tests/integration/desktop-env.spec.js
new file mode 100644
index 00000000000..bec363230e4
--- /dev/null
+++ b/e2e_tests/integration/desktop-env.spec.js
@@ -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 .
+ */
+
+/* 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')
+ })
+})
diff --git a/jest.config.js b/jest.config.js
index 9c0120a6f0b..f0ae80d7dd6 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -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)$':
'/test_utils/__mocks__/fileMock.js',
'\\.(css|less)$': '/test_utils/__mocks__/styleMock.js',
- '\\.(graphql)$': '/test_utils/__mocks__/graphqlMock.js',
'^browser-styles(.*)$': '/src/browser/styles$1',
'^browser-components(.*)$': '/src/browser/components$1',
'^browser-hooks(.*)$': '/src/browser/hooks$1',
diff --git a/package.json b/package.json
index 2c778cc6e0d..78b4858ac80 100644
--- a/package.json
+++ b/package.json
@@ -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",
@@ -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",
@@ -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"
diff --git a/src/browser/AppInit.jsx b/src/browser/AppInit.jsx
index eb4c00579ef..8ff02e749b8 100644
--- a/src/browser/AppInit.jsx
+++ b/src/browser/AppInit.jsx
@@ -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'
@@ -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(
@@ -85,11 +83,8 @@ 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 (
@@ -97,9 +92,11 @@ const AppInit = () => {
-
-
-
+
diff --git a/src/browser/components/DesktopIntegration/__snapshots__/index.test.js.snap b/src/browser/components/DesktopIntegration/__snapshots__/index.test.js.snap
new file mode 100644
index 00000000000..57c681448cb
--- /dev/null
+++ b/src/browser/components/DesktopIntegration/__snapshots__/index.test.js.snap
@@ -0,0 +1,11 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[` calls onMount with data on mounting 1`] = ``;
+
+exports[` calls onMount with data on mounting 2`] = ``;
+
+exports[` calls onXxx with data on event XXX 1`] = ``;
+
+exports[` does not render anything if no integration point 1`] = ``;
+
+exports[` does not render anything if there is an integration point 1`] = ``;
diff --git a/src/browser/components/DesktopIntegration/helpers.js b/src/browser/components/DesktopIntegration/helpers.js
new file mode 100644
index 00000000000..ec2913ce4ce
--- /dev/null
+++ b/src/browser/components/DesktopIntegration/helpers.js
@@ -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 .
+ */
+
+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
+}
diff --git a/src/browser/components/DesktopIntegration/helpers.test.js b/src/browser/components/DesktopIntegration/helpers.test.js
new file mode 100644
index 00000000000..e8a1c566b44
--- /dev/null
+++ b/src/browser/components/DesktopIntegration/helpers.test.js
@@ -0,0 +1,345 @@
+/*
+ * 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 .
+ */
+/* global test, expect, jest */
+import {
+ getCredentials,
+ getActiveGraph,
+ eventToHandler,
+ didChangeActiveGraph,
+ getActiveCredentials,
+ buildConnectionCredentialsObject
+} from './helpers'
+import { KERBEROS, NATIVE } from 'services/bolt/boltHelpers'
+
+test('getActiveGraph handles non objects and non-active projects', () => {
+ // Given
+ const graphs = [
+ null,
+ 'string',
+ undefined,
+ [1],
+ { project: null },
+ { projects: { x: 1 } },
+ { projects: [{ x: 1 }] },
+ { projects: [{ graphs: [{ status: 'NOPE' }] }] }
+ ]
+
+ // When && Then
+ graphs.forEach(graph => {
+ expect(getActiveGraph(graph)).toEqual(null)
+ })
+})
+test('getActiveGraph handles expected objects', () => {
+ // Given
+ const graph = {
+ status: 'ACTIVE'
+ }
+ const graph2 = {
+ status: 'INACTIVE'
+ }
+ const apiResponse = {
+ projects: [
+ {
+ graphs: [graph, graph2]
+ }
+ ]
+ }
+
+ // When
+ const activeGraph = getActiveGraph(apiResponse)
+
+ // Then
+ expect(activeGraph).toEqual(graph)
+})
+
+test('getCredentials handles non objects', () => {
+ // Given
+ const configs = [null, 'string', undefined, [1]]
+
+ // When && Then
+ configs.forEach(config => {
+ expect(getCredentials('xxx', config)).toBe(null)
+ })
+})
+
+test('getCredentials finds credentials on expected format', () => {
+ // Given
+
+ const config = {
+ bolt: {
+ username: 'molly',
+ password: 'stella'
+ },
+ http: {
+ username: 'oskar',
+ password: 'picachu'
+ }
+ }
+ const connection = {
+ configuration: { protocols: config }
+ }
+
+ // When
+ const boltRes = getCredentials('bolt', connection)
+ const httpRes = getCredentials('http', connection)
+ const notFoundRes = getCredentials('https', connection)
+
+ // Then
+ expect(boltRes).toEqual(config.bolt)
+ expect(httpRes).toEqual(config.http)
+ expect(notFoundRes).toBe(null)
+})
+
+test('XXX_YYY -> onXxxYyy', () => {
+ // Given
+ const tests = [
+ { type: undefined, expect: null },
+ { type: true, expect: null },
+ { type: 'XXX', expect: 'onXxx' },
+ { type: '_XXX', expect: 'onXxx' },
+ { type: 'XXX_YYY', expect: 'onXxxYyy' },
+ { type: 'XXX_YYY_ZZZ', expect: 'onXxxYyyZzz' },
+ { type: 'xxx', expect: 'onXxx' },
+ { type: 'xxx_yyy', expect: 'onXxxYyy' },
+ { type: 'XXX_123', expect: 'onXxx123' },
+ { type: '0', expect: 'on0' },
+ { type: '1', expect: 'on1' },
+ { type: 1, expect: null }
+ ]
+
+ // When && Then
+ tests.forEach(test => {
+ expect(eventToHandler(test.type)).toEqual(test.expect)
+ })
+})
+
+test('didChangeActiveGraph detects if the active graph changed', () => {
+ // Given
+ const createApiResponse = graphs => ({
+ projects: [{ graphs }]
+ })
+ const id1Active = createApiResponse([
+ { id: 1, status: 'ACTIVE' },
+ { id: 2, status: 'INACTIVE' }
+ ])
+ const id2Active = createApiResponse([
+ { id: 1, status: 'INACTIVE' },
+ { id: 2, status: 'ACTIVE' }
+ ])
+ const noActive = createApiResponse([
+ { id: 1, status: 'INACTIVE' },
+ { id: 2, status: 'INACTIVE' }
+ ])
+
+ // When
+ const noChange = didChangeActiveGraph(id1Active, id1Active)
+ const didChange = didChangeActiveGraph(id2Active, id1Active)
+ const didChange2 = didChangeActiveGraph(noActive, id1Active)
+ const noChange2 = didChangeActiveGraph(noActive, noActive)
+
+ // Then
+ expect(noChange).toBe(false)
+ expect(didChange).toBe(true)
+ expect(didChange2).toBe(true)
+ expect(noChange2).toBe(false)
+})
+
+test('getActiveCredentials finds the active connection from a context object and returns the creds', () => {
+ // Given
+ const bolt1 = {
+ username: 'one',
+ password: 'one1'
+ }
+ const bolt2 = {
+ username: 'two',
+ password: 'two2'
+ }
+ const createApiResponse = graphs => ({
+ projects: [{ graphs }]
+ })
+ const id1Active = createApiResponse([
+ {
+ id: 1,
+ status: 'ACTIVE',
+ connection: {
+ configuration: {
+ protocols: { bolt: bolt1 }
+ }
+ }
+ },
+ {
+ id: 2,
+ status: 'INACTIVE',
+ connection: {
+ configuration: {
+ protocols: { bolt: bolt2 }
+ }
+ }
+ }
+ ])
+ const id2Active = createApiResponse([
+ {
+ id: 1,
+ status: 'INACTIVE',
+ connection: {
+ configuration: {
+ protocols: { bolt: bolt1 }
+ }
+ }
+ },
+ {
+ id: 2,
+ status: 'ACTIVE',
+ connection: {
+ configuration: {
+ protocols: { bolt: bolt2 }
+ }
+ }
+ }
+ ])
+ const noActive = createApiResponse([
+ {
+ id: 1,
+ status: 'INACTIVE',
+ connection: {
+ configuration: {
+ protocols: { bolt: bolt1 }
+ }
+ }
+ },
+ {
+ id: 2,
+ status: 'INACTIVE',
+ connection: {
+ configuration: {
+ protocols: { bolt: bolt2 }
+ }
+ }
+ }
+ ])
+
+ // When
+ const firstActive = getActiveCredentials('bolt', id1Active)
+ const secondActive = getActiveCredentials('bolt', id2Active)
+ const zeroActive = getActiveCredentials('bolt', noActive)
+
+ // Then
+ expect(firstActive).toEqual(bolt1)
+ expect(secondActive).toEqual(bolt2)
+ expect(zeroActive).toEqual(null)
+})
+
+test('getActiveCredentials should extract https and http creds', () => {
+ const activeConnectionData = createApiResponse(activeGraph())
+
+ expect(getActiveCredentials('bolt', activeConnectionData)).toEqual(bolt())
+ expect(getActiveCredentials('http', activeConnectionData)).toEqual(http)
+ expect(getActiveCredentials('https', activeConnectionData)).toEqual(https)
+ expect(getActiveCredentials('foobar', activeConnectionData)).toBeFalsy()
+})
+
+describe('buildConnectionCredentialsObject', () => {
+ test('it creates an expected object from context, and adds kerberos ticket as password', async () => {
+ // Given
+ const kerberosTicket = 'kerberos-ticket-test'
+ const activeConnectionData = createApiResponse(
+ activeGraph({ enc: 'REQUIRED', kerberos: true })
+ )
+ const getKerberosTicket = jest.fn(() => kerberosTicket)
+ const connectionData = await buildConnectionCredentialsObject(
+ activeConnectionData,
+ {},
+ getKerberosTicket
+ )
+ expect(connectionData).toEqual({
+ username: 'one',
+ password: kerberosTicket,
+ url: 'bolt:port',
+ tlsLevel: 'REQUIRED',
+ encrypted: true,
+ host: 'bolt:port',
+ restApi: 'http://foo:bar',
+ authenticationMethod: KERBEROS
+ })
+ expect(getKerberosTicket).toHaveBeenCalledTimes(1)
+ expect(getKerberosTicket).toHaveBeenCalledWith('https')
+ })
+ test('it creates an expected object from context, without kerberos', async () => {
+ // Given
+ const kerberosTicket = 'kerberos-ticket-test'
+ const getKerberosTicket = jest.fn(() => kerberosTicket)
+ const activeConnectionData = createApiResponse(
+ activeGraph({ enc: 'REQUIRED', kerberos: false })
+ )
+ const connectionData = await buildConnectionCredentialsObject(
+ activeConnectionData,
+ {},
+ getKerberosTicket
+ )
+ expect(connectionData).toEqual({
+ username: 'one',
+ password: 'one1',
+ url: 'bolt:port',
+ tlsLevel: 'REQUIRED',
+ encrypted: true,
+ host: 'bolt:port',
+ restApi: 'http://foo:bar',
+ authenticationMethod: NATIVE
+ })
+ expect(getKerberosTicket).toHaveBeenCalledTimes(0)
+ })
+})
+
+const bolt = (enc = 'OPTIONAL') => ({
+ username: 'one',
+ password: 'one1',
+ url: 'bolt:port',
+ tlsLevel: enc
+})
+const http = {
+ host: 'foo',
+ port: 'bar'
+}
+
+const https = {
+ host: 'abc',
+ port: 'xyz'
+}
+const createApiResponse = graphs => ({
+ projects: [{ graphs }]
+})
+
+const activeGraph = (props = { enc: 'OPTIONAL', kerberos: false }) => [
+ {
+ id: 1,
+ status: 'ACTIVE',
+ connection: {
+ configuration: {
+ protocols: { bolt: bolt(props.enc), http, https },
+ authenticationMethods: {
+ kerberos: {
+ enabled: props.kerberos,
+ servicePrincipal: 'https'
+ }
+ }
+ }
+ }
+ }
+]
diff --git a/src/browser/components/DesktopIntegration/index.jsx b/src/browser/components/DesktopIntegration/index.jsx
new file mode 100644
index 00000000000..d12f1d9ecb3
--- /dev/null
+++ b/src/browser/components/DesktopIntegration/index.jsx
@@ -0,0 +1,82 @@
+/*
+ * 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 .
+ */
+
+import { Component } from 'react'
+import { getActiveGraph, getCredentials, eventToHandler } from './helpers'
+
+export default class DesktopIntegration extends Component {
+ setupListener () {
+ const { integrationPoint, onArgumentsChange = null } = this.props
+ if (integrationPoint && integrationPoint.onContextUpdate) {
+ const getKerberosTicket =
+ integrationPoint.getKerberosTicket || function () {}
+ integrationPoint.onContextUpdate((event, newContext, oldContext) => {
+ const handlerPropName = eventToHandler(event.type)
+ if (!handlerPropName) return
+ if (typeof this.props[handlerPropName] === 'undefined') return
+ this.props[handlerPropName](
+ event,
+ newContext,
+ oldContext,
+ getKerberosTicket
+ )
+ })
+ }
+ if (
+ integrationPoint &&
+ integrationPoint.onArgumentsChange &&
+ onArgumentsChange
+ ) {
+ integrationPoint.onArgumentsChange(onArgumentsChange)
+ }
+ }
+ loadInitialContext () {
+ const { integrationPoint, onMount = null } = this.props
+ if (integrationPoint && integrationPoint.getContext) {
+ const getKerberosTicket =
+ integrationPoint.getKerberosTicket || function () {}
+ integrationPoint
+ .getContext()
+ .then(context => {
+ const activeGraph = getActiveGraph(context) || {}
+ if (onMount) {
+ const connectionCredentials = getCredentials(
+ 'bolt',
+ activeGraph.connection || null
+ )
+ onMount(
+ activeGraph,
+ connectionCredentials,
+ context,
+ getKerberosTicket
+ )
+ }
+ })
+ .catch(e => {}) // Catch but don't bother
+ }
+ }
+ componentDidMount () {
+ this.loadInitialContext()
+ this.setupListener()
+ }
+ render () {
+ return null
+ }
+}
diff --git a/src/browser/components/DesktopIntegration/index.test.js b/src/browser/components/DesktopIntegration/index.test.js
new file mode 100644
index 00000000000..6e657b5ea3a
--- /dev/null
+++ b/src/browser/components/DesktopIntegration/index.test.js
@@ -0,0 +1,148 @@
+/*
+ * 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 .
+ */
+
+/* global test, expect, jest */
+
+import React from 'react'
+import { render, wait } from '@testing-library/react'
+import DesktopIntegration from './index'
+
+describe('', () => {
+ test('does not render anything if no integration point', () => {
+ // Given
+ const integrationPoint = null
+
+ // When
+ const { container } = render(
+
+ )
+
+ // Then
+ expect(container).toMatchSnapshot()
+ })
+ test('does not render anything if there is an integration point', () => {
+ // Given
+ const integrationPoint = { x: true }
+
+ // When
+ const { container } = render(
+
+ )
+
+ // Then
+ expect(container).toMatchSnapshot()
+ })
+ test('calls onMount with data on mounting', async () => {
+ // Given
+ const mFn = jest.fn()
+ const context = {
+ projects: [
+ {
+ graphs: [
+ {
+ status: 'ACTIVE',
+ configuration: {
+ protocols: {
+ bolt: {
+ username: 'neo4j'
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+ }
+ const integrationPoint = { getContext: () => Promise.resolve(context) }
+
+ // When
+ const { container, rerender } = render(
+
+ )
+ await wait(() => expect(mFn).toHaveBeenCalledTimes(1))
+ // Then
+ expect(container).toMatchSnapshot()
+
+ // When
+ rerender()
+
+ // Then
+ expect(mFn).toHaveBeenCalledTimes(1)
+ expect(container).toMatchSnapshot()
+ })
+ test('calls onXxx with data on event XXX', () => {
+ // Given
+ let componentOnContextUpdate
+ const fn = jest.fn()
+ const oldContext = { projects: [] }
+ const newContext = { projects: [{ project: {} }] }
+ const event = { type: 'XXX' }
+ const nonListenEvent = { type: 'YYY' }
+ const integrationPoint = {
+ onContextUpdate: fn => (componentOnContextUpdate = fn),
+ getKerberosTicket: jest.fn()
+ }
+
+ // When
+ const { container } = render(
+
+ )
+
+ // Then
+ expect(fn).toHaveBeenCalledTimes(0)
+
+ // When
+ componentOnContextUpdate(event, newContext, oldContext)
+
+ // Then
+ expect(fn).toHaveBeenCalledTimes(1)
+ expect(fn).toHaveBeenLastCalledWith(
+ event,
+ newContext,
+ oldContext,
+ integrationPoint.getKerberosTicket
+ )
+
+ // When
+ componentOnContextUpdate(nonListenEvent, newContext, oldContext) // We don't listen for this
+
+ // Then
+ expect(fn).toHaveBeenCalledTimes(1)
+ expect(fn).toHaveBeenLastCalledWith(
+ event,
+ newContext,
+ oldContext,
+ integrationPoint.getKerberosTicket
+ )
+
+ // When
+ componentOnContextUpdate(event, newContext, oldContext) // Another one we're listening on
+
+ // Then
+ expect(fn).toHaveBeenCalledTimes(2)
+ expect(fn).toHaveBeenLastCalledWith(
+ event,
+ newContext,
+ oldContext,
+ integrationPoint.getKerberosTicket
+ )
+ expect(container).toMatchSnapshot()
+ })
+})
diff --git a/src/browser/components/relate-api/on-workspace-change.graphql b/src/browser/components/relate-api/on-workspace-change.graphql
deleted file mode 100644
index b64fb2b6ee8..00000000000
--- a/src/browser/components/relate-api/on-workspace-change.graphql
+++ /dev/null
@@ -1,63 +0,0 @@
-subscription {
- onWorkspaceChange {
- me {
- activationKeys {
- featureName
- expirationDate
- }
- name
- }
- host {
- prefersColorScheme
- publicInternetAccess
- settings {
- allowSendReports
- allowSendStats
- allowStoreCredentials
- }
- }
- projects {
- id
- name
- graphs {
- id
- name
- status
- connection {
- principals {
- path
- protocols {
- bolt {
- enabled
- host
- password
- port
- tlsLevel
- url
- username
- }
- http {
- enabled
- host
- port
- url
- }
- https {
- enabled
- host
- port
- url
- }
- }
- authenticationMethods {
- servicePrincipal
- kerberos {
- enabled
- }
- }
- }
- }
- }
- }
- }
-}
diff --git a/src/browser/components/relate-api/relate-api-provider.jsx b/src/browser/components/relate-api/relate-api-provider.jsx
deleted file mode 100644
index a5c0eed08a5..00000000000
--- a/src/browser/components/relate-api/relate-api-provider.jsx
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 .
- */
-import React from 'react'
-import { ApolloProvider } from '@apollo/react-hooks'
-import { createClient } from 'browser-components/relate-api/relate-api.utils'
-
-export default function RelateApiProvider ({ urlString, children }) {
- // Load relate api graphql client
- const url = new URL(urlString)
- const apiEndpoint = url.searchParams.get('neo4jDesktopApiUrl')
- const apiClientId = url.searchParams.get('neo4jDesktopGraphAppClientId')
-
- // If not in relate-api env, render children
- if (!apiEndpoint) {
- return children
- }
- const relateApiClient = createClient(apiEndpoint, apiClientId)
-
- return {children}
-}
diff --git a/src/browser/components/relate-api/relate-api.hooks.jsx b/src/browser/components/relate-api/relate-api.hooks.jsx
deleted file mode 100644
index a2e80e871ee..00000000000
--- a/src/browser/components/relate-api/relate-api.hooks.jsx
+++ /dev/null
@@ -1,93 +0,0 @@
-import { useState, useEffect } from 'react'
-import { useApolloClient } from '@apollo/react-hooks'
-import workspaceQuery from './workspace.graphql'
-import workspaceSubscription from './on-workspace-change.graphql'
-import { getActiveGraphData, getPrefersColorScheme } from './relate-api.utils'
-import { deepEquals } from 'services/utils'
-
-export function useWorkspaceData () {
- let client
- // We might not be in a GraphQL env
- try {
- client = useApolloClient()
- } catch (e) {}
- const [workspaceData, setState] = useState(null)
- useEffect(() => {
- async function fetchData () {
- if (!client) {
- return
- }
- const { data } = await client.query({ query: workspaceQuery })
- setState(data)
- }
- fetchData()
- }, []) // initial load only
-
- return workspaceData
-}
-
-export function useWorkspaceDataOnChange () {
- let client
- // We might not be in a GraphQL env
- try {
- client = useApolloClient()
- } catch (e) {}
- const [workspaceData, setState] = useState(null)
- useEffect(() => {
- if (!client) {
- return
- }
- const observer = client.subscribe({ query: workspaceSubscription })
- observer.subscribe({
- next: ({ data }) => {
- setState(data)
- },
- error (err) {
- console.error('err', err)
- }
- })
- return () => observer.unsubscribe()
- }, []) // initial load only
-
- return workspaceData
-}
-
-export function useActiveGraphMonitor (onWorkspaceChangeData) {
- const [activeGraph, setActiveGraph] = useState(undefined)
- useEffect(
- () => {
- // Wait until initial data comes back
- if (activeGraph === undefined && !onWorkspaceChangeData) {
- return
- }
- const latestActiveGraph = getActiveGraphData({
- workspace: onWorkspaceChangeData.onWorkspaceChange
- })
- if (!deepEquals(activeGraph, latestActiveGraph)) {
- setActiveGraph(latestActiveGraph)
- }
- },
- [onWorkspaceChangeData]
- )
- return activeGraph
-}
-
-export function usePrefersColorSchemeMonitor (onWorkspaceChangeData) {
- const [prefersColorScheme, setPrefersColorScheme] = useState(undefined)
- useEffect(
- () => {
- // Wait until initial data comes back
- if (prefersColorScheme === undefined && !onWorkspaceChangeData) {
- return
- }
- const latestPrefersColorScheme = getPrefersColorScheme(
- onWorkspaceChangeData
- )
- if (prefersColorScheme !== latestPrefersColorScheme) {
- setPrefersColorScheme(latestPrefersColorScheme)
- }
- },
- [onWorkspaceChangeData]
- )
- return prefersColorScheme
-}
diff --git a/src/browser/components/relate-api/relate-api.jsx b/src/browser/components/relate-api/relate-api.jsx
deleted file mode 100644
index a8317278d3b..00000000000
--- a/src/browser/components/relate-api/relate-api.jsx
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * 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 .
- */
-import { useEffect } from 'react'
-import { withBus } from 'react-suber'
-import {
- useWorkspaceData,
- useWorkspaceDataOnChange,
- useActiveGraphMonitor,
- usePrefersColorSchemeMonitor
-} from './relate-api.hooks'
-import {
- getActiveGraphData,
- getPrefersColorScheme,
- detectDesktopThemeChanges,
- switchConnection,
- setInitialConnectionData
-} from './relate-api.utils'
-import { SILENT_DISCONNECT } from 'shared/modules/connections/connectionsDuck'
-
-function RelateApi ({ setEnvironmentTheme, defaultConnectionData, bus = {} }) {
- // Until supported in relate-api
- const getKerberosTicket =
- (window.neo4jDesktopApi || {}).getKerberosTicket || function () {}
-
- // Initial data from Relate-API
- const workspaceData = useWorkspaceData()
- useEffect(
- () => {
- if (!workspaceData) {
- return
- }
- const activeGraph = getActiveGraphData(workspaceData)
- setInitialConnectionData(
- activeGraph,
- getKerberosTicket,
- defaultConnectionData,
- bus
- )
- detectDesktopThemeChanges(
- setEnvironmentTheme,
- getPrefersColorScheme(workspaceData)
- )
- },
- [workspaceData]
- )
-
- // Subscriptions from Relate-API
- const onWorkspaceChangeData = useWorkspaceDataOnChange()
-
- const activeGraphMonitorData = useActiveGraphMonitor(onWorkspaceChangeData)
- useEffect(
- () => {
- if (activeGraphMonitorData === undefined) {
- // Not loaded yet
- return
- }
- if (activeGraphMonitorData === null) {
- bus.send(SILENT_DISCONNECT, {})
- return
- }
- switchConnection(
- activeGraphMonitorData,
- defaultConnectionData,
- getKerberosTicket,
- bus
- )
- },
- [activeGraphMonitorData]
- )
-
- const prefersColorSchemeMonitorData = usePrefersColorSchemeMonitor(
- onWorkspaceChangeData
- )
- useEffect(
- () => {
- detectDesktopThemeChanges(
- setEnvironmentTheme,
- prefersColorSchemeMonitorData
- )
- },
- [prefersColorSchemeMonitorData]
- )
-
- return null
-}
-
-export default withBus(RelateApi)
diff --git a/src/browser/components/relate-api/relate-api.utils.js b/src/browser/components/relate-api/relate-api.utils.js
deleted file mode 100644
index 0eeb61846fc..00000000000
--- a/src/browser/components/relate-api/relate-api.utils.js
+++ /dev/null
@@ -1,236 +0,0 @@
-/*
- * 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 .
- *
- */
-import { split } from 'apollo-link'
-import { createHttpLink } from 'apollo-link-http'
-import { WebSocketLink } from 'apollo-link-ws'
-import { setContext } from 'apollo-link-context'
-import ApolloClient from 'apollo-client'
-import { InMemoryCache } from 'apollo-cache-inmemory'
-import { getMainDefinition } from 'apollo-utilities'
-import { onError } from 'apollo-link-error'
-import { NATIVE, KERBEROS } from 'services/bolt/boltHelpers'
-import {
- SWITCH_CONNECTION,
- SWITCH_CONNECTION_FAILED
-} from 'shared/modules/connections/connectionsDuck'
-import { INJECTED_DISCOVERY } from 'shared/modules/discovery/discoveryDuck'
-
-export const createClient = (apiEndpoint, apiClientId = null) => {
- const apiEndpointUrl = new URL(apiEndpoint)
- const apiEndpointNoScheme = `${apiEndpointUrl.host}${
- apiEndpointUrl.pathname ? apiEndpointUrl.pathname : ''
- }`
-
- const httpLink = createHttpLink({
- uri: apiEndpoint
- })
-
- const wsLink = new WebSocketLink({
- uri: `ws://${apiEndpointNoScheme}`,
- options: {
- reconnect: true,
- connectionParams: {
- ClientId: apiClientId
- }
- }
- })
-
- const authLink = setContext((_, { headers }) => {
- return {
- headers: {
- ...headers,
- ClientId: apiClientId
- }
- }
- })
-
- const link = split(
- // split based on operation type
- ({ query }) => {
- const { kind, operation } = getMainDefinition(query)
- return kind === 'OperationDefinition' && operation === 'subscription'
- },
- wsLink,
- authLink.concat(httpLink)
- )
-
- const errorLink = onError(({ graphQLErrors, networkError }) => {
- if (graphQLErrors) {
- graphQLErrors.forEach(({ message, locations, path }) =>
- console.log(
- `Relate API GraphQL error: Message: ${message}, Location: ${locations}, Path: ${path}`
- )
- )
- }
-
- if (networkError) {
- console.log(`Relate API Network error: ${networkError}`)
- }
- })
-
- const client = new ApolloClient({
- link: errorLink.concat(link),
- cache: new InMemoryCache()
- })
-
- return client
-}
-
-export function getPrefersColorScheme (workspaceData) {
- return (
- ((workspaceData.workspace || workspaceData.onWorkspaceChange).host || {})
- .prefersColorScheme || null
- )
-}
-
-const graphStatus = {
- ACTIVE: 'ACTIVE'
-}
-export function getActiveGraphData (workspaceData) {
- return (
- (workspaceData &&
- workspaceData.workspace &&
- workspaceData.workspace.projects) ||
- []
- ).reduce((activeGraph, project) => {
- if (!project.graphs) {
- return activeGraph
- }
- const active = project.graphs.filter(
- graph => graph.status === graphStatus.ACTIVE
- )
- if (!active || !active.length) {
- return activeGraph
- }
- return active[0]
- }, null)
-}
-
-export const getCredentialsForGraph = (protocol, graph) => {
- if (!graph || !graph.connection) {
- return null
- }
- const { principals = null } = graph.connection
- if (!principals) {
- return null
- }
- if (!principals.protocols) {
- return null
- }
- if (typeof principals.protocols[protocol] === 'undefined') {
- return null
- }
- return principals.protocols[protocol]
-}
-
-export async function createConnectionCredentialsObject (
- activeGraph,
- existingData,
- getKerberosTicket = () => {}
-) {
- const creds = getCredentialsForGraph('bolt', activeGraph)
- if (!creds) return // No connection. Ignore and let browser show connection lost msgs.
- const httpsCreds = getCredentialsForGraph('https', activeGraph)
- const httpCreds = getCredentialsForGraph('http', activeGraph)
- const kerberos = isKerberosEnabled(activeGraph)
- 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
-}
-
-function isKerberosEnabled (activeGraph) {
- if (!activeGraph || typeof activeGraph.connection === 'undefined') {
- return false
- }
- if (!activeGraph.connection) return null
- const { principals = null } = activeGraph.connection
- if (!principals) {
- return false
- }
- if (
- !principals.authenticationMethods ||
- !principals.authenticationMethods.kerberos
- ) {
- return false
- }
- if (!principals.authenticationMethods.kerberos.enabled) {
- return false
- }
- return principals.authenticationMethods.kerberos
-}
-
-export function detectDesktopThemeChanges (
- setEnvironmentTheme,
- prefersColorScheme
-) {
- if (prefersColorScheme) {
- setEnvironmentTheme(prefersColorScheme)
- } else {
- setEnvironmentTheme(null)
- }
-}
-
-export async function switchConnection (
- activeGraph,
- defaultConnectionData,
- getKerberosTicket,
- bus
-) {
- const connectionCreds = await createConnectionCredentialsObject(
- activeGraph,
- defaultConnectionData,
- getKerberosTicket
- )
- bus.send(SWITCH_CONNECTION, connectionCreds)
-}
-export async function setInitialConnectionData (
- activeGraph,
- defaultConnectionData,
- getKerberosTicket,
- bus
-) {
- const connectionsCredentials = await createConnectionCredentialsObject(
- activeGraph,
- defaultConnectionData,
- getKerberosTicket
- )
-
- // No connection. Probably no graph active.
- if (!connectionsCredentials) {
- bus.send(SWITCH_CONNECTION_FAILED)
- return
- }
- bus.send(INJECTED_DISCOVERY, connectionsCredentials)
-}
diff --git a/src/browser/components/relate-api/workspace.graphql b/src/browser/components/relate-api/workspace.graphql
deleted file mode 100644
index 5f6a99013eb..00000000000
--- a/src/browser/components/relate-api/workspace.graphql
+++ /dev/null
@@ -1,76 +0,0 @@
-query {
- workspace {
- me {
- activationKeys {
- featureName
- expirationDate
- }
- name
- }
- host {
- prefersColorScheme
- publicInternetAccess
- settings {
- allowSendReports
- allowSendStats
- allowStoreCredentials
- }
- }
- projects {
- id
- name
- graphs {
- id
- name
- status
- connection {
- type
- databaseType
- databaseStatus
- info {
- edition
- version
- }
- principals {
- path
- protocols {
- bolt {
- enabled
- host
- password
- port
- tlsLevel
- url
- username
- }
- http {
- enabled
- host
- port
- url
- }
- https {
- enabled
- host
- port
- url
- }
- }
- authenticationMethods {
- servicePrincipal
- kerberos {
- enabled
- }
- }
- }
- }
- }
- apps {
- id
- name
- publisher
- version
- }
- }
- }
-}
diff --git a/src/browser/modules/App/App.jsx b/src/browser/modules/App/App.jsx
index deab7918c91..ce210801c81 100644
--- a/src/browser/modules/App/App.jsx
+++ b/src/browser/modules/App/App.jsx
@@ -39,10 +39,16 @@ import {
getLastConnectionUpdate,
getActiveConnectionData,
isConnected,
- getConnectionData
+ getConnectionData,
+ SILENT_DISCONNECT,
+ SWITCH_CONNECTION,
+ SWITCH_CONNECTION_FAILED
} from 'shared/modules/connections/connectionsDuck'
import { toggle } from 'shared/modules/sidebar/sidebarDuck'
-import { CONNECTION_ID } from 'shared/modules/discovery/discoveryDuck'
+import {
+ CONNECTION_ID,
+ INJECTED_DISCOVERY
+} from 'shared/modules/discovery/discoveryDuck'
import {
StyledWrapper,
StyledApp,
@@ -57,21 +63,24 @@ import asTitleString from '../DocTitle/titleStringBuilder'
import Intercom from '../Intercom'
import Render from 'browser-components/Render'
import BrowserSyncInit from '../Sync/BrowserSyncInit'
+import DesktopIntegration from 'browser-components/DesktopIntegration'
+import {
+ getActiveGraph,
+ buildConnectionCredentialsObject
+} from 'browser-components/DesktopIntegration/helpers'
import { getMetadata, getUserAuthStatus } from 'shared/modules/sync/syncDuck'
import ErrorBoundary from 'browser-components/ErrorBoundary'
import { getExperimentalFeatures } from 'shared/modules/experimentalFeatures/experimentalFeaturesDuck'
import FeatureToggleProvider from '../FeatureToggle/FeatureToggleProvider'
-import { inWebEnv } from 'shared/modules/app/appDuck'
+import { inWebEnv, URL_ARGUMENTS_CHANGE } from 'shared/modules/app/appDuck'
import useDerivedTheme from 'browser-hooks/useDerivedTheme'
import FileDrop from 'browser-components/FileDrop/FileDrop'
-import RelateApi from 'browser-components/relate-api/relate-api'
export function App (props) {
const [derivedTheme, setEnvironmentTheme] = useDerivedTheme(
props.theme,
LIGHT_THEME
)
- const themeData = themes[derivedTheme] || themes[LIGHT_THEME]
useEffect(() => {
document.addEventListener('keyup', focusEditorOnSlash)
@@ -83,6 +92,15 @@ export function App (props) {
}
}, [])
+ const detectDesktopThemeChanges = (_, newContext) => {
+ if (newContext.global.prefersColorScheme) {
+ setEnvironmentTheme(newContext.global.prefersColorScheme)
+ } else {
+ setEnvironmentTheme(null)
+ }
+ }
+ const themeData = themes[derivedTheme] || themes[LIGHT_THEME]
+
const focusEditorOnSlash = e => {
if (['INPUT', 'TEXTAREA'].indexOf(e.target.tagName) > -1) return
if (e.key !== '/') return
@@ -92,7 +110,6 @@ export function App (props) {
if (e.keyCode !== 27) return
props.bus && props.bus.send(EXPAND)
}
-
const {
drawer,
cmdchar,
@@ -109,8 +126,7 @@ export function App (props) {
browserSyncAuthStatus,
experimentalFeatures,
store,
- codeFontLigatures,
- defaultConnectionData
+ codeFontLigatures
} = props
const wrapperClassNames = []
@@ -120,16 +136,33 @@ export function App (props) {
return (
-
+ {
+ props.setInitialConnectionData(
+ activeGraph,
+ connectionsCredentials,
+ context,
+ getKerberosTicket
+ )
+ detectDesktopThemeChanges(null, context)
+ }}
+ onGraphActive={props.switchConnection}
+ onGraphInactive={props.closeConnectionMaybe}
+ onColorSchemeUpdated={detectDesktopThemeChanges}
+ />
@@ -198,9 +231,61 @@ const mapDispatchToProps = dispatch => {
}
}
+const mergeProps = (stateProps, dispatchProps, ownProps) => {
+ const switchConnection = async (
+ event,
+ newContext,
+ oldContext,
+ getKerberosTicket
+ ) => {
+ const connectionCreds = await buildConnectionCredentialsObject(
+ newContext,
+ stateProps.defaultConnectionData,
+ getKerberosTicket
+ )
+ ownProps.bus.send(SWITCH_CONNECTION, connectionCreds)
+ }
+ const setInitialConnectionData = async (
+ graph,
+ credentials,
+ context,
+ getKerberosTicket
+ ) => {
+ const connectionCreds = await buildConnectionCredentialsObject(
+ context,
+ stateProps.defaultConnectionData,
+ getKerberosTicket
+ )
+ // No connection. Probably no graph active.
+ if (!connectionCreds) {
+ ownProps.bus.send(SWITCH_CONNECTION_FAILED)
+ return
+ }
+ ownProps.bus.send(INJECTED_DISCOVERY, connectionCreds)
+ }
+ const closeConnectionMaybe = (event, newContext, oldContext) => {
+ const activeGraph = getActiveGraph(newContext)
+ if (activeGraph) return // We still got an active graph, do nothing
+ ownProps.bus.send(SILENT_DISCONNECT, {})
+ }
+ const onArgumentsChange = argsString => {
+ ownProps.bus.send(URL_ARGUMENTS_CHANGE, { url: `?${argsString}` })
+ }
+ return {
+ ...stateProps,
+ ...ownProps,
+ ...dispatchProps,
+ switchConnection,
+ setInitialConnectionData,
+ closeConnectionMaybe,
+ onArgumentsChange
+ }
+}
+
export default withBus(
connect(
mapStateToProps,
- mapDispatchToProps
+ mapDispatchToProps,
+ mergeProps
)(App)
)
diff --git a/src/browser/modules/App/App.test.js b/src/browser/modules/App/App.test.js
index 3c56b3e2a30..62b347847f8 100644
--- a/src/browser/modules/App/App.test.js
+++ b/src/browser/modules/App/App.test.js
@@ -23,6 +23,8 @@ import React from 'react'
import { render } from '@testing-library/react'
import configureMockStore from 'redux-mock-store'
import { App } from './App'
+import { buildConnectionCredentialsObject } from 'browser-components/DesktopIntegration/helpers'
+import { flushPromises } from 'services/utils'
const mockStore = configureMockStore()
const store = mockStore({})
@@ -34,21 +36,89 @@ jest.mock('./styled', () => {
const orig = require.requireActual('./styled')
return {
...orig,
- StyledApp: () => Loaded
+ StyledApp: () => null
}
})
describe('App', () => {
test('App loads', async () => {
// Given
+ const getKerberosTicket = jest.fn(() => Promise.resolve('xxx'))
+ const desktopIntegrationPoint = getIntegrationPoint(true, getKerberosTicket)
+ let connectionCreds = null
const props = {
- store
+ store,
+ desktopIntegrationPoint,
+ setInitialConnectionData: async (
+ graph,
+ credentials,
+ context,
+ getKerberosTicket
+ ) => {
+ connectionCreds = await buildConnectionCredentialsObject(
+ context,
+ {},
+ getKerberosTicket
+ )
+ }
}
// When
- const { getByText } = render()
+ render()
// Then
- expect(getByText('Loaded'))
+ await flushPromises()
+ expect(connectionCreds).toMatchObject({
+ authenticationMethod: 'KERBEROS',
+ password: 'xxx'
+ })
+ expect(getKerberosTicket).toHaveBeenCalledTimes(1)
})
})
+
+const getIntegrationPoint = (kerberosEnabled, getKerberosTicket) => {
+ const context = Promise.resolve(getDesktopContext(kerberosEnabled))
+ return {
+ getKerberosTicket: getKerberosTicket,
+ getContext: () => context
+ }
+}
+
+const getDesktopContext = (kerberosEnabled = false) => ({
+ projects: [
+ {
+ graphs: [
+ {
+ status: 'ACTIVE',
+ connection: {
+ type: 'REMOTE',
+ configuration: {
+ authenticationMethods: {
+ kerberos: {
+ enabled: kerberosEnabled,
+ servicePrincipal: 'KERBEROS'
+ }
+ },
+ protocols: {
+ bolt: {
+ enabled: true,
+ username: 'neo4j',
+ password: 'password',
+ tlsLevel: 'REQUIRED',
+ url: `bolt://localhost:7687`
+ },
+ http: {
+ enabled: true,
+ username: 'neo4j',
+ password: 'password',
+ host: 'localhost',
+ port: '7474'
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ ]
+})
diff --git a/test_utils/__mocks__/graphqlMock.js b/test_utils/__mocks__/graphqlMock.js
deleted file mode 100644
index 2dcc429cb72..00000000000
--- a/test_utils/__mocks__/graphqlMock.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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 .
- */
-
-module.exports = {}
diff --git a/yarn.lock b/yarn.lock
index 0f8d9919048..9c805adc740 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -2,24 +2,6 @@
# yarn lockfile v1
-"@apollo/react-common@^3.1.1":
- version "3.1.1"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/@apollo/react-common/-/react-common-3.1.1.tgz#540619d8276d750ce4f381c9cf8f51a6f657f75d"
- integrity sha1-VAYZ2CdtdQzk84HJz49RpvZX910=
- dependencies:
- ts-invariant "^0.4.4"
- tslib "^1.10.0"
-
-"@apollo/react-hooks@^3.0.1":
- version "3.1.1"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/@apollo/react-hooks/-/react-hooks-3.1.1.tgz#8e5a0f281a9778aaf17496ad38a178d23375077c"
- integrity sha1-jloPKBqXeKrxdJatOKF40jN1B3w=
- dependencies:
- "@apollo/react-common" "^3.1.1"
- "@wry/equality" "^0.1.9"
- ts-invariant "^0.4.4"
- tslib "^1.10.0"
-
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.5.5":
version "7.5.5"
resolved "https://neo.jfrog.io/neo/api/npm/npm/@babel/code-frame/-/code-frame-7.5.5.tgz#bc0782f6d69f7b7d49531219699b988f669a8f9d"
@@ -1420,7 +1402,7 @@
resolved "https://neo.jfrog.io/neo/api/npm/npm/@types/minimatch/-/minimatch-3.0.3.tgz#3dca0e3f33b200fc7d1139c0cd96c1268cadfd9d"
integrity sha1-PcoOPzOyAPx9ETnAzZbBJoyt/Z0=
-"@types/node@*", "@types/node@>=6":
+"@types/node@*":
version "12.7.5"
resolved "https://neo.jfrog.io/neo/api/npm/npm/@types/node/-/node-12.7.5.tgz#e19436e7f8e9b4601005d73673b6dc4784ffcc2f"
integrity sha1-4ZQ25/jptGAQBdc2c7bcR4T/zC8=
@@ -1489,11 +1471,6 @@
dependencies:
"@types/yargs-parser" "*"
-"@types/zen-observable@^0.8.0":
- version "0.8.0"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d"
- integrity sha1-i2OrfxqlMhJIqtWsiQpIVlbc6k0=
-
"@webassemblyjs/ast@1.8.5":
version "1.8.5"
resolved "https://neo.jfrog.io/neo/api/npm/npm/@webassemblyjs/ast/-/ast-1.8.5.tgz#51b1c5fe6576a34953bf4b253df9f0d490d9e359"
@@ -1640,21 +1617,6 @@
"@webassemblyjs/wast-parser" "1.8.5"
"@xtuc/long" "4.2.2"
-"@wry/context@^0.4.0":
- version "0.4.4"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/@wry/context/-/context-0.4.4.tgz#e50f5fa1d6cfaabf2977d1fda5ae91717f8815f8"
- integrity sha1-5Q9fodbPqr8pd9H9pa6RcX+IFfg=
- dependencies:
- "@types/node" ">=6"
- tslib "^1.9.3"
-
-"@wry/equality@^0.1.2", "@wry/equality@^0.1.9":
- version "0.1.9"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/@wry/equality/-/equality-0.1.9.tgz#b13e18b7a8053c6858aa6c85b54911fb31e3a909"
- integrity sha1-sT4Yt6gFPGhYqmyFtUkR+zHjqQk=
- dependencies:
- tslib "^1.9.3"
-
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
resolved "https://neo.jfrog.io/neo/api/npm/npm/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790"
@@ -1813,117 +1775,6 @@ anymatch@^2.0.0:
micromatch "^3.1.4"
normalize-path "^2.1.1"
-apollo-boost@^0.4.4:
- version "0.4.4"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/apollo-boost/-/apollo-boost-0.4.4.tgz#7c278dac6cb6fa3f2f710c56baddc6e3ae730651"
- integrity sha1-fCeNrGy2+j8vcQxWut3G465zBlE=
- dependencies:
- apollo-cache "^1.3.2"
- apollo-cache-inmemory "^1.6.3"
- apollo-client "^2.6.4"
- apollo-link "^1.0.6"
- apollo-link-error "^1.0.3"
- apollo-link-http "^1.3.1"
- graphql-tag "^2.4.2"
- ts-invariant "^0.4.0"
- tslib "^1.9.3"
-
-apollo-cache-inmemory@^1.6.3:
- version "1.6.3"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/apollo-cache-inmemory/-/apollo-cache-inmemory-1.6.3.tgz#826861d20baca4abc45f7ca7a874105905b8525d"
- integrity sha1-gmhh0guspKvEX3ynqHQQWQW4Ul0=
- dependencies:
- apollo-cache "^1.3.2"
- apollo-utilities "^1.3.2"
- optimism "^0.10.0"
- ts-invariant "^0.4.0"
- tslib "^1.9.3"
-
-apollo-cache@1.3.2, apollo-cache@^1.3.2:
- version "1.3.2"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/apollo-cache/-/apollo-cache-1.3.2.tgz#df4dce56240d6c95c613510d7e409f7214e6d26a"
- integrity sha1-303OViQNbJXGE1ENfkCfchTm0mo=
- dependencies:
- apollo-utilities "^1.3.2"
- tslib "^1.9.3"
-
-apollo-client@^2.6.4:
- version "2.6.4"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/apollo-client/-/apollo-client-2.6.4.tgz#872c32927263a0d34655c5ef8a8949fbb20b6140"
- integrity sha1-hywyknJjoNNGVcXviolJ+7ILYUA=
- dependencies:
- "@types/zen-observable" "^0.8.0"
- apollo-cache "1.3.2"
- apollo-link "^1.0.0"
- apollo-utilities "1.3.2"
- symbol-observable "^1.0.2"
- ts-invariant "^0.4.0"
- tslib "^1.9.3"
- zen-observable "^0.8.0"
-
-apollo-link-context@^1.0.18:
- version "1.0.19"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/apollo-link-context/-/apollo-link-context-1.0.19.tgz#3c9ba5bf75ed5428567ce057b8837ef874a58987"
- integrity sha1-PJulv3XtVChWfOBXuIN++HSliYc=
- dependencies:
- apollo-link "^1.2.13"
- tslib "^1.9.3"
-
-apollo-link-error@^1.0.3, apollo-link-error@^1.1.11:
- version "1.1.12"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/apollo-link-error/-/apollo-link-error-1.1.12.tgz#e24487bb3c30af0654047611cda87038afbacbf9"
- integrity sha1-4kSHuzwwrwZUBHYRzahwOK+6y/k=
- dependencies:
- apollo-link "^1.2.13"
- apollo-link-http-common "^0.2.15"
- tslib "^1.9.3"
-
-apollo-link-http-common@^0.2.15:
- version "0.2.15"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/apollo-link-http-common/-/apollo-link-http-common-0.2.15.tgz#304e67705122bf69a9abaded4351b10bc5efd6d9"
- integrity sha1-ME5ncFEiv2mpq63tQ1GxC8Xv1tk=
- dependencies:
- apollo-link "^1.2.13"
- ts-invariant "^0.4.0"
- tslib "^1.9.3"
-
-apollo-link-http@^1.3.1, apollo-link-http@^1.5.15:
- version "1.5.16"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/apollo-link-http/-/apollo-link-http-1.5.16.tgz#44fe760bcc2803b8a7f57fc9269173afb00f3814"
- integrity sha1-RP52C8woA7in9X/JJpFzr7APOBQ=
- dependencies:
- apollo-link "^1.2.13"
- apollo-link-http-common "^0.2.15"
- tslib "^1.9.3"
-
-apollo-link-ws@^1.0.18:
- version "1.0.19"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/apollo-link-ws/-/apollo-link-ws-1.0.19.tgz#dfa871d4df883a8777c9556c872fc892e103daa5"
- integrity sha1-36hx1N+IOod3yVVshy/IkuED2qU=
- dependencies:
- apollo-link "^1.2.13"
- tslib "^1.9.3"
-
-apollo-link@^1.0.0, apollo-link@^1.0.6, apollo-link@^1.2.12, apollo-link@^1.2.13:
- version "1.2.13"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/apollo-link/-/apollo-link-1.2.13.tgz#dff00fbf19dfcd90fddbc14b6a3f9a771acac6c4"
- integrity sha1-3/APvxnfzZD928FLaj+adxrKxsQ=
- dependencies:
- apollo-utilities "^1.3.0"
- ts-invariant "^0.4.0"
- tslib "^1.9.3"
- zen-observable-ts "^0.8.20"
-
-apollo-utilities@1.3.2, apollo-utilities@^1.3.0, apollo-utilities@^1.3.2:
- version "1.3.2"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/apollo-utilities/-/apollo-utilities-1.3.2.tgz#8cbdcf8b012f664cd6cb5767f6130f5aed9115c9"
- integrity sha1-jL3PiwEvZkzWy1dn9hMPWu2RFck=
- dependencies:
- "@wry/equality" "^0.1.2"
- fast-json-stable-stringify "^2.0.0"
- ts-invariant "^0.4.0"
- tslib "^1.9.3"
-
app-root-path@^2.0.0:
version "2.2.1"
resolved "https://neo.jfrog.io/neo/api/npm/npm/app-root-path/-/app-root-path-2.2.1.tgz#d0df4a682ee408273583d43f6f79e9892624bc9a"
@@ -2409,11 +2260,6 @@ babylon@^6.17.0, babylon@^6.18.0:
resolved "https://neo.jfrog.io/neo/api/npm/npm/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3"
integrity sha1-ry87iPpvXB5MY00aD46sT1WzleM=
-backo2@^1.0.2:
- version "1.0.2"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/backo2/-/backo2-1.0.2.tgz#31ab1ac8b129363463e35b3ebb69f4dfcfba7947"
- integrity sha1-MasayLEpNjRj41s+u2n038+6eUc=
-
balanced-match@0.1.0:
version "0.1.0"
resolved "https://neo.jfrog.io/neo/api/npm/npm/balanced-match/-/balanced-match-0.1.0.tgz#b504bd05869b39259dd0c5efc35d843176dccc4a"
@@ -4401,7 +4247,7 @@ etag@~1.8.1:
resolved "https://neo.jfrog.io/neo/api/npm/npm/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
-eventemitter3@^3.0.0, eventemitter3@^3.1.0:
+eventemitter3@^3.0.0:
version "3.1.2"
resolved "https://neo.jfrog.io/neo/api/npm/npm/eventemitter3/-/eventemitter3-3.1.2.tgz#2d3d48f9c346698fce83a85d7d664e98535df6e7"
integrity sha1-LT1I+cNGaY/Og6hdfWZOmFNd9uc=
@@ -5276,18 +5122,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6
resolved "https://neo.jfrog.io/neo/api/npm/npm/graceful-fs/-/graceful-fs-4.2.2.tgz#6f0952605d0140c1cfdb138ed005775b92d67b02"
integrity sha1-bwlSYF0BQMHP2xOO0AV3W5LWewI=
-graphql-tag@^2.10.1, graphql-tag@^2.4.2:
- version "2.10.1"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/graphql-tag/-/graphql-tag-2.10.1.tgz#10aa41f1cd8fae5373eaf11f1f67260a3cad5e02"
- integrity sha1-EKpB8c2PrlNz6vEfH2cmCjytXgI=
-
-graphql@^14.4.2:
- version "14.5.6"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/graphql/-/graphql-14.5.6.tgz#3fa12173b50e6ccdef953c31c82f37c50ef58bec"
- integrity sha1-P6Ehc7UObM3vlTwxyC83xQ71i+w=
- dependencies:
- iterall "^1.2.2"
-
growly@^1.3.0:
version "1.3.0"
resolved "https://neo.jfrog.io/neo/api/npm/npm/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
@@ -6264,11 +6098,6 @@ istanbul-reports@^2.2.6:
dependencies:
handlebars "^4.1.2"
-iterall@^1.2.1, iterall@^1.2.2:
- version "1.2.2"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/iterall/-/iterall-1.2.2.tgz#92d70deb8028e0c39ff3164fdbf4d8b088130cd7"
- integrity sha1-ktcN64Ao4MOf8xZP2/TYsIgTDNc=
-
jest-canvas-mock@^1.1.0:
version "1.1.0"
resolved "https://neo.jfrog.io/neo/api/npm/npm/jest-canvas-mock/-/jest-canvas-mock-1.1.0.tgz#f6ed0998b6be3ae2b7e095d7173441ae62cc831e"
@@ -8106,13 +7935,6 @@ opn@^5.5.0:
dependencies:
is-wsl "^1.1.0"
-optimism@^0.10.0:
- version "0.10.3"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/optimism/-/optimism-0.10.3.tgz#163268fdc741dea2fb50f300bedda80356445fd7"
- integrity sha1-FjJo/cdB3qL7UPMAvt2oA1ZEX9c=
- dependencies:
- "@wry/context" "^0.4.0"
-
optimist@^0.6.1:
version "0.6.1"
resolved "https://neo.jfrog.io/neo/api/npm/npm/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686"
@@ -10769,17 +10591,6 @@ suber@^5.0.1:
resolved "https://neo.jfrog.io/neo/api/npm/npm/suber/-/suber-5.0.1.tgz#78fc93bfc7444d11a23e160c5f062dc7967d6a58"
integrity sha1-ePyTv8dETRGiPhYMXwYtx5Z9alg=
-subscriptions-transport-ws@^0.9.16:
- version "0.9.16"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/subscriptions-transport-ws/-/subscriptions-transport-ws-0.9.16.tgz#90a422f0771d9c32069294c08608af2d47f596ec"
- integrity sha1-kKQi8HcdnDIGkpTAhgivLUf1luw=
- dependencies:
- backo2 "^1.0.2"
- eventemitter3 "^3.1.0"
- iterall "^1.2.1"
- symbol-observable "^1.0.4"
- ws "^5.2.0"
-
sugarss@^2.0.0:
version "2.0.0"
resolved "https://neo.jfrog.io/neo/api/npm/npm/sugarss/-/sugarss-2.0.0.tgz#ddd76e0124b297d40bf3cca31c8b22ecb43bc61d"
@@ -10835,7 +10646,7 @@ symbol-observable@1.0.1:
resolved "https://neo.jfrog.io/neo/api/npm/npm/symbol-observable/-/symbol-observable-1.0.1.tgz#8340fc4702c3122df5d22288f88283f513d3fdd4"
integrity sha1-g0D8RwLDEi310iKI+IKD9RPT/dQ=
-symbol-observable@^1.0.2, symbol-observable@^1.0.3, symbol-observable@^1.0.4, symbol-observable@^1.2.0:
+symbol-observable@^1.0.3, symbol-observable@^1.2.0:
version "1.2.0"
resolved "https://neo.jfrog.io/neo/api/npm/npm/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
integrity sha1-wiaIrtTqs83C3+rLtWFmBWCgCAQ=
@@ -11072,19 +10883,12 @@ tryer@^1.0.1:
resolved "https://neo.jfrog.io/neo/api/npm/npm/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8"
integrity sha1-8shUBoALmw90yfdGW4HqrSQSUvg=
-ts-invariant@^0.4.0, ts-invariant@^0.4.4:
- version "0.4.4"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/ts-invariant/-/ts-invariant-0.4.4.tgz#97a523518688f93aafad01b0e80eb803eb2abd86"
- integrity sha1-l6UjUYaI+TqvrQGw6A64A+sqvYY=
- dependencies:
- tslib "^1.9.3"
-
tslib@1.9.3:
version "1.9.3"
resolved "https://neo.jfrog.io/neo/api/npm/npm/tslib/-/tslib-1.9.3.tgz#d7e4dd79245d85428c4d7e4822a79917954ca286"
integrity sha1-1+TdeSRdhUKMTX5IIqeZF5VMooY=
-tslib@^1.10.0, tslib@^1.9.0, tslib@^1.9.3:
+tslib@^1.9.0:
version "1.10.0"
resolved "https://neo.jfrog.io/neo/api/npm/npm/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
integrity sha1-w8GflZc/sKYpc/sJ2Q2WHuQ+XIo=
@@ -11916,16 +11720,3 @@ yauzl@2.4.1:
integrity sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=
dependencies:
fd-slicer "~1.0.1"
-
-zen-observable-ts@^0.8.20:
- version "0.8.20"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/zen-observable-ts/-/zen-observable-ts-0.8.20.tgz#44091e335d3fcbc97f6497e63e7f57d5b516b163"
- integrity sha1-RAkeM10/y8l/ZJfmPn9X1bUWsWM=
- dependencies:
- tslib "^1.9.3"
- zen-observable "^0.8.0"
-
-zen-observable@^0.8.0:
- version "0.8.14"
- resolved "https://neo.jfrog.io/neo/api/npm/npm/zen-observable/-/zen-observable-0.8.14.tgz#d33058359d335bc0db1f0af66158b32872af3bf7"
- integrity sha1-0zBYNZ0zW8DbHwr2YVizKHKvO/c=