Skip to content

Commit

Permalink
Introduce notification configuration, severity and category (#1396)
Browse files Browse the repository at this point in the history
* Introduce notification configuration, severity and category

Improve and expand the existing notification API.

A new `NotificationSeverity` type represents a notification severity level.

It includes 2 public constants:
- `INFORMATION`
- `WARNING`

A new `NotificationCategory` type represents a notification category.

It includes 6 public constants:
- `HINT`
- `UNRECOGNIZED`
- `UNSUPPORTED`
- `PERFORMANCE`
- `DEPRECATION`
- `GENERIC`

The `Notification` class has been extended with the following 4 methods:
- `Optional<NotificationSeverity> severityLevel()`
- `Optional<String> rawSeverityLevel()`
- `Optional<NotificationCategory> category()`
- `Optional<String> rawCategory()`

The `raw` methods return a `String` representation returned by the server. In case of an unrecognised value, both `severityLevel()` and `category()` methods return an empty `Optional`. This may happen if a new value is introduced in a future server version and a previous driver version does not support it. Additionally, an empty `Optional` may be returned when driver communicates with a server version that does not supply these values.

The `severity()` method has been deprecated in favour of the `rawSeverityLevel()` method.

A new `NotificationConfig` type has been introduced to allow notification preferences management. By default, the server determines what notifications are provided to the driver. However, user can set a minimum severity level and/or a set of disabled notification categories to manage its expectations. This feature is only supported on Bolt protocol version 5.2 and above.

Both the `Config` and the `SessionConfig` support this new configuration.

Sample usage:
```java
// sets minimum notification severity level to WARNING
// and explicitly disables both GENERIC and HINT notification categories
var driverConfig = Config.builder()
        .withNotificationConfig(NotificationConfig.defaultConfig().enableMinimumSeverity(NotificationSeverity.WARNING).disableCategories(Set.of(NotificationCategory.GENERIC, NotificationCategory.HINT)))
        .build();
// disables all notifications
var sessionConfig = SessionConfig.builder()
        .withNotificationConfig(NotificationConfig.disableAll())
        .build();
```

This update includes support for both 5.1 and 5.2 versions. The latter is required for the full support of the new notification updates.

* Throw exception when notification configuration is not supported

This update introduces a new `UnsupportedFeatureException`. An instance of this exception will be emitted if a custom `NotificationConfig` is used and the driver comes across a Bolt connection that does not support the notification configuration.
  • Loading branch information
injectives authored Mar 29, 2023
1 parent e2962b8 commit a3912a5
Show file tree
Hide file tree
Showing 81 changed files with 2,423 additions and 266 deletions.
24 changes: 24 additions & 0 deletions driver/clirr-ignored-differences.xml
Original file line number Diff line number Diff line change
Expand Up @@ -433,4 +433,28 @@
<method>org.neo4j.driver.BookmarkManager queryTaskBookmarkManager()</method>
</difference>

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

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

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

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

</differences>
32 changes: 32 additions & 0 deletions driver/src/main/java/org/neo4j/driver/Config.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import org.neo4j.driver.exceptions.UnsupportedFeatureException;
import org.neo4j.driver.internal.SecuritySettings;
import org.neo4j.driver.internal.async.pool.PoolSettings;
import org.neo4j.driver.internal.cluster.RoutingSettings;
Expand Down Expand Up @@ -143,6 +144,10 @@ public final class Config implements Serializable {
* The user_agent configured for this driver.
*/
private final String userAgent;
/**
* The notification config.
*/
private final NotificationConfig notificationConfig;
/**
* The {@link MetricsAdapter}.
*/
Expand All @@ -166,6 +171,7 @@ private Config(ConfigBuilder builder) {
this.maxTransactionRetryTimeMillis = builder.maxTransactionRetryTimeMillis;
this.resolver = builder.resolver;
this.fetchSize = builder.fetchSize;
this.notificationConfig = builder.notificationConfig;

this.eventLoopThreads = builder.eventLoopThreads;
this.metricsAdapter = builder.metricsAdapter;
Expand Down Expand Up @@ -311,6 +317,15 @@ public long fetchSize() {
return fetchSize;
}

/**
* Returns notification config.
* @return the notification config
* @since 5.7
*/
public NotificationConfig notificationConfig() {
return notificationConfig;
}

/**
* Returns the number of {@link io.netty.channel.EventLoop} threads.
* @return the number of threads
Expand Down Expand Up @@ -363,6 +378,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 ConfigBuilder() {}

Expand Down Expand Up @@ -757,6 +773,22 @@ public ConfigBuilder withUserAgent(String userAgent) {
return this;
}

/**
* Sets notification config.
* <p>
* Any configuration other than the {@link NotificationConfig#defaultConfig()} requires a minimum Bolt protocol
* version 5.2. Otherwise, an {@link UnsupportedFeatureException} will be emitted when the driver comes across a
* Bolt connection that does not support this feature. For instance, when running a query.
*
* @param notificationConfig the notification config
* @return this builder
* @since 5.7
*/
public ConfigBuilder withNotificationConfig(NotificationConfig notificationConfig) {
this.notificationConfig = Objects.requireNonNull(notificationConfig, "notificationConfig must not be null");
return this;
}

/**
* Extracts the driver version from the driver jar MANIFEST.MF file.
*/
Expand Down
73 changes: 73 additions & 0 deletions driver/src/main/java/org/neo4j/driver/NotificationCategory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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 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 {
/**
* A hint category.
* <p>
* For instance, the given hint cannot be satisfied.
*/
NotificationCategory HINT = new InternalNotificationCategory(Type.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);

/**
* 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);

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

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

/**
* A generic category.
* <p>
* For instance, notifications that are not part of a more specific class.
*/
NotificationCategory GENERIC = new InternalNotificationCategory(Type.GENERIC);
}
81 changes: 81 additions & 0 deletions driver/src/main/java/org/neo4j/driver/NotificationConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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 org.neo4j.driver;

import java.io.Serializable;
import java.util.Set;
import org.neo4j.driver.internal.InternalNotificationConfig;
import org.neo4j.driver.internal.InternalNotificationSeverity;
import org.neo4j.driver.summary.ResultSummary;

/**
* A notification configuration defining what notifications should be supplied by the server.
* <p>
* There are currently 2 optional settings that may be activated:
* <ul>
* <li>Minimum notification severity - sets a baseline severity for notifications, similar to the logging levels.</li>
* <li>A set of disabled notification categories - explicitly disables a set of notification categories.</li>
* </ul>
* <p>
* By default, both options are not activated.
*
* @since 5.7
* @see ResultSummary#notifications()
* @see org.neo4j.driver.summary.Notification
*/
public sealed interface NotificationConfig extends Serializable permits InternalNotificationConfig {
/**
* Returns a default notification configuration.
* <p>
* The default configuration has no settings activated, meaning the resulting behaviour depends on an upstream
* context. For instance, when this config is set on the session level, the resulting behaviour depends on the
* driver's config. Likewise, when this config is set on the driver level, the resulting behaviour depends on the
* server.
*
* @return the default config
*/
static NotificationConfig defaultConfig() {
return new InternalNotificationConfig(null, null);
}

/**
* A config that disables all notifications.
*
* @return the config that disables all notifications
*/
static NotificationConfig disableAllConfig() {
return new InternalNotificationConfig(InternalNotificationSeverity.OFF, null);
}

/**
* Returns a config that sets a minimum severity level for notifications.
*
* @param minimumSeverity the minimum severity level
* @return the config
*/
NotificationConfig enableMinimumSeverity(NotificationSeverity minimumSeverity);

/**
* Returns a config that disables a set of notification categories.
*
* @param disabledCategories the categories to disable, an empty set means no categories are disabled
* @return the config
*/
NotificationConfig disableCategories(Set<NotificationCategory> disabledCategories);
}
41 changes: 41 additions & 0 deletions driver/src/main/java/org/neo4j/driver/NotificationSeverity.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* 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
*
* http://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 org.neo4j.driver;

import static org.neo4j.driver.internal.InternalNotificationSeverity.Type;

import java.io.Serializable;
import org.neo4j.driver.internal.InternalNotificationSeverity;

/**
* Notification severity level.
*
* @since 5.7
*/
public sealed interface NotificationSeverity extends Serializable, Comparable<NotificationSeverity>
permits InternalNotificationSeverity {
/**
* An information severity level.
*/
NotificationSeverity INFORMATION = new InternalNotificationSeverity(Type.INFORMATION, 800);
/**
* A warning severity level.
*/
NotificationSeverity WARNING = new InternalNotificationSeverity(Type.WARNING, 900);
}
35 changes: 34 additions & 1 deletion driver/src/main/java/org/neo4j/driver/SessionConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import java.util.Objects;
import java.util.Optional;
import org.neo4j.driver.async.AsyncSession;
import org.neo4j.driver.exceptions.UnsupportedFeatureException;
import org.neo4j.driver.reactive.ReactiveSession;
import org.neo4j.driver.reactive.RxSession;
import org.neo4j.driver.util.Experimental;
Expand Down Expand Up @@ -65,6 +66,10 @@ public final class SessionConfig implements Serializable {
* The bookmark manager.
*/
private final BookmarkManager bookmarkManager;
/**
* The notification config.
*/
private final NotificationConfig notificationConfig;

private SessionConfig(Builder builder) {
this.bookmarks = builder.bookmarks;
Expand All @@ -73,6 +78,7 @@ private SessionConfig(Builder builder) {
this.fetchSize = builder.fetchSize;
this.impersonatedUser = builder.impersonatedUser;
this.bookmarkManager = builder.bookmarkManager;
this.notificationConfig = builder.notificationConfig;
}

/**
Expand Down Expand Up @@ -161,6 +167,15 @@ public Optional<BookmarkManager> bookmarkManager() {
return Optional.ofNullable(bookmarkManager);
}

/**
* Returns notification config.
* @return the notification config
* @since 5.7
*/
public NotificationConfig notificationConfig() {
return notificationConfig;
}

@Override
public boolean equals(Object o) {
if (this == o) {
Expand All @@ -175,7 +190,8 @@ public boolean equals(Object o) {
&& Objects.equals(database, that.database)
&& Objects.equals(fetchSize, that.fetchSize)
&& Objects.equals(impersonatedUser, that.impersonatedUser)
&& Objects.equals(bookmarkManager, that.bookmarkManager);
&& Objects.equals(bookmarkManager, that.bookmarkManager)
&& Objects.equals(notificationConfig, that.notificationConfig);
}

@Override
Expand Down Expand Up @@ -203,6 +219,7 @@ public static final class Builder {
private String database = null;
private String impersonatedUser = null;
private BookmarkManager bookmarkManager;
private NotificationConfig notificationConfig = NotificationConfig.defaultConfig();

private Builder() {}

Expand Down Expand Up @@ -366,6 +383,22 @@ public Builder withBookmarkManager(BookmarkManager bookmarkManager) {
return this;
}

/**
* Sets notification config.
* <p>
* Any configuration other than the {@link NotificationConfig#defaultConfig()} requires a minimum Bolt protocol
* version 5.2. Otherwise, an {@link UnsupportedFeatureException} will be emitted when the driver comes across a
* Bolt connection that does not support this feature. For instance, when running a query.
*
* @param notificationConfig the notification config
* @return this builder
* @since 5.7
*/
public Builder withNotificationConfig(NotificationConfig notificationConfig) {
this.notificationConfig = Objects.requireNonNull(notificationConfig, "notificationConfig must not be null");
return this;
}

/**
* Builds the {@link SessionConfig}.
* @return the config
Expand Down
Loading

0 comments on commit a3912a5

Please sign in to comment.