Ejemplo n.º 1
0
        /// <inheritdoc />
        public void Apply(ApiOperationDescriptor descriptor)
        {
            var docs = descriptor.OperationType.GetXmlDocsElement();

            if (docs == null)
            {
                return;
            }

            var exceptionElements = docs.Elements("exception");

            foreach (var e in exceptionElements)
            {
                // The exception type is stored as T:[full name]. Strip the first two characters.
                var exceptionTypeText = e.Attribute("cref").Value.Substring(2);
                var exceptionType     = Type.GetType(exceptionTypeText);

                if (exceptionType == null)
                {
                    throw new InvalidOperationException(
                              $"Could not find type {exceptionTypeText} as described by an exception tag in documentation for the operation {descriptor.OperationType.FullName}.");
                }

                var status = e.Attribute("status") == null?ToHttpStatus(exceptionType) : int.Parse(e.Attribute("status").Value);

                var description = e.Value;

                descriptor.AddResponse(new ResponseDescriptor(
                                           exceptionType,
                                           status,
                                           description,
                                           e.Attributes().ToDictionary(a => a.Name.LocalName, a => a.Value)));
            }
        }
Ejemplo n.º 2
0
        private ApiOperationDescriptor CreateApiOperationDescriptor(Type type, string source)
        {
            var descriptor = new ApiOperationDescriptor(type, source)
            {
                AnonymousAccessAllowed = type.HasAttribute <AllowAnonymousAttribute>(true),
                IsExposed   = type.HasAttribute <UnexposedOperationAttribute>(true) == false,
                ShouldAudit = !type.HasAttribute <DoNotAuditOperationAttribute>(true),
                RecordPerformanceMetrics = !type.HasAttribute <DoNotRecordPerformanceMetricsAttribute>(true),
            };

            foreach (var linkAttribute in type.GetCustomAttributes <LinkAttribute>())
            {
                descriptor.AddLink(
                    new ApiOperationLink(descriptor, linkAttribute.RoutePattern, linkAttribute.Rel ?? descriptor.Name)
                {
                    ResourceType = linkAttribute.ResourceType,
                });
            }

            RegisterResponses(descriptor);

            foreach (var c in this._conventions)
            {
                c.Apply(descriptor);
            }

            return(descriptor);
        }
Ejemplo n.º 3
0
        /// <inheritdoc />
        public IApmSpan StartOperation(ApiOperationDescriptor operation, string spanKind, IDictionary <string, string> existingContext = null)
        {
            if (!global::Elastic.Apm.Agent.IsConfigured)
            {
                return(NullApmSpan.Instance);
            }

            var tracer = global::Elastic.Apm.Agent.Tracer;

            if (tracer.CurrentTransaction != null)
            {
                // If a transaction is already underway we want to set it's name for a more accurate picture (i.e. this is
                // a HTTP call but we want to use the operation name not the HTTP route name).
                tracer.CurrentTransaction.Name = operation.Name;

                // We will also start a new span as there may be work before and after in the framework that should
                // be tracked separately from the Blueprint processing work.
                return(new ElasticSpan(
                           tracer.CurrentTransaction.StartSpan(operation.Name, ApiConstants.TypeRequest, "operation", ApiConstants.ActionExec)));
            }

            DistributedTracingData?distributedTracingData = null;

            if (existingContext != null && existingContext.TryGetValue("ElasticDTD", out var d))
            {
                distributedTracingData = DistributedTracingData.TryDeserializeFromString(d);
            }

            return(new ElasticSpan(tracer.StartTransaction(
                                       operation.Name,
                                       spanKind,
                                       distributedTracingData)));
        }
Ejemplo n.º 4
0
 private List <IApiAuthoriser> GetForOperation(ApiOperationDescriptor descriptor)
 {
     return(_operationTypeAuthorisers.GetOrAdd(descriptor.OperationType, t =>
     {
         return this._apiAuthorisers.Where(checker => checker.AppliesTo(descriptor)).ToList();
     }));
 }
        /// <inheritdoc />
        public void Apply(ApiOperationDescriptor descriptor)
        {
            if (this.IsSupported(descriptor.OperationType))
            {
                string supportedMethod;

                var httpMethodAttribute = descriptor.OperationType.GetCustomAttribute <HttpMethodAttribute>(true);

                if (httpMethodAttribute != null)
                {
                    supportedMethod = httpMethodAttribute.HttpMethod;
                }
                else
                {
                    // By default, command are POST and everything else GET
                    supportedMethod = typeof(ICommand).IsAssignableFrom(descriptor.OperationType) ? "POST" : "GET";
                }

                descriptor.SetFeatureData(new HttpOperationFeatureData(supportedMethod));

                descriptor.AllowMultipleHandlers = false;
                descriptor.RequiresReturnValue   = true;

                RegisterResponses(descriptor);
            }
        }
Ejemplo n.º 6
0
        public void When_Placeholder_Followed_By_Static_Then_Includes_All()
        {
            // Act
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");
            var link       = new ApiOperationLink(descriptor, "/aUrl/{clientid:id}/some-more", "a.rel");

            // Assert
            link.CreateRelativeUrl(new { id = 15484 }).Should().EndWith("aUrl/15484/some-more");
        }
Ejemplo n.º 7
0
        public void When_Additional_Properties_Exists_Ignore_When_IncludeExtra_False()
        {
            // Act
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");
            var link       = new ApiOperationLink(descriptor, "/users", "a.rel");

            // Assert
            link.CreateRelativeUrl(new { sortBy = "name", page = 2 }, false).Should().Be("users");
        }
Ejemplo n.º 8
0
        public void When_Url_With_Placeholder_Requiring_Encoding_Then_CreateUrl_Replaces_With_Encoded_Property_In_PropValues()
        {
            // Act
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");
            var link       = new ApiOperationLink(descriptor, "/aUrl/{category}", "a.rel");

            // Assert
            link.CreateRelativeUrl(new { category = "all dogs" }).Should().EndWith("aUrl/all%20dogs");
        }
Ejemplo n.º 9
0
        public void When_Placeholder_Specifies_Alternate_Property_And_Followed_By_Another_Placeholder_Then_Uses_That_Property_From_Instance_Values()
        {
            // Act
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");
            var link       = new ApiOperationLink(descriptor, "/aUrl/{clientid:id}/{category}", "a.rel");

            // Assert
            link.CreateRelativeUrl(new { id = 15484, category = "value" }).Should().EndWith("aUrl/15484/value");
        }
Ejemplo n.º 10
0
        public void When_Url_With_Placeholder_Then_Returns_Null_If_Property_Is_Null()
        {
            // Act
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");
            var link       = new ApiOperationLink(descriptor, "/aUrl/{category}", "a.rel");

            // Assert
            link.CreateRelativeUrl(new { category = (string)null }).Should().BeNull();
        }
Ejemplo n.º 11
0
        public void When_Url_No_Placeholders_CreateUrl_Returns_Url_As_Is()
        {
            // Arrange
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");
            var link       = new ApiOperationLink(descriptor, "/aUrl", "a.rel");

            // Assert
            link.CreateRelativeUrl(new object()).Should().EndWith("aUrl");
        }
Ejemplo n.º 12
0
        public void When_Placeholder_Has_Format_Then_CreateUrl_Uses_Format()
        {
            // Act
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");
            var date       = new DateTime(2012, 04, 21);
            var link       = new ApiOperationLink(descriptor, "/aUrl/{date(yyyy-MM-dd)}", "a.rel");

            // Assert
            link.CreateRelativeUrl(new { date }).Should().EndWith("aUrl/2012-04-21");
        }
Ejemplo n.º 13
0
        public void When_Url_With_Invalid_Alternate_Placeholder_Then_Exception_Thrown()
        {
            // Act
            var    descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");
            Action tryCreate  = () => new ApiOperationLink(descriptor, "/aUrl/{clientId:doesNotExist}", "a.rel", typeof(LinkGeneratorTestsResource));

            // Assert
            tryCreate.Should().ThrowExactly <OperationLinkFormatException>()
            .WithMessage("Link /aUrl/{clientId:doesNotExist} for operation LinkGeneratorTestsOperation specifies placeholder {clientId:doesNotExist}. Cannot find alternate property doesNotExist on resource LinkGeneratorTestsResource");
        }
Ejemplo n.º 14
0
        public void When_Url_With_Placeholder_With_Missing_Property_In_ResourceType_Then_Exception_Thrown()
        {
            // Act
            var    descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");
            Action tryCreate  = () => new ApiOperationLink(descriptor, "/aUrl/{clientId}", "a.rel", typeof(LinkGeneratorTestsResource));

            // Assert
            tryCreate.Should().ThrowExactly <OperationLinkFormatException>()
            .WithMessage("Link /aUrl/{clientId} for operation LinkGeneratorTestsOperation specifies placeholder ClientId that cannot be found on resource LinkGeneratorTestsResource");
        }
Ejemplo n.º 15
0
        public void When_ResourceType_is_not_ILinkableResource_then_exception()
        {
            // Act
            var    descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");
            Action tryCreate  = () => new ApiOperationLink(descriptor, "/aUrl/{clientId:doesNotExist}", "a.rel", typeof(string));

            // Assert
            tryCreate.Should().ThrowExactly <OperationLinkFormatException>()
            .WithMessage("Resource type string is not assignable to ILinkableResource, cannot add a link for LinkGeneratorTestsOperation");
        }
Ejemplo n.º 16
0
        public void When_Url_With_Invalid_Placeholder_Then_Exception_thrown()
        {
            // Act
            var    descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");
            Action tryCreate  = () => new ApiOperationLink(descriptor, "/aUrl/{cannotBeFound}", "a.rel");

            // Assert
            tryCreate.Should().ThrowExactly <OperationLinkFormatException>()
            .WithMessage("URL /aUrl/{cannotBeFound} is invalid. Property cannotBeFound does not exist on operation type LinkGeneratorTestsOperation");
        }
Ejemplo n.º 17
0
        public void When_Placeholder_Has_Alternate_Name_Then_RoutingUrl_Strips()
        {
            // Arrange
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");

            // Act
            var link = new ApiOperationLink(descriptor, "/aUrl/{ClientId:Id}", "a.rel");

            // Assert
            link.RoutingUrl.Should().Be("aUrl/{ClientId}");
        }
Ejemplo n.º 18
0
        public void When_Created_Then_Exposes_Rel_Property()
        {
            // Arrange
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");

            // Act
            var link = new ApiOperationLink(descriptor, "/aUrl", "a.rel");

            // Assert
            link.Rel.Should().Be("a.rel");
        }
Ejemplo n.º 19
0
        public void When_Format_Has_QueryString_RoutingUrl_Strips()
        {
            // Arrange
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");

            // Act
            var link = new ApiOperationLink(descriptor, "/aUrl/{clientid:id}?format=pdf", "a.rel");

            // Assert
            link.RoutingUrl.Should().Be("aUrl/{ClientId}");
        }
Ejemplo n.º 20
0
        public void When_Placeholder_Has_Different_Case_RoutingUrl_Should_Normalise()
        {
            // Arrange
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");

            // Act
            var link = new ApiOperationLink(descriptor, "/aUrl/{clientId:Id}", "a.rel");

            // Assert
            link.RoutingUrl.Should().Be("aUrl/{ClientId}");
        }
Ejemplo n.º 21
0
        /// <inheritdoc />
        public IApmSpan StartOperation(ApiOperationDescriptor operation, string spanKind, IDictionary <string, string> existingContext = null)
        {
            // Stackify requires us to pass a Func or Action, but Blueprint uses disposables. To make this work we have
            // Stackify wait on an "empty" async method that waits on the TCS completed below, that will be trigged when the returned
            // StackifyApmSpan is disposed, or sets an exception.
            var tcs = new TaskCompletionSource <bool>();

            var dependency = ProfileTracer.CreateAsOperation(operation.Name);

            dependency.ExecAsync(async() => await tcs.Task);

            return(new StackifyApmSpan(this, tcs));
        }
Ejemplo n.º 22
0
        public void When_Placeholder_Has_Alternate_Name_Then_Placeholder_Created_With_AlternatePropertyName()
        {
            // Arrange
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");

            // Act
            var link = new ApiOperationLink(descriptor, "/aUrl/{ClientId:Id}", "a.rel");

            // Assert
            link.Placeholders[0].AlternatePropertyName.Should().Be("Id");
            link.Placeholders[0].OriginalText.Should().Be("{ClientId:Id}");
            link.Placeholders[0].Property.Name.Should().Be("ClientId");
            link.Placeholders[0].Format.Should().BeNull();
        }
Ejemplo n.º 23
0
        public void When_Additional_Properties_And_Placeholders_Exists_Adds_As_QueryString_When_IncludeExtra_True()
        {
            // Act
            var descriptor = new ApiOperationDescriptor(typeof(LinkGeneratorTestsOperation), "tests");
            var link       = new ApiOperationLink(descriptor, "/clients/{clientId}/users", "a.rel");

            // Act
            var url = link.CreateRelativeUrl(new LinkGeneratorTestsOperation {
                ClientId = 12, SortBy = "name", Page = 2
            }, true);

            // Assert
            url.Should().Be("clients/12/users?SortBy=name&Page=2");
        }
Ejemplo n.º 24
0
        /// <inheritdoc />
        public IApmSpan StartOperation(ApiOperationDescriptor operation, string spanKind, IDictionary <string, string> existingContext = null)
        {
            var request = this._telemetryClient.StartOperation <RequestTelemetry>(operation.Name);

            if (existingContext != null &&
                existingContext.TryGetValue("RootId", out var rootId) &&
                existingContext.TryGetValue("ParentId", out var parentId))
            {
                request.Telemetry.Context.Operation.Id       = rootId;
                request.Telemetry.Context.Operation.ParentId = parentId;
            }

            return(new ApplicationInsightsApmSpan <RequestTelemetry>(this, request));
        }
Ejemplo n.º 25
0
        /// <inheritdoc />
        public IApmSpan StartOperation(ApiOperationDescriptor operation, string spanKind, IDictionary <string, string> existingContext = null)
        {
            var spanBuilder = this._tracer.BuildSpan("operation.execute");

            if (existingContext != null)
            {
                spanBuilder.AsChildOf(this._tracer.Extract(BuiltinFormats.TextMap, new TextMapExtractAdapter(existingContext)));
            }

            var span = spanBuilder.Start();

            Tags.Component.Set(span, operation.Name);
            Tags.SpanKind.Set(span, spanKind);

            return(new OpenTracingSpan(this._tracer, span));
        }
        public void CreateGenerator()
        {
            options = new BlueprintApiOptions();

            new OperationScanner()
            .AddOperation <LinkGeneratorTestsOperation>()
            .FindOperations(options.Model);

            var httpContext = new DefaultHttpContext();

            httpContext.SetBaseUri("http://api.example.com/api/");

            linkGenerator = new ApiLinkGenerator(options.Model, new HttpContextAccessor {
                HttpContext = httpContext
            });
            descriptor = options.Model.FindOperation(typeof(LinkGeneratorTestsOperation));
        }
Ejemplo n.º 27
0
        private static void RegisterResponses(ApiOperationDescriptor descriptor)
        {
            var typedOperation = descriptor.OperationType
                                 .GetInterfaces()
                                 .SingleOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IReturn <>));

            if (typedOperation != null)
            {
                descriptor.AddResponse(
                    new ResponseDescriptor(typedOperation.GetGenericArguments()[0], 200, "OK"));
            }

            descriptor.AddResponse(
                new ResponseDescriptor(typeof(UnhandledExceptionOperationResult), 500, "Unexpected error"));

            descriptor.AddResponse(
                new ResponseDescriptor(typeof(ValidationFailedOperationResult), 422, "Validation failure"));
        }
        private static void RegisterResponses(ApiOperationDescriptor descriptor)
        {
            var typedOperation = descriptor.OperationType
                                 .GetInterfaces()
                                 .SingleOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IReturn <>));

            if (typedOperation != null)
            {
                var returnType = typedOperation.GetGenericArguments()[0];

                // If we have a StatusCodeResult then we either
                // 1. Have a specific subclass and therefore know the expected response code and can therefore add a response
                // 2. Have the base class and therefore can not determine the actual expected response code so leave it open and do not add anything specific
                if (typeof(StatusCodeResult).IsAssignableFrom(returnType))
                {
                    var instanceProperty = returnType.GetField("Instance", BindingFlags.Public | BindingFlags.Static);

                    if (instanceProperty != null)
                    {
                        // This is option 1, we have a specific subclass (see the .tt file that generates these, i.e. StatusCodeResult.Created)
                        var statusCode = ((StatusCodeResult)instanceProperty.GetValue(null)).StatusCode;

                        descriptor.AddResponse(
                            new ResponseDescriptor((int)statusCode, statusCode.ToString()));
                    }
                }
                else
                {
                    descriptor.AddResponse(
                        new ResponseDescriptor(returnType, (int)HttpStatusCode.OK, HttpStatusCode.OK.ToString()));
                }
            }

            descriptor.AddResponse(
                new ResponseDescriptor(typeof(UnhandledExceptionOperationResult), 500, "Unexpected error"));

            descriptor.AddResponse(
                new ResponseDescriptor(typeof(ValidationFailedOperationResult), 422, "Validation failure"));
        }
Ejemplo n.º 29
0
        /// <inheritdoc />
        public IApmSpan StartOperation(ApiOperationDescriptor operation, string spanKind, IDictionary <string, string> existingContext = null)
        {
            SpanContext parent = null;

            if (existingContext != null &&
                existingContext.TryGetValue("SpanId", out var spanId) &&
                existingContext.TryGetValue("TraceId", out var traceId) &&
                existingContext.TryGetValue("SamplingPriority", out var samplingPriority))
            {
                parent = new SpanContext(
                    ulong.Parse(traceId),
                    ulong.Parse(spanId),
                    Enum.TryParse <SamplingPriority>(samplingPriority, out var p) ? p : SamplingPriority.AutoKeep);
            }

            var scope = Tracer.Instance.StartActive("operation.execute", parent);

            scope.Span.Type         = "request";
            scope.Span.ResourceName = operation.Name;
            scope.Span.SetTag(Tags.SpanKind, spanKind);

            return(new OpenTracingSpan(this, scope));
        }
        /// <inheritdoc />
        public void Apply(ApiOperationDescriptor descriptor)
        {
            var staticFields = descriptor.OperationType.GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);

            foreach (var f in staticFields)
            {
                if (f.FieldType != typeof(ApiExceptionFactory))
                {
                    continue;
                }

                if (f.GetValue(null) is ApiExceptionFactory factory)
                {
                    descriptor.AddResponse(new ResponseDescriptor(
                                               typeof(ApiException),
                                               factory.HttpStatus,
                                               factory.Title,
                                               new Dictionary <string, string>
                    {
                        ["type"] = factory.Type,
                    }));
                }
            }
        }