Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create a sample for a Custom Endpoint #204

Open
hananiel opened this issue May 26, 2021 · 2 comments
Open

Create a sample for a Custom Endpoint #204

hananiel opened this issue May 26, 2021 · 2 comments
Labels
Component/Management Issues related to Steeltoe Management (actuators) good first issue This is a good issue to get started contributing to the projects help wanted Looking for help from community for a resolution Type/enhancement

Comments

@hananiel
Copy link
Contributor

Add a sample that shows how to add a custom endpoint using the management endpoints library.

@hananiel hananiel added Type/enhancement ReleaseLine/3.x Component/Management Issues related to Steeltoe Management (actuators) labels May 26, 2021
@hananiel
Copy link
Contributor Author

One way to do it would be below. I just noodled in a scratch file but haven't actually tested this.

    public static class TestExtensions
    {
        public static IEndpointConventionBuilder MapCustomActuatorEndpoint(this IEndpointRouteBuilder endpoints, Type typeEndpoint, EndpointCollectionConventionBuilder conventionBuilder = null)
        {
            if (endpoints == null)
            {
                throw new ArgumentNullException(nameof(endpoints));
            }

            var serviceProvider = endpoints.ServiceProvider;

            var options = serviceProvider.GetService<CustomEndpointOptions>() as IEndpointOptions;
            var mgmtOptionsCollection = endpoints.ServiceProvider.GetServices<IManagementOptions>();
            var builder = conventionBuilder ?? new EndpointCollectionConventionBuilder();
            foreach (var mgmtOptions in mgmtOptionsCollection)
            {
                var fullPath = options.GetContextPath(mgmtOptions);

                var pattern = RoutePatternFactory.Parse(fullPath);

                // only add middleware if the route hasn't already been mapped
                if (!endpoints.DataSources.Any(d => d.Endpoints.Any(ep => ((RouteEndpoint)ep).RoutePattern.RawText == pattern.RawText)))
                {
                    var pipeline = endpoints.CreateApplicationBuilder()
                        .UseMiddleware(typeof(CustomEndpointMiddleware), mgmtOptions)
                        .Build();
                    var allowedVerbs = options.AllowedVerbs ?? new List<string> { "Get" };

                    builder.AddConventionBuilder(endpoints.MapMethods(fullPath, allowedVerbs, pipeline));
                }
            }

            return builder;
        }

        public static void AddCustomActuator( this IServiceCollection services, IConfiguration config = null)
        {
            if (services == null)
            {
                throw new ArgumentNullException(nameof(services));
            }

            config ??= services.BuildServiceProvider().GetService<IConfiguration>();
            if (config == null)
            {
                throw new ArgumentNullException(nameof(config));
            }

            services.AddActuatorEndpointMapping<CustomEndpoint>();
        }
    }

    public class CustomEndpointOptions : AbstractEndpointOptions
    {
        private const string MANAGEMENT_INFO_PREFIX = "management:endpoints:custom";

        public CustomEndpointOptions()
            : base()
        {
            Id = "custom";
            RequiredPermissions = Permissions.RESTRICTED;
        }

        public CustomEndpointOptions(IConfiguration config)
            : base(MANAGEMENT_INFO_PREFIX, config)
        {
            if (string.IsNullOrEmpty(Id))
            {
                Id = "custom";
            }
        }
    }

    public class CustomEndpointMiddleware : EndpointMiddleware<string, string>
    {
        private readonly RequestDelegate _next;

        public CustomEndpointMiddleware(RequestDelegate next, CustomEndpoint endpoint, IManagementOptions mgmtOptions, ILogger<CustomEndpointMiddleware> logger = null)
            : base(endpoint, mgmtOptions, logger: logger)
        {
            _next = next;
        }

        public Task Invoke(HttpContext context)
        {
            _logger.LogDebug("Info middleware Invoke({0})", context.Request.Path.Value);

            if (_endpoint.ShouldInvoke(_mgmtOptions, _logger))
            {
                return HandleInfoRequestAsync(context);
            }

            return Task.CompletedTask;
        }

        protected internal Task HandleInfoRequestAsync(HttpContext context)
        {
            var serialInfo = HandleRequest();
            _logger?.LogDebug("Returning: {0}", serialInfo);

            context.HandleContentNegotiation(_logger);
            return context.Response.WriteAsync(serialInfo);
        }
    }

    public class CustomEndpoint : AbstractEndpoint<string, string>
    {
        public CustomEndpoint(IEndpointOptions options)
            : base(options)
        {
        }

        public override string Invoke(string arg)
        {
            return "stuff";
        }
    }

@hananiel
Copy link
Contributor Author

Then to use this ...

   ...
  .UseEndpoints(endpoints =>
    {
        endpoints.MapActuatorEndpoint(type).RequireAuthorization("TestAuth");
        endpoints.MapCustomActuatorEndpoint(typeof(CustomEndpoint)).RequireAuthorization("TestAuth");
    }))

@jkonicki jkonicki added good first issue This is a good issue to get started contributing to the projects help wanted Looking for help from community for a resolution labels Jun 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Component/Management Issues related to Steeltoe Management (actuators) good first issue This is a good issue to get started contributing to the projects help wanted Looking for help from community for a resolution Type/enhancement
Projects
None yet
Development

No branches or pull requests

3 participants