Skip to content
This repository has been archived by the owner on Aug 20, 2020. It is now read-only.

Commit

Permalink
Provide boilerplate for API
Browse files Browse the repository at this point in the history
Signed-off-by: Tom Kerkhove <kerkhove.tom@gmail.com>
  • Loading branch information
tomkerkhove committed Apr 8, 2020
1 parent 12530bd commit b08786c
Show file tree
Hide file tree
Showing 9 changed files with 378 additions and 1 deletion.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
# resource-discovery-sandbox
# Resource Discovery API Sandbox

Sandbox to build a resource discovery API built on top of Azure Resource Graph.

## License Information

This is licensed under The MIT License (MIT). Which means that you can use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the web application. But you always need to state that Tom Kerkhove is the original author of this web application.
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using GuardNet;
using Swashbuckle.AspNetCore.Filters;

namespace Promitor.ResourceDiscovery.Agent.Controllers
{
/// <summary>
/// API endpoint to check the health of the application.
/// </summary>
[ApiController]
[Route("api/v1/health")]
public class HealthController : ControllerBase
{
private readonly HealthCheckService _healthCheckService;

/// <summary>
/// Initializes a new instance of the <see cref="HealthController"/> class.
/// </summary>
/// <param name="healthCheckService">The service to provide the health of the API application.</param>
public HealthController(HealthCheckService healthCheckService)
{
Guard.NotNull(healthCheckService, nameof(healthCheckService));

_healthCheckService = healthCheckService;
}

/// <summary>
/// Get Health
/// </summary>
/// <remarks>Provides an indication about the health of the API.</remarks>
/// <response code="200">API is healthy</response>
/// <response code="503">API is unhealthy or in degraded state</response>
[HttpGet(Name = "Health_Get")]
[ProducesResponseType(typeof(HealthReport), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(HealthReport), StatusCodes.Status503ServiceUnavailable)]
[SwaggerResponseHeader(200, "RequestId", "string", "The header that has a request ID that uniquely identifies this operation call")]
[SwaggerResponseHeader(200, "X-Transaction-Id", "string", "The header that has the transaction ID is used to correlate multiple operation calls.")]
public async Task<IActionResult> Get()
{
HealthReport healthReport = await _healthCheckService.CheckHealthAsync();

if (healthReport?.Status == HealthStatus.Healthy)
{
return Ok(healthReport);
}
else
{
return StatusCode(StatusCodes.Status503ServiceUnavailable, healthReport);
}
}
}
}
19 changes: 19 additions & 0 deletions src/Promitor.ResourceDiscovery.Agent/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1.3-alpine3.10 AS base
WORKDIR /app
EXPOSE 80

FROM mcr.microsoft.com/dotnet/core/sdk:3.1.201-alpine3.10 AS build
WORKDIR /src
COPY ["Promitor.ResourceDiscovery.Agent.csproj", ""]

COPY . .
WORKDIR "/src/."
RUN dotnet build "Promitor.ResourceDiscovery.Agent.csproj" -c Release -o /app

FROM build AS publish
RUN dotnet publish "Promitor.ResourceDiscovery.Agent.csproj" -c Release -o /app

FROM base AS final
WORKDIR /app
COPY --from=publish /app .
ENTRYPOINT ["dotnet", "Promitor.ResourceDiscovery.Agent.dll"]
55 changes: 55 additions & 0 deletions src/Promitor.ResourceDiscovery.Agent/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace Promitor.ResourceDiscovery.Agent
{
public class Program
{
public static int Main(string[] args)
{
CreateHostBuilder(args)
.Build()
.Run();

return 0;
}

public static IHostBuilder CreateHostBuilder(string[] args)
{
IConfiguration configuration = CreateConfiguration(args);
IHostBuilder webHostBuilder = CreateHostBuilder(args, configuration);

return webHostBuilder;
}

private static IConfiguration CreateConfiguration(string[] args)
{
IConfigurationRoot configuration =
new ConfigurationBuilder()
.AddCommandLine(args)
.AddEnvironmentVariables()
.Build();

return configuration;
}

private static IHostBuilder CreateHostBuilder(string[] args, IConfiguration configuration)
{
string httpEndpointUrl = "http://+:" + configuration["ARCUS_HTTP_PORT"];
IHostBuilder webHostBuilder =
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration(configBuilder => configBuilder.AddConfiguration(configuration))
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.ConfigureKestrel(kestrelServerOptions => kestrelServerOptions.AddServerHeader = false)
.UseUrls(httpEndpointUrl)
.ConfigureLogging(logging => logging.AddConsole())
.UseStartup<Startup>();
});

return webHostBuilder;
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<DocumentationFile>Promitor.ResourceDiscovery.Agent.Open-Api.xml</DocumentationFile>
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
</PropertyGroup>



<ItemGroup>
<PackageReference Include="Guard.NET" Version="1.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Formatters.Json" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.1.3" />
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.9.10" />
<PackageReference Include="Arcus.WebApi.Correlation" Version="0.4.0" />
<PackageReference Include="Arcus.WebApi.Logging" Version="0.4.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.3.1" />
<PackageReference Include="Swashbuckle.AspNetCore.Filters" Version="5.1.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iisExpress": {
"applicationUrl": "http://localhost:54553/",
"sslPort": 44315
}
},
"profiles": {
"IIS Express": {
"commandName": "IISExpress",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"Promitor.ResourceDiscovery.Agent": {
"commandName": "Project",
"launchBrowser": true,
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:5001;http://localhost:5000"
},
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}",
"publishAllPorts": true,
"useSSL": false
}
}
}
122 changes: 122 additions & 0 deletions src/Promitor.ResourceDiscovery.Agent/Startup.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
using System;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Filters;
using Arcus.WebApi.Correlation;

namespace Promitor.ResourceDiscovery.Agent
{
public class Startup
{
private const string ApiName = "Promitor - Resource Discovery API";

/// <summary>
/// Initializes a new instance of the <see cref="Startup"/> class.
/// </summary>
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}

/// <summary>
/// Gets the configuration of key/value application properties.
/// </summary>
public IConfiguration Configuration { get; }

// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddRouting(options =>
{
options.LowercaseUrls = true;
options.LowercaseQueryStrings = true;
});
services.AddControllers(options =>
{
options.ReturnHttpNotAcceptable = true;
options.RespectBrowserAcceptHeader = true;

RestrictToJsonContentType(options);
AddEnumAsStringRepresentation(options);

});

services.AddHealthChecks();
services.AddCorrelation();

#if DEBUG
var openApiInformation = new OpenApiInfo
{
Title = ApiName,
Version = "v1"
};

services.AddSwaggerGen(swaggerGenerationOptions =>
{
swaggerGenerationOptions.SwaggerDoc("v1", openApiInformation);
swaggerGenerationOptions.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "Promitor.ResourceDiscovery.Agent.Open-Api.xml"));

swaggerGenerationOptions.OperationFilter<AddHeaderOperationFilter>("X-Transaction-Id", "Transaction ID is used to correlate multiple operation calls. A new transaction ID will be generated if not specified.", false);
swaggerGenerationOptions.OperationFilter<AddResponseHeadersFilter>();
});
#endif
}

private static void RestrictToJsonContentType(MvcOptions options)
{
var allButJsonInputFormatters = options.InputFormatters.Where(formatter => !(formatter is SystemTextJsonInputFormatter));
foreach (IInputFormatter inputFormatter in allButJsonInputFormatters)
{
options.InputFormatters.Remove(inputFormatter);
}

// Removing for text/plain, see https://docs.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-3.0#special-case-formatters
options.OutputFormatters.RemoveType<StringOutputFormatter>();
}

private static void AddEnumAsStringRepresentation(MvcOptions options)
{
var onlyJsonInputFormatters = options.InputFormatters.OfType<SystemTextJsonInputFormatter>();
foreach (SystemTextJsonInputFormatter inputFormatter in onlyJsonInputFormatters)
{
inputFormatter.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
}

var onlyJsonOutputFormatters = options.OutputFormatters.OfType<SystemTextJsonOutputFormatter>();
foreach (SystemTextJsonOutputFormatter outputFormatter in onlyJsonOutputFormatters)
{
outputFormatter.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
}
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseMiddleware<Arcus.WebApi.Logging.ExceptionHandlingMiddleware>();
app.UseCorrelation();
app.UseRouting();

app.UseSwagger(swaggerOptions =>
{
swaggerOptions.RouteTemplate = "api/{documentName}/docs.json";
});
app.UseSwaggerUI(swaggerUiOptions =>
{
swaggerUiOptions.SwaggerEndpoint("/api/v1/docs.json", ApiName);
swaggerUiOptions.RoutePrefix = "api/docs";
swaggerUiOptions.DocumentTitle = ApiName;
});
app.UseEndpoints(endpoints => endpoints.MapControllers());

}
}
}
25 changes: 25 additions & 0 deletions src/Promitor.ResourceDiscovery.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29609.76
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Promitor.ResourceDiscovery.Agent", "Promitor.ResourceDiscovery.Agent\Promitor.ResourceDiscovery.Agent.csproj", "{DE2E7C21-E261-4A78-8C63-7EEBD2CB3354}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{DE2E7C21-E261-4A78-8C63-7EEBD2CB3354}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DE2E7C21-E261-4A78-8C63-7EEBD2CB3354}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DE2E7C21-E261-4A78-8C63-7EEBD2CB3354}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DE2E7C21-E261-4A78-8C63-7EEBD2CB3354}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E9FC4DEA-2E5E-40A2-9399-B6AC11ECEC12}
EndGlobalSection
EndGlobal

0 comments on commit b08786c

Please sign in to comment.