Example #1
0
 /// <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
 }
Example #2
0
 /// <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,
     HqlBuilderContext builderContext,
     CompositeDefinitionProcessorContext processorContext)
 {
     _next.ProjectProperties(propertyProjections, builderContext, processorContext);
 }
        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);
            }
        }
Example #4
0
 /// <summary>
 /// Applies the provided local identifying properties to the build result using the suplied 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,
     HqlBuilderContext builderContext,
     CompositeDefinitionProcessorContext processorContext)
 {
     _next.ApplyLocalIdentifyingProperties(locallyDefinedIdentifyingProperties, builderContext, processorContext);
 }
Example #5
0
 /// <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);
 }
Example #6
0
        /// <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="buildResult">The build result.</param>
        /// <returns><b>true</b> if the result could be built; otherwise <b>false</b>.</returns>
        public bool TryBuildForRootResource(
            HqlBuilderContext builderContext,
            CompositeDefinitionProcessorContext processorContext,
            out CompositeQuery buildResult)
        {
            ApplyFilters(processorContext, builderContext);

            return(_next.TryBuildForRootResource(builderContext, processorContext, out buildResult));
        }
Example #7
0
        /// <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 CompositeQuery BuildForChildResource(
            CompositeQuery parentResult,
            HqlBuilderContext builderContext,
            CompositeDefinitionProcessorContext processorContext)
        {
            ApplyFilters(processorContext, builderContext);

            return(_next.BuildForChildResource(parentResult, builderContext, processorContext));
        }
 private void ApplyLocalIdentifyingProperties(TBuilderContext builderContext,
                                              CompositeDefinitionProcessorContext processorContext,
                                              List <EntityProperty> nonIncomingIdentifyingProperties)
 {
     // Apply local identifying properties to the artifact under construction
     _compositeBuilder.ApplyLocalIdentifyingProperties(
         nonIncomingIdentifyingProperties,
         builderContext,
         processorContext);
 }
Example #9
0
        /// <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;
        }
        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);
        }
Example #11
0
 /// <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)
     });
 }
        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());
        }
Example #15
0
 /// <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 HqlBuilderContext CreateChildContext(
     HqlBuilderContext parentingBuilderContext,
     CompositeDefinitionProcessorContext childProcessorContext)
 {
     return(_next.CreateChildContext(parentingBuilderContext, childProcessorContext));
 }
        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:

                ResourceMemberBase resourceMemberToUseForFlattening;

                if (!currentResourceClass.ReferenceByName.TryGetValue(childMemberName, out Reference reference))
                {
                    // Look for a "descriptor" property
                    if (currentResourceClass.PropertyByName.TryGetValue(
                            childMemberName,
                            out ResourceProperty descriptorProperty))
                    {
                        resourceMemberToUseForFlattening = descriptorProperty;
                        childModel            = descriptorProperty.DescriptorResource;
                        childEntityMemberName = childMemberName;
                        association           = descriptorProperty.EntityProperty.IncomingAssociations.FirstOrDefault();
                        resourceMember        = descriptorProperty;
                        resourceMemberName    = childMemberName;
                    }
                    else
                    {
                        ApplyValidationMessage("referenced resource", childMemberName, currentElementName);

                        return(null);
                    }
                }
                else
                {
                    _logger.Debug(
                        $"Current element '{currentElementName}' is a referenced resourced named '{childMemberName}'.");

                    resourceMemberToUseForFlattening = reference;
                    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(resourceMemberToUseForFlattening, 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));
        }
Example #17
0
 /// <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(
     HqlBuilderContext builderContext,
     CompositeDefinitionProcessorContext processorContext)
 {
     _next.ApplyChildResource(builderContext, processorContext);
 }
Example #18
0
 /// <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, HqlBuilderContext builderContext)
 {
     _next.ApplyRootResource(processorContext, builderContext);
 }
        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 resourceMemberToUse = 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))
                    {
                        // Look for a "descriptor" property
                        if (processorContext.CurrentResourceClass.PropertyByName.TryGetValue(
                                flattenedMemberName,
                                out ResourceProperty descriptorProperty))
                        {
                            resourceMemberToUse = descriptorProperty;

                            flattenedResource =
                                processorContext.CurrentResourceClass.ResourceModel.GetResourceByFullName(
                                    descriptorProperty.DescriptorResource.Entity.FullName);
                        }
                        else
                        {
                            ApplyValidationMessage("resource reference", flattenedMemberName, currentContainingElementName);
                            continue;
                        }
                    }
                    else
                    {
                        resourceMemberToUse = 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;
                    }

                    resourceMemberToUse = 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,
                    resourceMemberToUse);

                var shouldContinueWithValidation = resourceMemberToUse == 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(resourceMemberToUse, 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)resourceMemberToUse,
                        propertyElements,
                        currentContainingElementName);
                }

                _compositeBuilder.ProjectProperties(
                    flattenedPropertyProjections, flattenedBuilderContext, flattenedProcessingContext);

                // Recursively process flattened resource properties
                ProcessFlattenedMemberProperties(
                    flattenedProcessingContext,
                    flattenedBuilderContext);
            }
        }
Example #20
0
        /// <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.");
                }
            }
        }
Example #21
0
 /// <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)
 }
Example #22
0
        private void ApplyFilters(
            CompositeDefinitionProcessorContext processorContext,
            HqlBuilderContext builderContext)
        {
            var entityType = GetEntityType(processorContext.CurrentResourceClass);
            var filters    = builderContext.CurrentQueryFilterByName;

            // --------------------------
            //   Add security filtering
            // --------------------------
            if (filters != null && filters.Any())
            {
                foreach (var filterInfo in filters)
                {
                    // Get the filter text
                    string filterName = filterInfo.Key;
                    string filterHqlFormat;

                    if (!_nHibernateFilterTextProvider.TryGetHqlFilterText(entityType, filterName, out filterHqlFormat))
                    {
                        throw new Exception(
                                  string.Format(
                                      "Unable to apply authorization to query because filter '{0}' could not be found on entity '{1}'.",
                                      filterName,
                                      entityType.Name));
                    }

                    if (string.IsNullOrWhiteSpace(filterHqlFormat))
                    {
                        throw new Exception(
                                  string.Format(
                                      "Unable to apply authorization to query because filter '{0}' on entity '{1}' was found, but was null or empty.",
                                      filterName,
                                      entityType.Name));
                    }

                    // Set the current alias for the contextual fields
                    string filterHql = string.Format(filterHqlFormat, builderContext.CurrentAlias);

                    if (!string.IsNullOrWhiteSpace(filterHql))
                    {
                        // Add HQL to the current resource query's WHERE clause
                        builderContext.Where.AppendFormat(
                            "{0}({1})",
                            AndIfNeeded(builderContext.Where),
                            filterHql);

                        // Copy over the values of the named parameters, but only if they are actually present in the filter
                        var authorizationFilterDetails = filterInfo.Value;

                        string parameterName = authorizationFilterDetails.ClaimEndpointName;

                        if (filterHql.Contains($":{parameterName}"))
                        {
                            if (authorizationFilterDetails.ClaimValues.Length == 1)
                            {
                                builderContext.CurrentQueryFilterParameterValueByName[parameterName] =
                                    authorizationFilterDetails.ClaimValues.Single();
                            }
                            else
                            {
                                builderContext.CurrentQueryFilterParameterValueByName[parameterName] =
                                    authorizationFilterDetails.ClaimValues;
                            }
                        }
                    }
                }
            }
        }
        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);

            // 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);
        }
Example #24
0
        /// <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);
        }
Example #25
0
        /// <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, HqlBuilderContext builderContext)
        {
            var resourceClass = processorContext.CurrentResourceClass;

            if (!(resourceClass is Resource))
            {
                throw new InvalidOperationException(
                          $"Unable to evaluate resource '{resourceClass.FullName}' for inclusion in HQL query because it is not the root class of the resource.");
            }

            var resource = (Resource)resourceClass;

            // --------------------------
            //   Determine inclusion
            // --------------------------
            var entityType = GetEntityType(resource);

            var authorizationContext = new EdFiAuthorizationContext(
                ClaimsPrincipal.Current,
                _resourceClaimUriProvider.GetResourceClaimUris(resource),
                RequestActions.ReadActionUri,
                entityType);

            // Authorize and apply filtering
            IReadOnlyList <AuthorizationFilterDetails> authorizationFilters;

            try
            {
                // NOTE: Possible performance optimization - Allow for "Try" semantics (so no exceptions are thrown here)
                authorizationFilters = _authorizationProvider.GetAuthorizationFilters(authorizationContext);
            }
            catch (EdFiSecurityException ex)
            {
                // If this is the base resource, rethrow the exception to achieve a 401 response
                if (processorContext.IsBaseResource())
                {
                    Logger.Debug($"BaseResource: {processorContext.CurrentResourceClass.Name} could not be authorized.");
                    throw;
                }

                // In the case where we have an abstract class and it has no claim, eg EducationOrganization, we will allow
                // the join if the subtype has been included.
                if (processorContext.IsAbstract())
                {
                    Logger.Debug($"Resource {processorContext.CurrentResourceClass.Name} has no claim.");

                    if (processorContext.ShouldIncludeResourceSubtype())
                    {
                        Logger.Debug(
                            $"Resource is abstract and so target resource '{processorContext.CurrentResourceClass.Name}' cannot be authorized. Join will be included, but non-identifying resource members should be stripped from results.");

                        return(true);
                    }
                }

                Logger.Debug($"Resource {processorContext.CurrentResourceClass.Name} is excluded from the request.");
                Logger.Debug($"Security Exception Message: {ex.Message}.");

                return(false);
            }

            // Save the filters to be applied to this query for use later in the process
            builderContext.CurrentQueryFilterByName = authorizationFilters.ToDictionary(x => x.FilterName, x => x);

            return(true);
        }
Example #26
0
 /// <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);
 }