diff --git a/RelationshipAnalysis.Integration.Test/Resources.Designer.cs b/RelationshipAnalysis.Integration.Test/Resources.Designer.cs
deleted file mode 100644
index 434b002..0000000
--- a/RelationshipAnalysis.Integration.Test/Resources.Designer.cs
+++ /dev/null
@@ -1,204 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace RelationshipAnalysis.Integration.Test {
- using System;
-
-
- [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
- [System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources {
-
- private static System.Resources.ResourceManager resourceMan;
-
- private static System.Globalization.CultureInfo resourceCulture;
-
- [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources() {
- }
-
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static System.Resources.ResourceManager ResourceManager {
- get {
- if (object.Equals(null, resourceMan)) {
- System.Resources.ResourceManager temp = new System.Resources.ResourceManager("RelationshipAnalysis.Integration.Test.Resources", typeof(Resources).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- internal static string ValidFileMessage {
- get {
- return ResourceManager.GetString("ValidFileMessage", resourceCulture);
- }
- }
-
- internal static string FailedAddRecordsMessage {
- get {
- return ResourceManager.GetString("FailedAddRecordsMessage", resourceCulture);
- }
- }
-
- internal static string InvalidHeaderAttribute {
- get {
- return ResourceManager.GetString("InvalidHeaderAttribute", resourceCulture);
- }
- }
-
- internal static string TwoSameHeadersMessage {
- get {
- return ResourceManager.GetString("TwoSameHeadersMessage", resourceCulture);
- }
- }
-
- internal static string InvalidNodeCategory {
- get {
- return ResourceManager.GetString("InvalidNodeCategory", resourceCulture);
- }
- }
-
- internal static string SuccessfulNodeAdditionMessage {
- get {
- return ResourceManager.GetString("SuccessfulNodeAdditionMessage", resourceCulture);
- }
- }
-
- internal static string NoFileUploadedMessage {
- get {
- return ResourceManager.GetString("NoFileUploadedMessage", resourceCulture);
- }
- }
-
- internal static string EmailExistsMessage {
- get {
- return ResourceManager.GetString("EmailExistsMessage", resourceCulture);
- }
- }
-
- internal static string SuccessfulLogoutMessage {
- get {
- return ResourceManager.GetString("SuccessfulLogoutMessage", resourceCulture);
- }
- }
-
- internal static string InvalidPasswordMessage {
- get {
- return ResourceManager.GetString("InvalidPasswordMessage", resourceCulture);
- }
- }
-
- internal static string UserNotFoundMessage {
- get {
- return ResourceManager.GetString("UserNotFoundMessage", resourceCulture);
- }
- }
-
- internal static string WrongOldPasswordMessage {
- get {
- return ResourceManager.GetString("WrongOldPasswordMessage", resourceCulture);
- }
- }
-
- internal static string OldPasswordRequired {
- get {
- return ResourceManager.GetString("OldPasswordRequired", resourceCulture);
- }
- }
-
- internal static string NewPasswordRequired {
- get {
- return ResourceManager.GetString("NewPasswordRequired", resourceCulture);
- }
- }
-
- internal static string UsernameRequired {
- get {
- return ResourceManager.GetString("UsernameRequired", resourceCulture);
- }
- }
-
- internal static string PasswordRequired {
- get {
- return ResourceManager.GetString("PasswordRequired", resourceCulture);
- }
- }
-
- internal static string SuccessfulUpdateUserMessage {
- get {
- return ResourceManager.GetString("SuccessfulUpdateUserMessage", resourceCulture);
- }
- }
-
- internal static string SuccessfulDeleteUserMessage {
- get {
- return ResourceManager.GetString("SuccessfulDeleteUserMessage", resourceCulture);
- }
- }
-
- internal static string UsernameExistsMessage {
- get {
- return ResourceManager.GetString("UsernameExistsMessage", resourceCulture);
- }
- }
-
- internal static string SucceddfulCreateUser {
- get {
- return ResourceManager.GetString("SucceddfulCreateUser", resourceCulture);
- }
- }
-
- internal static string EmptyRolesMessage {
- get {
- return ResourceManager.GetString("EmptyRolesMessage", resourceCulture);
- }
- }
-
- internal static string InvalidRolesListMessage {
- get {
- return ResourceManager.GetString("InvalidRolesListMessage", resourceCulture);
- }
- }
-
- internal static string SuccessfulUpdateRolesMessage {
- get {
- return ResourceManager.GetString("SuccessfulUpdateRolesMessage", resourceCulture);
- }
- }
-
- internal static string LoginFailedMessage {
- get {
- return ResourceManager.GetString("LoginFailedMessage", resourceCulture);
- }
- }
-
- internal static string SuccessfulLoginMessage {
- get {
- return ResourceManager.GetString("SuccessfulLoginMessage", resourceCulture);
- }
- }
-
- internal static string DeleteAccountAccessErrorMessage {
- get {
- return ResourceManager.GetString("DeleteAccountAccessErrorMessage", resourceCulture);
- }
- }
- }
-}
diff --git a/RelationshipAnalysis.Integration.Test/Resources.resx b/RelationshipAnalysis.Integration.Test/Resources.resx
deleted file mode 100644
index 6f03822..0000000
--- a/RelationshipAnalysis.Integration.Test/Resources.resx
+++ /dev/null
@@ -1,108 +0,0 @@
-
-
-
-
-
- File is valid!
-
-
- Add records failed!
-
-
- Entered header is invalid
-
-
- Two headers have the same title!
-
-
- Entered node category is invalid
-
-
- Nodes added successfully
-
-
- No file uploaded!
-
-
- Chosen email already exists
-
-
- Logout was successful!
-
-
- Password must contain one digit from 1 to 9, one lowercase letter, one uppercase letter, one special
- character, no space, and it must be 8-16 characters long.
-
-
-
- Could not find user!
-
-
- Entered old password is wrong
-
-
- Old password is required
-
-
- New password is required
-
-
- Username is required!
-
-
- Password is required!
-
-
- User updated successfully!
-
-
- User Deleted successfully!
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 1.3
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
-
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
-
-
-
- Chosen username already exists
-
-
- User Created Successfuly!
-
-
- Roles cant be empty!
-
-
- Roles are invalid!
-
-
- User roles updated successfuly!
-
-
- Login Failed!
-
-
- Login was successful!
-
-
- You cant delete your account!
-
-
\ No newline at end of file
diff --git a/RelationshipAnalysis.Test/Resources.Designer.cs b/RelationshipAnalysis.Test/Resources.Designer.cs
deleted file mode 100644
index 7b293ca..0000000
--- a/RelationshipAnalysis.Test/Resources.Designer.cs
+++ /dev/null
@@ -1,246 +0,0 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace RelationshipAnalysis.Test {
- using System;
-
-
- [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
- [System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources {
-
- private static System.Resources.ResourceManager resourceMan;
-
- private static System.Globalization.CultureInfo resourceCulture;
-
- [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources() {
- }
-
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static System.Resources.ResourceManager ResourceManager {
- get {
- if (object.Equals(null, resourceMan)) {
- System.Resources.ResourceManager temp = new System.Resources.ResourceManager("RelationshipAnalysis.Test.Resources", typeof(Resources).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- internal static string InvalidTargetNodeCategory {
- get {
- return ResourceManager.GetString("InvalidTargetNodeCategory", resourceCulture);
- }
- }
-
- internal static string InvalidSourceNodeCategory {
- get {
- return ResourceManager.GetString("InvalidSourceNodeCategory", resourceCulture);
- }
- }
-
- internal static string InvalidEdgeCategory {
- get {
- return ResourceManager.GetString("InvalidEdgeCategory", resourceCulture);
- }
- }
-
- internal static string SuccessfulEdgeAdditionMessage {
- get {
- return ResourceManager.GetString("SuccessfulEdgeAdditionMessage", resourceCulture);
- }
- }
-
- internal static string ValidFileMessage {
- get {
- return ResourceManager.GetString("ValidFileMessage", resourceCulture);
- }
- }
-
- internal static string FailedAddRecordsMessage {
- get {
- return ResourceManager.GetString("FailedAddRecordsMessage", resourceCulture);
- }
- }
-
- internal static string InvalidHeaderAttribute {
- get {
- return ResourceManager.GetString("InvalidHeaderAttribute", resourceCulture);
- }
- }
-
- internal static string TwoSameHeadersMessage {
- get {
- return ResourceManager.GetString("TwoSameHeadersMessage", resourceCulture);
- }
- }
-
- internal static string InvalidNodeCategory {
- get {
- return ResourceManager.GetString("InvalidNodeCategory", resourceCulture);
- }
- }
-
- internal static string SuccessfulNodeAdditionMessage {
- get {
- return ResourceManager.GetString("SuccessfulNodeAdditionMessage", resourceCulture);
- }
- }
-
- internal static string NoFileUploadedMessage {
- get {
- return ResourceManager.GetString("NoFileUploadedMessage", resourceCulture);
- }
- }
-
- internal static string EmailExistsMessage {
- get {
- return ResourceManager.GetString("EmailExistsMessage", resourceCulture);
- }
- }
-
- internal static string SuccessfulLogoutMessage {
- get {
- return ResourceManager.GetString("SuccessfulLogoutMessage", resourceCulture);
- }
- }
-
- internal static string InvalidPasswordMessage {
- get {
- return ResourceManager.GetString("InvalidPasswordMessage", resourceCulture);
- }
- }
-
- internal static string UserNotFoundMessage {
- get {
- return ResourceManager.GetString("UserNotFoundMessage", resourceCulture);
- }
- }
-
- internal static string WrongOldPasswordMessage {
- get {
- return ResourceManager.GetString("WrongOldPasswordMessage", resourceCulture);
- }
- }
-
- internal static string OldPasswordRequired {
- get {
- return ResourceManager.GetString("OldPasswordRequired", resourceCulture);
- }
- }
-
- internal static string NewPasswordRequired {
- get {
- return ResourceManager.GetString("NewPasswordRequired", resourceCulture);
- }
- }
-
- internal static string UsernameRequired {
- get {
- return ResourceManager.GetString("UsernameRequired", resourceCulture);
- }
- }
-
- internal static string PasswordRequired {
- get {
- return ResourceManager.GetString("PasswordRequired", resourceCulture);
- }
- }
-
- internal static string SuccessfulUpdateUserMessage {
- get {
- return ResourceManager.GetString("SuccessfulUpdateUserMessage", resourceCulture);
- }
- }
-
- internal static string SuccessfulDeleteUserMessage {
- get {
- return ResourceManager.GetString("SuccessfulDeleteUserMessage", resourceCulture);
- }
- }
-
- internal static string UsernameExistsMessage {
- get {
- return ResourceManager.GetString("UsernameExistsMessage", resourceCulture);
- }
- }
-
- internal static string SucceddfulCreateUser {
- get {
- return ResourceManager.GetString("SucceddfulCreateUser", resourceCulture);
- }
- }
-
- internal static string EmptyRolesMessage {
- get {
- return ResourceManager.GetString("EmptyRolesMessage", resourceCulture);
- }
- }
-
- internal static string InvalidRolesListMessage {
- get {
- return ResourceManager.GetString("InvalidRolesListMessage", resourceCulture);
- }
- }
-
- internal static string SuccessfulUpdateRolesMessage {
- get {
- return ResourceManager.GetString("SuccessfulUpdateRolesMessage", resourceCulture);
- }
- }
-
- internal static string LoginFailedMessage {
- get {
- return ResourceManager.GetString("LoginFailedMessage", resourceCulture);
- }
- }
-
- internal static string SuccessfulLoginMessage {
- get {
- return ResourceManager.GetString("SuccessfulLoginMessage", resourceCulture);
- }
- }
-
- internal static string DeleteAccountAccessErrorMessage {
- get {
- return ResourceManager.GetString("DeleteAccountAccessErrorMessage", resourceCulture);
- }
- }
-
- internal static string NullDtoErrorMessage {
- get {
- return ResourceManager.GetString("NullDtoErrorMessage", resourceCulture);
- }
- }
-
- internal static string NotUniqueCategoryNameErrorMessage {
- get {
- return ResourceManager.GetString("NotUniqueCategoryNameErrorMessage", resourceCulture);
- }
- }
-
- internal static string SuccessfulCreateCategory {
- get {
- return ResourceManager.GetString("SuccessfulCreateCategory", resourceCulture);
- }
- }
- }
-}
diff --git a/RelationshipAnalysis.Test/Resources.resx b/RelationshipAnalysis.Test/Resources.resx
deleted file mode 100644
index a3c6f68..0000000
--- a/RelationshipAnalysis.Test/Resources.resx
+++ /dev/null
@@ -1,130 +0,0 @@
-
-
-
-
- Target Node category is invalid!
-
-
- Source Node category is invalid!
-
-
- Edge category is invalid!
-
-
- Edges added successfully!
-
-
-
-
- File is valid!
-
-
- Add records failed!
-
-
- Entered header is invalid
-
-
- Two headers have the same title!
-
-
- Entered node category is invalid
-
-
- Nodes added successfully
-
-
- No file uploaded!
-
-
- Chosen email already exists
-
-
- Logout was successful!
-
-
- Password must contain one digit from 1 to 9, one lowercase letter, one uppercase letter, one special
- character, no space, and it must be 8-16 characters long.
-
-
-
- Could not find user!
-
-
- Entered old password is wrong
-
-
- Old password is required
-
-
- New password is required
-
-
- Username is required!
-
-
- Password is required!
-
-
- User updated successfully!
-
-
- User Deleted successfully!
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 1.3
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
-
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
- PublicKeyToken=b77a5c561934e089
-
-
-
- Chosen username already exists
-
-
- User Created Successfuly!
-
-
- Roles cant be empty!
-
-
- Roles are invalid!
-
-
- User roles updated successfuly!
-
-
- Login Failed!
-
-
- Login was successful!
-
-
- You cant delete your account!
-
-
- Input Dto can not be null!
-
-
- Category Name is not unique!
-
-
- Category Created successfylly!
-
-
\ No newline at end of file
diff --git a/RelationshipAnalysis.Test/Services/GraphServices/ClauseValidatorServiceTests.cs b/RelationshipAnalysis.Test/Services/GraphServices/ClauseValidatorServiceTests.cs
new file mode 100644
index 0000000..c07f75c
--- /dev/null
+++ b/RelationshipAnalysis.Test/Services/GraphServices/ClauseValidatorServiceTests.cs
@@ -0,0 +1,108 @@
+
+using RelationshipAnalysis.Dto.Graph;
+using RelationshipAnalysis.Enums;
+using RelationshipAnalysis.Services.GraphServices;
+
+namespace RelationshipAnalysis.Test.Services.GraphServices;
+
+public class ClauseValidatorServiceTests
+{
+ private readonly ClauseValidatorService _sut;
+
+ public ClauseValidatorServiceTests()
+ {
+ _sut = new ClauseValidatorService();
+ }
+
+ [Fact]
+ public async Task AreClausesValid_ShouldReturnSuccess_WhenAllClausesAreValid()
+ {
+ // Arrange
+ var searchGraphDto = new SearchGraphDto
+ {
+ SourceCategoryClauses = new Dictionary { { "validSource", "value" } },
+ TargetCategoryClauses = new Dictionary { { "validTarget", "value" } },
+ EdgeCategoryClauses = new Dictionary { { "validEdge", "value" } }
+ };
+
+ var sourceAttributes = new List { "validSource" };
+ var targetAttributes = new List { "validTarget" };
+ var edgeAttributes = new List { "validEdge" };
+
+ // Act
+ var result = await _sut.AreClausesValid(searchGraphDto, sourceAttributes, targetAttributes, edgeAttributes);
+
+ // Assert
+ Assert.Equal(StatusCodeType.Success, result.StatusCode);
+ Assert.Null(result.Data);
+ }
+
+ [Fact]
+ public async Task AreClausesValid_ShouldReturnNotFound_WhenSourceCategoryClausesAreInvalid()
+ {
+ // Arrange
+ var searchGraphDto = new SearchGraphDto
+ {
+ SourceCategoryClauses = new Dictionary { { "invalidSource", "value" } },
+ TargetCategoryClauses = new Dictionary { { "validTarget", "value" } },
+ EdgeCategoryClauses = new Dictionary { { "validEdge", "value" } }
+ };
+
+ var sourceAttributes = new List { "validSource" };
+ var targetAttributes = new List { "validTarget" };
+ var edgeAttributes = new List { "validEdge" };
+
+ // Act
+ var result = await _sut.AreClausesValid(searchGraphDto, sourceAttributes, targetAttributes, edgeAttributes);
+
+ // Assert
+ Assert.Equal(StatusCodeType.NotFound, result.StatusCode);
+ Assert.Equal(Resources.InvalidClauseInSourceCategory, result.Data.Message);
+ }
+
+ [Fact]
+ public async Task AreClausesValid_ShouldReturnNotFound_WhenTargetCategoryClausesAreInvalid()
+ {
+ // Arrange
+ var searchGraphDto = new SearchGraphDto
+ {
+ SourceCategoryClauses = new Dictionary { { "validSource", "value" } },
+ TargetCategoryClauses = new Dictionary { { "invalidTarget", "value" } },
+ EdgeCategoryClauses = new Dictionary { { "validEdge", "value" } }
+ };
+
+ var sourceAttributes = new List { "validSource" };
+ var targetAttributes = new List { "validTarget" };
+ var edgeAttributes = new List { "validEdge" };
+
+ // Act
+ var result = await _sut.AreClausesValid(searchGraphDto, sourceAttributes, targetAttributes, edgeAttributes);
+
+ // Assert
+ Assert.Equal(StatusCodeType.NotFound, result.StatusCode);
+ Assert.Equal(Resources.InvalidClauseInDestinationCategory, result.Data.Message);
+ }
+
+ [Fact]
+ public async Task AreClausesValid_ShouldReturnNotFound_WhenEdgeCategoryClausesAreInvalid()
+ {
+ // Arrange
+ var searchGraphDto = new SearchGraphDto
+ {
+ SourceCategoryClauses = new Dictionary { { "validSource", "value" } },
+ TargetCategoryClauses = new Dictionary { { "validTarget", "value" } },
+ EdgeCategoryClauses = new Dictionary { { "invalidEdge", "value" } }
+ };
+
+ var sourceAttributes = new List { "validSource" };
+ var targetAttributes = new List { "validTarget" };
+ var edgeAttributes = new List { "validEdge" };
+
+ // Act
+ var result = await _sut.AreClausesValid(searchGraphDto, sourceAttributes, targetAttributes, edgeAttributes);
+
+ // Assert
+ Assert.Equal(StatusCodeType.NotFound, result.StatusCode);
+ Assert.Equal(Resources.InvalidClauseInDestinationCategory, result.Data.Message);
+ }
+}
diff --git a/RelationshipAnalysis.Test/Services/GraphServices/Edge/EdgeSearchValidatorTests.cs b/RelationshipAnalysis.Test/Services/GraphServices/Edge/EdgeSearchValidatorTests.cs
new file mode 100644
index 0000000..5c86903
--- /dev/null
+++ b/RelationshipAnalysis.Test/Services/GraphServices/Edge/EdgeSearchValidatorTests.cs
@@ -0,0 +1,137 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using RelationshipAnalysis.Context;
+using RelationshipAnalysis.Models.Graph.Edge;
+using RelationshipAnalysis.Services.GraphServices.Edge;
+
+namespace RelationshipAnalysis.Test.Services.GraphServices.Edge
+{
+ public class EdgeSearchValidatorTests
+ {
+ private readonly IServiceProvider _serviceProvider;
+ private readonly EdgeSearchValidator _sut;
+
+ private readonly Models.Graph.Node.Node _sourceNode1 = new()
+ {
+ NodeId = 1,
+ NodeCategoryId = 1,
+ NodeUniqueString = "1"
+ };
+
+ private readonly Models.Graph.Node.Node _targetNode1 = new()
+ {
+ NodeId = 2,
+ NodeCategoryId = 2,
+ NodeUniqueString = "2"
+ };
+
+ private readonly EdgeCategory _edgeCategory1 = new() { EdgeCategoryId = 1, EdgeCategoryName = "Category1" };
+
+ private readonly Models.Graph.Edge.Edge _edge1 = new()
+ {
+ EdgeUniqueString = "E",
+ EdgeCategoryId = 1,
+ EdgeSourceNodeId = 1,
+ EdgeDestinationNodeId = 2,
+ EdgeValues = new List
+ {
+ new() { EdgeAttribute = new EdgeAttribute { EdgeAttributeName = "Relationship" }, ValueData = "Friend" }
+ }
+ };
+
+ public EdgeSearchValidatorTests()
+ {
+ var serviceCollection = new ServiceCollection();
+
+ var options = new DbContextOptionsBuilder()
+ .UseInMemoryDatabase(Guid.NewGuid().ToString())
+ .UseLazyLoadingProxies()
+ .Options;
+
+ serviceCollection.AddScoped(_ => new ApplicationDbContext(options));
+
+ _serviceProvider = serviceCollection.BuildServiceProvider();
+
+ _sut = new EdgeSearchValidator(_serviceProvider);
+ }
+
+ private void SeedDatabase()
+ {
+ using var scope = _serviceProvider.CreateScope();
+ var context = scope.ServiceProvider.GetRequiredService();
+ context.Nodes.AddRange(_sourceNode1, _targetNode1);
+ context.EdgeCategories.Add(_edgeCategory1);
+ context.Edges.Add(_edge1);
+ context.SaveChanges();
+ }
+
+ [Fact]
+ public async Task GetValidEdges_ShouldReturnEmptyList_WhenNoEdgesMatch()
+ {
+ // Arrange
+ SeedDatabase();
+ var sourceNodes = new List { _sourceNode1 };
+ var targetNodes = new List { _targetNode1 };
+ var clauses = new Dictionary { { "Relationship", "Colleague" } };
+
+ // Act
+ var result = await _sut.GetValidEdges(sourceNodes, targetNodes, "Category1", clauses);
+
+ // Assert
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public async Task GetValidEdges_ShouldReturnEdges_WhenClauseMatches()
+ {
+ // Arrange
+ SeedDatabase();
+ var sourceNodes = new List { _sourceNode1 };
+ var targetNodes = new List { _targetNode1 };
+ var clauses = new Dictionary { { "Relationship", "Friend" } }; // "Friend" exists in Edge1
+
+ // Act
+ var result = await _sut.GetValidEdges(sourceNodes, targetNodes, "Category1", clauses);
+
+ // Assert
+ Assert.Single(result);
+ Assert.Contains(result, edge => edge.EdgeValues.Any(ev => ev.ValueData == "Friend"));
+ }
+
+ [Fact]
+ public async Task GetValidEdges_ShouldReturnEmptyList_WhenEdgeCategoryNameDoesNotMatch()
+ {
+ // Arrange
+ SeedDatabase();
+ var sourceNodes = new List { _sourceNode1 };
+ var targetNodes = new List { _targetNode1 };
+ var clauses = new Dictionary { { "Relationship", "Friend" } };
+
+ // Act
+ var result = await _sut.GetValidEdges(sourceNodes, targetNodes, "Category2", clauses);
+
+ // Assert
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public async Task GetValidEdges_ShouldReturnCorrectEdges_WhenMultipleClausesMatch()
+ {
+ // Arrange
+ SeedDatabase();
+ var sourceNodes = new List { _sourceNode1 };
+ var targetNodes = new List { _targetNode1 };
+ var clauses = new Dictionary
+ {
+ { "Relationship", "Friend" }
+ };
+
+ // Act
+ var result = await _sut.GetValidEdges(sourceNodes, targetNodes, "Category1", clauses);
+
+ // Assert
+ Assert.Single(result);
+ Assert.Contains(result, edge => edge.EdgeValues.Any(ev => ev.ValueData == "Friend"));
+ }
+ }
+}
diff --git a/RelationshipAnalysis.Test/Services/GraphServices/Graph/GraphSearcherServiceTests.cs b/RelationshipAnalysis.Test/Services/GraphServices/Graph/GraphSearcherServiceTests.cs
new file mode 100644
index 0000000..940d85c
--- /dev/null
+++ b/RelationshipAnalysis.Test/Services/GraphServices/Graph/GraphSearcherServiceTests.cs
@@ -0,0 +1,199 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using RelationshipAnalysis.Context;
+using RelationshipAnalysis.Dto;
+using RelationshipAnalysis.Dto.Graph;
+using RelationshipAnalysis.Enums;
+using RelationshipAnalysis.Services.GraphServices.Graph;
+using Moq;
+using RelationshipAnalysis.Dto.Graph.Edge;
+using RelationshipAnalysis.Dto.Graph.Node;
+using RelationshipAnalysis.Services.GraphServices.Abstraction;
+using RelationshipAnalysis.Services.GraphServices.Edge.Abstraction;
+using RelationshipAnalysis.Services.GraphServices.Graph.Abstraction;
+
+namespace RelationshipAnalysis.Test.Services.GraphServices.Graph
+{
+ public class GraphSearcherServiceTests
+ {
+ private readonly IServiceProvider _serviceProvider;
+ private readonly Mock _mockGraphDtoCreator;
+ private readonly Mock _mockNodeCategoryReceiver;
+ private readonly Mock _mockEdgeCategoryReceiver;
+ private readonly Mock _mockNodeValidator;
+ private readonly Mock _mockEdgeValidator;
+ private readonly Mock _mockClauseValidatorService;
+ private readonly GraphSearcherService _sut;
+
+ public GraphSearcherServiceTests()
+ {
+ var serviceCollection = new ServiceCollection();
+
+ var options = new DbContextOptionsBuilder()
+ .UseInMemoryDatabase(Guid.NewGuid().ToString())
+ .Options;
+
+ serviceCollection.AddScoped(_ => new ApplicationDbContext(options));
+
+ _serviceProvider = serviceCollection.BuildServiceProvider();
+
+ _mockGraphDtoCreator = new Mock();
+ _mockNodeCategoryReceiver = new Mock();
+ _mockEdgeCategoryReceiver = new Mock();
+ _mockNodeValidator = new Mock();
+ _mockEdgeValidator = new Mock();
+ _mockClauseValidatorService = new Mock();
+
+ _sut = new GraphSearcherService(
+ _mockGraphDtoCreator.Object,
+ _mockNodeCategoryReceiver.Object,
+ _mockEdgeCategoryReceiver.Object,
+ _mockNodeValidator.Object,
+ _mockEdgeValidator.Object,
+ _mockClauseValidatorService.Object
+ );
+ }
+
+ [Fact]
+ public async Task Search_ShouldReturnValidationError_WhenClauseValidationFails()
+ {
+ // Arrange
+ var searchGraphDto = new SearchGraphDto
+ {
+ SourceCategoryName = "Category1",
+ TargetCategoryName = "Category2",
+ EdgeCategoryName = "EdgeCategory"
+ };
+
+ _mockClauseValidatorService
+ .Setup(s => s.AreClausesValid(It.IsAny(), It.IsAny>(), It.IsAny>(), It.IsAny>()))
+ .ReturnsAsync(new ActionResponse { StatusCode = StatusCodeType.BadRequest });
+
+ // Act
+ var result = await _sut.Search(searchGraphDto);
+
+ // Assert
+ Assert.Equal(StatusCodeType.BadRequest, result.StatusCode);
+ }
+
+ [Fact]
+ public async Task Search_ShouldReturnEmptyGraph_WhenNoValidNodesOrEdges()
+ {
+ // Arrange
+
+ _mockGraphDtoCreator
+ .Setup(gdc => gdc.CreateResultGraphDto(It.IsAny>(), It.IsAny>()))
+ .Returns(new GraphDto
+ {
+ Nodes = new List(),
+ Edges = new List()
+ });
+
+ var searchGraphDto = new SearchGraphDto
+ {
+ SourceCategoryName = "Category1",
+ TargetCategoryName = "Category2",
+ EdgeCategoryName = "EdgeCategory",
+ EdgeCategoryClauses = new Dictionary(),
+ SourceCategoryClauses = new Dictionary(),
+ TargetCategoryClauses = new Dictionary()
+ };
+
+ _mockClauseValidatorService
+ .Setup(s => s.AreClausesValid(It.IsAny(), It.IsAny>(), It.IsAny>(), It.IsAny>()))
+ .ReturnsAsync(new ActionResponse { StatusCode = StatusCodeType.Success });
+
+ _mockNodeValidator
+ .Setup(nv => nv.GetValidNodes(It.IsAny>(), It.IsAny()))
+ .ReturnsAsync(new List());
+
+ _mockEdgeValidator
+ .Setup(ev => ev.GetValidEdges(It.IsAny>(), It.IsAny>(), It.IsAny(), It.IsAny>()))
+ .ReturnsAsync(new List());
+
+ // Act
+ var result = await _sut.Search(searchGraphDto);
+
+ // Assert
+ Assert.NotNull(result.Data);
+ Assert.Empty(result.Data.Nodes);
+ Assert.Empty(result.Data.Edges);
+ }
+
+ [Fact]
+ public async Task Search_ShouldReturnValidGraph_WhenValidNodesAndEdgesAreFound()
+ {
+ // Arrange
+ var searchGraphDto = new SearchGraphDto
+ {
+ SourceCategoryName = "Category1",
+ TargetCategoryName = "Category2",
+ EdgeCategoryName = "EdgeCategory",
+ EdgeCategoryClauses = new Dictionary(),
+ SourceCategoryClauses = new Dictionary(),
+ TargetCategoryClauses = new Dictionary()
+ };
+
+ var sourceNodes = new List
+ {
+ new Models.Graph.Node.Node { NodeId = 1, NodeCategoryId = 1, NodeUniqueString = "Node1" }
+ };
+
+ var targetNodes = new List
+ {
+ new Models.Graph.Node.Node { NodeId = 2, NodeCategoryId = 2, NodeUniqueString = "Node2" }
+ };
+
+ var edges = new List
+ {
+ new Models.Graph.Edge.Edge { EdgeUniqueString = "Edge1" }
+ };
+
+ _mockClauseValidatorService
+ .Setup(s => s.AreClausesValid(It.IsAny(), It.IsAny>(), It.IsAny>(), It.IsAny>()))
+ .ReturnsAsync(new ActionResponse { StatusCode = StatusCodeType.Success });
+
+ _mockNodeValidator
+ .Setup(nv => nv.GetValidNodes(It.IsAny>(), It.IsAny()))
+ .ReturnsAsync(sourceNodes);
+
+ _mockEdgeValidator
+ .Setup(ev => ev.GetValidEdges(sourceNodes, targetNodes, "EdgeCategory", It.IsAny>()))
+ .ReturnsAsync(edges);
+
+ _mockGraphDtoCreator
+ .Setup(gdc => gdc.CreateResultGraphDto(It.IsAny>(), It.IsAny>()))
+ .Returns(new GraphDto
+ {
+ Nodes = new List()
+ {
+ new NodeDto()
+ {
+ id = "1", label = "Category1/Node1"
+ },
+ new NodeDto()
+ {
+ id = "2", label = "Category2/Node2"
+ }
+ },
+ Edges = new List()
+ {
+ new EdgeDto()
+ {
+ id = "1",
+ source = "1",
+ target = "2"
+ }
+ }
+ });
+
+ // Act
+ var result = await _sut.Search(searchGraphDto);
+
+ // Assert
+ Assert.Equal(StatusCodeType.Success, result.StatusCode);
+ Assert.Equal(2, result.Data.Nodes.Count);
+ Assert.Single(result.Data.Edges);
+ }
+ }
+}
diff --git a/RelationshipAnalysis.Test/Services/GraphServices/Node/NodeSearchValidatorTests.cs b/RelationshipAnalysis.Test/Services/GraphServices/Node/NodeSearchValidatorTests.cs
new file mode 100644
index 0000000..3c5c7f5
--- /dev/null
+++ b/RelationshipAnalysis.Test/Services/GraphServices/Node/NodeSearchValidatorTests.cs
@@ -0,0 +1,127 @@
+using Microsoft.EntityFrameworkCore;
+using Microsoft.Extensions.DependencyInjection;
+using RelationshipAnalysis.Context;
+using RelationshipAnalysis.Models.Graph.Node;
+using RelationshipAnalysis.Services.GraphServices.Node;
+using Xunit;
+
+namespace RelationshipAnalysis.Test.Services.GraphServices.Node
+{
+ public class NodeSearchValidatorTests
+ {
+ private readonly IServiceProvider _serviceProvider;
+ private readonly NodeSearchValidator _sut;
+
+ private readonly NodeCategory _nodeCategory1 = new() { NodeCategoryId = 1, NodeCategoryName = "Category1" };
+ private readonly NodeCategory _nodeCategory2 = new() { NodeCategoryId = 2, NodeCategoryName = "Category2" };
+
+ private readonly Models.Graph.Node.Node _node1 = new()
+ {
+ NodeCategoryId = 1,
+ NodeUniqueString = "1",
+ Values = new List
+ {
+ new() { NodeAttribute = new NodeAttribute { NodeAttributeName = "Name" }, ValueData = "Alice" },
+ new() { NodeAttribute = new NodeAttribute { NodeAttributeName = "Age" }, ValueData = "25" }
+ }
+ };
+
+ private readonly Models.Graph.Node.Node _node2 = new()
+ {
+ NodeCategoryId = 2,
+ NodeUniqueString = "2",
+ Values = new List
+ {
+ new() { NodeAttribute = new NodeAttribute { NodeAttributeName = "Name" }, ValueData = "Bob" },
+ new() { NodeAttribute = new NodeAttribute { NodeAttributeName = "Age" }, ValueData = "30" }
+ }
+ };
+
+ public NodeSearchValidatorTests()
+ {
+ var serviceCollection = new ServiceCollection();
+
+ var options = new DbContextOptionsBuilder()
+ .UseInMemoryDatabase(Guid.NewGuid().ToString())
+ .UseLazyLoadingProxies()
+ .Options;
+
+ serviceCollection.AddScoped(_ => new ApplicationDbContext(options));
+
+ _serviceProvider = serviceCollection.BuildServiceProvider();
+
+ _sut = new NodeSearchValidator(_serviceProvider);
+ }
+
+ private void SeedDatabase()
+ {
+ using var scope = _serviceProvider.CreateScope();
+ var context = scope.ServiceProvider.GetRequiredService();
+ context.NodeCategories.AddRange(_nodeCategory1, _nodeCategory2);
+ context.Nodes.AddRange(_node1, _node2);
+ context.SaveChanges();
+ }
+
+ [Fact]
+ public async Task GetValidNodes_ShouldReturnEmptyList_WhenNoNodesMatch()
+ {
+ // Arrange
+ SeedDatabase();
+ var clauses = new Dictionary { { "Name", "Charlie" } };
+
+ // Act
+ var result = await _sut.GetValidNodes(clauses, "Category1");
+
+ // Assert
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public async Task GetValidNodes_ShouldReturnNodes_WhenClauseMatches()
+ {
+ // Arrange
+ SeedDatabase();
+ var clauses = new Dictionary { { "Name", "Alice" } }; // "Alice" exists in Category1
+
+ // Act
+ var result = await _sut.GetValidNodes(clauses, "Category1");
+
+ // Assert
+ Assert.Single(result);
+ Assert.Contains(result, node => node.Values.Any(v => v.ValueData == "Alice"));
+ }
+
+ [Fact]
+ public async Task GetValidNodes_ShouldReturnEmptyList_WhenCategoryNameDoesNotMatch()
+ {
+ // Arrange
+ SeedDatabase();
+ var clauses = new Dictionary { { "Name", "Alice" } };
+
+ // Act
+ var result = await _sut.GetValidNodes(clauses, "Category2");
+
+ // Assert
+ Assert.Empty(result);
+ }
+
+ [Fact]
+ public async Task GetValidNodes_ShouldReturnCorrectNodes_WhenMultipleClausesMatch()
+ {
+ // Arrange
+ SeedDatabase();
+ var clauses = new Dictionary
+ {
+ { "Name", "Alice" },
+ { "Age", "25" }
+ };
+
+ // Act
+ var result = await _sut.GetValidNodes(clauses, "Category1");
+
+ // Assert
+ Assert.Single(result);
+ Assert.Contains(result, node => node.Values.Any(v => v.ValueData == "Alice") && node.Values.Any(v => v.ValueData == "25"));
+ }
+ }
+}
diff --git a/RelationshipAnalysis/Program.cs b/RelationshipAnalysis/Program.cs
index 67d8dda..27cd9bb 100644
--- a/RelationshipAnalysis/Program.cs
+++ b/RelationshipAnalysis/Program.cs
@@ -116,6 +116,9 @@
.AddSingleton()
.AddSingleton()
.AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
+ .AddSingleton()
.AddKeyedSingleton("node")
.AddKeyedSingleton("edge")
.AddKeyedSingleton("node")
diff --git a/RelationshipAnalysis/Resources.Designer.cs b/RelationshipAnalysis/Resources.Designer.cs
index a73de16..2038af7 100644
--- a/RelationshipAnalysis/Resources.Designer.cs
+++ b/RelationshipAnalysis/Resources.Designer.cs
@@ -14,18 +14,18 @@ namespace RelationshipAnalysis {
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class Resources {
+ public class Resources {
private static System.Resources.ResourceManager resourceMan;
private static System.Globalization.CultureInfo resourceCulture;
[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal Resources() {
+ public Resources() {
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static System.Resources.ResourceManager ResourceManager {
+ public static System.Resources.ResourceManager ResourceManager {
get {
if (object.Equals(null, resourceMan)) {
System.Resources.ResourceManager temp = new System.Resources.ResourceManager("RelationshipAnalysis.Resources", typeof(Resources).Assembly);
@@ -36,7 +36,7 @@ internal static System.Resources.ResourceManager ResourceManager {
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static System.Globalization.CultureInfo Culture {
+ public static System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
@@ -45,211 +45,211 @@ internal static System.Globalization.CultureInfo Culture {
}
}
- internal static string InvalidPasswordMessage {
+ public static string InvalidPasswordMessage {
get {
return ResourceManager.GetString("InvalidPasswordMessage", resourceCulture);
}
}
- internal static string InvalidClauseInSourceCategory {
+ public static string InvalidClauseInSourceCategory {
get {
return ResourceManager.GetString("InvalidClauseInSourceCategory", resourceCulture);
}
}
- internal static string InvalidClauseInDestinationCategory {
+ public static string InvalidClauseInDestinationCategory {
get {
return ResourceManager.GetString("InvalidClauseInDestinationCategory", resourceCulture);
}
}
- internal static string InvalidClauseInEdgeCategory {
+ public static string InvalidClauseInEdgeCategory {
get {
return ResourceManager.GetString("InvalidClauseInEdgeCategory", resourceCulture);
}
}
- internal static string SuccessfulLogoutMessage {
+ public static string SuccessfulLogoutMessage {
get {
return ResourceManager.GetString("SuccessfulLogoutMessage", resourceCulture);
}
}
- internal static string UserNotFoundMessage {
+ public static string UserNotFoundMessage {
get {
return ResourceManager.GetString("UserNotFoundMessage", resourceCulture);
}
}
- internal static string WrongOldPasswordMessage {
+ public static string WrongOldPasswordMessage {
get {
return ResourceManager.GetString("WrongOldPasswordMessage", resourceCulture);
}
}
- internal static string NoFileUploadedMessage {
+ public static string NoFileUploadedMessage {
get {
return ResourceManager.GetString("NoFileUploadedMessage", resourceCulture);
}
}
- internal static string SuccessfulNodeAdditionMessage {
+ public static string SuccessfulNodeAdditionMessage {
get {
return ResourceManager.GetString("SuccessfulNodeAdditionMessage", resourceCulture);
}
}
- internal static string TwoSameHeadersMessage {
+ public static string TwoSameHeadersMessage {
get {
return ResourceManager.GetString("TwoSameHeadersMessage", resourceCulture);
}
}
- internal static string InvalidNodeCategory {
+ public static string InvalidNodeCategory {
get {
return ResourceManager.GetString("InvalidNodeCategory", resourceCulture);
}
}
- internal static string InvalidHeaderAttribute {
+ public static string InvalidHeaderAttribute {
get {
return ResourceManager.GetString("InvalidHeaderAttribute", resourceCulture);
}
}
- internal static string FailedAddRecordsMessage {
+ public static string FailedAddRecordsMessage {
get {
return ResourceManager.GetString("FailedAddRecordsMessage", resourceCulture);
}
}
- internal static string ValidFileMessage {
+ public static string ValidFileMessage {
get {
return ResourceManager.GetString("ValidFileMessage", resourceCulture);
}
}
- internal static string OldPasswordRequired {
+ public static string OldPasswordRequired {
get {
return ResourceManager.GetString("OldPasswordRequired", resourceCulture);
}
}
- internal static string NewPasswordRequired {
+ public static string NewPasswordRequired {
get {
return ResourceManager.GetString("NewPasswordRequired", resourceCulture);
}
}
- internal static string UsernameRequired {
+ public static string UsernameRequired {
get {
return ResourceManager.GetString("UsernameRequired", resourceCulture);
}
}
- internal static string PasswordRequired {
+ public static string PasswordRequired {
get {
return ResourceManager.GetString("PasswordRequired", resourceCulture);
}
}
- internal static string SuccessfulUpdateUserMessage {
+ public static string SuccessfulUpdateUserMessage {
get {
return ResourceManager.GetString("SuccessfulUpdateUserMessage", resourceCulture);
}
}
- internal static string SuccessfulDeleteUserMessage {
+ public static string SuccessfulDeleteUserMessage {
get {
return ResourceManager.GetString("SuccessfulDeleteUserMessage", resourceCulture);
}
}
- internal static string UsernameExistsMessage {
+ public static string UsernameExistsMessage {
get {
return ResourceManager.GetString("UsernameExistsMessage", resourceCulture);
}
}
- internal static string EmailExistsMessage {
+ public static string EmailExistsMessage {
get {
return ResourceManager.GetString("EmailExistsMessage", resourceCulture);
}
}
- internal static string SucceddfulCreateUser {
+ public static string SucceddfulCreateUser {
get {
return ResourceManager.GetString("SucceddfulCreateUser", resourceCulture);
}
}
- internal static string EmptyRolesMessage {
+ public static string EmptyRolesMessage {
get {
return ResourceManager.GetString("EmptyRolesMessage", resourceCulture);
}
}
- internal static string InvalidRolesListMessage {
+ public static string InvalidRolesListMessage {
get {
return ResourceManager.GetString("InvalidRolesListMessage", resourceCulture);
}
}
- internal static string SuccessfulUpdateRolesMessage {
+ public static string SuccessfulUpdateRolesMessage {
get {
return ResourceManager.GetString("SuccessfulUpdateRolesMessage", resourceCulture);
}
}
- internal static string LoginFailedMessage {
+ public static string LoginFailedMessage {
get {
return ResourceManager.GetString("LoginFailedMessage", resourceCulture);
}
}
- internal static string SuccessfulLoginMessage {
+ public static string SuccessfulLoginMessage {
get {
return ResourceManager.GetString("SuccessfulLoginMessage", resourceCulture);
}
}
- internal static string DeleteAccountAccessErrorMessage {
+ public static string DeleteAccountAccessErrorMessage {
get {
return ResourceManager.GetString("DeleteAccountAccessErrorMessage", resourceCulture);
}
}
- internal static string NullDtoErrorMessage {
+ public static string NullDtoErrorMessage {
get {
return ResourceManager.GetString("NullDtoErrorMessage", resourceCulture);
}
}
- internal static string NotUniqueCategoryNameErrorMessage {
+ public static string NotUniqueCategoryNameErrorMessage {
get {
return ResourceManager.GetString("NotUniqueCategoryNameErrorMessage", resourceCulture);
}
}
- internal static string SuccessfulCreateCategory {
+ public static string SuccessfulCreateCategory {
get {
return ResourceManager.GetString("SuccessfulCreateCategory", resourceCulture);
}
}
- internal static string SuccessfulEdgeAdditionMessage {
+ public static string SuccessfulEdgeAdditionMessage {
get {
return ResourceManager.GetString("SuccessfulEdgeAdditionMessage", resourceCulture);
}
}
- internal static string InvalidEdgeCategory {
+ public static string InvalidEdgeCategory {
get {
return ResourceManager.GetString("InvalidEdgeCategory", resourceCulture);
}
}
- internal static string InvalidSourceNodeCategory {
+ public static string InvalidSourceNodeCategory {
get {
return ResourceManager.GetString("InvalidSourceNodeCategory", resourceCulture);
}
}
- internal static string InvalidTargetNodeCategory {
+ public static string InvalidTargetNodeCategory {
get {
return ResourceManager.GetString("InvalidTargetNodeCategory", resourceCulture);
}
diff --git a/RelationshipAnalysis/Services/GraphServices/Abstraction/IClauseValidatorService.cs b/RelationshipAnalysis/Services/GraphServices/Abstraction/IClauseValidatorService.cs
new file mode 100644
index 0000000..1f46dd2
--- /dev/null
+++ b/RelationshipAnalysis/Services/GraphServices/Abstraction/IClauseValidatorService.cs
@@ -0,0 +1,13 @@
+using RelationshipAnalysis.Dto;
+using RelationshipAnalysis.Dto.Graph;
+
+namespace RelationshipAnalysis.Services.GraphServices.Abstraction;
+
+public interface IClauseValidatorService
+{
+ Task> AreClausesValid(
+ SearchGraphDto searchGraphDto,
+ List sourceAttributes,
+ List targetAttributes,
+ List edgeAttributes);
+}
\ No newline at end of file
diff --git a/RelationshipAnalysis/Services/GraphServices/ClauseValidatorService.cs b/RelationshipAnalysis/Services/GraphServices/ClauseValidatorService.cs
new file mode 100644
index 0000000..1996173
--- /dev/null
+++ b/RelationshipAnalysis/Services/GraphServices/ClauseValidatorService.cs
@@ -0,0 +1,49 @@
+using RelationshipAnalysis.Dto;
+using RelationshipAnalysis.Dto.Graph;
+using RelationshipAnalysis.Enums;
+using RelationshipAnalysis.Services.GraphServices.Abstraction;
+
+namespace RelationshipAnalysis.Services.GraphServices
+{
+ public class ClauseValidatorService : IClauseValidatorService
+ {
+ public async Task> AreClausesValid(
+ SearchGraphDto searchGraphDto,
+ List sourceAttributes,
+ List targetAttributes,
+ List edgeAttributes)
+ {
+ if (!searchGraphDto.SourceCategoryClauses.Keys.All(item => sourceAttributes.Contains(item)))
+ return NotFoundResult(Resources.InvalidClauseInSourceCategory);
+
+ if (!searchGraphDto.TargetCategoryClauses.Keys.All(item => targetAttributes.Contains(item)))
+ return NotFoundResult(Resources.InvalidClauseInDestinationCategory);
+
+ if (!searchGraphDto.EdgeCategoryClauses.Keys.All(item => edgeAttributes.Contains(item)))
+ return NotFoundResult(Resources.InvalidClauseInDestinationCategory);
+
+ return SuccessResult();
+ }
+
+ private ActionResponse SuccessResult()
+ {
+ return new ActionResponse
+ {
+ StatusCode = StatusCodeType.Success,
+ Data = null
+ };
+ }
+
+ private ActionResponse NotFoundResult(string message)
+ {
+ return new ActionResponse
+ {
+ StatusCode = StatusCodeType.NotFound,
+ Data = new GraphDto
+ {
+ Message = message
+ }
+ };
+ }
+ }
+}
\ No newline at end of file
diff --git a/RelationshipAnalysis/Services/GraphServices/Edge/Abstraction/ISearchEdgeValidator.cs b/RelationshipAnalysis/Services/GraphServices/Edge/Abstraction/ISearchEdgeValidator.cs
new file mode 100644
index 0000000..1756d52
--- /dev/null
+++ b/RelationshipAnalysis/Services/GraphServices/Edge/Abstraction/ISearchEdgeValidator.cs
@@ -0,0 +1,8 @@
+namespace RelationshipAnalysis.Services.GraphServices.Edge.Abstraction;
+
+public interface ISearchEdgeValidator
+{
+ Task> GetValidEdges(List sourceNodes,
+ List targetNodes,
+ string edgeCategory, Dictionary clauses);
+}
\ No newline at end of file
diff --git a/RelationshipAnalysis/Services/GraphServices/Edge/EdgeSearchValidator.cs b/RelationshipAnalysis/Services/GraphServices/Edge/EdgeSearchValidator.cs
new file mode 100644
index 0000000..125f7fa
--- /dev/null
+++ b/RelationshipAnalysis/Services/GraphServices/Edge/EdgeSearchValidator.cs
@@ -0,0 +1,45 @@
+using Microsoft.EntityFrameworkCore;
+using RelationshipAnalysis.Context;
+using RelationshipAnalysis.Services.GraphServices.Abstraction;
+using RelationshipAnalysis.Services.GraphServices.Edge.Abstraction;
+
+namespace RelationshipAnalysis.Services.GraphServices.Edge;
+
+public class EdgeSearchValidator(IServiceProvider serviceProvider) : ISearchEdgeValidator
+{
+ public async Task> GetValidEdges(List sourceNodes,
+ List targetNodes,
+ string edgeCategory, Dictionary clauses)
+ {
+ var sourceNodeIds = sourceNodes.Select(n => n.NodeId).ToList();
+ var targetNodeIds = targetNodes.Select(n => n.NodeId).ToList();
+
+ using var scope = serviceProvider.CreateScope();
+ var context = scope.ServiceProvider.GetRequiredService();
+
+ var edges = await context.Edges
+ .Include(e => e.EdgeValues)
+ .ThenInclude(ev => ev.EdgeAttribute)
+ .Where(e => edgeCategory == e.EdgeCategory.EdgeCategoryName &&
+ sourceNodeIds.Contains(e.EdgeSourceNodeId) &&
+ targetNodeIds.Contains(e.EdgeDestinationNodeId))
+ .ToListAsync();
+
+ var validEdgesByClauses = edges.Where(e => AreEdgeAttributesValid(e, clauses)).ToList();
+ return validEdgesByClauses;
+ }
+
+ private bool AreEdgeAttributesValid(Models.Graph.Edge.Edge edge, Dictionary clauses)
+ {
+ var attributeValues = new Dictionary();
+ edge.EdgeValues.ToList().ForEach(ev => attributeValues.Add(ev.EdgeAttribute.EdgeAttributeName, ev.ValueData));
+
+ foreach (var kvp in clauses)
+ {
+ var actualValue = attributeValues[kvp.Key];
+ if (!actualValue.StartsWith(kvp.Value)) return false;
+ }
+
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/RelationshipAnalysis/Services/GraphServices/Graph/GraphSearcherService.cs b/RelationshipAnalysis/Services/GraphServices/Graph/GraphSearcherService.cs
index 90b36a6..95dce57 100644
--- a/RelationshipAnalysis/Services/GraphServices/Graph/GraphSearcherService.cs
+++ b/RelationshipAnalysis/Services/GraphServices/Graph/GraphSearcherService.cs
@@ -3,129 +3,39 @@
using RelationshipAnalysis.Dto.Graph;
using RelationshipAnalysis.Enums;
using RelationshipAnalysis.Services.GraphServices.Abstraction;
+using RelationshipAnalysis.Services.GraphServices.Edge.Abstraction;
using RelationshipAnalysis.Services.GraphServices.Graph.Abstraction;
using ApplicationDbContext = RelationshipAnalysis.Context.ApplicationDbContext;
-namespace RelationshipAnalysis.Services.GraphServices.Graph;
-
-public class GraphSearcherService(
- IGraphDtoCreator graphDtoCreator,
- IServiceProvider serviceProvider,
- [FromKeyedServices("node")] IAttributesReceiver nodeCategoryReceiver,
- [FromKeyedServices("edge")] IAttributesReceiver edgeCategoryReceiver) : IGraphSearcherService
+namespace RelationshipAnalysis.Services.GraphServices.Graph
{
- public async Task> Search(SearchGraphDto searchGraphDto)
+ public class GraphSearcherService(
+ IGraphDtoCreator graphDtoCreator,
+ [FromKeyedServices("node")] IAttributesReceiver nodeCategoryReceiver,
+ [FromKeyedServices("edge")] IAttributesReceiver edgeCategoryReceiver,
+ ISearchNodeValidator nodeValidator,
+ ISearchEdgeValidator edgeValidator,
+ IClauseValidatorService clauseValidatorService) : IGraphSearcherService
{
- using var scope = serviceProvider.CreateScope();
- var context = scope.ServiceProvider.GetRequiredService();
-
-
- var sourceAttributes = await nodeCategoryReceiver.GetAllAttributes(searchGraphDto.SourceCategoryName);
- var targetAttributes = await nodeCategoryReceiver.GetAllAttributes(searchGraphDto.TargetCategoryName);
- var edgeAttributes = await edgeCategoryReceiver.GetAllAttributes(searchGraphDto.EdgeCategoryName);
-
- var validation = await AreClausesValid(searchGraphDto, sourceAttributes, targetAttributes, edgeAttributes);
- if (validation.StatusCode != StatusCodeType.Success) return validation;
-
- var sourceNodes = await context.Nodes
- .Where(n => searchGraphDto.SourceCategoryName == n.NodeCategory.NodeCategoryName).ToListAsync();
- var targetNodes = await context.Nodes
- .Where(n => searchGraphDto.TargetCategoryName == n.NodeCategory.NodeCategoryName).ToListAsync();
-
- sourceNodes = sourceNodes.Where(sn => IsNodeValid(sn, searchGraphDto.SourceCategoryClauses)).ToList();
- targetNodes = targetNodes.Where(tn => IsNodeValid(tn, searchGraphDto.TargetCategoryClauses)).ToList();
-
- var edges = await GetValidEdges(sourceNodes, targetNodes, searchGraphDto.SourceCategoryName,
- searchGraphDto.TargetCategoryName, searchGraphDto.EdgeCategoryName);
-
- edges = edges.Where(e => IsEdgeValid(e, searchGraphDto.EdgeCategoryClauses)).ToList();
-
- validation.Data = graphDtoCreator.CreateResultGraphDto(sourceNodes.Union(targetNodes).ToList(), edges);
- return validation;
- }
-
- private bool IsNodeValid(Models.Graph.Node.Node node, Dictionary clauses)
- {
- var attributeValues = new Dictionary();
- node.Values.ToList().ForEach(nv => attributeValues.Add(nv.NodeAttribute.NodeAttributeName, nv.ValueData));
-
- foreach (var kvp in clauses)
+ public async Task> Search(SearchGraphDto searchGraphDto)
{
- var actualValue = attributeValues[kvp.Key];
- if (!actualValue.StartsWith(kvp.Value)) return false;
- }
- return true;
- }
+ var sourceAttributes = await nodeCategoryReceiver.GetAllAttributes(searchGraphDto.SourceCategoryName);
+ var targetAttributes = await nodeCategoryReceiver.GetAllAttributes(searchGraphDto.TargetCategoryName);
+ var edgeAttributes = await edgeCategoryReceiver.GetAllAttributes(searchGraphDto.EdgeCategoryName);
- private bool IsEdgeValid(Models.Graph.Edge.Edge edge, Dictionary clauses)
- {
- var attributeValues = new Dictionary();
- edge.EdgeValues.ToList().ForEach(ev => attributeValues.Add(ev.EdgeAttribute.EdgeAttributeName, ev.ValueData));
+ var validation = await clauseValidatorService.AreClausesValid(searchGraphDto, sourceAttributes, targetAttributes, edgeAttributes);
+ if (validation.StatusCode != StatusCodeType.Success) return validation;
- foreach (var kvp in clauses)
- {
- var actualValue = attributeValues[kvp.Key];
- if (!actualValue.StartsWith(kvp.Value)) return false;
- }
+ var sourceNodes = await nodeValidator.GetValidNodes(searchGraphDto.SourceCategoryClauses, searchGraphDto.SourceCategoryName);
+ var targetNodes = await nodeValidator.GetValidNodes(searchGraphDto.TargetCategoryClauses, searchGraphDto.TargetCategoryName);
- return true;
- }
-
- private async Task> GetValidEdges(List sourceNodes,
- List targetNodes, string sourceCategory, string targetCategory,
- string edgeCategory)
- {
- var sourceNodeIds = sourceNodes.Select(n => n.NodeId).ToList();
- var targetNodeIds = targetNodes.Select(n => n.NodeId).ToList();
-
- using var scope = serviceProvider.CreateScope();
- var context = scope.ServiceProvider.GetRequiredService();
-
- var edges = await context.Edges.Include(e => e.EdgeValues)
- .ThenInclude(ev => ev.EdgeAttribute)
- .Where(e => edgeCategory == e.EdgeCategory.EdgeCategoryName &&
- sourceNodeIds.Contains(e.EdgeSourceNodeId) &&
- targetNodeIds.Contains(e.EdgeDestinationNodeId))
- .ToListAsync();
- return edges;
- }
-
- private async Task> AreClausesValid(SearchGraphDto searchGraphDto,
- List sourceAttributes, List targetAttributes,
- List edgeAttributes)
- {
- if (!searchGraphDto.SourceCategoryClauses.Keys.All(item => sourceAttributes.Contains(item)))
- return NotFoundResult(Resources.InvalidClauseInSourceCategory);
-
- if (!searchGraphDto.TargetCategoryClauses.Keys.All(item => targetAttributes.Contains(item)))
- return NotFoundResult(Resources.InvalidClauseInDestinationCategory);
-
- if (!searchGraphDto.EdgeCategoryClauses.Keys.All(item => edgeAttributes.Contains(item)))
- return NotFoundResult(Resources.InvalidClauseInDestinationCategory);
-
- return SuccessResult();
- }
-
- private ActionResponse SuccessResult()
- {
- return new ActionResponse
- {
- StatusCode = StatusCodeType.Success,
- Data = null
- };
- }
+ var edges = await edgeValidator.GetValidEdges(sourceNodes, targetNodes, searchGraphDto.EdgeCategoryName, searchGraphDto.EdgeCategoryClauses);
+
+ validation.Data = graphDtoCreator.CreateResultGraphDto(sourceNodes.UnionBy(targetNodes, node => node.NodeId).ToList(), edges);
+ return validation;
+ }
- private ActionResponse NotFoundResult(string message)
- {
- return new ActionResponse
- {
- StatusCode = StatusCodeType.NotFound,
- Data = new GraphDto
- {
- Message = message
- }
- };
}
-}
\ No newline at end of file
+}
diff --git a/RelationshipAnalysis/Services/GraphServices/Node/Abstraction/ISearchNodeValidator.cs b/RelationshipAnalysis/Services/GraphServices/Node/Abstraction/ISearchNodeValidator.cs
new file mode 100644
index 0000000..a4cc4ce
--- /dev/null
+++ b/RelationshipAnalysis/Services/GraphServices/Node/Abstraction/ISearchNodeValidator.cs
@@ -0,0 +1,6 @@
+namespace RelationshipAnalysis.Services.GraphServices.Abstraction;
+
+public interface ISearchNodeValidator
+{
+ Task> GetValidNodes(Dictionary clauses, string categoryName);
+}
\ No newline at end of file
diff --git a/RelationshipAnalysis/Services/GraphServices/Node/NodeSearchValidator.cs b/RelationshipAnalysis/Services/GraphServices/Node/NodeSearchValidator.cs
new file mode 100644
index 0000000..4d9a419
--- /dev/null
+++ b/RelationshipAnalysis/Services/GraphServices/Node/NodeSearchValidator.cs
@@ -0,0 +1,33 @@
+using Microsoft.EntityFrameworkCore;
+using RelationshipAnalysis.Context;
+using RelationshipAnalysis.Services.GraphServices.Abstraction;
+
+namespace RelationshipAnalysis.Services.GraphServices.Node;
+
+public class NodeSearchValidator(IServiceProvider serviceProvider) : ISearchNodeValidator
+{
+ public async Task> GetValidNodes(Dictionary clauses, string categoryName)
+ {
+ using var scope = serviceProvider.CreateScope();
+ var context = scope.ServiceProvider.GetRequiredService();
+ var allNodes = await context.Nodes.ToListAsync();
+ var validNodes = allNodes.Where(sn => IsNodeValid(sn, clauses, categoryName)).ToList();
+ return validNodes;
+ }
+
+ private bool IsNodeValid(Models.Graph.Node.Node node, Dictionary clauses, string categoryName)
+ {
+ if (node.NodeCategory.NodeCategoryName != categoryName) return false;
+
+ var attributeValues = new Dictionary();
+ node.Values.ToList().ForEach(nv => attributeValues.Add(nv.NodeAttribute.NodeAttributeName, nv.ValueData));
+
+ foreach (var kvp in clauses)
+ {
+ if (!attributeValues.TryGetValue(kvp.Key, out var actualValue) || !actualValue.StartsWith(kvp.Value))
+ return false;
+ }
+
+ return true;
+ }
+}
\ No newline at end of file