diff --git a/e2e_tests/integration/multi-db.spec.js b/e2e_tests/integration/multi-db.spec.js index dc0fda213cb..10f9c0ccb1b 100644 --- a/e2e_tests/integration/multi-db.spec.js +++ b/e2e_tests/integration/multi-db.spec.js @@ -212,5 +212,38 @@ describe('Multi database', () => { .first() resultFrame.should('contain', 'could not be found') }) + if (isEnterpriseEdition()) { + it('re-runs query from frame action button on original db', () => { + cy.executeCommand(':clear') + cy.executeCommand(':use neo4j') + cy.executeCommand(':clear') + cy.executeCommand('RETURN "Test string"') + cy.executeCommand(':use system') + + // Close first frame + cy.get('[title="Close"]', { timeout: 10000 }) + .first() + .click() + + // Make sure it's closed + cy.get('[data-testid="frame"]', { timeout: 10000 }).should( + 'have.length', + 1 + ) + + // Click re-run + cy.get('[data-testid="rerunFrameButton"]', { timeout: 10000 }) + .first() + .click() + + // Make sure we have what we expect + cy.get('[data-testid="frame"]', { timeout: 10000 }) + .first() + .should(frame => { + expect(frame).to.contain('"Test string"') + expect(frame).to.not.contain('ERROR') + }) + }) + } } }) diff --git a/src/browser/modules/DecoratedText/TextCommand.jsx b/src/browser/modules/DecoratedText/TextCommand.jsx index 8690620ad17..713faf8ccad 100644 --- a/src/browser/modules/DecoratedText/TextCommand.jsx +++ b/src/browser/modules/DecoratedText/TextCommand.jsx @@ -64,4 +64,7 @@ const mapDispatchToProps = dispatch => { } } -export default connect(mapStateToProps, mapDispatchToProps)(TextCommand) +export default connect( + mapStateToProps, + mapDispatchToProps +)(TextCommand) diff --git a/src/browser/modules/Frame/FrameTitlebar.jsx b/src/browser/modules/Frame/FrameTitlebar.jsx index f07c3d3f0d2..ecba1059c0d 100644 --- a/src/browser/modules/Frame/FrameTitlebar.jsx +++ b/src/browser/modules/Frame/FrameTitlebar.jsx @@ -252,9 +252,7 @@ class FrameTitlebar extends Component { - props.onReRunClick(frame.cmd, frame.id, frame.requestId) - } + onClick={() => props.onReRunClick(frame)} > @@ -295,11 +293,11 @@ const mapDispatchToProps = (dispatch, ownProps) => { } dispatch(remove(id)) }, - onReRunClick: (cmd, id, requestId) => { + onReRunClick: ({ cmd, useDb, id, requestId }) => { if (requestId) { dispatch(cancelRequest(requestId)) } - dispatch(commands.executeCommand(cmd, id)) + dispatch(commands.executeCommand(cmd, { id, useDb })) }, togglePinning: (id, isPinned) => { isPinned ? dispatch(unpin(id)) : dispatch(pin(id)) @@ -308,5 +306,8 @@ const mapDispatchToProps = (dispatch, ownProps) => { } export default withBus( - connect(mapStateToProps, mapDispatchToProps)(FrameTitlebar) + connect( + mapStateToProps, + mapDispatchToProps + )(FrameTitlebar) ) diff --git a/src/browser/modules/Stream/StyleFrame.jsx b/src/browser/modules/Stream/StyleFrame.jsx index 0eadf5a344f..fe28bdd5205 100644 --- a/src/browser/modules/Stream/StyleFrame.jsx +++ b/src/browser/modules/Stream/StyleFrame.jsx @@ -83,7 +83,9 @@ const StyleStatusbar = ({ resetStyleAction, rerunAction, onResetClick }) => { const mapStateToProps = (state, ownProps) => { return { resetStyleAction: executeSystemCommand(`${getCmdChar(state)}style reset`), - rerunAction: executeCommand(ownProps.frame.cmd, ownProps.frame.id) + rerunAction: executeCommand(ownProps.frame.cmd, { + id: ownProps.frame.id + }) } } const mapDispatchToProps = dispatch => ({ @@ -93,6 +95,9 @@ const mapDispatchToProps = dispatch => ({ } }) -const Statusbar = connect(mapStateToProps, mapDispatchToProps)(StyleStatusbar) +const Statusbar = connect( + mapStateToProps, + mapDispatchToProps +)(StyleStatusbar) export default StyleFrame diff --git a/src/shared/modules/commands/commandsDuck.js b/src/shared/modules/commands/commandsDuck.js index 8ef579da812..4cad1fd847e 100644 --- a/src/shared/modules/commands/commandsDuck.js +++ b/src/shared/modules/commands/commandsDuck.js @@ -91,22 +91,27 @@ export default function reducer(state = initialState, action) { // Action creators -export const executeCommand = (cmd, id, requestId, parentId) => { +export const executeCommand = ( + cmd, + { id, requestId, parentId, useDb } = {} +) => { return { type: COMMAND_QUEUED, cmd, id, requestId, - parentId + parentId, + useDb } } -export const executeSingleCommand = (cmd, id, requestId) => { +export const executeSingleCommand = (cmd, { id, requestId, useDb } = {}) => { return { type: SINGLE_COMMAND_QUEUED, cmd, id, - requestId + requestId, + useDb } } @@ -162,7 +167,11 @@ export const handleCommandEpic = (action$, store) => if (statements.length === 1) { // Single command return store.dispatch( - executeSingleCommand(statements[0], action.id, action.requestId) + executeSingleCommand(statements[0], { + id: action.id, + requestId: action.requestId, + useDb: action.useDb + }) ) } const parentId = action.parentId || v4() diff --git a/src/shared/modules/commands/commandsDuck.test.js b/src/shared/modules/commands/commandsDuck.test.js index b88e8cd3c83..687089f423c 100644 --- a/src/shared/modules/commands/commandsDuck.test.js +++ b/src/shared/modules/commands/commandsDuck.test.js @@ -91,7 +91,10 @@ describe('commandsDuck', () => { const cmd = 'RETURN 1' const id = 2 const requestId = 'xxx' - const action = commands.executeSingleCommand(cmd, id, requestId) + const action = commands.executeSingleCommand(cmd, { + id, + requestId + }) bus.take('NOOP', currentAction => { // Then expect(store.getActions()).toEqual([ @@ -138,7 +141,9 @@ describe('commandsDuck', () => { const cmd = store.getState().settings.cmdchar + 'param' const cmdString = cmd + ' x: 2' const id = 1 - const action = commands.executeSingleCommand(cmdString, id) + const action = commands.executeSingleCommand(cmdString, { + id + }) bus.take('NOOP', currentAction => { // Then @@ -172,7 +177,9 @@ describe('commandsDuck', () => { const cmd = store.getState().settings.cmdchar + 'param' const cmdString = cmd + ' x => 2' const id = 1 - const action = commands.executeSingleCommand(cmdString, id) + const action = commands.executeSingleCommand(cmdString, { + id + }) bolt.routedWriteTransaction = jest.fn(() => Promise.resolve({ records: [{ get: () => 2 }] @@ -205,7 +212,9 @@ describe('commandsDuck', () => { const cmd = store.getState().settings.cmdchar + 'params' const cmdString = cmd + ' {x: 2, y: 3}' const id = 1 - const action = commands.executeSingleCommand(cmdString, id) + const action = commands.executeSingleCommand(cmdString, { + id + }) bus.take('NOOP', currentAction => { // Then expect(store.getActions()).toEqual([ @@ -232,7 +241,9 @@ describe('commandsDuck', () => { // Given const cmdString = store.getState().settings.cmdchar + 'params' const id = 1 - const action = commands.executeSingleCommand(cmdString, id) + const action = commands.executeSingleCommand(cmdString, { + id + }) bus.take('NOOP', currentAction => { // Then expect(store.getActions()).toEqual([ @@ -254,7 +265,9 @@ describe('commandsDuck', () => { const cmd = store.getState().settings.cmdchar + 'config' const cmdString = cmd + ' "x": 2' const id = 1 - const action = commands.executeSingleCommand(cmdString, id) + const action = commands.executeSingleCommand(cmdString, { + id + }) bus.take('NOOP', currentAction => { // Then expect(store.getActions()).toEqual([ @@ -281,7 +294,9 @@ describe('commandsDuck', () => { const cmd = store.getState().settings.cmdchar + 'config' const cmdString = cmd + ' {"x": 2, "y":3}' const id = 1 - const action = commands.executeSingleCommand(cmdString, id) + const action = commands.executeSingleCommand(cmdString, { + id + }) bus.take('NOOP', currentAction => { // Then expect(store.getActions()).toEqual([ @@ -309,7 +324,9 @@ describe('commandsDuck', () => { const cmd = store.getState().settings.cmdchar + 'config' const cmdString = cmd const id = 1 - const action = commands.executeSingleCommand(cmdString, id) + const action = commands.executeSingleCommand(cmdString, { + id + }) bus.take('NOOP', currentAction => { // Then expect(store.getActions()).toEqual([ @@ -336,7 +353,9 @@ describe('commandsDuck', () => { const cmd = store.getState().settings.cmdchar + 'style' const cmdString = cmd const id = 1 - const action = commands.executeSingleCommand(cmdString, id) + const action = commands.executeSingleCommand(cmdString, { + id + }) bus.take('NOOP', currentAction => { // Then expect(store.getActions()).toEqual([ @@ -361,7 +380,7 @@ describe('commandsDuck', () => { test('does the right thing for list queries', done => { const cmd = store.getState().settings.cmdchar + 'queries' const id = 1 - const action = commands.executeSingleCommand(cmd, id) + const action = commands.executeSingleCommand(cmd, { id }) bus.take('NOOP', currentAction => { expect(store.getActions()).toEqual([ @@ -385,7 +404,10 @@ describe('commandsDuck', () => { const cmd = comment + '\n' + actualCommand const id = 2 const requestId = 'xxx' - const action = commands.executeSingleCommand(cmd, id, requestId) + const action = commands.executeSingleCommand(cmd, { + id, + requestId + }) bus.take('NOOP', currentAction => { // Then expect(store.getActions()).toEqual([ @@ -413,7 +435,7 @@ describe('commandsDuck', () => { const cmdString = 'history' const cmd = comment + '\n' + store.getState().settings.cmdchar + cmdString const id = 1 - const action = commands.executeSingleCommand(cmd, id) + const action = commands.executeSingleCommand(cmd, { id }) const cmdChar = store.getState().settings.cmdchar bus.take('NOOP', currentAction => { @@ -441,7 +463,7 @@ describe('commandsDuck', () => { const serverCmd = 'disconnect' const cmd = store.getState().settings.cmdchar + 'server ' + serverCmd const id = 3 - const action = commands.executeSingleCommand(cmd, id) + const action = commands.executeSingleCommand(cmd, { id }) bus.take('NOOP', currentAction => { // Then expect(store.getActions()).toEqual([ diff --git a/src/shared/modules/commands/cypher.test.js b/src/shared/modules/commands/cypher.test.js index 2cfe9c544a7..d027f8d0093 100644 --- a/src/shared/modules/commands/cypher.test.js +++ b/src/shared/modules/commands/cypher.test.js @@ -76,7 +76,10 @@ describe('tx metadata with cypher', () => { const bus = createBus() bus.applyReduxMiddleware(createEpicMiddleware(handleSingleCommandEpic)) const $$responseChannel = 'test-channel' - const action = executeSingleCommand('RETURN 1', 'id', 'rqid') + const action = executeSingleCommand('RETURN 1', { + id: 'id', + requestId: 'rqid' + }) action.$$responseChannel = $$responseChannel bus.send(action.type, action) diff --git a/src/shared/modules/commands/helpers/cypher.js b/src/shared/modules/commands/helpers/cypher.js index 6dcf8d33551..bd7261942f7 100644 --- a/src/shared/modules/commands/helpers/cypher.js +++ b/src/shared/modules/commands/helpers/cypher.js @@ -42,7 +42,8 @@ export const handleCypherCommand = ( requestId: action.requestId, cancelable: true, ...txMetadata, - autoCommit + autoCommit, + useDb: action.useDb } ) put(send('cypher', id)) diff --git a/src/shared/modules/commands/multiCommands.test.js b/src/shared/modules/commands/multiCommands.test.js index ebaa5b7a8da..7fc77a0b535 100644 --- a/src/shared/modules/commands/multiCommands.test.js +++ b/src/shared/modules/commands/multiCommands.test.js @@ -69,14 +69,14 @@ describe('handleCommandEpic', () => { const cmd = 'RETURN 1' const id = 2 const requestId = 'xxx' - const action = commands.executeCommand(cmd, id, requestId) + const action = commands.executeCommand(cmd, { id, requestId }) bus.take('NOOP', currentAction => { // Then expect(store.getActions()).toEqual([ action, commands.clearErrorMessage(), addHistory(action.cmd, maxHistory), - commands.executeSingleCommand(cmd, id, requestId), + commands.executeSingleCommand(cmd, { id, requestId }), { type: 'NOOP' } ]) done() @@ -94,7 +94,11 @@ describe('handleCommandEpic', () => { const id = 2 const requestId = 'xxx' const parentId = 'yyy' - const action = commands.executeCommand(cmd, id, requestId, parentId) + const action = commands.executeCommand(cmd, { + id, + requestId, + parentId + }) bus.take('NOOP', currentAction => { // Then diff --git a/src/shared/modules/commands/use-db-no-ww.test.js b/src/shared/modules/commands/use-db-no-ww.test.js new file mode 100644 index 00000000000..5853f8949fc --- /dev/null +++ b/src/shared/modules/commands/use-db-no-ww.test.js @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2002-2020 "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 { createEpicMiddleware } from 'redux-observable' +import { createBus } from 'suber' +import { flushPromises } from 'services/utils' +import { executeSingleCommand, handleSingleCommandEpic } from './commandsDuck' +import bolt from 'services/bolt/bolt' + +jest.mock('services/bolt/boltConnection', () => { + const orig = require.requireActual('services/bolt/boltConnection') + return { + ...orig, + routedWriteTransaction: jest.fn(() => [ + 'id', + Promise.resolve({ records: [] }) + ]) + } +}) +const boltConnection = require.requireMock('services/bolt/boltConnection') + +jest.mock('shared/modules/settings/settingsDuck', () => { + const orig = require.requireActual('shared/modules/settings/settingsDuck') + return { + ...orig, + getCmdChar: () => ':', + shouldUseCypherThread: () => false + } +}) +const settingsDuck = require.requireMock('shared/modules/settings/settingsDuck') + +jest.mock('shared/modules/params/paramsDuck', () => { + const orig = require.requireActual('shared/modules/params/paramsDuck') + return { + ...orig, + getParams: () => ({}) + } +}) + +jest.mock('shared/modules/dbMeta/dbMetaDuck', () => { + const orig = require.requireActual('shared/modules/dbMeta/dbMetaDuck') + return { + ...orig, + getVersion: () => '4.0.0' + } +}) + +describe('Specified target database', () => { + test('it uses the db in store if no specific db specified with the action', done => { + // Given + bolt.useDb('autoDb') // Fake setting the db + boltConnection.routedWriteTransaction.mockClear() + + const bus = createBus() + bus.applyReduxMiddleware(createEpicMiddleware(handleSingleCommandEpic)) + const $$responseChannel = 'test-channel' + const action = executeSingleCommand(`RETURN 1`) + action.$$responseChannel = $$responseChannel + + bus.send(action.type, action) + flushPromises().then(() => { + expect(boltConnection.routedWriteTransaction).toHaveBeenCalledTimes(1) + expect(boltConnection.routedWriteTransaction).toHaveBeenCalledWith( + 'RETURN 1', + {}, + expect.objectContaining({ useDb: 'autoDb' }) + ) + + done() + }) + }) + test('it uses the specified db if passed in with the action', done => { + // Given + bolt.useDb('autoDb') // Fake setting the db + boltConnection.routedWriteTransaction.mockClear() + + const bus = createBus() + bus.applyReduxMiddleware(createEpicMiddleware(handleSingleCommandEpic)) + const $$responseChannel = 'test-channel' + const action = executeSingleCommand(`RETURN 1`, { useDb: 'manualDb' }) // <-- specify db + action.$$responseChannel = $$responseChannel + + bus.send(action.type, action) + flushPromises().then(() => { + expect(boltConnection.routedWriteTransaction).toHaveBeenCalledTimes(1) + expect(boltConnection.routedWriteTransaction).toHaveBeenCalledWith( + 'RETURN 1', + {}, + expect.objectContaining({ useDb: 'manualDb' }) + ) + + done() + }) + }) +}) diff --git a/src/shared/modules/commands/use-db-ww.test.js b/src/shared/modules/commands/use-db-ww.test.js new file mode 100644 index 00000000000..99acc82e44a --- /dev/null +++ b/src/shared/modules/commands/use-db-ww.test.js @@ -0,0 +1,115 @@ +/* + * Copyright (c) 2002-2020 "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 { createEpicMiddleware } from 'redux-observable' +import { createBus } from 'suber' +import { flushPromises } from 'services/utils' +import { executeSingleCommand, handleSingleCommandEpic } from './commandsDuck' +import bolt from 'services/bolt/bolt' + +jest.mock('services/bolt/setup-bolt-worker', () => { + const orig = require.requireActual('services/bolt/setup-bolt-worker') + return { + ...orig, + setupBoltWorker: jest.fn(() => Promise.resolve({ records: [] })) + } +}) +const setupWorkerModule = require.requireMock('services/bolt/setup-bolt-worker') + +jest.mock('shared/modules/settings/settingsDuck', () => { + const orig = require.requireActual('shared/modules/settings/settingsDuck') + return { + ...orig, + getCmdChar: () => ':', + shouldUseCypherThread: () => true + } +}) + +jest.mock('shared/modules/params/paramsDuck', () => { + const orig = require.requireActual('shared/modules/params/paramsDuck') + return { + ...orig, + getParams: () => ({}) + } +}) + +jest.mock('shared/modules/dbMeta/dbMetaDuck', () => { + const orig = require.requireActual('shared/modules/dbMeta/dbMetaDuck') + return { + ...orig, + getVersion: () => '4.0.0' + } +}) + +describe('Specified target database, using web workers', () => { + beforeAll(() => { + // Fake window worker object + window.Worker = true + bolt.useDb('autoDb') // Fake setting the db + }) + test('it uses the db in store if no specific db specified with the action', done => { + // Given + setupWorkerModule.setupBoltWorker.mockClear() + const bus = createBus() + bus.applyReduxMiddleware(createEpicMiddleware(handleSingleCommandEpic)) + const $$responseChannel = 'test-channel' + const action = executeSingleCommand(`RETURN 1`) + action.$$responseChannel = $$responseChannel + + bus.send(action.type, action) + flushPromises().then(() => { + expect(setupWorkerModule.setupBoltWorker).toHaveBeenCalledTimes(1) + expect(setupWorkerModule.setupBoltWorker).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + expect.objectContaining({ + connectionProperties: expect.objectContaining({ useDb: 'autoDb' }) + }), + expect.anything() + ) + + done() + }) + }) + test('it uses the specified db if passed in with the action', done => { + // Given + setupWorkerModule.setupBoltWorker.mockClear() + const bus = createBus() + bus.applyReduxMiddleware(createEpicMiddleware(handleSingleCommandEpic)) + const $$responseChannel = 'test-channel' + const action = executeSingleCommand(`RETURN 1`, { useDb: 'manualDb' }) // <-- specify db + action.$$responseChannel = $$responseChannel + + bus.send(action.type, action) + flushPromises().then(() => { + expect(setupWorkerModule.setupBoltWorker).toHaveBeenCalledTimes(1) + expect(setupWorkerModule.setupBoltWorker).toHaveBeenCalledWith( + expect.anything(), + expect.anything(), + expect.objectContaining({ + connectionProperties: expect.objectContaining({ useDb: 'manualDb' }) + }), + expect.anything() + ) + + done() + }) + }) +}) diff --git a/src/shared/services/bolt/bolt.js b/src/shared/services/bolt/bolt.js index 0bddaeeabca..27c2a29504a 100644 --- a/src/shared/services/bolt/bolt.js +++ b/src/shared/services/bolt/bolt.js @@ -27,13 +27,11 @@ import { generateBoltHost } from 'services/utils' import { runCypherMessage, cancelTransactionMessage, - closeConnectionMessage, - CYPHER_ERROR_MESSAGE, - CYPHER_RESPONSE_MESSAGE, - POST_CANCEL_TRANSACTION_MESSAGE, - BOLT_CONNECTION_ERROR_MESSAGE + closeConnectionMessage } from './boltWorkerMessages' import { NATIVE } from 'services/bolt/boltHelpers' +import { setupBoltWorker, addTypesAsField } from './setup-bolt-worker' + import BoltWorkerModule from 'worker-loader?inline!./boltWorker.js' let connectionProperties = null @@ -78,7 +76,8 @@ function routedWriteTransaction(input, parameters, requestMetaData = {}) { cancelable = false, onLostConnection = () => {}, txMetadata = undefined, - autoCommit = false + autoCommit = false, + useDb = null } = requestMetaData if (useCypherThread && window.Worker) { const id = requestId || v4() @@ -96,22 +95,25 @@ function routedWriteTransaction(input, parameters, requestMetaData = {}) { ) ), txMetadata, - useDb: _useDb, + useDb: useDb || _useDb, autoCommit } ) - const workerPromise = setupBoltWorker(id, workFn, onLostConnection) + const workerPromise = setupBoltWorker( + boltWorkPool, + id, + workFn, + onLostConnection + ) return [id, workerPromise] } else { - return boltConnection.routedWriteTransaction( - input, - parameters, + return boltConnection.routedWriteTransaction(input, parameters, { requestId, cancelable, txMetadata, - _useDb, + useDb: useDb || _useDb, autoCommit - ) + }) } } @@ -121,7 +123,8 @@ function routedReadTransaction(input, parameters, requestMetaData = {}) { requestId = null, cancelable = false, onLostConnection = () => {}, - txMetadata = undefined + txMetadata = undefined, + useDb = null } = requestMetaData if (useCypherThread && window.Worker) { const id = requestId || v4() @@ -139,20 +142,23 @@ function routedReadTransaction(input, parameters, requestMetaData = {}) { ) ), txMetadata, - useDb: _useDb + useDb: useDb || _useDb } ) - const workerPromise = setupBoltWorker(id, workFn, onLostConnection) + const workerPromise = setupBoltWorker( + boltWorkPool, + id, + workFn, + onLostConnection + ) return workerPromise } else { - return boltConnection.routedReadTransaction( - input, - parameters, + return boltConnection.routedReadTransaction(input, parameters, { requestId, cancelable, txMetadata, - _useDb - ) + useDb: useDb || _useDb + }) } } @@ -163,7 +169,7 @@ function directTransaction(input, parameters, requestMetaData = {}) { cancelable = false, onLostConnection = () => {}, txMetadata = undefined, - useDb + useDb = null } = requestMetaData if (useCypherThread && window.Worker) { const id = requestId || v4() @@ -181,63 +187,24 @@ function directTransaction(input, parameters, requestMetaData = {}) { ) ), txMetadata, - useDb: useDb !== undefined ? useDb : _useDb + useDb: useDb || _useDb } ) - const workerPromise = setupBoltWorker(id, workFn, onLostConnection) + const workerPromise = setupBoltWorker( + boltWorkPool, + id, + workFn, + onLostConnection + ) return workerPromise } else { - return boltConnection.directTransaction( - input, - parameters, + return boltConnection.directTransaction(input, parameters, { requestId, cancelable, txMetadata, - _useDb - ) - } -} - -const addTypesAsField = result => { - const records = result.records.map(record => { - const typedRecord = new neo4j.types.Record( - record.keys, - record._fields, - record._fieldLookup - ) - if (typedRecord._fields) { - typedRecord._fields = mappings.applyGraphTypes(typedRecord._fields) - } - return typedRecord - }) - const summary = mappings.applyGraphTypes(result.summary) - return { summary, records } -} - -function setupBoltWorker(id, workFn, onLostConnection = () => {}) { - const workerPromise = new Promise((resolve, reject) => { - const work = boltWorkPool.doWork({ - id, - payload: workFn, - onmessage: msg => { - if (msg.data.type === BOLT_CONNECTION_ERROR_MESSAGE) { - work.finish() - onLostConnection(msg.data.error) - return reject(msg.data.error) - } - if (msg.data.type === CYPHER_ERROR_MESSAGE) { - work.finish() - reject(msg.data.error) - } else if (msg.data.type === CYPHER_RESPONSE_MESSAGE) { - work.finish() - resolve(addTypesAsField(msg.data.result)) - } else if (msg.data.type === POST_CANCEL_TRANSACTION_MESSAGE) { - work.finish() - } - } + useDb: useDb || _useDb }) - }) - return workerPromise + } } const closeConnectionInWorkers = () => { diff --git a/src/shared/services/bolt/boltConnection.js b/src/shared/services/bolt/boltConnection.js index 9d315c6bce9..c75693c032e 100644 --- a/src/shared/services/bolt/boltConnection.js +++ b/src/shared/services/bolt/boltConnection.js @@ -289,14 +289,13 @@ export function cancelTransaction(id, cb) { } } -export function directTransaction( - input, - parameters, - requestId = null, - cancelable = false, - txMetadata = undefined, - useDb = undefined -) { +export function directTransaction(input, parameters, opts = {}) { + const { + requestId = null, + cancelable = false, + txMetadata = undefined, + useDb = undefined + } = opts const session = _drivers ? _drivers .getDirectDriver() @@ -306,14 +305,13 @@ export function directTransaction( return _trackedTransaction(input, parameters, session, requestId, txMetadata) } -export function routedReadTransaction( - input, - parameters, - requestId = null, - cancelable = false, - txMetadata = undefined, - useDb = undefined -) { +export function routedReadTransaction(input, parameters, opts = {}) { + const { + requestId = null, + cancelable = false, + txMetadata = undefined, + useDb = undefined + } = opts const session = _drivers ? _drivers .getRoutedDriver() @@ -323,15 +321,14 @@ export function routedReadTransaction( return _trackedTransaction(input, parameters, session, requestId, txMetadata) } -export function routedWriteTransaction( - input, - parameters, - requestId = null, - cancelable = false, - txMetadata = undefined, - useDb = undefined, - autoCommit = false -) { +export function routedWriteTransaction(input, parameters, opts = {}) { + const { + requestId = null, + cancelable = false, + txMetadata = undefined, + useDb = undefined, + autoCommit = false + } = opts const session = _drivers ? _drivers .getRoutedDriver() diff --git a/src/shared/services/bolt/boltWorker.js b/src/shared/services/bolt/boltWorker.js index 5d4bd8e9a63..1af26f054f0 100644 --- a/src/shared/services/bolt/boltWorker.js +++ b/src/shared/services/bolt/boltWorker.js @@ -80,11 +80,7 @@ const onmessage = function(message) { const res = connectionTypeMap[connectionType].create( input, applyGraphTypes(parameters), - requestId, - cancelable, - txMetadata, - useDb, - autoCommit + { requestId, cancelable, txMetadata, useDb, autoCommit } ) connectionTypeMap[connectionType] .getPromise(res) diff --git a/src/shared/services/bolt/setup-bolt-worker.js b/src/shared/services/bolt/setup-bolt-worker.js new file mode 100644 index 00000000000..ea5e825ad81 --- /dev/null +++ b/src/shared/services/bolt/setup-bolt-worker.js @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2002-2020 "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 { types } from 'neo4j-driver' +import { applyGraphTypes } from './boltMappings' +import { + CYPHER_ERROR_MESSAGE, + CYPHER_RESPONSE_MESSAGE, + POST_CANCEL_TRANSACTION_MESSAGE, + BOLT_CONNECTION_ERROR_MESSAGE +} from './boltWorkerMessages' + +export const setupBoltWorker = ( + boltWorkPool, + id, + workFn, + onLostConnection = () => {} +) => { + const workerPromise = new Promise((resolve, reject) => { + const work = boltWorkPool.doWork({ + id, + payload: workFn, + onmessage: msg => { + if (msg.data.type === BOLT_CONNECTION_ERROR_MESSAGE) { + work.finish() + onLostConnection(msg.data.error) + return reject(msg.data.error) + } + if (msg.data.type === CYPHER_ERROR_MESSAGE) { + work.finish() + reject(msg.data.error) + } else if (msg.data.type === CYPHER_RESPONSE_MESSAGE) { + work.finish() + resolve(addTypesAsField(msg.data.result)) + } else if (msg.data.type === POST_CANCEL_TRANSACTION_MESSAGE) { + work.finish() + } + } + }) + }) + return workerPromise +} + +export const addTypesAsField = result => { + const records = result.records.map(record => { + const typedRecord = new types.Record( + record.keys, + record._fields, + record._fieldLookup + ) + if (typedRecord._fields) { + typedRecord._fields = applyGraphTypes(typedRecord._fields) + } + return typedRecord + }) + const summary = applyGraphTypes(result.summary) + return { summary, records } +} diff --git a/src/shared/services/commandInterpreterHelper.js b/src/shared/services/commandInterpreterHelper.js index 64dffb51ce6..477bbac9c13 100644 --- a/src/shared/services/commandInterpreterHelper.js +++ b/src/shared/services/commandInterpreterHelper.js @@ -377,8 +377,8 @@ const availableCommands = [ put(cypher(action.cmd)) put( frames.add({ - useDb: getUseDb(store.getState()), ...action, + useDb: action.useDb || getUseDb(store.getState()), type: 'cypher', requestId: id }) diff --git a/src/shared/services/commandUtils.js b/src/shared/services/commandUtils.js index 233a90dfa1e..b419d070c57 100644 --- a/src/shared/services/commandUtils.js +++ b/src/shared/services/commandUtils.js @@ -63,7 +63,7 @@ export const buildCommandObject = (action, interpret, cmdchar) => { cmdchar, action.ignore ) - return { action, interpreted, cmdchar } + return { action, interpreted, cmdchar, useDb: action.useDb } } export const getInterpreter = (interpret, cmd, cmdchar, ignore = false) => {