Skip to content

Commit

Permalink
feat: Provide ConnectionDetails without retrieving the whole chain …
Browse files Browse the repository at this point in the history
…of applied migrations.
  • Loading branch information
michael-simons committed Feb 17, 2022
1 parent 085cb4f commit 5814489
Show file tree
Hide file tree
Showing 26 changed files with 312 additions and 133 deletions.
2 changes: 1 addition & 1 deletion neo4j-migrations-cli/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>eu.michael-simons.neo4j</groupId>
<artifactId>neo4j-migrations-parent</artifactId>
<version>1.3.4-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>

<artifactId>neo4j-migrations-cli</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion neo4j-migrations-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<parent>
<groupId>eu.michael-simons.neo4j</groupId>
<artifactId>neo4j-migrations-parent</artifactId>
<version>1.3.4-SNAPSHOT</version>
<version>1.4.0-SNAPSHOT</version>
</parent>

<artifactId>neo4j-migrations</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,8 @@
import java.util.Map;
import java.util.Optional;

import org.neo4j.driver.Record;
import org.neo4j.driver.Result;
import org.neo4j.driver.Session;
import org.neo4j.driver.internal.util.ServerVersion;
import org.neo4j.driver.summary.DatabaseInfo;
import org.neo4j.driver.summary.ResultSummary;
import org.neo4j.driver.summary.ServerInfo;
import org.neo4j.driver.types.IsoDuration;
import org.neo4j.driver.types.Node;
import org.neo4j.driver.types.Path;
Expand Down Expand Up @@ -81,55 +76,7 @@ MigrationChain buildChain(MigrationContext context, List<Migration> discoveredMi
MigrationChain buildChain(MigrationContext context, List<Migration> discoveredMigrations, boolean detailedCauses) {

final Map<MigrationVersion, Element> elements = buildChain0(context, discoveredMigrations, detailedCauses);

class ExtendedResultSummary {
final boolean showCurrentUserExists;
final ServerVersion version;
final ServerInfo server;
final DatabaseInfo database;

ExtendedResultSummary(boolean showCurrentUserExists, ServerVersion version, ResultSummary actualSummary) {
this.showCurrentUserExists = showCurrentUserExists;
this.version = version;
this.server = actualSummary.server();
this.database = actualSummary.database();
}
}

try (Session session = context.getSchemaSession()) {

// Auth maybe disabled. In such cases, we cannot get the current user.
ExtendedResultSummary databaseInformation = session.readTransaction(tx -> {
Result result = tx.run(""
+ "CALL dbms.procedures() YIELD name "
+ "WHERE name = 'dbms.showCurrentUser' "
+ "WITH count(*) > 0 AS showCurrentUserExists "
+ "CALL dbms.components() YIELD versions "
+ "RETURN showCurrentUserExists, 'Neo4j/' + versions[0] AS version"
);
Record singleResultRecord = result.single();
boolean showCurrentUserExists = singleResultRecord.get("showCurrentUserExists").asBoolean();
ServerVersion version = ServerVersion.version(singleResultRecord.get("version").asString());
ResultSummary summary = result.consume();
return new ExtendedResultSummary(showCurrentUserExists, version, summary);
});

String username = "anonymous";
if (databaseInformation.showCurrentUserExists) {

username = session.readTransaction(tx -> tx.run(""
+ "CALL dbms.procedures() YIELD name "
+ "WHERE name = 'dbms.showCurrentUser' "
+ "CALL dbms.showCurrentUser() YIELD username RETURN username"
).single().get("username").asString());
}

ServerInfo serverInfo = databaseInformation.server;
String schemaDatabase = databaseInformation.database == null ? null : databaseInformation.database.name();
String targetDatabase = context.getConfig().getMigrationTargetIn(context).orElse(schemaDatabase);
return new DefaultMigrationChain(serverInfo.address(), databaseInformation.version.toString(),
username, targetDatabase, schemaDatabase, elements);
}
return new DefaultMigrationChain(context.getConnectionDetails(), elements);
}

private Map<MigrationVersion, Element> buildChain0(MigrationContext context, List<Migration> discoveredMigrations, boolean detailedCauses) {
Expand Down Expand Up @@ -205,56 +152,44 @@ private Map<MigrationVersion, Element> getChainOfAppliedMigrations(MigrationCont

private static class DefaultMigrationChain implements MigrationChain {

private final String serverAdress;

private final String serverVersion;

private final String username;

private final String databaseName;

private final String schemaDatabaseName;
private final ConnectionDetails connectionDetailsDelegate;

private final Map<MigrationVersion, Element> elements;

DefaultMigrationChain(String serverAdress, String serverVersion, String username, String databaseName, String schemaDatabaseName,
Map<MigrationVersion, Element> elements) {
this.serverAdress = serverAdress;
this.serverVersion = serverVersion;
this.username = username;
this.databaseName = databaseName;
this.schemaDatabaseName = schemaDatabaseName;
DefaultMigrationChain(ConnectionDetails connectionDetailsDelegate, Map<MigrationVersion, Element> elements) {
this.connectionDetailsDelegate = connectionDetailsDelegate;
this.elements = elements;
}

@Override
public String getServerAddress() {
return serverAdress;
return connectionDetailsDelegate.getServerAddress();
}

@Override
public String getServerVersion() {
return serverVersion;
return connectionDetailsDelegate.getServerVersion();
}

@Override public String getUsername() {
return username;
}

@SuppressWarnings("deprecation")
@Override
public String getDatabaseName() {
return databaseName;
public String getUsername() {
return connectionDetailsDelegate.getUsername();
}

@Override
public Optional<String> getOptionalDatabaseName() {
return Optional.ofNullable(databaseName);
return connectionDetailsDelegate.getOptionalDatabaseName();
}

@Override
public Optional<String> getOptionalSchemaDatabaseName() {
return Optional.ofNullable(schemaDatabaseName);
return connectionDetailsDelegate.getOptionalSchemaDatabaseName();
}

@SuppressWarnings("deprecation")
@Override
public String getDatabaseName() {
return getOptionalDatabaseName().orElse(null);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ac.simons.neo4j.migrations.core;

import java.util.Optional;

/**
* Provides detailed information about the connection being used when invoking any method that talks to the database.
*
* @author Michael J. Simons
* @soundtrack Snoop Dogg - Doggystyle
* @since 1.4.0
*/
public interface ConnectionDetails {

/**
* @return the address of the server used
*/
String getServerAddress();

/**
* @return the Neo4j version the server is running
*/
String getServerVersion();

/**
* @return the Neo4j user that ran the migrations
*/
String getUsername();

/**
* @return the database if applicable (Neo4j 4.0 and up)
*/
Optional<String> getOptionalDatabaseName();

/**
* @return the database if applicable (Neo4j 4.0 and up)
*/
Optional<String> getOptionalSchemaDatabaseName();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2020-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ac.simons.neo4j.migrations.core;

import java.util.Optional;

/**
* @author Michael J. Simons
* @soundtrack Snoop Dogg - Doggystyle
* @since 1.4.0
*/
final class DefaultConnectionDetails implements ConnectionDetails {

private final String serverAddress;

private final String serverVersion;

private final String username;

private final String databaseName;

private final String schemaDatabaseName;

DefaultConnectionDetails(String serverAddress, String serverVersion, String username,
String databaseName, String schemaDatabaseName) {
this.serverAddress = serverAddress;
this.serverVersion = serverVersion;
this.username = username;
this.databaseName = databaseName;
this.schemaDatabaseName = schemaDatabaseName;
}

@Override
public String getServerAddress() {
return serverAddress;
}

@Override
public String getServerVersion() {
return serverVersion;
}

@Override
public String getUsername() {
return username;
}

@Override
public Optional<String> getOptionalDatabaseName() {
return Optional.ofNullable(databaseName);
}

@Override
public Optional<String> getOptionalSchemaDatabaseName() {
return Optional.ofNullable(schemaDatabaseName);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,14 @@
import org.neo4j.driver.AccessMode;
import org.neo4j.driver.Bookmark;
import org.neo4j.driver.Driver;
import org.neo4j.driver.Record;
import org.neo4j.driver.Result;
import org.neo4j.driver.Session;
import org.neo4j.driver.SessionConfig;
import org.neo4j.driver.internal.util.ServerVersion;
import org.neo4j.driver.summary.DatabaseInfo;
import org.neo4j.driver.summary.ResultSummary;
import org.neo4j.driver.summary.ServerInfo;

/**
* Default implementation of the {@link MigrationContext}, including the logic of wrapping driver and blocking sessions
Expand Down Expand Up @@ -54,6 +60,8 @@ private static Method findWithImpersonatedUser() {

private final Driver driver;

private volatile ConnectionDetails connectionDetails;

DefaultMigrationContext(MigrationsConfig config, Driver driver) {

if (config.getOptionalImpersonatedUser().isPresent() && WITH_IMPERSONATED_USER == null) {
Expand Down Expand Up @@ -125,6 +133,74 @@ public Session getSchemaSession() {
return getDriver().session(getSessionConfig(applySchemaDatabase));
}

@Override
public ConnectionDetails getConnectionDetails() {

ConnectionDetails availableConnectionDetails = this.connectionDetails;
if (availableConnectionDetails == null) {
synchronized (this) {
availableConnectionDetails = this.connectionDetails;
if (availableConnectionDetails == null) {
this.connectionDetails = getConnectionDetails0();
availableConnectionDetails = this.connectionDetails;
}
}
}
return availableConnectionDetails;
}

private ConnectionDetails getConnectionDetails0() {

class ExtendedResultSummary {
final boolean showCurrentUserExists;
final ServerVersion version;
final ServerInfo server;
final DatabaseInfo database;

ExtendedResultSummary(boolean showCurrentUserExists, ServerVersion version, ResultSummary actualSummary) {
this.showCurrentUserExists = showCurrentUserExists;
this.version = version;
this.server = actualSummary.server();
this.database = actualSummary.database();
}
}

try (Session session = this.getSchemaSession()) {

// Auth maybe disabled. In such cases, we cannot get the current user.
ExtendedResultSummary databaseInformation = session.readTransaction(tx -> {
Result result = tx.run(""
+ "CALL dbms.procedures() YIELD name "
+ "WHERE name = 'dbms.showCurrentUser' "
+ "WITH count(*) > 0 AS showCurrentUserExists "
+ "CALL dbms.components() YIELD versions "
+ "RETURN showCurrentUserExists, 'Neo4j/' + versions[0] AS version"
);
Record singleResultRecord = result.single();
boolean showCurrentUserExists = singleResultRecord.get("showCurrentUserExists").asBoolean();
ServerVersion version = ServerVersion.version(singleResultRecord.get("version").asString());
ResultSummary summary = result.consume();
return new ExtendedResultSummary(showCurrentUserExists, version, summary);
});

String username = "anonymous";
if (databaseInformation.showCurrentUserExists) {

username = session.readTransaction(tx -> tx.run(""
+ "CALL dbms.procedures() YIELD name "
+ "WHERE name = 'dbms.showCurrentUser' "
+ "CALL dbms.showCurrentUser() YIELD username RETURN username"
).single().get("username").asString());
}

ServerInfo serverInfo = databaseInformation.server;
String schemaDatabase = databaseInformation.database == null ? null : databaseInformation.database.name();
String targetDatabase = getConfig().getMigrationTargetIn(this).orElse(schemaDatabase);
return new DefaultConnectionDetails(serverInfo.address(), databaseInformation.version.toString(), username,
targetDatabase, schemaDatabase);
}
}

/**
* This proxy catches all calls to {@link Driver#session()} and {@link Driver#session(SessionConfig)} and
* makes sure that a session config with the current set of bookmarks is used correctly.
Expand Down
Loading

0 comments on commit 5814489

Please sign in to comment.