Skip to content

Commit

Permalink
Add database migration with yuniql (#224)
Browse files Browse the repository at this point in the history
  • Loading branch information
SandGrainOne authored Oct 18, 2024
1 parent 62a0ed0 commit d6f56ce
Show file tree
Hide file tree
Showing 22 changed files with 266 additions and 46 deletions.
8 changes: 6 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
FROM mcr.microsoft.com/dotnet/sdk:8.0.403-alpine3.20 AS build
WORKDIR Altinn.Profile/
WORKDIR /app

COPY src/Altinn.Profile/*.csproj ./src/Altinn.Profile/
COPY src/Altinn.Profile.Core/*.csproj ./src/Altinn.Profile.Core/
COPY src/Altinn.Profile.Integrations/*.csproj ./src/Altinn.Profile.Integrations/
RUN dotnet restore ./src/Altinn.Profile/Altinn.Profile.csproj

RUN dotnet restore ./src/Altinn.Profile/Altinn.Profile.csproj

COPY src ./src
RUN dotnet publish -c Release -o /app_output ./src/Altinn.Profile/Altinn.Profile.csproj

FROM mcr.microsoft.com/dotnet/aspnet:8.0.10-alpine3.20 AS final
EXPOSE 5030
WORKDIR /app

COPY --from=build /app_output .
COPY --from=build /app/src/Altinn.Profile.Integrations/Migration ./Migration

# setup the user and group
# the user will have no password, using shell /bin/false and using the group dotnet
RUN addgroup -g 3000 dotnet && adduser -u 1000 -G dotnet -D -s /bin/false dotnet

# update permissions of files if neccessary before becoming dotnet user
USER dotnet
RUN mkdir /tmp/logtelemetry
Expand Down
5 changes: 3 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,10 @@ services:
networks:
- altinnplatform_network
environment:
- ASPNETCORE_ENVIRONMENT=Development
- ASPNETCORE_ENVIRONMENT=Docker
- ASPNETCORE_URLS=http://+:5030
- GeneralSettings:BridgeApiEndpoint=https://at22.altinn.cloud/sblbridge/profile/api
- PostgreSqlSettings__AdminConnectionString=Host=host.docker.internal;Port=5432;Username=platform_profile_admin;Password={0};Database=profiledb
- PostgreSqlSettings__ConnectionString=Host=host.docker.internal;Port=5432;Username=platform_profile;Password={0};Database=profiledb
ports:
- "5030:5030"
build:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@
<PackageReference Include="AutoMapper" Version="13.0.1" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.1" />
<PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="8.0.8" />
<PackageReference Include="Yuniql.AspNetCore" Version="1.2.25" />
<PackageReference Include="Yuniql.PostgreSql" Version="1.3.15" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Altinn.Profile.Core\Altinn.Profile.Core.csproj" />
<InternalsVisibleTo Include="Altinn.Profile.Tests" />
<InternalsVisibleTo Include="Altinn.Profile.Tests" />
</ItemGroup>

<ItemGroup Condition="'$(Configuration)'=='Debug'">
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PrivateAssets>all</PrivateAssets>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,44 @@
using Microsoft.Extensions.Configuration;
using System.Diagnostics.CodeAnalysis;

using Microsoft.Extensions.Configuration;

namespace Altinn.Profile.Integrations.Extensions;

/// <summary>
/// Extension class for <see cref="IConfiguration"/> to add more members.
/// </summary>
[ExcludeFromCodeCoverage]
public static class ConfigurationExtensions
{
private const string ProfileDbAdminUserNameKey = "PostgreSqlSettings:ProfileDbAdminUserName";
private const string ProfileDbAdminPasswordKey = "PostgreSqlSettings:ProfileDbAdminPassword";
private const string ProfileDbConnectionStringKey = "PostgreSqlSettings:ProfileDbConnectionString";
private const string ConnectionStringKey = "PostgreSqlSettings:ConnectionString";
private const string ProfileDbPasswordKey = "PostgreSqlSettings:ProfileDbPwd";

Check warning on line 14 in src/Altinn.Profile.Integrations/Extensions/ConfigurationExtensions.cs

View workflow job for this annotation

GitHub Actions / Build, test & analyze

"password" detected here, make sure this is not a hard-coded credential. (https://rules.sonarsource.com/csharp/RSPEC-2068)

/// <summary>
/// Retrieves the database connection string from the configuration.
/// </summary>
/// <param name="config">The configuration instance containing the connection settings.</param>
/// <returns>The formatted database connection string if all required settings are present; otherwise, an empty string.</returns>
/// <remarks>
/// This method expects the configuration to contain the following keys:
/// This method expects IConfiguration to contain the following keys:
/// <list type="bullet">
/// <item><description><c>PostgreSqlSettings--ProfileDbAdminUserName</c></description></item>
/// <item><description><c>PostgreSqlSettings--ProfileDbAdminPassword</c></description></item>
/// <item><description><c>PostgreSqlSettings--ProfileDbConnectionString</c></description></item>
/// <item><description><c>PostgreSqlSettings:ConnectionString</c></description></item>
/// <item><description><c>PostgreSqlSettings:ProfileDbPwd</c></description></item>
/// </list>
/// The connection string is formatted using the administrator user name and password.
/// The connection string is expected to contain a placeholder for the password. The password is added to the connection string
/// through string formatting. The connection string value should be provieded as a value in the helm chart for profile. The password
/// is retrieved from the platform KeyVault. The names can therefore not be changed, but must follow the above naming conventions.
/// </remarks>
public static string GetDatabaseConnectionString(this IConfiguration config)
{
var adminUserName = config[ProfileDbAdminUserNameKey];
var adminPassword = config[ProfileDbAdminPasswordKey];
var connectionString = config[ProfileDbConnectionStringKey];
var connectionString = config[ConnectionStringKey];
var userPassword = config[ProfileDbPasswordKey];

if (string.IsNullOrWhiteSpace(adminUserName) ||
string.IsNullOrWhiteSpace(adminPassword) ||
if (string.IsNullOrWhiteSpace(userPassword) ||
string.IsNullOrWhiteSpace(connectionString))
{
return string.Empty;
}

return string.Format(connectionString, adminUserName, adminPassword);
return string.Format(connectionString, userPassword);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using System.Diagnostics.CodeAnalysis;

using Altinn.Profile.Integrations.Persistence;

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;

using Yuniql.AspNetCore;
using Yuniql.PostgreSql;

namespace Altinn.Profile.Integrations.Extensions;

/// <summary>
/// Extension class for web application
/// </summary>
[ExcludeFromCodeCoverage]
public static class WebApplicationExtensions
{
/// <summary>
/// Configure and set up db
/// </summary>
/// <param name="app">app</param>
/// <param name="isDevelopment">is environment dev</param>
/// <param name="config">the configuration collection</param>
public static void SetUpPostgreSql(this IApplicationBuilder app, bool isDevelopment, IConfiguration config)
{
PostgreSqlSettings? settings = config.GetSection("PostgreSQLSettings")
.Get<PostgreSqlSettings>()
?? throw new ArgumentNullException(nameof(config), "Required PostgreSQLSettings is missing from application configuration");

if (settings.EnableDBConnection)
{
ConsoleTraceService traceService = new() { IsDebugEnabled = true };

string connectionString = string.Format(settings.AdminConnectionString, settings.ProfileDbAdminPwd);

string fullWorkspacePath = isDevelopment ?
Path.Combine(Directory.GetParent(Environment.CurrentDirectory)!.FullName, settings.MigrationScriptPath) :
Path.Combine(Environment.CurrentDirectory, settings.MigrationScriptPath);

app.UseYuniql(
new PostgreSqlDataService(traceService),
new PostgreSqlBulkImportService(traceService),
traceService,
new Configuration
{
Workspace = fullWorkspacePath,
ConnectionString = connectionString,
IsAutoCreateDatabase = false,
IsDebug = settings.EnableDebug
});
}
}
}
2 changes: 2 additions & 0 deletions src/Altinn.Profile.Integrations/Migration/_draft/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The `_draft` directory
Scripts in progress. Scripts that you are currently working and have not moved to specific version directory yet. Executed every time after the latest version.
2 changes: 2 additions & 0 deletions src/Altinn.Profile.Integrations/Migration/_erase/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The `_erase` directory
Database cleanup scripts. Executed once only when you do `yuniql erase`.
2 changes: 2 additions & 0 deletions src/Altinn.Profile.Integrations/Migration/_init/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The `_init` directory
Initialization scripts. Executed once. This is called the first time you do `yuniql run`.
2 changes: 2 additions & 0 deletions src/Altinn.Profile.Integrations/Migration/_post/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The `_post` directory
Post migration scripts. Executed every time and always the last batch to run.
2 changes: 2 additions & 0 deletions src/Altinn.Profile.Integrations/Migration/_pre/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The `_pre` directory
Pre migration scripts. Executed every time before any version.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- Create schema if it doesn't exist
CREATE SCHEMA IF NOT EXISTS contact_and_reservation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- Grant access to the schema
GRANT ALL ON SCHEMA contact_and_reservation TO platform_profile_admin;
GRANT USAGE ON SCHEMA contact_and_reservation TO platform_profile;
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
-- Create schema if it doesn't exist
CREATE SCHEMA IF NOT EXISTS contact_and_reservation;

-- Grant access to the schema
GRANT ALL ON SCHEMA contact_and_reservation TO platform_profile_admin;
GRANT USAGE ON SCHEMA contact_and_reservation TO platform_profile;

-- Create table MailboxSupplier
CREATE TABLE IF NOT EXISTS contact_and_reservation.mailbox_supplier (
mailbox_supplier_id INT GENERATED ALWAYS AS IDENTITY (START WITH 1 INCREMENT BY 1) PRIMARY KEY,
Expand Down
77 changes: 77 additions & 0 deletions src/Altinn.Profile.Integrations/Persistence/ConsoleTraceService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System.Diagnostics.CodeAnalysis;

using Yuniql.Extensibility;

namespace Altinn.Profile.Integrations.Persistence;

/// <summary>
/// Copied from sample project.
/// </summary>
[ExcludeFromCodeCoverage]
public class ConsoleTraceService : ITraceService
{
/// <summary>
/// Debug enabled
/// </summary>
public bool IsDebugEnabled { get; set; } = false;

/// <inheritdoc/>>
public bool IsTraceSensitiveData { get; set; } = false;

/// <inheritdoc/>>
public bool IsTraceToFile { get; set; } = false;

/// <inheritdoc/>>
public bool IsTraceToDirectory { get; set; } = false;

/// <inheritdoc/>>
public string? TraceDirectory { get; set; }

/// <summary>
/// Info
/// </summary>
public void Info(string message, object? payload = null)
{
var traceMessage = $"INF {DateTime.UtcNow:o} {message}{Environment.NewLine}";
Console.Write(traceMessage);
}

/// <summary>
/// Error
/// </summary>
public void Error(string message, object? payload = null)
{
var traceMessage = $"ERR {DateTime.UtcNow:o} {message}{Environment.NewLine}";
Console.Write(traceMessage);
}

/// <summary>
/// Debug
/// </summary>
public void Debug(string message, object? payload = null)
{
if (IsDebugEnabled)
{
var traceMessage = $"DBG {DateTime.UtcNow:o} {message}{Environment.NewLine}";
Console.Write(traceMessage);
}
}

/// <summary>
/// Success
/// </summary>
public void Success(string message, object? payload = null)
{
var traceMessage = $"INF {DateTime.UtcNow:u} {message}{Environment.NewLine}";
Console.Write(traceMessage);
}

/// <summary>
/// Warn
/// </summary>
public void Warn(string message, object? payload = null)
{
var traceMessage = $"WRN {DateTime.UtcNow:o} {message}{Environment.NewLine}";
Console.Write(traceMessage);
}
}
47 changes: 47 additions & 0 deletions src/Altinn.Profile.Integrations/Persistence/PostgreSQLSettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
namespace Altinn.Profile.Integrations.Persistence;

/// <summary>
/// Settings for Postgre database
/// </summary>
public class PostgreSqlSettings
{
/// <summary>
/// Boolean indicating if database should be connected
/// </summary>
public bool EnableDBConnection { get; set; } = true;

/// <summary>
/// Path to migration scripts
/// </summary>
public string MigrationScriptPath { get; set; } = string.Empty;

/// <summary>
/// Connection string for the admin user of postgre db
/// </summary>
public string AdminConnectionString { get; set; } = string.Empty;

/// <summary>
/// Password for admin user for the postgre db
/// </summary>
public string ProfileDbAdminPwd { get; set; } = string.Empty;

/// <summary>
/// Connection string for app user the postgre db
/// </summary>
public string ConnectionString { get; set; } = string.Empty;

/// <summary>
/// Password for app user for the postgre db
/// </summary>
public string ProfileDbPwd { get; set; } = string.Empty;

/// <summary>
/// Gets or sets a value indicating whether to include parameter values in logging/tracing
/// </summary>
public bool LogParameters { get; set; } = false;

/// <summary>
/// Boolean indicating if connection to db should be in debug mode
/// </summary>
public bool EnableDebug { get; set; } = false;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using Altinn.Profile.Core.Integrations;
using System.Diagnostics.CodeAnalysis;

using Altinn.Profile.Core.Integrations;
using Altinn.Profile.Integrations.Extensions;
using Altinn.Profile.Integrations.Mappings;
using Altinn.Profile.Integrations.Persistence;
using Altinn.Profile.Integrations.Repositories;
using Altinn.Profile.Integrations.SblBridge;
Expand All @@ -16,6 +19,7 @@ namespace Altinn.Profile.Integrations;
/// <summary>
/// Extension class for <see cref="IServiceCollection"/>
/// </summary>
[ExcludeFromCodeCoverage]
public static class ServiceCollectionExtensions
{
/// <summary>
Expand Down Expand Up @@ -56,7 +60,7 @@ public static void AddRegisterService(this IServiceCollection services, IConfigu

services.AddDbContext<ProfileDbContext>(options => options.UseNpgsql(connectionString));

services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddAutoMapper(typeof(PersonContactDetailsProfile));

services.AddScoped<IPersonService, PersonService>();
services.AddScoped<IPersonRepository, PersonRepository>();
Expand Down
Loading

0 comments on commit d6f56ce

Please sign in to comment.