コード例 #1
0
        /// <summary>
        /// Registers the given operation descriptor.
        /// </summary>
        /// <param name="descriptor">The descriptor to register, must be non-null.</param>
        public void RegisterOperation(ApiOperationDescriptor descriptor)
        {
            Guard.NotNull(nameof(descriptor), descriptor);

            this._allOperations[descriptor.OperationType] = descriptor;

            foreach (var link in descriptor.Links)
            {
                this.RegisterLink(link);
            }
        }
コード例 #2
0
 private static string NormaliseTypeName(ApiOperationDescriptor operation)
 {
     // Replace + with _ to enable nested operation classes to compile successfully
     // Replace , with _ to separate generic arguments
     // Replace < with Of at start of generic arguments (i.e. IWrapperOfT for IWrapper<T>)
     // Replace > with "" at end of generic argument list
     return(ReflectionUtilities
            .PrettyTypeName(operation.OperationType)
            .Replace("+", "_")
            .Replace(",", "_")
            .Replace("<", "Of")
            .Replace(">", string.Empty)
            + "ExecutorPipeline");
 }
コード例 #3
0
        internal MiddlewareBuilderContext(
            GeneratedMethod executeMethod,
            ApiOperationDescriptor descriptor,
            ApiDataModel model,
            IServiceProvider serviceProvider,
            InstanceFrameProvider instanceFrameProvider,
            bool isNested)
        {
            this.ExecuteMethod   = executeMethod;
            this.Descriptor      = descriptor;
            this.Model           = model;
            this.ServiceProvider = serviceProvider;
            this.IsNested        = isNested;

            this._instanceFrameProvider = instanceFrameProvider;
        }
コード例 #4
0
        /// <summary>
        /// Constructs a link that applies at a 'system' level, can be overriden by setting
        /// <see cref="ResourceType"/>.
        /// </summary>
        /// <param name="operationDescriptor">The operation this link represents.</param>
        /// <param name="urlFormat">The URL from which this link is accessed, may be templated.</param>
        /// <param name="rel">The <strong>rel</strong>ationship this link represents.</param>
        public ApiOperationLink(ApiOperationDescriptor operationDescriptor, string urlFormat, string rel)
        {
            Guard.NotNull(nameof(operationDescriptor), operationDescriptor);
            Guard.NotNull(nameof(urlFormat), urlFormat);
            Guard.NotNull(nameof(rel), rel);

            this.OperationDescriptor = operationDescriptor;
            this.UrlFormat           = urlFormat.TrimStart('/');
            this.Rel = rel;

            var placeholders = new List <ApiOperationLinkPlaceholder>(5);

            this.RoutingUrl = _queryStringRegex.Replace(
                _parameterRegex.Replace(
                    this.UrlFormat,
                    match =>
            {
                var propertyName = match.Groups["propName"].Value;
                var property     = operationDescriptor.OperationType.GetProperty(
                    propertyName,
                    BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);

                if (property == null)
                {
                    throw new InvalidOperationException(
                        $"Property {propertyName} does not exist on operation type {operationDescriptor.OperationType.Name}.");
                }

                placeholders.Add(new ApiOperationLinkPlaceholder(
                                     match.Value,
                                     match.Index,
                                     property,
                                     match.Groups["alternatePropName"].Success ? match.Groups["alternatePropName"].Value : null,
                                     match.Groups["format"].Success ? match.Groups["format"].Value : null));

                return("{" + property.Name + "}");
            }),
                string.Empty);

            this.Placeholders = placeholders;

            // Let's precalculate this as ApiOperationLinks are expected to stay around for lifetime of the application, so may as well
            // pay this cost upfront.
            this._description = $"{this.ResourceType?.Name ?? "Root"}#{this.Rel} => {this.OperationDescriptor.OperationType.Name}";
        }
コード例 #5
0
        /// <summary>
        /// Initialises a new instance of the <see cref="ApiOperationContext" /> class, using
        /// <see cref="ApiOperationDescriptor.CreateInstance" /> to construct the operation
        /// instance.
        /// </summary>
        /// <param name="serviceProvider">The service provider (typically a nested scope) for this context.</param>
        /// <param name="dataModel">The data model that represents the API in which this context is being executed.</param>
        /// <param name="operationDescriptor">A descriptor for the operation that is being executed.</param>
        /// <param name="token">A cancellation token to indicate the operation should stop.</param>
        public ApiOperationContext(
            IServiceProvider serviceProvider,
            ApiDataModel dataModel,
            ApiOperationDescriptor operationDescriptor,
            CancellationToken token)
        {
            Guard.NotNull(nameof(serviceProvider), serviceProvider);
            Guard.NotNull(nameof(dataModel), dataModel);
            Guard.NotNull(nameof(operationDescriptor), operationDescriptor);

            this.Descriptor         = operationDescriptor;
            this.OperationCancelled = token;
            this.DataModel          = dataModel;
            this.ServiceProvider    = serviceProvider;
            this.Operation          = operationDescriptor.CreateInstance();
            this.ApmSpan            = NullApmSpan.Instance;

            this.Data = new Dictionary <string, object>();
        }
コード例 #6
0
        /// <summary>
        /// Constructs a link that applies at a resource level.
        /// </summary>
        /// <param name="operationDescriptor">The operation this link represents.</param>
        /// <param name="urlFormat">The URL from which this link is accessed, may be templated.</param>
        /// <param name="rel">The <strong>rel</strong>ationship this link represents.</param>
        /// <param name="resourceType">he resource from which this link can be created, for example a `UserResource` value that
        /// is returned from an operation. This <b>MUST</b> be an <see cref="ILinkableResource" />.</param>
        public ApiOperationLink(ApiOperationDescriptor operationDescriptor, string urlFormat, string rel, [CanBeNull] Type resourceType)
            : this(operationDescriptor, urlFormat, rel)
        {
            this.ResourceType = resourceType;

            if (resourceType != null)
            {
                if (resourceType.IsAssignableTo(typeof(ILinkableResource)) == false)
                {
                    throw new OperationLinkFormatException(
                              $"Resource type {resourceType.Name} is not assignable to {nameof(ILinkableResource)}, cannot add a link for {operationDescriptor.Name}");
                }

                foreach (var placeholder in this.Placeholders)
                {
                    var prop = resourceType.GetProperty(placeholder.Property.Name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);

                    if (prop != null)
                    {
                        continue;
                    }

                    if (placeholder.AlternatePropertyName == null)
                    {
                        throw new OperationLinkFormatException(
                                  $"Link {urlFormat} for operation {operationDescriptor.Name} specifies placeholder {placeholder.Property.Name} that cannot be found on resource {resourceType.Name}");
                    }

                    var alternateProperty = resourceType.GetProperty(
                        placeholder.AlternatePropertyName,
                        BindingFlags.Instance | BindingFlags.Public | BindingFlags.IgnoreCase);

                    if (alternateProperty == null)
                    {
                        throw new OperationLinkFormatException(
                                  $"Link {urlFormat} for operation {operationDescriptor.Name} specifies placeholder {placeholder.OriginalText}. Cannot find alternate property {placeholder.AlternatePropertyName} on resource {resourceType.Name}");
                    }
                }
            }
        }
コード例 #7
0
        public Task <ExecutionAllowed> CanShowLinkAsync(ApiOperationContext operationContext, ApiOperationDescriptor descriptor, object resource)
        {
            if (resource is TResource r && !this.IsLinkAvailableForOperation(operationContext, r))
            {
                return(_stateCheckFailed);
            }

            return(ExecutionAllowed.YesTask);
        }
コード例 #8
0
 /// <summary>
 /// Always returns <see cref="ExecutionAllowed.YesTask" /> as state-based checks do not apply to the execution of
 /// the operations, only link generation.
 /// </summary>
 /// <param name="operationContext">The operation context.</param>
 /// <param name="descriptor">The operation description.</param>
 /// <param name="operation">The operation.</param>
 /// <returns><see cref="ExecutionAllowed.YesTask" />.</returns>
 public Task <ExecutionAllowed> CanExecuteOperationAsync(ApiOperationContext operationContext, ApiOperationDescriptor descriptor, object operation)
 {
     return(ExecutionAllowed.YesTask);
 }
コード例 #9
0
 public bool AppliesTo(ApiOperationDescriptor descriptor)
 {
     return(descriptor.OperationType == typeof(TOperation));
 }
コード例 #10
0
        private void Generate(
            BlueprintApiOptions options,
            IServiceProvider serviceProvider,
            GeneratedMethod executeMethod,
            ApiOperationDescriptor operation,
            ApiDataModel model,
            IServiceScope serviceScope,
            bool isNested)
        {
            var operationContextVariable = executeMethod.Arguments[0];

            var instanceFrameProvider             = serviceProvider.GetRequiredService <InstanceFrameProvider>();
            var dependencyInjectionVariableSource = new DependencyInjectionVariableSource(executeMethod, instanceFrameProvider);

            var castFrame = new ConcreteOperationCastFrame(operationContextVariable, operation.OperationType);

            var apiOperationContextSource =
                new ApiOperationContextVariableSource(operationContextVariable, castFrame.CastOperationVariable);

            var context = new MiddlewareBuilderContext(
                executeMethod,
                operation,
                model,
                serviceScope.ServiceProvider,
                instanceFrameProvider,
                isNested);

            // For the base Exception type we will add, as the first step, logging to the exception sinks. This frame DOES NOT
            // include a return frame, as we add that after all the other middleware builders have had chance to potentially add
            // more frames to perform other operations on unknown Exception
            context.RegisterUnhandledExceptionHandler(typeof(Exception), e => new Frame[]
            {
                // Exceptions do not escape from a pipeline because we always convert to a result type
                new PushExceptionToActivityFrame(e, false),
            });

            executeMethod.Sources.Add(apiOperationContextSource);
            executeMethod.Sources.Add(dependencyInjectionVariableSource);

            foreach (var source in options.GenerationRules.VariableSources)
            {
                executeMethod.Sources.Add(source);
            }

            var startActivityFrame = ActivityFrame.Start(ActivityKind.Internal, operation.Name + (isNested ? "NestedPipeline" : "Pipeline"));

            executeMethod.Frames.Add(startActivityFrame);

            executeMethod.Frames.Add(castFrame);
            executeMethod.Frames.Add(new ErrorHandlerFrame(context));
            executeMethod.Frames.Add(new BlankLineFrame());

            foreach (var behaviour in this._builders)
            {
                if (isNested && !behaviour.SupportsNestedExecution)
                {
                    continue;
                }

                if (behaviour.Matches(operation))
                {
                    executeMethod.Frames.Add(new CommentFrame(behaviour.GetType().Name));

                    behaviour.Build(context);

                    executeMethod.Frames.Add(new BlankLineFrame());
                }
            }

            // For the base Exception type we will add, as a last frame, a return of an OperationResult.
            context.RegisterUnhandledExceptionHandler(typeof(Exception), e => new[]
            {
                new ReturnFrame(new Variable(typeof(UnhandledExceptionOperationResult), $"new {typeof(UnhandledExceptionOperationResult).FullNameInCode()}({e})")),
            });
        }
コード例 #11
0
 /// <inheritdoc />
 public abstract bool Matches(ApiOperationDescriptor operation);