Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit cfed70a

Browse files
committedApr 25, 2025·
feat(gql-status-object): Introduce GqlNotification
Following up on the feedback received on the GQL Status Object feature, which is currently in preview, this update separates the new GQL Status Object hierarchy from the legacy `Notification` type by introducing a new `GqlNotification` interface that extends the `GqlStatusObject`. This should make it easier to understand the new GQL Status Objects and migrate from using the legacy `Notification` to the new types. Eventually, the new hierarchy is expected to replace the legacy `Notification` that is expected to be deprecated and phased out at a later date.
1 parent 9d0315e commit cfed70a

File tree

11 files changed

+319
-173
lines changed

11 files changed

+319
-173
lines changed
 

‎driver/clirr-ignored-differences.xml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,4 +645,28 @@
645645
<method>java.lang.Object as(java.lang.Class)</method>
646646
</difference>
647647

648+
<difference>
649+
<className>org/neo4j/driver/summary/Notification</className>
650+
<differenceType>4001</differenceType>
651+
<to>org/neo4j/driver/summary/GqlStatusObject</to>
652+
</difference>
653+
654+
<difference>
655+
<className>org/neo4j/driver/summary/Notification</className>
656+
<differenceType>7002</differenceType>
657+
<method>java.util.Optional inputPosition()</method>
658+
</difference>
659+
660+
<difference>
661+
<className>org/neo4j/driver/summary/Notification</className>
662+
<differenceType>7002</differenceType>
663+
<method>java.util.Optional classification()</method>
664+
</difference>
665+
666+
<difference>
667+
<className>org/neo4j/driver/summary/Notification</className>
668+
<differenceType>7002</differenceType>
669+
<method>java.util.Optional rawClassification()</method>
670+
</difference>
671+
648672
</differences>

‎driver/src/main/java/org/neo4j/driver/NotificationConfig.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import org.neo4j.driver.internal.InternalNotificationConfig;
2222
import org.neo4j.driver.internal.InternalNotificationSeverity;
2323
import org.neo4j.driver.summary.ResultSummary;
24-
import org.neo4j.driver.util.Preview;
2524

2625
/**
2726
* A notification configuration defining what notifications should be supplied by the server.
@@ -40,7 +39,6 @@
4039
* @see ResultSummary#notifications()
4140
* @see org.neo4j.driver.summary.Notification
4241
*/
43-
@Preview(name = "GQL-status object")
4442
public sealed interface NotificationConfig extends Serializable permits InternalNotificationConfig {
4543
/**
4644
* Returns a default notification configuration.
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright (c) "Neo4j"
3+
* Neo4j Sweden AB [https://neo4j.com]
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.neo4j.driver.internal.summary;
18+
19+
import java.util.Map;
20+
import java.util.Optional;
21+
import org.neo4j.driver.NotificationClassification;
22+
import org.neo4j.driver.NotificationSeverity;
23+
import org.neo4j.driver.Value;
24+
import org.neo4j.driver.summary.GqlNotification;
25+
import org.neo4j.driver.summary.InputPosition;
26+
27+
public class InternalGqlNotification extends InternalGqlStatusObject implements GqlNotification {
28+
private final InputPosition position;
29+
private final NotificationSeverity severityLevel;
30+
private final String rawSeverityLevel;
31+
private final NotificationClassification classification;
32+
private final String rawClassification;
33+
34+
public InternalGqlNotification(
35+
String gqlStatus,
36+
String statusDescription,
37+
Map<String, Value> diagnosticRecord,
38+
InputPosition position,
39+
NotificationSeverity severityLevel,
40+
String rawSeverityLevel,
41+
NotificationClassification classification,
42+
String rawClassification) {
43+
super(gqlStatus, statusDescription, diagnosticRecord);
44+
this.position = position;
45+
this.severityLevel = severityLevel;
46+
this.rawSeverityLevel = rawSeverityLevel;
47+
this.classification = classification;
48+
this.rawClassification = rawClassification;
49+
}
50+
51+
@Override
52+
public Optional<InputPosition> inputPosition() {
53+
return Optional.ofNullable(position);
54+
}
55+
56+
@Override
57+
public Optional<NotificationSeverity> severityLevel() {
58+
return Optional.ofNullable(severityLevel);
59+
}
60+
61+
@Override
62+
public Optional<String> rawSeverityLevel() {
63+
return Optional.ofNullable(rawSeverityLevel);
64+
}
65+
66+
@Override
67+
public Optional<NotificationClassification> classification() {
68+
return Optional.ofNullable(classification);
69+
}
70+
71+
@Override
72+
public Optional<String> rawClassification() {
73+
return Optional.ofNullable(rawClassification);
74+
}
75+
76+
@Override
77+
public String toString() {
78+
return "InternalGqlNotification{" + "gqlStatus='"
79+
+ gqlStatus + '\'' + ", statusDescription='"
80+
+ statusDescription + '\'' + ", diagnosticRecord="
81+
+ diagnosticRecord + '}';
82+
}
83+
}

‎driver/src/main/java/org/neo4j/driver/internal/summary/InternalGqlStatusObject.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,9 @@ public class InternalGqlStatusObject implements GqlStatusObject {
5252
Map.entry("OPERATION", Values.value("")),
5353
Map.entry("OPERATION_CODE", Values.value("0"))));
5454

55-
private final String gqlStatus;
56-
private final String statusDescription;
57-
private final Map<String, Value> diagnosticRecord;
55+
protected final String gqlStatus;
56+
protected final String statusDescription;
57+
protected final Map<String, Value> diagnosticRecord;
5858

5959
public InternalGqlStatusObject(String gqlStatus, String statusDescription, Map<String, Value> diagnosticRecord) {
6060
this.gqlStatus = Objects.requireNonNull(gqlStatus);

‎driver/src/main/java/org/neo4j/driver/internal/summary/InternalNotification.java

Lines changed: 40 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,20 @@
1616
*/
1717
package org.neo4j.driver.internal.summary;
1818

19+
import static org.neo4j.driver.internal.value.NullValue.NULL;
20+
1921
import java.util.Arrays;
20-
import java.util.Map;
21-
import java.util.Objects;
2222
import java.util.Optional;
23+
import java.util.function.Function;
2324
import org.neo4j.driver.NotificationCategory;
2425
import org.neo4j.driver.NotificationClassification;
2526
import org.neo4j.driver.NotificationSeverity;
2627
import org.neo4j.driver.Value;
28+
import org.neo4j.driver.internal.InternalNotificationSeverity;
2729
import org.neo4j.driver.summary.InputPosition;
2830
import org.neo4j.driver.summary.Notification;
2931

30-
public class InternalNotification extends InternalGqlStatusObject implements Notification {
32+
public class InternalNotification implements Notification {
3133
public static Optional<NotificationCategory> valueOf(String value) {
3234
return Arrays.stream(NotificationClassification.values())
3335
.filter(type -> type.toString().equals(value))
@@ -45,57 +47,73 @@ public static Optional<NotificationCategory> valueOf(String value) {
4547
});
4648
}
4749

50+
public static final Function<Value, Notification> VALUE_TO_NOTIFICATION = value -> {
51+
var code = value.get("code").asString();
52+
var title = value.get("title").asString();
53+
var description = value.get("description").asString();
54+
var rawSeverityLevel =
55+
value.containsKey("severity") ? value.get("severity").asString() : null;
56+
var severityLevel =
57+
InternalNotificationSeverity.valueOf(rawSeverityLevel).orElse(null);
58+
var rawCategory = value.containsKey("category") ? value.get("category").asString() : null;
59+
var category = valueOf(rawCategory).orElse(null);
60+
61+
var posValue = value.get("position");
62+
InputPosition position = null;
63+
if (posValue != NULL) {
64+
position = new InternalInputPosition(
65+
posValue.get("offset").asInt(),
66+
posValue.get("line").asInt(),
67+
posValue.get("column").asInt());
68+
}
69+
70+
return new InternalNotification(
71+
code, title, description, severityLevel, rawSeverityLevel, category, rawCategory, position);
72+
};
73+
4874
private final String code;
4975
private final String title;
5076
private final String description;
5177
private final NotificationSeverity severityLevel;
5278
private final String rawSeverityLevel;
53-
private final NotificationClassification classification;
54-
private final String rawClassification;
79+
private final NotificationCategory category;
80+
private final String rawCategory;
5581
private final InputPosition position;
5682

5783
public InternalNotification(
58-
String gqlStatus,
59-
String statusDescription,
60-
Map<String, Value> diagnosticRecord,
6184
String code,
6285
String title,
6386
String description,
6487
NotificationSeverity severityLevel,
6588
String rawSeverityLevel,
66-
NotificationClassification classification,
67-
String rawClassification,
89+
NotificationCategory category,
90+
String rawCategory,
6891
InputPosition position) {
69-
super(gqlStatus, statusDescription, diagnosticRecord);
70-
this.code = Objects.requireNonNull(code);
92+
this.code = code;
7193
this.title = title;
7294
this.description = description;
7395
this.severityLevel = severityLevel;
7496
this.rawSeverityLevel = rawSeverityLevel;
75-
this.classification = classification;
76-
this.rawClassification = rawClassification;
97+
this.category = category;
98+
this.rawCategory = rawCategory;
7799
this.position = position;
78100
}
79101

80-
@SuppressWarnings({"deprecation", "RedundantSuppression"})
81102
@Override
82103
public String code() {
83104
return code;
84105
}
85106

86-
@SuppressWarnings({"deprecation", "RedundantSuppression"})
87107
@Override
88108
public String title() {
89109
return title;
90110
}
91111

92-
@SuppressWarnings({"deprecation", "RedundantSuppression"})
93112
@Override
94113
public String description() {
95114
return description;
96115
}
97116

98-
@SuppressWarnings({"deprecation", "RedundantSuppression"})
99117
@Override
100118
public InputPosition position() {
101119
return position;
@@ -111,62 +129,21 @@ public Optional<String> rawSeverityLevel() {
111129
return Optional.ofNullable(rawSeverityLevel);
112130
}
113131

114-
@Override
115-
public Optional<NotificationClassification> classification() {
116-
return Optional.ofNullable(classification);
117-
}
118-
119-
@Override
120-
public Optional<String> rawClassification() {
121-
return Optional.ofNullable(rawClassification);
122-
}
123-
124132
@Override
125133
public Optional<NotificationCategory> category() {
126-
return Optional.ofNullable(classification);
134+
return Optional.ofNullable(category);
127135
}
128136

129137
@Override
130138
public Optional<String> rawCategory() {
131-
return Optional.ofNullable(rawClassification);
132-
}
133-
134-
@Override
135-
public boolean equals(Object o) {
136-
if (this == o) return true;
137-
if (o == null || getClass() != o.getClass()) return false;
138-
if (!super.equals(o)) return false;
139-
var that = (InternalNotification) o;
140-
return Objects.equals(code, that.code)
141-
&& Objects.equals(title, that.title)
142-
&& Objects.equals(description, that.description)
143-
&& Objects.equals(severityLevel, that.severityLevel)
144-
&& Objects.equals(rawSeverityLevel, that.rawSeverityLevel)
145-
&& classification == that.classification
146-
&& Objects.equals(rawClassification, that.rawClassification)
147-
&& Objects.equals(position, that.position);
148-
}
149-
150-
@Override
151-
public int hashCode() {
152-
return Objects.hash(
153-
super.hashCode(),
154-
code,
155-
title,
156-
description,
157-
severityLevel,
158-
rawSeverityLevel,
159-
classification,
160-
rawClassification,
161-
position);
139+
return Optional.ofNullable(rawCategory);
162140
}
163141

164142
@Override
165143
public String toString() {
166144
var info = "code=" + code + ", title=" + title + ", description=" + description + ", severityLevel="
167-
+ severityLevel + ", rawSeverityLevel=" + rawSeverityLevel + ", classification=" + classification
168-
+ ", rawClassification="
169-
+ rawClassification;
145+
+ severityLevel + ", rawSeverityLevel=" + rawSeverityLevel + ", category=" + category + ", rawCategory="
146+
+ rawCategory;
170147
return position == null ? info : info + ", position={" + position + "}";
171148
}
172149
}

‎driver/src/main/java/org/neo4j/driver/internal/util/MetadataExtractor.java

Lines changed: 56 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import static java.util.Collections.unmodifiableSet;
2020
import static java.util.stream.Collectors.collectingAndThen;
21+
import static java.util.stream.Collectors.filtering;
22+
import static java.util.stream.Collectors.mapping;
2123
import static java.util.stream.Collectors.teeing;
2224
import static java.util.stream.Collectors.toCollection;
2325
import static java.util.stream.Collectors.toUnmodifiableList;
@@ -30,6 +32,7 @@
3032
import java.util.LinkedHashSet;
3133
import java.util.List;
3234
import java.util.Map;
35+
import java.util.Objects;
3336
import java.util.OptionalInt;
3437
import java.util.Set;
3538
import java.util.TreeSet;
@@ -45,6 +48,7 @@
4548
import org.neo4j.driver.internal.InternalNotificationSeverity;
4649
import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection;
4750
import org.neo4j.driver.internal.summary.InternalDatabaseInfo;
51+
import org.neo4j.driver.internal.summary.InternalGqlNotification;
4852
import org.neo4j.driver.internal.summary.InternalGqlStatusObject;
4953
import org.neo4j.driver.internal.summary.InternalInputPosition;
5054
import org.neo4j.driver.internal.summary.InternalNotification;
@@ -104,29 +108,38 @@ public ResultSummary extractSummary(
104108
Set<GqlStatusObject> gqlStatusObjects;
105109
List<Notification> notifications;
106110
if (legacyNotifications) {
107-
var gqlStatusObjectsAndNotifications = extractGqlStatusObjectsFromNotifications(metadata)
111+
var gqlStatusObjectsAndNotifications = generateGqlStatusObjectsAndExtractNotifications(metadata)
108112
.collect(teeing(
109-
collectingAndThen(
110-
toCollection(
111-
() -> (Set<GqlStatusObject>) new TreeSet<>(GQL_STATUS_OBJECT_COMPARATOR)),
112-
set -> {
113-
if (gqlStatusObject != null) {
114-
set.add(gqlStatusObject);
115-
}
116-
return unmodifiableSet(set);
117-
}),
118-
toUnmodifiableList(),
113+
mapping(
114+
GqlStatusObjectAndNotification::gqlStatusObject,
115+
collectingAndThen(
116+
toCollection(() ->
117+
(Set<GqlStatusObject>) new TreeSet<>(GQL_STATUS_OBJECT_COMPARATOR)),
118+
set -> {
119+
if (gqlStatusObject != null) {
120+
set.add(gqlStatusObject);
121+
}
122+
return unmodifiableSet(set);
123+
})),
124+
mapping(GqlStatusObjectAndNotification::notification, toUnmodifiableList()),
119125
GqlStatusObjectsAndNotifications::new));
120126
gqlStatusObjects = gqlStatusObjectsAndNotifications.gqlStatusObjects();
121127
notifications = gqlStatusObjectsAndNotifications.notifications();
122128
} else {
123-
gqlStatusObjects = extractGqlStatusObjects(metadata)
124-
.collect(collectingAndThen(
125-
toCollection(() -> (Set<GqlStatusObject>) new LinkedHashSet<GqlStatusObject>()),
126-
Collections::unmodifiableSet));
127-
notifications = gqlStatusObjects.stream()
128-
.flatMap(status -> status instanceof Notification ? Stream.of((Notification) status) : null)
129-
.toList();
129+
var gqlStatusObjectsAndNotifications = extractGqlStatusObjectsAndGenerateNotifications(metadata)
130+
.collect(teeing(
131+
mapping(
132+
GqlStatusObjectAndNotification::gqlStatusObject,
133+
collectingAndThen(
134+
toCollection(
135+
() -> (Set<GqlStatusObject>) new LinkedHashSet<GqlStatusObject>()),
136+
Collections::unmodifiableSet)),
137+
mapping(
138+
GqlStatusObjectAndNotification::notification,
139+
filtering(Objects::nonNull, toUnmodifiableList())),
140+
GqlStatusObjectsAndNotifications::new));
141+
gqlStatusObjects = gqlStatusObjectsAndNotifications.gqlStatusObjects();
142+
notifications = gqlStatusObjectsAndNotifications.notifications();
130143
}
131144
return new InternalResultSummary(
132145
query,
@@ -200,7 +213,8 @@ private static ProfiledPlan extractProfiledPlan(Map<String, Value> metadata) {
200213
return null;
201214
}
202215

203-
private static Stream<Notification> extractGqlStatusObjectsFromNotifications(Map<String, Value> metadata) {
216+
private static Stream<GqlStatusObjectAndNotification> generateGqlStatusObjectsAndExtractNotifications(
217+
Map<String, Value> metadata) {
204218
var notificationsValue = metadata.get("notifications");
205219
if (notificationsValue != null && TypeSystem.getDefault().LIST().isTypeOf(notificationsValue)) {
206220
var iterable = notificationsValue.values(value -> {
@@ -259,36 +273,37 @@ private static Stream<Notification> extractGqlStatusObjectsFromNotifications(Map
259273
Values.value(position.column()))));
260274
}
261275

262-
return new InternalNotification(
276+
var gqlNotification = new InternalGqlNotification(
263277
gqlStatusCode,
264278
gqlStatusDescription,
265279
Collections.unmodifiableMap(diagnosticRecord),
266-
code,
267-
title,
268-
description,
280+
position,
269281
severityLevel,
270282
rawSeverityLevel,
271283
(NotificationClassification) category,
272-
rawCategory,
273-
position);
284+
rawCategory);
285+
var notification = new InternalNotification(
286+
code, title, description, severityLevel, rawSeverityLevel, category, rawCategory, position);
287+
return new GqlStatusObjectAndNotification(gqlNotification, notification);
274288
});
275-
return StreamSupport.stream(iterable.spliterator(), false).map(Notification.class::cast);
289+
return StreamSupport.stream(iterable.spliterator(), false);
276290
} else {
277291
return Stream.empty();
278292
}
279293
}
280294

281-
private static Stream<GqlStatusObject> extractGqlStatusObjects(Map<String, Value> metadata) {
295+
private static Stream<GqlStatusObjectAndNotification> extractGqlStatusObjectsAndGenerateNotifications(
296+
Map<String, Value> metadata) {
282297
var statuses = metadata.get("statuses");
283298
if (statuses != null && TypeSystem.getDefault().LIST().isTypeOf(statuses)) {
284-
var iterable = statuses.values(MetadataExtractor::extractGqlStatusObject);
299+
var iterable = statuses.values(MetadataExtractor::extractGqlStatusObjectAndGenerateNotification);
285300
return StreamSupport.stream(iterable.spliterator(), false);
286301
} else {
287302
return Stream.empty();
288303
}
289304
}
290305

291-
private static GqlStatusObject extractGqlStatusObject(Value value) {
306+
private static GqlStatusObjectAndNotification extractGqlStatusObjectAndGenerateNotification(Value value) {
292307
var status = value.get("gql_status").asString();
293308
var description = value.get("status_description").asString();
294309
Map<String, Value> diagnosticRecord;
@@ -322,7 +337,8 @@ private static GqlStatusObject extractGqlStatusObject(Value value) {
322337
var neo4jCode = value.get("neo4j_code").asString(null);
323338

324339
if (neo4jCode == null || neo4jCode.trim().isEmpty()) {
325-
return new InternalGqlStatusObject(status, description, diagnosticRecord);
340+
var gqlStatusObject = new InternalGqlStatusObject(status, description, diagnosticRecord);
341+
return new GqlStatusObjectAndNotification(gqlStatusObject, null);
326342
} else {
327343
var title = value.get("title").asString();
328344
var notificationDescription =
@@ -354,10 +370,16 @@ private static GqlStatusObject extractGqlStatusObject(Value value) {
354370
var classification = (NotificationClassification)
355371
InternalNotification.valueOf(rawClassification).orElse(null);
356372

357-
return new InternalNotification(
373+
var gqlNotification = new InternalGqlNotification(
358374
status,
359375
description,
360376
diagnosticRecord,
377+
position,
378+
severity,
379+
rawSeverity,
380+
classification,
381+
rawClassification);
382+
var notification = new InternalNotification(
361383
neo4jCode,
362384
title,
363385
notificationDescription,
@@ -366,6 +388,7 @@ private static GqlStatusObject extractGqlStatusObject(Value value) {
366388
classification,
367389
rawClassification,
368390
position);
391+
return new GqlStatusObjectAndNotification(gqlNotification, notification);
369392
}
370393
}
371394

@@ -388,4 +411,6 @@ private static long extractResultConsumedAfter(Map<String, Value> metadata, Stri
388411

389412
private record GqlStatusObjectsAndNotifications(
390413
Set<GqlStatusObject> gqlStatusObjects, List<Notification> notifications) {}
414+
415+
private record GqlStatusObjectAndNotification(GqlStatusObject gqlStatusObject, Notification notification) {}
391416
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Copyright (c) "Neo4j"
3+
* Neo4j Sweden AB [https://neo4j.com]
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.neo4j.driver.summary;
18+
19+
import java.util.Optional;
20+
import org.neo4j.driver.NotificationClassification;
21+
import org.neo4j.driver.NotificationSeverity;
22+
import org.neo4j.driver.util.Preview;
23+
24+
/**
25+
* TODO
26+
*/
27+
@Preview(name = "GQL-status object")
28+
public interface GqlNotification extends GqlStatusObject {
29+
30+
/**
31+
* TODO
32+
* @return TODO
33+
*/
34+
Optional<InputPosition> inputPosition();
35+
36+
/**
37+
* TODO
38+
* @return TODO
39+
*/
40+
Optional<NotificationSeverity> severityLevel();
41+
42+
/**
43+
* TODO
44+
* @return TODO
45+
*/
46+
Optional<String> rawSeverityLevel();
47+
48+
/**
49+
* TODO
50+
* @return TODO
51+
*/
52+
Optional<NotificationClassification> classification();
53+
54+
/**
55+
* TODO
56+
* @return TODO
57+
*/
58+
Optional<String> rawClassification();
59+
}

‎driver/src/main/java/org/neo4j/driver/summary/Notification.java

Lines changed: 3 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,8 @@
1818

1919
import java.util.Optional;
2020
import org.neo4j.driver.NotificationCategory;
21-
import org.neo4j.driver.NotificationClassification;
2221
import org.neo4j.driver.NotificationSeverity;
2322
import org.neo4j.driver.util.Immutable;
24-
import org.neo4j.driver.util.Preview;
2523

2624
/**
2725
* Representation for notifications found when executing a query.
@@ -30,7 +28,7 @@
3028
* @since 1.0
3129
*/
3230
@Immutable
33-
public interface Notification extends GqlStatusObject {
31+
public interface Notification {
3432
/**
3533
* Returns a notification code for the discovered issue.
3634
* @return the notification code
@@ -40,7 +38,6 @@ public interface Notification extends GqlStatusObject {
4038
/**
4139
* Returns a short summary of the notification.
4240
* @return the title of the notification.
43-
* @see #gqlStatus()
4441
*/
4542
String title();
4643

@@ -59,19 +56,6 @@ public interface Notification extends GqlStatusObject {
5956
*/
6057
InputPosition position();
6158

62-
/**
63-
* Returns a position in the query where this notification points to.
64-
* <p>
65-
* Not all notifications have a unique position to point to and in that case an empty {@link Optional} is returned.
66-
*
67-
* @return an {@link Optional} of the {@link InputPosition} if available or an empty {@link Optional} otherwise
68-
* @since 5.22.0
69-
*/
70-
@Preview(name = "GQL-status object")
71-
default Optional<InputPosition> inputPosition() {
72-
return Optional.ofNullable(position());
73-
}
74-
7559
/**
7660
* The severity level of the notification.
7761
*
@@ -84,52 +68,25 @@ default String severity() {
8468
}
8569

8670
/**
87-
* Returns the severity level of the notification derived from the diagnostic record.
71+
* Returns the severity level of the notification.
8872
*
8973
* @return the severity level of the notification
9074
* @since 5.7
91-
* @see #diagnosticRecord()
9275
*/
9376
default Optional<NotificationSeverity> severityLevel() {
9477
return Optional.empty();
9578
}
9679

9780
/**
98-
* Returns the raw severity level of the notification as a String value retrieved directly from the diagnostic
99-
* record.
81+
* Returns the raw severity level of the notification as a String returned by the server.
10082
*
10183
* @return the severity level of the notification
10284
* @since 5.7
103-
* @see #diagnosticRecord()
10485
*/
10586
default Optional<String> rawSeverityLevel() {
10687
return Optional.empty();
10788
}
10889

109-
/**
110-
* Returns {@link NotificationClassification} derived from the diagnostic record.
111-
* @return an {@link Optional} of {@link NotificationClassification} or an empty {@link Optional} when the
112-
* classification is either absent or unrecognised
113-
* @since 5.22.0
114-
* @see #diagnosticRecord()
115-
*/
116-
@Preview(name = "GQL-status object")
117-
default Optional<NotificationClassification> classification() {
118-
return Optional.empty();
119-
}
120-
121-
/**
122-
* Returns notification classification from the diagnostic record as a {@link String} value retrieved directly from
123-
* the diagnostic record.
124-
* @return an {@link Optional} of notification classification or an empty {@link Optional} when it is absent
125-
* @since 5.22.0
126-
* @see #diagnosticRecord()
127-
*/
128-
@Preview(name = "GQL-status object")
129-
default Optional<String> rawClassification() {
130-
return Optional.empty();
131-
}
132-
13390
/**
13491
* Returns the category of the notification.
13592
*

‎driver/src/main/java/org/neo4j/driver/summary/ResultSummary.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,13 +85,9 @@ public interface ResultSummary {
8585
* in a client.
8686
* <p>
8787
* Unlike failures or errors, notifications do not affect the execution of a query.
88-
* <p>
89-
* Since {@link Notification} is a subtype of {@link GqlStatusObject}, the list of notifications is a subset of all
90-
* GQL-status objects that are of {@link Notification} type. However, the order might be different.
9188
*
9289
* @return a list of notifications produced while executing the query. The list will be empty if no
9390
* notifications produced while executing the query.
94-
* @see #gqlStatusObjects()
9591
*/
9692
List<Notification> notifications();
9793

‎driver/src/test/java/org/neo4j/driver/internal/util/MetadataExtractorTest.java

Lines changed: 47 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.neo4j.driver.exceptions.value.Uncoercible;
5252
import org.neo4j.driver.internal.adaptedbolt.DriverBoltConnection;
5353
import org.neo4j.driver.internal.summary.InternalInputPosition;
54+
import org.neo4j.driver.summary.GqlNotification;
5455
import org.neo4j.driver.summary.Notification;
5556
import org.neo4j.driver.summary.ResultSummary;
5657

@@ -243,9 +244,13 @@ void shouldBuildResultSummaryWithNotifications() {
243244

244245
var summary = extractor.extractSummary(query(), connectionMock(), 42, metadata, true, null);
245246

247+
assertEquals(2, summary.gqlStatusObjects().size());
246248
assertEquals(2, summary.notifications().size());
249+
var gqlStatusObjectsIterator = summary.gqlStatusObjects().iterator();
247250
var firstNotification = summary.notifications().get(0);
251+
var firstGqlStatusObject = (GqlNotification) gqlStatusObjectsIterator.next();
248252
var secondNotification = summary.notifications().get(1);
253+
var secondGqlStatusObject = (GqlNotification) gqlStatusObjectsIterator.next();
249254

250255
assertEquals("Almost bad thing", firstNotification.description());
251256
assertEquals("Neo.DummyNotification", firstNotification.code());
@@ -258,6 +263,19 @@ void shouldBuildResultSummaryWithNotifications() {
258263
NotificationCategory.DEPRECATION, firstNotification.category().get());
259264
assertEquals("DEPRECATION", firstNotification.rawCategory().get());
260265
assertEquals(new InternalInputPosition(42, 4242, 424242), firstNotification.position());
266+
267+
assertEquals("Almost bad thing", firstGqlStatusObject.statusDescription());
268+
assertEquals(
269+
NotificationSeverity.WARNING,
270+
firstGqlStatusObject.severityLevel().get());
271+
assertEquals("WARNING", firstGqlStatusObject.rawSeverityLevel().get());
272+
assertEquals(
273+
NotificationCategory.DEPRECATION,
274+
firstGqlStatusObject.classification().get());
275+
assertEquals("DEPRECATION", firstGqlStatusObject.rawClassification().get());
276+
assertEquals(
277+
new InternalInputPosition(42, 4242, 424242),
278+
firstGqlStatusObject.inputPosition().get());
261279
assertEquals(
262280
Map.of(
263281
"OPERATION",
@@ -275,28 +293,27 @@ void shouldBuildResultSummaryWithNotifications() {
275293
"offset", 42,
276294
"line", 4242,
277295
"column", 424242)),
278-
firstNotification.diagnosticRecord());
296+
firstGqlStatusObject.diagnosticRecord());
279297

280298
assertEquals("Almost good thing", secondNotification.description());
281299
assertEquals("Neo.GoodNotification", secondNotification.code());
282300
assertEquals("Good", secondNotification.title());
283301
assertEquals("INFO", secondNotification.severity());
284-
assertTrue(secondNotification.inputPosition().isEmpty());
285302
assertNull(secondNotification.position());
303+
304+
assertEquals("Almost good thing", secondGqlStatusObject.statusDescription());
305+
assertFalse(secondGqlStatusObject.severityLevel().isPresent());
306+
assertEquals("INFO", secondGqlStatusObject.rawSeverityLevel().get());
307+
assertFalse(secondGqlStatusObject.classification().isPresent());
308+
assertFalse(secondGqlStatusObject.rawClassification().isPresent());
309+
assertTrue(secondGqlStatusObject.inputPosition().isEmpty());
286310
assertEquals(
287311
Map.of(
288312
"OPERATION", Values.value(""),
289313
"OPERATION_CODE", Values.value("0"),
290314
"CURRENT_SCHEMA", Values.value("/"),
291315
"_severity", Values.value("INFO")),
292-
secondNotification.diagnosticRecord());
293-
294-
assertEquals(2, summary.gqlStatusObjects().size());
295-
var gqlStatusObjectsIterator = summary.gqlStatusObjects().iterator();
296-
var firstGqlStatusObject = (Notification) gqlStatusObjectsIterator.next();
297-
var secondGqlStatusObject = (Notification) gqlStatusObjectsIterator.next();
298-
assertEquals(firstNotification, firstGqlStatusObject);
299-
assertEquals(secondNotification, secondGqlStatusObject);
316+
secondGqlStatusObject.diagnosticRecord());
300317
}
301318

302319
@Test
@@ -339,24 +356,25 @@ void shouldBuildResultSummaryWithGqlStatusObjects() {
339356
var summary = extractor.extractSummary(query(), connectionMock(), 42, metadata, false, null);
340357

341358
assertEquals(2, summary.gqlStatusObjects().size());
359+
assertEquals(1, summary.notifications().size());
342360
var gqlStatusObjectsIterator = summary.gqlStatusObjects().iterator();
343-
var firstGqlStatusObject = (Notification) gqlStatusObjectsIterator.next();
361+
var firstGqlStatusObject = (GqlNotification) gqlStatusObjectsIterator.next();
362+
var firstNotification = summary.notifications().get(0);
344363
var secondGqlStatusObject = gqlStatusObjectsIterator.next();
345364

346365
assertEquals("gql_status", firstGqlStatusObject.gqlStatus());
347366
assertEquals("status_description", firstGqlStatusObject.statusDescription());
348-
assertEquals("notification_description", firstGqlStatusObject.description());
349-
assertEquals("neo4j_code", firstGqlStatusObject.code());
350-
assertEquals("title", firstGqlStatusObject.title());
351-
assertEquals("WARNING", firstGqlStatusObject.severity());
352367
assertEquals(
353368
NotificationSeverity.WARNING,
354369
firstGqlStatusObject.severityLevel().get());
355370
assertEquals("WARNING", firstGqlStatusObject.rawSeverityLevel().get());
356371
assertEquals(
357-
NotificationCategory.SECURITY, firstGqlStatusObject.category().get());
358-
assertEquals("SECURITY", firstGqlStatusObject.rawCategory().get());
359-
assertEquals(new InternalInputPosition(42, 4242, 424242), firstGqlStatusObject.position());
372+
NotificationCategory.SECURITY,
373+
firstGqlStatusObject.classification().get());
374+
assertEquals("SECURITY", firstGqlStatusObject.rawClassification().get());
375+
assertEquals(
376+
new InternalInputPosition(42, 4242, 424242),
377+
firstGqlStatusObject.inputPosition().get());
360378
assertEquals(
361379
Map.of(
362380
"OPERATION",
@@ -376,6 +394,17 @@ void shouldBuildResultSummaryWithGqlStatusObjects() {
376394
"column", 424242)),
377395
firstGqlStatusObject.diagnosticRecord());
378396

397+
assertEquals("notification_description", firstNotification.description());
398+
assertEquals("neo4j_code", firstNotification.code());
399+
assertEquals("title", firstNotification.title());
400+
assertEquals("WARNING", firstNotification.severity());
401+
assertEquals(
402+
NotificationSeverity.WARNING, firstNotification.severityLevel().get());
403+
assertEquals("WARNING", firstNotification.rawSeverityLevel().get());
404+
assertEquals(NotificationCategory.SECURITY, firstNotification.category().get());
405+
assertEquals("SECURITY", firstNotification.rawCategory().get());
406+
assertEquals(new InternalInputPosition(42, 4242, 424242), firstNotification.position());
407+
379408
assertFalse(secondGqlStatusObject instanceof Notification);
380409
assertEquals("gql_status", secondGqlStatusObject.gqlStatus());
381410
assertEquals("status_description", secondGqlStatusObject.statusDescription());
@@ -387,9 +416,6 @@ void shouldBuildResultSummaryWithGqlStatusObjects() {
387416
"_severity", Values.value("WARNING"),
388417
"_classification", Values.value("SECURITY")),
389418
secondGqlStatusObject.diagnosticRecord());
390-
391-
assertEquals(1, summary.notifications().size());
392-
assertEquals(firstGqlStatusObject, summary.notifications().get(0));
393419
}
394420

395421
@Test

‎testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SummaryUtil.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@
2424
import java.util.stream.Collectors;
2525
import neo4j.org.testkit.backend.messages.responses.Summary;
2626
import org.neo4j.driver.internal.InternalNotificationSeverity;
27+
import org.neo4j.driver.summary.GqlNotification;
2728
import org.neo4j.driver.summary.InputPosition;
28-
import org.neo4j.driver.summary.Notification;
2929
import org.neo4j.driver.summary.Plan;
3030
import org.neo4j.driver.summary.ProfiledPlan;
3131
import org.neo4j.driver.summary.QueryType;
@@ -83,8 +83,9 @@ public static Summary.SummaryBody toSummaryBody(org.neo4j.driver.summary.ResultS
8383
.gqlStatus(gqlStatusObject.gqlStatus())
8484
.statusDescription(gqlStatusObject.statusDescription())
8585
.diagnosticRecord(gqlStatusObject.diagnosticRecord());
86-
if (gqlStatusObject instanceof Notification notification) {
87-
builder = builder.position(toInputPosition(notification.position()))
86+
if (gqlStatusObject instanceof GqlNotification notification) {
87+
builder = builder.position(toInputPosition(
88+
notification.inputPosition().orElse(null)))
8889
.severity(notification
8990
.severityLevel()
9091
.map(InternalNotificationSeverity.class::cast)

0 commit comments

Comments
 (0)
Please sign in to comment.