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