diff --git a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Exceptions/ExceptionManager.cs b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Exceptions/ExceptionManager.cs index 2094e435e..1a01d50d5 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Exceptions/ExceptionManager.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Exceptions/ExceptionManager.cs @@ -65,7 +65,7 @@ internal static class ExceptionManager { typeof(NotSupportedException), "NotSupportedException" }, { typeof(ArgumentException), "ArgumentError" }, { typeof(InvalidBookmarkMixtureException), "InvalidBookmarkMixtureError" }, - { typeof(ArgumentErrorException), "ArgumentError" }, + { typeof(StatementArgumentException), "ArgumentError" }, { typeof(TypeException), "TypeError" }, { typeof(ForbiddenException), "ForbiddenError" }, { typeof(UnknownSecurityException), "OtherSecurityException" }, diff --git a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Protocol/DriverQuery/ExecuteQuery.cs b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Protocol/DriverQuery/ExecuteQuery.cs index a79f79cc4..e338fc060 100644 --- a/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Protocol/DriverQuery/ExecuteQuery.cs +++ b/Neo4j.Driver/Neo4j.Driver.Tests.TestBackend/Protocol/DriverQuery/ExecuteQuery.cs @@ -17,6 +17,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using Neo4j.Driver.Internal.Auth; using Newtonsoft.Json; namespace Neo4j.Driver.Tests.TestBackend; diff --git a/Neo4j.Driver/Neo4j.Driver.Tests/Exceptions/Neo4jExceptionFactoryTests.cs b/Neo4j.Driver/Neo4j.Driver.Tests/Exceptions/Neo4jExceptionFactoryTests.cs new file mode 100644 index 000000000..24c910a4f --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver.Tests/Exceptions/Neo4jExceptionFactoryTests.cs @@ -0,0 +1,55 @@ +// 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. + +using System; +using System.Collections.Generic; +using FluentAssertions; +using Neo4j.Driver.Internal.ExceptionHandling; +using Xunit; + +namespace Neo4j.Driver.Tests.Exceptions; + +public class Neo4jExceptionFactoryTests +{ + public static IEnumerable CodeToTypeMapping = new[] + { + new object[] { "Neo.ClientError.Statement.ArgumentError", typeof(StatementArgumentException) }, + new object[] { "Neo.ClientError.Security.Unauthorized", typeof(AuthenticationException) }, + new object[] { "Neo.ClientError.Security.AuthorizationExpired", typeof(AuthorizationException) }, + new object[] { "Neo.ClientError.Database.DatabaseNotFound", typeof(FatalDiscoveryException) }, + new object[] { "Neo.ClientError.Security.Forbidden", typeof(ForbiddenException) }, + new object[] { "Neo.ClientError.Transaction.InvalidBookmark", typeof(InvalidBookmarkException) }, + new object[] { "Neo.ClientError.Transaction.InvalidBookmarkMixture", typeof(InvalidBookmarkMixtureException) }, + new object[] { "Neo.ClientError.Request.Invalid", typeof(ProtocolException) }, + new object[] { "Neo.ClientError.Request.InvalidFormat", typeof(ProtocolException) }, + new object[] { "Neo.ClientError.Security.TokenExpired", typeof(TokenExpiredException) }, + new object[] { "Neo.ClientError.Statement.TypeError", typeof(TypeException) }, + new object[] { "Neo.ClientError.Security.##unknown##", typeof(UnknownSecurityException) }, + new object[] { "Neo.DatabaseError.blah", typeof(DatabaseException) }, + new object[] { "Neo.TransientError.TemporaryDisabled", typeof(TransientException) }, + }; + + [Theory, MemberData(nameof(CodeToTypeMapping))] + public void ShouldCreateCorrectExceptionType(string code, Type exceptionType) + { + var subject = new Neo4jExceptionFactory(); + var exception = subject.GetException(code, "test message"); + exception.Should().BeOfType(exceptionType); + exception.Code.Should().Be(code); + exception.Message.Should().Be("test message"); + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/AuthenticationException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/AuthenticationException.cs new file mode 100644 index 000000000..6d7844ebf --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/AuthenticationException.cs @@ -0,0 +1,38 @@ +// 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. + +using System.Runtime.Serialization; +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// Failed to authenticate the client to the server due to bad credentials +/// To recover from this error, close the current driver and restart with the correct credentials +/// +[DataContract] +[ErrorCode("Neo.ClientError.Security.Unauthorized")] +public class AuthenticationException : SecurityException +{ + /// + /// Create a new with an error message. + /// + /// The error message. + public AuthenticationException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/AuthorizationException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/AuthorizationException.cs new file mode 100644 index 000000000..e9f57c24c --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/AuthorizationException.cs @@ -0,0 +1,37 @@ +// 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. + +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// The authorization information maintained on the server has expired. The client should reconnect. +/// +[ErrorCode("Neo.ClientError.Security.AuthorizationExpired")] +public class AuthorizationException : SecurityException +{ + public override bool IsRetriable => true; + + /// + /// Create a new with an error message. + /// + /// The error message. + public AuthorizationException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/ClientException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/ClientException.cs new file mode 100644 index 000000000..47429a4bb --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/ClientException.cs @@ -0,0 +1,76 @@ +// 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. + +using System; +using System.Runtime.Serialization; +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// A indicates that the client has carried out an operation incorrectly. +/// The error code provided can be used to determine further detail for the problem. +/// +[DataContract] +[ErrorCode("Neo.ClientError.*")] +public class ClientException : Neo4jException +{ + /// + /// Create a new . + /// + public ClientException() + { + } + + /// + /// Create a new with an error message. + /// + /// The error message. + public ClientException(string message) : base(message) + { + } + + /// + /// Create a new with an error code and an error message. + /// + /// The error code. + /// The error message. + public ClientException(string code, string message) : base(code, message) + { + } + + /// + /// Create a new with an error message and an exception. + /// + /// The error message. + /// The inner exception. + public ClientException(string message, Exception innerException) + : base(message, innerException) + { + } + + /// + /// Create a new with an error code, an error message and an exception. + /// + /// The error code. + /// The error message. + /// The inner exception. + public ClientException(string code, string message, Exception innerException) + : base(code, message, innerException) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/ConnectionReadTimeoutException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/ConnectionReadTimeoutException.cs new file mode 100644 index 000000000..4cc4d5d94 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/ConnectionReadTimeoutException.cs @@ -0,0 +1,47 @@ +// 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. + +using System; +using System.Runtime.Serialization; + +namespace Neo4j.Driver; + +/// +/// A indicates that the driver timed out trying to read from the network socket. +/// +[DataContract] +public class ConnectionReadTimeoutException : Neo4jException +{ + public override bool IsRetriable => true; + + /// + /// Create a new with an error message. + /// + /// The error message. + public ConnectionReadTimeoutException(string message) : base(message) + { + } + + /// + /// Create a new with an error message and an exception. + /// + /// The error message. + /// The inner exception. + public ConnectionReadTimeoutException(string message, Exception innerException) : base(message, innerException) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/DatabaseException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/DatabaseException.cs new file mode 100644 index 000000000..18e00920a --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/DatabaseException.cs @@ -0,0 +1,66 @@ +// 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. + +using System; +using System.Runtime.Serialization; +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// A indicates that there is a problem within the underlying database. +/// The error code provided can be used to determine further detail for the problem. +/// +[DataContract] +[ErrorCode("*")] // mop up any errors that haven't already been handled +public class DatabaseException : Neo4jException +{ + /// + /// Create a new . + /// + public DatabaseException() + { + } + + /// + /// Create a new with an error error message. + /// + /// The error message. + public DatabaseException(string message) : base(string.Empty, message) + { + } + + /// + /// Create a new with an error code and an error message. + /// + /// The error code. + /// The error message. + public DatabaseException(string code, string message) : base(code, message) + { + } + + /// + /// Create a new with an error code, an error message and an exception. + /// + /// The error code. + /// The error message. + /// The inner exception which caused this error. + public DatabaseException(string code, string message, Exception innerException) + : base(code, message, innerException) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/FatalDiscoveryException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/FatalDiscoveryException.cs new file mode 100644 index 000000000..fe530d48f --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/FatalDiscoveryException.cs @@ -0,0 +1,39 @@ +// 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. + +using System.Runtime.Serialization; +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// There was an error that points us to a fatal problem for routing table discovery, like the requested database +/// could not be found. This kind of errors are identified as non-transient and are not retried. +/// +[DataContract] +[ErrorCode("Neo.ClientError.Database.DatabaseNotFound")] +public class FatalDiscoveryException : ClientException +{ + /// + /// Create a new with an error code and an error message. + /// + /// The error message. + public FatalDiscoveryException(string message) + : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/ForbiddenException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/ForbiddenException.cs new file mode 100644 index 000000000..889b2bed4 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/ForbiddenException.cs @@ -0,0 +1,37 @@ +// 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. + +using System.Runtime.Serialization; +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// This operation is forbidden. +/// +[DataContract] +[ErrorCode("Neo.ClientError.Security.Forbidden")] +public class ForbiddenException : SecurityException +{ + /// + /// Create a new with an error message. + /// + /// The error message. + public ForbiddenException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/InvalidBookmarkException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/InvalidBookmarkException.cs new file mode 100644 index 000000000..440621379 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/InvalidBookmarkException.cs @@ -0,0 +1,35 @@ +// 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. + +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// The provided bookmark is invalid. To recover from this a new session needs to be created. +/// +[ErrorCode("Neo.ClientError.Transaction.InvalidBookmark")] +public class InvalidBookmarkException : ClientException +{ + /// + /// Create a new with an error message. + /// + /// The error message. + public InvalidBookmarkException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/InvalidBookmarkMixtureException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/InvalidBookmarkMixtureException.cs new file mode 100644 index 000000000..26a5945e9 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/InvalidBookmarkMixtureException.cs @@ -0,0 +1,35 @@ +// 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. + +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// The provided bookmark is invalid. To recover from this a new session needs to be created. +/// +[ErrorCode("Neo.ClientError.Transaction.InvalidBookmarkMixture")] +public class InvalidBookmarkMixtureException : ClientException +{ + /// + /// Create a new with an error message. + /// + /// The error message. + public InvalidBookmarkMixtureException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/Neo4jException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/Neo4jException.cs new file mode 100644 index 000000000..db95b3920 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/Neo4jException.cs @@ -0,0 +1,87 @@ +// 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. + +using System; +using System.Runtime.Serialization; + +namespace Neo4j.Driver +{ + /// + /// The base class for all Neo4j exceptions. + /// + [DataContract] + public class Neo4jException : Exception + { + /// + /// Create a new + /// + public Neo4jException() + { + } + + /// + /// Create a new with an error message + /// + /// The error message. + public Neo4jException(string message) : this(null, message) + { + } + + /// + /// Create a new with an error code and an error message + /// + /// The error code. + /// The error message + public Neo4jException(string code, string message) + : base(message) + { + Code = code; + } + + /// + /// Create a new with an error message and an exception. + /// + /// The error message. + /// The inner exception + public Neo4jException(string message, Exception innerException) + : this(null, message, innerException) + { + } + + /// + /// Create a new with an error code, an error message and an exception. + /// + /// The error code. + /// The error message. + /// The inner exception. + public Neo4jException(string code, string message, Exception innerException) + : base(message, innerException) + { + Code = code; + } + + /// + /// Gets whether the exception retriable or not. + /// + public virtual bool IsRetriable => false; + + /// + /// Gets or sets the code of a Neo4j exception. + /// + public string Code { get; set; } + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/ProtocolException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/ProtocolException.cs new file mode 100644 index 000000000..83dfc600d --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/ProtocolException.cs @@ -0,0 +1,38 @@ +// 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. + +using System.Runtime.Serialization; +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// There was a bolt protocol violation of the contract between the driver and the server. +/// When seen this error, contact driver developers. +/// +[DataContract] +[ErrorCode("Neo.ClientError.Request.Invalid*")] +public class ProtocolException : Neo4jException +{ + /// + /// Create a new with an error message. + /// + /// The error message. + public ProtocolException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/ResultConsumedException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/ResultConsumedException.cs new file mode 100644 index 000000000..b737bfd4e --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/ResultConsumedException.cs @@ -0,0 +1,37 @@ +// 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. + +using System.Runtime.Serialization; + +namespace Neo4j.Driver; + +/// +/// The result has already been consumed either by explicit consume call, +/// or by termination of session or transaction where the result was obtained. +/// Once a result is consumed, the records in the result is not accessible anymore. +/// +[DataContract] +public class ResultConsumedException : ClientException +{ + /// + /// Create a new with an error message + /// + /// The error message + public ResultConsumedException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/SecurityException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/SecurityException.cs new file mode 100644 index 000000000..5f606eae2 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/SecurityException.cs @@ -0,0 +1,64 @@ +// 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. + +using System; +using System.Runtime.Serialization; + +namespace Neo4j.Driver; + +/// +/// Failed to connect the driver to the server due to security errors +/// When this type of error happens, recreation of the driver might be required. +/// +[DataContract] +public class SecurityException : Neo4jException +{ + internal bool Notified = false; + internal bool Retriable = false; + + /// + /// Create a new with an error message. + /// + /// The error message. + public SecurityException(string message) : base(message) + { + } + + /// + /// Create a new with an error code and an error message. + /// + /// The error code. + /// The error message. + public SecurityException(string code, string message) : base(code, message) + { + } + + /// + /// Create a new with an error message and an exception. + /// + /// The error message. + /// The inner exception. + public SecurityException(string message, Exception innerException) : base(message, innerException) + { + } + + /// + /// Whether or not the exception is retriable. If the exception is retriable, the driver will try to + /// re-run the operation that caused the exception. + /// + public override bool IsRetriable => Retriable; +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/ServiceUnavailableException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/ServiceUnavailableException.cs new file mode 100644 index 000000000..a5b9b5af4 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/ServiceUnavailableException.cs @@ -0,0 +1,47 @@ +// 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. + +using System; +using System.Runtime.Serialization; + +namespace Neo4j.Driver; + +/// +/// A indicates that the driver cannot communicate with the cluster. +/// +[DataContract] +public class ServiceUnavailableException : Neo4jException +{ + public override bool IsRetriable => true; + + /// + /// Create a new with an error message. + /// + /// The error message. + public ServiceUnavailableException(string message) : base(message) + { + } + + /// + /// Create a new with an error message and an exception. + /// + /// The error message. + /// The inner exception. + public ServiceUnavailableException(string message, Exception innerException) : base(message, innerException) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/SessionExpiredException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/SessionExpiredException.cs new file mode 100644 index 000000000..997fef169 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/SessionExpiredException.cs @@ -0,0 +1,50 @@ +// 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. + +using System; +using System.Runtime.Serialization; + +namespace Neo4j.Driver; + +/// +/// A indicates that the session can no longer satisfy the criteria under which it was acquired, +/// e.g. a server no longer accepts write requests. +/// +/// A new session needs to be acquired from the driver and all actions taken on the expired session must be replayed. +/// +[DataContract] +public class SessionExpiredException : Neo4jException +{ + public override bool IsRetriable => true; + + /// + /// Create a new with an error message. + /// + /// The error message. + public SessionExpiredException(string message) : base(message) + { + } + + /// + /// Create a new with an error message and an exception. + /// + /// The error message. + /// The inner exception. + public SessionExpiredException(string message, Exception innerException) : base(message, innerException) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/StatementArgumentException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/StatementArgumentException.cs new file mode 100644 index 000000000..115e27ecb --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/StatementArgumentException.cs @@ -0,0 +1,38 @@ +// 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. + +using System; +using System.Runtime.Serialization; +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// A generic argument error has occurred. To recover from this a new session needs to be created. +/// +[DataContract] +[ErrorCode("Neo.ClientError.Statement.ArgumentError")] +public class StatementArgumentException : ClientException +{ + /// + /// Create a new with an error message. + /// + /// The error message. + public StatementArgumentException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/TokenExpiredException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/TokenExpiredException.cs new file mode 100644 index 000000000..1eb97723b --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/TokenExpiredException.cs @@ -0,0 +1,37 @@ +// 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. + +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// The provided token has expired. The current driver instance is considered invalid. It should not +/// be used anymore. The client must create a new driver instance with a valid token. +/// +[ErrorCode("Neo.ClientError.Security.TokenExpired")] +public class TokenExpiredException : SecurityException +{ + /// + /// Create a new with an error message. + /// + /// The error message. + public TokenExpiredException(string message) : base(message) + { + + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/TransactionClosedException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/TransactionClosedException.cs new file mode 100644 index 000000000..28738f327 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/TransactionClosedException.cs @@ -0,0 +1,36 @@ +// 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. + +using System.Runtime.Serialization; + +namespace Neo4j.Driver; + +/// +/// The exception that is thrown when calling or +/// on an that has already been closed. +/// +[DataContract] +public class TransactionClosedException : ClientException +{ + /// + /// Create a new with an error message. + /// + /// The error message + public TransactionClosedException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/TransactionNestingException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/TransactionNestingException.cs new file mode 100644 index 000000000..ec40badc5 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/TransactionNestingException.cs @@ -0,0 +1,37 @@ +// 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. + +using System.Runtime.Serialization; + +namespace Neo4j.Driver; + +/// +/// An attempt to BeginTransaction has been made before the sessions existing transaction +/// has been consumed or rolled back. e.g. An attempt to nest transactions has occurred. +/// A session can only have a single transaction at a time. +/// +[DataContract] +public class TransactionNestingException : ClientException +{ + /// + /// Create a new with an error message + /// + /// The error message + public TransactionNestingException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/TransactionTerminatedException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/TransactionTerminatedException.cs new file mode 100644 index 000000000..dd621af6e --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/TransactionTerminatedException.cs @@ -0,0 +1,20 @@ +using System; +using System.Runtime.Serialization; + +namespace Neo4j.Driver; + +/// +/// The exception that is thrown when trying to further interact with a terminated transaction. +/// Transactions are terminated when they incur errors.
+/// If created by the driver the will be null. +///
+[DataContract] +public sealed class TransactionTerminatedException : ClientException +{ + public override bool IsRetriable => (InnerException as Neo4jException)?.IsRetriable ?? false; + + internal TransactionTerminatedException(Exception inner) : + base((inner as Neo4jException)?.Code, inner.Message, inner) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/TransientException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/TransientException.cs new file mode 100644 index 000000000..3b9593fc2 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/TransientException.cs @@ -0,0 +1,62 @@ +// 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. + +using System; +using System.Runtime.Serialization; +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// A signals a failed operation that may be able to succeed +/// if this operation is retried without any intervention by application-level functionality. +/// The error code provided can be used to determine further details for the problem. +/// +[DataContract] +[ErrorCode("Neo.TransientError.*")] +public class TransientException : Neo4jException +{ + /// + public override bool IsRetriable => true; + + /// + /// Create a new . + /// + public TransientException() + { + } + + /// + /// Create a new with an error code and an error message. + /// + /// The error code. + /// The error message. + public TransientException(string code, string message) : base(code, message) + { + } + + /// + /// Create a new with an error code, an error message and an exception. + /// + /// The error code. + /// The error message. + /// The inner exception which caused this error. + public TransientException(string code, string message, Exception innerException) + : base(code, message, innerException) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/TypeException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/TypeException.cs new file mode 100644 index 000000000..ed906bedc --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/TypeException.cs @@ -0,0 +1,37 @@ +// 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. + +using System.Runtime.Serialization; +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// An error occurred related to data typing. +/// +[DataContract] +[ErrorCode("Neo.ClientError.Statement.TypeError")] +public class TypeException : ClientException +{ + /// + /// Create a new with an error message. + /// + /// The error message. + public TypeException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/UnknownSecurityException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/UnknownSecurityException.cs new file mode 100644 index 000000000..7dd549279 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/UnknownSecurityException.cs @@ -0,0 +1,37 @@ +// 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. + +using System.Runtime.Serialization; +using Neo4j.Driver.Internal.ExceptionHandling; + +namespace Neo4j.Driver; + +/// +/// An unknown security error occurred. +/// +[DataContract] +[ErrorCode("Neo.ClientError.Security.*")] +public class UnknownSecurityException : SecurityException +{ + /// + /// Create a new with an error message. + /// + /// The error message. + public UnknownSecurityException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/UnsupportedFeatureException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/UnsupportedFeatureException.cs new file mode 100644 index 000000000..793e9a32d --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/UnsupportedFeatureException.cs @@ -0,0 +1,22 @@ +using System.Runtime.Serialization; + +namespace Neo4j.Driver; + +/// +/// The exception that is thrown when calling an operation in the driver which uses a server feature that is not +/// available on the connected server version. +/// +[DataContract] +public class UnsupportedFeatureException : ClientException +{ + /// + public override bool IsRetriable => false; + + /// + /// Creates a new with an error message. + /// + /// The error message + internal UnsupportedFeatureException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/ValueOverflowException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/ValueOverflowException.cs new file mode 100644 index 000000000..3d6f77fe2 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/ValueOverflowException.cs @@ -0,0 +1,36 @@ +// 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. + +using System.Runtime.Serialization; + +namespace Neo4j.Driver; + +/// +/// A value retrieved from the database cannot be represented with the type to be converted, and will +/// cause working with a modified data. +/// +[DataContract] +public class ValueOverflowException : ClientException +{ + /// + /// Create a new with an error message. + /// + /// The error message. + public ValueOverflowException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Exceptions/ValueTruncationException.cs b/Neo4j.Driver/Neo4j.Driver/Exceptions/ValueTruncationException.cs new file mode 100644 index 000000000..7dfc57759 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Exceptions/ValueTruncationException.cs @@ -0,0 +1,36 @@ +// 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. + +using System.Runtime.Serialization; + +namespace Neo4j.Driver; + +/// +/// A value retrieved from the database needs to be truncated for this conversion to work, and will +/// cause working with a modified data. +/// +[DataContract] +public class ValueTruncationException : ClientException +{ + /// + /// Create a new with an error message. + /// + /// The error message. + public ValueTruncationException(string message) : base(message) + { + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/ExceptionHandling/ErrorCodeAttribute.cs b/Neo4j.Driver/Neo4j.Driver/Internal/ExceptionHandling/ErrorCodeAttribute.cs new file mode 100644 index 000000000..c117830a2 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Internal/ExceptionHandling/ErrorCodeAttribute.cs @@ -0,0 +1,39 @@ +// 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. + +using System; + +namespace Neo4j.Driver.Internal.ExceptionHandling; + +/// +/// Use this attribute to decorate an exception class to declare that the class +/// is the correct class to create when an error with the specified code is raised. +/// +[AttributeUsage(AttributeTargets.Class, Inherited = false)] +internal class ErrorCodeAttribute : Attribute +{ + public string Code { get; } + + /// + /// Creates a new instance of the class. + /// + /// The error code that the decorated class is the exception for. + public ErrorCodeAttribute(string code) + { + Code = code; + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/ExceptionHandling/Neo4jExceptionFactory.cs b/Neo4j.Driver/Neo4j.Driver/Internal/ExceptionHandling/Neo4jExceptionFactory.cs new file mode 100644 index 000000000..5f1211049 --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Internal/ExceptionHandling/Neo4jExceptionFactory.cs @@ -0,0 +1,120 @@ +// 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. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Neo4j.Driver.Internal.ExceptionHandling; + +internal class Neo4jExceptionFactory +{ + private record FactoryInfo(string Code, Func ExceptionFactory); + private readonly List _exceptionFactories = new(); + private readonly SimpleWildcardHelper _simpleWildcardHelper = new(); + + public Neo4jExceptionFactory() + { + // get all the types + var codesAndExceptions = GetCodesAndExceptions(); + codesAndExceptions.Sort(CompareByCode); + BuildExceptionFactories(codesAndExceptions); + } + + private static List<(string code, Type exceptionType)> GetCodesAndExceptions() + { + var exceptionTypes = GetAllNeo4jExceptions(); + + return exceptionTypes + .Select( + exceptionType => new + { + exceptionType, + attr = exceptionType.GetCustomAttribute() + }) + .Where(t => t.attr is not null) + .Select(t => (t.attr.Code, t.exceptionType)) + .ToList(); + } + + private static IEnumerable GetAllNeo4jExceptions() + { + var type = typeof(Neo4jException); + var assembly = type.Assembly; + var types = assembly.GetExportedTypes().Where(t => type.IsAssignableFrom(t)); + return types; + } + + private int CompareByCode((string code, Type exceptionType) x, (string code, Type exceptionType) y) + { + // x comes before y if y matches x - this would happen if: + // x = Error.Specific + // y = Error.* + // this means that less-specific wildcards are at the end of the list, so the first + // matching wildcard will always be the most specific + + if (_simpleWildcardHelper.StringMatches(x.code, y.code)) + { + return -1; + } + + if (_simpleWildcardHelper.StringMatches(y.code, x.code)) + { + return 1; + } + + return string.Compare(x.code, y.code, StringComparison.InvariantCultureIgnoreCase); + } + + private void BuildExceptionFactories(IEnumerable<(string code, Type exceptionType)> codesAndExceptions) + { + foreach (var (code, type) in codesAndExceptions) + { + Func factory; + if (type.GetConstructor(new[] { typeof(string), typeof(string), typeof(Exception) }) is {} threeParamCtr) + { + factory = (c, m, x) => (Neo4jException)threeParamCtr.Invoke(new object[] { c, m, x }); + } + else if (type.GetConstructor(new[] { typeof(string), typeof(Exception) }) is {} twoParamCtr) + { + factory = (_, m, x) => (Neo4jException)twoParamCtr.Invoke(new object[] { m, x }); + } + else if (type.GetConstructor(new[] { typeof(string) }) is {} oneParamCtr) + { + factory = (_, m, _) => (Neo4jException)oneParamCtr.Invoke(new object[] { m }); + } + else + { + continue; + } + + _exceptionFactories.Add(new FactoryInfo(code, factory)); + } + } + + public Neo4jException GetException(string code, string message, Exception innerException = null) + { + var factoryInfo = _exceptionFactories.FirstOrDefault(f => _simpleWildcardHelper.StringMatches(code, f.Code)); + var exception = factoryInfo is null + ? new Neo4jException(code, message, innerException) + : factoryInfo.ExceptionFactory(code, message, innerException); + + exception.Code = code; + return exception; + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Extensions/ErrorExtensions.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Extensions/ErrorExtensions.cs index 38949b733..7cb44615b 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Extensions/ErrorExtensions.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Extensions/ErrorExtensions.cs @@ -16,104 +16,38 @@ using System; using System.IO; using System.Net.Sockets; +using Neo4j.Driver.Internal.ExceptionHandling; namespace Neo4j.Driver.Internal; internal static class ErrorExtensions { + private static Neo4jExceptionFactory _exceptionFactory = new(); + public static Neo4jException ParseServerException(string code, string message) { - Neo4jException error; - var parts = code.Split('.'); - var classification = parts[1].ToLowerInvariant(); - switch (classification) - { - case "clienterror": - if (AuthenticationException.IsAuthenticationError(code)) - { - error = new AuthenticationException(message); - } - else if (AuthorizationException.IsAuthorizationError(code)) - { - error = new AuthorizationException(message); - } - else if (ProtocolException.IsProtocolError(code)) - { - error = new ProtocolException(code, message); - } - else if (FatalDiscoveryException.IsFatalDiscoveryError(code)) - { - error = new FatalDiscoveryException(message); - } - else if (TokenExpiredException.IsTokenExpiredError(code)) - { - error = new TokenExpiredException(message); - } - else if (InvalidBookmarkException.IsInvalidBookmarkException(code)) - { - error = new InvalidBookmarkException(message); - } - else if (InvalidBookmarkMixtureException.IsInvalidBookmarkMixtureException(code)) - { - error = new InvalidBookmarkMixtureException(message); - } - else if (ArgumentErrorException.IsArgumentErrorException(code)) - { - error = new ArgumentErrorException(message); - } - else if (TypeException.IsTypeException(code)) - { - error = new TypeException(message); - } - else if (ForbiddenException.IsForbiddenException(code)) - { - error = new ForbiddenException(message); - } - // this one needs to come after it has checked all other possibilities - else if (UnknownSecurityException.IsUnknownSecurityException(code)) - { - return new UnknownSecurityException(message, code); - } - else - { - error = new ClientException(code, message); - } - - break; - - case "transienterror": - error = new TransientException(code, message); - break; - - default: - error = new DatabaseException(code, message); - break; - } - - return error; + return _exceptionFactory.GetException(code, message); } public static bool CanBeRetried(this Exception error) { - return error is Neo4jException neo4JException && neo4JException.IsRetriable; + return error is Neo4jException { IsRetriable: true }; } public static bool IsRecoverableError(this Exception error) { - return error is ClientException || error is TransientException; + return error is ClientException or TransientException; } public static bool IsConnectionError(this Exception error) { - return error is IOException || - error is SocketException || - error.GetBaseException() is IOException || - error.GetBaseException() is SocketException; + return error is IOException or SocketException || + error.GetBaseException() is IOException or SocketException; } - public static bool IsAuthorizationError(this Exception error) + public static bool HasErrorCode(this Exception error, string errorCode) { - return error is AuthorizationException; + return error is Neo4jException neo4jException && neo4jException.Code == errorCode; } public static bool IsDatabaseUnavailableError(this Exception error) @@ -136,12 +70,6 @@ private static bool IsForbiddenOnReadOnlyDatabaseError(this Exception error) return error.HasErrorCode("Neo.ClientError.General.ForbiddenOnReadOnlyDatabase"); } - private static bool HasErrorCode(this Exception error, string code) - { - var exception = error as Neo4jException; - return exception?.Code != null && exception.Code.Equals(code); - } - public static ResultConsumedException NewResultConsumedException() { return new ResultConsumedException( diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Helpers/SimpleWildcardHelper.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Helpers/SimpleWildcardHelper.cs new file mode 100644 index 000000000..db65436dd --- /dev/null +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Helpers/SimpleWildcardHelper.cs @@ -0,0 +1,40 @@ +// 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. + +using System.Collections.Generic; + +namespace Neo4j.Driver.Internal; + +internal class SimpleWildcardHelper +{ + /// + /// Returns true if the two strings are the same, or, if ends with an asterisk (*), + /// returns true if starts with (minus the asterisk). + /// + /// The string to check. + /// The (potential) wildcard to compare with + /// True if the strings match; false otherwise. + public bool StringMatches(string x, string y) + { + if (!y.EndsWith("*")) + { + return x == y; + } + + return x.StartsWith(y.Substring(0, y.Length - 1)); + } +} diff --git a/Neo4j.Driver/Neo4j.Driver/Internal/Routing/RoutingTableManager.cs b/Neo4j.Driver/Neo4j.Driver/Internal/Routing/RoutingTableManager.cs index bfd438157..a5910906c 100644 --- a/Neo4j.Driver/Neo4j.Driver/Internal/Routing/RoutingTableManager.cs +++ b/Neo4j.Driver/Neo4j.Driver/Internal/Routing/RoutingTableManager.cs @@ -371,7 +371,7 @@ private static bool IsFailFastException(Exception ex) is (FatalDiscoveryException // Neo.ClientError.Database.DatabaseNotFound or InvalidBookmarkException // Neo.ClientError.Transaction.InvalidBookmark or InvalidBookmarkMixtureException // Neo.ClientError.Transaction.InvalidBookmarkMixture - or ArgumentErrorException // Neo.ClientError.Statement.ArgumentError + or StatementArgumentException // Neo.ClientError.Statement.ArgumentError or ProtocolException // Neo.ClientError.Request.Invalid and (special to .NET driver) Neo.ClientError.Request.InvalidFormat or TypeException // Neo.ClientError.Statement.TypeError or SecurityException // Neo.ClientError.Security.* diff --git a/Neo4j.Driver/Neo4j.Driver/Neo4jException.cs b/Neo4j.Driver/Neo4j.Driver/Neo4jException.cs deleted file mode 100644 index fbe463312..000000000 --- a/Neo4j.Driver/Neo4j.Driver/Neo4jException.cs +++ /dev/null @@ -1,619 +0,0 @@ -// Copyright (c) "Neo4j" -// Neo4j Sweden AB [https://neo4j.com] -// -// 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. - -using System; -using System.Runtime.Serialization; - -namespace Neo4j.Driver; - -/// The base class for all Neo4j exceptions. -[DataContract] -public class Neo4jException : Exception -{ - /// Create a new - public Neo4jException() - { - } - - /// Create a new with an error message - /// The error message. - public Neo4jException(string message) : this(null, message) - { - } - - /// Create a new with an error code and an error message - /// The error code. - /// The error message - public Neo4jException(string code, string message) - : base(message) - { - Code = code; - } - - /// Create a new with an error message and an exception. - /// The error message. - /// The inner exception - public Neo4jException(string message, Exception innerException) - : this(null, message, innerException) - { - } - - /// Create a new with an error code, an error message and an exception. - /// The error code. - /// The error message. - /// The inner exception. - public Neo4jException(string code, string message, Exception innerException) - : base(message, innerException) - { - Code = code; - } - - /// Gets whether the exception retriable or not. - public virtual bool IsRetriable => false; - - /// Gets or sets the code of a Neo4j exception. - public string Code { get; set; } -} - -/// -/// A indicates that the client has carried out an operation incorrectly. The error -/// code provided can be used to determine further detail for the problem. -/// -[DataContract] -public class ClientException : Neo4jException -{ - /// Create a new . - public ClientException() - { - } - - /// Create a new with an error message. - /// The error message. - public ClientException(string message) : base(message) - { - } - - /// Create a new with an error code and an error message. - /// The error code. - /// The error message. - public ClientException(string code, string message) : base(code, message) - { - } - - /// Create a new with an error message and an exception. - /// The error message. - /// The inner exception. - public ClientException(string message, Exception innerException) - : base(message, innerException) - { - } - - /// Create a new with an error code, an error message and an exception. - /// The error code. - /// The error message. - /// The inner exception. - public ClientException(string code, string message, Exception innerException) - : base(code, message, innerException) - { - } -} - -/// -/// A signals a failed operation that may be able to succeed if this operation is -/// retried without any intervention by application-level functionality. The error code provided can be used to determine -/// further details for the problem. -/// -[DataContract] -public class TransientException : Neo4jException -{ - /// Create a new . - public TransientException() - { - } - - /// Create a new with an error code and an error message. - /// The error code. - /// The error message. - public TransientException(string code, string message) : base(code, message) - { - } - - /// Create a new with an error code, an error message and an exception. - /// The error code. - /// The error message. - /// The inner exception which caused this error. - public TransientException(string code, string message, Exception innerException) - : base(code, message, innerException) - { - } - - /// - public override bool IsRetriable => true; -} - -/// -/// A indicates that there is a problem within the underlying database. The error -/// code provided can be used to determine further detail for the problem. -/// -[DataContract] -public class DatabaseException : Neo4jException -{ - /// Create a new . - public DatabaseException() - { - } - - /// Create a new with an error error message. - /// The error message. - public DatabaseException(string message) : base(string.Empty, message) - { - } - - /// Create a new with an error code and an error message. - /// The error code. - /// The error message. - public DatabaseException(string code, string message) : base(code, message) - { - } - - /// Create a new with an error code, an error message and an exception. - /// The error code. - /// The error message. - /// The inner exception which caused this error. - public DatabaseException(string code, string message, Exception innerException) - : base(code, message, innerException) - { - } -} - -/// A indicates that the driver cannot communicate with the cluster. -[DataContract] -public class ServiceUnavailableException : Neo4jException -{ - /// Create a new with an error message. - /// The error message. - public ServiceUnavailableException(string message) : base(message) - { - } - - /// Create a new with an error message and an exception. - /// The error message. - /// The inner exception. - public ServiceUnavailableException(string message, Exception innerException) : base(message, innerException) - { - } - - /// - public override bool IsRetriable => true; -} - -/// -/// A indicates that the session can no longer satisfy the criteria under -/// which it was acquired, e.g. a server no longer accepts write requests. A new session needs to be acquired from the -/// driver and all actions taken on the expired session must be replayed. -/// -[DataContract] -public class SessionExpiredException : Neo4jException -{ - /// Create a new with an error message. - /// The error message. - public SessionExpiredException(string message) : base(message) - { - } - - /// Create a new with an error message and an exception. - /// The error message. - /// The inner exception. - public SessionExpiredException(string message, Exception innerException) : base(message, innerException) - { - } - - /// - public override bool IsRetriable => true; -} - -/// -/// A indicates that the driver timed out trying to read from the -/// network socket. -/// -[DataContract] -public class ConnectionReadTimeoutException : Neo4jException -{ - /// Create a new with an error message. - /// The error message. - public ConnectionReadTimeoutException(string message) : base(message) - { - } - - /// Create a new with an error message and an exception. - /// The error message. - /// The inner exception. - public ConnectionReadTimeoutException(string message, Exception innerException) : base(message, innerException) - { - } - - /// - public override bool IsRetriable => true; -} - -/// -/// There was a bolt protocol violation of the contract between the driver and the server. When seen this error, -/// contact driver developers. -/// -[DataContract] -public class ProtocolException : Neo4jException -{ - private const string ErrorCodeInvalid = "Neo.ClientError.Request.Invalid"; - private const string ErrorCodeInvalidFormat = "Neo.ClientError.Request.InvalidFormat"; - - /// Create a new with an error message. - /// The error message. - public ProtocolException(string message) : base(message) - { - } - - /// Create a new with an error code and an error message. - /// The error code. - /// The error message. - public ProtocolException(string code, string message) : base(code, message) - { - } - - /// Create a new with an error message and an exception. - /// The error message. - /// The inner exception. - public ProtocolException(string message, Exception innerException) : base(message, innerException) - { - } - - internal static bool IsProtocolError(string code) - { - return code.Equals(ErrorCodeInvalid) || code.Equals(ErrorCodeInvalidFormat); - } -} - -/// -/// Failed to connect the driver to the server due to security errors When this type of error happens, recreation -/// of the driver might be required. -/// -[DataContract] -public class SecurityException : Neo4jException -{ - internal bool Notified = false; - internal bool Retriable = false; - - /// Create a new with an error message. - /// The error message. - public SecurityException(string message) : base(message) - { - } - - /// Create a new with an error code and an error message. - /// The error code. - /// The error message. - public SecurityException(string code, string message) : base(code, message) - { - } - - /// Create a new with an error message and an exception. - /// The error message. - /// The inner exception. - public SecurityException(string message, Exception innerException) : base(message, innerException) - { - } - - /// - /// Whether or not the exception is retriable. If the exception is retriable, the driver will try to - /// re-run the operation that caused the exception. - /// - public override bool IsRetriable => Retriable; -} - -/// -/// Failed to authentication the client to the server due to bad credentials To recover from this error, close the -/// current driver and restart with the correct credentials -/// -[DataContract] -public class AuthenticationException : SecurityException -{ - private const string ErrorCode = "Neo.ClientError.Security.Unauthorized"; - - /// Create a new with an error message. - /// The error message. - public AuthenticationException(string message) : base(ErrorCode, message) - { - } - - internal static bool IsAuthenticationError(string code) - { - return code.Equals(ErrorCode); - } -} - -/// The authorization information maintained on the server has expired. The client should reconnect. -public class AuthorizationException : SecurityException -{ - private const string ErrorCode = "Neo.ClientError.Security.AuthorizationExpired"; - - /// Create a new with an error message. - /// The error message. - public AuthorizationException(string message) : base(ErrorCode, message) - { - } - - /// - public override bool IsRetriable => true; - - internal static bool IsAuthorizationError(string code) - { - return code.Equals(ErrorCode); - } -} - -/// -/// The provided token has expired. The current driver instance is considered invalid. It should not be used -/// anymore. The client must create a new driver instance with a valid token. -/// -public class TokenExpiredException : SecurityException -{ - private const string ErrorCode = "Neo.ClientError.Security.TokenExpired"; - - /// Create a new with an error message. - /// The error message. - public TokenExpiredException(string message) : base(ErrorCode, message) - { - } - - internal static bool IsTokenExpiredError(string code) - { - return string.Equals(code, ErrorCode); - } -} - -/// The provided bookmark is invalid. To recover from this a new session needs to be created. -public class InvalidBookmarkException : ClientException -{ - private const string ErrorCode = "Neo.ClientError.Transaction.InvalidBookmark"; - - /// Create a new with an error message. - /// The error message. - public InvalidBookmarkException(string message) : base(ErrorCode, message) - { - } - - internal static bool IsInvalidBookmarkException(string code) - { - return string.Equals(code, ErrorCode); - } -} - -/// The provided bookmark is invalid. To recover from this a new session needs to be created. -public class InvalidBookmarkMixtureException : ClientException -{ - private const string ErrorCode = "Neo.ClientError.Transaction.InvalidBookmarkMixture"; - - /// Create a new with an error message. - /// The error message. - public InvalidBookmarkMixtureException(string message) : base(ErrorCode, message) - { - } - - internal static bool IsInvalidBookmarkMixtureException(string code) - { - return string.Equals(code, ErrorCode); - } -} - -/// A generic argument error has occurred. To recover from this a new session needs to be created. -[DataContract] -public class ArgumentErrorException : ClientException -{ - private const string ErrorCode = "Neo.ClientError.Statement.ArgumentError"; - - /// Create a new with an error message. - /// The error message. - public ArgumentErrorException(string message) : base(ErrorCode, message) - { - } - - internal static bool IsArgumentErrorException(string code) - { - return string.Equals(code, ErrorCode); - } -} - -/// An error occurred related to data typing. -[DataContract] -public class TypeException : ClientException -{ - private const string ErrorCode = "Neo.ClientError.Statement.TypeError"; - - /// Create a new with an error message. - /// The error message. - public TypeException(string message) : base(ErrorCode, message) - { - } - - internal static bool IsTypeException(string code) - { - return string.Equals(code, ErrorCode); - } -} - -/// This operation is forbidden. -[DataContract] -public class ForbiddenException : SecurityException -{ - private const string ErrorCode = "Neo.ClientError.Security.Forbidden"; - - /// Create a new with an error message. - /// The error message. - public ForbiddenException(string message) : base(ErrorCode, message) - { - } - - internal static bool IsForbiddenException(string code) - { - return string.Equals(code, ErrorCode); - } -} - -/// An unknown security error occurred. -[DataContract] -public class UnknownSecurityException : SecurityException -{ - private const string ErrorCodePrefix = "Neo.ClientError.Security."; - - /// Create a new with an error message. - /// The error message. - /// The error code. - public UnknownSecurityException(string message, string code) : base($"{ErrorCodePrefix}*", message) - { - Code = code; - } - - internal static bool IsUnknownSecurityException(string code) - { - return code.StartsWith(ErrorCodePrefix); - } -} - -/// -/// A value retrieved from the database needs to be truncated for this conversion to work, and will cause working -/// with a modified data. -/// -[DataContract] -public class ValueTruncationException : ClientException -{ - /// Create a new with an error message. - /// The error message. - public ValueTruncationException(string message) : base(message) - { - } -} - -/// -/// A value retrieved from the database cannot be represented with the type to be converted, and will cause -/// working with a modified data. -/// -[DataContract] -public class ValueOverflowException : ClientException -{ - /// Create a new with an error message. - /// The error message. - public ValueOverflowException(string message) : base(message) - { - } -} - -/// -/// There was an error that points us to a fatal problem for routing table discovery, like the requested database -/// could not be found. This kind of errors are identified as non-transient and are not retried. -/// -[DataContract] -public class FatalDiscoveryException : ClientException -{ - private const string ErrorCode = "Neo.ClientError.Database.DatabaseNotFound"; - - /// Create a new with an error code and an error message. - /// The error message. - public FatalDiscoveryException(string message) - : base(ErrorCode, message) - { - } - - internal static bool IsFatalDiscoveryError(string code) - { - return code.Equals(ErrorCode); - } -} - -/// -/// The result has already been consumed either by explicit consume call, or by termination of session or -/// transaction where the result was obtained. Once a result is consumed, the records in the result is not accessible -/// anymore. -/// -[DataContract] -public class ResultConsumedException : ClientException -{ - /// Create a new with an error message - /// The error message - public ResultConsumedException(string message) : base(message) - { - } -} - -/// -/// An attempt to BeginTransaction has been made before the sessions existing transaction has been consumed or -/// rolled back. e.g. An attempt to nest transactions has occurred. A session can only have a single transaction at a time. -/// -[DataContract] -public class TransactionNestingException : ClientException -{ - /// Create a new with an error message - /// The error message - public TransactionNestingException(string message) : base(message) - { - } -} - -/// -/// The exception that is thrown when calling or -/// on an that has already been closed. -/// -[DataContract] -public class TransactionClosedException : ClientException -{ - /// Create a new with an error message. - /// The error message - public TransactionClosedException(string message) : base(message) - { - } -} - -/// -/// The exception that is thrown when trying to further interact with a terminated transaction. -/// Transactions are terminated when they incur errors.
-/// If created by the driver the will be null. -///
-[DataContract] -public sealed class TransactionTerminatedException : ClientException -{ - public override bool IsRetriable => (InnerException as Neo4jException)?.IsRetriable ?? false; - - internal TransactionTerminatedException(Exception inner) : - base((inner as Neo4jException)?.Code, inner.Message, inner) - { - } -} - -/// -/// The exception that is thrown when calling an operation in the driver which uses a server feature that is not -/// available on the connected server version. -/// -[DataContract] -public class UnsupportedFeatureException : ClientException -{ - /// - public override bool IsRetriable => false; - - /// - /// Creates a new with an error message. - /// - /// The error message - internal UnsupportedFeatureException(string message) : base(message) - { - } -}