Skip to content

Commit

Permalink
Add support for GQL-status objects (#1555)
Browse files Browse the repository at this point in the history
* Add support for GQL-status objects

This update adds support for GQL-status objects defined in the GQL standard.

* Add GqlStatusObject defaults assertions

* Move API updates into Preview

* Update Preview

* Update generated GQL status description

* Delete redundant suppression
  • Loading branch information
injectives authored Jun 20, 2024
1 parent b5f09d4 commit e5d1bfe
Show file tree
Hide file tree
Showing 85 changed files with 1,709 additions and 549 deletions.
24 changes: 24 additions & 0 deletions driver/clirr-ignored-differences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -603,4 +603,28 @@
<method>org.neo4j.driver.ExecutableQuery withAuthToken(org.neo4j.driver.AuthToken)</method>
</difference>

<difference>
<className>org/neo4j/driver/summary/ResultSummary</className>
<differenceType>7012</differenceType>
<method>java.util.Set gqlStatusObjects()</method>
</difference>

<difference>
<className>org/neo4j/driver/summary/Notification</className>
<differenceType>7012</differenceType>
<method>java.util.Optional inputPosition()</method>
</difference>

<difference>
<className>org/neo4j/driver/summary/Notification</className>
<differenceType>7012</differenceType>
<method>java.util.Optional classification()</method>
</difference>

<difference>
<className>org/neo4j/driver/summary/Notification</className>
<differenceType>7012</differenceType>
<method>java.util.Optional rawClassification()</method>
</difference>

</differences>
98 changes: 88 additions & 10 deletions driver/src/main/java/org/neo4j/driver/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static org.neo4j.driver.internal.logging.DevNullLogging.DEV_NULL_LOGGING;
import static org.neo4j.driver.internal.util.DriverInfoUtil.driverVersion;

import io.netty.channel.EventLoop;
import java.io.File;
import java.io.Serial;
import java.io.Serializable;
Expand All @@ -28,9 +29,14 @@
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.neo4j.driver.async.AsyncSession;
import org.neo4j.driver.exceptions.UnsupportedFeatureException;
import org.neo4j.driver.internal.InternalNotificationConfig;
import org.neo4j.driver.internal.SecuritySettings;
import org.neo4j.driver.internal.async.pool.PoolSettings;
import org.neo4j.driver.internal.cluster.RoutingSettings;
Expand All @@ -39,6 +45,8 @@
import org.neo4j.driver.net.ServerAddressResolver;
import org.neo4j.driver.util.Experimental;
import org.neo4j.driver.util.Immutable;
import org.neo4j.driver.util.Preview;
import org.neo4j.driver.util.Resource;

/**
* A configuration class to config driver properties.
Expand Down Expand Up @@ -223,6 +231,7 @@ public int connectionTimeoutMillis() {

/**
* Returns the maximum connection pool size.
*
* @return the maximum size
*/
public int maxConnectionPoolSize() {
Expand All @@ -231,6 +240,7 @@ public int maxConnectionPoolSize() {

/**
* Returns the connection acquisition timeout in milliseconds.
*
* @return the acquisition timeout
*/
public long connectionAcquisitionTimeoutMillis() {
Expand Down Expand Up @@ -296,6 +306,7 @@ public long maxTransactionRetryTimeMillis() {

/**
* Returns the fetch size.
*
* @return the fetch size
*/
public long fetchSize() {
Expand All @@ -304,6 +315,7 @@ public long fetchSize() {

/**
* Returns notification config.
*
* @return the notification config
* @since 5.7
*/
Expand All @@ -312,7 +324,35 @@ public NotificationConfig notificationConfig() {
}

/**
* Returns the number of {@link io.netty.channel.EventLoop} threads.
* Returns a minimum notification severity.
*
* @return an {@link Optional} of minimum {@link NotificationSeverity} or an empty {@link Optional} if it is not set
* @since 5.22.0
*/
@Preview(name = "GQL-status object")
public Optional<NotificationSeverity> minimumNotificationSeverity() {
return Optional.ofNullable(((InternalNotificationConfig) notificationConfig).minimumSeverity());
}

/**
* Returns a set of disabled notification classifications.
* @return the {@link Set} of disabled {@link NotificationClassification}
* @since 5.22.0
*/
@Preview(name = "GQL-status object")
public Set<NotificationClassification> disabledNotificationClassifications() {
var disabledCategories = ((InternalNotificationConfig) notificationConfig).disabledCategories();
return disabledCategories != null
? ((InternalNotificationConfig) notificationConfig)
.disabledCategories().stream()
.map(NotificationClassification.class::cast)
.collect(Collectors.toUnmodifiableSet())
: Collections.emptySet();
}

/**
* Returns the number of {@link EventLoop} threads.
*
* @return the number of threads
*/
public int eventLoopThreads() {
Expand All @@ -328,6 +368,7 @@ public boolean isMetricsEnabled() {

/**
* Returns the {@link MetricsAdapter}.
*
* @return the metrics adapter
*/
public MetricsAdapter metricsAdapter() {
Expand Down Expand Up @@ -373,6 +414,7 @@ public static final class ConfigBuilder {
private MetricsAdapter metricsAdapter = MetricsAdapter.DEV_NULL;
private long fetchSize = FetchSizeUtil.DEFAULT_FETCH_SIZE;
private int eventLoopThreads = 0;

private NotificationConfig notificationConfig = NotificationConfig.defaultConfig();

private boolean telemetryDisabled = false;
Expand All @@ -399,7 +441,7 @@ public ConfigBuilder withLogging(Logging logging) {
* Enable logging of leaked sessions.
* <p>
* Each {@link Session session} is associated with a network connection and thus is a
* {@link org.neo4j.driver.util.Resource resource} that needs to be explicitly closed.
* {@link Resource resource} that needs to be explicitly closed.
* Unclosed sessions will result in socket leaks and could cause {@link OutOfMemoryError}s.
* <p>
* Session is considered to be leaked when it is finalized via {@link Object#finalize()} while not being
Expand Down Expand Up @@ -579,8 +621,8 @@ public ConfigBuilder withTrustStrategy(TrustStrategy trustStrategy) {
public ConfigBuilder withRoutingTablePurgeDelay(long delay, TimeUnit unit) {
var routingTablePurgeDelayMillis = unit.toMillis(delay);
if (routingTablePurgeDelayMillis < 0) {
throw new IllegalArgumentException(String.format(
"The routing table purge delay may not be smaller than 0, but was %d %s.", delay, unit));
throw new IllegalArgumentException(
format("The routing table purge delay may not be smaller than 0, but was %d %s.", delay, unit));
}
this.routingTablePurgeDelayMillis = routingTablePurgeDelayMillis;
return this;
Expand All @@ -591,11 +633,11 @@ public ConfigBuilder withRoutingTablePurgeDelay(long delay, TimeUnit unit) {
* This config is only valid when the driver is used with servers that support Bolt V4 (Server version 4.0 and later).
* <p>
* Bolt V4 enables pulling records in batches to allow client to take control of data population and apply back pressure to server.
* This config specifies the default fetch size for all query runs using {@link Session} and {@link org.neo4j.driver.async.AsyncSession}.
* This config specifies the default fetch size for all query runs using {@link Session} and {@link AsyncSession}.
* By default, the value is set to {@code 1000}.
* Use {@code -1} to disables back pressure and config client to pull all records at once after each run.
* <p>
* This config only applies to run results obtained via {@link Session} and {@link org.neo4j.driver.async.AsyncSession}.
* This config only applies to run results obtained via {@link Session} and {@link AsyncSession}.
* As with the reactive sessions the batch size is managed by the subscription requests instead.
*
* @param size the default record fetch size when pulling records in batches using Bolt V4.
Expand Down Expand Up @@ -627,11 +669,11 @@ public ConfigBuilder withConnectionTimeout(long value, TimeUnit unit) {
var connectionTimeoutMillis = unit.toMillis(value);
if (connectionTimeoutMillis < 0) {
throw new IllegalArgumentException(
String.format("The connection timeout may not be smaller than 0, but was %d %s.", value, unit));
format("The connection timeout may not be smaller than 0, but was %d %s.", value, unit));
}
var connectionTimeoutMillisInt = (int) connectionTimeoutMillis;
if (connectionTimeoutMillisInt != connectionTimeoutMillis) {
throw new IllegalArgumentException(String.format(
throw new IllegalArgumentException(format(
"The connection timeout must represent int value when converted to milliseconds %d.",
connectionTimeoutMillis));
}
Expand All @@ -655,7 +697,7 @@ public ConfigBuilder withMaxTransactionRetryTime(long value, TimeUnit unit) {
var maxRetryTimeMs = unit.toMillis(value);
if (maxRetryTimeMs < 0) {
throw new IllegalArgumentException(
String.format("The max retry time may not be smaller than 0, but was %d %s.", value, unit));
format("The max retry time may not be smaller than 0, but was %d %s.", value, unit));
}
this.maxTransactionRetryTimeMillis = maxRetryTimeMs;
return this;
Expand Down Expand Up @@ -732,7 +774,7 @@ public ConfigBuilder withMetricsAdapter(MetricsAdapter metricsAdapter) {
public ConfigBuilder withEventLoopThreads(int size) {
if (size < 1) {
throw new IllegalArgumentException(
String.format("The event loop thread may not be smaller than 1, but was %d.", size));
format("The event loop thread may not be smaller than 1, but was %d.", size));
}
this.eventLoopThreads = size;
return this;
Expand Down Expand Up @@ -768,6 +810,42 @@ public ConfigBuilder withNotificationConfig(NotificationConfig notificationConfi
return this;
}

/**
* Sets a minimum severity for notifications produced by the server.
*
* @param minimumNotificationSeverity the minimum notification severity
* @return this builder
* @since 5.22.0
*/
@Preview(name = "GQL-status object")
public ConfigBuilder withMinimumNotificationSeverity(NotificationSeverity minimumNotificationSeverity) {
if (minimumNotificationSeverity == null) {
notificationConfig = NotificationConfig.disableAllConfig();
} else {
notificationConfig = notificationConfig.enableMinimumSeverity(minimumNotificationSeverity);
}
return this;
}

/**
* Sets a set of disabled classifications for notifications produced by the server.
*
* @param disabledNotificationClassifications the set of disabled notification classifications
* @return this builder
* @since 5.22.0
*/
@Preview(name = "GQL-status object")
public ConfigBuilder withDisabledNotificationClassifications(
Set<NotificationClassification> disabledNotificationClassifications) {
var disabledCategories = disabledNotificationClassifications == null
? Collections.<NotificationCategory>emptySet()
: disabledNotificationClassifications.stream()
.map(NotificationCategory.class::cast)
.collect(Collectors.toSet());
notificationConfig = notificationConfig.disableCategories(disabledCategories);
return this;
}

/**
* Sets if telemetry is disabled on the driver side.
* <p>
Expand Down
20 changes: 9 additions & 11 deletions driver/src/main/java/org/neo4j/driver/NotificationCategory.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,50 +17,48 @@
package org.neo4j.driver;

import java.io.Serializable;
import org.neo4j.driver.internal.InternalNotificationCategory;
import org.neo4j.driver.internal.InternalNotificationCategory.Type;

/**
* Notification category.
*
* @since 5.7
*/
public sealed interface NotificationCategory extends Serializable permits InternalNotificationCategory {
public sealed interface NotificationCategory extends Serializable permits NotificationClassification {
/**
* A hint category.
* <p>
* For instance, the given hint cannot be satisfied.
*/
NotificationCategory HINT = new InternalNotificationCategory(Type.HINT);
NotificationCategory HINT = NotificationClassification.HINT;

/**
* An unrecognized category.
* <p>
* For instance, the query or command mentions entities that are unknown to the system.
*/
NotificationCategory UNRECOGNIZED = new InternalNotificationCategory(Type.UNRECOGNIZED);
NotificationCategory UNRECOGNIZED = NotificationClassification.UNRECOGNIZED;

/**
* An unsupported category.
* <p>
* For instance, the query/command is trying to use features that are not supported by the current system or using
* features that are experimental and should not be used in production.
*/
NotificationCategory UNSUPPORTED = new InternalNotificationCategory(Type.UNSUPPORTED);
NotificationCategory UNSUPPORTED = NotificationClassification.UNSUPPORTED;

/**
* A performance category.
* <p>
* For instance, the query uses costly operations and might be slow.
*/
NotificationCategory PERFORMANCE = new InternalNotificationCategory(Type.PERFORMANCE);
NotificationCategory PERFORMANCE = NotificationClassification.PERFORMANCE;

/**
* A deprecation category.
* <p>
* For instance, the query/command use deprecated features that should be replaced.
*/
NotificationCategory DEPRECATION = new InternalNotificationCategory(Type.DEPRECATION);
NotificationCategory DEPRECATION = NotificationClassification.DEPRECATION;

/**
* A security category.
Expand All @@ -72,7 +70,7 @@ public sealed interface NotificationCategory extends Serializable permits Intern
*
* @since 5.14
*/
NotificationCategory SECURITY = new InternalNotificationCategory(Type.SECURITY);
NotificationCategory SECURITY = NotificationClassification.SECURITY;

/**
* A topology category.
Expand All @@ -84,12 +82,12 @@ public sealed interface NotificationCategory extends Serializable permits Intern
*
* @since 5.14
*/
NotificationCategory TOPOLOGY = new InternalNotificationCategory(Type.TOPOLOGY);
NotificationCategory TOPOLOGY = NotificationClassification.TOPOLOGY;

/**
* A generic category.
* <p>
* For instance, notifications that are not part of a more specific class.
*/
NotificationCategory GENERIC = new InternalNotificationCategory(Type.GENERIC);
NotificationCategory GENERIC = NotificationClassification.GENERIC;
}
Loading

0 comments on commit e5d1bfe

Please sign in to comment.