/// <inheritdoc />
        public void Build(MiddlewareBuilderContext context)
        {
            var sources = context.ServiceProvider
                          .GetServices <IMessagePopulationSource>()
                          .OrderBy(s => s.Priority)
                          .ToList();

            var allOwned       = new List <OwnedPropertyDescriptor>();
            var ownedBySources = new Dictionary <IMessagePopulationSource, IReadOnlyCollection <OwnedPropertyDescriptor> >();

            foreach (var s in sources)
            {
                var ownedBySource = s.GetOwnedProperties(context.Model, context.Descriptor).ToList();

                ownedBySources[s] = ownedBySource;

                foreach (var p in ownedBySource)
                {
                    if (allOwned.Contains(p))
                    {
                        throw new InvalidOperationException(
                                  $"Property {p.Property.DeclaringType.Name}.{p.Property.Name} has been marked as owned by multiple sources. A property can only be owned by a single source.");
                    }

                    allOwned.Add(p);
                }
            }

            foreach (var s in sources)
            {
                s.Build(allOwned, ownedBySources[s], context);
            }
        }
 /// <inheritdoc />
 public void Build(MiddlewareBuilderContext context)
 {
     // Force the creation of the stopwatch variable to be at the very start of the method, ensures it
     // starts before _anything_ happens and makes it available to the finally block too
     context.ExecuteMethod.Frames.Insert(0, new LoggingStartFrame());
     context.RegisterFinallyFrames(new LoggingEndFrame(context.Descriptor.OperationType));
 }
Example #3
0
        /// <inheritdoc />
        public void Build(MiddlewareBuilderContext context)
        {
            var getClaimsIdentityProvider = context.TryGetVariableFromContainer <IClaimsIdentityProvider>();

            // A claims identity provider may not be registered, we can skip generation altogether
            if (getClaimsIdentityProvider == null)
            {
                return;
            }

            /* Generates:
             * if (context.ClaimsIdentity == null)
             * {
             *   var claimsIdentity = GetInstance<IClaimsIdentityProvider>.Get(context);
             *   context.ClaimsIdentity = claimsIdentity;
             * }
             */

            var claimsIdentityVariable = context.FindVariable <ClaimsIdentity>();
            var getClaimsIdentity      = MethodCall.For <IClaimsIdentityProvider>(p => p.Get(null));

            context.AppendFrames(
                new IfNullBlock(claimsIdentityVariable)
            {
                getClaimsIdentityProvider,
                getClaimsIdentity,
                new VariableSetterFrame(claimsIdentityVariable, getClaimsIdentity.ReturnVariable),
            });
        }
Example #4
0
        private static void BuildForSingleHandler(MiddlewareBuilderContext context)
        {
            // We only have a single handler, return the result of that
            var allHandlers = context.Descriptor.Handlers;
            var handler     = allHandlers.Single();

            var apmFrame = ActivityFrame.Start(ActivityKind.Internal, handler.HandlerType.NameInCode());

            context.AppendFrames(apmFrame);

            var returnVariable = handler.Build(context, ExecutorReturnType.Return);

            if (returnVariable == null && context.Descriptor.RequiresReturnValue)
            {
                throw new InvalidOperationException(
                          $@"Unable to build an executor for the operation {context.Descriptor} because the single handler registered, {handler}, did not return a variable but the operation has {nameof(context.Descriptor.RequiresReturnValue)} set to true. 

This can happen if an the only registered handler for an operation is one that is NOT of the same type (for example a handler IApiOperationHandler<ConcreteClass> for the operation IOperationInterface) where it cannot be guaranteed that the handler will be executed.");
            }

            if (returnVariable != null)
            {
                returnVariable.OverrideName("handlerResult");

                context.AppendFrames(new OperationResultCastFrame(returnVariable));
            }

            context.AppendFrames(apmFrame.Complete());
        }
        /// <inheritdoc />
        public Variable Build(MiddlewareBuilderContext context, ExecutorReturnType executorReturnType)
        {
            // We rely on the compiler infrastructure to make the correct calls, to the correct type (i.e. the actual
            // operation), and to fill in the parameters of that method as required.
            var handlerInvokeCall = new MethodCall(context.Descriptor.OperationType, this._method)
            {
                IgnoreReturnVariable = executorReturnType == ExecutorReturnType.NoReturn,
            };

            // Note that although we know the handler type at compile time, we still specify it as a
            // parameter to logging so that it is output as a structured value (as it changes between
            // invocations)
            context.AppendFrames(
                LogFrame.Debug(
                    _apiOperationExecutorLogEvent,
                    "Executing API operation {OperationType} with inline handler",
                    ReflectionUtilities.PrettyTypeName(context.Descriptor.OperationType)),
                handlerInvokeCall);

            // We have a void, or a Task (i.e. async with no return) so we will convert to a 'NoResult'
            if (handlerInvokeCall.ReturnVariable == null || handlerInvokeCall.ReturnVariable.VariableType == typeof(Task))
            {
                var emptyResultCreation = new VariableCreationFrame(
                    typeof(NoResultOperationResult),
                    $"{typeof(NoResultOperationResult).FullNameInCode()}.{nameof(NoResultOperationResult.Instance)};");

                context.AppendFrames(emptyResultCreation);

                return(emptyResultCreation.CreatedVariable);
            }

            return(handlerInvokeCall.ReturnVariable);
        }
        public void Build(MiddlewareBuilderContext context)
        {
            var getResourceEventRepository = context.VariableFromContainer <IResourceEventRepository>();

            context.AppendFrames(
                getResourceEventRepository,
                new MethodCall(typeof(ResourceEventHandler), nameof(ResourceEventHandler.HandleAsync)));
        }
        public void Build(MiddlewareBuilderContext context)
        {
            var getGenerators = context.VariableFromContainer <IEnumerable <IResourceLinkGenerator> >();

            context.AppendFrames(
                getGenerators,
                new MethodCall(typeof(LinkGeneratorHandler), nameof(LinkGeneratorHandler.AddLinksAsync)));
        }
Example #8
0
 /// <inheritdoc />
 public void Build(MiddlewareBuilderContext context)
 {
     if (context.Descriptor.RequiresReturnValue)
     {
         context.ExecuteMethod.Frames.Add(new ReturnFrame(typeof(OperationResult)));
     }
     else
     {
         context.ExecuteMethod.Frames.Add(new ReturnFrame(Variable.StaticFrom <NoResultOperationResult>(nameof(NoResultOperationResult.Instance))));
     }
 }
Example #9
0
 /// <inheritdoc />
 public void Build(MiddlewareBuilderContext context)
 {
     if (context.Descriptor.Handlers.Count > 1)
     {
         BuildForMultipleHandlers(context);
     }
     else
     {
         BuildForSingleHandler(context);
     }
 }
        /// <inheritdoc />
        public void Build(MiddlewareBuilderContext context)
        {
            var apmFrame = ApmSpanFrame.Start(SpanKinds.Internal, "Handler", "exec");

            context.AppendFrames(apmFrame);

            if (context.Descriptor.Handlers.Count > 1)
            {
                if (context.Descriptor.RequiresReturnValue)
                {
                    throw new InvalidOperationException(
                              $@"Unable to build an executor for the operation {context.Descriptor} because multiple handlers have been registered but the operation has {nameof(context.Descriptor.RequiresReturnValue)} set to true. 

When {nameof(context.Descriptor.RequiresReturnValue)} is true multiple handlers cannot be used as there is not one, obvious, return value that could be used.

Handlers found:
  - {string.Join("\n  - ", context.Descriptor.Handlers)}
");
                }

                foreach (var handler in context.Descriptor.Handlers)
                {
                    handler.Build(context, ExecutorReturnType.NoReturn);
                }
            }
            else
            {
                // We only have a single handler, return the result of that
                var allHandlers = context.Descriptor.Handlers;
                var handler     = allHandlers.Single();

                var returnVariable = handler.Build(context, ExecutorReturnType.Return);

                if (returnVariable == null && context.Descriptor.RequiresReturnValue)
                {
                    throw new InvalidOperationException(
                              $@"Unable to build an executor for the operation {context.Descriptor} because the single handler registered, {handler}, did not return a variable but the operation has {nameof(context.Descriptor.RequiresReturnValue)} set to true. 

This can happen if an the only registered handler for an operation is one that is NOT of the same type (for example a handler IApiOperationHandler<ConcreteClass> for the operation IOperationInterface) where it cannot be guaranteed that the handler will be executed.");
                }

                if (returnVariable != null)
                {
                    returnVariable.OverrideName("handlerResult");

                    context.AppendFrames(new OperationResultCastFrame(returnVariable));
                }
            }

            context.AppendFrames(apmFrame.Complete());
        }
Example #11
0
        /// <inheritdoc />
        public Variable Build(MiddlewareBuilderContext context, ExecutorReturnType executorReturnType)
        {
            var getInstanceFrame = context.VariableFromContainer(this._iocServiceType);

            // We must look for the _exact_ method call that corresponds to the operation type as
            // we support handlers that implement multiple IApiOperationHandler<T> interfaces
            var handlerInvokeCall = new MethodCall(
                this._iocServiceType,
                this._iocServiceType.GetMethods().First(m => m.Name == nameof(IApiOperationHandler <object> .Handle)))
            {
                IgnoreReturnVariable = executorReturnType == ExecutorReturnType.NoReturn,
            };

            var invocationFrames = new Frame[]
            {
                getInstanceFrame,
                LogFrame.Debug(
                    _apiOperationExecutorLogEvent,
                    "Executing API operation {OperationType} with handler {HandlerType}",
                    ReflectionUtilities.PrettyTypeName(context.Descriptor.OperationType),
                    new Variable(typeof(string), $"{getInstanceFrame.InstanceVariable}.GetType().Name")),
                handlerInvokeCall,
            };

            // If it is not directly assignable then we need to do a runtime check and cast. This handles the case
            // of a concrete handler for an interface operation (i.e. operation is IGenericMessage and the handler
            // is IApiOperationHandler<ConcreteMessage> where ConcreteMessage : IGenericMessage)
            if (this._handledOperationType.IsAssignableFrom(this._operationType) == false)
            {
                var operationVariable = context.FindVariable(this._operationType);

                handlerInvokeCall.TrySetArgument(new CastVariable(
                                                     operationVariable,
                                                     this._handledOperationType));

                context.AppendFrames(
                    new IfBlock($"{operationVariable} is {this._handledOperationType.FullNameInCode()}", invocationFrames));

                return(null);
            }

            // We are explicit about setting the operation argument as it may be that the operation type is not
            // exactly the same (inheritance) and would therefore not be found by the variable system
            handlerInvokeCall.Arguments[0] = context.FindVariable(this._operationType);

            context.AppendFrames(invocationFrames);

            return(handlerInvokeCall.ReturnVariable);
        }
Example #12
0
        public void Build(MiddlewareBuilderContext context)
        {
            context.AppendFrames(new MethodCall(this.GetType(), nameof(WriteSuccess)));

            context.RegisterUnhandledExceptionHandler(typeof(Exception), (e) =>
            {
                var methodCall = new MethodCall(this.GetType(), nameof(WriteFailure));
                methodCall.TrySetArgument(e);

                return(new[]
                {
                    methodCall,
                });
            });
        }
        public void Build(MiddlewareBuilderContext context)
        {
            var checkFrames = new List <Frame>();

            // Generates:
            //
            // if (!context.SkipAuthorisation)
            // {
            //     if (context.UserAuthorisationContext == null)
            //     {
            //         throw new SecurityException("Access denied. Anonymous access is not allowed.");
            //     }
            //
            //     foreach (authorisers) { await a.EnforceAsync(); }
            // }

            var authorisationContextVariable = context.FindVariable <IUserAuthorisationContext>();

            checkFrames.Add(
                new IfBlock($"{authorisationContextVariable} == null")
            {
                new ThrowExceptionFrame <SecurityException>(AccessDeniedExceptionMessage),
            });

            foreach (var checker in context.ServiceProvider.GetServices <IApiAuthoriser>())
            {
                if (checker.AppliesTo(context.Descriptor))
                {
                    var getInstanceVariable = context.VariableFromContainer(checker.GetType());
                    var methodCall          = new MethodCall(typeof(AuthorisationMiddlewareBuilder), nameof(EnforceAsync));

                    // HACK: We cannot set just by variable type as compiler fails with index out of range (believe this
                    // is because the declared type is IApiAuthoriser but variable is subtype)
                    methodCall.TrySetArgument("authoriser", getInstanceVariable.InstanceVariable);

                    checkFrames.Add(getInstanceVariable);
                    checkFrames.Add(methodCall);
                }
            }

            // We only run authorisation checks if SkipAuthorisation is false, which it will be by default
            context.AppendFrames(
                new IfBlock(
                    $"{context.FindVariable<ApiOperationContext>()}.{nameof(ApiOperationContext.SkipAuthorisation)} == false",
                    checkFrames.ToArray()));

            context.RegisterFinallyFrames(new SetApmUserDetailsFrame());
        }
        public void Build(MiddlewareBuilderContext context)
        {
            if (context.Descriptor.AnonymousAccessAllowed)
            {
                // TODO: Should we be loading user auth context even if anon access allowed as some may still use if available?

                // Generates
                //
                // context.UserAuthorisationContext = AnonymousUserAuthorisationContext.Instance;

                // When we allow anonymous just inject the AnonymousUserAuthorisationContext at all times (is this right?)

                context.AppendFrames(
                    new VariableSetterFrame(
                        context.FindVariable <IUserAuthorisationContext>(),
                        Variable.StaticFrom <AnonymousUserAuthorisationContext>(nameof(AnonymousUserAuthorisationContext.Instance))));
            }
            else
            {
                // Generates:
                //
                // if (context.ClaimsIdentity != null && context.ClaimsIdentity.IsAuthenticated == true)
                // {
                //     var userSecurityContext = await this.userSecurityContextFactory.CreateContextAsync(context.ClaimsIdentity);
                //     context.UserAuthorisationContext = userSecurityContext;
                // }
                var claimsIdentityVariable = context.FindVariable <ClaimsIdentity>();

                var userSecurityContextFactoryCreator = context.VariableFromContainer <IUserAuthorisationContextFactory>();
                var createContextCall = userSecurityContextFactoryCreator.Method(f => f.CreateContextAsync(null));
                createContextCall.TrySetArgument(claimsIdentityVariable);

                context.AppendFrames(
                    new IfBlock($"{claimsIdentityVariable} != null && {claimsIdentityVariable.GetProperty(nameof(ClaimsIdentity.IsAuthenticated))} == true")
                {
                    userSecurityContextFactoryCreator,
                    createContextCall,
                    new VariableSetterFrame(context.FindVariable <IUserAuthorisationContext>(), createContextCall.ReturnVariable),
                });
            }
        }
Example #15
0
        private static void BuildForMultipleHandlers(MiddlewareBuilderContext context)
        {
            if (context.Descriptor.RequiresReturnValue)
            {
                throw new InvalidOperationException(
                          $@"Unable to build an executor for the operation {context.Descriptor} because multiple handlers have been registered but the operation has {nameof(context.Descriptor.RequiresReturnValue)} set to true. 

When {nameof(context.Descriptor.RequiresReturnValue)} is true multiple handlers cannot be used as there is not one, obvious, return value that could be used.

Handlers found:
  - {string.Join("\n  - ", context.Descriptor.Handlers)}
");
            }

            foreach (var handler in context.Descriptor.Handlers)
            {
                var apmFrame = ActivityFrame.Start(ActivityKind.Internal, handler.HandlerType.NameInCode());
                context.AppendFrames(apmFrame);

                handler.Build(context, ExecutorReturnType.NoReturn);

                context.AppendFrames(apmFrame.Complete());
            }
        }
 public void Build(MiddlewareBuilderContext context)
 {
     context.RegisterUnhandledExceptionHandler(exceptionType, create);
 }
Example #17
0
        /// <inheritdoc />
        public void Build(
            IReadOnlyCollection <OwnedPropertyDescriptor> ownedProperties,
            IReadOnlyCollection <OwnedPropertyDescriptor> ownedBySource,
            MiddlewareBuilderContext context)
        {
            // We can bail early on any code generation as we know that all properties are fulfilled by
            // other sources
            if (ownedProperties.Count == context.Descriptor.Properties.Length && ownedBySource.Count == 0)
            {
                return;
            }

            if (context.Descriptor.TryGetFeatureData <HttpOperationFeatureData>(out var httpData) == false)
            {
                return;
            }

            if (ownedBySource.Count > 1)
            {
                throw new InvalidOperationException(
                          $"Operation {context.Descriptor.OperationType} declares multiple properties with the {nameof(FromBodyAttribute)} which is not allowed. At most one property can be decorated with that attribute.");
            }

            // If the request method is a GET then there will be no body, and therefore we do not need to attempt to
            // read the message body at all.
            if (httpData.HttpMethod != "GET")
            {
                var operationVariable = context.FindVariable(context.Descriptor.OperationType);

                if (ownedBySource.Any())
                {
                    // We have [FromBody] so that needs to be set, instead of populating the context.Operation property.
                    var prop = ownedBySource.Single();
                    var operationProperty = operationVariable.GetProperty(prop.Property.Name);

                    var readCall = new MethodCall(
                        typeof(HttpBodyMessagePopulationSource),
                        _populateBodyMethod.MakeGenericMethod(operationProperty.VariableType));

                    readCall.TrySetArgument(new Variable(operationProperty.VariableType, $"new {operationProperty.VariableType.FullNameInCode()}()"));

                    context.AppendFrames(
                        readCall,
                        new VariableSetterFrame(operationProperty, readCall.ReturnVariable));
                }
                else
                {
                    var readCall = new MethodCall(
                        typeof(HttpBodyMessagePopulationSource),
                        _populateBodyMethod.MakeGenericMethod(context.Descriptor.OperationType));

                    readCall.TrySetArgument(operationVariable);
                    readCall.ReturnVariable.OverrideName("parseBodyResult");

                    var setInOperation = new VariableSetterFrame(operationVariable, readCall.ReturnVariable);
                    var setAmbient     =
                        new VariableSetterFrame(
                            context.FindVariable(typeof(ApiOperationContext)).GetProperty(nameof(ApiOperationContext.Operation)),
                            readCall.ReturnVariable);

                    context.AppendFrames(
                        readCall,
                        setInOperation,
                        setAmbient);
                }
            }
        }
Example #18
0
 /// <summary>
 /// Initialises a new instance of the <see cref="PushToErrorLoggerExceptionCatchFrame" /> class.
 /// </summary>
 /// <param name="context">The builder context this frame belongs to.</param>
 /// <param name="exceptionVariable">The variable representing the <see cref="Exception" /> that has
 /// been raised.</param>
 public PushToErrorLoggerExceptionCatchFrame(MiddlewareBuilderContext context, Variable exceptionVariable)
 {
     this._context           = context;
     this._exceptionVariable = exceptionVariable;
 }
Example #19
0
 /// <inheritdoc/>
 public void Build(MiddlewareBuilderContext context)
 {
     context.AppendFrames(new ConditionalFrame(
                              (v, _) => v.TryFindVariable(typeof(OperationResult)) != null,
                              new MethodCall(typeof(LinkGeneratorHandler), nameof(LinkGeneratorHandler.AddLinksAsync))));
 }
Example #20
0
        /// <inheritdoc />
        public void Build(MiddlewareBuilderContext context)
        {
            var properties        = context.Descriptor.Properties;
            var operationVariable = context.FindVariable(context.Descriptor.OperationType);
            var apiOperationDescriptorVariable = context.FindVariable <ApiOperationDescriptor>();
            var resultsCreator      = new ConstructorFrame <ValidationFailures>(() => new ValidationFailures());
            var hasValidationFrames = false;
            var sources             = context.ServiceProvider.GetServices <IValidationSourceBuilder>();

            void AddValidatorFrame(Frame frame)
            {
                if (!hasValidationFrames)
                {
                    // Only add the "results creator" frame if there are any actual validation calls. This is the line
                    // that creates an empty ValidationFailures
                    context.ExecuteMethod.Frames.Add(resultsCreator);

                    hasValidationFrames = true;
                }

                context.ExecuteMethod.Frames.Add(frame);
            }

            var operationProperties = new List <OperationProperty>();

            for (var i = 0; i < properties.Length; i++)
            {
                var p = properties[i];

                var propertyInfoVariable       = new PropertyInfoVariable(p, $"{apiOperationDescriptorVariable}.{nameof(ApiOperationDescriptor.Properties)}[{i}]");
                var propertyAttributesVariable = new Variable(typeof(object[]), $"{apiOperationDescriptorVariable}.{nameof(ApiOperationDescriptor.PropertyAttributes)}[{i}]");
                var propertyValueVariable      = operationVariable.GetProperty(p.Name);

                operationProperties.Add(new OperationProperty
                {
                    PropertyInfoVariable       = propertyInfoVariable,
                    PropertyValueVariable      = propertyValueVariable,
                    PropertyAttributesVariable = propertyAttributesVariable,
                });
            }

            foreach (var s in sources)
            {
                foreach (var frame in s.GetFrames(operationVariable, operationProperties))
                {
                    AddValidatorFrame(frame);
                }
            }

            // Only bother trying to validate if properties actually exist that are validated
            if (hasValidationFrames)
            {
                /*
                 * var validationFailures = _from above_;
                 *
                 * if (validationFailures.Count > 0)
                 * {
                 *     var validationResult = new ValidationResult(validationFailures);
                 *
                 *     return validationResult;
                 * }
                 */
                var createResult = new ConstructorFrame <ValidationFailedOperationResult>(() => new ValidationFailedOperationResult((ValidationFailures)null));

                var failureCount = $"{resultsCreator.Variable}.{nameof(ValidationFailures.Count)}";

                context.AppendFrames(
                    new IfBlock($"{failureCount} > 0")
                {
                    LogFrame.Debug(
                        _validationFailedLogEvent,
                        "Validation failed with {ValidationFailureCount} failures, returning ValidationFailedOperationResult",
                        new Variable <int>(failureCount)),
                    createResult,
                    new ReturnFrame(createResult.Variable),
                });
            }

            var apiOperationContext = context.FindVariable(typeof(ApiOperationContext));
            var activityVariable    = apiOperationContext.GetProperty(nameof(ApiOperationContext.Activity));

            // Always need to register extra exception handlers because the operation handler itself may do additional validation
            // and throw an exception to indicate a problem, even if the operation itself is _not_ validated
            context.RegisterUnhandledExceptionHandler(typeof(ValidationException), e => RegisterBlueprintExceptionHandler(activityVariable, e));
            context.RegisterUnhandledExceptionHandler(typeof(System.ComponentModel.DataAnnotations.ValidationException), e => RegisterDataAnnotationsExceptionHandler(activityVariable, e));
        }
Example #21
0
        /// <inheritdoc />
        public void Build(
            IReadOnlyCollection <OwnedPropertyDescriptor> ownedProperties,
            IReadOnlyCollection <OwnedPropertyDescriptor> ownedBySource,
            MiddlewareBuilderContext context)
        {
            if (context.Descriptor.TryGetFeatureData <HttpOperationFeatureData>(out var _) == false)
            {
                return;
            }

            var allLinks = context.Model.GetLinksForOperation(context.Descriptor.OperationType).ToList();

            var placeholderProperties = allLinks
                                        .SelectMany(l => l.Placeholders)
                                        .Select(p => p.Property)
                                        .Distinct()
                                        .ToList();

            var operationVariable = context.FindVariable(context.Descriptor.OperationType);

            // Add a single setter frame so we only grab using GetRouteData() once
            var routeDataFrame = new MethodCall(typeof(ApiOperationContextHttpExtensions), nameof(ApiOperationContextHttpExtensions.GetRouteData));

            if (placeholderProperties.Any())
            {
                context.ExecuteMethod.Frames.Add(routeDataFrame);
            }

            foreach (var routePropertyPlaceholder in placeholderProperties)
            {
                var routeProperty = routePropertyPlaceholder;

                if (routeProperty.PropertyType != typeof(string) && !TypeDescriptor.GetConverter(routeProperty.PropertyType).CanConvertFrom(typeof(string)))
                {
                    throw new InvalidOperationException(
                              $"Property {context.Descriptor.OperationType.Name}.{routeProperty.Name} cannot be used in routes, it cannot be converted from string");
                }

                var operationProperty   = operationVariable.GetProperty(routeProperty.Name);
                var routeValuesVariable = routeDataFrame.ReturnVariable.GetProperty(nameof(RouteData.Values));

                // When this property is not in ALL routes we use TryGetValue from the RouteData dictionary and pass
                // the var output from that to `GetConversionExpression` as part of ternary. If the route data
                // does not exist then we fallback to the value already on the operation
                var outVariableName      = "routeValue" + routeProperty.Name;
                var conversionExpression = HttpPartMessagePopulationSource.GetConversionExpression(
                    routeProperty,
                    outVariableName,
                    false,
                    "Route");

                // context.RouteData.TryGetValue("PropertyName", out var routeValuePropertyName) ? [conversion of routeValuePropertyName] : [operationProperty];
                var tryConversionExpression =
                    $"{routeValuesVariable}.{nameof(RouteValueDictionary.TryGetValue)}(\"{routeProperty.Name}\", out var {outVariableName}) ? " +
                    $"{conversionExpression} : " +
                    $"{operationProperty}";

                context.ExecuteMethod.Frames.Add(new VariableSetterFrame(
                                                     operationProperty,
                                                     tryConversionExpression));
            }
        }
 /// <summary>
 /// Initialises a new instance of the <see cref="SetTagsFromMessageDataFrame" /> class.
 /// </summary>
 /// <param name="context">The builder context this frame belongs to.</param>
 public SetTagsFromMessageDataFrame(MiddlewareBuilderContext context)
 {
     this._context = context;
 }
 /// <summary>
 /// Initialises a new instance of the <see cref="ErrorHandlerFrame" /> class.
 /// </summary>
 /// <param name="context">The builder context for this frame.</param>
 public ErrorHandlerFrame(MiddlewareBuilderContext context)
 {
     this._context = context;
 }
Example #24
0
        /// <inheritdoc />
        public void Build(
            IReadOnlyCollection <OwnedPropertyDescriptor> ownedProperties,
            IReadOnlyCollection <OwnedPropertyDescriptor> ownedBySource,
            MiddlewareBuilderContext context)
        {
            // Never apply if we are not in a HTTP-supported operation
            if (context.Descriptor.TryGetFeatureData <HttpOperationFeatureData>(out var _) == false)
            {
                return;
            }

            if (this._applies?.Invoke(context) == false)
            {
                return;
            }

            var operationVariable   = context.FindVariable(context.Descriptor.OperationType);
            var httpContextVariable = context.FindVariable(typeof(HttpContext));
            var sourceVariable      = this._sourceCodeExpression(httpContextVariable);

            foreach (var prop in this._isCatchAll ? context.Descriptor.Properties : ownedBySource.Select(s => s.Property))
            {
                if (prop.CanWrite == false)
                {
                    continue;
                }

                // If this is a catch-all instance we DO NOT want to generate any code for a property
                // that is owned by a different source.
                if (this._isCatchAll && ownedProperties.Any(p => p.Property == prop))
                {
                    continue;
                }

                var partKey           = this.GetPartKey(prop);
                var operationProperty = operationVariable.GetProperty(prop.Name);

                // When this property is not in ALL routes we use TryGetValue from the RouteData dictionary and pass
                // the var output from that to `GetConversionExpression` as part of ternary. If the route data
                // does not exist then we fallback to the value already on the operation
                var outVariableName      = $"{this._variablePrefix}{prop.Name}";
                var conversionExpression = GetConversionExpression(prop, outVariableName, this._supportsMultiValues, this._variablePrefix);

                var indexProperty = sourceVariable.VariableType.GetProperties().SingleOrDefault(p => p.GetIndexParameters().Length == 1);

                if (indexProperty == null)
                {
                    throw new InvalidOperationException($"Source variable {sourceVariable} has no index property specified. Cannot use");
                }

                var isStringValues = indexProperty.PropertyType == typeof(StringValues);
                var emptyCheckRhs  = isStringValues ? $"{typeof(StringValues).FullNameInCode()}.Empty" : "null";

                var source = $"{sourceVariable}[\"{partKey}\"]";

                // We want to support the key naming scheme of key[] to indicate an array, therefore
                // we add a null-coalesce to partKey[]
                if (IsArrayLike(prop.PropertyType, out _))
                {
                    source = $"{source} == {emptyCheckRhs} ? {sourceVariable}[\"{partKey}[]\"] : {source}";
                }

                var sourceVariableGetter = new VariableCreationFrame(typeof(object), outVariableName, source);

                // [Source] == null ? [conversion of valuePropertyName] : [operationProperty];
                var tryConversionExpression =
                    $"{sourceVariableGetter.CreatedVariable} != {emptyCheckRhs} ? " +
                    $"{conversionExpression} : " +
                    $"{operationProperty}";

                context.AppendFrames(
                    sourceVariableGetter,
                    new VariableSetterFrame(
                        operationProperty,
                        tryConversionExpression),
                    new BlankLineFrame());
            }
        }
        /// <inheritdoc />
        public void Build(MiddlewareBuilderContext context)
        {
            var runNowCall = MethodCall.For <IBackgroundTaskScheduler>(s => s.RunNowAsync());

            context.AppendFrames(runNowCall);
        }