Skip to content

Commit

Permalink
Shell for Inspection Data API
Browse files Browse the repository at this point in the history
  • Loading branch information
Christdej committed Oct 14, 2024
1 parent 35a4b91 commit 9d59765
Show file tree
Hide file tree
Showing 38 changed files with 1,631 additions and 150 deletions.
13 changes: 0 additions & 13 deletions .config/dotnet-tools.json

This file was deleted.

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,6 @@ bld/
# Python
*.venv
*venv

# Environment
*.env
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

IDA (Inspection Data Analyzer) is a repository for running pipelines to analyze data coming from various inspections.

When running locally the endpoint can be reached at
https://localhost:8100

TODO: At the moment the application is using FlotillaKV and Flotilla App Reg in Azure, needs to be changed to a new one for IDA

See [LocalFunctionProj](./functions/LocalFunctionProj/) for an example of how to set up your pipeline. You can also run this function locally by running
'func start' from the [LocalFunctionProj](./functions/LocalFunctionProj/) folder, and then going to 'http://localhost:7071/api/HttpExample' to trigger it.

Expand Down
10 changes: 10 additions & 0 deletions api/Configurations/AzureAdOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace api.Configurations
{
public class AzureAdOptions
{
public string ClientId { get; set; } = "";
public string ClientSecret { get; set; } = "";
public string TenantId { get; set; } = "";
public string StorageAccount { get; set; } = "";
}
}
95 changes: 95 additions & 0 deletions api/Configurations/ConfigurationBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
namespace api.Configurations
{
public static class ConfigurationBuilderExtensions
{
/// <summary>
/// Creates the AZURE_CLIENT_ID, AZURE_TENANT_ID and LOCAL_DEVUSERID configuration values for the
/// <see href="https://docs.microsoft.com/en-us/dotnet/api/azure.identity.environmentcredential?view=azure-dotnet">Environment Credentials</see>
/// used by the application when dockerized.
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static void AddAppSettingsEnvironmentVariables(this WebApplicationBuilder builder)
{
string? clientId = builder.Configuration
.GetSection("AzureAd")
.GetValue<string?>("ClientId");
if (clientId is not null)
{
Environment.SetEnvironmentVariable("AZURE_CLIENT_ID", clientId);
Console.WriteLine("'AZURE_CLIENT_ID' set to " + clientId);
}

string? tenantId = builder.Configuration
.GetSection("AzureAd")
.GetValue<string?>("TenantId");
if (tenantId is not null)
{
Environment.SetEnvironmentVariable("AZURE_TENANT_ID", tenantId);
Console.WriteLine("'AZURE_TENANT_ID' set to " + tenantId);
}

if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Local")
{

string? userId = builder.Configuration
.GetSection("Local")
.GetValue<string?>("DevUserId");
if (tenantId is not null)
{
Environment.SetEnvironmentVariable("LOCAL_DEVUSERID", userId);
Console.WriteLine("'LOCAL_DEVUSERID' set to " + userId);
}
}
}

/// <summary>
/// Creates if don't already exist/sets all the configuration variables present on the .env file for the
/// <see href="https://docs.microsoft.com/en-us/dotnet/api/azure.identity.environmentcredential?view=azure-dotnet">Environment Credentials</see>
/// used by the application when dockerized.
/// </summary>
/// <param name="builder"></param>
/// <param name="filePath"></param>
/// <returns></returns>
public static void AddDotEnvironmentVariables(this WebApplicationBuilder builder, string filePath)
{
if (!File.Exists(filePath)) return;

foreach (string line in File.ReadAllLines(filePath))
{
string[] parts = line.Split(
'=',
StringSplitOptions.RemoveEmptyEntries);

if (parts.Length == 0 || parts[0].StartsWith('#'))
continue;

Environment.SetEnvironmentVariable(parts[0], parts[1]);
}

builder.Configuration.AddEnvironmentVariables();
}

/// <summary>
/// Configures the logger used by the application
/// </summary>
/// <param name="builder"></param>
/// <returns></returns>
public static void ConfigureLogger(this WebApplicationBuilder builder)
{
builder.Logging.AddSimpleConsole(
options =>
{
options.IncludeScopes = true;
options.TimestampFormat = "yyyy-MM-dd HH:mm:ss - ";
options.ColorBehavior = Microsoft
.Extensions
.Logging
.Console
.LoggerColorBehavior
.Enabled;
}
);
}
}
}
127 changes: 127 additions & 0 deletions api/Configurations/CustomServiceConfigurations.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
using System.Reflection;
using Microsoft.OpenApi.Models;
using api.Models;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
namespace api.Configurations;

public static class CustomServiceConfigurations
{
public static IServiceCollection ConfigureDatabase(
this IServiceCollection services,
IConfiguration configuration
)
{
bool useInMemoryDatabase = configuration
.GetSection("Database")
.GetValue<bool>("UseInMemoryDatabase");

if (useInMemoryDatabase)
{
DbContextOptionsBuilder dbBuilder =
new DbContextOptionsBuilder<IdaDbContext>();
string sqlConnectionString = new SqliteConnectionStringBuilder
{
DataSource = "file::memory:",
Cache = SqliteCacheMode.Shared
}.ToString();

// In-memory sqlite requires an open connection throughout the whole lifetime of the database
var connectionToInMemorySqlite = new SqliteConnection(sqlConnectionString);
connectionToInMemorySqlite.Open();
dbBuilder.UseSqlite(connectionToInMemorySqlite);

using var context = new IdaDbContext(dbBuilder.Options);
context.Database.EnsureCreated();
// InitDb.PopulateDb(context);

// Setting splitting behavior explicitly to avoid warning
services.AddDbContext<IdaDbContext>(
options =>
options.UseSqlite(
sqlConnectionString,
o => o.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery)
)
);
}
else
{
string? connection = configuration["Database:PostgreSqlConnectionString"];
// Setting splitting behavior explicitly to avoid warning
services.AddDbContext<IdaDbContext>(
options =>
options.UseNpgsql(
connection,
o =>
{
o.UseQuerySplittingBehavior(QuerySplittingBehavior.SingleQuery);
o.EnableRetryOnFailure();
}
),
ServiceLifetime.Transient
);
}
return services;
}

public static IServiceCollection ConfigureSwagger(
this IServiceCollection services,
IConfiguration configuration
)
{
services.AddSwaggerGen(
c =>
{
// Add Authorization button in UI
c.AddSecurityDefinition(
"oauth2",
new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
TokenUrl = new Uri(
$"{configuration["AzureAd:Instance"]}/{configuration["AzureAd:TenantId"]}/oauth2/token"
),
AuthorizationUrl = new Uri(
$"{configuration["AzureAd:Instance"]}/{configuration["AzureAd:TenantId"]}/oauth2/authorize"
),
Scopes = new Dictionary<string, string>
{
{
$"api://{configuration["AzureAd:ClientId"]}/user_impersonation", "User Impersonation"
}
}
}
}
}
);
// Show which endpoints have authorization in the UI
c.AddSecurityRequirement(
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme, Id = "oauth2"
}
},
Array.Empty<string>()
}
}
);

// Make swagger use xml comments from functions
string xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
string xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
}
);

return services;
}
}
75 changes: 75 additions & 0 deletions api/Controllers/AnalysisController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using api.Models;
using api.Controllers.Models;
using api.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using api.Utilities;

namespace api.Controllers;

[ApiController]
[Route("[controller]")]
public class AnalysisController(ILogger<AnalysisController> logger, IAnalysisService analysisService) : ControllerBase
{
/// <summary>
/// List all analysis in database
/// </summary>
/// <remarks>
/// <para> This query gets all analysis </para>
/// </remarks>
[HttpGet]
[Authorize(Roles = Role.Any)]
[ProducesResponseType(typeof(IList<AnalysisResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<IList<AnalysisResponse>>> GetAllInspectionData([FromQuery] QueryParameters parameters)
{
PagedList<Analysis> analysis;
try
{
analysis = await analysisService.GetAnalysis(parameters);
var response = analysis.Select(analysis => new AnalysisResponse(analysis));
return Ok(response);
}
catch (Exception e)
{
logger.LogError(e, "Error during GET of analysis from database");
throw;
}
}

/// <summary>
/// Get Inspection by id from data database
/// </summary>
/// <remarks>
/// <para> This query gets inspection data by id</para>
/// </remarks>
[HttpGet]
[Authorize(Roles = Role.Any)]
[Route("id/{id}")]
[ProducesResponseType(typeof(InspectionDataResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(StatusCodes.Status403Forbidden)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<InspectionDataResponse>> GetInspectionDataById([FromRoute] string id)
{
try
{
var analysis = await analysisService.ReadById(id);
if (analysis == null)
{
return NotFound($"Could not find inspection data with id {id}");
}
var response = new AnalysisResponse(analysis);
return Ok(response);
}
catch (Exception e)
{
logger.LogError(e, "Error during GET of inspectionData from database");
throw;
}
}
}
Loading

0 comments on commit 9d59765

Please sign in to comment.