private void ProcessChildren(TBuildResult parentResult, CompositeDefinitionProcessorContext processorContext, TBuilderContext parentingBuilderContext) { var resourceModel = processorContext.ResourceModel; XElement currentElt = processorContext.CurrentElement; ResourceClassBase currentResourceClass = processorContext.CurrentResourceClass; // Iterate through children (Collection, EmbeddedObject, Resource or Reference) var otherChildren = currentElt.Elements(CompositeDefinitionHelper.Collection) .Concat(currentElt.Elements(CompositeDefinitionHelper.EmbeddedObject)) .Concat(currentElt.Elements(CompositeDefinitionHelper.LinkedCollection)) .Concat(currentElt.Elements(CompositeDefinitionHelper.ReferencedResource)); int childIndex = 0; foreach (var childElt in otherChildren) { var childProcessorContext = CreateChildProcessorContext( parentResult, processorContext, parentingBuilderContext, childElt, currentResourceClass, resourceModel, childIndex); if (childProcessorContext == null) { continue; } var childBuilderContext = _compositeBuilder.CreateChildContext(parentingBuilderContext, childProcessorContext); childIndex++; ProcessDefinition(parentResult, childBuilderContext, childProcessorContext); } }
/// <summary> /// Applies properties necessary to support self-referencing association behavior. /// </summary> /// <param name="selfReferencingAssociations">The relevant self-referencing associations.</param> /// <param name="builderContext">The current builder context.</param> /// <param name="processorContext">The composite definition processor context.</param> /// <remarks>The associations supplied may not be from the current resource class. In cases where the self-referencing /// behavior is obtained through a referenced resource, the associations will be from the referenced resource.</remarks> public void ApplySelfReferencingProperties( IReadOnlyList <AssociationView> selfReferencingAssociations, CompositeResourceModelBuilderContext builderContext, CompositeDefinitionProcessorContext processorContext) { // Nothing to do }
/// <summary> /// Applies the provided local identifying properties to the build result using the supplied builder context. /// </summary> /// <param name="locallyDefinedIdentifyingProperties">The list of local identifying properties to be applied to the build result.</param> /// <param name="builderContext">The builder context.</param> /// <param name="processorContext">The composite definition processor context.</param> public void ApplyLocalIdentifyingProperties( IReadOnlyList <EntityProperty> locallyDefinedIdentifyingProperties, CompositeResourceModelBuilderContext builderContext, CompositeDefinitionProcessorContext processorContext) { // Nothing to do }
/// <summary> /// Builds the artifact for the root resource of the composite definition. /// </summary> /// <param name="builderContext">The builder context.</param> /// <param name="processorContext">The composite definition processor context.</param> /// <param name="resource">The resource that is built.</param> /// <returns>The build result.</returns> public bool TryBuildForRootResource( CompositeResourceModelBuilderContext builderContext, CompositeDefinitionProcessorContext processorContext, out Resource resource) { resource = builderContext.RootResource; return(true); }
/// <summary> /// Builds the artifact for the root resource of the composite definition. /// </summary> /// <param name="parentResult">The parent build result, for compositional behavior (if applicable).</param> /// <param name="builderContext">The builder context.</param> /// <param name="processorContext">The composite definition processor context.</param> /// <returns>The build result.</returns> public Resource BuildForChildResource( Resource parentResult, CompositeResourceModelBuilderContext builderContext, CompositeDefinitionProcessorContext processorContext) { string currentElementName = processorContext.CurrentElement.Name.LocalName; switch (currentElementName) { case Collection: AddItem(builderContext.ParentResource.Collections, new Collection( builderContext.ParentResource, builderContext.CurrentResource as ResourceChildItem, processorContext.JoinAssociation, processorContext.MemberDisplayName)); break; case LinkedCollection: AddItem(builderContext.ParentResource.Collections, new Collection( builderContext.ParentResource, builderContext.CurrentResource as ResourceChildItem, processorContext.JoinAssociation, processorContext.MemberDisplayName)); break; case ReferencedResource: AddItem(builderContext.ParentResource.EmbeddedObjects, new EmbeddedObject( builderContext.ParentResource, builderContext.CurrentResource as ResourceChildItem, processorContext.MemberDisplayName)); break; case EmbeddedObject: AddItem(builderContext.ParentResource.EmbeddedObjects, new EmbeddedObject( builderContext.ParentResource, builderContext.CurrentResource as ResourceChildItem, processorContext.MemberDisplayName)); break; default: throw new NotSupportedException($"Child element type '{currentElementName}' is not yet supported."); } // Nothing to compose (results are built in-place using the builder context) return(null); }
private void ApplyLocalIdentifyingProperties(TBuilderContext builderContext, CompositeDefinitionProcessorContext processorContext, List <EntityProperty> nonIncomingIdentifyingProperties) { // Apply local identifying properties to the artifact under construction _compositeBuilder.ApplyLocalIdentifyingProperties( nonIncomingIdentifyingProperties, builderContext, processorContext); }
/// <summary> /// Applies the composite resource's root resource to the build result using the supplied builder context. /// </summary> /// <param name="processorContext"></param> /// <param name="builderContext">The builder context.</param> public void ApplyRootResource(CompositeDefinitionProcessorContext processorContext, CompositeResourceModelBuilderContext builderContext) { string compositeName = processorContext.CurrentElement.Ancestors("Composite") .First() .AttributeValue("name"); string resourceName = CompositeTermInflector.MakeSingular(compositeName); builderContext.RootResource = new Resource(resourceName); //resource.Entity.Name); builderContext.CurrentResource = builderContext.RootResource; }
/// <summary> /// Creates a new builder context to be used for processing a child element. /// </summary> /// <param name="parentingBuilderContext">The parent context to be used to derive the new child context.</param> /// <param name="childProcessorContext"></param> /// <returns>The new builder context.</returns> public CompositeResourceModelBuilderContext CreateChildContext( CompositeResourceModelBuilderContext parentingBuilderContext, CompositeDefinitionProcessorContext childProcessorContext) { return(new CompositeResourceModelBuilderContext { RootResource = parentingBuilderContext.RootResource, ParentResource = parentingBuilderContext.CurrentResource, CurrentResource = new ResourceChildItem( childProcessorContext.CurrentResourceClass.Name, parentingBuilderContext.CurrentResource) }); }
public static IReadOnlyDictionary <string, ResourceProperty> ValidPropertiesByName( CompositeDefinitionProcessorContext processorContext, IReadOnlyDictionary <string, ResourceProperty> propertiesByName) { Preconditions.ThrowIfNull(processorContext, nameof(processorContext)); Preconditions.ThrowIfNull(propertiesByName, nameof(propertiesByName)); return(processorContext.ShouldIncludeResourceSubtype() ? propertiesByName .Where(p => p.Value.IsIdentifying) .ToDictionary(k => k.Key, v => v.Value) : propertiesByName); }
private void ApplyReferenceHierarchy(TBuilderContext builderContext, CompositeDefinitionProcessorContext processorContext) { if (processorContext.ShouldUseReferenceHierarchy()) { string referenceName = processorContext.AttributeValue(CompositeDefinitionHelper.HierarchicalReferenceName); var referencedResourceClass = processorContext.CurrentResourceClass.ReferenceByName[referenceName] .ReferencedResource; _compositeBuilder.ApplySelfReferencingProperties( referencedResourceClass.Entity.SelfReferencingAssociations, builderContext, processorContext); } }
private List <CompositePropertyProjection> GetPropertyProjectionsForNonAggregateRoot( CompositeDefinitionProcessorContext processorContext, EmbeddedObject currentMember, IEnumerable <XElement> propertyElements, string containingElementName) { var selectedElements = CompositeDefinitionHelper.CreateSelectedElements(propertyElements); var validPropertiesByName = CompositeDefinitionHelper.ValidPropertiesByName(processorContext, currentMember.ObjectType.PropertyByName); var validProperties = CompositeDefinitionHelper.GetValidProperties(selectedElements, validPropertiesByName); ValidateSelectedElements(selectedElements, validProperties, containingElementName, processorContext.ShouldIncludeResourceSubtype()); return(validProperties .Select(pn => new CompositePropertyProjection(currentMember.ObjectType.AllPropertyByName[pn.Name], pn.DisplayName)) .ToList()); }
public TBuildResult Process( XElement compositeDefinition, IResourceModel resourceModel, TBuilderContext builderContext) { var currentElt = compositeDefinition.Element(CompositeDefinitionHelper.BaseResource); if (currentElt == null) { throw new Exception("Unable to find the main 'Resource' element of the composite definition."); } var resourceLogicalName = currentElt.AttributeValue(CompositeDefinitionHelper.LogicalSchema) ?? EdFiConventions.LogicalName; var resourcePhysicalName = resourceModel.SchemaNameMapProvider .GetSchemaMapByLogicalName(resourceLogicalName) .PhysicalName; // Composites does not support extensions var currentModel = resourceModel.GetResourceByFullName( new FullName(resourcePhysicalName, currentElt.AttributeValue(CompositeDefinitionHelper.Name))); var processorContext = new CompositeDefinitionProcessorContext( compositeDefinition, resourceModel, currentElt, currentModel, null, null, null, 0, null); return(ProcessDefinition(default(TBuildResult), builderContext, processorContext)); }
private List <CompositePropertyProjection> GetPropertyProjections( CompositeDefinitionProcessorContext processorContext, IEnumerable <XElement> propertyElements) { var selectedElements = CompositeDefinitionHelper.CreateSelectedElements(propertyElements); // only allow identifying properties for pass through resources. var validPropertiesByName = CompositeDefinitionHelper.ValidPropertiesByName( processorContext, processorContext.CurrentResourceClass.AllPropertyByName); var validProperties = CompositeDefinitionHelper.GetValidProperties(selectedElements, validPropertiesByName); ValidateSelectedElements( selectedElements, validProperties, processorContext.CurrentElement.AttributeValue(CompositeDefinitionHelper.Name), processorContext.ShouldIncludeResourceSubtype()); return(validProperties.Select( pn => new CompositePropertyProjection( processorContext.CurrentResourceClass.AllPropertyByName[pn.Name], pn.DisplayName)) .ToList()); }
private TBuildResult ProcessDefinition( TBuildResult parentResult, TBuilderContext builderContext, CompositeDefinitionProcessorContext processorContext) { // Apply authorization for aggregate roots here if (processorContext.CurrentResourceClass.Entity.IsAggregateRoot) { // Provide opportunity to perform processing related to navigating into another resource (i.e. authorization) if (!_compositeBuilder.TryIncludeResource(processorContext, builderContext)) { return(default(TBuildResult)); } } bool isMainQuery = processorContext.IsBaseResource(); if (isMainQuery) { _compositeBuilder.ApplyRootResource(processorContext, builderContext); } else { _compositeBuilder.ApplyChildResource(builderContext, processorContext); } var nonIncomingIdentifyingProperties = processorContext.NonIncomingIdentifyingProperties(); ApplyLocalIdentifyingProperties(builderContext, processorContext, nonIncomingIdentifyingProperties); ApplySelfReferencingAssociations(builderContext, processorContext); ApplyReferenceHierarchy(builderContext, processorContext); // Capture current applicable builder state so it can be modified further at this level without changes affecting children _compositeBuilder.SnapshotParentingContext(builderContext); // Select projected properties var propertyProjections = GetPropertyProjections(processorContext, processorContext.PropertyElements()); // Project the properties into the artifact under construction _compositeBuilder.ProjectProperties(propertyProjections, builderContext, processorContext); // Process flattened References ProcessFlattenedMemberProperties(processorContext, builderContext); TBuildResult thisBuildResult; if (isMainQuery) { // Short circuit the rest of the processing if the root result is null if (!_compositeBuilder.TryBuildForRootResource(builderContext, processorContext, out thisBuildResult)) { return(default(TBuildResult)); } } else { thisBuildResult = _compositeBuilder.BuildForChildResource( parentResult, builderContext, processorContext); } var childBuilderContext = _compositeBuilder.CreateParentingContext(builderContext); ProcessChildren(thisBuildResult, processorContext, childBuilderContext); if (_performValidation && _validationErrors.Any()) { throw new Exception(string.Join(Environment.NewLine, _validationErrors.ToArray())); } return(thisBuildResult); }
private void ProcessFlattenedMemberProperties( CompositeDefinitionProcessorContext processorContext, TBuilderContext builderContext) { string currentContainingElementName = processorContext.CurrentElement.AttributeValue(CompositeDefinitionHelper.Name); var flattenedMemberElements = processorContext.CurrentElement .Elements(CompositeDefinitionHelper.ReferencedResource) .Where(CompositeDefinitionHelper.ShouldFlatten) .ToList(); flattenedMemberElements.AddRange( processorContext.CurrentElement .Elements(CompositeDefinitionHelper.EmbeddedObject) .Where(CompositeDefinitionHelper.ShouldFlatten)); foreach (var flattenedMemberElt in flattenedMemberElements) { string flattenedMemberName = flattenedMemberElt.AttributeValue(CompositeDefinitionHelper.Name); ResourceMemberBase resourceToUse = null; Resource flattenedResource = null; bool memberIsReference = flattenedMemberElt.Name.LocalName == CompositeDefinitionHelper.ReferencedResource; bool memberIsEmbeddedObject = flattenedMemberElt.Name.LocalName == CompositeDefinitionHelper.EmbeddedObject; if (memberIsReference) { if (!processorContext.CurrentResourceClass.ReferenceByName.TryGetValue( flattenedMemberName, out Reference resourceToUseAsReference)) { ApplyValidationMessage("resource reference", flattenedMemberName, currentContainingElementName); continue; } resourceToUse = resourceToUseAsReference; flattenedResource = processorContext.CurrentResourceClass.ResourceModel.GetResourceByFullName( resourceToUseAsReference.ReferencedResource.Entity.FullName); } else if (memberIsEmbeddedObject) { if (!processorContext.CurrentResourceClass.EmbeddedObjectByName.TryGetValue( flattenedMemberName, out EmbeddedObject resourceAsEmbeddedObject)) { ApplyValidationMessage("embedded object", flattenedMemberName, currentContainingElementName); continue; } resourceToUse = resourceAsEmbeddedObject; flattenedResource = processorContext.CurrentResourceClass.ResourceModel.GetResourceByFullName(resourceAsEmbeddedObject.Parent.Entity.FullName); } else { // Defensive programming, but also throw a helpful message in the event it does ever happen throw new NotSupportedException($"Flattened elements of type '{flattenedMemberElt.Name.LocalName}' are not yet supported."); } var flattenedBuilderContext = _compositeBuilder.CreateFlattenedMemberContext(builderContext); var flattenedProcessingContext = new CompositeDefinitionProcessorContext( processorContext.CompositeDefinitionElement, processorContext.ResourceModel, flattenedMemberElt, flattenedResource, null, null, null, 0, resourceToUse); var shouldContinueWithValidation = resourceToUse == null && _performValidation; if (flattenedResource.Entity.IsAggregateRoot) { // Provide opportunity to perform processing related to navigating into another resource (i.e. authorization) if (!_compositeBuilder.TryIncludeResource(flattenedProcessingContext, flattenedBuilderContext)) { continue; } } if (shouldContinueWithValidation) { continue; } _compositeBuilder.ApplyFlattenedMember(resourceToUse, flattenedBuilderContext); // TODO: Consider refining what is passed into Resource model classes rather than XElements var propertyElements = flattenedMemberElt .Elements(CompositeDefinitionHelper.Property) .ToList(); List <CompositePropertyProjection> flattenedPropertyProjections = new List <CompositePropertyProjection>(); if (memberIsReference) { flattenedPropertyProjections = GetPropertyProjections( flattenedProcessingContext, propertyElements); } else if (memberIsEmbeddedObject) { flattenedPropertyProjections = GetPropertyProjectionsForNonAggregateRoot( processorContext, (EmbeddedObject)resourceToUse, propertyElements, currentContainingElementName); } _compositeBuilder.ProjectProperties(flattenedPropertyProjections, flattenedBuilderContext, flattenedProcessingContext); // Recursively process flattened resource properties ProcessFlattenedMemberProperties( flattenedProcessingContext, flattenedBuilderContext); } }
private CompositeDefinitionProcessorContext CreateChildProcessorContext(TBuildResult parentResult, CompositeDefinitionProcessorContext processorContext, TBuilderContext parentingBuilderContext, XElement childElement, ResourceClassBase currentResourceClass, IResourceModel resourceModel, int childIndex) { ResourceClassBase childModel; AssociationView association; ResourceMemberBase resourceMember; string childMemberName = childElement.AttributeValue(CompositeDefinitionHelper.Name); string childMemberDisplayName = childElement.AttributeValue(CompositeDefinitionHelper.DisplayName); string currentElementName = processorContext.CurrentElement.AttributeValue(CompositeDefinitionHelper.Name); string resourceMemberName = string.Empty; string childEntityMemberName = string.Empty; switch (childElement.Name.LocalName) { case CompositeDefinitionHelper.Collection: if (!currentResourceClass.CollectionByName.TryGetValue(childMemberName, out Collection collection)) { ApplyValidationMessage("collection", childMemberName, currentElementName); return(null); } _logger.Debug($"Current element '{currentElementName}' is a collection named '{childMemberName}'."); childModel = collection.ItemType; childEntityMemberName = childMemberName; association = collection.Association; resourceMember = collection; resourceMemberName = collection.PropertyName; break; case CompositeDefinitionHelper.LinkedCollection: var resource = currentResourceClass as Resource; if (resource == null) { _logger.Debug($"Current resource class '{currentResourceClass.Name} is not an aggregate root."); return(null); } if (!resource.LinkedResourceCollectionByName.TryGetValue(childMemberName, out LinkedCollection linkedCollection)) { ApplyValidationMessage("linked collection", childMemberName, currentElementName); return(null); } _logger.Debug( $"Current element '{currentElementName} is a linked collection named '{childMemberName}' for resource '{currentResourceClass.Name}'."); childModel = linkedCollection.Resource; childEntityMemberName = childMemberName; association = linkedCollection.Association; resourceMember = linkedCollection; resourceMemberName = linkedCollection.PropertyName; break; case CompositeDefinitionHelper.EmbeddedObject: if (!currentResourceClass.EmbeddedObjectByName.TryGetValue(childMemberName, out EmbeddedObject embeddedObject)) { ApplyValidationMessage("embedded object", childMemberName, currentElementName); return(null); } _logger.Debug($"Current element '{currentElementName}' is an embedded object named '{childMemberName}'."); childModel = embeddedObject.ObjectType; childEntityMemberName = embeddedObject.Association.Name; association = embeddedObject.Association; resourceMember = embeddedObject; resourceMemberName = embeddedObject.PropertyName; if (CompositeDefinitionHelper.ShouldFlatten(childElement)) { _logger.Debug($"Flattening embedded object {childModel.Name}"); _compositeBuilder.ApplyFlattenedMember(embeddedObject, parentingBuilderContext); var childBuilderContextForObject = _compositeBuilder.CreateFlattenedReferenceChildContext(parentingBuilderContext); var childProcessorContextForObject = new CompositeDefinitionProcessorContext( processorContext.CompositeDefinitionElement, resourceModel, childElement, childModel, association, childEntityMemberName, resourceMemberName, childIndex, resourceMember); ProcessChildren(parentResult, childProcessorContextForObject, childBuilderContextForObject); return(null); } break; case CompositeDefinitionHelper.ReferencedResource: if (!currentResourceClass.ReferenceByName.TryGetValue(childMemberName, out Reference reference)) { ApplyValidationMessage("referenced resource", childMemberName, currentElementName); return(null); } _logger.Debug($"Current element '{currentElementName}' is an referenced resourced named '{childMemberName}'."); childModel = reference.ReferencedResource; childEntityMemberName = reference.Association.Name; association = reference.Association; resourceMember = reference; resourceMemberName = reference.PropertyName; // If reference is flattened, process recursively without adding another query at this level. if (CompositeDefinitionHelper.ShouldFlatten(childElement)) { _logger.Debug($"Flattening referenced resource {childModel.Name}"); _compositeBuilder.ApplyFlattenedMember(reference, parentingBuilderContext); var childBuilderContextForReference = _compositeBuilder.CreateFlattenedReferenceChildContext(parentingBuilderContext); var childProcessorContextForReference = new CompositeDefinitionProcessorContext( processorContext.CompositeDefinitionElement, resourceModel, childElement, childModel, association, childEntityMemberName, resourceMemberName, childIndex, resourceMember); if (childModel.Entity.IsAggregateRoot) { // Provide opportunity to perform processing related to navigating into another resource (i.e. authorization) if (!_compositeBuilder.TryIncludeResource(childProcessorContextForReference, childBuilderContextForReference)) { return(null); } } ProcessChildren(parentResult, childProcessorContextForReference, childBuilderContextForReference); return(null); } break; default: throw new NotSupportedException($"Element '{childElement.Name.LocalName}' is not supported."); } return(new CompositeDefinitionProcessorContext( processorContext.CompositeDefinitionElement, resourceModel, childElement, childModel, association, childEntityMemberName, childMemberDisplayName ?? resourceMemberName, childIndex, resourceMember)); }
/// <summary> /// Applies processing related to the usage/entry to another top-level resource (e.g. applying authorization concerns). /// </summary> /// <param name="processorContext">The composite definition processor context.</param> /// <param name="builderContext">The current builder context.</param> /// <returns><b>true</b> if the resource can be processed; otherwise <b>false</b>.</returns> public bool TryIncludeResource(CompositeDefinitionProcessorContext processorContext, CompositeResourceModelBuilderContext builderContext) { // No reason not to include the resource in this usage scenario return(true); }
/// <summary> /// Applies the composite resource's child resource to the build result using the supplied builder context. /// </summary> /// <param name="builderContext">The builder context.</param> /// <param name="processorContext">The composite definition processor context.</param> public void ApplyChildResource( CompositeResourceModelBuilderContext builderContext, CompositeDefinitionProcessorContext processorContext) { // Nothing to apply (we only need to actually build the model) }
private void ApplySelfReferencingAssociations(TBuilderContext builderContext, CompositeDefinitionProcessorContext processorContext) { if (processorContext.CurrentResourceClass.Entity.HasSelfReferencingAssociations && processorContext.ShouldUseHierarchy()) { _compositeBuilder.ApplySelfReferencingProperties( processorContext.CurrentResourceClass.Entity.SelfReferencingAssociations, builderContext, processorContext); } }
/// <summary> /// Apply the provided property projections onto the build result with the provided builder and composite /// definition processor contexts. /// </summary> /// <param name="propertyProjections">A list of property projections to be applied to the build result.</param> /// <param name="builderContext">The builder context.</param> /// <param name="processorContext">The composite definition processor context.</param> public void ProjectProperties( IReadOnlyList <CompositePropertyProjection> propertyProjections, CompositeResourceModelBuilderContext builderContext, CompositeDefinitionProcessorContext processorContext) { // Local reused functional predicate var isIdProperty = new Func <CompositePropertyProjection, bool>( p => p.ResourceProperty.PropertyName.EqualsIgnoreCase("Id")); // Ensure Id property appears first var orderedPropertyProjections = propertyProjections.Where(isIdProperty) .Concat(propertyProjections.Where(p => !isIdProperty(p))); if (processorContext.ShouldIncludeResourceSubtype()) { Logger.Debug($"Adding the resource type the property collection"); AddItem( builderContext.CurrentResource.Properties, new ResourceProperty( builderContext.CurrentResource, processorContext.CurrentResourceClass.Name.ToCamelCase() + "Type", new PropertyType(DbType.AnsiString, 128, 0, 0, true), new PropertyCharacteristics( false, false, false, true, // All projected properties are "local" properties on the new object false, null), null)); } foreach (var property in orderedPropertyProjections) { Logger.Debug($"Projecting property {property.ResourceProperty.PropertyName}."); var entityProperty = property.ResourceProperty.EntityProperty; if (!builderContext.CurrentResource.IsAbstract() || builderContext.CurrentResource.IsAbstract() && entityProperty.IsIdentifying && processorContext.ShouldIncludeResourceSubtype()) { Logger.Debug($"Adding property {property.ResourceProperty.PropertyName} to the builder context."); AddItem( builderContext.CurrentResource.Properties, new ResourceProperty( builderContext.CurrentResource, property.DisplayName ?? property.ResourceProperty.PropertyName, property.ResourceProperty.PropertyType, new PropertyCharacteristics( entityProperty.IsLookup, entityProperty.IsDirectLookup, entityProperty.IsIdentifying, true, // All projected properties are "local" properties on the new object entityProperty.IsServerAssigned, entityProperty.LookupEntity == null ? null as FullName? : entityProperty.LookupEntity.FullName), property.ResourceProperty.Description)); } else { Logger.Debug($"Stripping property {property.ResourceProperty.PropertyName} from the builder context."); } } }