From 3f5ddbb0b4a7b8c4650c17d6e9dc8f838875a993 Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Fri, 19 Jul 2024 11:45:43 -0500 Subject: [PATCH] Use plural resource name, more tests --- .../ApiSchema/DependencyCalculatorTests.cs | 138 +++++++++++++++++- .../ApiSchema/DependencyCalculator.cs | 43 ++++-- 2 files changed, 163 insertions(+), 18 deletions(-) diff --git a/src/core/EdFi.DataManagementService.Core.Tests.Unit/ApiSchema/DependencyCalculatorTests.cs b/src/core/EdFi.DataManagementService.Core.Tests.Unit/ApiSchema/DependencyCalculatorTests.cs index 75853c917..6523fa35c 100644 --- a/src/core/EdFi.DataManagementService.Core.Tests.Unit/ApiSchema/DependencyCalculatorTests.cs +++ b/src/core/EdFi.DataManagementService.Core.Tests.Unit/ApiSchema/DependencyCalculatorTests.cs @@ -26,10 +26,14 @@ public class Given_A_Sample_ApiSchema() : DependencyCalculatorTests }, "projectSchemas": { "ed-fi": { + "resourceNameMapping": { + "AbsenceEventCategory": "absenceEventCategoryDescriptors" + }, "resourceSchemas": { "absenceEventCategoryDescriptors": { "documentPathsMapping": { }, + "isSchoolYearEnumeration": false, "resourceName": "AbsenceEventCategoryDescriptor" } } @@ -42,7 +46,7 @@ public class Given_A_Sample_ApiSchema() : DependencyCalculatorTests """ [ { - "resource": "/ed-fi/absenceEventCategoryDescriptor", + "resource": "/ed-fi/absenceEventCategoryDescriptors", "order": 1, "operations": [ "Create", @@ -84,11 +88,17 @@ public class Given_A_Sample_ApiSchema_With_Subclass_Resources() : DependencyCalc }, "projectSchemas": { "ed-fi": { + "resourceNameMapping": { + "EducationOrganizationCategory": "educationOrganizationCategoryDescriptors", + "LocalEducationAgency": "localEducationAgencies", + "School": "schools" + }, "resourceSchemas": { "educationOrganizationCategoryDescriptors": { "documentPathsMapping": { }, - "isDescriptor": true, + "isDescriptor": true, + "isSchoolYearEnumeration": false, "isSubclass": false, "resourceName": "EducationOrganizationCategoryDescriptor" }, @@ -106,7 +116,8 @@ public class Given_A_Sample_ApiSchema_With_Subclass_Resources() : DependencyCalc "resourceName": "LocalEducationAgency" } }, - "isSubclass": true, + "isSubclass": true, + "isSchoolYearEnumeration": false, "resourceName": "LocalEducationAgency", "subclassType": "domainEntity", "superclassProjectName": "Ed-Fi", @@ -131,6 +142,7 @@ public class Given_A_Sample_ApiSchema_With_Subclass_Resources() : DependencyCalc } }, "isSubclass": true, + "isSchoolYearEnumeration": false, "resourceName": "School", "subclassType": "domainEntity", "superclassProjectName": "Ed-Fi", @@ -146,7 +158,7 @@ public class Given_A_Sample_ApiSchema_With_Subclass_Resources() : DependencyCalc """ [ { - "resource": "/ed-fi/educationOrganizationCategoryDescriptor", + "resource": "/ed-fi/educationOrganizationCategoryDescriptors", "order": 1, "operations": [ "Create", @@ -154,7 +166,7 @@ public class Given_A_Sample_ApiSchema_With_Subclass_Resources() : DependencyCalc ] }, { - "resource": "/ed-fi/localEducationAgency", + "resource": "/ed-fi/localEducationAgencies", "order": 2, "operations": [ "Create", @@ -162,7 +174,7 @@ public class Given_A_Sample_ApiSchema_With_Subclass_Resources() : DependencyCalc ] }, { - "resource": "/ed-fi/school", + "resource": "/ed-fi/schools", "order": 3, "operations": [ "Create", @@ -192,6 +204,120 @@ public void It_should_calculate_dependencies() } } + + [TestFixture] + public class Given_A_Sample_ApiSchema_With_Superclass_Reference() : DependencyCalculatorTests + { + private readonly string _sampleSchema = + """ + { + "projectNameMapping": { + "Ed-Fi": "ed-fi" + }, + "projectSchemas": { + "ed-fi": { + "resourceNameMapping": { + "EducationOrganizationCategory": "educationOrganizationCategoryDescriptors", + "LocalEducationAgency": "localEducationAgencies", + "OpenStaffPosition": "openStaffPositions", + "School": "schools" + }, + "resourceSchemas": { + "educationOrganizationCategoryDescriptors": { + "documentPathsMapping": { + }, + "isDescriptor": true, + "isSchoolYearEnumeration": false, + "isSubclass": false, + "resourceName": "EducationOrganizationCategoryDescriptor" + }, + "openStaffPositions": { + "allowIdentityUpdates": false, + "documentPathsMapping": { + "EducationOrganization": { + "isDescriptor": false, + "isReference": true, + "projectName": "Ed-Fi", + "resourceName": "EducationOrganization" + } + }, + "isSubclass": false, + "isSchoolYearEnumeration": false, + "resourceName": "OpenStaffPosition" + }, + "schools": { + "documentPathsMapping": { + "EducationOrganizationCategoryDescriptor": { + "isDescriptor": true, + "isReference": true, + "projectName": "Ed-Fi", + "resourceName": "EducationOrganizationCategoryDescriptor" + }, + "LocalEducationAgency": { + "isReference": true, + "projectName": "Ed-Fi", + "resourceName": "LocalEducationAgency" + } + }, + "isSubclass": true, + "isSchoolYearEnumeration": false, + "resourceName": "School" + } + } + } + } + } + """; + + private readonly string _expectedDescriptor = + """ + [ + { + "resource": "/ed-fi/educationOrganizationCategoryDescriptors", + "order": 1, + "operations": [ + "Create", + "Update" + ] + }, + { + "resource": "/ed-fi/schools", + "order": 2, + "operations": [ + "Create", + "Update" + ] + }, + { + "resource": "/ed-fi/openStaffPositions", + "order": 3, + "operations": [ + "Create", + "Update" + ] + } + ] + """; + + [SetUp] + public void Setup() + { + var logger = NullLogger.Instance; + _dependencyCalculator = new DependencyCalculator(JsonNode.Parse(_sampleSchema)!, logger); + } + + [Test] + public void It_should_calculate_dependencies() + { + var dependencies = _dependencyCalculator!.GetDependencies(); + dependencies.Should().NotBeEmpty(); + + var expectedDependencies = JsonNode.Parse(_expectedDescriptor)!.AsArray(); + dependencies!.Should().BeEquivalentTo(expectedDependencies!, options => options + .WithoutStrictOrdering() + .IgnoringCyclicReferences()); + } + } [TestFixture] public class Given_A_Sample_ApiSchema_Missing_ProjectSchemas() : DependencyCalculatorTests { diff --git a/src/core/EdFi.DataManagementService.Core/ApiSchema/DependencyCalculator.cs b/src/core/EdFi.DataManagementService.Core/ApiSchema/DependencyCalculator.cs index 7dd119b71..a210586ca 100644 --- a/src/core/EdFi.DataManagementService.Core/ApiSchema/DependencyCalculator.cs +++ b/src/core/EdFi.DataManagementService.Core/ApiSchema/DependencyCalculator.cs @@ -19,26 +19,44 @@ public JsonArray GetDependencies() var resourceSchemas = projectSchemaNode["resourceSchemas"]?.AsObject().Select(x => new ResourceSchema(x.Value!)).ToList()!; Dictionary> dependencies = - resourceSchemas.ToDictionary(rs => rs.ResourceName.Value, rs => new List()); + resourceSchemas + .Where(rs => !rs.IsSchoolYearEnumeration) + .ToDictionary(rs => rs.ResourceName.Value, rs => rs.DocumentPaths.Where(d => d.IsReference).Select(d => d.ResourceName.Value).ToList()); - foreach (var resourceSchema in resourceSchemas) - { - foreach (var documentPath in resourceSchema.DocumentPaths.Where(d => d.IsReference)) - { - dependencies[resourceSchema.ResourceName.Value].Add(documentPath.ResourceName.Value); - } - } - - List> orderedResources = []; + Dictionary orderedResources = dependencies.ToDictionary(d => d.Key, _ => 0); Dictionary visitedResources = []; foreach (var dependency in dependencies.OrderBy(d => d.Value.Count).ThenBy(d => d.Key).Select(d => d.Key)) { RecursivelyDetermineDependencies(dependency, 0); } + string ResourceNameMapping(string resourceName) + { + var resourceNameNode = projectSchemaNode["resourceNameMapping"]; + if (resourceNameNode == null) + { + throw new InvalidOperationException("ResourceNameMapping missing"); + } + + if (resourceName.EndsWith("Descriptor")) + { + resourceName = resourceName.Replace("Descriptor", string.Empty); + } + + var resourceNode = resourceNameNode[resourceName]; + if (resourceNode == null) + { + throw new InvalidOperationException($"No resource name mapping for {resourceName}"); + } + + return resourceNode.GetValue(); + } + foreach (var orderedResource in orderedResources.OrderBy(o => o.Value).ThenBy(o => o.Key)) { - dependenciesJsonArray.Add(new { resource = $"/{projectSchemaNode!.GetPropertyName()}/{orderedResource.Key.First().ToString().ToLowerInvariant()}{orderedResource.Key.Substring(1)}", order = orderedResource.Value, operations = new[] { "Create", "Update" } }); + string resourceName = ResourceNameMapping(orderedResource.Key); + + dependenciesJsonArray.Add(new { resource = $"/{projectSchemaNode!.GetPropertyName()}/{resourceName}", order = orderedResource.Value, operations = new[] { "Create", "Update" } }); } int RecursivelyDetermineDependencies(string resourceName, int depth) @@ -80,7 +98,8 @@ int RecursivelyDetermineDependencies(string resourceName, int depth) maxDepth = level; } } - orderedResources.Add(new KeyValuePair(resourceName, maxDepth + 1)); + + orderedResources[resourceName] = maxDepth + 1; visitedResources[resourceName] = maxDepth + 1; }