예제 #1
0
        /// <summary>
        /// Applies the provided flattened resource reference 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)
        {
            // Add selects for local PK fields, (Note: May be able to optimize this by only doing this if it has children in composite definition.)
            locallyDefinedIdentifyingProperties.ForEach(
                pk =>
            {
                builderContext.Select.AppendFormat(
                    "{0}{1}.{2} as PK{3}{4}_{2}",
                    CommaIfNeeded(builderContext.Select),
                    builderContext.CurrentAlias,
                    pk.PropertyName,
                    builderContext.Depth,
                    (char)(processorContext.ChildIndex + 'a'));
            });

            // Add ORDER BY for the primary keys
            locallyDefinedIdentifyingProperties.ForEach(
                pk =>
                builderContext.OrderBy.AppendFormat(
                    "{0}{1}.{2}",
                    CommaIfNeeded(builderContext.OrderBy),
                    builderContext.CurrentAlias,
                    pk.PropertyName));
        }
 /// <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);
 }
 /// <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);
 }
 /// <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,
     HqlBuilderContext builderContext,
     CompositeDefinitionProcessorContext processorContext)
 {
     _next.ApplySelfReferencingProperties(selfReferencingAssociations, builderContext, processorContext);
 }
예제 #5
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)
        {
            string hql =
                $"select {Environment.NewLine}\t" + builderContext.Select
                + $"{Environment.NewLine}from " + builderContext.From
                + (builderContext.Where.Length > 0
                                    ? $"{Environment.NewLine}where " + builderContext.Where
                                    : string.Empty)
                + $"{Environment.NewLine}order by " + builderContext.OrderBy;

            if (_logger.IsDebugEnabled)
            {
                object correlationId;

                if (builderContext.QueryStringParameters.TryGetValue(
                        SpecialQueryStringParameters.CorrelationId, out correlationId))
                {
                    _logger.DebugFormat("HQL[{0}]:{1}{2}", correlationId, Environment.NewLine, hql);
                }
                else
                {
                    _logger.DebugFormat("HQL:{0}{1}", Environment.NewLine, hql);
                }
            }

            var session = _sessionFactory.GetCurrentSession();
            var query   = session.CreateQuery(hql);

            object idValues;

            if (builderContext.ParameterValueByName.TryGetValue(BaseEntityIdName, out idValues))
            {
                _parameterListSetter.SetParameterList(query, "BaseEntityId", idValues as IEnumerable);
            }

            // Apply current query's filter parameters.
            SetQueryParameters(query, builderContext.CurrentQueryFilterParameterValueByName);

            bool isSingleItemResult =
                processorContext.IsReferenceResource() ||
                processorContext.IsEmbeddedObject();

            var thisQuery = new CompositeQuery(
                parentResult,
                processorContext.MemberDisplayName.ToCamelCase(),
                builderContext.PropertyProjections.Select(x => x.DisplayName.ToCamelCase() ?? x.ResourceProperty.PropertyName.ToCamelCase())
                .ToArray(),
                query
                .SetResultTransformer(Transformers.AliasToEntityMap)
                .Future <object>(),
                isSingleItemResult);

            parentResult.ChildQueries.Add(thisQuery);

            return(thisQuery);
        }
        /// <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));
        }
        /// <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));
        }
예제 #8
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(new HqlBuilderContext(
                new StringBuilder(parentingBuilderContext.Select.ToString()),
                new StringBuilder(parentingBuilderContext.From.ToString()),
                new StringBuilder(parentingBuilderContext.Where.ToString()),
                new StringBuilder(parentingBuilderContext.OrderBy.ToString()),
                parentingBuilderContext.ParameterValueByName,
                parentingBuilderContext.ParentAlias,
                parentingBuilderContext.Depth + 1,
                parentingBuilderContext.FilterCriteria,
                parentingBuilderContext.QueryStringParameters,
                parentingBuilderContext.QueryRangeParameters,
                parentingBuilderContext.AliasGenerator));
 }
예제 #9
0
            /// <summary>
            /// Executes the code to be exercised for the scenario.
            /// </summary>
            protected override void Act()
            {
                Resource resource = CreateStudentResource();

                // Create the builder context
                var hqlBuilderContext = new HqlBuilderContext(
                    new StringBuilder(), new StringBuilder(), new StringBuilder(), new StringBuilder());

                // Create the processor context
                var processorContext = new CompositeDefinitionProcessorContext(
                    null,
                    null,
                    new XElement("BaseResource"),
                    resource,
                    null,
                    null,
                    null,
                    int.MinValue,
                    null);

                SystemUnderTest.TryIncludeResource(processorContext, hqlBuilderContext);
            }
예제 #10
0
        public void ApplyChildResource(
            HqlBuilderContext builderContext,
            CompositeDefinitionProcessorContext processorContext)
        {
            builderContext.CurrentAlias = builderContext.AliasGenerator.GetNextAlias();

            builderContext.From.AppendFormat(
                "{0}\tjoin {1}.{2} {3}",
                Environment.NewLine,
                builderContext.ParentAlias,
                processorContext.EntityMemberName,
                builderContext.CurrentAlias);

            var collection = processorContext.CurrentResourceMember as Collection;

            if (collection != null && collection.ValueFilters.Length > 0)
            {
                StringBuilder filterWhere = new StringBuilder();

                foreach (var valueFilter in collection.ValueFilters)
                {
                    // Get the actual filter to which the property applies
                    var filterProperty = collection.ItemType.AllPropertyByName[valueFilter.PropertyName];

                    string parameterName =
                        builderContext.CurrentAlias
                        + "_" + valueFilter.PropertyName
                        + "_" + (valueFilter.FilterMode == ItemFilterMode.ExcludeOnly
                            ? "0"
                            : "1");

                    // Set the filter values
                    object parametersAsObject;

                    // Is this a first time parameter value assignment?
                    if (!builderContext.CurrentQueryFilterParameterValueByName.TryGetValue(parameterName, out parametersAsObject))
                    {
                        // Process filters into the query
                        filterWhere.AppendFormat(
                            "{0}{1}.{2}Id {3} (:{4})",
                            OrIfNeeded(filterWhere),
                            builderContext.CurrentAlias,
                            valueFilter.PropertyName,
                            valueFilter.FilterMode == ItemFilterMode.ExcludeOnly
                                ? "NOT IN"
                                : "IN",
                            parameterName);

                        // Set the parameter values
                        builderContext.CurrentQueryFilterParameterValueByName[parameterName]
                            = valueFilter.Values
                              .Select(x => _descriptorsCache.GetId(filterProperty.LookupTypeName, x))
                              .ToArray();
                    }
                    else
                    {
                        // Concatenate the current filter's values to the existing parameter list
                        builderContext.CurrentQueryFilterParameterValueByName[parameterName]
                            = (parametersAsObject as int[])
                              .Concat(
                                  valueFilter.Values
                                  .Select(x => _descriptorsCache.GetId(filterProperty.LookupTypeName, x))
                                  )
                              .ToArray();
                    }
                }

                // Apply all the filters using an AND clause
                builderContext.Where.AppendFormat(
                    "{0}({1})",
                    AndIfNeeded(builderContext.Where),
                    filterWhere);
            }
        }
            /// <summary>
            /// Prepares the state of the scenario (creating stubs, test data, etc.).
            /// </summary>
            protected override void Arrange()
            {
                Given <IAuthorizationFilteringProvider>(
                    new FakeAuthorizationFilteringProvider(
                        SuppliedFilterName,
                        SuppliedParameterName,
                        SuppliedParameterValue));

                var suppliedFilterText = $"TheField = :{SuppliedParameterName}";

                A.CallTo(() =>
                         Given <IEducationOrganizationIdNamesProvider>()
                         .GetAllNames())
                .Returns(Array.Empty <string>());

                AuthorizationFilterDefinition ignored;

                A.CallTo(
                    () => Given <IAuthorizationFilterDefinitionProvider>()
                    .TryGetAuthorizationFilterDefinition(SuppliedFilterName, out ignored))
                .Returns(true)
                .AssignsOutAndRefParameters(
                    new AuthorizationFilterDefinition(
                        SuppliedFilterName,
                        // This is how the HQL filter text is now obtained (with elimination of the INHibernateFilterTextProvider)
                        suppliedFilterText,
                        (criteria, junction, arg3, arg4) => { },
                        (ctx1, ctx2) => null,
                        (t, props) => false));

                Supplied("ResourceUriValue", "uri://some-value");

                A.CallTo(() =>
                         Given <IResourceClaimUriProvider>()
                         .GetResourceClaimUris(A <Resource> .Ignored))
                .Returns(new[] { Supplied <string>("ResourceUriValue") });

                var claimsIdentityProvider = new ClaimsIdentityProvider(
                    new ApiKeyContextProvider(new CallContextStorage()),
                    new StubSecurityRepository());

                var apiClientDetails = new ApiClientDetails
                {
                    ApiKey = Guid.NewGuid()
                             .ToString("n"),
                    ApplicationId     = 999,
                    ClaimSetName      = "SomeClaimSet",
                    NamespacePrefixes = new List <string> {
                        "Namespace"
                    },
                    EducationOrganizationIds = new[] { 123, 234 },
                    OwnershipTokenIds        = new List <short?> {
                        1
                    }
                };

                var claimsIdentity = claimsIdentityProvider.GetClaimsIdentity(
                    apiClientDetails.EducationOrganizationIds,
                    apiClientDetails.ClaimSetName,
                    apiClientDetails.NamespacePrefixes,
                    apiClientDetails.Profiles.ToList(),
                    apiClientDetails.OwnershipTokenIds.ToList());

                _expectedClaimsPrincipal = new ClaimsPrincipal(claimsIdentity);

                ClaimsPrincipal.ClaimsPrincipalSelector = () => _expectedClaimsPrincipal;

                Resource resource = CreateStudentResource();

                // Create the builder context
                _hqlBuilderContext = new HqlBuilderContext(
                    new StringBuilder(),
                    new StringBuilder(),
                    new StringBuilder(),
                    new StringBuilder(),
                    new Dictionary <string, object>(),
                    null,
                    0,
                    new Dictionary <string, CompositeSpecificationParameter>(),
                    new Dictionary <string, object>(StringComparer.InvariantCultureIgnoreCase),
                    new Dictionary <string, string>(StringComparer.InvariantCultureIgnoreCase),
                    new AliasGenerator());

                // Create the processor context
                _processorContext = new CompositeDefinitionProcessorContext(
                    null,
                    null,
                    new XElement("BaseResource"),
                    resource,
                    null,
                    null,
                    null,
                    int.MinValue,
                    null);
            }
예제 #12
0
            /// <summary>
            /// Prepares the state of the scenario (creating stubs, test data, etc.).
            /// </summary>
            protected override void Arrange()
            {
                Given <IEdFiAuthorizationProvider>(
                    new FakeAuthorizationProvider(
                        SuppliedFilterName,
                        SuppliedParameterName,
                        SuppliedParameterValue));

                var suppliedFilterText = $"TheField = :{SuppliedParameterName}";

                A.CallTo(() =>
                         Given <INHibernateFilterTextProvider>()
                         .TryGetHqlFilterText(
                             A <Type> .Ignored,
                             A <string> .Ignored,
                             out suppliedFilterText))
                .Returns(true);

                Supplied("ResourceUriValue", "uri://some-value");

                A.CallTo(() =>
                         Given <IResourceClaimUriProvider>()
                         .GetResourceClaimUris(A <Resource> .Ignored))
                .Returns(new[] { Supplied <string>("ResourceUriValue") });

                var claimsIdentityProvider = new ClaimsIdentityProvider(
                    new ApiKeyContextProvider(new CallContextStorage()),
                    new StubSecurityRepository());

                var apiClientDetails = new ApiClientDetails
                {
                    ApiKey = Guid.NewGuid()
                             .ToString("n"),
                    ApplicationId     = 999,
                    ClaimSetName      = "SomeClaimSet",
                    NamespacePrefixes = new List <string> {
                        "Namespace"
                    },
                    EducationOrganizationIds = new List <int>
                    {
                        123,
                        234
                    }
                };

                var claimsIdentity = claimsIdentityProvider.GetClaimsIdentity(
                    apiClientDetails.EducationOrganizationIds,
                    apiClientDetails.ClaimSetName,
                    apiClientDetails.NamespacePrefixes,
                    apiClientDetails.Profiles.ToList());

                _expectedClaimsPrincipal = new ClaimsPrincipal(claimsIdentity);

                ClaimsPrincipal.ClaimsPrincipalSelector = () => _expectedClaimsPrincipal;

                Resource resource = CreateStudentResource();

                // Create the builder context
                _hqlBuilderContext = new HqlBuilderContext(
                    new StringBuilder(),
                    new StringBuilder(),
                    new StringBuilder(),
                    new StringBuilder(),
                    new Dictionary <string, object>(),
                    null,
                    0,
                    new Dictionary <string, CompositeSpecificationParameter>(),
                    new Dictionary <string, object>(StringComparer.InvariantCultureIgnoreCase),
                    new Dictionary <string, string>(StringComparer.InvariantCultureIgnoreCase),
                    new AliasGenerator());

                // Create the processor context
                _processorContext = new CompositeDefinitionProcessorContext(
                    null,
                    null,
                    new XElement("BaseResource"),
                    resource,
                    null,
                    null,
                    null,
                    int.MinValue,
                    null);
            }
        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;
                            }
                        }
                    }
                }
            }
        }
        /// <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);
        }
예제 #15
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)
 {
     return(true);
 }
 /// <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));
 }
 /// <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);
 }
예제 #18
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)
        {
            if (builderContext.PropertyProjections == null)
            {
                builderContext.PropertyProjections = new List <CompositePropertyProjection>();
            }

            // in the case where we have an abstract reference, we add the discriminator to the query
            if (processorContext.ShouldIncludeResourceSubtype())
            {
                string discriminatorDisplayName = processorContext.CurrentResourceClass.Name.ToCamelCase() + "Type";

                builderContext.Select.AppendFormat(
                    "{0}{1}.{2} as {3}__PassThrough",
                    CommaIfNeeded(builderContext.Select),
                    builderContext.CurrentAlias,
                    "Discriminator",
                    discriminatorDisplayName);
            }

            propertyProjections
            .ForEach(
                p =>
            {
                builderContext.PropertyProjections.Add(p);

                if (p.ResourceProperty.EntityProperty.IsLookup)
                {
                    string lookupAlias = builderContext.AliasGenerator.GetNextAlias();

                    builderContext.Select.AppendFormat(
                        "{0}{1}.Namespace as {2}__Namespace",
                        CommaIfNeeded(builderContext.Select),
                        lookupAlias,
                        p.DisplayName.ToCamelCase() ?? p.ResourceProperty.PropertyName.ToCamelCase());

                    builderContext.Select.AppendFormat(
                        "{0}{1}.{2} as {3}",
                        CommaIfNeeded(builderContext.Select),
                        lookupAlias,
                        "CodeValue",
                        p.DisplayName.ToCamelCase() ?? p.ResourceProperty.PropertyName.ToCamelCase());

                    builderContext.From.AppendFormat(
                        "{0}\t\tleft join {1}.{2} {3} ",
                        Environment.NewLine,
                        builderContext.CurrentAlias,
                        p.ResourceProperty.PropertyName,
                        lookupAlias);
                }
                else
                {
                    builderContext.Select.AppendFormat(
                        "{0}{1}.{2} as {3}",
                        CommaIfNeeded(builderContext.Select),
                        builderContext.CurrentAlias,
                        p.ResourceProperty.EntityProperty.PropertyName,
                        p.DisplayName.ToCamelCase() ?? p.ResourceProperty.EntityProperty.PropertyName.ToCamelCase());
                }
            });
        }
 /// <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);
 }
예제 #20
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="rootCompositeQuery">The root composite query, if any records could be found.</param>
        /// <returns><b>true</b> if the root/base query returned records; otherwise <b>false</b>.</returns>
        public bool TryBuildForRootResource(
            HqlBuilderContext builderContext,
            CompositeDefinitionProcessorContext processorContext,
            out CompositeQuery rootCompositeQuery)
        {
            rootCompositeQuery = null;

            // If this is the main query, execute the query and get the Ids and use as criteria for child queries.
            string hql =
                "select " + (builderContext.NeedDistinct
                              ? "distinct "
                              : string.Empty)
                + $"{Environment.NewLine}\t" + builderContext.Select
                + $"{Environment.NewLine}from " + builderContext.From + builderContext.SpecificationFrom
                + (builderContext.SpecificationWhere.Length > 0 || builderContext.Where.Length > 0
                              ? $"{Environment.NewLine}where " + builderContext.SpecificationWhere
                   + ConnectingAndIfNeeded(builderContext.SpecificationWhere, builderContext.Where) +
                   builderContext.Where
                              : string.Empty)
                + (builderContext.OrderBy.Length > 0
                              ? $"{Environment.NewLine}order by " + builderContext.OrderBy
                              : string.Empty);

            if (_logger.IsDebugEnabled)
            {
                object correlationId;

                if (builderContext.QueryStringParameters.TryGetValue(
                        SpecialQueryStringParameters.CorrelationId, out correlationId))
                {
                    _logger.DebugFormat("HQL[{0}]:{1}{2}", correlationId, Environment.NewLine, hql);
                }
                else
                {
                    _logger.DebugFormat("HQL:{0}{1}", Environment.NewLine, hql);
                }
            }

            var session = _sessionFactory.GetCurrentSession();
            var query   = session.CreateQuery(hql);

            object offsetParameterObject = 0;
            object limitParameterObject  = 0;

            int limit  = 0;
            int offset = 0;

            if (builderContext.QueryStringParameters.TryGetValue("Limit", out limitParameterObject))
            {
                if (!int.TryParse(limitParameterObject.ToString(), out limit))
                {
                    throw new BadRequestException("Invalid limit specified.");
                }
            }

            if (builderContext.QueryStringParameters.TryGetValue("Offset", out offsetParameterObject))
            {
                if (!int.TryParse(offsetParameterObject.ToString(), out offset))
                {
                    throw new BadRequestException("Invalid offset specified.");
                }
            }

            query.SetFirstResult(offset);

            query.SetMaxResults(
                limit == 0
                    ? 25
                    : limit);

            SetQueryParameters(query, builderContext.ParameterValueByName);
            SetQueryParameters(query, builderContext.CurrentQueryFilterParameterValueByName);

            // Append the where clause for Id selection
            // Add the selection of the main query Id
            if (processorContext.CurrentResourceClass.IdentifyingProperties.Count > 1)
            {
                builderContext.ParentingContext.Where.AppendFormat(
                    "{0}{1}.Id IN (:BaseEntityId)",
                    AndIfNeeded(builderContext.ParentingContext.Where),
                    builderContext.CurrentAlias);
            }
            else
            {
                builderContext.ParentingContext.Where.AppendFormat(
                    "{0}{1}.{2} IN (:BaseEntityId)",
                    AndIfNeeded(builderContext.ParentingContext.Where),
                    builderContext.CurrentAlias,
                    processorContext.CurrentResourceClass.Entity.Identifier.Properties.Single()
                    .PropertyName);
            }

            // This is the main/base query, so execute the query and get the Ids and use as criteria for child queries.

            IList <object> queryResults = null;

            try
            {
                queryResults = query
                               .SetResultTransformer(Transformers.AliasToEntityMap)
                               .List <object>();
            }
            catch (GenericADOException ex)
            {
                _logger.Error("Query execution failed (likely due to invalid parameter values). ", ex);
                throw new ArgumentException("Query execution failed (likely due to invalid parameter values).");
            }

            // Get the Ids and assign to the parameters
            var mainQueryIds = queryResults.Cast <Hashtable>()
                               .Select(ht => ht[BaseEntityIdName])
                               .ToList();

            if (!mainQueryIds.Any())
            {
                return(false);
            }

            builderContext.ParameterValueByName[BaseEntityIdName] = mainQueryIds;

            var thisQuery = new CompositeQuery(
                processorContext.MemberDisplayName,
                builderContext.PropertyProjections.Select(
                    x => x.DisplayName.ToCamelCase() ?? x.ResourceProperty.PropertyName.ToCamelCase())
                .ToArray(),
                queryResults,
                builderContext.IsSingleItemResult);

            rootCompositeQuery = thisQuery;
            return(true);
        }
예제 #21
0
        private void ProcessQueryStringParameters(HqlBuilderContext builderContext, CompositeDefinitionProcessorContext processorContext)
        {
            // Get all non "special" query string parameter for property value equality processing
            var queryStringParameters = GetCriteriaQueryStringParameters(builderContext);

            foreach (var queryStringParameter in queryStringParameters)
            {
                ResourceProperty targetProperty;

                // TODO: Embedded convention. Types and descriptors at the top level
                if (processorContext.CurrentResourceClass.AllPropertyByName.TryGetValue(queryStringParameter.Key, out targetProperty))
                {
                    string criteriaPropertyName;
                    object parameterValue;
                    string personType;

                    // Handle Lookup conversions
                    if (targetProperty.IsLookup)
                    {
                        var id = _descriptorsCache.GetId(
                            targetProperty.LookupTypeName,
                            Convert.ToString(queryStringParameter.Value));

                        criteriaPropertyName = targetProperty.EntityProperty.PropertyName;
                        parameterValue       = id;
                    }

                    // Handle UniqueId conversions
                    else if (UniqueIdSpecification.TryGetUniqueIdPersonType(targetProperty.PropertyName, out personType))
                    {
                        int usi = _personUniqueIdToUsiCache.GetUsi(personType, Convert.ToString(queryStringParameter.Value));

                        // TODO: Embedded convention - Convert UniqueId to USI from Resource model to query Entity model on Person entities
                        // The resource model maps uniqueIds to uniqueIds on the main entity(Student,Staff,Parent)
                        if (PersonEntitySpecification.IsPersonEntity(targetProperty.ParentFullName.Name))
                        {
                            criteriaPropertyName = targetProperty.EntityProperty.PropertyName.Replace("UniqueId", "USI");
                        }
                        else
                        {
                            criteriaPropertyName = targetProperty.EntityProperty.PropertyName;
                        }

                        parameterValue = usi;
                    }
                    else
                    {
                        criteriaPropertyName = targetProperty.PropertyName;
                        parameterValue       = ConvertParameterValueForProperty(targetProperty, Convert.ToString(queryStringParameter.Value));
                    }

                    // Add criteria to the query
                    builderContext.SpecificationWhere.AppendFormat(
                        "{0}{1}.{2} = :{2}",
                        AndIfNeeded(builderContext.SpecificationWhere),
                        builderContext.CurrentAlias,
                        criteriaPropertyName);

                    if (builderContext.CurrentQueryFilterParameterValueByName.ContainsKey(criteriaPropertyName))
                    {
                        throw new ArgumentException(
                                  string.Format(
                                      "The value for parameter '{0}' was already assigned and cannot be reassigned using the query string.",
                                      criteriaPropertyName));
                    }

                    builderContext.CurrentQueryFilterParameterValueByName[criteriaPropertyName] =
                        parameterValue;
                }
                else
                {
                    ThrowPropertyNotFoundException(queryStringParameter.Key);
                }
            }
        }
예제 #22
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)
        {
            var resource = (Resource)processorContext.CurrentResourceClass;

            builderContext.CurrentAlias = builderContext.AliasGenerator.GetNextAlias();

            builderContext.SpecificationFrom  = new StringBuilder();
            builderContext.SpecificationWhere = new StringBuilder();

            // Fully qualified entity name is required to perform hql queries for an entity
            // This requirement was added in phase 3 when multiple entity extensions were added to the same entity
            var properCaseName =
                resource.Entity.DomainModel.SchemaNameMapProvider.GetSchemaMapByPhysicalName(
                    resource.Entity.Schema)
                .ProperCaseName;

            // Root level queries start with the "Q" version of the model
            var aggregateNamespace = Namespaces.Entities.NHibernate.QueryModels
                                     .GetAggregateNamespace(resource.Entity.Name, properCaseName);

            builderContext.From
            .AppendFormat(
                "{0}\t{1}Q {2}",
                Environment.NewLine,
                $@"{aggregateNamespace}.{resource.Entity.Name}",
                builderContext.CurrentAlias);

            // Add the selection of the main query Id
            if (resource.IdentifyingProperties.Count > 1)
            {
                builderContext.Select.AppendFormat("{0}.Id As {1}", builderContext.CurrentAlias, BaseEntityIdName);
            }
            else
            {
                builderContext.Select.AppendFormat(
                    "{0}.{1} As {2}",
                    builderContext.CurrentAlias,
                    resource.Entity.Identifier.Properties.Single(),
                    BaseEntityIdName);
            }

            if (builderContext.FilterCriteria.Count > 0)
            {
                // Process specification parameters
                foreach (var kvp in builderContext.FilterCriteria)
                {
                    string key            = kvp.Key;
                    object value          = kvp.Value.Value;
                    string filterJoinPath = kvp.Value.FilterPath;

                    string thisFilterJoinAlias = builderContext.CurrentAlias;

                    string parentFilterJoinAlias = thisFilterJoinAlias;

                    // Assumption: "id" in the filter criteria represents a GetById pattern in the route.
                    if (key.EqualsIgnoreCase("id"))
                    {
                        builderContext.IsSingleItemResult = true;
                    }

                    _resourceJoinPathExpressionProcessor.ProcessPath(
                        resource,
                        kvp.Key,
                        filterJoinPath,
                        (prop, pathPart) =>
                    {
                        parentFilterJoinAlias = thisFilterJoinAlias;

                        // Add property to where clause
                        string parameterName = key.Replace(".", "_");

                        builderContext.SpecificationWhere.AppendFormat(
                            "{0}{1}.{2} = :{3}",
                            AndIfNeeded(builderContext.SpecificationWhere),
                            parentFilterJoinAlias,
                            pathPart,
                            parameterName);

                        builderContext.ParameterValueByName.Add(parameterName, value);
                    },
                        (reference, pathPart) =>
                    {
                        parentFilterJoinAlias = thisFilterJoinAlias;

                        // Add a join
                        thisFilterJoinAlias = builderContext.AliasGenerator.GetNextAlias();

                        builderContext.SpecificationFrom.AppendFormat(
                            "{0}\tjoin {1}.{2} {3}",
                            Environment.NewLine,
                            parentFilterJoinAlias,
                            reference.Association.Name,
                            thisFilterJoinAlias);
                    },
                        (collection, pathPart) =>
                    {
                        parentFilterJoinAlias       = thisFilterJoinAlias;
                        builderContext.NeedDistinct = true;

                        // Add a join
                        thisFilterJoinAlias = builderContext.AliasGenerator.GetNextAlias();

                        builderContext.SpecificationFrom.AppendFormat(
                            "{0}\tjoin {1}.{2} {3}",
                            Environment.NewLine,
                            parentFilterJoinAlias,
                            collection.PropertyName,
                            thisFilterJoinAlias);
                    },
                        (linkedCollection, pathPart) =>
                    {
                        parentFilterJoinAlias       = thisFilterJoinAlias;
                        builderContext.NeedDistinct = true;

                        // Add a join
                        thisFilterJoinAlias = builderContext.AliasGenerator.GetNextAlias();

                        builderContext.SpecificationFrom.AppendFormat(
                            "{0}\tjoin {1}.{2} {3}",
                            Environment.NewLine,
                            parentFilterJoinAlias,
                            linkedCollection.PropertyName,
                            thisFilterJoinAlias);
                    },
                        (embeddedObject, pathPart) =>
                    {
                        parentFilterJoinAlias = thisFilterJoinAlias;

                        // Add a join
                        thisFilterJoinAlias = builderContext.AliasGenerator.GetNextAlias();

                        builderContext.SpecificationFrom.AppendFormat(
                            "{0}\tjoin {1}.{2} {3}",
                            Environment.NewLine,
                            parentFilterJoinAlias,
                            embeddedObject.PropertyName,
                            thisFilterJoinAlias);
                    });
                }
            }

            if (builderContext.QueryStringParameters.Any())
            {
                object queryExpressionObject;

                if (builderContext.QueryStringParameters.TryGetValue(SpecialQueryStringParameters.Q, out queryExpressionObject))
                {
                    ProcessQueryExpressions(builderContext, processorContext, queryExpressionObject.ToString());
                }

                ProcessQueryStringParameters(builderContext, processorContext);

                // Perform root level processing related to GetById and GetByKey request patterns
                builderContext.IsSingleItemResult =
                    processorContext.CurrentResourceClass.IsSingleItemRequest(GetCriteriaQueryStringParameters(builderContext));
            }
        }
예제 #23
0
        private static void ProcessQueryExpressions(
            HqlBuilderContext builderContext,
            CompositeDefinitionProcessorContext processorContext,
            string queryExpressionText)
        {
            var queryExpressions = queryExpressionText.Split(',');

            int n = 0;

            foreach (var queryExpression in queryExpressions)
            {
                var rangeQueryMatch = _rangeRegex.Match(queryExpression);

                if (!rangeQueryMatch.Success)
                {
                    throw new BadRequestException(
                              "The query filter expression was invalid. Currently, only numeric and date range expressions are supported.");
                }

                string targetPropertyName = rangeQueryMatch.Groups["PropertyName"]
                                            .Value;

                ResourceProperty targetProperty;

                if (!processorContext.CurrentResourceClass.AllPropertyByName.TryGetValue(targetPropertyName, out targetProperty))
                {
                    ThrowPropertyNotFoundException(targetPropertyName);
                }

                string rangeBeginParameterName = "Range" + n + "Begin";
                string rangeEndParameterName   = "Range" + n + "End";

                // Add the value to the parameter value collection
                builderContext.CurrentQueryFilterParameterValueByName.Add(
                    rangeBeginParameterName,
                    ConvertParameterValueForProperty(
                        targetProperty,
                        rangeQueryMatch.Groups["BeginValue"]
                        .Value));

                builderContext.CurrentQueryFilterParameterValueByName.Add(
                    rangeEndParameterName,
                    ConvertParameterValueForProperty(
                        targetProperty,
                        rangeQueryMatch.Groups["EndValue"]
                        .Value));

                // Add the query criteria to the HQL query
                builderContext.SpecificationWhere.AppendFormat(
                    "{0}{1}.{2} {3} :{4} and {1}.{2} {5} :{6}",
                    AndIfNeeded(builderContext.SpecificationWhere),
                    builderContext.CurrentAlias,
                    targetProperty.PropertyName,
                    RangeOperatorBySymbol[rangeQueryMatch.Groups["BeginRangeSymbol"]
                                          .Value],
                    rangeBeginParameterName,
                    RangeOperatorBySymbol[rangeQueryMatch.Groups["EndRangeSymbol"]
                                          .Value],
                    rangeEndParameterName);

                n++;
            }
        }