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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/vscode-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@
"category": "Neo4j",
"icon": "$(trash)"
},
{
"command": "neo4j.deleteAllConnections",
"title": "Delete all connections",
"category": "Neo4j",
"icon": "$(trash)"
},
{
"command": "neo4j.refreshConnections",
"title": "Refresh connections",
Expand Down Expand Up @@ -206,6 +212,11 @@
"when": "view == neo4jConnections",
"group": "navigation"
},
{
"command": "neo4j.deleteAllConnections",
"when": "view == neo4jConnections",
"group": "navigation"
},
{
"command": "neo4j.clearParameters",
"when": "view == neo4jParameters",
Expand Down
28 changes: 28 additions & 0 deletions packages/vscode-extension/src/commandHandlers/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
Connection,
deleteConnectionAndUpdateDatabaseConnection,
getActiveConnection,
getAllConnections,
getConnectionByKey,
getConnections,
getPasswordForConnection,
Expand All @@ -15,6 +16,7 @@ import { CONSTANTS } from '../constants';
import { getExtensionContext, getQueryRunner } from '../contextService';
import { ConnectionItem } from '../treeviews/connectionTreeDataProvider';
import {
displayConfirmAllConnectionsDeletionPrompt,
displayConfirmConnectionDeletionPrompt,
displayMessageForConnectionResult,
displayMessageForSwitchDatabaseResult,
Expand Down Expand Up @@ -119,6 +121,32 @@ export async function promptUserToDeleteConnectionAndDisplayConnectionResult(
}
}

/**
* Handler for DELETE_ALL_CONNECTION_COMMAND (neo4j.deleteAllConnections)
* This can be triggered from the Connection tree view and from the Command Palette
* Deletes all connections
* @param connectionItem The ConnectionItem to delete.
* @returns A promise that resolves when the handler has completed.
*/
export async function promptUserToDeleteAllConnectionsAndDisplayConnectionResult(): Promise<void> {
const result = await displayConfirmAllConnectionsDeletionPrompt();

if (result === 'Yes') {
const connections = getAllConnections();
await Promise.allSettled(
connections.map((connection) =>
deleteConnectionAndUpdateDatabaseConnection(connection.key),
),
);
void commands.executeCommand(
CONSTANTS.COMMANDS.REFRESH_CONNECTIONS_COMMAND,
);
void window.showInformationMessage(
CONSTANTS.MESSAGES.ALL_CONNECTIONS_DELETED,
);
}
}

/**
* Handler for CONNECT_COMMAND and DISCONNECT_COMMAND (neo4j.connect and neo4j.disconnect)
* This may only be triggered from the Connection tree view.
Expand Down
2 changes: 2 additions & 0 deletions packages/vscode-extension/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export const CONSTANTS = {
EDIT_CONNECTION_COMMAND: 'neo4j.editConnection',
DELETE_CONNECTION_COMMAND: 'neo4j.deleteConnection',
REFRESH_CONNECTIONS_COMMAND: 'neo4j.refreshConnections',
DELETE_ALL_CONNECTIONS_COMMAND: 'neo4j.deleteAllConnections',
CONNECT_COMMAND: 'neo4j.connect',
DISCONNECT_COMMAND: 'neo4j.disconnect',
SWITCH_DATABASE_COMMAND: 'neo4j.switchDatabase',
Expand All @@ -32,6 +33,7 @@ export const CONSTANTS = {
RECONNECTED_MESSAGE: 'Reconnected to Neo4j.',
CONNECTION_SAVED: 'Neo4j connection saved.',
CONNECTION_DELETED: 'Neo4j connection deleted.',
ALL_CONNECTIONS_DELETED: 'All Neo4j connections have been deleted.',
CONNECTION_VALIDATION_MESSAGE: 'Please fill in all required fields.',
SUCCESSFULLY_SWITCHED_DATABASE_MESSAGE: 'Switched to database',
ERROR_SWITCHING_DATABASE_MESSAGE: 'Error switching to database',
Expand Down
5 changes: 5 additions & 0 deletions packages/vscode-extension/src/registrationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
cypherFileFromSelection,
forceConnect,
forceDisconnect,
promptUserToDeleteAllConnectionsAndDisplayConnectionResult,
promptUserToDeleteConnectionAndDisplayConnectionResult,
runCypher,
saveConnectionAndDisplayConnectionResult,
Expand Down Expand Up @@ -98,6 +99,10 @@ export function registerDisposables(): Disposable[] {
CONSTANTS.COMMANDS.DELETE_CONNECTION_COMMAND,
promptUserToDeleteConnectionAndDisplayConnectionResult,
),
commands.registerCommand(
CONSTANTS.COMMANDS.DELETE_ALL_CONNECTIONS_COMMAND,
promptUserToDeleteAllConnectionsAndDisplayConnectionResult,
),
commands.registerCommand(
CONSTANTS.COMMANDS.CONNECT_COMMAND,
(connectionItem: ConnectionItem) =>
Expand Down
10 changes: 10 additions & 0 deletions packages/vscode-extension/src/uiUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ export async function displayConfirmConnectionDeletionPrompt(
);
}

export async function displayConfirmAllConnectionsDeletionPrompt(): Promise<
string | null
> {
return await window.showWarningMessage<string>(
`Are you sure you want to delete all connections?`,
{ modal: true },
'Yes',
);
}

/**
* Utility function to display a message to the user based on the result of switching databases.
* @param database The database that was switched to.
Expand Down
2 changes: 1 addition & 1 deletion packages/vscode-extension/syntaxes/cypher.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@
"name": "keyword.operator"
},
{
"match": "(?i)\\b(ACCESS|ACTIVE|ADD|ADMIN|ADMINISTRATOR|ALIAS|ALIASES|ALL|ALLREDUCE|allShortestPaths|ALTER|AND|ANY|ARRAY|AS|ASC|ASCENDING|ASSIGN|AT|AUTH|BINDINGS|BOOL|BOOLEAN|BOOSTED|BOTH|BREAK|BUILT|BY|CALL|CASCADE|CASE|CIDR|CHANGE|COLLECT|COMMAND|COMMANDS|COMPOSITE|CONSTRAINT|CONSTRAINTS|CONTAINS|CONTINUE|COPY|COUNT|CREATE|CSV|CONCURRENT|CURRENT|DATA|DATABASE|DATABASES|DATE|DATETIME|DBMS|DEALLOCATE|DEFAULT|DEFINED|DELETE|DENY|DESC|DESCENDING|DESTROY|DETACH|DIFFERENT|DISTINCT|DRIVER|DROP|DRYRUN|DUMP|DURATION|EACH|EDGE|ELEMENT|ELEMENTS|ELSE|ENABLE|ENCRYPTED|END|ENDS|ERROR|EXECUTABLE|EXECUTE|EXIST|EXISTENCE|EXISTS|EXTENDED_IDENTIFIER|FAIL|FALSE|FIELDTERMINATOR|FILTER|FINISH|FLOAT|FLOAT32|FLOAT64|FOR|FOREACH|FROM|FULLTEXT|FUNCTION|FUNCTIONS|GRANT|GRAPH|GRAPHS|GROUP|GROUPS|HEADERS|HOME|ID|IF|IMMUTABLE|IMPERSONATE|IMPLIES|IN|INDEX|INDEXES|INF|INFINITY|INSERT|INT|INT8|INT16|INT32|INT64|INTEGER|INTEGER8|INTEGER16|INTEGER32|INTEGER64|IS|JOIN|KEY|LABEL|LABELS|LANGUAGE|LEADING|LET|LIMIT|LIST|LOAD|LOCAL|LOOKUP|MANAGEMENT|MAP|MATCH|MERGE|NAME|NAMES|NAN|NEW|NEXT|NFC|NFD|NFKC|NFKD|NODE|NODETACH|NODES|NONE|NORMALIZE|NORMALIZED|NOT|NOTHING|NOWAIT|NULL|OF|OFFSET|ON|ONLY|OPTION|OPTIONAL|OPTIONS|OR|ORDER|PASSWORD|PASSWORDS|PATH|PATHS|PLAINTEXT|POINT|POPULATED|PRIMARY|PRIMARIES|PRIVILEGE|PRIVILEGES|PROCEDURE|PROCEDURES|PROPERTIES|PROPERTY|PROVIDER|PROVIDERS|RANGE|READ|REALLOCATE|REDUCE|REL|RELATIONSHIP|RELATIONSHIPS|REMOVE|RENAME|REPEATABLE|REPLACE|REPLICA|REPLICAS|REPORT|REQUIRE|REQUIRED|RESTRICT|RETRY|RETURN|REVOKE|ROLE|ROLES|ROW|ROWS|SCAN|SECONDARY|SECONDARIES|SEC|SECOND|SECONDS|SEEK|SERVER|SERVERS|SET|SETTING|SETTINGS|SHARD|SHARDS|SHORTEST|shortestPath|SHOW|SIGNED|SINGLE|SKIP|START|STARTS|STATUS|STOP|VARCHAR|STRING|SUPPORTED|SUSPENDED|TARGET|TERMINATE|TEXT|THEN|TIME|TIMESTAMP|TIMEZONE|TO|TOPOLOGY|TRAILING|TRANSACTION|TRANSACTIONS|TRAVERSE|TRIM|TRUE|TYPE|TYPED|TYPES|UNION|UNIQUE|UNIQUENESS|UNWIND|URL|USE|USER|USERS|USING|VALUE|VECTOR|VERTEX|WAIT|WHEN|WHERE|WITH|WITHOUT|WRITE|XOR|YIELD|ZONE|ZONED|EXPLAIN|PROFILE|CYPHER)\\b",
"match": "(?i)\\b(ACCESS|ACTIVE|ADD|ADMIN|ADMINISTRATOR|ALIAS|ALIASES|ALL|ALLREDUCE|allShortestPaths|ALTER|AND|ANY|ARRAY|AS|ASC|ASCENDING|ASSIGN|AT|AUTH|BINDINGS|BOOL|BOOLEAN|BOOSTED|BOTH|BREAK|BUILT|BY|CALL|CASCADE|CASE|CIDR|CHANGE|COLLECT|COMMAND|COMMANDS|COMPOSITE|CONSTRAINT|CONSTRAINTS|CONTAINS|CONTINUE|COPY|COSINE|COUNT|CREATE|CSV|CONCURRENT|CURRENT|DATA|DATABASE|DATABASES|DATE|DATETIME|DBMS|DEALLOCATE|DEFAULT|DEFINED|DELETE|DENY|DESC|DESCENDING|DESTROY|DETACH|DIFFERENT|DISTINCT|DOT|DRIVER|DROP|DRYRUN|DUMP|DURATION|EACH|EDGE|ELEMENT|ELEMENTS|ELSE|ENABLE|ENCRYPTED|END|ENDS|ERROR|EUCLIDEAN|EUCLIDEAN_SQUARED|EXECUTABLE|EXECUTE|EXIST|EXISTENCE|EXISTS|EXTENDED_IDENTIFIER|FAIL|FALSE|FIELDTERMINATOR|FILTER|FINISH|FLOAT|FLOAT32|FLOAT64|FOR|FOREACH|FROM|FULLTEXT|FUNCTION|FUNCTIONS|GRANT|GRAPH|GRAPHS|GROUP|GROUPS|HAMMING|HEADERS|HOME|ID|IF|IMMUTABLE|IMPERSONATE|IMPLIES|IN|INDEX|INDEXES|INF|INFINITY|INSERT|INT|INT8|INT16|INT32|INT64|INTEGER|INTEGER8|INTEGER16|INTEGER32|INTEGER64|IS|JOIN|KEY|LABEL|LABELS|LANGUAGE|LEADING|LET|LIMIT|LIST|LOAD|LOCAL|LOOKUP|MANAGEMENT|MANHATTAN|MAP|MATCH|MERGE|NAME|NAMES|NAN|NEW|NEXT|NFC|NFD|NFKC|NFKD|NODE|NODETACH|NODES|NONE|NORMALIZE|NORMALIZED|NOT|NOTHING|NOWAIT|NULL|OF|OFFSET|ON|ONLY|OPTION|OPTIONAL|OPTIONS|OR|ORDER|PASSWORD|PASSWORDS|PATH|PATHS|PLAINTEXT|POINT|POPULATED|PRIMARY|PRIMARIES|PRIVILEGE|PRIVILEGES|PROCEDURE|PROCEDURES|PROPERTIES|PROPERTY|PROVIDER|PROVIDERS|RANGE|READ|REALLOCATE|REDUCE|REL|RELATIONSHIP|RELATIONSHIPS|REMOVE|RENAME|REPEATABLE|REPLACE|REPLICA|REPLICAS|REPORT|REQUIRE|REQUIRED|RESTRICT|RETRY|RETURN|REVOKE|ROLE|ROLES|ROW|ROWS|SCAN|SECONDARY|SECONDARIES|SEC|SECOND|SECONDS|SEEK|SERVER|SERVERS|SET|SETTING|SETTINGS|SHARD|SHARDS|SHORTEST|shortestPath|SHOW|SIGNED|SINGLE|SKIP|START|STARTS|STATUS|STOP|VARCHAR|STRING|SUPPORTED|SUSPENDED|TARGET|TERMINATE|TEXT|THEN|TIME|TIMESTAMP|TIMEZONE|TO|TOPOLOGY|TRAILING|TRANSACTION|TRANSACTIONS|TRAVERSE|TRIM|TRUE|TYPE|TYPED|TYPES|UNION|UNIQUE|UNIQUENESS|UNWIND|URL|USE|USER|USERS|USING|VALUE|VECTOR|VECTOR_DISTANCE|VECTOR_NORM|VERTEX|WAIT|WHEN|WHERE|WITH|WITHOUT|WRITE|XOR|YIELD|ZONE|ZONED|EXPLAIN|PROFILE|CYPHER)\\b",
"name": "keyword"
}
]
Expand Down
166 changes: 164 additions & 2 deletions packages/vscode-extension/tests/specs/unit/executeCommands.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ import { after, afterEach, beforeEach } from 'mocha';
import * as sinon from 'sinon';
import { commands, MessageOptions, window } from 'vscode';
import { CONSTANTS } from '../../../src/constants';
import { getNeo4j2025Configuration } from '../../helpers';
import {
getNeo4j2025Configuration,
getNeo4j5Configuration,
} from '../../helpers';
import {
connectDefault,
neo4j2025ConnectionKey,
saveDefaultConnection,
} from '../../suiteSetup';

suite('Execute commands spec', () => {
suite.only('Execute commands spec', () => {
let sandbox: sinon.SinonSandbox;
let showInformationMessageStub: sinon.SinonStub;
let showErrorMessageStub: sinon.SinonStub;
Expand Down Expand Up @@ -459,4 +462,163 @@ suite('Execute commands spec', () => {
sandbox.assert.notCalled(showErrorMessageStub);
});
});

suite('deleteAllConnectionsCommand', () => {
const connection2025 = {
name: 'deleteAllConnectionsCommand 2025',
key: 'deleteAllConnectionsCommand 2025',
scheme: getNeo4j2025Configuration().scheme,
host: getNeo4j2025Configuration().host,
port: getNeo4j2025Configuration().port,
user: getNeo4j2025Configuration().user,
database: getNeo4j2025Configuration().database,
state: 'inactive',
};
const connection2025Password = getNeo4j2025Configuration().password;

const connection5 = {
name: 'deleteAllConnectionsCommand 5',
key: 'deleteAllConnectionsCommand 5',
scheme: getNeo4j5Configuration().scheme,
host: getNeo4j5Configuration().host,
port: getNeo4j5Configuration().port,
user: getNeo4j5Configuration().user,
database: getNeo4j5Configuration().database,
state: 'inactive',
};

const connection5Password = getNeo4j5Configuration().password;

test('Deleting all connections should show a success message', async () => {
await commands.executeCommand(
CONSTANTS.COMMANDS.SAVE_CONNECTION_COMMAND,
connection2025,
connection2025Password,
);

await commands.executeCommand(
CONSTANTS.COMMANDS.SAVE_CONNECTION_COMMAND,
connection5,
connection5Password,
);

const stub = sandbox.stub(
window,
'showWarningMessage',
) as unknown as sinon.SinonStub<
[string, MessageOptions, ...string[]],
Thenable<string>
>;

stub
.withArgs(sinon.match.string, sinon.match.object, sinon.match.string)
.resolves('Yes');

await commands.executeCommand(
CONSTANTS.COMMANDS.DELETE_ALL_CONNECTIONS_COMMAND,
);

sandbox.assert.calledWith(
showInformationMessageStub,
CONSTANTS.MESSAGES.ALL_CONNECTIONS_DELETED,
);
});

test('Deleting all connections should disconnect active connections', async () => {
await commands.executeCommand(
CONSTANTS.COMMANDS.SAVE_CONNECTION_COMMAND,
connection2025,
connection2025Password,
);

await commands.executeCommand(
CONSTANTS.COMMANDS.SAVE_CONNECTION_COMMAND,
connection5,
connection5Password,
);

const stub = sandbox.stub(
window,
'showWarningMessage',
) as unknown as sinon.SinonStub<
[string, MessageOptions, ...string[]],
Thenable<string>
>;

stub
.withArgs(sinon.match.string, sinon.match.object, sinon.match.string)
.resolves('Yes');

await commands.executeCommand(
CONSTANTS.COMMANDS.DELETE_ALL_CONNECTIONS_COMMAND,
);

sandbox.assert.calledWith(
showInformationMessageStub,
CONSTANTS.MESSAGES.ALL_CONNECTIONS_DELETED,
);

sandbox.assert.calledWith(
showInformationMessageStub,
CONSTANTS.MESSAGES.DISCONNECTED_MESSAGE,
);
});

test('Dismissing delete all connections prompt should not show any messages', async () => {
await commands.executeCommand(
CONSTANTS.COMMANDS.SAVE_CONNECTION_COMMAND,
connection2025,
connection2025Password,
);

await commands.executeCommand(
CONSTANTS.COMMANDS.SAVE_CONNECTION_COMMAND,
connection5,
connection5Password,
);

sandbox.stub(window, 'showWarningMessage').resolves(undefined);
showInformationMessageStub.reset();

await commands.executeCommand(
CONSTANTS.COMMANDS.DELETE_ALL_CONNECTIONS_COMMAND,
);

sandbox.assert.notCalled(showInformationMessageStub);
});

test('Any other response from delete all connections prompt should not show any messages', async () => {
await commands.executeCommand(
CONSTANTS.COMMANDS.SAVE_CONNECTION_COMMAND,
connection2025,
connection2025Password,
);

await commands.executeCommand(
CONSTANTS.COMMANDS.SAVE_CONNECTION_COMMAND,
connection5,
connection5Password,
);

const stub = sandbox.stub(
window,
'showWarningMessage',
) as unknown as sinon.SinonStub<
[string, MessageOptions, ...string[]],
Thenable<string>
>;

stub
.withArgs(sinon.match.string, sinon.match.object, sinon.match.string)
.resolves('No');

showInformationMessageStub.reset();

await commands.executeCommand(
CONSTANTS.COMMANDS.DELETE_ALL_CONNECTIONS_COMMAND,
);

sandbox.assert.notCalled(showInformationMessageStub);
});
});
});