/// <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); }
/// <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)); }
/// <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)); }
/// <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); }
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); }
/// <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); }
/// <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); }
/// <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); }
/// <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); }
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); } } }
/// <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)); } }
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++; } }