├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── .vs └── config │ └── applicationhost.config ├── LICENSE ├── Neo4jClient.FSharp.Tests ├── CypherFluentQueryTests.fs └── Neo4jClient.FSharp.Tests.fsproj ├── Neo4jClient.Tests ├── AddressResolverTests.cs ├── ApiModels │ └── RootApiResponseTests.cs ├── ApiUsageIdeas.cs ├── BoltGraphClientTests │ ├── BoltGraphClientTests.cs │ ├── Bookmarks │ │ └── BookmarkTests.cs │ ├── ConnectAsyncTests.cs │ └── Cypher │ │ └── ExecuteGetCypherResultsTests.cs ├── BoltTestHarness.cs ├── CultureInfoSetupFixture.cs ├── Cypher │ ├── AggregateTests.cs │ ├── CypherFluentQueryAdvancedTests.cs │ ├── CypherFluentQueryCallTests.cs │ ├── CypherFluentQueryConstraintTest.cs │ ├── CypherFluentQueryCreateUniqueTests.cs │ ├── CypherFluentQueryCustomHeaderTests.cs │ ├── CypherFluentQueryDatabaseAdministrationTests.cs │ ├── CypherFluentQueryDatabaseTests.cs │ ├── CypherFluentQueryDeleteTests.cs │ ├── CypherFluentQueryDetachDeleteTests.cs │ ├── CypherFluentQueryDropTests.cs │ ├── CypherFluentQueryForEachTests.cs │ ├── CypherFluentQueryLimitTests.cs │ ├── CypherFluentQueryLoadCsvTests.cs │ ├── CypherFluentQueryMatchTests.cs │ ├── CypherFluentQueryMaxExecutionTimeTests.cs │ ├── CypherFluentQueryMergeTests.cs │ ├── CypherFluentQueryParserVersionTests.cs │ ├── CypherFluentQueryPlannerTests.cs │ ├── CypherFluentQueryQueryStatsTests.cs │ ├── CypherFluentQueryReadWriteTests.cs │ ├── CypherFluentQueryRemoveTests.cs │ ├── CypherFluentQueryResultsTests.cs │ ├── CypherFluentQueryReturnTests.cs │ ├── CypherFluentQuerySetClientTests.cs │ ├── CypherFluentQuerySetTests.cs │ ├── CypherFluentQuerySkipTests.cs │ ├── CypherFluentQueryTests.cs │ ├── CypherFluentQueryUnwindTests.cs │ ├── CypherFluentQueryUseTests.cs │ ├── CypherFluentQueryUsingIndexTests.cs │ ├── CypherFluentQueryWhereTests.cs │ ├── CypherFluentQueryWithBookmarkTests.cs │ ├── CypherFluentQueryWithIdentifierTests.cs │ ├── CypherFluentQueryWithParamTests.cs │ ├── CypherFluentQueryWithTests.cs │ ├── CypherFluentQueryYieldTests.cs │ ├── CypherQueryTests.cs │ ├── CypherReturnExpressionBuilderTests.cs │ ├── CypherWhereExpressionBuilderTests.cs │ ├── DocumentationExamples.cs │ ├── QueryWriterTests.cs │ ├── StartBitFormatterTests.cs │ └── UnionTests.cs ├── Domain │ ├── Product.cs │ ├── StorageLocation.cs │ └── User.cs ├── Extensions │ ├── MemberInfoExtensionsTests.cs │ ├── Neo4jDriverExtensionsTests.cs │ └── ObjectExtensionTests.cs ├── GraphClientTests │ ├── ConnectAsyncTests.cs │ ├── ConnectTests.cs │ ├── CreateIndexTests.cs │ ├── CreateNodeTests.cs │ ├── CreateRelationshipTests.cs │ ├── Cypher │ │ ├── ExecuteCypherTests.cs │ │ └── ExecuteGetCypherResultsTests.cs │ ├── DefaultDatabaseTests.cs │ ├── DeleteIndexTests.cs │ ├── DeleteNodeTests.cs │ ├── DeleteRelationshipTests.cs │ ├── FactoryTests.cs │ ├── GetIndexesTests.cs │ ├── GetNodeTests.cs │ ├── IndexExistsTests.cs │ ├── LookupIndexTests.cs │ ├── ReIndexNodesTests.cs │ ├── ReIndexRelationshipsTests.cs │ ├── RootNodeTests.cs │ ├── UpdateNodeTests.cs │ └── UpdateRelationshipTests.cs ├── HttpClientWrapperTests.cs ├── IndexEntryTests.cs ├── MockRequest.cs ├── MockResponse.cs ├── MockResponseThrows.cs ├── MockResponseThrowsException.cs ├── Neo4jClient.Tests.csproj ├── NodeReferenceTests.cs ├── NodeTests.cs ├── QueryStatsTests.cs ├── RelationshipReferenceTests.cs ├── RelationshipTests.cs ├── Relationships │ ├── OwnedBy.cs │ ├── Requires.cs │ └── StoredIn.cs ├── RestTestHarness.cs ├── Serialization │ ├── CustomJsonDeserializerTests.cs │ ├── CustomJsonSerializerTests.cs │ ├── CypherJsonDeserializerTests.cs │ ├── SerializesDateTimeAsNeoDateTimeTests.cs │ └── UserSuppliedSerializationTests.cs ├── StatementResultHelperTests.cs ├── TestUtilities.cs ├── TestsImplementFixture.cs ├── Transactions │ ├── BoltClientTestHelper.cs │ ├── BoltQueriesInTransactionTests.cs │ ├── Neo4jTransactionResourceManagerTests.cs │ ├── QueriesInTransactionTests.cs │ ├── RestCallScenarioTests.cs │ ├── TransactionExecutionEnvironmentTests.cs │ ├── TransactionManagementTests.cs │ ├── TransactionRestResponseHelper.cs │ └── TransactionTests.cs └── UtilitiesTests.cs ├── Neo4jClient.Vb.Tests ├── Neo4jClient.Vb.Tests.vbproj └── WhereTests.vb ├── Neo4jClient.gpState ├── Neo4jClient.ncrunchsolution ├── Neo4jClient.ndproj ├── Neo4jClient.nuspec ├── Neo4jClient.sln ├── Neo4jClient.sln.DotSettings ├── Neo4jClient ├── AddressResolver.cs ├── AggregateExceptionExtensions.cs ├── AmbiguousRelationshipDirectionException.cs ├── ApiModels │ ├── BatchResponse.cs │ ├── BatchStep.cs │ ├── BatchStepExtensions.cs │ ├── BatchStepResult.cs │ ├── Cypher │ │ ├── CypherApiQuery.cs │ │ ├── CypherStatementList.cs │ │ ├── CypherTransactionStatement.cs │ │ ├── PathsResult.cs │ │ ├── PathsResultBolt.cs │ │ └── QueryStats.cs │ ├── ExceptionResponse.cs │ ├── FieldChange.cs │ ├── NodeApiResponse.cs │ ├── NodeOrRelationshipApiResponse.cs │ ├── RelationshipApiResponse.cs │ ├── RelationshipTemplate.cs │ └── RootApiResponse.cs ├── Attributes │ ├── Neo4jDateTimeAttribute.cs │ └── Neo4jIgnoreAttribute.cs ├── BoltGraphClient.cs ├── Cypher │ ├── All.cs │ ├── CypherCapabilities.cs │ ├── CypherFluentQuery.cs │ ├── CypherFluentQueryAdvanced.cs │ ├── CypherFluentQuery`DatabaseAdministration.cs │ ├── CypherFluentQuery`Return.cs │ ├── CypherFluentQuery`TResult.cs │ ├── CypherFluentQuery`Where.cs │ ├── CypherFluentQuery`WhereIf.cs │ ├── CypherFluentQuery`With.cs │ ├── CypherFluentQuery`WithBookmark.cs │ ├── CypherPlanner.cs │ ├── CypherQuery.cs │ ├── CypherResultMode.cs │ ├── CypherReturnExpressionBuilder.cs │ ├── CypherRuntime.cs │ ├── CypherWhereExpressionBuilder.cs │ ├── CypherWhereExpressionVisitor.cs │ ├── CypherWithExpressionBuilder.cs │ ├── ICypherFluentQuery.cs │ ├── ICypherFluentQueryAdvanced.cs │ ├── ICypherFluentQuery`DatabaseAdministration.cs │ ├── ICypherFluentQuery`TResult.cs │ ├── ICypherFluentQuery`Where.cs │ ├── ICypherFluentQuery`WhereIf.cs │ ├── ICypherFluentQuery`With.cs │ ├── ICypherResultItem.cs │ ├── IFluentCypherResultItem.cs │ ├── IOrderedCypherFluentQuery.cs │ ├── IOrderedCypherFluentQuery`TResult.cs │ ├── Node.cs │ ├── OrderByType.cs │ ├── QueryWriter.cs │ ├── Relationship.cs │ ├── Return.cs │ ├── ReturnExpression.cs │ ├── StartBit.cs │ ├── StartBitFormatter.cs │ └── VbComparer.cs ├── CypherPartialResult.cs ├── DeleteMode.cs ├── DetachedNodeException.cs ├── Diagrams │ ├── Batching.cd │ ├── GraphClientCore.cd │ └── Responses.cd ├── DriverWrapper.cs ├── Execution │ ├── BatchExecutionPolicy.cs │ ├── CypherExecutionPolicy.cs │ ├── CypherTransactionExecutionPolicy.cs │ ├── EndpointBuilderExtension.cs │ ├── ErrorGenerator.cs │ ├── ExecutionConfiguration.cs │ ├── ExecutionPolicyFactory.cs │ ├── GraphClientBasedExecutionPolicy.cs │ ├── HttpResponseMessageExtensions.cs │ ├── IExecutionPolicy.cs │ ├── IExecutionPolicyFactory.cs │ ├── IHttpClient.cs │ ├── IRequestTypeBuilder.cs │ ├── IRequestWithPendingContentBuilder.cs │ ├── IResponseBuilder.cs │ ├── IResponseBuilder`TResult.cs │ ├── IResponseFailBuilder.cs │ ├── NodeIndexExecutionPolicy.cs │ ├── RelationshipIndexExecutionPolicy.cs │ ├── Request.cs │ ├── RequestTypeBuilder.cs │ ├── RequestWithPendingContentBuilder.cs │ ├── ResponseBuilder.cs │ ├── ResponseBuilder`TParse.cs │ ├── ResponseFailBuilder.cs │ ├── ResponseFailBuilder`TParse.cs │ └── RestExecutionPolicy.cs ├── Extensions │ ├── DriverExtensions.cs │ ├── EnumerableExtensions.cs │ ├── MemberInfoExtensions.cs │ └── ObjectExtensions.cs ├── GraphClient.cs ├── GraphClientExtensions.cs ├── GraphClientFactory.cs ├── HttpClient.cs ├── HttpContentExtensions.cs ├── IAttachedReference.cs ├── IBoltGraphClient.cs ├── ICypherGraphClient.cs ├── IGraphClient.cs ├── IGraphClientFactory.cs ├── IHasNodeReference.cs ├── IRawGraphClient.cs ├── IRelationshipAllowingParticipantNode`TNode.cs ├── IRelationshipAllowingSourceNode`TNode.cs ├── IRelationshipAllowingTargetNode`TNode.cs ├── ITypedNodeReference.cs ├── IndexConfiguration.cs ├── IndexEntry.cs ├── IndexFor.cs ├── IndexMetaData.cs ├── IndexProvider.cs ├── IndexType.cs ├── Indexing.cd ├── JTokenExtensions.cs ├── Neo4jClient.Shared.v3.ncrunchproject ├── Neo4jClient.csproj ├── Neo4jDriverExtensions.cs ├── NeoException.cs ├── NeoServerConfiguration.cs ├── NodeReference.cs ├── NodeReference`TNode.cs ├── Node`TNode.cs ├── OperationCompletedEventHandler.cs ├── OrphanedTransactionException.cs ├── Relationship.cs ├── RelationshipDirection.cs ├── RelationshipEnd.cs ├── RelationshipInstance.cs ├── RelationshipInstance`TData.cs ├── RelationshipReference.cs ├── RelationshipReference`TData.cs ├── Relationship`TData.cs ├── Serialization │ ├── CommonDeserializerMethods.cs │ ├── CustomJsonDeserializer.cs │ ├── CustomJsonSerializer.cs │ ├── CypherJsonDeserializer.cs │ ├── DeserializationContext.cs │ ├── DeserializationException.cs │ ├── EnumValueConverter.cs │ ├── ICypherJsonDeserializer.cs │ ├── ISerializer.cs │ ├── Neo4jContractSerializer.cs │ ├── NullableEnumValueConverter.cs │ ├── PartialDeserializationContext.cs │ ├── TimeZoneInfoConverter.cs │ ├── TypeConverterBasedJsonConverter.cs │ ├── TypeMapping.cs │ └── ZonedDateTimeConverter.cs ├── StatementResultHelper.cs ├── Transactions │ ├── Bolt │ │ ├── BoltNeo4jTransaction.cs │ │ ├── BoltNeo4jTransactionProxy.cs │ │ ├── BoltResponse.cs │ │ ├── BoltSuppressTransactionProxy.cs │ │ ├── BoltTransactionContext.cs │ │ ├── BoltTransactionManager.cs │ │ └── BoltTransactionScopeProxy.cs │ ├── ClosedTransactionException.cs │ ├── IInternalTransactionalGraphClient.cs │ ├── INeo4jTransaction.cs │ ├── ITransaction.cs │ ├── ITransactionExecutionEnvironment.cs │ ├── ITransactionManager.cs │ ├── ITransactionResourceManager.cs │ ├── ITransactionalGraphClient.cs │ ├── Neo4jRestTransaction.cs │ ├── Neo4jTransactionProxy.cs │ ├── Neo4jTransactionResourceManager.cs │ ├── SuppressTransactionProxy.cs │ ├── ThreadContextHelpers.cs │ ├── TransactionConnectionContext.cs │ ├── TransactionContext.cs │ ├── TransactionContextBase.cs │ ├── TransactionExecutionEnvironment.cs │ ├── TransactionHttpUtils.cs │ ├── TransactionManager.cs │ └── TransactionScopeProxy.cs ├── UriCreator.cs └── Utilities.cs ├── README.md ├── build.bat ├── coverage.xml └── tools ├── nuget.exe └── vswhere.exe /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: cskardon 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **Versions:** 14 | - Neo4jClient [e.g. 4.0.0-preview10] 15 | - Bolt or Http [e.g. BoltGraphClient] 16 | - .NET Version [e.g. Core 2.1] 17 | - Server Version [e.g. 4.1.1 Cluster] 18 | 19 | **To Reproduce** 20 | Steps to reproduce the behavior: 21 | 1. Go to '...' 22 | 2. Click on '....' 23 | 3. Scroll down to '....' 24 | 4. See error 25 | This should contain a working minimum example project if possible, with classes and scripts to create an example database (if needed) - if possible! 26 | 27 | **Expected behaviour** 28 | A clear and concise description of what you expected to happen. 29 | 30 | **Screenshots** 31 | If applicable, add screenshots to help explain your problem. 32 | 33 | **Additional context** 34 | Add any other context about the problem here. 35 | (any things which are different about your setup, indexes etc) 36 | -------------------------------------------------------------------------------- /Neo4jClient.FSharp.Tests/CypherFluentQueryTests.fs: -------------------------------------------------------------------------------- 1 | module Neo4jClient.Tests.Cypher.Where 2 | 3 | open Neo4jClient 4 | open Neo4jClient.Cypher 5 | open Xunit 6 | open Moq 7 | 8 | [] 9 | type ObjWithCount = { Name:string; Count:int } 10 | 11 | [] 12 | let where_should_inject_correct_parameter () = 13 | let mockGc = new Mock() 14 | let cfq = new CypherFluentQuery(mockGc.Object); 15 | let query = 16 | cfq 17 | .Where(fun u -> u.Count = 1000).Query 18 | Assert.NotNull query 19 | Assert.Equal(query.QueryText, "WHERE (u.Count = $p0)") 20 | Assert.Equal(query.QueryParameters.["p0"], 1000) 21 | 22 | [] 23 | let generates_the_correct_return_statement () = 24 | let mockGc = new Mock() 25 | let cfq = new CypherFluentQuery(mockGc.Object); 26 | let query = 27 | cfq 28 | .Return(fun (u : Cypher.ICypherResultItem) (t : Cypher.ICypherResultItem) -> u.As(), t.Count()).Query 29 | Assert.NotNull query 30 | Assert.Equal(query.QueryText, "RETURN u AS Item1, count(t) AS Item2") 31 | -------------------------------------------------------------------------------- /Neo4jClient.FSharp.Tests/Neo4jClient.FSharp.Tests.fsproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | all 18 | runtime; build; native; contentfiles; analyzers; buildtransitive 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/AddressResolverTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using FluentAssertions; 4 | using Neo4j.Driver; 5 | using Xunit; 6 | 7 | namespace Neo4jClient.Tests 8 | { 9 | public class AddressResolverTests : IClassFixture 10 | { 11 | [Fact] 12 | public void CreatesEmptyListIfNullIsPassedIn() 13 | { 14 | var ar = new AddressResolver(new Uri("bolt://virtual.uri:1234"), null); 15 | var response = ar.Resolve(ServerAddress.From("virtual.uri", 1234)); 16 | response.Should().HaveCount(0); 17 | } 18 | 19 | [Fact] 20 | public void PassesBackCorrectUris() 21 | { 22 | const string uri1 = "x.acme.com"; 23 | const string uri2 = "y.acme.com"; 24 | 25 | var ar = new AddressResolver("bolt://virtual.uri", new []{$"bolt://{uri1}", "bolt://" + uri2}); 26 | var response = ar.Resolve(null).ToList(); 27 | response.Should().HaveCount(2); 28 | response.Any(x => x.Host == uri1).Should().BeTrue(); 29 | response.Any(x => x.Host == uri2).Should().BeTrue(); 30 | } 31 | } 32 | 33 | public class UriCreatorTests : IClassFixture 34 | { 35 | [Theory] 36 | [InlineData("x.foo.com", "scheme://x.foo.com:7687/", 7687)] 37 | [InlineData("x.foo.com:7687", "scheme://x.foo.com:7687/", 7687)] 38 | [InlineData("x.foo.com:7688", "scheme://x.foo.com:7688/", 7688)] 39 | [InlineData("bolt://x.foo.com:7688", "bolt://x.foo.com:7688/", 7688)] 40 | public void GeneratesTheCorrectUri(string input, string expectedUri, int expectedPort) 41 | { 42 | var response = UriCreator.From(input); 43 | response.AbsoluteUri.Should().Be(expectedUri); 44 | response.Port.Should().Be(expectedPort); 45 | } 46 | 47 | [Theory] 48 | [InlineData(null)] 49 | [InlineData("")] 50 | [InlineData(" ")] 51 | public void ReturnsNullWhenNullOrWhitespacePassedIn(string uri) 52 | { 53 | UriCreator.From(uri).Should().BeNull(); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/ApiModels/RootApiResponseTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Neo4jClient.ApiModels; 3 | using Xunit; 4 | 5 | namespace Neo4jClient.Tests.ApiModels 6 | { 7 | 8 | public class RootApiResponseTests : IClassFixture 9 | { 10 | [Theory] 11 | [InlineData("", "0.0")] 12 | [InlineData("kgrkjkj", "0.0")] 13 | [InlineData("1.5-82-g7cb21bb1-dirty", "1.5")] //Description = "http://docs.neo4j.org/chunked/snapshot/rest-api-service-root.html")] 14 | [InlineData("1.5M02", "1.5.0.2")] 15 | [InlineData("1.8.RC1", "1.8.0.1")] 16 | [InlineData("1.5.M02", "1.5.0.2")] //Description = "Retrieved via REST call from running 1.5M02 install")] 17 | [InlineData("1.7", "1.7")] //Description = "http://docs.neo4j.org/chunked/1.7/rest-api-service-root.html")] 18 | [InlineData("1.7.2", "1.7.2")] //Description = "http://docs.neo4j.org/chunked/1.7.2/rest-api-service-root.html")] 19 | [InlineData("1.8.M07-1-g09701c5", "1.8.0.7")] //Description = "http://docs.neo4j.org/chunked/1.8.M07/rest-api-service-root.html")] 20 | [InlineData("1.9.RC1", "1.9.0.1")] 21 | [InlineData("1.9.RC2", "1.9.0.2")] 22 | public void Version(string versionString, string expectedResult) 23 | { 24 | var response = new RootApiResponse { Neo4jVersion = versionString }; 25 | response.Version.ToString().Should().Be(expectedResult); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/CultureInfoSetupFixture.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using System.Threading; 3 | 4 | namespace Neo4jClient.Tests 5 | { 6 | public class CultureInfoSetupFixture 7 | { 8 | //SetCultureToSomethingNonLatinToEnsureCodeUnderTestDoesntAssumeEnAu() 9 | public static readonly CultureInfo CultureInfo = new CultureInfo("zh-CN"); 10 | 11 | public static void SetDeterministicCulture() 12 | { 13 | Thread.CurrentThread.CurrentCulture = Thread.CurrentThread.CurrentUICulture = CultureInfo; 14 | } 15 | 16 | public CultureInfoSetupFixture() 17 | { 18 | // Issue https://bitbucket.org/Readify/neo4jclient/issue/15/take-cultureinfo-into-account-for-proper 19 | 20 | // The idea is to minimize developer mistake by surprising culture-info assumptions. This may not be the best setup for culture-dependent 21 | // tests. The alternative of introducing test base class is deliberately not taken because deriving from it is another assumption by itself. 22 | SetDeterministicCulture(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryAdvancedTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Neo4jClient.Cypher; 6 | using NSubstitute; 7 | using Xunit; 8 | 9 | namespace Neo4jClient.Tests.Cypher 10 | { 11 | public class CypherFluentQueryAdvancedTests : IClassFixture 12 | { 13 | [Fact] 14 | public async Task ReturnColumnAlias() 15 | { 16 | // http://docs.neo4j.org/chunked/1.6/query-return.html#return-column-alias 17 | // START a=node(1) 18 | // RETURN a.Age AS SomethingTotallyDifferent 19 | 20 | var client = Substitute.For(); 21 | 22 | client 23 | .ExecuteGetCypherResultsAsync(Arg.Any()) 24 | .Returns(Enumerable.Empty()); 25 | 26 | var cypher = new CypherFluentQuery(client); 27 | var results = cypher 28 | .Match("a") 29 | .Advanced.Return(new ReturnExpression 30 | { 31 | ResultFormat = CypherResultFormat.DependsOnEnvironment, 32 | ResultMode = CypherResultMode.Projection, 33 | Text = "a.Age AS SomethingTotallyDifferent" 34 | }); 35 | Assert.Equal($"MATCH a{Environment.NewLine}RETURN a.Age AS SomethingTotallyDifferent", results.Query.DebugQueryText); 36 | Assert.IsAssignableFrom>(await results.ResultsAsync); 37 | } 38 | 39 | public class ReturnPropertyQueryResult 40 | { 41 | public int SomethingTotallyDifferent { get; set; } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryConstraintTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Neo4jClient.Cypher; 4 | using NSubstitute; 5 | using Xunit; 6 | 7 | namespace Neo4jClient.Tests.Cypher 8 | { 9 | 10 | public class CypherFluentQueryConstraintTest : IClassFixture 11 | { 12 | [Fact] 13 | public void CreateUniqueConstraint() 14 | { 15 | // Arrange 16 | var client = Substitute.For(); 17 | var query = new CypherFluentQuery(client) 18 | .CreateUniqueConstraint("book:Book", "book.isbn") 19 | .Query; 20 | 21 | // Assert 22 | Assert.Equal("CREATE CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE", query.QueryText); 23 | } 24 | 25 | [Fact] 26 | public void DropUniqueConstraint() 27 | { 28 | // Arrange 29 | var client = Substitute.For(); 30 | var query = new CypherFluentQuery(client) 31 | .DropUniqueConstraint("book:Book", "book.isbn") 32 | .Query; 33 | 34 | // Assert 35 | Assert.Equal("DROP CONSTRAINT ON (book:Book) ASSERT book.isbn IS UNIQUE", query.QueryText); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryCreateUniqueTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Neo4jClient.Cypher; 3 | using NSubstitute; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests.Cypher 7 | { 8 | public class CypherFluentQueryCreateUniqueTests : IClassFixture 9 | { 10 | [Fact] 11 | public void CreateNodeWithValuesViaCreateUnique() 12 | { 13 | // http://docs.neo4j.org/chunked/1.8.M03/query-relate.html#relate-create-nodes-with-values 14 | //START root=node(2) 15 | //CREATE UNIQUE root-[:X]-(leaf {name:'D'} ) 16 | //RETURN leaf 17 | 18 | var client = Substitute.For(); 19 | client.ServerVersion.Returns(new Version(1, 8)); 20 | var query = new CypherFluentQuery(client) 21 | .Match("root") 22 | .CreateUnique("root-[:X]-(leaf {name:'D'} )") 23 | .Return("leaf") 24 | .Query; 25 | 26 | Assert.Equal($"MATCH root{Environment.NewLine}CREATE UNIQUE root-[:X]-(leaf {{name:'D'}} ){Environment.NewLine}RETURN leaf", query.QueryText); 27 | } 28 | 29 | [Fact] 30 | public void CreateNodeWithValuesViaCreateUniqueAfterMatch() 31 | { 32 | //START root=node(2) 33 | //MATCH root-[:X]-foo 34 | //CREATE UNIQUE foo-[:Y]-(leaf {name:'D'} ) 35 | //RETURN leaf 36 | 37 | var client = Substitute.For(); 38 | client.ServerVersion.Returns(new Version(1, 8)); 39 | var query = new CypherFluentQuery(client) 40 | .Match("root-[:X]-foo") 41 | .CreateUnique("foo-[:Y]-(leaf {name:'D'} )") 42 | .Return("leaf") 43 | .Query; 44 | 45 | Assert.Equal($"MATCH root-[:X]-foo{Environment.NewLine}CREATE UNIQUE foo-[:Y]-(leaf {{name:'D'}} ){Environment.NewLine}RETURN leaf", query.QueryText); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryCustomHeaderTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | using Neo4jClient.Cypher; 3 | using NSubstitute; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests.Cypher 7 | { 8 | public class CypherFluentQueryCustomHeaderTests : IClassFixture 9 | { 10 | [Fact] 11 | public void SetsMaxExecutionTimeAndCustomHeader_WhenUsingAReturnTypeQuery() 12 | { 13 | const string headerName = "HeaderName"; 14 | const string headerValue = "TestHeaderValue"; 15 | var client = Substitute.For(); 16 | var customHeaders = new NameValueCollection {{headerName, headerValue}}; 17 | 18 | var query = new CypherFluentQuery(client) 19 | .MaxExecutionTime(100) 20 | .CustomHeaders(customHeaders) 21 | .Match("n") 22 | .Return("n") 23 | .Query; 24 | 25 | Assert.Equal(100, query.MaxExecutionTime); 26 | Assert.Equal(customHeaders, query.CustomHeaders); 27 | } 28 | 29 | [Fact] 30 | public void SetsCustomHeader_WhenUsingAReturnTypeQuery() 31 | { 32 | const string headerName = "HeaderName"; 33 | const string headerValue = "TestHeaderValue"; 34 | var client = Substitute.For(); 35 | var customHeaders = new NameValueCollection {{headerName, headerValue}}; 36 | 37 | var query = new CypherFluentQuery(client) 38 | .CustomHeaders(customHeaders) 39 | .Match("n") 40 | .Return("n") 41 | .Query; 42 | 43 | Assert.Equal(customHeaders, query.CustomHeaders); 44 | } 45 | 46 | [Fact] 47 | public void SetsCustomHeader_WhenUsingANonReturnTypeQuery() 48 | { 49 | const string headerName = "HeaderName"; 50 | const string headerValue = "TestHeaderValue"; 51 | var customHeaders = new NameValueCollection {{headerName, headerValue}}; 52 | 53 | var client = Substitute.For(); 54 | var query = new CypherFluentQuery(client) 55 | .CustomHeaders(customHeaders) 56 | .Match("n") 57 | .Set("n.Value = 'value'") 58 | .Query; 59 | 60 | Assert.Equal(customHeaders, query.CustomHeaders); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryForEachTests.cs: -------------------------------------------------------------------------------- 1 | using Neo4jClient.Cypher; 2 | using NSubstitute; 3 | using Xunit; 4 | 5 | namespace Neo4jClient.Tests.Cypher 6 | { 7 | 8 | public class CypherFluentQueryForEachTests : IClassFixture 9 | { 10 | [Fact] 11 | public void ForEachRawText() 12 | { 13 | // http://docs.neo4j.org/chunked/milestone/query-foreach.html 14 | // FOREACH (n IN nodes(p) | SET n.marked = TRUE) 15 | 16 | var client = Substitute.For(); 17 | var query = new CypherFluentQuery(client) 18 | .ForEach("(n IN nodes(p) | SET n.marked = TRUE)") 19 | .Query; 20 | 21 | Assert.Equal("FOREACH (n IN nodes(p) | SET n.marked = TRUE)", query.QueryText); 22 | Assert.Equal(0, query.QueryParameters.Count); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryMaxExecutionTimeTests.cs: -------------------------------------------------------------------------------- 1 | using Neo4jClient.Cypher; 2 | using NSubstitute; 3 | using Xunit; 4 | 5 | namespace Neo4jClient.Tests.Cypher 6 | { 7 | 8 | public class CypherFluentQueryMaxExecutionTimeTests : IClassFixture 9 | { 10 | [Fact] 11 | public void SetsMaxExecutionTime_WhenUsingAReturnTypeQuery() 12 | { 13 | var client = Substitute.For(); 14 | var query = new CypherFluentQuery(client) 15 | .MaxExecutionTime(100) 16 | .Match("n") 17 | .Return("n") 18 | .Query; 19 | 20 | Assert.Equal(100, query.MaxExecutionTime); 21 | } 22 | 23 | [Fact] 24 | public void SetsMaxExecutionTime_WhenUsingANonReturnTypeQuery() 25 | { 26 | var client = Substitute.For(); 27 | var query = new CypherFluentQuery(client) 28 | .MaxExecutionTime(100) 29 | .Match("n") 30 | .Set("n.Value = 'value'") 31 | .Query; 32 | 33 | Assert.Equal(100, query.MaxExecutionTime); 34 | } 35 | 36 | } 37 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryParserVersionTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Neo4jClient.Cypher; 3 | using NSubstitute; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests.Cypher 7 | { 8 | public class CypherFluentQueryParserVersionTests : IClassFixture 9 | { 10 | [Fact] 11 | public void SetsVersionToFreeTextGiven() 12 | { 13 | var client = Substitute.For(); 14 | var query = new CypherFluentQuery(client) 15 | .ParserVersion("2.1.experimental") 16 | .Match("n") 17 | .Return("n") 18 | .Query; 19 | 20 | Assert.Equal("CYPHER 2.1.experimental" + Environment.NewLine + "MATCH n" + Environment.NewLine + "RETURN n", query.QueryText); 21 | Assert.Equal(0, query.QueryParameters.Count); 22 | } 23 | 24 | [Fact] 25 | public void SetsVersion_WhenUsingVersionOverload() 26 | { 27 | var client = Substitute.For(); 28 | var query = new CypherFluentQuery(client) 29 | .ParserVersion(new Version(1, 9)) 30 | .Match("n") 31 | .Return("n") 32 | .Query; 33 | 34 | Assert.Equal("CYPHER 1.9" + Environment.NewLine + "MATCH n" + Environment.NewLine + "RETURN n", query.QueryText); 35 | Assert.Equal(0, query.QueryParameters.Count); 36 | } 37 | 38 | [Fact] 39 | public void SetsVersion_WhenUsingIntOverload() 40 | { 41 | var client = Substitute.For(); 42 | var query = new CypherFluentQuery(client) 43 | .ParserVersion(1, 9) 44 | .Match("n") 45 | .Return("n") 46 | .Query; 47 | 48 | Assert.Equal("CYPHER 1.9" + Environment.NewLine + "MATCH n" + Environment.NewLine + "RETURN n", query.QueryText); 49 | Assert.Equal(0, query.QueryParameters.Count); 50 | } 51 | 52 | [Fact] 53 | public void UsesLegacy_WhenVersionRequestedIsLessThan1_9() 54 | { 55 | var client = Substitute.For(); 56 | var query = new CypherFluentQuery(client) 57 | .ParserVersion(1, 8) 58 | .Match("n") 59 | .Return("n") 60 | .Query; 61 | 62 | Assert.Equal("CYPHER LEGACY" + Environment.NewLine + "MATCH n" + Environment.NewLine + "RETURN n", query.QueryText); 63 | Assert.Equal(0, query.QueryParameters.Count); 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryQueryStatsTests.cs: -------------------------------------------------------------------------------- 1 | using Neo4jClient.Cypher; 2 | using NSubstitute; 3 | using Xunit; 4 | 5 | namespace Neo4jClient.Tests.Cypher 6 | { 7 | public class CypherFluentQueryQueryStatsTests : IClassFixture 8 | { 9 | [Fact] 10 | public void SetsIncludeQueryStatsToTrueWhenUsingQueryStatsProperty() 11 | { 12 | var client = Substitute.For(); 13 | var query = new CypherFluentQuery(client) 14 | .WithQueryStats 15 | .Create("(n:Foo)") 16 | .Query; 17 | 18 | Assert.Equal("CREATE (n:Foo)", query.QueryText); 19 | Assert.True(query.IncludeQueryStats); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryReadWriteTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using Neo4jClient.Cypher; 4 | using NSubstitute; 5 | using Xunit; 6 | 7 | namespace Neo4jClient.Tests.Cypher 8 | { 9 | 10 | public class CypherFluentQueryReadWriteTests : IClassFixture 11 | { 12 | [Fact] 13 | public void SetsIsWriteToTrueWhenUsingWriteProperty() 14 | { 15 | var client = Substitute.For(); 16 | var query = new CypherFluentQuery(client) 17 | .Write 18 | .Create("(n:Foo)") 19 | .Query; 20 | 21 | Assert.Equal("CREATE (n:Foo)", query.QueryText); 22 | Assert.True(query.IsWrite); 23 | } 24 | 25 | [Fact] 26 | public void SetsIsWriteToTrueByDefault() 27 | { 28 | var client = Substitute.For(); 29 | var query = new CypherFluentQuery(client) 30 | .Create("(n:Foo)") 31 | .Query; 32 | 33 | Assert.Equal("CREATE (n:Foo)", query.QueryText); 34 | Assert.True(query.IsWrite); 35 | } 36 | 37 | [Fact] 38 | public void SetsIsWriteToFalseWhenUsingWriteProperty() 39 | { 40 | var client = Substitute.For(); 41 | var query = new CypherFluentQuery(client) 42 | .Read 43 | .Match("(n:Foo)") 44 | .Query; 45 | 46 | Assert.Equal("MATCH (n:Foo)", query.QueryText); 47 | Assert.False(query.IsWrite); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryRemoveTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Neo4jClient.Cypher; 3 | using NSubstitute; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests.Cypher 7 | { 8 | 9 | public class CypherFluentQueryRemoveTests : IClassFixture 10 | { 11 | [Fact] 12 | public void RemoveProperty() 13 | { 14 | // Arrange 15 | var client = Substitute.For(); 16 | var query = new CypherFluentQuery(client) 17 | .Match("n") 18 | .Remove("n.age") 19 | .Return>("n") 20 | .Query; 21 | 22 | // Assert 23 | Assert.Equal("MATCH n" + Environment.NewLine + "REMOVE n.age" + Environment.NewLine + "RETURN n", query.QueryText); 24 | } 25 | 26 | [Fact] 27 | public void RemoveLabel() 28 | { 29 | // Arrange 30 | var client = Substitute.For(); 31 | var query = new CypherFluentQuery(client) 32 | .Match("n") 33 | .Remove("n:Person") 34 | .Return>("n") 35 | .Query; 36 | 37 | // Assert 38 | Assert.Equal("MATCH n" + Environment.NewLine + "REMOVE n:Person" + Environment.NewLine + "RETURN n", query.QueryText); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQuerySetClientTests.cs: -------------------------------------------------------------------------------- 1 | using Neo4jClient.Cypher; 2 | using NSubstitute; 3 | using Xunit; 4 | 5 | namespace Neo4jClient.Tests.Cypher 6 | { 7 | 8 | public class CypherFluentQuerySetClientTests : IClassFixture 9 | { 10 | [Fact] 11 | public void SetClient() 12 | { 13 | var client1 = Substitute.For(); 14 | var client2 = Substitute.For(); 15 | 16 | var query = new CypherFluentQuery(client1).Match("(n)"); 17 | Assert.Same(((CypherFluentQuery) query).Client, client1); 18 | 19 | query = query.Advanced.SetClient(client2); 20 | Assert.Same(((CypherFluentQuery) query).Client, client2); 21 | } 22 | 23 | [Fact] 24 | public void SetClient_TResult() 25 | { 26 | var client1 = Substitute.For(); 27 | var client2 = Substitute.For(); 28 | 29 | var query = new CypherFluentQuery(client1).Match("(n)").Return(n => n.Count()); 30 | Assert.Same(((CypherFluentQuery) query).Client, client1); 31 | 32 | query = query.Advanced.SetClient(client2); 33 | Assert.Same(((CypherFluentQuery) query).Client, client2); 34 | } 35 | 36 | [Fact] 37 | public void JoinQueries() 38 | { 39 | var client1 = Substitute.For(); 40 | var client2 = Substitute.For(); 41 | 42 | var query = new CypherFluentQuery(client1).Match("(bar)"); 43 | Assert.Same(((CypherFluentQuery)query).Client, client1); 44 | query = query.Advanced.SetClient(client2).Return(bar => new {Foo = bar.As()}); 45 | Assert.Same(((CypherFluentQuery)query).Client, client2); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQuerySetTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Neo4jClient.Cypher; 3 | using NSubstitute; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests.Cypher 7 | { 8 | 9 | public class CypherFluentQuerySetTests : IClassFixture 10 | { 11 | [Fact] 12 | public void SetProperty() 13 | { 14 | // Arrange 15 | var client = Substitute.For(); 16 | var query = new CypherFluentQuery(client) 17 | .Match("n") 18 | .Set("n.age = 30") 19 | .Return>("n") 20 | .Query; 21 | 22 | // Assert 23 | Assert.Equal("MATCH n" + Environment.NewLine + "SET n.age = 30" + Environment.NewLine + "RETURN n", query.QueryText); 24 | } 25 | 26 | [Fact] 27 | public void SetWithoutReturn() 28 | { 29 | // Arrange 30 | var client = Substitute.For(); 31 | var query = new CypherFluentQuery(client) 32 | .Match("n") 33 | .Set("n.name = \"Ted\"") 34 | .Query; 35 | 36 | // Assert 37 | Assert.Equal("MATCH n" + Environment.NewLine + "SET n.name = \"Ted\"", query.QueryText); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryUnwindTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Neo4jClient.Cypher; 3 | using NSubstitute; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests.Cypher 7 | { 8 | /// 9 | /// Tests for the UNWIND operator 10 | /// 11 | 12 | public class CypherFluentQueryUnwindTests : IClassFixture 13 | { 14 | [Fact] 15 | public void TestUnwindConstruction() 16 | { 17 | var client = Substitute.For(); 18 | var query = new CypherFluentQuery(client) 19 | .Unwind("collection", "column") 20 | .Query; 21 | 22 | Assert.Equal("UNWIND collection AS column", query.QueryText); 23 | } 24 | 25 | [Fact] 26 | public void TestUnwindAfterWithTResultVariant() 27 | { 28 | var client = Substitute.For(); 29 | var query = new CypherFluentQuery(client) 30 | .With(collection => new { collection }) 31 | .Unwind("collection", "column") 32 | .Query; 33 | 34 | Assert.Equal("WITH collection" + Environment.NewLine + "UNWIND collection AS column", query.QueryText); 35 | } 36 | 37 | [Fact] 38 | public void TestUnwindUsingCollection() 39 | { 40 | var collection = new[] { 1, 2, 3 }; 41 | var client = Substitute.For(); 42 | var query = new CypherFluentQuery(client) 43 | .Unwind(collection, "alias") 44 | .Query; 45 | 46 | Assert.Equal("UNWIND $p0 AS alias", query.QueryText); 47 | Assert.Equal(1, query.QueryParameters.Count); 48 | Assert.Equal(collection, query.QueryParameters["p0"]); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryUseTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentAssertions; 3 | using Neo4jClient.Cypher; 4 | using NSubstitute; 5 | using Xunit; 6 | 7 | namespace Neo4jClient.Tests.Cypher 8 | { 9 | public class CypherFluentQueryUseTests : IClassFixture 10 | { 11 | // https://neo4j.com/docs/cypher-manual/current/clauses/use/ 12 | [Fact] 13 | public void GeneratesTheCorrectCypher() 14 | { 15 | var client = Substitute.For(); 16 | var query = new CypherFluentQuery(client) 17 | .Use("neo4jclient") 18 | .Match("(n)") 19 | .Return("n") 20 | .Query; 21 | 22 | query.QueryText.Should().Be($"USE neo4jclient{Environment.NewLine}MATCH (n){Environment.NewLine}RETURN n"); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryUsingIndexTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Neo4jClient.Cypher; 3 | using NSubstitute; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests.Cypher 7 | { 8 | 9 | public class CypherFluentQueryUsingIndexTests : IClassFixture 10 | { 11 | [Fact] 12 | public void UsesIndex() 13 | { 14 | var client = Substitute.For(); 15 | var query = new CypherFluentQuery(client) 16 | .Match("(foo:Bar { id: 123 })") 17 | .UsingIndex(":Bar(id)") 18 | .Return(foo => new { qux = foo.As() } ) 19 | .Query; 20 | 21 | Assert.Equal("MATCH (foo:Bar { id: 123 })" + Environment.NewLine + "USING INDEX :Bar(id)" + Environment.NewLine + "RETURN foo AS qux", query.QueryText); 22 | } 23 | 24 | [Theory] 25 | [InlineData(null)] 26 | [InlineData("")] 27 | public void UsingEmptyIndexIsInvalid(string index) 28 | { 29 | var client = Substitute.For(); 30 | Assert.Throws(() => 31 | { 32 | var query = new CypherFluentQuery(client) 33 | .Match("(foo:Bar { id: 123 })") 34 | .UsingIndex(index) 35 | .Return(foo => new {qux = foo.As()}) 36 | .Query; 37 | }); 38 | 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/CypherFluentQueryYieldTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Neo4jClient.Cypher; 3 | using NSubstitute; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests.Cypher 7 | { 8 | 9 | public class CypherFluentQueryYieldTests : IClassFixture 10 | { 11 | private static IRawGraphClient GraphClient_30 12 | { 13 | get 14 | { 15 | var client = Substitute.For(); 16 | client.CypherCapabilities.Returns(CypherCapabilities.Cypher30); 17 | return client; 18 | } 19 | } 20 | 21 | [Fact] 22 | public void YieldsGivenText() 23 | { 24 | var client = GraphClient_30; 25 | var query = new CypherFluentQuery(client) 26 | .Yield("uuid") 27 | .Query; 28 | 29 | Assert.Equal("YIELD uuid", query.QueryText); 30 | } 31 | 32 | [Fact] 33 | public void ThrowArgumentException_WhenNoStoredProcedureIsGiven() 34 | { 35 | var client = GraphClient_30; 36 | Assert.Throws(() => 37 | { 38 | var query = new CypherFluentQuery(client).Yield(null).Query; 39 | }); 40 | } 41 | 42 | [Fact] 43 | 44 | public void ThrowsInvalidOperationException_WhenClientVersionIsLessThan_30() 45 | { 46 | var client = GraphClient_30; 47 | client.CypherCapabilities.Returns(CypherCapabilities.Cypher23); 48 | 49 | Assert.Throws(() => { var query = new CypherFluentQuery(client).Yield("uuid").Query; }); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Cypher/UnionTests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Neo4jClient.Cypher; 3 | using NSubstitute; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests.Cypher 7 | { 8 | 9 | public class UnionTests : IClassFixture 10 | { 11 | [Fact] 12 | //[Description("http://docs.neo4j.org/chunked/2.0.0-M01/query-union.html#union-combine-two-queries-and-removing-duplicates")] 13 | public void UnionAll() 14 | { 15 | var client = Substitute.For(); 16 | var query = new CypherFluentQuery(client) 17 | .UnionAll() 18 | .Query; 19 | 20 | Assert.Equal("UNION ALL", query.QueryText); 21 | Assert.Equal(0, query.QueryParameters.Count()); 22 | } 23 | 24 | [Fact] 25 | //[Description("http://docs.neo4j.org/chunked/2.0.0-M01/query-union.html#union-union-two-queries")] 26 | public void Union() 27 | { 28 | var client = Substitute.For(); 29 | var query = new CypherFluentQuery(client) 30 | .Union() 31 | .Query; 32 | 33 | Assert.Equal("UNION", query.QueryText); 34 | Assert.Equal(0, query.QueryParameters.Count()); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Domain/Product.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Tests.Domain 2 | { 3 | public class Product 4 | { 5 | public string Name { get; set; } 6 | public double Weight { get; set; } 7 | } 8 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Domain/StorageLocation.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Tests.Domain 2 | { 3 | public class StorageLocation 4 | { 5 | public string Name { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Domain/User.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Tests.Domain 2 | { 3 | public class Part 4 | { 5 | public string Name { get; set; } 6 | } 7 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Extensions/MemberInfoExtensionsTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Neo4jClient.Extensions; 4 | using Newtonsoft.Json; 5 | using Xunit; 6 | 7 | namespace Neo4jClient.Tests.Extensions 8 | { 9 | 10 | public class MemberInfoExtensionsTests 11 | { 12 | class Foo 13 | { 14 | [JsonProperty] 15 | public string Property1 { get; set; } 16 | 17 | [JsonProperty("property_2")] 18 | public string Property2 { get; set; } 19 | 20 | public string Property3 { get; set; } 21 | } 22 | 23 | 24 | public class GetNameUsingJsonPropertyMethod : IClassFixture 25 | { 26 | [Fact] 27 | public void ThrowsArgumentNullException_WhenMemberInfoIsNull() 28 | { 29 | Assert.Throws(() => Neo4jClient.Extensions.MemberInfoExtensions.GetNameUsingJsonProperty(null)); 30 | } 31 | 32 | [Fact] 33 | public void ReturnsCorrectName_WhenPropertyHasJsonPropertyAddedButNoNameSet() 34 | { 35 | const string expected = "Property1"; 36 | 37 | var member = typeof (Foo).GetMember("Property1"); 38 | var actual = member.Single().GetNameUsingJsonProperty(); 39 | 40 | Assert.Equal(expected, actual); 41 | } 42 | 43 | [Fact] 44 | public void ReturnsCorrectName_WhenPropertyHasJsonPropertyAddedWithNameSet() 45 | { 46 | const string expected = "property_2"; 47 | 48 | var member = typeof(Foo).GetMember("Property2"); 49 | var actual = member.Single().GetNameUsingJsonProperty(); 50 | 51 | Assert.Equal(expected, actual); 52 | } 53 | 54 | [Fact] 55 | public void ReturnsCorrectName_WhenPropertyHasNoJsonPropertyAdded() 56 | { 57 | const string expected = "Property3"; 58 | 59 | var member = typeof(Foo).GetMember("Property3"); 60 | var actual = member.Single().GetNameUsingJsonProperty(); 61 | 62 | Assert.Equal(expected, actual); 63 | } 64 | 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Extensions/ObjectExtensionTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using FluentAssertions; 3 | using Neo4jClient.Extensions; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests.Extensions 7 | { 8 | public class ObjectExtensionTests 9 | { 10 | public class InMethod : IClassFixture 11 | { 12 | [Fact] 13 | public void ReturnsTrue_WhenItemIsInList() 14 | { 15 | var list = new List {1, 2, 3, 4}; 16 | 2.In(list).Should().BeTrue(); 17 | } 18 | 19 | [Fact] 20 | public void ReturnsFalse_WhenItemNotInList() 21 | { 22 | var list = new List { 1, 2, 3, 4 }; 23 | 5.In(list).Should().BeFalse(); 24 | } 25 | } 26 | 27 | public class NotInMethod : IClassFixture 28 | { 29 | [Fact] 30 | public void ReturnsTrue_WhenItemIsNotInList() 31 | { 32 | var list = new List { 1, 2, 3, 4 }; 33 | 5.NotIn(list).Should().BeTrue(); 34 | } 35 | 36 | [Fact] 37 | public void ReturnsTrue_WhenItemIsNotInList2() 38 | { 39 | 5.NotIn(new [] {1,2,3,4}).Should().BeTrue(); 40 | } 41 | 42 | [Fact] 43 | public void ReturnsFalse_WhenItemInList() 44 | { 45 | var list = new List { 1, 2, 3, 4 }; 46 | 4.NotIn(list).Should().BeFalse(); 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/GraphClientTests/DefaultDatabaseTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using FluentAssertions; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests.GraphClientTests 7 | { 8 | 9 | public class DefaultDatabaseTests : IClassFixture 10 | { 11 | [Theory] 12 | [InlineData("FOO")] 13 | [InlineData("foo")] 14 | [InlineData("Foo")] 15 | [InlineData("fOO")] 16 | [InlineData("FoO")] 17 | public void ShouldAlwaysBeLowercase(string database) 18 | { 19 | const string expected = "foo"; 20 | var client = new GraphClient(new Uri("http://foo")){DefaultDatabase = database}; 21 | client.DefaultDatabase.Should().Be(expected); 22 | } 23 | 24 | [Fact] 25 | public void ShouldBeNeo4jIfNotSet() 26 | { 27 | const string expected = "neo4j"; 28 | var client = new GraphClient(new Uri("http://foo")); 29 | client.DefaultDatabase.Should().Be(expected); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/GraphClientTests/DeleteIndexTests.cs: -------------------------------------------------------------------------------- 1 | // using System.Threading.Tasks; 2 | // using Xunit; 3 | // 4 | // namespace Neo4jClient.Tests.GraphClientTests 5 | // { 6 | // 7 | // public class DeleteIndexTests : IClassFixture 8 | // { 9 | // [Fact] 10 | // public async Task ShouldExecuteSilentlyForSuccessfulDelete() 11 | // { 12 | // using (var testHarness = new RestTestHarness 13 | // { 14 | // { 15 | // MockRequest.Delete("/index/node/MyIndex"), 16 | // MockResponse.Http(204) 17 | // } 18 | // }) 19 | // { 20 | // var graphClient = await testHarness.CreateAndConnectGraphClient(); 21 | // 22 | // //Act 23 | // await graphClient.DeleteIndexAsync("MyIndex", IndexFor.Node); 24 | // } 25 | // } 26 | // } 27 | // } 28 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/GraphClientTests/DeleteRelationshipTests.cs: -------------------------------------------------------------------------------- 1 | // using System; 2 | // using System.Threading.Tasks; 3 | // using FluentAssertions; 4 | // using Xunit; 5 | // 6 | // namespace Neo4jClient.Tests.GraphClientTests 7 | // { 8 | // 9 | // public class DeleteRelationshipTests : IClassFixture 10 | // { 11 | // [Fact] 12 | // public async Task ShouldThrowInvalidOperationExceptionIfNotConnected() 13 | // { 14 | // var client = new GraphClient(new Uri("http://foo")); 15 | // await Assert.ThrowsAsync(async () => await client.DeleteRelationshipAsync(123)); 16 | // } 17 | // 18 | // [Fact] 19 | // public async Task ShouldDeleteRelationship() 20 | // { 21 | // using (var testHarness = new RestTestHarness 22 | // { 23 | // { 24 | // MockRequest.Delete("/relationship/456"), 25 | // MockResponse.Http(204) 26 | // } 27 | // }) 28 | // { 29 | // var graphClient = await testHarness.CreateAndConnectGraphClient(); 30 | // await graphClient.DeleteRelationshipAsync(456); 31 | // } 32 | // } 33 | // 34 | // [Fact] 35 | // public async Task ShouldThrowExceptionWhenDeleteFails() 36 | // { 37 | // using (var testHarness = new RestTestHarness 38 | // { 39 | // { 40 | // MockRequest.Delete("/relationship/456"), 41 | // MockResponse.Http(404) 42 | // } 43 | // }) 44 | // { 45 | // var graphClient = await testHarness.CreateAndConnectGraphClient(); 46 | // var ex = await Assert.ThrowsAsync(async () => await graphClient.DeleteRelationshipAsync(456)); 47 | // ex.Message.Should().Be("Unable to delete the relationship. The response status was: 404 NotFound"); 48 | // } 49 | // } 50 | // } 51 | // } 52 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/GraphClientTests/IndexExistsTests.cs: -------------------------------------------------------------------------------- 1 | // using System.Net; 2 | // using System.Threading.Tasks; 3 | // using FluentAssertions; 4 | // using Xunit; 5 | // 6 | // namespace Neo4jClient.Tests.GraphClientTests 7 | // { 8 | // 9 | // public class IndexExistsTests : IClassFixture 10 | // { 11 | // [Theory] 12 | // [InlineData(IndexFor.Node, "/index/node/MyIndex", HttpStatusCode.OK, true)] 13 | // [InlineData(IndexFor.Node, "/index/node/MyIndex", HttpStatusCode.NotFound, false)] 14 | // [InlineData(IndexFor.Relationship, "/index/relationship/MyIndex", HttpStatusCode.OK, true)] 15 | // [InlineData(IndexFor.Relationship, "/index/relationship/MyIndex", HttpStatusCode.NotFound, false)] 16 | // public async Task ShouldReturnIfIndexIsFound( 17 | // IndexFor indexFor, 18 | // string indexPath, 19 | // HttpStatusCode httpStatusCode, bool expectedResult) 20 | // { 21 | // using (var testHarness = new RestTestHarness 22 | // { 23 | // { 24 | // MockRequest.Get(indexPath), 25 | // MockResponse.Json(httpStatusCode, "") 26 | // } 27 | // }) 28 | // { 29 | // var graphClient = await testHarness.CreateAndConnectGraphClient(); 30 | // (await graphClient.CheckIndexExistsAsync("MyIndex", indexFor)).Should().Be(expectedResult); 31 | // } 32 | // } 33 | // } 34 | // } 35 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/GraphClientTests/RootNodeTests.cs: -------------------------------------------------------------------------------- 1 | // using System.Threading.Tasks; 2 | // using Xunit; 3 | // 4 | // namespace Neo4jClient.Tests.GraphClientTests 5 | // { 6 | // 7 | // public class RootNodeTests : IClassFixture 8 | // { 9 | // [Fact] 10 | // public async Task RootNodeShouldHaveReferenceBackToClient() 11 | // { 12 | // using (var testHarness = new RestTestHarness()) 13 | // { 14 | // var client = await testHarness.CreateAndConnectGraphClient(); 15 | // var rootNode = client.RootNode; 16 | // Assert.Equal(client, ((IAttachedReference) rootNode).Client); 17 | // } 18 | // } 19 | // } 20 | // } 21 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/MockRequest.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using Neo4jClient.Serialization; 3 | 4 | namespace Neo4jClient.Tests 5 | { 6 | public class MockRequest 7 | { 8 | MockRequest() {} 9 | 10 | public HttpMethod Method { get; set; } 11 | public string Resource { get; set; } 12 | public string Body { get; set; } 13 | 14 | public static MockRequest Get(string uri) 15 | { 16 | if (uri == "") uri = "/"; 17 | return new MockRequest { Resource = uri, Method = HttpMethod.Get }; 18 | } 19 | 20 | public static MockRequest PostJson(string uri, string json) 21 | { 22 | return new MockRequest 23 | { 24 | Resource = uri, 25 | Method = HttpMethod.Post, 26 | Body = json 27 | }; 28 | } 29 | 30 | public static MockRequest PostObjectAsJson(string uri, object body) 31 | { 32 | var test = new MockRequest 33 | { 34 | Resource = uri, 35 | Method = HttpMethod.Post, 36 | Body = new CustomJsonSerializer { JsonConverters = GraphClient.DefaultJsonConverters }.Serialize(body) 37 | }; 38 | 39 | return new MockRequest 40 | { 41 | Resource = uri, 42 | Method = HttpMethod.Post, 43 | Body = new CustomJsonSerializer{JsonConverters = GraphClient.DefaultJsonConverters}.Serialize(body) 44 | }; 45 | } 46 | 47 | public static MockRequest PutObjectAsJson(string uri, object body) 48 | { 49 | return new MockRequest 50 | { 51 | Resource = uri, 52 | Method = HttpMethod.Put, 53 | Body = new CustomJsonSerializer{JsonConverters = GraphClient.DefaultJsonConverters}.Serialize(body) 54 | }; 55 | } 56 | 57 | public static MockRequest Delete(string uri) 58 | { 59 | return new MockRequest { Resource = uri, Method = HttpMethod.Delete }; 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/MockResponseThrows.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Neo4jClient.Tests 3 | { 4 | public class MockResponseThrows : MockResponse 5 | { 6 | public override string Content 7 | { 8 | get 9 | { 10 | throw new MockResponseThrowsException(); 11 | } 12 | set 13 | { 14 | throw new MockResponseThrowsException(); 15 | } 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/MockResponseThrowsException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient.Tests 4 | { 5 | public class MockResponseThrowsException : Exception 6 | { 7 | public MockResponseThrowsException() 8 | : base("Murphy's law") 9 | { 10 | // Nothing more to do 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Neo4jClient.Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.1 5 | latest 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | all 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/RelationshipReferenceTests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Xunit; 3 | 4 | namespace Neo4jClient.Tests 5 | { 6 | 7 | public class RelationshipReferenceTests : IClassFixture 8 | { 9 | [Fact] 10 | public void ShouldImplicitlyCastFromInt() 11 | { 12 | RelationshipReference relationshipReference = 3; 13 | Assert.Equal(3, relationshipReference.Id); 14 | } 15 | 16 | [Fact] 17 | public void ShouldExplicitlyCastFromInt() 18 | { 19 | var relationshipReference = (RelationshipReference)3; 20 | Assert.IsAssignableFrom(relationshipReference); 21 | Assert.Equal(3, relationshipReference.Id); 22 | } 23 | 24 | [Theory] 25 | [InlineData(1, 2, false)] 26 | [InlineData(3, 3, true)] 27 | public void EqualsTest(int lhs, int rhs, bool expected) 28 | { 29 | (new RelationshipReference(lhs) == new RelationshipReference(rhs)).Should().Be(expected); 30 | } 31 | 32 | [Theory] 33 | [InlineData(1, 2, false)] 34 | [InlineData(3, 3, true)] 35 | public void GetHashCodeTest(int lhs, int rhs, bool expected) 36 | { 37 | (new RelationshipReference(lhs).GetHashCode() == new RelationshipReference(rhs).GetHashCode()).Should().Be(expected); 38 | } 39 | 40 | [Fact] 41 | public void EqualsOperatorShouldReturnFalseWhenComparingInstanceWithNull() 42 | { 43 | var lhs = new RelationshipReference(3); 44 | Assert.False(lhs == null); 45 | } 46 | 47 | [Fact] 48 | public void EqualsOperatorShouldReturnTrueWhenComparingNullWithNull() 49 | { 50 | RelationshipReference lhs = null; 51 | Assert.True(lhs == null); 52 | } 53 | 54 | [Fact] 55 | public void EqualsShouldReturnFalseWhenComparingWithNull() 56 | { 57 | var lhs = new RelationshipReference(3); 58 | Assert.False(lhs.Equals(null)); 59 | } 60 | 61 | [Fact] 62 | public void EqualsShouldReturnFalseWhenComparingWithDifferentType() 63 | { 64 | var lhs = new RelationshipReference(3); 65 | Assert.False(lhs.Equals(new object())); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Relationships/OwnedBy.cs: -------------------------------------------------------------------------------- 1 | // using Neo4jClient.Tests.Domain; 2 | // 3 | // namespace Neo4jClient.Tests.Relationships 4 | // { 5 | // public class OwnedBy : 6 | // Relationship, 7 | // IRelationshipAllowingSourceNode, 8 | // IRelationshipAllowingTargetNode 9 | // { 10 | // public OwnedBy(NodeReference otherNode) 11 | // : base(otherNode) 12 | // {} 13 | // 14 | // public override string RelationshipTypeKey 15 | // { 16 | // get { return "OWNED_BY"; } 17 | // } 18 | // } 19 | // } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Relationships/Requires.cs: -------------------------------------------------------------------------------- 1 | using Neo4jClient.Tests.Domain; 2 | 3 | namespace Neo4jClient.Tests.Relationships 4 | { 5 | public class Requires : 6 | Relationship, 7 | IRelationshipAllowingSourceNode, 8 | IRelationshipAllowingSourceNode, 9 | IRelationshipAllowingTargetNode 10 | { 11 | public Requires(NodeReference otherUser, Payload data) 12 | : base(otherUser, data) 13 | {} 14 | 15 | public class Payload 16 | { 17 | public int Count { get; set; } 18 | } 19 | 20 | public override string RelationshipTypeKey 21 | { 22 | get { return "REQUIRES"; } 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Relationships/StoredIn.cs: -------------------------------------------------------------------------------- 1 | using Neo4jClient.Tests.Domain; 2 | 3 | namespace Neo4jClient.Tests.Relationships 4 | { 5 | public class StoredIn : 6 | Relationship, 7 | IRelationshipAllowingSourceNode, 8 | IRelationshipAllowingSourceNode, 9 | IRelationshipAllowingTargetNode 10 | { 11 | public StoredIn(NodeReference otherNode) 12 | : base(otherNode) 13 | {} 14 | 15 | public override string RelationshipTypeKey 16 | { 17 | get { return "STORED_IN"; } 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/TestUtilities.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Neo4jClient.Tests 4 | { 5 | public static class TestUtilities 6 | { 7 | public static bool IsEqualTo(this IDictionary d1, IDictionary d2) 8 | { 9 | if (d1 == null && d2 == null) 10 | return true; 11 | if (d1 == null || d2 == null) 12 | return false; 13 | 14 | if (d1.Count != d2.Count) 15 | return false; 16 | 17 | foreach (var d1Key in d1.Keys) 18 | { 19 | if (!d2.ContainsKey(d1Key)) 20 | return false; 21 | 22 | var v1 = d1[d1Key]; 23 | var v2 = d2[d1Key]; 24 | if (v1?.GetType() == typeof(Dictionary)) 25 | return IsEqualTo((IDictionary)v1, (IDictionary)v2); 26 | 27 | if(!Equals(d1[d1Key], d2[d1Key])) 28 | return false; 29 | } 30 | return true; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/TestsImplementFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests 7 | { 8 | public class TestsImplementFixture : IClassFixture 9 | { 10 | [Fact] 11 | public void AllClassesImplementIClassFixture() 12 | { 13 | var typesWithFactsNotImplementingIClassFixture = 14 | //Get this assembly and it's types. 15 | Assembly.GetExecutingAssembly().GetTypes() 16 | //Get the types with their interfaces and methods 17 | .Select(type => new {type, interfaces = type.GetInterfaces(), methods = type.GetMethods()}) 18 | //First we only want types where they have a method which is a 'Fact' 19 | .Where(t => t.methods.Select(Attribute.GetCustomAttributes).Any(attributes => attributes.Any(a => a.GetType() == typeof(FactAttribute)))) 20 | //Then check if that type implements the type 21 | .Where(t => t.interfaces.All(i => i != typeof(IClassFixture))) 22 | //Select the name 23 | .Select(t => t.type.FullName) 24 | //ToList it to avoid multiple enumeration 25 | .ToList(); 26 | 27 | if (typesWithFactsNotImplementingIClassFixture.Any()) 28 | throw new InvalidOperationException($"All test classes must implement {nameof(IClassFixture)}{Environment.NewLine}These don't:{Environment.NewLine} * {string.Join($"{Environment.NewLine} * ", typesWithFactsNotImplementingIClassFixture)}"); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Transactions/Neo4jTransactionResourceManagerTests.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Tests.Transactions 2 | { 3 | 4 | public class Neo4jTransactionResourceManagerTests 5 | { 6 | 7 | } 8 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/Transactions/TransactionExecutionEnvironmentTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Neo4jClient.Execution; 3 | using Neo4jClient.Transactions; 4 | using Xunit; 5 | 6 | namespace Neo4jClient.Tests.Transactions 7 | { 8 | 9 | public class TransactionExecutionEnvironmentTests : IClassFixture 10 | { 11 | [Fact] 12 | public void ResourceManagerIdDefaultValueIsSet() 13 | { 14 | var configuration = new ExecutionConfiguration(); 15 | var executionEnvironment = new TransactionExecutionEnvironment(configuration); 16 | Assert.Equal(configuration.ResourceManagerId, executionEnvironment.ResourceManagerId); 17 | } 18 | 19 | [Fact] 20 | public void UserCanSetResourceManagerId() 21 | { 22 | var resourceManagerId = Guid.NewGuid(); 23 | var configuration = new ExecutionConfiguration {ResourceManagerId = resourceManagerId}; 24 | var executionEnvironment = new TransactionExecutionEnvironment(configuration); 25 | Assert.Equal(resourceManagerId, executionEnvironment.ResourceManagerId); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Neo4jClient.Tests/Transactions/TransactionRestResponseHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient.Tests.Transactions 4 | { 5 | internal static class TransactionRestResponseHelper 6 | { 7 | internal static string ResetTransactionTimer() 8 | { 9 | return new DateTime().AddSeconds(60).ToString("ddd, dd, MMM yyyy HH:mm:ss +0000"); 10 | } 11 | 12 | internal static string GenerateInitTransactionResponse(int id, string results) 13 | { 14 | return string.Format( 15 | @"{{'commit': 'http://foo/db/data/transaction/{0}/commit', 'results': [{1}], 'errors': [], 'transaction': {{ 'expires': '{2}' }} }}", 16 | id, 17 | results, ResetTransactionTimer() 18 | ); 19 | } 20 | 21 | internal static string GenerateInitTransactionResponse(int id) 22 | { 23 | return GenerateInitTransactionResponse(id, string.Empty); 24 | } 25 | 26 | internal static string GenerateCypherErrorResponse(int id, string error, string results = "") 27 | { 28 | return string.Format( 29 | @"{{'commit': 'http://foo/db/data/transaction/{0}/commit', 'results': [{1}], 'errors': [{2}], 'transaction': {{ 'expires': '{3}' }} }}", 30 | id, 31 | results, 32 | error, 33 | ResetTransactionTimer() 34 | ); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Neo4jClient.Tests/UtilitiesTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Xunit; 4 | 5 | namespace Neo4jClient.Tests 6 | { 7 | 8 | public class UtilitiesTests : IClassFixture 9 | { 10 | [Fact] 11 | public void ShouldReturnDifferenceBetweenDictionaries() 12 | { 13 | var dic1 = new Dictionary { { "key1", "value1" }, { "key2", "value2" }, { "key3", "value3" }}; 14 | var dic2 = new Dictionary { { "key1", "newValue1" }, { "key2", "value2" }, { "key3", "newValue3" }}; 15 | var dic3 = new Dictionary { { "key1", "value1" }, { "key2", "value2" }, { "key3", "value3" } }; 16 | var dic4 = new Dictionary { { "key2", "value2" }, { "key3", "newValue3" } }; 17 | 18 | 19 | var differences12 = Utilities.GetDifferencesBetweenDictionaries(dic1, dic2).ToArray(); 20 | var differences13 = Utilities.GetDifferencesBetweenDictionaries(dic1, dic3).ToArray(); 21 | var differences14 = Utilities.GetDifferencesBetweenDictionaries(dic1, dic4).ToArray(); 22 | 23 | Assert.True(differences12.Count() == 2); 24 | Assert.True(differences12.Any(d=>d.FieldName == "key1" && d.OldValue == "value1" && d.NewValue == "newValue1")); 25 | Assert.True(differences12.Any(d => d.FieldName == "key3" && d.OldValue == "value3" && d.NewValue == "newValue3")); 26 | Assert.True(!differences13.Any()); 27 | Assert.True(differences14.Any(d => d.FieldName == "key1" && d.OldValue == "value1" && d.NewValue == "")); 28 | Assert.True(differences14.Any(d => d.FieldName == "key3" && d.OldValue == "value3" && d.NewValue == "newValue3")); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Neo4jClient.Vb.Tests/Neo4jClient.Vb.Tests.vbproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Neo4jClient.Vb.Tests 5 | netcoreapp3.1 6 | 7 | false 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | all 17 | runtime; build; native; contentfiles; analyzers; buildtransitive 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /Neo4jClient.Vb.Tests/WhereTests.vb: -------------------------------------------------------------------------------- 1 | Imports FluentAssertions 2 | Imports Moq 3 | Imports Neo4jClient.Cypher 4 | Imports Xunit 5 | 6 | Public Class WhereTests 7 | 8 | Public Sub CreatesCorrectCypherWhenStringComparisonPassedIn() 9 | Dim mock As New Mock(Of IRawGraphClient)() 10 | 11 | Dim c = New CypherFluentQuery(mock.[Object]).Where(Function(n As Foo) n.Name = "NameIn").Query 12 | c.DebugQueryText.Should().Be("WHERE (n.Name = ""NameIn"")") 13 | End Sub 14 | 15 | 16 | 17 | Public Sub CreatesCorrectCypherWhenUsingClassInstance() 18 | Dim mock As New Mock(Of IRawGraphClient)() 19 | Dim fooInstance As New Foo() 20 | fooInstance.Name = "Bar" 21 | 22 | Dim c = New CypherFluentQuery(mock.[Object]).Where(Function(n As Foo) n.Name = fooInstance.Name).Query 23 | c.DebugQueryText.Should().Be("WHERE (n.Name = ""Bar"")") 24 | End Sub 25 | 26 | End Class 27 | 28 | 29 | Public Class Foo 30 | Public Property Name As String 31 | End Class -------------------------------------------------------------------------------- /Neo4jClient.gpState: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Neo4jClient.ncrunchsolution: -------------------------------------------------------------------------------- 1 | 2 | 1 3 | True 4 | true 5 | true 6 | UseDynamicAnalysis 7 | UseStaticAnalysis 8 | UseStaticAnalysis 9 | UseStaticAnalysis 10 | Run all tests automatically:BFRydWU=;Run all tests manually:BUZhbHNl;Run impacted tests automatically, others manually (experimental!):CklzSW1wYWN0ZWQ=;Run pinned tests automatically, others manually:CElzUGlubmVk 11 | 12 | 13 | -------------------------------------------------------------------------------- /Neo4jClient.nuspec: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Neo4jClient 5 | $version$ 6 | Readify, Tatham Oddie, Charlotte Skardon 7 | Charlotte Skardon 8 | https://github.com/DotNet4Neo4j/Neo4jClient/blob/master/LICENSE 9 | https://github.com/DotNet4Neo4j/Neo4jClient 10 | A .NET client for neo4j: an open source, transactional graph database. It's pretty awesome. 11 | neo4j nosql cypher bolt graph 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Neo4jClient.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | False 3 | False 4 | False 5 | <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> 6 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> 7 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> 8 | <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> 9 | <Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static fields (private)"><ElementKinds><Kind Name="FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy> 10 | True -------------------------------------------------------------------------------- /Neo4jClient/AddressResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Neo4j.Driver; 5 | 6 | namespace Neo4jClient 7 | { 8 | internal class AddressResolver : IServerAddressResolver 9 | { 10 | private readonly Dictionary> servers = new Dictionary>(); 11 | 12 | public AddressResolver() 13 | :this(null, new string[0]) 14 | { } 15 | 16 | public AddressResolver(string virtualUri, IEnumerable uris) 17 | :this(UriCreator.From(virtualUri), uris?.Select(UriCreator.From)) 18 | {} 19 | 20 | public AddressResolver(Uri virtualUri, IEnumerable uris) 21 | { 22 | servers.Add(ServerAddress.From(virtualUri), uris == null 23 | ? new List() 24 | : new List(uris.Select(ServerAddress.From))); 25 | } 26 | 27 | public ISet Resolve(ServerAddress address) 28 | { 29 | if(address == null && servers.Keys.Count > 1) 30 | throw new InvalidOperationException("Unable to resolve addresses to use as no virtual uri passed in."); 31 | 32 | var localServers = address == null || !servers.ContainsKey(address) ? servers.First().Value : servers[address]; 33 | return new HashSet(localServers); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /Neo4jClient/AggregateExceptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace Neo4jClient 5 | { 6 | static class AggregateExceptionExtensions 7 | { 8 | internal static bool TryUnwrap(this AggregateException ex, out Exception exception) 9 | { 10 | ex = ex.Flatten(); 11 | if (ex.InnerExceptions.Count() == 1) 12 | { 13 | exception = ex.InnerExceptions.Single(); 14 | return true; 15 | } 16 | exception = null; 17 | return false; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Neo4jClient/AmbiguousRelationshipDirectionException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient 4 | { 5 | public class AmbiguousRelationshipDirectionException : Exception 6 | { 7 | public AmbiguousRelationshipDirectionException() 8 | : base("The direction of the relationship is ambiguous as both node participants are valid as either source, or target nodes. Specify the relationship direction explicitly or ammend the relationship definition to be more restrictive around which node types are allowed as sources and targets.") 9 | {} 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/BatchResponse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Neo4jClient.ApiModels 5 | { 6 | class BatchResponse : List 7 | { 8 | public BatchStepResult this[BatchStep step] 9 | { 10 | get { return this.SingleOrDefault(r => r.Id == step.Id); } 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/BatchStep.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | using System.Net.Http; 3 | using Newtonsoft.Json; 4 | 5 | namespace Neo4jClient.ApiModels 6 | { 7 | [DebuggerDisplay("{Id}: {Method} {To}")] 8 | class BatchStep 9 | { 10 | [JsonIgnore] 11 | public HttpMethod Method { get; set; } 12 | 13 | [JsonProperty("method")] 14 | public string MethodAsString 15 | { 16 | get { return Method.Method; } 17 | } 18 | 19 | [JsonProperty("to")] 20 | public string To { get; set; } 21 | 22 | [JsonProperty("body")] 23 | public object Body { get; set; } 24 | 25 | [JsonProperty("id")] 26 | public int Id { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/BatchStepExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net.Http; 3 | 4 | namespace Neo4jClient.ApiModels 5 | { 6 | static class BatchStepExtensions 7 | { 8 | public static BatchStep Add(this IList list, HttpMethod method, string to, object body) 9 | { 10 | var id = list.Count; 11 | var step = new BatchStep {Method = method, To = to, Body = body, Id = id}; 12 | list.Add(step); 13 | return step; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/BatchStepResult.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using Newtonsoft.Json; 3 | 4 | namespace Neo4jClient.ApiModels 5 | { 6 | class BatchStepResult 7 | { 8 | [JsonProperty("id")] 9 | public int Id { get; set; } 10 | 11 | [JsonProperty("from")] 12 | public string From { get; set; } 13 | 14 | [JsonProperty("location")] 15 | public string Location { get; set; } 16 | 17 | [JsonProperty("status")] 18 | public HttpStatusCode Status { get; set; } 19 | 20 | [JsonProperty("body")] 21 | public string Body { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/Cypher/CypherApiQuery.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Neo4jClient.Cypher; 3 | using Newtonsoft.Json; 4 | 5 | namespace Neo4jClient.ApiModels.Cypher 6 | { 7 | class CypherApiQuery 8 | { 9 | public CypherApiQuery(CypherQuery query) 10 | { 11 | Query = query.QueryText; 12 | Parameters = query.QueryParameters ?? new Dictionary(); 13 | } 14 | 15 | [JsonProperty("query")] 16 | public string Query { get; } 17 | 18 | [JsonProperty("params")] 19 | public IDictionary Parameters { get; } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/Cypher/CypherTransactionStatement.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Neo4jClient.Cypher; 3 | using Newtonsoft.Json; 4 | 5 | namespace Neo4jClient.ApiModels.Cypher 6 | { 7 | /// 8 | /// Very similar to CypherApiQuery but it's used for opened transactions as their serialization 9 | /// is different 10 | /// 11 | internal class CypherTransactionStatement 12 | { 13 | private readonly string[] formatContents; 14 | 15 | public CypherTransactionStatement(CypherQuery query) 16 | { 17 | Statement = query.QueryText; 18 | Parameters = query.QueryParameters ?? new Dictionary(); 19 | formatContents = new string[] {}; 20 | if (query.IncludeQueryStats) 21 | IncludeStats = query.IncludeQueryStats; 22 | } 23 | 24 | [JsonProperty("statement")] 25 | public string Statement { get; } 26 | 27 | [JsonProperty("resultDataContents")] 28 | public IEnumerable ResultDataContents => formatContents; 29 | 30 | [JsonProperty("parameters")] 31 | public IDictionary Parameters { get; } 32 | 33 | [JsonProperty("includeStats", NullValueHandling = NullValueHandling.Ignore)] 34 | public bool? IncludeStats { get; } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/Cypher/PathsResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Newtonsoft.Json; 3 | 4 | namespace Neo4jClient.ApiModels.Cypher 5 | { 6 | public class PathsResult 7 | { 8 | [JsonProperty("start")] 9 | public string Start { get; set; } 10 | 11 | [JsonProperty("nodes")] 12 | public List Nodes { get; set; } 13 | 14 | [JsonProperty("length")] 15 | public int Length { get; set; } 16 | 17 | [JsonProperty("relationships")] 18 | public List Relationships { get; set; } 19 | 20 | [JsonProperty("end")] 21 | public string End { get; set; } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/Cypher/QueryStats.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Neo4jClient.ApiModels.Cypher 4 | { 5 | public class QueryStats 6 | { 7 | [JsonProperty("contains_updates")] public bool ContainsUpdates { get; set; } 8 | [JsonProperty("nodes_created")] public int NodesCreated { get; set; } 9 | [JsonProperty("nodes_deleted")] public int NodesDeleted { get; set; } 10 | [JsonProperty("properties_set")] public int PropertiesSet { get; set; } 11 | [JsonProperty("relationships_created")] public int RelationshipsCreated { get; set; } 12 | [JsonProperty("relationship_deleted")] public int RelationshipsDeleted { get; set; } 13 | [JsonProperty("labels_added")] public int LabelsAdded { get; set; } 14 | [JsonProperty("labels_removed")] public int LabelsRemoved { get; set; } 15 | [JsonProperty("indexes_added")] public int IndexesAdded { get; set; } 16 | [JsonProperty("indexes_removed")] public int IndexesRemoved { get; set; } 17 | [JsonProperty("constraints_added")] public int ConstraintsAdded { get; set; } 18 | [JsonProperty("constraints_removed")] public int ConstraintsRemoved { get; set; } 19 | [JsonProperty("contains_system_updates")] public bool ContainsSystemUpdates { get; set; } 20 | [JsonProperty("system_updates")] public int SystemUpdates { get; set; } 21 | 22 | public QueryStats(){} 23 | public QueryStats(Neo4j.Driver.ICounters counters) 24 | { 25 | ContainsUpdates = counters.ContainsUpdates; 26 | NodesCreated = counters.NodesCreated; 27 | NodesDeleted = counters.NodesDeleted; 28 | PropertiesSet = counters.PropertiesSet; 29 | RelationshipsCreated = counters.RelationshipsCreated; 30 | RelationshipsDeleted = counters.RelationshipsDeleted; 31 | LabelsAdded = counters.LabelsAdded; 32 | LabelsRemoved = counters.LabelsRemoved; 33 | IndexesAdded = counters.IndexesAdded; 34 | IndexesRemoved = counters.IndexesRemoved; 35 | ConstraintsAdded = counters.ConstraintsAdded; 36 | ConstraintsRemoved = counters.ConstraintsRemoved; 37 | ContainsSystemUpdates = counters.ContainsSystemUpdates; 38 | SystemUpdates = counters.SystemUpdates; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/ExceptionResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Neo4jClient.ApiModels 4 | { 5 | internal class ExceptionResponse 6 | { 7 | [JsonProperty("message")] 8 | public string Message { get; set; } 9 | 10 | [JsonProperty("exception")] 11 | public string Exception { get; set; } 12 | 13 | [JsonProperty("fullname")] 14 | public string FullName { get; set; } 15 | 16 | [JsonProperty("stacktrace")] 17 | public string[] StackTrace { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/FieldChange.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.ApiModels 2 | { 3 | public class FieldChange 4 | { 5 | public string FieldName { get; set; } 6 | public string OldValue { get; set; } 7 | public string NewValue { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/NodeApiResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using Newtonsoft.Json; 5 | 6 | namespace Neo4jClient.ApiModels 7 | { 8 | internal class NodeApiResponse 9 | { 10 | [JsonProperty("self")] 11 | public string Self { get; set; } 12 | 13 | [JsonProperty("data")] 14 | public TNode Data { get; set; } 15 | 16 | public Node ToNode(IGraphClient client) 17 | { 18 | var nodeId = long.Parse(GetLastPathSegment(Self)); 19 | return new Node(Data, new NodeReference(nodeId, client)); 20 | } 21 | 22 | static string GetLastPathSegment(string uri) 23 | { 24 | var path = new Uri(uri).AbsolutePath; 25 | return path 26 | .Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) 27 | .LastOrDefault(); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/NodeOrRelationshipApiResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Neo4jClient.ApiModels 4 | { 5 | internal class NodeOrRelationshipApiResponse 6 | { 7 | [JsonProperty("data")] 8 | public TNode Data { get; set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/RelationshipApiResponse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using Newtonsoft.Json; 5 | 6 | namespace Neo4jClient.ApiModels 7 | { 8 | internal class RelationshipApiResponse 9 | where TData : class, new() 10 | { 11 | [JsonProperty("self")] 12 | public string Self { get; set; } 13 | 14 | [JsonProperty("start")] 15 | public string Start { get; set; } 16 | 17 | [JsonProperty("end")] 18 | public string End { get; set; } 19 | 20 | [JsonProperty("type")] 21 | public string TypeKey { get; set; } 22 | 23 | [JsonProperty("data")] 24 | public TData Data { get; set; } 25 | 26 | public RelationshipInstance ToRelationshipInstance(IGraphClient client) 27 | { 28 | var relationshipId = long.Parse(GetLastPathSegment(Self)); 29 | var startNodeId = long.Parse(GetLastPathSegment(Start)); 30 | var endNodeId = long.Parse(GetLastPathSegment(End)); 31 | 32 | return new RelationshipInstance( 33 | new RelationshipReference(relationshipId, client), 34 | new NodeReference(startNodeId, client), 35 | new NodeReference(endNodeId, client), 36 | TypeKey, 37 | Data); 38 | } 39 | 40 | public RelationshipReference ToRelationshipReference(IGraphClient client) 41 | { 42 | var relationshipId = int.Parse(GetLastPathSegment(Self)); 43 | return new RelationshipReference(relationshipId, client); 44 | } 45 | 46 | static string GetLastPathSegment(string uri) 47 | { 48 | var path = new Uri(uri).AbsolutePath; 49 | return path 50 | .Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) 51 | .LastOrDefault(); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Neo4jClient/ApiModels/RelationshipTemplate.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Neo4jClient.ApiModels 4 | { 5 | class RelationshipTemplate 6 | { 7 | [JsonProperty("to")] 8 | public string To { get; set; } 9 | 10 | [JsonProperty("data", NullValueHandling = NullValueHandling.Ignore)] 11 | public object Data { get; set; } 12 | 13 | [JsonProperty("type")] 14 | public string Type { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Neo4jClient/Attributes/Neo4jDateTimeAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient 4 | { 5 | /// 6 | /// If this is used on a property - it will be serialized as a Neo4j Date object rather than a string 7 | /// 8 | public class Neo4jDateTimeAttribute : Attribute 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Neo4jClient/Attributes/Neo4jIgnoreAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient 4 | { 5 | [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] 6 | public class Neo4jIgnoreAttribute : Attribute 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /Neo4jClient/Cypher/All.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient.Cypher 4 | { 5 | /// 6 | /// Represents a star in a Cypher function, so All.Count() 7 | /// is equivalent to count(*). Only for use in return expressions 8 | /// like .Return(() => new { Count = All.Count() }), or start 9 | /// expressions like Start(new { n = All.Nodes }), but not to be 10 | /// called directly. (This class is just syntactic sugar for lambda expressions; 11 | /// there is no .NET implementation of its methods.) 12 | /// 13 | public abstract class All 14 | { 15 | /// 16 | /// Equivalent to count(*) 17 | /// http://docs.neo4j.org/chunked/stable/query-aggregation.html#_count 18 | /// 19 | public static long Count() 20 | { 21 | throw new InvalidOperationException("This method can't be executed directly: it has no .NET implementation. You need to use it as part of a Cypher return expression, like .Return(() => new { Count = All.Count() });"); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Neo4jClient/Cypher/CypherFluentQuery`TResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | using System.Threading.Tasks; 4 | 5 | namespace Neo4jClient.Cypher 6 | { 7 | [DebuggerDisplay("{Query.DebugQueryText}")] 8 | public class CypherFluentQuery : 9 | CypherFluentQuery, 10 | IOrderedCypherFluentQuery 11 | { 12 | public CypherFluentQuery(IGraphClient client, QueryWriter writer, bool isWrite = true, bool includeQueryStats = false) 13 | : base(client, writer, isWrite, includeQueryStats) 14 | {} 15 | 16 | public new ICypherFluentQuery Unwind(string collectionName, string columnName) 17 | { 18 | return Mutate(w => w.AppendClause($"UNWIND {collectionName} AS {columnName}")); 19 | } 20 | 21 | public new ICypherFluentQuery Limit(int? limit) 22 | { 23 | return limit.HasValue 24 | ? Mutate(w => w.AppendClause("LIMIT {0}", limit)) 25 | : this; 26 | } 27 | 28 | public new ICypherFluentQuery Skip(int? skip) 29 | { 30 | return skip.HasValue 31 | ? Mutate(w => w.AppendClause("SKIP {0}", skip)) 32 | : this; 33 | } 34 | 35 | public new IOrderedCypherFluentQuery OrderBy(params string[] properties) 36 | { 37 | return MutateOrdered(w => 38 | w.AppendClause($"ORDER BY {string.Join(", ", properties)}")); 39 | } 40 | 41 | public new IOrderedCypherFluentQuery OrderByDescending(params string[] properties) 42 | { 43 | return MutateOrdered(w => 44 | w.AppendClause($"ORDER BY {string.Join(" DESC, ", properties)} DESC")); 45 | } 46 | 47 | public new IOrderedCypherFluentQuery ThenBy(params string[] properties) 48 | { 49 | return MutateOrdered(w => 50 | w.AppendToClause($", {string.Join(", ", properties)}")); 51 | } 52 | 53 | public new IOrderedCypherFluentQuery ThenByDescending(params string[] properties) 54 | { 55 | return MutateOrdered(w => 56 | w.AppendToClause($", {string.Join(" DESC, ", properties)} DESC")); 57 | } 58 | 59 | public Task> ResultsAsync => Client.ExecuteGetCypherResultsAsync(Query); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Neo4jClient/Cypher/CypherFluentQuery`WithBookmark.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using Neo4j.Driver; 3 | 4 | namespace Neo4jClient.Cypher 5 | { 6 | public partial class CypherFluentQuery 7 | { 8 | /// 9 | public ICypherFluentQuery WithBookmark(string bookmark) 10 | { 11 | if (string.IsNullOrWhiteSpace(bookmark)) 12 | return this; 13 | 14 | QueryWriter.Bookmarks.Add(Bookmark.From(bookmark)); 15 | return this; 16 | } 17 | 18 | /// 19 | public ICypherFluentQuery WithBookmarks(params string[] bookmarks) 20 | { 21 | if (bookmarks == null) 22 | return this; 23 | 24 | QueryWriter.Bookmarks.AddRange(bookmarks.Where(b => !string.IsNullOrWhiteSpace(b)).Select(b => Bookmark.From(b))); 25 | return this; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /Neo4jClient/Cypher/CypherPlanner.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Cypher 2 | { 3 | public enum CypherPlanner 4 | { 5 | /// The rule based planner (RULE) 6 | Rule, 7 | /// The new cost based planner (IDP) 8 | CostIdp, 9 | /// The first cost based planner (COST) 10 | CostGreedy 11 | } 12 | } -------------------------------------------------------------------------------- /Neo4jClient/Cypher/CypherResultMode.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Cypher 2 | { 3 | public enum CypherResultMode 4 | { 5 | /// 6 | /// In this mode, we expect the Cypher table to contain a single column. When deserializing it, 7 | /// instead of giving the developer a list of one-column rows, we'll just give them a list of objects. 8 | /// Effectively, we unwrap the column into a straight up array of entries. This is done to make the 9 | /// syntax a little nicer when a developer wants to return a single identity and not a full table. 10 | /// 11 | Set, 12 | 13 | /// 14 | /// This is the default mode, and tells the serializer to treat each row as one object to deserialize. 15 | /// 16 | Projection 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Neo4jClient/Cypher/CypherReturnExpressionBuilder.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotNet4Neo4j/Neo4jClient/a18f7a74d1b424251895796479c13ab94c5e7cac/Neo4jClient/Cypher/CypherReturnExpressionBuilder.cs -------------------------------------------------------------------------------- /Neo4jClient/Cypher/CypherRuntime.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Cypher 2 | { 3 | public enum CypherRuntime 4 | { 5 | /// The default for the Community Edition of Neo4j 6 | Slotted, 7 | /// The default for the Enterprise Edition of Neo4j 8 | Pipelined, 9 | /// Allows for multi-threaded execution of queries. 10 | /// This does not always result in increased performance. 11 | Parallel, 12 | } 13 | } -------------------------------------------------------------------------------- /Neo4jClient/Cypher/CypherWhereExpressionBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq.Expressions; 3 | 4 | namespace Neo4jClient.Cypher 5 | { 6 | public class CypherWhereExpressionBuilder 7 | { 8 | public static string BuildText( 9 | LambdaExpression expression, 10 | Func createParameterCallback, 11 | CypherCapabilities capabilities = null, 12 | bool camelCaseProperties = false) 13 | { 14 | capabilities = capabilities ?? CypherCapabilities.Default; 15 | 16 | if (expression.NodeType == ExpressionType.Lambda && 17 | expression.Body.NodeType == ExpressionType.MemberAccess) 18 | throw new NotSupportedException("Member access expressions, like Where(f => f.Foo), are not supported because these become ambiguous between C# and Cypher based on how Neo4j handles null values. Use a comparison instead, like Where(f => f.Foo == true)."); 19 | 20 | var myVisitor = new CypherWhereExpressionVisitor(createParameterCallback, capabilities, camelCaseProperties); 21 | myVisitor.Visit(new VbCompareReplacer().Visit(expression)); 22 | return myVisitor.TextOutput.ToString(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Neo4jClient/Cypher/ICypherFluentQueryAdvanced.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Cypher 2 | { 3 | /// 4 | /// Exposes low level return api to allow more control when developing libraries on top of Neo4jClient 5 | /// 6 | public interface ICypherFluentQueryAdvanced 7 | { 8 | ICypherFluentQuery Return(ReturnExpression returnExpression); 9 | 10 | ICypherFluentQuery ReturnDistinct(ReturnExpression returnExpression); 11 | 12 | ICypherFluentQuery SetClient(IGraphClient graphClient); 13 | ICypherFluentQuery SetClient(IGraphClient graphClient); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Neo4jClient/Cypher/ICypherFluentQuery`TResult.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | 4 | namespace Neo4jClient.Cypher 5 | { 6 | public interface ICypherFluentQuery : ICypherFluentQuery 7 | { 8 | Task> ResultsAsync { get; } 9 | 10 | new ICypherFluentQuery Unwind(string collectionName, string columnName); 11 | new ICypherFluentQuery Limit(int? limit); 12 | new ICypherFluentQuery Skip(int? skip); 13 | new IOrderedCypherFluentQuery OrderBy(params string[] properties); 14 | new IOrderedCypherFluentQuery OrderByDescending(params string[] properties); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Neo4jClient/Cypher/IFluentCypherResultItem.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Cypher 2 | { 3 | public interface IFluentCypherResultItem 4 | { 5 | T CollectAs(); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Neo4jClient/Cypher/IOrderedCypherFluentQuery.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Cypher 2 | { 3 | public interface IOrderedCypherFluentQuery : ICypherFluentQuery 4 | { 5 | IOrderedCypherFluentQuery ThenBy(params string[] properties); 6 | IOrderedCypherFluentQuery ThenByDescending(params string[] properties); 7 | } 8 | } -------------------------------------------------------------------------------- /Neo4jClient/Cypher/IOrderedCypherFluentQuery`TResult.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Cypher 2 | { 3 | public interface IOrderedCypherFluentQuery : IOrderedCypherFluentQuery, ICypherFluentQuery 4 | { 5 | new IOrderedCypherFluentQuery ThenBy(params string[] properties); 6 | new IOrderedCypherFluentQuery ThenByDescending(params string[] properties); 7 | } 8 | } -------------------------------------------------------------------------------- /Neo4jClient/Cypher/Node.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Cypher 2 | { 3 | public static class Node 4 | { 5 | /// 6 | /// Used for Cypher START clauses, like Start(new { foo = Node.ByIndexLookup(…) }) 7 | /// 8 | public static StartBit ByIndexLookup(string indexName, string propertyName, object value) 9 | { 10 | return new StartBit(createParameterCallback => 11 | string.Format( 12 | "node:`{0}`({1} = {2})", 13 | indexName, 14 | propertyName, 15 | createParameterCallback(value))); 16 | } 17 | 18 | /// 19 | /// Used for Cypher START clauses, like Start(new { foo = Node.ByIndexQuery(…) }) 20 | /// 21 | public static StartBit ByIndexQuery(string indexName, string query) 22 | { 23 | return new StartBit(createParameterCallback => 24 | string.Format( 25 | "node:`{0}`({1})", 26 | indexName, 27 | createParameterCallback(query))); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Neo4jClient/Cypher/OrderByType.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Cypher 2 | { 3 | public enum OrderByType {Ascending, Descending} 4 | } -------------------------------------------------------------------------------- /Neo4jClient/Cypher/Relationship.cs: -------------------------------------------------------------------------------- 1 | using Neo4jClient.Cypher; 2 | 3 | // There's already a Neo4jClient.Relationship class that we're going to conflict with 4 | // if we try to have Neo4jClient.Cypher.Relationship. Instead, we keep the code here 5 | // in the Cypher folder and just mash it into the root type. It's ugly, but it works 6 | // well enough for now. Sometime in the not too distant future, I'm also hoping to redo 7 | // the relationship infrastructure, and we'll probably remove that type then. 8 | // ReSharper disable CheckNamespace 9 | namespace Neo4jClient 10 | // ReSharper restore CheckNamespace 11 | { 12 | public partial class Relationship 13 | { 14 | /// 15 | /// Used for Cypher START clauses, like Start(new { foo = Relationship.ByIndexLookup(…) }) 16 | /// 17 | public static StartBit ByIndexLookup(string indexName, string propertyName, object value) 18 | { 19 | return new StartBit(createParameterCallback => 20 | string.Format( 21 | "relationship:`{0}`({1} = {2})", 22 | indexName, 23 | propertyName, 24 | createParameterCallback(value))); 25 | } 26 | 27 | /// 28 | /// Used for Cypher START clauses, like Start(new { foo = Relationship.ByIndexQuery(…) }) 29 | /// 30 | public static StartBit ByIndexQuery(string indexName, string query) 31 | { 32 | return new StartBit(createParameterCallback => 33 | string.Format( 34 | "relationship:`{0}`({1})", 35 | indexName, 36 | createParameterCallback(query))); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Neo4jClient/Cypher/Return.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient.Cypher 4 | { 5 | public static class Return 6 | { 7 | /// 8 | /// Used for Cypher RETURN clauses, like Return(() => new { Foo = Return.As<string>("weird_func(foo).wow") }) 9 | /// 10 | public static T As(string cypherText) 11 | { 12 | throw new InvalidOperationException("This method should only be called from lambda expressions in the RETURN clause of Cypher fluent queries. Do not call it directly: it is just syntactic sugar, there is no .NET implementation."); 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Neo4jClient/Cypher/ReturnExpression.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Cypher 2 | { 3 | public struct ReturnExpression 4 | { 5 | public string Text { get; set; } 6 | public CypherResultMode ResultMode { get; set; } 7 | public CypherResultFormat ResultFormat { get; set; } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Neo4jClient/Cypher/StartBit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using CreateParameterCallback = System.Func; 3 | 4 | namespace Neo4jClient.Cypher 5 | { 6 | public class StartBit 7 | { 8 | readonly Func formatCallback; 9 | 10 | public StartBit(Func formatCallback) 11 | { 12 | this.formatCallback = formatCallback; 13 | } 14 | 15 | public string ToCypherText(CreateParameterCallback createParameterCallback) 16 | { 17 | return formatCallback(createParameterCallback); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Neo4jClient/Cypher/VbComparer.cs: -------------------------------------------------------------------------------- 1 | using System.Linq.Expressions; 2 | using Microsoft.VisualBasic.CompilerServices; 3 | 4 | namespace Neo4jClient.Cypher 5 | { 6 | internal class VbCompareReplacer : ExpressionVisitor 7 | { 8 | public override Expression Visit(Expression node) 9 | { 10 | if (!(node is BinaryExpression)) 11 | { 12 | return base.Visit(node); 13 | } 14 | 15 | var binaryExpression = (BinaryExpression) node; 16 | 17 | if (!(binaryExpression.Left is MethodCallExpression)) 18 | { 19 | return base.Visit(node); 20 | } 21 | 22 | dynamic method = (MethodCallExpression) binaryExpression.Left; 23 | 24 | if (!(method.Method.DeclaringType == typeof (Operators) && method.Method.Name == "CompareString")) 25 | { 26 | return base.Visit(node); 27 | } 28 | 29 | dynamic left = method.Arguments[0]; 30 | dynamic right = method.Arguments[1]; 31 | 32 | return binaryExpression.NodeType == ExpressionType.Equal ? Expression.Equal(left, right) : Expression.NotEqual(left, right); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Neo4jClient/CypherPartialResult.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using Neo4jClient.Serialization; 3 | 4 | namespace Neo4jClient 5 | { 6 | /// 7 | /// This class is only used to hold partial results on the execution of a cypher query. 8 | /// Depending on whether we are running inside a transaction, the result string is already 9 | /// deserialized (because it has to be checked for errors), or we need the full HttpResponseMessage 10 | /// object. 11 | /// 12 | internal class CypherPartialResult 13 | { 14 | public HttpResponseMessage ResponseObject { get; set; } 15 | public PartialDeserializationContext DeserializationContext { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Neo4jClient/DeleteMode.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | public enum DeleteMode 4 | { 5 | NodeOnly, 6 | NodeAndRelationships 7 | } 8 | } -------------------------------------------------------------------------------- /Neo4jClient/DetachedNodeException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient 4 | { 5 | public class DetachedNodeException : Exception 6 | { 7 | public DetachedNodeException() 8 | : base( 9 | "A detached node is being used as the base node of a query. That is, the " + 10 | "node reference was created independently. To execute queries against nodes, " + 11 | "the node reference must have a reference back to an instance of IGraphClient. " + 12 | "The simplest way to achieve this is to retrieve the node from IGraphClient " + 13 | "in the first place, then query against it. Alternatively, you can suppy an " + 14 | "IGraphClient instance to the node reference at time of construction.") 15 | { 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Neo4jClient/Diagrams/Batching.cd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | AAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAA= 7 | ApiModels\BatchResponse.cs 8 | 9 | 10 | 11 | 12 | 13 | AAACAAAAAAAAAAAAAAAhAAAAAAAIAAAAAAAAAAAAAAE= 14 | ApiModels\BatchStep.cs 15 | 16 | 17 | 18 | 19 | 20 | AAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= 21 | ApiModels\BatchStepExtensions.cs 22 | 23 | 24 | 25 | 26 | 27 | AgACAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAA= 28 | ApiModels\BatchStepResult.cs 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Neo4jClient/Diagrams/GraphClientCore.cd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | QAAAAAAAAEAAAAAAAAAQAAIAAAAAAAAAAAAAAAAAACA= 7 | Gremlin\BasicSteps.cs 8 | 9 | 10 | 11 | 12 | 13 | AAASACAAAIAAAAAAgAAAAAAAAAAAAIAACABABAAAAmA= 14 | NodeReference.cs 15 | 16 | 17 | 18 | 19 | 20 | 21 | AAAAAACAAIAAgAAAgAAAAAAAAAAAAJAACAAQAAAAAmA= 22 | Node`TNode.cs 23 | 24 | 25 | 26 | 27 | 28 | 29 | AgQSAAABAAAAACBAADAAAAAAAAEAQEAAIAgAAAAAABQ= 30 | IGraphClient.cs 31 | 32 | 33 | 34 | 35 | 36 | AAAAAAAAAAAACAAAAAAAAQAAAAAAAAAAAAAAAAAAAAQ= 37 | Gremlin\IGremlinQuery.cs 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Neo4jClient/Diagrams/Responses.cd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAA= 7 | ApiModels\ExtensionsApiResponse.cs 8 | 9 | 10 | 11 | 12 | 13 | AAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAA= 14 | ApiModels\GremlinPluginApiResponse.cs 15 | 16 | 17 | 18 | 19 | 20 | AAIAAAAAACAAAAAAAAABAAAAAAAAAAAABAAAAAQAAAA= 21 | ApiModels\RelationshipApiResponse.cs 22 | 23 | 24 | 25 | 26 | 27 | AQAAAAAAAAgAAAAAAAAIAAAEAAAAAQAAAAgAAAEAAAA= 28 | ApiModels\RootApiResponse.cs 29 | 30 | 31 | 32 | 33 | 34 | AAAAEAAAAAAAAAAAAAABAAAAAAAAABAABAAAAAAAAAA= 35 | ApiModels\NodeApiResponse.cs 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Neo4jClient/Execution/BatchExecutionPolicy.cs: -------------------------------------------------------------------------------- 1 | // using System; 2 | // using System.Collections.Generic; 3 | // using System.Linq; 4 | // using System.Text; 5 | // 6 | // namespace Neo4jClient.Execution 7 | // { 8 | // internal class BatchExecutionPolicy : GraphClientBasedExecutionPolicy 9 | // { 10 | // public BatchExecutionPolicy(IGraphClient client) : base(client) 11 | // { 12 | // } 13 | // 14 | // public override TransactionExecutionPolicy TransactionExecutionPolicy 15 | // { 16 | // get { return TransactionExecutionPolicy.Denied; } 17 | // } 18 | // 19 | // public override void AfterExecution(IDictionary executionMetadata, object executionContext) 20 | // { 21 | // } 22 | // 23 | // public override Uri BaseEndpoint(string database = null) 24 | // { 25 | // return Replace(Client.BatchEndpoint, database); 26 | // } 27 | // } 28 | // } 29 | -------------------------------------------------------------------------------- /Neo4jClient/Execution/CypherTransactionExecutionPolicy.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Execution 2 | { 3 | internal class CypherTransactionExecutionPolicy : CypherExecutionPolicy 4 | { 5 | public CypherTransactionExecutionPolicy(IGraphClient client) 6 | : base(client) 7 | { 8 | } 9 | 10 | public override TransactionExecutionPolicy TransactionExecutionPolicy => TransactionExecutionPolicy.Required; 11 | } 12 | } -------------------------------------------------------------------------------- /Neo4jClient/Execution/EndpointBuilderExtension.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient.Execution 4 | { 5 | /// 6 | /// Restricts the end point URI builder to adding paths 7 | /// 8 | internal static class EndpointBuilderExtension 9 | { 10 | public static Uri AddPath(this Uri startUri, string path) 11 | { 12 | var uriBuilder = new UriBuilder(startUri); 13 | if (path.StartsWith("/") || uriBuilder.Path.EndsWith("/")) 14 | { 15 | uriBuilder.Path += path; 16 | } 17 | else 18 | { 19 | uriBuilder.Path += "/" + path; 20 | } 21 | return uriBuilder.Uri; 22 | } 23 | 24 | public static Uri AddQuery(this Uri startUri, string query) 25 | { 26 | var uriBuilder = new UriBuilder(startUri); 27 | if (uriBuilder.Query != null && uriBuilder.Query.Length > 1) 28 | uriBuilder.Query = uriBuilder.Query.Substring(1) + "&" + query; 29 | else 30 | uriBuilder.Query = query; 31 | return uriBuilder.Uri; 32 | } 33 | 34 | public static Uri AddPath(this Uri startUri, object startReference, IExecutionPolicy policy) 35 | { 36 | return policy.AddPath(startUri, startReference); 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Neo4jClient/Execution/ErrorGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | 4 | namespace Neo4jClient.Execution 5 | { 6 | internal class ErrorGenerator 7 | { 8 | public Func Condition { get; set; } 9 | public Func Generator { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /Neo4jClient/Execution/ExecutionConfiguration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Neo4j.Driver; 4 | using Newtonsoft.Json; 5 | 6 | namespace Neo4jClient.Execution 7 | { 8 | public class ExecutionConfiguration 9 | { 10 | public ExecutionConfiguration() 11 | { 12 | ResourceManagerId = new Guid("{BB792575-FAA7-4C72-A6B1-A69876CC3E1E}"); 13 | } 14 | 15 | public IHttpClient HttpClient { get; set; } 16 | public bool UseJsonStreaming { get; set; } 17 | public string UserAgent { get; set; } 18 | public IEnumerable JsonConverters { get; set; } 19 | public string Username { get; set; } 20 | public string Password { get; set; } 21 | public bool HasErrors { get; set; } 22 | public Guid ResourceManagerId { get; set; } 23 | public string Realm { get; set; } 24 | public EncryptionLevel? EncryptionLevel { get; set; } 25 | public bool SerializeNullValues { get; set; } 26 | } 27 | } -------------------------------------------------------------------------------- /Neo4jClient/Execution/ExecutionPolicyFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Neo4jClient.Execution 7 | { 8 | class ExecutionPolicyFactory : IExecutionPolicyFactory 9 | { 10 | private IGraphClient _client; 11 | 12 | public ExecutionPolicyFactory(IGraphClient client) 13 | { 14 | _client = client; 15 | } 16 | 17 | public IExecutionPolicy GetPolicy(PolicyType type) 18 | { 19 | if (!_client.IsConnected) 20 | { 21 | throw new InvalidOperationException("Client has not connected to the Neo4j server"); 22 | } 23 | 24 | // todo: this should be a prototype-based object creation 25 | switch (type) 26 | { 27 | case PolicyType.Cypher: 28 | return new CypherExecutionPolicy(_client); 29 | // case PolicyType.Batch: 30 | // return new BatchExecutionPolicy(_client); 31 | // case PolicyType.Rest: 32 | // return new RestExecutionPolicy(_client); 33 | case PolicyType.Transaction: 34 | return new CypherTransactionExecutionPolicy(_client); 35 | // case PolicyType.NodeIndex: 36 | // return new NodeIndexExecutionPolicy(_client); 37 | // case PolicyType.RelationshipIndex: 38 | // return new RelationshipIndexExecutionPolicy(_client); 39 | default: 40 | throw new InvalidOperationException("Unknown execution policy:" + type.ToString()); 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Neo4jClient/Execution/GraphClientBasedExecutionPolicy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Transactions; 4 | using Neo4jClient.Transactions; 5 | 6 | namespace Neo4jClient.Execution 7 | { 8 | internal abstract class GraphClientBasedExecutionPolicy : IExecutionPolicy 9 | { 10 | protected IGraphClient Client; 11 | 12 | protected GraphClientBasedExecutionPolicy(IGraphClient client) 13 | { 14 | Client = client; 15 | } 16 | 17 | public bool InTransaction => 18 | Client is ITransactionalGraphClient transactionalGraphClient && 19 | (transactionalGraphClient.InTransaction || Transaction.Current != null); 20 | 21 | public abstract TransactionExecutionPolicy TransactionExecutionPolicy { get; } 22 | 23 | public abstract void AfterExecution(IDictionary executionMetadata, object executionContext); 24 | 25 | public virtual string SerializeRequest(object toSerialize) 26 | { 27 | return Client.Serializer.Serialize(toSerialize); 28 | } 29 | 30 | public abstract string Database { get; set; } 31 | 32 | public abstract Uri BaseEndpoint(string database = null, bool autoCommit = false); 33 | 34 | public virtual Uri AddPath(Uri startUri, object startReference) 35 | { 36 | return startUri; 37 | } 38 | 39 | protected Uri Replace(Uri toReplace, string replaceWith) 40 | { 41 | //TODO: Less CopyPasta 42 | return new Uri(toReplace.AbsoluteUri.Replace("{databaseName}", replaceWith ?? Client.DefaultDatabase)); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Neo4jClient/Execution/IExecutionPolicy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Neo4jClient.Execution 5 | { 6 | internal interface IExecutionPolicy 7 | { 8 | bool InTransaction { get; } 9 | TransactionExecutionPolicy TransactionExecutionPolicy { get; } 10 | void AfterExecution(IDictionary executionMetadata, object executionContext); 11 | string SerializeRequest(object toSerialize); 12 | Uri BaseEndpoint(string database, bool autoCommit); 13 | Uri AddPath(Uri startUri, object startReference); 14 | string Database { get; set; } 15 | } 16 | 17 | internal enum TransactionExecutionPolicy 18 | { 19 | Allowed, 20 | Denied, 21 | Required 22 | } 23 | } -------------------------------------------------------------------------------- /Neo4jClient/Execution/IExecutionPolicyFactory.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Execution 2 | { 3 | /// 4 | /// A factory class that returns a policy factory given the type and a IGraphClient connection. 5 | /// 6 | internal interface IExecutionPolicyFactory 7 | { 8 | IExecutionPolicy GetPolicy(PolicyType type); 9 | } 10 | 11 | /// 12 | /// Possible enumerations of queries that a policy may represent 13 | /// 14 | public enum PolicyType 15 | { 16 | Cypher, 17 | Rest, 18 | Batch, 19 | Transaction, 20 | NodeIndex, 21 | RelationshipIndex 22 | } 23 | } -------------------------------------------------------------------------------- /Neo4jClient/Execution/IHttpClient.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using System.Threading.Tasks; 3 | 4 | namespace Neo4jClient.Execution 5 | { 6 | public interface IHttpClient 7 | { 8 | Task SendAsync(HttpRequestMessage request); 9 | string Username { get; } 10 | string Password { get; } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Neo4jClient/Execution/IRequestTypeBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Specialized; 3 | 4 | namespace Neo4jClient.Execution 5 | { 6 | internal interface IRequestTypeBuilder 7 | { 8 | IResponseBuilder Delete(Uri endpoint); 9 | IResponseBuilder Get(Uri endpoint); 10 | IRequestWithPendingContentBuilder Post(Uri endpoint); 11 | IRequestWithPendingContentBuilder Put(Uri endpoint); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Neo4jClient/Execution/IRequestWithPendingContentBuilder.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Execution 2 | { 3 | internal interface IRequestWithPendingContentBuilder 4 | { 5 | IResponseBuilder WithContent(string content); 6 | IResponseBuilder WithJsonContent(string jsonContent); 7 | } 8 | } -------------------------------------------------------------------------------- /Neo4jClient/Execution/IResponseBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | 6 | namespace Neo4jClient.Execution 7 | { 8 | internal interface IResponseBuilder 9 | { 10 | IResponseBuilder WithExpectedStatusCodes(params HttpStatusCode[] statusCodes); 11 | IResponseFailBuilder FailOnCondition(Func condition); 12 | Task ExecuteAsync(); 13 | Task ExecuteAsync(string commandDescription); 14 | Task ExecuteAsync(Func continuationFunction); 15 | Task ExecuteAsync(string commandDescription, Func continuationFunction); 16 | 17 | Task ExecuteAsync(Func continuationFunction); 18 | Task ExecuteAsync(string commandDescription, Func continuationFunction); 19 | 20 | IResponseBuilder ParseAs() where TParse : new(); 21 | } 22 | 23 | internal interface IResponseFailBuilder 24 | { 25 | IResponseBuilder WithError(Func errorBuilder); 26 | IResponseBuilder WithNull(); 27 | } 28 | } -------------------------------------------------------------------------------- /Neo4jClient/Execution/IResponseBuilder`TResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using Neo4jClient.Cypher; 6 | 7 | namespace Neo4jClient.Execution 8 | { 9 | internal interface IResponseBuilder where TResult : new() 10 | { 11 | IResponseBuilder WithExpectedStatusCodes(params HttpStatusCode[] statusCodes); 12 | IResponseFailBuilder FailOnCondition(Func condition); 13 | Task ExecuteAsync(string commandDescription); 14 | Task ExecuteAsync(Func continuationFunction); 15 | Task ExecuteAsync(string commandDescription, Func continuationFunction); 16 | Task ExecuteAsync(); 17 | 18 | Task ExecuteAsync(Func continuationFunction); 19 | Task ExecuteAsync(string commandDescription, Func continuationFunction); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Neo4jClient/Execution/IResponseFailBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | 4 | namespace Neo4jClient.Execution 5 | { 6 | internal interface IResponseFailBuilder where TResult : new() 7 | { 8 | IResponseBuilder WithError(Func errorBuilder); 9 | IResponseBuilder WithDefault(); 10 | } 11 | } -------------------------------------------------------------------------------- /Neo4jClient/Execution/NodeIndexExecutionPolicy.cs: -------------------------------------------------------------------------------- 1 | // using System; 2 | // 3 | // namespace Neo4jClient.Execution 4 | // { 5 | // internal class NodeIndexExecutionPolicy : RestExecutionPolicy 6 | // { 7 | // public NodeIndexExecutionPolicy(IGraphClient client) 8 | // : base(client) 9 | // { 10 | // } 11 | // 12 | // public override Uri BaseEndpoint(string database = null) 13 | // { 14 | // return Replace(Client.NodeIndexEndpoint, database); 15 | // } 16 | // } 17 | // } -------------------------------------------------------------------------------- /Neo4jClient/Execution/RelationshipIndexExecutionPolicy.cs: -------------------------------------------------------------------------------- 1 | // using System; 2 | // 3 | // namespace Neo4jClient.Execution 4 | // { 5 | // internal class RelationshipIndexExecutionPolicy : RestExecutionPolicy 6 | // { 7 | // public RelationshipIndexExecutionPolicy(IGraphClient client) 8 | // : base(client) 9 | // { 10 | // } 11 | // 12 | // public override Uri BaseEndpoint(string database = null) 13 | // { 14 | // return Replace(Client.RelationshipIndexEndpoint, database); 15 | // } 16 | // } 17 | // } -------------------------------------------------------------------------------- /Neo4jClient/Execution/Request.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Specialized; 2 | 3 | namespace Neo4jClient.Execution 4 | { 5 | internal static class Request 6 | { 7 | public static IRequestTypeBuilder With(ExecutionConfiguration configuration, NameValueCollection customerHeaders = null, int? maxExecutionTime = null) 8 | { 9 | return new RequestTypeBuilder(configuration, customerHeaders, maxExecutionTime); 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /Neo4jClient/Execution/RequestTypeBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.Linq; 5 | using System.Net.Http; 6 | using System.Text; 7 | 8 | namespace Neo4jClient.Execution 9 | { 10 | internal class RequestTypeBuilder : IRequestTypeBuilder 11 | { 12 | private readonly ExecutionConfiguration executionConfiguration; 13 | private readonly NameValueCollection customHeaders; 14 | private readonly int? maxExecutionTime; 15 | 16 | public RequestTypeBuilder(ExecutionConfiguration executionConfiguration, NameValueCollection customHeaders, int? maxExecutionTime) 17 | { 18 | this.executionConfiguration = executionConfiguration; 19 | this.customHeaders = customHeaders; 20 | this.maxExecutionTime = maxExecutionTime; 21 | } 22 | 23 | public IResponseBuilder Delete(Uri endpoint) 24 | { 25 | return new ResponseBuilder(new HttpRequestMessage(HttpMethod.Delete, endpoint), executionConfiguration, customHeaders); 26 | } 27 | 28 | public IResponseBuilder Get(Uri endpoint) 29 | { 30 | return new ResponseBuilder(new HttpRequestMessage(HttpMethod.Get, endpoint), executionConfiguration, customHeaders); 31 | } 32 | 33 | public IRequestWithPendingContentBuilder Post(Uri endpoint) 34 | { 35 | return new RequestWithPendingContentBuilder(HttpMethod.Post, endpoint, executionConfiguration, customHeaders, maxExecutionTime); 36 | } 37 | 38 | public IRequestWithPendingContentBuilder Put(Uri endpoint) 39 | { 40 | return new RequestWithPendingContentBuilder(HttpMethod.Put, endpoint, executionConfiguration, customHeaders, maxExecutionTime); 41 | } 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /Neo4jClient/Execution/RequestWithPendingContentBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Specialized; 3 | using System.Net.Http; 4 | using System.Text; 5 | 6 | namespace Neo4jClient.Execution 7 | { 8 | internal class RequestWithPendingContentBuilder : IRequestWithPendingContentBuilder 9 | { 10 | private readonly HttpMethod httpMethod; 11 | private readonly Uri endpoint; 12 | private readonly ExecutionConfiguration executionConfiguration; 13 | private readonly NameValueCollection customHeaders; 14 | private readonly int? maxExecutionTime; 15 | 16 | public RequestWithPendingContentBuilder(HttpMethod httpMethod, Uri endpoint, ExecutionConfiguration executionConfiguration, NameValueCollection customHeaders, int? maxExecutionTime) 17 | { 18 | this.httpMethod = httpMethod; 19 | this.endpoint = endpoint; 20 | this.executionConfiguration = executionConfiguration; 21 | this.customHeaders = customHeaders; 22 | this.maxExecutionTime = maxExecutionTime; 23 | } 24 | 25 | public IResponseBuilder WithContent(string content) 26 | { 27 | return new ResponseBuilder( 28 | new HttpRequestMessage(httpMethod, endpoint) 29 | { 30 | Content = new StringContent(content, Encoding.UTF8) 31 | }, 32 | executionConfiguration, 33 | customHeaders, 34 | maxExecutionTime 35 | ); 36 | } 37 | 38 | public IResponseBuilder WithJsonContent(string jsonContent) 39 | { 40 | return new ResponseBuilder( 41 | new HttpRequestMessage(httpMethod, endpoint) 42 | { 43 | Content = new StringContent(jsonContent, Encoding.UTF8, "application/json") 44 | }, 45 | executionConfiguration, 46 | customHeaders, 47 | maxExecutionTime 48 | ); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Neo4jClient/Execution/ResponseFailBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using System.Net; 5 | using System.Net.Http; 6 | 7 | namespace Neo4jClient.Execution 8 | { 9 | internal class ResponseFailBuilder : IResponseFailBuilder 10 | { 11 | private readonly HttpRequestMessage _request; 12 | private readonly ISet _expectedStatusCodes; 13 | private readonly ExecutionConfiguration _executionConfiguration; 14 | private readonly IList _errorGenerators; 15 | private readonly Func _errorCondition; 16 | private readonly NameValueCollection _customHeaders; 17 | 18 | public ResponseFailBuilder(HttpRequestMessage request, ISet expectedStatusCodes, 19 | ExecutionConfiguration executionConfiguration, IList errorGenerators, 20 | Func errorCondition, NameValueCollection customHeaders) 21 | { 22 | _request = request; 23 | _expectedStatusCodes = expectedStatusCodes; 24 | _executionConfiguration = executionConfiguration; 25 | _errorGenerators = errorGenerators; 26 | _errorCondition = errorCondition; 27 | _customHeaders = customHeaders; 28 | } 29 | 30 | public IResponseBuilder WithError(Func errorBuilder) 31 | { 32 | var newGenerators = new List(_errorGenerators) 33 | { 34 | new ErrorGenerator 35 | { 36 | Condition = _errorCondition, 37 | Generator = errorBuilder 38 | } 39 | }; 40 | 41 | return new ResponseBuilder( 42 | _request, 43 | _expectedStatusCodes, 44 | _executionConfiguration, 45 | newGenerators, 46 | _customHeaders 47 | ); 48 | } 49 | 50 | public IResponseBuilder WithNull() 51 | { 52 | var newGenerators = new List(_errorGenerators) 53 | { 54 | new ErrorGenerator 55 | { 56 | Condition = _errorCondition, 57 | Generator = null 58 | } 59 | }; 60 | 61 | return new ResponseBuilder( 62 | _request, 63 | _expectedStatusCodes, 64 | _executionConfiguration, 65 | newGenerators 66 | ,_customHeaders 67 | ); 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /Neo4jClient/Execution/ResponseFailBuilder`TParse.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Net; 5 | using System.Net.Http; 6 | using System.Text; 7 | 8 | namespace Neo4jClient.Execution 9 | { 10 | internal class ResponseFailBuilder : IResponseFailBuilder where TParse : new() 11 | { 12 | private readonly HttpRequestMessage _request; 13 | private readonly ISet _expectedStatusCodes; 14 | private readonly ExecutionConfiguration _executionConfiguration; 15 | private readonly IList _errorGenerators; 16 | private readonly Func _errorCondition; 17 | 18 | public ResponseFailBuilder(HttpRequestMessage request, ISet expectedStatusCodes, 19 | ExecutionConfiguration executionConfiguration, IList errorGenerators, 20 | Func condition) 21 | { 22 | _request = request; 23 | _expectedStatusCodes = expectedStatusCodes; 24 | _executionConfiguration = executionConfiguration; 25 | _errorGenerators = errorGenerators; 26 | _errorCondition = condition; 27 | } 28 | 29 | public IResponseBuilder WithError(Func errorBuilder) 30 | { 31 | var newGenerators = new List(_errorGenerators) 32 | { 33 | new ErrorGenerator 34 | { 35 | Condition = _errorCondition, 36 | Generator = errorBuilder 37 | } 38 | }; 39 | return new ResponseBuilder(_request, _expectedStatusCodes, _executionConfiguration, newGenerators); 40 | } 41 | 42 | public IResponseBuilder WithDefault() 43 | { 44 | var newGenerators = new List(_errorGenerators) 45 | { 46 | new ErrorGenerator 47 | { 48 | Condition = _errorCondition, 49 | Generator = null 50 | } 51 | }; 52 | return new ResponseBuilder(_request, _expectedStatusCodes, _executionConfiguration, newGenerators); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Neo4jClient/Execution/RestExecutionPolicy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Neo4jClient.Execution 7 | { 8 | //TODO : Remove this 9 | internal class RestExecutionPolicy : GraphClientBasedExecutionPolicy 10 | { 11 | 12 | 13 | public RestExecutionPolicy(IGraphClient client) : base(client) 14 | { 15 | } 16 | 17 | public override TransactionExecutionPolicy TransactionExecutionPolicy => TransactionExecutionPolicy.Denied; 18 | 19 | public override void AfterExecution(IDictionary executionMetadata, object executionContext) 20 | { 21 | } 22 | 23 | public override Uri BaseEndpoint(string database = null, bool autoCommit = false) 24 | { 25 | return Replace(Client.RootEndpoint, database); 26 | } 27 | 28 | public override Uri AddPath(Uri startUri, object startReference) 29 | { 30 | if (startReference == null) 31 | { 32 | return startUri; 33 | } 34 | 35 | if (startReference is NodeReference) 36 | { 37 | return AddPath(startUri, startReference as NodeReference); 38 | } 39 | 40 | if (startReference is RelationshipReference) 41 | { 42 | return AddPath(startUri, startReference as RelationshipReference); 43 | } 44 | 45 | throw new NotImplementedException("Unknown startReference parameter for REST policy"); 46 | } 47 | 48 | public override string Database { get; set; } 49 | 50 | private Uri AddPath(Uri startUri, NodeReference node) 51 | { 52 | return startUri.AddPath("node").AddPath(node.Id.ToString()); 53 | } 54 | 55 | private Uri AddPath(Uri startUri, RelationshipReference relationship) 56 | { 57 | return startUri.AddPath("relationship").AddPath(relationship.Id.ToString()); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /Neo4jClient/Extensions/DriverExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Neo4jClient.Extensions 6 | { 7 | using System.Linq; 8 | using Neo4j.Driver; 9 | 10 | public static class DriverExtensions 11 | { 12 | 13 | public static IAsyncSession AsyncSession(this IDriver driver, Version databaseVersion, string database, bool isWrite, IEnumerable bookmarks) 14 | { 15 | IEnumerable properBookmarks = null; 16 | if (bookmarks != null) 17 | properBookmarks = bookmarks.Select(b => Bookmark.From(b)); 18 | 19 | return driver.AsyncSession(databaseVersion, database, isWrite, properBookmarks); 20 | } 21 | 22 | public static IAsyncSession AsyncSession(this IDriver driver, Version databaseVersion, string database, bool isWrite, IEnumerable bookmarks) 23 | { 24 | return driver.AsyncSession(builder => 25 | { 26 | if(databaseVersion.Major >= 4) 27 | builder.WithDatabase(database); 28 | builder.WithDefaultAccessMode(isWrite ? AccessMode.Write : AccessMode.Read); 29 | if (bookmarks != null) 30 | builder.WithBookmarks(bookmarks.ToArray()); 31 | }); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Neo4jClient/Extensions/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Neo4jClient.Extensions 5 | { 6 | public static class EnumerableExtensions 7 | { 8 | public static bool ContentsEqual(this IReadOnlyCollection collection1, IReadOnlyCollection collection2) 9 | { 10 | if (collection1 == null && collection2 == null) 11 | return true; 12 | 13 | if (collection1 == null || collection2 == null) 14 | return false; 15 | 16 | return collection1.Count == collection2.Count && collection1.All(collection2.Contains); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Neo4jClient/Extensions/MemberInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Serialization; 7 | 8 | namespace Neo4jClient.Extensions 9 | { 10 | public static class MemberInfoExtensions 11 | { 12 | /// Gets the name of the property, if a is attached, it will attempt to use that first, then if a is attached it will use the next. 13 | /// The to get the name from 14 | /// The name of the property, if a is attached, it will use that first, then if a is attached it will use the next. 15 | internal static string GetNameUsingJsonProperty(this MemberInfo info) 16 | { 17 | var jsonPropertyAttribute = info.GetCustomAttributes(typeof (JsonPropertyAttribute)).FirstOrDefault() as JsonPropertyAttribute; 18 | var jsonObjectAttribute = info.DeclaringType.GetTypeInfo().GetCustomAttributes(typeof(JsonObjectAttribute)).FirstOrDefault() as JsonObjectAttribute; 19 | 20 | var hasSpecifiedName = jsonPropertyAttribute != null && !string.IsNullOrWhiteSpace(jsonPropertyAttribute.PropertyName); 21 | var hasNamingStrategy = jsonObjectAttribute != null && jsonObjectAttribute.NamingStrategyType != null; 22 | 23 | if (!hasSpecifiedName && !hasNamingStrategy) 24 | return info.Name; 25 | 26 | if (hasSpecifiedName) 27 | { 28 | return jsonPropertyAttribute.PropertyName; 29 | } 30 | 31 | var strategy = (NamingStrategy)Activator.CreateInstance(jsonObjectAttribute.NamingStrategyType); 32 | return strategy.GetPropertyName(info.Name, false); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /Neo4jClient/Extensions/ObjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace Neo4jClient.Extensions 5 | { 6 | public static class ObjectExtensions 7 | { 8 | 9 | public static bool In(this T obj, IEnumerable enumerable) 10 | { 11 | return enumerable.Contains(obj); 12 | } 13 | 14 | public static bool NotIn(this T obj, IEnumerable enumerable) 15 | { 16 | return !obj.In(enumerable); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Neo4jClient/GraphClientExtensions.cs: -------------------------------------------------------------------------------- 1 | // using System.Linq; 2 | // using System.Threading.Tasks; 3 | // 4 | // namespace Neo4jClient 5 | // { 6 | // public static class GraphClientExtensions 7 | // { 8 | // public static Task> CreateAsync(this IGraphClient graphClient, TNode node, params IRelationshipAllowingParticipantNode[] relationships) 9 | // where TNode : class 10 | // { 11 | // return graphClient.CreateAsync(node, relationships, Enumerable.Empty()); 12 | // } 13 | // } 14 | // } -------------------------------------------------------------------------------- /Neo4jClient/GraphClientFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Neo4jClient.Execution; 4 | 5 | namespace Neo4jClient 6 | { 7 | public class GraphClientFactory : IGraphClientFactory 8 | { 9 | private readonly NeoServerConfiguration _configuration; 10 | 11 | public GraphClientFactory(NeoServerConfiguration configuration) 12 | { 13 | if (configuration == null) 14 | throw new ArgumentNullException("configuration", "Neo server configuration is null"); 15 | 16 | _configuration = configuration; 17 | } 18 | 19 | public async Task CreateAsync() 20 | { 21 | var client = new GraphClient( 22 | _configuration.RootUri, 23 | _configuration.Username, 24 | _configuration.Password); 25 | 26 | await client.ConnectAsync(_configuration).ConfigureAwait(false); 27 | 28 | return client; 29 | } 30 | 31 | public async Task CreateAsync(IHttpClient httpClient) 32 | { 33 | var client = new GraphClient(_configuration.RootUri, httpClient); 34 | 35 | await client.ConnectAsync(_configuration).ConfigureAwait(false); 36 | 37 | return client; 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Neo4jClient/HttpClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Http; 3 | using System.Net.Http.Headers; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Neo4jClient.Execution; 7 | 8 | namespace Neo4jClient 9 | { 10 | public class HttpClientWrapper : IHttpClient 11 | { 12 | internal AuthenticationHeaderValue AuthenticationHeaderValue { get; private set; } 13 | internal readonly HttpClient Client; 14 | 15 | public HttpClientWrapper(string username = null, string password = null, HttpClient client = null) 16 | : this(client ?? new HttpClient()) 17 | { 18 | Username = username; 19 | Password = password; 20 | if (string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password)) 21 | return; 22 | 23 | var encoded = Encoding.ASCII.GetBytes(string.Format("{0}:{1}", username, password)); 24 | AuthenticationHeaderValue = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(encoded)); 25 | } 26 | 27 | public HttpClientWrapper(HttpClient client) 28 | { 29 | this.Client = client; 30 | } 31 | 32 | public Task SendAsync(HttpRequestMessage request) 33 | { 34 | if (AuthenticationHeaderValue != null) 35 | request.Headers.Authorization = AuthenticationHeaderValue; 36 | 37 | return Client.SendAsync(request); 38 | } 39 | 40 | public string Username { get; private set; } 41 | public string Password { get; private set; } 42 | } 43 | } -------------------------------------------------------------------------------- /Neo4jClient/HttpContentExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Net.Http; 3 | using System.Threading.Tasks; 4 | using Neo4jClient.Serialization; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Serialization; 7 | 8 | namespace Neo4jClient 9 | { 10 | internal static class HttpContentExtensions 11 | { 12 | public static Task ReadAsStringAsync(this HttpContent content) 13 | { 14 | return content.ReadAsStringAsync(); 15 | } 16 | 17 | public static async Task ReadAsJsonAsync(this HttpContent content, IEnumerable jsonConverters, DefaultContractResolver resolver) 18 | where T : new() 19 | { 20 | var stringContent = await content.ReadAsStringAsync().ConfigureAwait(false); 21 | return new CustomJsonDeserializer(jsonConverters, resolver:resolver).Deserialize(stringContent); 22 | } 23 | 24 | public static Task ReadAsJsonAsync(this HttpContent content, IEnumerable jsonConverters) where T : new() 25 | { 26 | return content.ReadAsJsonAsync(jsonConverters, null); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Neo4jClient/IAttachedReference.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | public interface IAttachedReference 4 | { 5 | IGraphClient Client { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Neo4jClient/IBoltGraphClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Neo4jClient.Cypher; 4 | using Neo4jClient.Execution; 5 | using Neo4jClient.Serialization; 6 | 7 | namespace Neo4jClient 8 | { 9 | public interface IBoltGraphClient : ICypherGraphClient 10 | { 11 | event OperationCompletedEventHandler OperationCompleted; 12 | 13 | CypherCapabilities CypherCapabilities { get; } 14 | 15 | Version ServerVersion { get; } 16 | 17 | ISerializer Serializer { get; } 18 | 19 | ExecutionConfiguration ExecutionConfiguration { get; } 20 | 21 | bool IsConnected { get; } 22 | 23 | Task ConnectAsync(NeoServerConfiguration configuration = null); 24 | 25 | /// 26 | /// Indicates if client should use the native driver date time types, instead of serialized datetime strings. 27 | /// 28 | bool UseDriverDateTypes { get; } 29 | } 30 | } -------------------------------------------------------------------------------- /Neo4jClient/ICypherGraphClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Neo4jClient.Cypher; 3 | 4 | namespace Neo4jClient 5 | { 6 | public interface ICypherGraphClient : IDisposable 7 | { 8 | ICypherFluentQuery Cypher { get; } 9 | } 10 | } -------------------------------------------------------------------------------- /Neo4jClient/IGraphClient.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Neo4jClient.ApiModels; 5 | using Neo4jClient.Cypher; 6 | using Neo4jClient.Execution; 7 | using Neo4jClient.Serialization; 8 | using Neo4jClient.Transactions; 9 | using Newtonsoft.Json; 10 | using Newtonsoft.Json.Serialization; 11 | 12 | namespace Neo4jClient 13 | { 14 | public interface IGraphClient : ICypherGraphClient 15 | { 16 | event OperationCompletedEventHandler OperationCompleted; 17 | 18 | /// 19 | /// Sets the default database to use. You can still override with the method on a per query basis. 20 | /// 21 | /// The default if this is not set, is "neo4j" 22 | string DefaultDatabase { get; set; } 23 | 24 | // ReSharper disable once InconsistentNaming 25 | CypherCapabilities CypherCapabilities { get; } 26 | 27 | Uri RootEndpoint { get; } 28 | 29 | Version ServerVersion { get; } 30 | 31 | Uri TransactionEndpoint { get; } 32 | 33 | ISerializer Serializer { get; } 34 | 35 | ExecutionConfiguration ExecutionConfiguration { get; } 36 | 37 | bool IsConnected { get; } 38 | 39 | Task ConnectAsync(NeoServerConfiguration configuration = null); 40 | 41 | List JsonConverters { get; } 42 | DefaultContractResolver JsonContractResolver { get; set; } 43 | Uri GetTransactionEndpoint(string database, bool autoCommit); 44 | ITransactionalGraphClient Tx { get; } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Neo4jClient/IGraphClientFactory.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Neo4jClient.Execution; 3 | 4 | namespace Neo4jClient 5 | { 6 | public interface IGraphClientFactory 7 | { 8 | Task CreateAsync(); 9 | Task CreateAsync(IHttpClient client); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Neo4jClient/IHasNodeReference.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | internal interface IHasNodeReference 4 | { 5 | NodeReference Reference { get; } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /Neo4jClient/IRawGraphClient.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Collections.Specialized; 3 | using System.Threading.Tasks; 4 | using Neo4jClient.Cypher; 5 | 6 | namespace Neo4jClient 7 | { 8 | /// 9 | /// These are signatures that our exposes to 10 | /// support things like our Cypher infrastructure, but we don't want 11 | /// people to call directly. (Because there are nicer ways to do it.) 12 | /// 13 | public interface IRawGraphClient : IGraphClient 14 | { 15 | Task> ExecuteGetCypherResultsAsync(CypherQuery query); 16 | Task ExecuteCypherAsync(CypherQuery query); 17 | bool InTransaction { get; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Neo4jClient/IRelationshipAllowingParticipantNode`TNode.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | public interface IRelationshipAllowingParticipantNode 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /Neo4jClient/IRelationshipAllowingSourceNode`TNode.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | public interface IRelationshipAllowingSourceNode 4 | : IRelationshipAllowingParticipantNode 5 | { 6 | } 7 | } -------------------------------------------------------------------------------- /Neo4jClient/IRelationshipAllowingTargetNode`TNode.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | public interface IRelationshipAllowingTargetNode 4 | : IRelationshipAllowingParticipantNode 5 | { 6 | } 7 | } -------------------------------------------------------------------------------- /Neo4jClient/ITypedNodeReference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient 4 | { 5 | public interface ITypedNodeReference 6 | { 7 | Type NodeType { get; } 8 | } 9 | } -------------------------------------------------------------------------------- /Neo4jClient/IndexConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Neo4jClient 4 | { 5 | public class IndexConfiguration 6 | { 7 | [JsonProperty("type")] 8 | public IndexType Type { get; set; } 9 | [JsonProperty("provider")] 10 | public IndexProvider Provider { get; set; } 11 | } 12 | } -------------------------------------------------------------------------------- /Neo4jClient/IndexEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | 5 | namespace Neo4jClient 6 | { 7 | public class IndexEntry : IEnumerable> 8 | { 9 | readonly IList> originalKeyValuesList; 10 | 11 | public IndexEntry() 12 | { 13 | originalKeyValuesList = null; 14 | KeyValues = originalKeyValuesList = new List>(); 15 | } 16 | 17 | public IndexEntry(string name) : this() 18 | { 19 | Name = name; 20 | } 21 | 22 | public string Name { get; set; } 23 | public IEnumerable> KeyValues { get; set; } 24 | 25 | public void Add(string key, object value) 26 | { 27 | if (originalKeyValuesList == null || 28 | !ReferenceEquals(originalKeyValuesList, KeyValues)) 29 | throw new InvalidOperationException(@"You can only call the Add method if you haven't directly set the KeyValues property. Write your code like this: 30 | 31 | new IndexEntry(""index-name"") 32 | { 33 | { ""Foo"", 1234 }, 34 | { ""Bar"", ""Baz"" } 35 | }"); 36 | 37 | originalKeyValuesList.Add(new KeyValuePair(key, value)); 38 | } 39 | 40 | public IEnumerator> GetEnumerator() 41 | { 42 | return KeyValues.GetEnumerator(); 43 | } 44 | 45 | IEnumerator IEnumerable.GetEnumerator() 46 | { 47 | return GetEnumerator(); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /Neo4jClient/IndexFor.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | public enum IndexFor 4 | { 5 | Node, 6 | Relationship 7 | } 8 | } -------------------------------------------------------------------------------- /Neo4jClient/IndexMetaData.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace Neo4jClient 4 | { 5 | public class IndexMetaData 6 | { 7 | [JsonProperty("to_lower_case")] 8 | public bool ToLowerCase { get; set; } 9 | 10 | [JsonProperty("template")] 11 | public string Template { get; set; } 12 | 13 | [JsonProperty("provider")] 14 | public string Provider { get; set; } 15 | 16 | [JsonProperty("type")] 17 | public string Type { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /Neo4jClient/IndexProvider.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | public enum IndexProvider 4 | { 5 | lucene 6 | } 7 | } -------------------------------------------------------------------------------- /Neo4jClient/IndexType.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | public enum IndexType 4 | { 5 | fulltext, 6 | exact 7 | } 8 | } -------------------------------------------------------------------------------- /Neo4jClient/Indexing.cd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | AAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAA= 7 | IndexConfiguration.cs 8 | 9 | 10 | 11 | 12 | 13 | AAIAAAAAAAACABAAAAAAAIQAAAAAAAAEAAAAAAAAAAA= 14 | IndexEntry.cs 15 | 16 | 17 | 18 | 19 | 20 | 21 | AAAAAAAAAEAEAAAAAAAAAAAAAAAAAAAEAQAAAAAAAAA= 22 | IndexMetaData.cs 23 | 24 | 25 | 26 | 27 | 28 | AgQSAAgDBQAAADBDADAAQYAEAgEgQUAAIAiIIAAAATQ= 29 | IGraphClient.cs 30 | 31 | 32 | 33 | 34 | 35 | AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAEAAAA= 36 | IndexFor.cs 37 | 38 | 39 | 40 | 41 | 42 | AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA= 43 | IndexProvider.cs 44 | 45 | 46 | 47 | 48 | 49 | AAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAAAA= 50 | IndexType.cs 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /Neo4jClient/JTokenExtensions.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace Neo4jClient 4 | { 5 | internal static class JTokenExtensions 6 | { 7 | internal static string AsString(this JToken token) 8 | { 9 | return token.Type != JTokenType.String 10 | ? token.ToString() 11 | : token.Value(); 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Neo4jClient/Neo4jClient.Shared.v3.ncrunchproject: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | True 7 | 8 | -------------------------------------------------------------------------------- /Neo4jClient/Neo4jClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 2022 Readify, Tatham Oddie, Charlotte Skardon 7 | Readify, Tatham Oddie, Charlotte Skardon 8 | https://github.com/DotNet4Neo4j/Neo4jClient 9 | LICENSE 10 | A .NET client for neo4j: an open source, transactional graph database. It's pretty awesome. 11 | neo4j, nosql, cypher, bolt, graph 12 | 4.0.4 13 | 14 | 15 | 16 | 4.0.0 17 | https://github.com/DotNet4Neo4j/neo4jclient 18 | git 19 | true 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | <_Parameter1>$(MSBuildProjectName).Tests 38 | 39 | 40 | 41 | 42 | 43 | True 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /Neo4jClient/NeoException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | using Neo4jClient.ApiModels; 4 | 5 | namespace Neo4jClient 6 | { 7 | [DataContract] 8 | public class NeoException : Exception 9 | { 10 | readonly string neoMessage; 11 | readonly string neoException; 12 | readonly string neoFullName; 13 | readonly string[] neoStackTrace; 14 | 15 | internal NeoException(ExceptionResponse response) 16 | : base(response.Exception + ": " + response.Message) 17 | { 18 | neoMessage = response.Message; 19 | neoException = response.Exception; 20 | neoFullName = response.FullName; 21 | neoStackTrace = response.StackTrace; 22 | } 23 | 24 | public string NeoMessage { get { return neoMessage; } } 25 | public string NeoExceptionName { get { return neoException; } } 26 | public string NeoFullName { get { return neoFullName; } } 27 | public string[] NeoStackTrace { get { return neoStackTrace; } } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Neo4jClient/NodeReference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Neo4jClient 5 | { 6 | [DebuggerDisplay("Node {Id}")] 7 | public class NodeReference : IAttachedReference 8 | { 9 | private readonly IGraphClient client; 10 | 11 | public NodeReference(long id) : this(id, null) 12 | { 13 | } 14 | 15 | public NodeReference(long id, IGraphClient client) 16 | { 17 | this.Id = id; 18 | this.client = client; 19 | } 20 | 21 | public long Id { get; } 22 | 23 | public Type NodeType 24 | { 25 | get 26 | { 27 | var typedThis = this as ITypedNodeReference; 28 | return typedThis?.NodeType; 29 | } 30 | } 31 | 32 | IGraphClient IAttachedReference.Client => client; 33 | 34 | public static implicit operator NodeReference(long nodeId) 35 | { 36 | return new NodeReference(nodeId); 37 | } 38 | 39 | public static bool operator ==(NodeReference lhs, NodeReference rhs) 40 | { 41 | if (ReferenceEquals(lhs, null) && ReferenceEquals(rhs, null)) 42 | return true; 43 | 44 | return !ReferenceEquals(lhs, null) && lhs.Equals(rhs); 45 | } 46 | 47 | public static bool operator !=(NodeReference lhs, NodeReference rhs) 48 | { 49 | return !(lhs == rhs); 50 | } 51 | 52 | public override bool Equals(object obj) 53 | { 54 | var other = obj as NodeReference; 55 | if (ReferenceEquals(null, other)) return false; 56 | if (ReferenceEquals(this, other)) return true; 57 | return other.Id == Id; 58 | } 59 | 60 | public override int GetHashCode() 61 | { 62 | return Id.GetHashCode(); 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Neo4jClient/NodeReference`TNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Reflection; 4 | 5 | namespace Neo4jClient 6 | { 7 | [DebuggerDisplay("Node {Id}")] 8 | public class NodeReference : NodeReference, ITypedNodeReference 9 | { 10 | public NodeReference(long id) 11 | : base(id) 12 | { 13 | CheckTNode(); 14 | } 15 | 16 | public NodeReference(long id, IGraphClient client) 17 | : base(id, client) 18 | { 19 | CheckTNode(); 20 | } 21 | 22 | static void CheckTNode() 23 | { 24 | var type = typeof (TNode); 25 | if (!type.GetTypeInfo().IsGenericType) return; 26 | if (type.GetGenericTypeDefinition() != typeof(Node<>)) return; 27 | 28 | throw new NotSupportedException(string.Format( 29 | "You're trying to initialize NodeReference> which is too many levels of nesting. You should just be using NodeReference<{0}> instead. (You use a Node, or a NodeReference, but not both together.)", 30 | type.GetGenericArguments()[0].FullName 31 | )); 32 | } 33 | 34 | Type ITypedNodeReference.NodeType 35 | { 36 | get { return typeof (TNode); } 37 | } 38 | 39 | public static implicit operator NodeReference(long nodeId) 40 | { 41 | return new NodeReference(nodeId); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Neo4jClient/Node`TNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient 4 | { 5 | public class Node : IHasNodeReference, IAttachedReference 6 | { 7 | public Node(TNode data, NodeReference reference) 8 | { 9 | if (reference == null) 10 | throw new ArgumentNullException("reference"); 11 | 12 | this.Data = data; 13 | this.Reference = reference; 14 | } 15 | 16 | public Node(TNode data, long id, IGraphClient client) 17 | { 18 | this.Data = data; 19 | Reference = new NodeReference(id, client); 20 | } 21 | 22 | public NodeReference Reference { get; } 23 | 24 | public TNode Data { get; } 25 | 26 | 27 | IGraphClient IAttachedReference.Client => ((IAttachedReference) Reference).Client; 28 | 29 | NodeReference IHasNodeReference.Reference => Reference; 30 | 31 | public static bool operator ==(Node lhs, Node rhs) 32 | { 33 | if (ReferenceEquals(lhs, null) && ReferenceEquals(rhs, null)) 34 | return true; 35 | 36 | return !ReferenceEquals(lhs, null) && lhs.Equals(rhs); 37 | } 38 | 39 | public static bool operator !=(Node lhs, Node rhs) 40 | { 41 | return !(lhs == rhs); 42 | } 43 | 44 | public override bool Equals(object obj) 45 | { 46 | var other = obj as Node; 47 | if (ReferenceEquals(null, other)) return false; 48 | if (ReferenceEquals(this, other)) return true; 49 | return other.Reference == Reference; 50 | } 51 | 52 | public override int GetHashCode() 53 | { 54 | return Reference.Id.GetHashCode(); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Neo4jClient/OperationCompletedEventHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Specialized; 4 | using Neo4j.Driver; 5 | using Neo4jClient.ApiModels.Cypher; 6 | 7 | namespace Neo4jClient 8 | { 9 | public delegate void OperationCompletedEventHandler(object sender, OperationCompletedEventArgs e); 10 | 11 | public class OperationCompletedEventArgs : EventArgs 12 | { 13 | public string Database { get; set; } 14 | public string Identifier { get; set; } 15 | [Obsolete("Replaced with 'LastBookmarks' will be removed in the next version.")] 16 | public Bookmark LastBookmark { get; set; } 17 | public Bookmarks LastBookmarks { get; set; } 18 | public string QueryText { get; set; } 19 | public int ResourcesReturned { get; set; } 20 | public TimeSpan TimeTaken { get; set; } 21 | public Exception Exception { get; set; } 22 | public bool HasException => Exception != null; 23 | public int? MaxExecutionTime { get; set; } 24 | public NameValueCollection CustomHeaders { get; set; } 25 | 26 | public IEnumerable BookmarksUsed { get; set; } 27 | public QueryStats QueryStats { get; set; } 28 | 29 | /// This is only set with the . 30 | public Config ConfigUsed { get; set; } 31 | 32 | public override string ToString() 33 | { 34 | return $"HasException={HasException}, QueryText={QueryText}"; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Neo4jClient/OrphanedTransactionException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Neo4jClient 7 | { 8 | public class OrphanedTransactionException : Exception 9 | { 10 | private readonly NeoException _internalException; 11 | private readonly string _transactionEndpoint; 12 | 13 | internal OrphanedTransactionException(NeoException internalException, string transactionEndpoint) 14 | : base("The transaction has timed out and it has been rolled back by the server") 15 | { 16 | _internalException = internalException; 17 | _transactionEndpoint = transactionEndpoint; 18 | } 19 | 20 | public NeoException InternalException 21 | { 22 | get { return _internalException; } 23 | } 24 | 25 | public string TransactionEndpoint 26 | { 27 | get { return _transactionEndpoint; } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Neo4jClient/RelationshipDirection.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | public enum RelationshipDirection 4 | { 5 | Automatic = 0, 6 | Incoming, 7 | Outgoing 8 | } 9 | } -------------------------------------------------------------------------------- /Neo4jClient/RelationshipEnd.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | public enum RelationshipEnd 4 | { 5 | SourceNode, 6 | TargetNode 7 | } 8 | } -------------------------------------------------------------------------------- /Neo4jClient/RelationshipInstance.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Neo4jClient 4 | { 5 | [DebuggerDisplay("Relationship {Reference.Id} of type {TypeKey} from node {StartNodeReference.Id} to node {EndNodeReference.Id}")] 6 | public class RelationshipInstance 7 | { 8 | readonly RelationshipReference reference; 9 | readonly NodeReference startNodeReference; 10 | readonly NodeReference endNodeReference; 11 | readonly string typeKey; 12 | 13 | public RelationshipInstance( 14 | RelationshipReference reference, 15 | NodeReference startNodeReference, 16 | NodeReference endNodeReference, 17 | string typeKey) 18 | { 19 | this.reference = reference; 20 | this.startNodeReference = startNodeReference; 21 | this.endNodeReference = endNodeReference; 22 | this.typeKey = typeKey; 23 | } 24 | 25 | public RelationshipReference Reference 26 | { 27 | get { return reference; } 28 | } 29 | 30 | public NodeReference StartNodeReference 31 | { 32 | get { return startNodeReference; } 33 | } 34 | 35 | public NodeReference EndNodeReference 36 | { 37 | get { return endNodeReference; } 38 | } 39 | 40 | public string TypeKey 41 | { 42 | get { return typeKey; } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Neo4jClient/RelationshipInstance`TData.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | public class RelationshipInstance : RelationshipInstance 4 | where TData : class, new() 5 | { 6 | readonly TData data; 7 | 8 | public RelationshipInstance( 9 | RelationshipReference reference, 10 | NodeReference startNodeReference, 11 | NodeReference endNodeReference, 12 | string typeKey, 13 | TData data) 14 | : base(reference, startNodeReference, endNodeReference, typeKey) 15 | { 16 | this.data = data; 17 | } 18 | 19 | public new RelationshipReference Reference 20 | { 21 | get 22 | { 23 | var baseReference = base.Reference; 24 | return new RelationshipReference(baseReference.Id, ((IAttachedReference) baseReference).Client); 25 | } 26 | } 27 | 28 | public TData Data 29 | { 30 | get { return data; } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Neo4jClient/RelationshipReference.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | 4 | namespace Neo4jClient 5 | { 6 | [DebuggerDisplay("Relationship {id}")] 7 | public class RelationshipReference : IAttachedReference 8 | { 9 | readonly long id; 10 | readonly IGraphClient client; 11 | 12 | public RelationshipReference(long id) : this(id, null) {} 13 | 14 | public RelationshipReference(long id, IGraphClient client) 15 | { 16 | this.id = id; 17 | this.client = client; 18 | } 19 | 20 | public long Id { get { return id; } } 21 | 22 | public static implicit operator RelationshipReference(long relationshipId) 23 | { 24 | return new RelationshipReference(relationshipId); 25 | } 26 | 27 | public static bool operator ==(RelationshipReference lhs, RelationshipReference rhs) 28 | { 29 | if (ReferenceEquals(lhs, null) && ReferenceEquals(rhs, null)) 30 | return true; 31 | 32 | return !ReferenceEquals(lhs, null) && lhs.Equals(rhs); 33 | } 34 | 35 | public static bool operator !=(RelationshipReference lhs, RelationshipReference rhs) 36 | { 37 | return !(lhs == rhs); 38 | } 39 | 40 | public override bool Equals(object obj) 41 | { 42 | var other = obj as RelationshipReference; 43 | if (ReferenceEquals(null, other)) return false; 44 | if (ReferenceEquals(this, other)) return true; 45 | return other.id == id; 46 | } 47 | 48 | public override int GetHashCode() 49 | { 50 | return id.GetHashCode(); 51 | } 52 | 53 | IGraphClient IAttachedReference.Client 54 | { 55 | get { return client; } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Neo4jClient/RelationshipReference`TData.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Neo4jClient 4 | { 5 | [DebuggerDisplay("Relationship {Id}")] 6 | public class RelationshipReference : RelationshipReference 7 | { 8 | public RelationshipReference(long id) 9 | : base(id) 10 | { 11 | } 12 | 13 | public RelationshipReference(long id, IGraphClient client) 14 | : base(id, client) 15 | { 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Neo4jClient/Relationship`TData.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient 2 | { 3 | public abstract class Relationship : 4 | Relationship 5 | where TData : class, new() 6 | { 7 | protected Relationship(NodeReference targetNode, TData data) 8 | : base(targetNode, data) 9 | { 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Neo4jClient/Serialization/CustomJsonDeserializer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Linq; 7 | using System.Globalization; 8 | using Newtonsoft.Json.Serialization; 9 | 10 | namespace Neo4jClient.Serialization 11 | { 12 | public class CustomJsonDeserializer 13 | { 14 | readonly IEnumerable jsonConverters; 15 | readonly CultureInfo culture; 16 | readonly DefaultContractResolver jsonResolver; 17 | 18 | public CustomJsonDeserializer(IEnumerable jsonConverters) : this(jsonConverters, null) 19 | { 20 | } 21 | 22 | public CustomJsonDeserializer(IEnumerable jsonConverters, CultureInfo cultureInfo = null, DefaultContractResolver resolver = null) 23 | { 24 | this.jsonConverters = jsonConverters; 25 | culture = cultureInfo ?? CultureInfo.InvariantCulture; 26 | jsonResolver = resolver ?? GraphClient.DefaultJsonContractResolver; 27 | } 28 | 29 | public T Deserialize(string content) where T : new() 30 | { 31 | var context = new DeserializationContext 32 | { 33 | Culture = culture, 34 | JsonConverters = (jsonConverters ?? new List(0)).Reverse().ToArray(), 35 | JsonContractResolver = jsonResolver 36 | }; 37 | 38 | content = CommonDeserializerMethods.ReplaceAllDateInstancesWithNeoDates(content); 39 | 40 | var reader = new JsonTextReader(new StringReader(content)) 41 | { 42 | DateParseHandling = DateParseHandling.None 43 | }; 44 | 45 | var target = new T(); 46 | 47 | if (target is IList) 48 | { 49 | var objType = target.GetType(); 50 | var json = JToken.ReadFrom(reader); 51 | target = (T)CommonDeserializerMethods.BuildList(context, objType, json.Root.Children(), new TypeMapping[0], 0); 52 | } 53 | else if (target is IDictionary) 54 | { 55 | var root = JToken.ReadFrom(reader).Root; 56 | target = (T)CommonDeserializerMethods.BuildDictionary(context, target.GetType(), root.Children(), new TypeMapping[0], 0); 57 | } 58 | else 59 | { 60 | var root = JToken.ReadFrom(reader).Root; 61 | CommonDeserializerMethods.Map(context, target, root, new TypeMapping[0], 0); 62 | } 63 | 64 | return target; 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Neo4jClient/Serialization/DeserializationContext.cs: -------------------------------------------------------------------------------- 1 | using System.Globalization; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Serialization; 4 | 5 | namespace Neo4jClient.Serialization 6 | { 7 | public class DeserializationContext 8 | { 9 | public CultureInfo Culture { get; set; } 10 | public JsonConverter[] JsonConverters { get; set; } 11 | public IContractResolver JsonContractResolver { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Neo4jClient/Serialization/DeserializationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient.Serialization 4 | { 5 | public class DeserializationException : Exception 6 | { 7 | public DeserializationException(string message) 8 | : base(message) 9 | {} 10 | 11 | public DeserializationException(string message, Exception innerException) 12 | : base(message, innerException) 13 | { } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Neo4jClient/Serialization/EnumValueConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Newtonsoft.Json; 4 | 5 | namespace Neo4jClient.Serialization 6 | { 7 | public class EnumValueConverter : JsonConverter 8 | { 9 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 10 | { 11 | writer.WriteValue(value.ToString()); 12 | } 13 | 14 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 15 | { 16 | return Enum.Parse(objectType, reader.Value.ToString()); 17 | } 18 | 19 | public override bool CanConvert(Type objectType) 20 | { 21 | return objectType.GetTypeInfo().IsEnum; 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /Neo4jClient/Serialization/ICypherJsonDeserializer.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Neo4jClient.Serialization 4 | { 5 | public interface ICypherJsonDeserializer 6 | { 7 | PartialDeserializationContext CheckForErrorsInTransactionResponse(string content); 8 | IEnumerable Deserialize(string content, bool isHttp); 9 | IEnumerable DeserializeFromTransactionPartialContext(PartialDeserializationContext context, bool isHttp); 10 | } 11 | } -------------------------------------------------------------------------------- /Neo4jClient/Serialization/ISerializer.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Serialization 2 | { 3 | public interface ISerializer 4 | { 5 | string Serialize(object toSerialize); 6 | } 7 | } -------------------------------------------------------------------------------- /Neo4jClient/Serialization/Neo4jContractSerializer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Serialization; 7 | 8 | namespace Neo4jClient.Serialization 9 | { 10 | public class Neo4jContractResolver : DefaultContractResolver 11 | { 12 | private readonly Dictionary> renames; 13 | 14 | public Neo4jContractResolver() 15 | { 16 | renames = new Dictionary>(); 17 | } 18 | 19 | protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization) 20 | { 21 | var property = base.CreateProperty(member, memberSerialization); 22 | 23 | PropertyInfo prop = property.DeclaringType.GetProperty(property.PropertyName); 24 | if (prop != null && prop.CustomAttributes.Any(a => a.AttributeType == typeof(Neo4jIgnoreAttribute))) 25 | { 26 | property.ShouldSerialize = i => false; 27 | } 28 | 29 | if (IsRenamed(property.DeclaringType, property.PropertyName, out var newJsonPropertyName)) 30 | property.PropertyName = newJsonPropertyName; 31 | 32 | return property; 33 | } 34 | 35 | private bool IsRenamed(Type type, string jsonPropertyName, out string newJsonPropertyName) 36 | { 37 | if (renames.TryGetValue(type, out var dictionary) && 38 | dictionary.TryGetValue(jsonPropertyName, out newJsonPropertyName)) 39 | return true; 40 | 41 | newJsonPropertyName = null; 42 | return false; 43 | 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /Neo4jClient/Serialization/NullableEnumValueConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using Newtonsoft.Json; 4 | 5 | namespace Neo4jClient.Serialization 6 | { 7 | public class NullableEnumValueConverter : JsonConverter 8 | { 9 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 10 | { 11 | writer.WriteValue(value.ToString()); 12 | } 13 | 14 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 15 | { 16 | if (string.IsNullOrEmpty(reader.Value.ToString())) 17 | return null; 18 | 19 | var enumType = Nullable.GetUnderlyingType(objectType); 20 | return Enum.Parse(enumType, reader.Value.ToString()); 21 | } 22 | 23 | public override bool CanConvert(Type objectType) 24 | { 25 | return objectType.GetTypeInfo().IsGenericType && 26 | objectType.GetGenericTypeDefinition() == typeof (Nullable<>) && 27 | Nullable.GetUnderlyingType(objectType).GetTypeInfo().IsEnum; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /Neo4jClient/Serialization/PartialDeserializationContext.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | 3 | namespace Neo4jClient.Serialization 4 | { 5 | public class PartialDeserializationContext 6 | { 7 | public JToken RootResult { get; set; } 8 | public DeserializationContext DeserializationContext { get; set; } 9 | } 10 | } -------------------------------------------------------------------------------- /Neo4jClient/Serialization/TimeZoneInfoConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using Newtonsoft.Json; 4 | 5 | namespace Neo4jClient.Serialization 6 | { 7 | public class TimeZoneInfoConverter : JsonConverter 8 | { 9 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 10 | { 11 | var timeZone = (TimeZoneInfo) value; 12 | writer.WriteValue(timeZone.Id); 13 | } 14 | 15 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 16 | { 17 | try 18 | { 19 | return TimeZoneInfo.FindSystemTimeZoneById(reader.Value.ToString()); 20 | } 21 | catch 22 | { 23 | Debug.WriteLine("Could not deserialize TimeZoneInfo, defaulting to Utc. Ensure the TimeZoneId is valid. Valid TimeZone Ids are:"); 24 | foreach (var timeZone in TimeZoneInfo.GetSystemTimeZones()) 25 | { 26 | Debug.WriteLine(timeZone.Id); 27 | } 28 | return TimeZoneInfo.Utc; 29 | } 30 | } 31 | 32 | public override bool CanConvert(Type objectType) 33 | { 34 | return typeof(TimeZoneInfo) == objectType; 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /Neo4jClient/Serialization/TypeMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient.Serialization 4 | { 5 | internal class TypeMapping 6 | { 7 | public Func ShouldTriggerForPropertyType { get; set; } 8 | public Func DetermineTypeToParseJsonIntoBasedOnPropertyType { get; set; } 9 | public Func MutationCallback { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Neo4jClient/Serialization/ZonedDateTimeConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Diagnostics; 4 | using Neo4j.Driver; 5 | using Newtonsoft.Json; 6 | 7 | namespace Neo4jClient.Serialization 8 | { 9 | public class ZonedDateTimeConverter : JsonConverter 10 | { 11 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 12 | { 13 | var zdt = (ZonedDateTime)value; 14 | zdt.As(); 15 | 16 | var typeConverter = TypeDescriptor.GetConverter(zdt.GetType()); 17 | writer.WriteValue(typeConverter.ConvertToInvariantString(value)); 18 | } 19 | 20 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 21 | { 22 | if (reader.Value == null) return null; 23 | var typeConverter = TypeDescriptor.GetConverter(typeof(DateTimeOffset)); 24 | var dto = (DateTimeOffset) typeConverter.ConvertFromString(reader.Value.ToString()); 25 | return new ZonedDateTime(dto); 26 | } 27 | 28 | public override bool CanConvert(Type objectType) 29 | { 30 | return typeof(ZonedDateTime) == objectType; 31 | } 32 | } 33 | 34 | 35 | public class LocalDateTimeConverter : JsonConverter 36 | { 37 | public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 38 | { 39 | var ldt = (LocalDateTime)value; 40 | ldt.As(); 41 | 42 | var typeConverter = TypeDescriptor.GetConverter(ldt.GetType()); 43 | writer.WriteValue(typeConverter.ConvertToInvariantString(value)); 44 | } 45 | 46 | public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 47 | { 48 | if (reader.Value == null) return null; 49 | var typeConverter = TypeDescriptor.GetConverter(typeof(DateTime)); 50 | var dt = typeConverter.ConvertFromString(reader.Value.ToString()) as DateTime?; 51 | return new LocalDateTime(dt ?? DateTime.MinValue); 52 | } 53 | 54 | public override bool CanConvert(Type objectType) 55 | { 56 | return typeof(LocalDateTime) == objectType; 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Neo4jClient/Transactions/Bolt/BoltNeo4jTransactionProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Neo4jClient.Transactions.Bolt 4 | { 5 | using Neo4j.Driver; 6 | 7 | /// 8 | /// Implements the TransactionScopeProxy interfaces for INeo4jTransaction 9 | /// 10 | internal class BoltNeo4jTransactionProxy : BoltTransactionScopeProxy 11 | { 12 | private readonly bool doCommitInScope; 13 | 14 | public BoltNeo4jTransactionProxy(ITransactionalGraphClient client, BoltTransactionContext transactionContext, bool newScope) 15 | : base(client, transactionContext) 16 | { 17 | doCommitInScope = newScope; 18 | } 19 | 20 | public override bool Committable => true; 21 | 22 | public override bool IsOpen => TransactionContext != null && TransactionContext.IsOpen; 23 | public override Bookmark LastBookmark => TransactionContext?.BoltTransaction?.LastBookmark; 24 | 25 | protected override async Task DoCommitAsync() 26 | { 27 | if (doCommitInScope) 28 | await TransactionContext.CommitAsync().ConfigureAwait(false); 29 | } 30 | 31 | public override string Database { get; set; } 32 | 33 | protected override bool ShouldDisposeTransaction() 34 | { 35 | return doCommitInScope; 36 | } 37 | 38 | public override Task RollbackAsync() 39 | { 40 | return TransactionContext.RollbackAsync(); 41 | } 42 | 43 | public override Task KeepAliveAsync() 44 | { 45 | return TransactionContext.KeepAliveAsync(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Neo4jClient/Transactions/Bolt/BoltResponse.cs: -------------------------------------------------------------------------------- 1 | using Neo4j.Driver; 2 | 3 | namespace Neo4jClient.Transactions.Bolt 4 | { 5 | public class BoltResponse 6 | { 7 | public IResultCursor StatementResult { get; set; } 8 | } 9 | } -------------------------------------------------------------------------------- /Neo4jClient/Transactions/Bolt/BoltSuppressTransactionProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | namespace Neo4jClient.Transactions.Bolt 5 | { 6 | using Neo4j.Driver; 7 | 8 | internal class BoltSuppressTransactionProxy : BoltTransactionScopeProxy 9 | { 10 | public BoltSuppressTransactionProxy(ITransactionalGraphClient client) 11 | : base(client, null) 12 | { 13 | } 14 | 15 | public override string Database { get; set; } 16 | 17 | protected override bool ShouldDisposeTransaction() 18 | { 19 | return false; 20 | } 21 | 22 | protected override Task DoCommitAsync() 23 | { 24 | throw new InvalidOperationException("Committing during a suppressed transaction scope"); 25 | } 26 | 27 | public override bool Committable => false; 28 | 29 | public override Task RollbackAsync() 30 | { 31 | throw new InvalidOperationException("Rolling back during a suppressed transaction scope"); 32 | } 33 | 34 | #pragma warning disable 1998 35 | public override async Task KeepAliveAsync() { } 36 | #pragma warning restore 1998 37 | 38 | public override bool IsOpen => true; 39 | public override Bookmark LastBookmark => throw new NotImplementedException(); 40 | } 41 | } -------------------------------------------------------------------------------- /Neo4jClient/Transactions/Bolt/BoltTransactionContext.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using Neo4jClient.Cypher; 3 | using Neo4jClient.Execution; 4 | 5 | namespace Neo4jClient.Transactions.Bolt 6 | { 7 | /// 8 | /// Encapsulates a transaction object with its transaction scheduler. 9 | /// 10 | /// 11 | /// All requests to the same transaction have to made sequentially. The purpose of this class is to ensure 12 | /// that such calls are made in that fashion. 13 | /// 14 | internal class BoltTransactionContext : TransactionContextBase 15 | { 16 | internal BoltNeo4jTransaction BoltTransaction => Transaction as BoltNeo4jTransaction; 17 | 18 | public BoltTransactionContext(ITransaction transaction) : base(transaction) 19 | { 20 | } 21 | 22 | protected override async Task RunQuery(BoltGraphClient graphClient, CypherQuery query, 23 | IExecutionPolicy policy, string commandDescription) 24 | { 25 | var result = await BoltTransaction.DriverTransaction.RunAsync(query, graphClient).ConfigureAwait(false); 26 | var resp = new BoltResponse {StatementResult = result}; 27 | return resp; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Neo4jClient/Transactions/ClosedTransactionException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient.Transactions 4 | { 5 | public class ClosedTransactionException : Exception 6 | { 7 | public ClosedTransactionException(string transactionEndpoint) 8 | : base("The transaction has been committed or rolled back.") 9 | { 10 | TransactionEndpoint = string.IsNullOrEmpty(transactionEndpoint) ? "No transaction endpoint. No requests were made for the transaction." : transactionEndpoint; 11 | } 12 | 13 | public string TransactionEndpoint { get; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Neo4jClient/Transactions/IInternalTransactionalGraphClient.cs: -------------------------------------------------------------------------------- 1 | namespace Neo4jClient.Transactions 2 | { 3 | /// 4 | /// Exposes the same methods and members as ITransactionalGraphClient, however it is used 5 | /// internally to access the ITransactionManager that the GraphClient uses. 6 | /// 7 | internal interface IInternalTransactionalGraphClient : ITransactionalGraphClient 8 | { 9 | ITransactionManager TransactionManager { get; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Neo4jClient/Transactions/INeo4jTransaction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Specialized; 3 | 4 | namespace Neo4jClient.Transactions 5 | { 6 | /// 7 | /// Represents the same interface as ITransaction, however this interface is used 8 | /// internally to manage the properties that requires interaction with the Neo4j HTTP interface 9 | /// 10 | internal interface INeo4jTransaction : ITransaction 11 | { 12 | /// 13 | /// The Neo4j base endpoint for this transaction 14 | /// 15 | Uri Endpoint { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Neo4jClient/Transactions/ITransaction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Specialized; 3 | using System.Threading.Tasks; 4 | 5 | namespace Neo4jClient.Transactions 6 | { 7 | using Neo4j.Driver; 8 | 9 | /// 10 | /// Represents a Neo4j transaction shared between multiple HTTP requests 11 | /// 12 | /// 13 | /// Neo4j server prevents abandoned transactions from clogging server resources 14 | /// by rolling back those that do not have requests in the configured timeout period. 15 | /// 16 | public interface ITransaction : IDisposable 17 | { 18 | /// 19 | /// In 4 + databases, which database this transaction is against. 20 | /// 21 | string Database { get; } 22 | 23 | /// 24 | /// Commits our open transaction. 25 | /// 26 | Task CommitAsync(); 27 | 28 | /// 29 | /// Rollbacks any changes made by our open transaction 30 | /// 31 | Task RollbackAsync(); 32 | 33 | /// 34 | /// Prevents the transaction from being claimed as an orphaned transaction. 35 | /// 36 | Task KeepAliveAsync(); 37 | 38 | /// 39 | /// Returns true if the transaction is still open, that is, if the programmer has not called 40 | /// Commit() or Rollback(). 41 | /// 42 | bool IsOpen { get; } 43 | 44 | /// 45 | /// Custom header collection This will be the same for the entire transaction. 46 | /// So the commit will use the same custom header(s) as the cypher custom header 47 | /// 48 | NameValueCollection CustomHeaders { get; set; } 49 | 50 | /// 51 | /// The last bookmark provided by the database to allow for causal consistency. 52 | /// 53 | Bookmark LastBookmark { get; } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Neo4jClient/Transactions/ITransactionExecutionEnvironment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Newtonsoft.Json; 4 | 5 | namespace Neo4jClient.Transactions 6 | { 7 | internal interface ITransactionExecutionEnvironmentBolt 8 | { 9 | Guid TransactionId { get; } 10 | IEnumerable JsonConverters { get; set; } 11 | string Username { get; set; } 12 | string Password { get; set; } 13 | Guid ResourceManagerId { get; set; } 14 | Neo4j.Driver.IAsyncTransaction DriverTransaction { get; set; } 15 | } 16 | 17 | internal interface ITransactionExecutionEnvironment 18 | { 19 | Uri TransactionBaseEndpoint { get; } 20 | int TransactionId { get; } 21 | bool UseJsonStreaming { get; set; } 22 | string UserAgent { get; set; } 23 | IEnumerable JsonConverters { get; set; } 24 | string Username { get; set; } 25 | string Password { get; set; } 26 | Guid ResourceManagerId { get; set; } 27 | } 28 | } -------------------------------------------------------------------------------- /Neo4jClient/Transactions/ITransactionManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading.Tasks; 4 | using Neo4jClient.Cypher; 5 | 6 | namespace Neo4jClient.Transactions 7 | { 8 | using Neo4j.Driver; 9 | 10 | /// 11 | /// Interface that handles all the queries related to transactions that could be needed in a ITransactionalGraphClient 12 | /// for implementation. 13 | /// 14 | public interface ITransactionManager : IDisposable 15 | { 16 | /// 17 | /// Is this in a transaction at the moment? 18 | /// 19 | bool InTransaction { get; } 20 | 21 | /// Begins a transaction. 22 | /// How should the transaction scope be created. 23 | /// 24 | /// for more information. 25 | /// Bookmarks for use with this transaction. 26 | /// The database to execute the transaction against. 27 | /// An object representing the transaction. 28 | ITransaction BeginTransaction(TransactionScopeOption option, IEnumerable bookmarks, string database); 29 | void EndTransaction(); 30 | ITransaction CurrentTransaction { get; } 31 | Bookmark LastBookmark { get; } 32 | Task EnqueueCypherRequest(string commandDescription, IGraphClient graphClient, CypherQuery query); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Neo4jClient/Transactions/ITransactionResourceManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient.Transactions 4 | { 5 | internal interface ITransactionResourceManager 6 | { 7 | void Enlist(ITransactionExecutionEnvironment transactionExecutionEnvironment, byte[] transactionToken); 8 | byte[] Promote(ITransactionExecutionEnvironment transactionExecutionEnvironment); 9 | void CommitTransaction(int transactionId); 10 | void RollbackTransaction(int transactionId); 11 | 12 | } 13 | 14 | internal interface ITransactionResourceManagerBolt 15 | { 16 | void Enlist(ITransactionExecutionEnvironmentBolt transactionExecutionEnvironment, byte[] transactionToken); 17 | byte[] Promote(ITransactionExecutionEnvironmentBolt transactionExecutionEnvironment); 18 | void RollbackTransaction(Guid transactionId); 19 | void CommitTransaction(Guid transactionId); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Neo4jClient/Transactions/Neo4jTransactionProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | 3 | namespace Neo4jClient.Transactions 4 | { 5 | /// 6 | /// Implements the TransactionScopeProxy interfaces for INeo4jTransaction 7 | /// 8 | internal class Neo4jTransactionProxy : TransactionScopeProxy 9 | { 10 | private readonly bool doCommitInScope; 11 | 12 | public Neo4jTransactionProxy(ITransactionalGraphClient client, TransactionContext transactionContext, bool newScope) 13 | : base(client, transactionContext) 14 | { 15 | doCommitInScope = newScope; 16 | } 17 | 18 | public override bool Committable => true; 19 | 20 | public override bool IsOpen => (TransactionContext != null) && TransactionContext.IsOpen; 21 | 22 | protected override Task DoCommitAsync() 23 | { 24 | if (doCommitInScope) 25 | return TransactionContext.CommitAsync(); 26 | #if NET45 27 | return Task.FromResult(0); 28 | #else 29 | return Task.CompletedTask; 30 | #endif 31 | } 32 | 33 | protected override bool ShouldDisposeTransaction() 34 | { 35 | return doCommitInScope; 36 | } 37 | 38 | public override Task RollbackAsync() 39 | { 40 | return TransactionContext.RollbackAsync(); 41 | } 42 | 43 | public override Task KeepAliveAsync() 44 | { 45 | return TransactionContext.KeepAliveAsync(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Neo4jClient/Transactions/SuppressTransactionProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Neo4jClient.Transactions 8 | { 9 | internal class SuppressTransactionProxy : TransactionScopeProxy 10 | { 11 | public SuppressTransactionProxy(ITransactionalGraphClient client) 12 | : base(client, null) 13 | { 14 | } 15 | 16 | protected override bool ShouldDisposeTransaction() 17 | { 18 | return false; 19 | } 20 | 21 | protected override Task DoCommitAsync() 22 | { 23 | throw new InvalidOperationException("Committing during a suppressed transaction scope"); 24 | } 25 | 26 | public override bool Committable 27 | { 28 | get { return false; } 29 | } 30 | 31 | public override Task RollbackAsync() 32 | { 33 | throw new InvalidOperationException("Rolling back during a suppressed transaction scope"); 34 | } 35 | 36 | public override Task KeepAliveAsync() 37 | { 38 | // no-op 39 | #if NET45 40 | return Task.FromResult(0); 41 | #else 42 | return Task.CompletedTask; 43 | #endif 44 | } 45 | 46 | public override bool IsOpen 47 | { 48 | // we cannot call Commit() or Rollback() for this proxy 49 | get { return true; } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Neo4jClient/Transactions/ThreadContextHelpers.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using Neo4jClient.Transactions.Bolt; 3 | 4 | namespace Neo4jClient.Transactions 5 | { 6 | internal static class ThreadContextHelper 7 | { 8 | internal static IScopedTransactions CreateScopedTransactions() 9 | { 10 | return new ThreadContextWrapper(); 11 | } 12 | 13 | internal static IScopedTransactions CreateBoltScopedTransactions() 14 | { 15 | return new ThreadContextWrapper(); 16 | } 17 | } 18 | 19 | internal interface IScopedTransactions where T : class 20 | { 21 | int Count { get; } 22 | bool HasValue { get; } 23 | void Push(T item); 24 | T Pop(); 25 | T TryPop(); 26 | T Peek(); 27 | T TryPeek(); 28 | } 29 | 30 | internal class ThreadContextWrapper : IScopedTransactions where T : class 31 | { 32 | private readonly Stack _stack; 33 | 34 | public ThreadContextWrapper() 35 | { 36 | _stack = new Stack(); 37 | } 38 | 39 | public int Count => _stack?.Count ?? 0; 40 | public bool HasValue => _stack != null; 41 | 42 | 43 | public T Peek() 44 | { 45 | return _stack.Peek(); 46 | } 47 | 48 | public T TryPeek() 49 | { 50 | return _stack.Count > 0 ? _stack.Peek() : null; 51 | } 52 | 53 | public T Pop() 54 | { 55 | return _stack.Pop(); 56 | } 57 | 58 | public T TryPop() 59 | { 60 | return _stack.Count > 0 ? _stack.Pop() : null; 61 | } 62 | 63 | public void Push(T item) 64 | { 65 | _stack.Push(item); 66 | } 67 | } 68 | } -------------------------------------------------------------------------------- /Neo4jClient/Transactions/TransactionConnectionContext.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Neo4jClient.Transactions 7 | { 8 | internal class TransactionConnectionContext 9 | { 10 | public ITransactionalGraphClient Client { get; set; } 11 | 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Neo4jClient/Transactions/TransactionExecutionEnvironment.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Neo4j.Driver; 4 | using Neo4jClient.Execution; 5 | using Newtonsoft.Json; 6 | 7 | namespace Neo4jClient.Transactions 8 | { 9 | internal class BoltTransactionExecutionEnvironment : MarshalByRefObject, ITransactionExecutionEnvironmentBolt 10 | { 11 | public BoltTransactionExecutionEnvironment(ExecutionConfiguration executionConfiguration) 12 | { 13 | Username = executionConfiguration.Username; 14 | Password = executionConfiguration.Password; 15 | JsonConverters = executionConfiguration.JsonConverters; 16 | ResourceManagerId = executionConfiguration.ResourceManagerId; 17 | } 18 | 19 | public IAsyncSession Session { get; set; } 20 | public IList Bookmarks { get; set; } 21 | public Guid TransactionId { get; set; } 22 | public IEnumerable JsonConverters { get; set; } 23 | public string Username { get; set; } 24 | public string Password { get; set; } 25 | public Guid ResourceManagerId { get; set; } 26 | public IAsyncTransaction DriverTransaction { get; set; } 27 | } 28 | 29 | /// 30 | /// Because the resource manager is held in another application domain, the transaction execution environment 31 | /// has to be serialized to cross app domain boundaries. 32 | /// 33 | internal class TransactionExecutionEnvironment : MarshalByRefObject, ITransactionExecutionEnvironment 34 | { 35 | public TransactionExecutionEnvironment(ExecutionConfiguration executionConfiguration) 36 | { 37 | UserAgent = executionConfiguration.UserAgent; 38 | UseJsonStreaming = executionConfiguration.UseJsonStreaming; 39 | Username = executionConfiguration.Username; 40 | Password = executionConfiguration.Password; 41 | JsonConverters = executionConfiguration.JsonConverters; 42 | ResourceManagerId = executionConfiguration.ResourceManagerId; 43 | } 44 | 45 | public Uri TransactionBaseEndpoint { get; set; } 46 | public int TransactionId { get; set; } 47 | public bool UseJsonStreaming { get; set; } 48 | public string UserAgent { get; set; } 49 | public IEnumerable JsonConverters { get; set; } 50 | public string Username { get; set; } 51 | public string Password { get; set; } 52 | public Guid ResourceManagerId { get; set; } 53 | } 54 | } -------------------------------------------------------------------------------- /Neo4jClient/Transactions/TransactionHttpUtils.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Net.Http; 4 | 5 | namespace Neo4jClient.Transactions 6 | { 7 | /// 8 | /// Contains utility methods for handling HttpResponseMessages in a transaction scope 9 | /// 10 | internal static class TransactionHttpUtils 11 | { 12 | public static IDictionary GetMetadataFromResponse(HttpResponseMessage response) 13 | { 14 | return response.Headers.ToDictionary( 15 | headerPair => headerPair.Key, 16 | headerPair => (object)headerPair.Value); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Neo4jClient/UriCreator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Neo4jClient 4 | { 5 | internal static class UriCreator 6 | { 7 | /// 8 | /// Creates a from the given instance. 9 | /// 10 | /// A representing a URI, can contain the scheme, or not. 11 | /// A from the given instance. 12 | public static Uri From(string uri) 13 | { 14 | if (string.IsNullOrWhiteSpace(uri)) 15 | return null; 16 | 17 | if (uri.IndexOf("://", StringComparison.Ordinal) == -1) 18 | uri = $"scheme://{uri}"; 19 | 20 | var output = new UriBuilder(uri); 21 | if (output.Uri.IsDefaultPort) 22 | output.Port = 7687; 23 | 24 | return output.Uri; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /Neo4jClient/Utilities.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using Neo4jClient.ApiModels; 4 | 5 | namespace Neo4jClient 6 | { 7 | public class Utilities 8 | { 9 | public static IEnumerable GetDifferencesBetweenDictionaries(IDictionary originalValues, IDictionary newValues) 10 | { 11 | var changes = new List(); 12 | 13 | changes.AddRange(originalValues 14 | .Keys 15 | .Where(k => newValues.Keys.All(n => n != k)) 16 | .Select(k => new FieldChange 17 | { 18 | FieldName = k, 19 | OldValue = originalValues[k], 20 | NewValue = "" 21 | })); 22 | 23 | changes.AddRange(newValues 24 | .Keys 25 | .Where(k => originalValues.Keys.All(n => n != k)) 26 | .Select(k => new FieldChange 27 | { 28 | FieldName = k, 29 | OldValue = "", 30 | NewValue = newValues[k], 31 | })); 32 | 33 | changes.AddRange(newValues 34 | .Keys 35 | .Where(k => originalValues.Keys.Any(n => n == k) && originalValues[k] != newValues[k]) 36 | .Select(k =>new FieldChange 37 | { 38 | FieldName = k, 39 | OldValue = originalValues[k], 40 | NewValue = newValues[k], 41 | } 42 | )); 43 | 44 | return changes; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /build.bat: -------------------------------------------------------------------------------- 1 | REM MyGet Build Commands 2 | 3 | @echo Off 4 | set config=%1 5 | if "%config%" == "" ( 6 | set config=Release 7 | ) 8 | 9 | set nuget_version=-Version 2.0.0.0 10 | if not "%PackageVersion%" == "" ( 11 | set nuget_version=-Version %PackageVersion% 12 | ) 13 | 14 | REM Restore packages 15 | tools\nuget.exe restore Neo4jClient.sln 16 | if not "%errorlevel%"=="0" goto failure 17 | @echo Packages restored - on to build... 18 | 19 | pushd tools 20 | REM Find location of the latest MSBuild using vswhere: https://github.com/Microsoft/vswhere 21 | for /f "usebackq tokens=*" %%i in (`vswhere -latest -products * -requires Microsoft.Component.MSBuild -property installationPath`) do ( 22 | set InstallDir=%%i 23 | ) 24 | 25 | set msbuild="" 26 | 27 | if exist "%InstallDir%\MSBuild\15.0\Bin\MSBuild.exe" ( 28 | set msbuild="%InstallDir%\MSBuild\15.0\Bin\MSBuild.exe" 29 | ) 30 | 31 | if %msbuild%=="" goto failure 32 | 33 | echo MSBuild located at "%msbuild%" 34 | popd 35 | 36 | REM Build 37 | %msbuild% /p:Configuration="%config%" /m /v:M /fl /flp:LogFile=msbuild.log;Verbosity=Normal /nr:false 38 | if not "%errorlevel%"=="0" goto failure 39 | @echo Built and onto tests.... 40 | 41 | 42 | REM XUnit tests 43 | tools\nuget.exe install xunit.runner.console -Version 2.2.0 -OutputDirectory packages 44 | if not "%errorlevel%"=="0" goto failure 45 | @echo XUnit installed... 46 | 47 | packages\xunit.runner.console.2.2.0\tools\xunit.console.x86.exe Neo4jClient.Tests\bin\%config%\Neo4jClient.Tests.dll 48 | if not "%errorlevel%"=="0" goto failure 49 | @echo Neo4jClient tests... SUCCESS. 50 | 51 | packages\xunit.runner.console.2.2.0\tools\xunit.console.x86.exe Neo4jClient.Vb.Tests\bin\%config%\Neo4jClient.Vb.Tests.dll 52 | if not "%errorlevel%"=="0" goto failure 53 | @echo Neo4jClient VB tests... SUCCESS. 54 | 55 | REM Package 56 | mkdir Artifacts 57 | tools\nuget.exe pack "Neo4jClient.nuspec" -o Artifacts -p Configuration=%config% %nuget_version% 58 | if not "%errorlevel%"=="0" goto failure 59 | @echo Packed and ready to roll! 60 | 61 | :success 62 | exit /b 0 63 | 64 | :failure 65 | exit /b -1 -------------------------------------------------------------------------------- /tools/nuget.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotNet4Neo4j/Neo4jClient/a18f7a74d1b424251895796479c13ab94c5e7cac/tools/nuget.exe -------------------------------------------------------------------------------- /tools/vswhere.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DotNet4Neo4j/Neo4jClient/a18f7a74d1b424251895796479c13ab94c5e7cac/tools/vswhere.exe --------------------------------------------------------------------------------