From 04104da27f153bf14c993962cbcf4b94f4425384 Mon Sep 17 00:00:00 2001 From: Mirko Sekulic Date: Fri, 10 Jan 2025 21:05:24 +0100 Subject: [PATCH] feat: normalize deployments table (#14366) --- README.md | 1 + backend/Migrations.Dockerfile | 2 +- .../Infrastructure/ServiceRegistration.cs | 4 +- ...uidsTableAndDeploymentsColumns.Designer.cs | 242 ++++++++++++++++++ ...1305_AddBuidsTableAndDeploymentsColumns.cs | 117 +++++++++ ...21538_NormalizeDeploymentsData.Designer.cs | 242 ++++++++++++++++++ ...20250107121538_NormalizeDeploymentsData.cs | 23 ++ .../20250110192106_BuildsGrants.Designer.cs | 242 ++++++++++++++++++ .../Migrations/20250110192106_BuildsGrants.cs | 23 ++ .../DesignerdbContextModelSnapshot.cs | 73 +++++- .../SqlScripts/Builds/01-setup-grants.sql | 1 + .../normalization.sql | 38 +++ .../Repository/DeploymentRepository.cs | 164 ------------ .../ORMImplementation/AppScopesRepository.cs | 2 +- .../Data/DesignerdbContext.cs | 7 +- .../AppScopesConfiguration.cs | 4 +- .../BuildConfiguration.cs | 49 ++++ .../DeploymentConfiguration.cs | 20 +- .../ReleaseConfiguration.cs | 4 +- ...tRepository.cs => DeploymentRepository.cs} | 15 +- .../Mappers/AppScopesMapper.cs | 22 +- .../ORMImplementation/Mappers/BuildMapper.cs | 30 +++ .../Mappers/DeploymentMapper.cs | 31 ++- .../Mappers/ReleaseMapper.cs | 12 +- ...pScopesDbObject.cs => AppScopesDbModel.cs} | 2 +- .../ORMImplementation/Models/BuildDbModel.cs | 26 ++ .../{Deployment.cs => DeploymentDbModel.cs} | 10 +- .../Models/{Release.cs => ReleaseDbModel.cs} | 2 +- ...easeRepository.cs => ReleaseRepository.cs} | 4 +- .../Designer/Repository/ReleaseRepository.cs | 215 ---------------- .../Utils/AppScopesEntityAsserts.cs | 14 +- .../DeploymentEntityIntegrationTestsBase.cs | 18 +- .../CreateIntegrationTests.cs | 4 +- .../GetIntegrationTests.cs | 16 +- .../GetSingleIntegrationTests.cs | 9 +- .../CreateGetCompatibilityTests.cs | 55 ---- .../UpdateIntegrationTests.cs | 4 +- .../Utils/DeploymentEntityAsserts.cs | 22 +- .../Base/ReleaseEntityIntegrationTestsBase.cs | 2 +- .../CreateIntegrationTests.cs | 2 +- ...dStatusAndResultsFilterIntegrationTests.cs | 2 +- .../GetSingleIntegrationTests.cs | 2 +- ...tSucceededReleaseFromDbIntegrationTests.cs | 2 +- .../GetTopAndSortedIntegrationTests.cs | 4 +- .../CreateGetCompatibilityTests.cs | 56 ---- .../UpdateIntegrationTests.cs | 2 +- .../Utils/ReleaseEntityAsserts.cs | 8 +- .../Fixtures/DesignerDbFixtureExtensions.cs | 2 +- compose.yaml | 16 ++ 49 files changed, 1283 insertions(+), 584 deletions(-) create mode 100644 backend/src/Designer/Migrations/20250107121305_AddBuidsTableAndDeploymentsColumns.Designer.cs create mode 100644 backend/src/Designer/Migrations/20250107121305_AddBuidsTableAndDeploymentsColumns.cs create mode 100644 backend/src/Designer/Migrations/20250107121538_NormalizeDeploymentsData.Designer.cs create mode 100644 backend/src/Designer/Migrations/20250107121538_NormalizeDeploymentsData.cs create mode 100644 backend/src/Designer/Migrations/20250110192106_BuildsGrants.Designer.cs create mode 100644 backend/src/Designer/Migrations/20250110192106_BuildsGrants.cs create mode 100644 backend/src/Designer/Migrations/SqlScripts/Builds/01-setup-grants.sql create mode 100644 backend/src/Designer/Migrations/SqlScripts/DeploymentsNormalization/normalization.sql delete mode 100644 backend/src/Designer/Repository/DeploymentRepository.cs create mode 100644 backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/BuildConfiguration.cs rename backend/src/Designer/Repository/ORMImplementation/{ORMDeploymentRepository.cs => DeploymentRepository.cs} (70%) create mode 100644 backend/src/Designer/Repository/ORMImplementation/Mappers/BuildMapper.cs rename backend/src/Designer/Repository/ORMImplementation/Models/{AppScopesDbObject.cs => AppScopesDbModel.cs} (97%) create mode 100644 backend/src/Designer/Repository/ORMImplementation/Models/BuildDbModel.cs rename backend/src/Designer/Repository/ORMImplementation/Models/{Deployment.cs => DeploymentDbModel.cs} (66%) rename backend/src/Designer/Repository/ORMImplementation/Models/{Release.cs => ReleaseDbModel.cs} (93%) rename backend/src/Designer/Repository/ORMImplementation/{ORMReleaseRepository.cs => ReleaseRepository.cs} (96%) delete mode 100644 backend/src/Designer/Repository/ReleaseRepository.cs delete mode 100644 backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/OldRepositoryCompatibility/CreateGetCompatibilityTests.cs delete mode 100644 backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/OldRepositoryCompatibility/CreateGetCompatibilityTests.cs diff --git a/README.md b/README.md index 366fd92c22e..26cb6aea024 100644 --- a/README.md +++ b/README.md @@ -61,6 +61,7 @@ The development environment consist of several services defined in [compose.yaml - `studio-repos` which is [gitea][14] with some custom config. More [here](gitea/README.md). - `studio-db` which is a postgres database used by both `studio-designer` and `studio-repos`. - `database_migrations` which is a one-time task container designed to perform and complete database migrations before exiting. +- `pgadmin` which is a administration and development platform for PostgreSQL. - `redis` which is a redis cache used by designer. - `redis-commander` which is a ui for redis cache. diff --git a/backend/Migrations.Dockerfile b/backend/Migrations.Dockerfile index 03dff76c05b..227b6c86db8 100644 --- a/backend/Migrations.Dockerfile +++ b/backend/Migrations.Dockerfile @@ -11,7 +11,7 @@ ENV OidcLoginSettings__FetchClientIdAndSecretFromRootEnvFile=false ENV OidcLoginSettings__ClientId=dummyRequired ENV OidcLoginSettings__ClientSecret=dummyRequired -RUN dotnet ef migrations script --project src/Designer/Designer.csproj -o /app/migrations.sql +RUN dotnet ef migrations script --project src/Designer/Designer.csproj --idempotent -o /app/migrations.sql FROM alpine:3.21.0 AS final COPY --from=build /app/migrations.sql migrations.sql diff --git a/backend/src/Designer/Infrastructure/ServiceRegistration.cs b/backend/src/Designer/Infrastructure/ServiceRegistration.cs index 5d3b0607d8b..104b7d7ae9b 100644 --- a/backend/src/Designer/Infrastructure/ServiceRegistration.cs +++ b/backend/src/Designer/Infrastructure/ServiceRegistration.cs @@ -51,8 +51,8 @@ public static IServiceCollection RegisterServiceImplementations(this IServiceCol options.UseNpgsql(postgresSettings.FormattedConnectionString()); }); - services.AddScoped(); - services.AddScoped(); + services.AddScoped(); + services.AddScoped(); services.AddScoped(); services.AddTransient(); services.AddTransient(); diff --git a/backend/src/Designer/Migrations/20250107121305_AddBuidsTableAndDeploymentsColumns.Designer.cs b/backend/src/Designer/Migrations/20250107121305_AddBuidsTableAndDeploymentsColumns.Designer.cs new file mode 100644 index 00000000000..9bf605921d8 --- /dev/null +++ b/backend/src/Designer/Migrations/20250107121305_AddBuidsTableAndDeploymentsColumns.Designer.cs @@ -0,0 +1,242 @@ +// +using System; +using Altinn.Studio.Designer.Repository.ORMImplementation.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Altinn.Studio.Designer.Migrations +{ + [DbContext(typeof(DesignerdbContext))] + [Migration("20250107121305_AddBuidsTableAndDeploymentsColumns")] + partial class AddBuidsTableAndDeploymentsColumns + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseSerialColumns(modelBuilder); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.AppScopesDbObject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("App") + .IsRequired() + .HasColumnType("character varying") + .HasColumnName("app"); + + b.Property("Created") + .HasColumnType("timestamptz") + .HasColumnName("created"); + + b.Property("CreatedBy") + .HasColumnType("character varying") + .HasColumnName("created_by"); + + b.Property("LastModifiedBy") + .HasColumnType("character varying") + .HasColumnName("last_modified_by"); + + b.Property("Org") + .IsRequired() + .HasColumnType("character varying") + .HasColumnName("org"); + + b.Property("Scopes") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("scopes"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id") + .HasName("app_scopes_pkey"); + + b.HasIndex(new[] { "Org", "App" }, "idx_app_scopes_org_app") + .IsUnique(); + + b.ToTable("app_scopes", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.BuildDbObject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BuildType") + .HasColumnType("integer") + .HasColumnName("build_type"); + + b.Property("ExternalId") + .HasColumnType("character varying") + .HasColumnName("external_id"); + + b.Property("Finished") + .HasColumnType("timestamptz") + .HasColumnName("finished"); + + b.Property("Result") + .HasColumnType("character varying") + .HasColumnName("result"); + + b.Property("Started") + .HasColumnType("timestamptz") + .HasColumnName("started"); + + b.Property("Status") + .HasColumnType("character varying") + .HasColumnName("status"); + + b.HasKey("Id"); + + b.HasIndex("BuildType"); + + b.HasIndex("ExternalId", "BuildType") + .IsUnique(); + + b.ToTable("builds", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.Deployment", b => + { + b.Property("Sequenceno") + .ValueGeneratedOnAdd() + .HasColumnType("BIGSERIAL") + .HasColumnName("sequenceno"); + + NpgsqlPropertyBuilderExtensions.UseSerialColumn(b.Property("Sequenceno")); + + b.Property("App") + .HasColumnType("character varying") + .HasColumnName("app"); + + b.Property("Buildid") + .HasColumnType("character varying") + .HasColumnName("buildid"); + + b.Property("Buildresult") + .HasColumnType("character varying") + .HasColumnName("buildresult"); + + b.Property("Created") + .HasColumnType("timestamptz") + .HasColumnName("created"); + + b.Property("CreatedBy") + .HasColumnType("character varying") + .HasColumnName("created_by"); + + b.Property("Entity") + .HasColumnType("text") + .HasColumnName("entity"); + + b.Property("EnvName") + .HasColumnType("character varying") + .HasColumnName("envname"); + + b.Property("InternalBuildId") + .HasColumnType("bigint") + .HasColumnName("internal_build_id"); + + b.Property("Org") + .HasColumnType("character varying") + .HasColumnName("org"); + + b.Property("Tagname") + .HasColumnType("character varying") + .HasColumnName("tagname"); + + b.HasKey("Sequenceno") + .HasName("deployments_pkey"); + + b.HasIndex("InternalBuildId"); + + b.HasIndex(new[] { "Org", "App" }, "idx_deployments_org_app"); + + b.ToTable("deployments", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.Release", b => + { + b.Property("Sequenceno") + .ValueGeneratedOnAdd() + .HasColumnType("BIGSERIAL") + .HasColumnName("sequenceno"); + + NpgsqlPropertyBuilderExtensions.UseSerialColumn(b.Property("Sequenceno")); + + b.Property("App") + .HasColumnType("character varying") + .HasColumnName("app"); + + b.Property("Buildid") + .HasColumnType("character varying") + .HasColumnName("buildid"); + + b.Property("Buildresult") + .HasColumnType("character varying") + .HasColumnName("buildresult"); + + b.Property("Buildstatus") + .HasColumnType("character varying") + .HasColumnName("buildstatus"); + + b.Property("Created") + .HasColumnType("timestamptz") + .HasColumnName("created"); + + b.Property("Entity") + .HasColumnType("text") + .HasColumnName("entity"); + + b.Property("Org") + .HasColumnType("character varying") + .HasColumnName("org"); + + b.Property("Tagname") + .HasColumnType("character varying") + .HasColumnName("tagname"); + + b.HasKey("Sequenceno") + .HasName("releases_pkey"); + + b.HasIndex(new[] { "Org", "App" }, "idx_releases_org_app"); + + b.ToTable("releases", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.Deployment", b => + { + b.HasOne("Altinn.Studio.Designer.Repository.ORMImplementation.Models.BuildDbObject", "Build") + .WithMany() + .HasForeignKey("InternalBuildId") + .HasConstraintName("fk_deployments_builds_buildid"); + + b.Navigation("Build"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Designer/Migrations/20250107121305_AddBuidsTableAndDeploymentsColumns.cs b/backend/src/Designer/Migrations/20250107121305_AddBuidsTableAndDeploymentsColumns.cs new file mode 100644 index 00000000000..ddd7005b54d --- /dev/null +++ b/backend/src/Designer/Migrations/20250107121305_AddBuidsTableAndDeploymentsColumns.cs @@ -0,0 +1,117 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Altinn.Studio.Designer.Migrations +{ + /// + public partial class AddBuidsTableAndDeploymentsColumns : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.AddColumn( + name: "created_by", + schema: "designer", + table: "deployments", + type: "character varying", + nullable: true); + + migrationBuilder.AddColumn( + name: "envname", + schema: "designer", + table: "deployments", + type: "character varying", + nullable: true); + + migrationBuilder.AddColumn( + name: "internal_build_id", + schema: "designer", + table: "deployments", + type: "bigint", + nullable: true); + + migrationBuilder.CreateTable( + name: "builds", + schema: "designer", + columns: table => new + { + id = table.Column(type: "bigint", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + external_id = table.Column(type: "character varying", nullable: true), + status = table.Column(type: "character varying", nullable: true), + result = table.Column(type: "character varying", nullable: true), + build_type = table.Column(type: "integer", nullable: false), + started = table.Column(type: "timestamptz", nullable: true), + finished = table.Column(type: "timestamptz", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_builds", x => x.id); + }); + + migrationBuilder.CreateIndex( + name: "IX_deployments_internal_build_id", + schema: "designer", + table: "deployments", + column: "internal_build_id"); + + migrationBuilder.CreateIndex( + name: "IX_builds_build_type", + schema: "designer", + table: "builds", + column: "build_type"); + + migrationBuilder.CreateIndex( + name: "IX_builds_external_id_build_type", + schema: "designer", + table: "builds", + columns: new[] { "external_id", "build_type" }, + unique: true); + + migrationBuilder.AddForeignKey( + name: "fk_deployments_builds_buildid", + schema: "designer", + table: "deployments", + column: "internal_build_id", + principalSchema: "designer", + principalTable: "builds", + principalColumn: "id"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropForeignKey( + name: "fk_deployments_builds_buildid", + schema: "designer", + table: "deployments"); + + migrationBuilder.DropTable( + name: "builds", + schema: "designer"); + + migrationBuilder.DropIndex( + name: "IX_deployments_internal_build_id", + schema: "designer", + table: "deployments"); + + migrationBuilder.DropColumn( + name: "created_by", + schema: "designer", + table: "deployments"); + + migrationBuilder.DropColumn( + name: "envname", + schema: "designer", + table: "deployments"); + + migrationBuilder.DropColumn( + name: "internal_build_id", + schema: "designer", + table: "deployments"); + } + } +} diff --git a/backend/src/Designer/Migrations/20250107121538_NormalizeDeploymentsData.Designer.cs b/backend/src/Designer/Migrations/20250107121538_NormalizeDeploymentsData.Designer.cs new file mode 100644 index 00000000000..64213bdb92e --- /dev/null +++ b/backend/src/Designer/Migrations/20250107121538_NormalizeDeploymentsData.Designer.cs @@ -0,0 +1,242 @@ +// +using System; +using Altinn.Studio.Designer.Repository.ORMImplementation.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Altinn.Studio.Designer.Migrations +{ + [DbContext(typeof(DesignerdbContext))] + [Migration("20250107121538_NormalizeDeploymentsData")] + partial class NormalizeDeploymentsData + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseSerialColumns(modelBuilder); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.AppScopesDbObject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("App") + .IsRequired() + .HasColumnType("character varying") + .HasColumnName("app"); + + b.Property("Created") + .HasColumnType("timestamptz") + .HasColumnName("created"); + + b.Property("CreatedBy") + .HasColumnType("character varying") + .HasColumnName("created_by"); + + b.Property("LastModifiedBy") + .HasColumnType("character varying") + .HasColumnName("last_modified_by"); + + b.Property("Org") + .IsRequired() + .HasColumnType("character varying") + .HasColumnName("org"); + + b.Property("Scopes") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("scopes"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id") + .HasName("app_scopes_pkey"); + + b.HasIndex(new[] { "Org", "App" }, "idx_app_scopes_org_app") + .IsUnique(); + + b.ToTable("app_scopes", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.BuildDbObject", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BuildType") + .HasColumnType("integer") + .HasColumnName("build_type"); + + b.Property("ExternalId") + .HasColumnType("character varying") + .HasColumnName("external_id"); + + b.Property("Finished") + .HasColumnType("timestamptz") + .HasColumnName("finished"); + + b.Property("Result") + .HasColumnType("character varying") + .HasColumnName("result"); + + b.Property("Started") + .HasColumnType("timestamptz") + .HasColumnName("started"); + + b.Property("Status") + .HasColumnType("character varying") + .HasColumnName("status"); + + b.HasKey("Id"); + + b.HasIndex("BuildType"); + + b.HasIndex("ExternalId", "BuildType") + .IsUnique(); + + b.ToTable("builds", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.Deployment", b => + { + b.Property("Sequenceno") + .ValueGeneratedOnAdd() + .HasColumnType("BIGSERIAL") + .HasColumnName("sequenceno"); + + NpgsqlPropertyBuilderExtensions.UseSerialColumn(b.Property("Sequenceno")); + + b.Property("App") + .HasColumnType("character varying") + .HasColumnName("app"); + + b.Property("Buildid") + .HasColumnType("character varying") + .HasColumnName("buildid"); + + b.Property("Buildresult") + .HasColumnType("character varying") + .HasColumnName("buildresult"); + + b.Property("Created") + .HasColumnType("timestamptz") + .HasColumnName("created"); + + b.Property("CreatedBy") + .HasColumnType("character varying") + .HasColumnName("created_by"); + + b.Property("Entity") + .HasColumnType("text") + .HasColumnName("entity"); + + b.Property("EnvName") + .HasColumnType("character varying") + .HasColumnName("envname"); + + b.Property("InternalBuildId") + .HasColumnType("bigint") + .HasColumnName("internal_build_id"); + + b.Property("Org") + .HasColumnType("character varying") + .HasColumnName("org"); + + b.Property("Tagname") + .HasColumnType("character varying") + .HasColumnName("tagname"); + + b.HasKey("Sequenceno") + .HasName("deployments_pkey"); + + b.HasIndex("InternalBuildId"); + + b.HasIndex(new[] { "Org", "App" }, "idx_deployments_org_app"); + + b.ToTable("deployments", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.Release", b => + { + b.Property("Sequenceno") + .ValueGeneratedOnAdd() + .HasColumnType("BIGSERIAL") + .HasColumnName("sequenceno"); + + NpgsqlPropertyBuilderExtensions.UseSerialColumn(b.Property("Sequenceno")); + + b.Property("App") + .HasColumnType("character varying") + .HasColumnName("app"); + + b.Property("Buildid") + .HasColumnType("character varying") + .HasColumnName("buildid"); + + b.Property("Buildresult") + .HasColumnType("character varying") + .HasColumnName("buildresult"); + + b.Property("Buildstatus") + .HasColumnType("character varying") + .HasColumnName("buildstatus"); + + b.Property("Created") + .HasColumnType("timestamptz") + .HasColumnName("created"); + + b.Property("Entity") + .HasColumnType("text") + .HasColumnName("entity"); + + b.Property("Org") + .HasColumnType("character varying") + .HasColumnName("org"); + + b.Property("Tagname") + .HasColumnType("character varying") + .HasColumnName("tagname"); + + b.HasKey("Sequenceno") + .HasName("releases_pkey"); + + b.HasIndex(new[] { "Org", "App" }, "idx_releases_org_app"); + + b.ToTable("releases", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.Deployment", b => + { + b.HasOne("Altinn.Studio.Designer.Repository.ORMImplementation.Models.BuildDbObject", "Build") + .WithMany() + .HasForeignKey("InternalBuildId") + .HasConstraintName("fk_deployments_builds_buildid"); + + b.Navigation("Build"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Designer/Migrations/20250107121538_NormalizeDeploymentsData.cs b/backend/src/Designer/Migrations/20250107121538_NormalizeDeploymentsData.cs new file mode 100644 index 00000000000..df801941b54 --- /dev/null +++ b/backend/src/Designer/Migrations/20250107121538_NormalizeDeploymentsData.cs @@ -0,0 +1,23 @@ +using Altinn.Studio.Designer.Migrations.SqlScripts; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Altinn.Studio.Designer.Migrations +{ + /// + public partial class NormalizeDeploymentsData : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(SqlScriptsReadHelper.ReadSqlScript("DeploymentsNormalization/normalization.sql")); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + // No data being removed so no need to implement this method + } + } +} diff --git a/backend/src/Designer/Migrations/20250110192106_BuildsGrants.Designer.cs b/backend/src/Designer/Migrations/20250110192106_BuildsGrants.Designer.cs new file mode 100644 index 00000000000..7f84d9a5050 --- /dev/null +++ b/backend/src/Designer/Migrations/20250110192106_BuildsGrants.Designer.cs @@ -0,0 +1,242 @@ +// +using System; +using Altinn.Studio.Designer.Repository.ORMImplementation.Data; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Altinn.Studio.Designer.Migrations +{ + [DbContext(typeof(DesignerdbContext))] + [Migration("20250110192106_BuildsGrants")] + partial class BuildsGrants + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "9.0.0") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseSerialColumns(modelBuilder); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.AppScopesDbModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("App") + .IsRequired() + .HasColumnType("character varying") + .HasColumnName("app"); + + b.Property("Created") + .HasColumnType("timestamptz") + .HasColumnName("created"); + + b.Property("CreatedBy") + .HasColumnType("character varying") + .HasColumnName("created_by"); + + b.Property("LastModifiedBy") + .HasColumnType("character varying") + .HasColumnName("last_modified_by"); + + b.Property("Org") + .IsRequired() + .HasColumnType("character varying") + .HasColumnName("org"); + + b.Property("Scopes") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("scopes"); + + b.Property("Version") + .IsConcurrencyToken() + .ValueGeneratedOnAddOrUpdate() + .HasColumnType("xid") + .HasColumnName("xmin"); + + b.HasKey("Id") + .HasName("app_scopes_pkey"); + + b.HasIndex(new[] { "Org", "App" }, "idx_app_scopes_org_app") + .IsUnique(); + + b.ToTable("app_scopes", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.BuildDbModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BuildType") + .HasColumnType("integer") + .HasColumnName("build_type"); + + b.Property("ExternalId") + .HasColumnType("character varying") + .HasColumnName("external_id"); + + b.Property("Finished") + .HasColumnType("timestamptz") + .HasColumnName("finished"); + + b.Property("Result") + .HasColumnType("character varying") + .HasColumnName("result"); + + b.Property("Started") + .HasColumnType("timestamptz") + .HasColumnName("started"); + + b.Property("Status") + .HasColumnType("character varying") + .HasColumnName("status"); + + b.HasKey("Id"); + + b.HasIndex("BuildType"); + + b.HasIndex("ExternalId", "BuildType") + .IsUnique(); + + b.ToTable("builds", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.DeploymentDbModel", b => + { + b.Property("Sequenceno") + .ValueGeneratedOnAdd() + .HasColumnType("BIGSERIAL") + .HasColumnName("sequenceno"); + + NpgsqlPropertyBuilderExtensions.UseSerialColumn(b.Property("Sequenceno")); + + b.Property("App") + .HasColumnType("character varying") + .HasColumnName("app"); + + b.Property("Buildid") + .HasColumnType("character varying") + .HasColumnName("buildid"); + + b.Property("Buildresult") + .HasColumnType("character varying") + .HasColumnName("buildresult"); + + b.Property("Created") + .HasColumnType("timestamptz") + .HasColumnName("created"); + + b.Property("CreatedBy") + .HasColumnType("character varying") + .HasColumnName("created_by"); + + b.Property("Entity") + .HasColumnType("text") + .HasColumnName("entity"); + + b.Property("EnvName") + .HasColumnType("character varying") + .HasColumnName("envname"); + + b.Property("InternalBuildId") + .HasColumnType("bigint") + .HasColumnName("internal_build_id"); + + b.Property("Org") + .HasColumnType("character varying") + .HasColumnName("org"); + + b.Property("Tagname") + .HasColumnType("character varying") + .HasColumnName("tagname"); + + b.HasKey("Sequenceno") + .HasName("deployments_pkey"); + + b.HasIndex("InternalBuildId"); + + b.HasIndex(new[] { "Org", "App" }, "idx_deployments_org_app"); + + b.ToTable("deployments", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.ReleaseDbModel", b => + { + b.Property("Sequenceno") + .ValueGeneratedOnAdd() + .HasColumnType("BIGSERIAL") + .HasColumnName("sequenceno"); + + NpgsqlPropertyBuilderExtensions.UseSerialColumn(b.Property("Sequenceno")); + + b.Property("App") + .HasColumnType("character varying") + .HasColumnName("app"); + + b.Property("Buildid") + .HasColumnType("character varying") + .HasColumnName("buildid"); + + b.Property("Buildresult") + .HasColumnType("character varying") + .HasColumnName("buildresult"); + + b.Property("Buildstatus") + .HasColumnType("character varying") + .HasColumnName("buildstatus"); + + b.Property("Created") + .HasColumnType("timestamptz") + .HasColumnName("created"); + + b.Property("Entity") + .HasColumnType("text") + .HasColumnName("entity"); + + b.Property("Org") + .HasColumnType("character varying") + .HasColumnName("org"); + + b.Property("Tagname") + .HasColumnType("character varying") + .HasColumnName("tagname"); + + b.HasKey("Sequenceno") + .HasName("releases_pkey"); + + b.HasIndex(new[] { "Org", "App" }, "idx_releases_org_app"); + + b.ToTable("releases", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.DeploymentDbModel", b => + { + b.HasOne("Altinn.Studio.Designer.Repository.ORMImplementation.Models.BuildDbModel", "Build") + .WithMany() + .HasForeignKey("InternalBuildId") + .HasConstraintName("fk_deployments_builds_buildid"); + + b.Navigation("Build"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/Designer/Migrations/20250110192106_BuildsGrants.cs b/backend/src/Designer/Migrations/20250110192106_BuildsGrants.cs new file mode 100644 index 00000000000..1a94a537ab3 --- /dev/null +++ b/backend/src/Designer/Migrations/20250110192106_BuildsGrants.cs @@ -0,0 +1,23 @@ +using Altinn.Studio.Designer.Migrations.SqlScripts; +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Altinn.Studio.Designer.Migrations +{ + /// + public partial class BuildsGrants : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.Sql(SqlScriptsReadHelper.ReadSqlScript("Builds/01-setup-grants.sql")); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + + } + } +} diff --git a/backend/src/Designer/Migrations/DesignerdbContextModelSnapshot.cs b/backend/src/Designer/Migrations/DesignerdbContextModelSnapshot.cs index 56876fa4667..c1d9a32c5ef 100644 --- a/backend/src/Designer/Migrations/DesignerdbContextModelSnapshot.cs +++ b/backend/src/Designer/Migrations/DesignerdbContextModelSnapshot.cs @@ -22,7 +22,7 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlModelBuilderExtensions.UseSerialColumns(modelBuilder); - modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.AppScopesDbObject", b => + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.AppScopesDbModel", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -73,7 +73,50 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("app_scopes", "designer"); }); - modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.Deployment", b => + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.BuildDbModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("bigint") + .HasColumnName("id"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("BuildType") + .HasColumnType("integer") + .HasColumnName("build_type"); + + b.Property("ExternalId") + .HasColumnType("character varying") + .HasColumnName("external_id"); + + b.Property("Finished") + .HasColumnType("timestamptz") + .HasColumnName("finished"); + + b.Property("Result") + .HasColumnType("character varying") + .HasColumnName("result"); + + b.Property("Started") + .HasColumnType("timestamptz") + .HasColumnName("started"); + + b.Property("Status") + .HasColumnType("character varying") + .HasColumnName("status"); + + b.HasKey("Id"); + + b.HasIndex("BuildType"); + + b.HasIndex("ExternalId", "BuildType") + .IsUnique(); + + b.ToTable("builds", "designer"); + }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.DeploymentDbModel", b => { b.Property("Sequenceno") .ValueGeneratedOnAdd() @@ -98,10 +141,22 @@ protected override void BuildModel(ModelBuilder modelBuilder) .HasColumnType("timestamptz") .HasColumnName("created"); + b.Property("CreatedBy") + .HasColumnType("character varying") + .HasColumnName("created_by"); + b.Property("Entity") .HasColumnType("text") .HasColumnName("entity"); + b.Property("EnvName") + .HasColumnType("character varying") + .HasColumnName("envname"); + + b.Property("InternalBuildId") + .HasColumnType("bigint") + .HasColumnName("internal_build_id"); + b.Property("Org") .HasColumnType("character varying") .HasColumnName("org"); @@ -113,12 +168,14 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.HasKey("Sequenceno") .HasName("deployments_pkey"); + b.HasIndex("InternalBuildId"); + b.HasIndex(new[] { "Org", "App" }, "idx_deployments_org_app"); b.ToTable("deployments", "designer"); }); - modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.Release", b => + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.ReleaseDbModel", b => { b.Property("Sequenceno") .ValueGeneratedOnAdd() @@ -166,6 +223,16 @@ protected override void BuildModel(ModelBuilder modelBuilder) b.ToTable("releases", "designer"); }); + + modelBuilder.Entity("Altinn.Studio.Designer.Repository.ORMImplementation.Models.DeploymentDbModel", b => + { + b.HasOne("Altinn.Studio.Designer.Repository.ORMImplementation.Models.BuildDbModel", "Build") + .WithMany() + .HasForeignKey("InternalBuildId") + .HasConstraintName("fk_deployments_builds_buildid"); + + b.Navigation("Build"); + }); #pragma warning restore 612, 618 } } diff --git a/backend/src/Designer/Migrations/SqlScripts/Builds/01-setup-grants.sql b/backend/src/Designer/Migrations/SqlScripts/Builds/01-setup-grants.sql new file mode 100644 index 00000000000..a0da32232f8 --- /dev/null +++ b/backend/src/Designer/Migrations/SqlScripts/Builds/01-setup-grants.sql @@ -0,0 +1 @@ +GRANT SELECT,INSERT,UPDATE,DELETE ON ALL TABLES IN SCHEMA designer TO designer; diff --git a/backend/src/Designer/Migrations/SqlScripts/DeploymentsNormalization/normalization.sql b/backend/src/Designer/Migrations/SqlScripts/DeploymentsNormalization/normalization.sql new file mode 100644 index 00000000000..bf741301dfe --- /dev/null +++ b/backend/src/Designer/Migrations/SqlScripts/DeploymentsNormalization/normalization.sql @@ -0,0 +1,38 @@ +DO $$ +DECLARE + rec RECORD; + entity_data JSONB; + build_data JSONB; + new_build_id BIGINT; + build_type CONSTANT INTEGER := 0; +BEGIN + FOR rec IN + SELECT sequenceno, entity::jsonb AS entity_data + FROM designer.deployments + LOOP + entity_data := rec.entity_data; + build_data := entity_data->'build'; + + BEGIN -- Inner block for exception handling only + INSERT INTO designer.builds (external_id, status, result, build_type, started, finished) + VALUES ( + build_data->>'id', + build_data->>'status', + build_data->>'result', + build_type, + NULLIF(build_data->>'started', 'null')::TIMESTAMPTZ, + NULLIF(build_data->>'finished', 'null')::TIMESTAMPTZ + ) + RETURNING id INTO new_build_id; + + UPDATE designer.deployments + SET internal_build_id = new_build_id, + created_by = entity_data->>'createdBy', + envname = entity_data->>'envName' + WHERE sequenceno = rec.sequenceno; + + EXCEPTION WHEN OTHERS THEN + RAISE NOTICE 'Error processing deployment sequenceno %: %', rec.sequenceno, SQLERRM; + END; + END LOOP; +END $$; diff --git a/backend/src/Designer/Repository/DeploymentRepository.cs b/backend/src/Designer/Repository/DeploymentRepository.cs deleted file mode 100644 index bc25f553fff..00000000000 --- a/backend/src/Designer/Repository/DeploymentRepository.cs +++ /dev/null @@ -1,164 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Threading.Tasks; -using Altinn.Studio.Designer.Configuration; -using Altinn.Studio.Designer.Repository.Models; -using Altinn.Studio.Designer.TypedHttpClients.AzureDevOps.Enums; -using Altinn.Studio.Designer.ViewModels.Request; -using Altinn.Studio.Designer.ViewModels.Request.Enums; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Npgsql; -using NpgsqlTypes; - -namespace Altinn.Studio.Designer.Repository -{ - /// - /// Handles deployment repository. - /// - [ExcludeFromCodeCoverage] - public class DeploymentRepository : IDeploymentRepository - { - private readonly string insertDeploymentSql = "call designer.insert_deployment(@buildid, @tagName, @org, @app, @buildresult, @created, @entity)"; - private readonly string getDeploymentsSql = "select designer.get_deployments(@_org, @_app, @_limit, @_order_asc_desc)"; - private readonly string getDeploymentSql = "select designer.get_deployment(@_org, @_buildid)"; - private readonly string updateDeploymentBuildSql = "call designer.update_deployment_build(@_org, @_buildid, @_buildresult, @_entity)"; - private readonly string _connectionString; - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - public DeploymentRepository(PostgreSQLSettings postgresSettings, ILogger logger) - { - _connectionString = postgresSettings.FormattedConnectionString(); - _logger = logger; - } - - /// - public async Task Create(DeploymentEntity deploymentEntity) - { - try - { - using NpgsqlConnection conn = new(_connectionString); - await conn.OpenAsync(); - - using NpgsqlCommand pgcom = new(insertDeploymentSql, conn); - pgcom.Parameters.AddWithValue("buildid", deploymentEntity.Build.Id); - pgcom.Parameters.AddWithValue("tagName", deploymentEntity.TagName); - pgcom.Parameters.AddWithValue("org", deploymentEntity.Org); - pgcom.Parameters.AddWithValue("app", deploymentEntity.App); - pgcom.Parameters.AddWithValue("buildresult", deploymentEntity.Build.Result.ToEnumMemberAttributeValue()); - pgcom.Parameters.AddWithValue("created", deploymentEntity.Created); - pgcom.Parameters.AddWithValue("entity", JsonString(deploymentEntity)); - - await pgcom.ExecuteNonQueryAsync(); - - return deploymentEntity; - } - catch (Exception e) - { - _logger.LogError(e, "DeploymentRepository // Create // Exception"); - throw; - } - } - - /// - public async Task> Get(string org, string app, DocumentQueryModel query) - { - List searchResult = new(); - - try - { - using NpgsqlConnection conn = new(_connectionString); - await conn.OpenAsync(); - - using NpgsqlCommand pgcom = new(getDeploymentsSql, conn); - pgcom.Parameters.AddWithValue("_org", NpgsqlDbType.Varchar, org); - pgcom.Parameters.AddWithValue("_app", NpgsqlDbType.Varchar, app); - pgcom.Parameters.AddWithValue("_limit", NpgsqlDbType.Integer, query.Top ?? int.MaxValue); - pgcom.Parameters.AddWithValue("_order_asc_desc", NpgsqlDbType.Varchar, query.SortDirection == SortDirection.Ascending ? "asc" : "desc"); - - using (NpgsqlDataReader reader = pgcom.ExecuteReader()) - { - while (reader.Read()) - { - DeploymentEntity deploymentEntity = Deserialize(reader[0].ToString()); - searchResult.Add(deploymentEntity); - } - } - - return searchResult; - } - catch (Exception e) - { - _logger.LogError(e, "DeploymentRepository // Get(DocumentQueryModel) // Exception"); - throw; - } - } - - /// - public async Task Get(string org, string buildId) - { - try - { - DeploymentEntity deploymentEntity = null; - using NpgsqlConnection conn = new(_connectionString); - await conn.OpenAsync(); - - using NpgsqlCommand pgcom = new(getDeploymentSql, conn); - pgcom.Parameters.AddWithValue("_org", NpgsqlDbType.Varchar, org); - pgcom.Parameters.AddWithValue("_buildid", NpgsqlDbType.Varchar, buildId); - - using (NpgsqlDataReader reader = pgcom.ExecuteReader()) - { - while (reader.Read()) - { - deploymentEntity = Deserialize(reader[0].ToString()); - } - } - - return deploymentEntity; - } - catch (Exception e) - { - _logger.LogError(e, "DeploymentRepository // Get(string org, string buildId) // Exception"); - throw; - } - } - - /// - public async Task Update(DeploymentEntity deploymentEntity) - { - try - { - using NpgsqlConnection conn = new(_connectionString); - await conn.OpenAsync(); - - using NpgsqlCommand pgcom = new(updateDeploymentBuildSql, conn); - pgcom.Parameters.AddWithValue("_org", deploymentEntity.Org); - pgcom.Parameters.AddWithValue("_buildid", deploymentEntity.Build.Id); - pgcom.Parameters.AddWithValue("_buildresult", deploymentEntity.Build.Result.ToEnumMemberAttributeValue()); - pgcom.Parameters.AddWithValue("_entity", JsonString(deploymentEntity)); - - await pgcom.ExecuteNonQueryAsync(); - } - catch (Exception e) - { - _logger.LogError(e, "DeploymentRepository // Update // Exception"); - throw; - } - } - - private static string JsonString(DeploymentEntity deploymentEntity) - { - return JsonConvert.SerializeObject(deploymentEntity); - } - - private static DeploymentEntity Deserialize(string deploymentEntityString) - { - return JsonConvert.DeserializeObject(deploymentEntityString); - } - } -} diff --git a/backend/src/Designer/Repository/ORMImplementation/AppScopesRepository.cs b/backend/src/Designer/Repository/ORMImplementation/AppScopesRepository.cs index 3320834b210..2cd622c2968 100644 --- a/backend/src/Designer/Repository/ORMImplementation/AppScopesRepository.cs +++ b/backend/src/Designer/Repository/ORMImplementation/AppScopesRepository.cs @@ -29,7 +29,7 @@ public async Task GetAppScopesAsync(AltinnRepoContext repoConte public async Task UpsertAppScopesAsync(AppScopesEntity appScopesEntity, CancellationToken cancellationToken = default) { - AppScopesDbObject existing = await _dbContext.AppScopes.AsNoTracking().SingleOrDefaultAsync(a => a.Org == appScopesEntity.Org && a.App == appScopesEntity.App, cancellationToken); + AppScopesDbModel existing = await _dbContext.AppScopes.AsNoTracking().SingleOrDefaultAsync(a => a.Org == appScopesEntity.Org && a.App == appScopesEntity.App, cancellationToken); var dbObject = existing is null ? AppScopesMapper.MapToDbModel(appScopesEntity) diff --git a/backend/src/Designer/Repository/ORMImplementation/Data/DesignerdbContext.cs b/backend/src/Designer/Repository/ORMImplementation/Data/DesignerdbContext.cs index 7131d1b85fb..53e1af8ca8b 100644 --- a/backend/src/Designer/Repository/ORMImplementation/Data/DesignerdbContext.cs +++ b/backend/src/Designer/Repository/ORMImplementation/Data/DesignerdbContext.cs @@ -11,13 +11,14 @@ public DesignerdbContext(DbContextOptions options) { } - public virtual DbSet Deployments { get; set; } - public virtual DbSet Releases { get; set; } - public virtual DbSet AppScopes { get; set; } + public virtual DbSet Deployments { get; set; } + public virtual DbSet Releases { get; set; } + public virtual DbSet AppScopes { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.UseSerialColumns(); + modelBuilder.ApplyConfiguration(new BuildConfiguration()); modelBuilder.ApplyConfiguration(new DeploymentConfiguration()); modelBuilder.ApplyConfiguration(new ReleaseConfiguration()); modelBuilder.ApplyConfiguration(new AppScopesConfiguration()); diff --git a/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/AppScopesConfiguration.cs b/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/AppScopesConfiguration.cs index a07757d8589..2a6694c34d6 100644 --- a/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/AppScopesConfiguration.cs +++ b/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/AppScopesConfiguration.cs @@ -4,9 +4,9 @@ namespace Altinn.Studio.Designer.Repository.ORMImplementation.Data.EntityConfigurations; -public class AppScopesConfiguration : IEntityTypeConfiguration +public class AppScopesConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) + public void Configure(EntityTypeBuilder builder) { builder.ToTable("app_scopes", "designer"); diff --git a/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/BuildConfiguration.cs b/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/BuildConfiguration.cs new file mode 100644 index 00000000000..c659a0cfe16 --- /dev/null +++ b/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/BuildConfiguration.cs @@ -0,0 +1,49 @@ +using Altinn.Studio.Designer.Repository.ORMImplementation.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Altinn.Studio.Designer.Repository.ORMImplementation.Data.EntityConfigurations; + +public class BuildConfiguration : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.ToTable("builds", "designer"); + + builder.HasKey(e => e.Id); + + builder.Property(e => e.Id) + .HasColumnName("id") + .UseIdentityColumn() + .ValueGeneratedOnAdd(); + + builder.Property(e => e.ExternalId) + .HasColumnType("character varying") + .HasColumnName("external_id"); + + builder.Property(e => e.Status) + .HasColumnType("character varying") + .HasColumnName("status"); + + builder.Property(e => e.Result) + .HasColumnType("character varying") + .HasColumnName("result"); + + builder.Property(e => e.BuildType) + .HasColumnType("integer") + .HasColumnName("build_type"); + + builder.Property(e => e.Started) + .HasColumnType("timestamptz") + .HasColumnName("started"); + + builder.Property(e => e.Finished) + .HasColumnType("timestamptz") + .HasColumnName("finished"); + + builder.HasIndex(b => new { b.ExternalId, b.BuildType }) + .IsUnique(); + + builder.HasIndex(b => b.BuildType); + } +} diff --git a/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/DeploymentConfiguration.cs b/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/DeploymentConfiguration.cs index 725381c73d2..e5aa04fee85 100644 --- a/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/DeploymentConfiguration.cs +++ b/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/DeploymentConfiguration.cs @@ -4,9 +4,9 @@ namespace Altinn.Studio.Designer.Repository.ORMImplementation.Data.EntityConfigurations; -public class DeploymentConfiguration : IEntityTypeConfiguration +public class DeploymentConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) + public void Configure(EntityTypeBuilder builder) { builder.HasKey(e => e.Sequenceno).HasName("deployments_pkey"); @@ -23,6 +23,10 @@ public void Configure(EntityTypeBuilder builder) .HasColumnType("character varying") .HasColumnName("app"); + builder.Property(e => e.EnvName) + .HasColumnType("character varying") + .HasColumnName("envname"); + builder.Property(e => e.Buildid) .HasColumnType("character varying") .HasColumnName("buildid"); @@ -46,5 +50,17 @@ public void Configure(EntityTypeBuilder builder) builder.Property(e => e.Tagname) .HasColumnType("character varying") .HasColumnName("tagname"); + + builder.Property(e => e.CreatedBy) + .HasColumnType("character varying") + .HasColumnName("created_by"); + + builder.Property(e => e.InternalBuildId) + .HasColumnName("internal_build_id"); + + builder.HasOne(d => d.Build) + .WithMany() + .HasForeignKey(d => d.InternalBuildId) + .HasConstraintName("fk_deployments_builds_buildid"); } } diff --git a/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/ReleaseConfiguration.cs b/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/ReleaseConfiguration.cs index b1bce3e63c6..474caaee851 100644 --- a/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/ReleaseConfiguration.cs +++ b/backend/src/Designer/Repository/ORMImplementation/Data/EntityConfigurations/ReleaseConfiguration.cs @@ -4,9 +4,9 @@ namespace Altinn.Studio.Designer.Repository.ORMImplementation.Data.EntityConfigurations; -public class ReleaseConfiguration : IEntityTypeConfiguration +public class ReleaseConfiguration : IEntityTypeConfiguration { - public void Configure(EntityTypeBuilder builder) + public void Configure(EntityTypeBuilder builder) { builder.HasKey(e => e.Sequenceno).HasName("releases_pkey"); diff --git a/backend/src/Designer/Repository/ORMImplementation/ORMDeploymentRepository.cs b/backend/src/Designer/Repository/ORMImplementation/DeploymentRepository.cs similarity index 70% rename from backend/src/Designer/Repository/ORMImplementation/ORMDeploymentRepository.cs rename to backend/src/Designer/Repository/ORMImplementation/DeploymentRepository.cs index a3cee73ad7a..f9513429de7 100644 --- a/backend/src/Designer/Repository/ORMImplementation/ORMDeploymentRepository.cs +++ b/backend/src/Designer/Repository/ORMImplementation/DeploymentRepository.cs @@ -10,11 +10,11 @@ namespace Altinn.Studio.Designer.Repository.ORMImplementation; -public class ORMDeploymentRepository : IDeploymentRepository +public class DeploymentRepository : IDeploymentRepository { private readonly DesignerdbContext _dbContext; - public ORMDeploymentRepository(DesignerdbContext dbContext) + public DeploymentRepository(DesignerdbContext dbContext) { _dbContext = dbContext; } @@ -29,7 +29,7 @@ public async Task Create(DeploymentEntity deploymentEntity) public async Task> Get(string org, string app, DocumentQueryModel query) { - var deploymentsQuery = _dbContext.Deployments.AsNoTracking().Where(x => x.Org == org && x.App == app); + var deploymentsQuery = _dbContext.Deployments.Include(d => d.Build).AsNoTracking().Where(x => x.Org == org && x.App == app); deploymentsQuery = query.SortDirection == SortDirection.Descending ? deploymentsQuery.OrderByDescending(d => d.Created) @@ -43,20 +43,21 @@ public async Task> Get(string org, string app, Doc public async Task Get(string org, string buildId) { - var dbObject = await _dbContext.Deployments.AsNoTracking().SingleAsync(d => d.Org == org && d.Buildid == buildId); + var dbObject = await _dbContext.Deployments.Include(d => d.Build).AsNoTracking().SingleAsync(d => d.Org == org && d.Buildid == buildId); return DeploymentMapper.MapToModel(dbObject); } public async Task Update(DeploymentEntity deploymentEntity) { - long sequenceNo = await _dbContext.Deployments.AsNoTracking() + var dbIds = await _dbContext.Deployments.Include(d => d.Build).AsNoTracking() .Where(d => d.Org == deploymentEntity.Org && d.Buildid == deploymentEntity.Build.Id) - .Select(d => d.Sequenceno) + .Select(d => new { SequnceNo = d.Sequenceno, BuildId = d.Build.Id }) .SingleAsync(); - var mappedDbObject = DeploymentMapper.MapToDbModel(sequenceNo, deploymentEntity); + var mappedDbObject = DeploymentMapper.MapToDbModel(deploymentEntity, dbIds.SequnceNo, dbIds.BuildId); _dbContext.Entry(mappedDbObject).State = EntityState.Modified; + _dbContext.Entry(mappedDbObject.Build).State = EntityState.Modified; await _dbContext.SaveChangesAsync(); } } diff --git a/backend/src/Designer/Repository/ORMImplementation/Mappers/AppScopesMapper.cs b/backend/src/Designer/Repository/ORMImplementation/Mappers/AppScopesMapper.cs index ca348b6c87d..11b86115baa 100644 --- a/backend/src/Designer/Repository/ORMImplementation/Mappers/AppScopesMapper.cs +++ b/backend/src/Designer/Repository/ORMImplementation/Mappers/AppScopesMapper.cs @@ -13,9 +13,9 @@ public class AppScopesMapper WriteIndented = false }; - public static AppScopesDbObject MapToDbModel(AppScopesEntity appScopes) + public static AppScopesDbModel MapToDbModel(AppScopesEntity appScopes) { - return new AppScopesDbObject + return new AppScopesDbModel { App = appScopes.App, Org = appScopes.Org, @@ -27,24 +27,24 @@ public static AppScopesDbObject MapToDbModel(AppScopesEntity appScopes) }; } - public static AppScopesDbObject MapToDbModel(AppScopesEntity appScopes, long id) + public static AppScopesDbModel MapToDbModel(AppScopesEntity appScopes, long id) { var dbModel = MapToDbModel(appScopes); dbModel.Id = id; return dbModel; } - public static AppScopesEntity MapToModel(AppScopesDbObject appScopesDbObject) + public static AppScopesEntity MapToModel(AppScopesDbModel appScopesDbModel) { return new AppScopesEntity { - App = appScopesDbObject.App, - Org = appScopesDbObject.Org, - Created = appScopesDbObject.Created, - Scopes = JsonSerializer.Deserialize>(appScopesDbObject.Scopes, s_jsonOptions), - CreatedBy = appScopesDbObject.CreatedBy, - LastModifiedBy = appScopesDbObject.LastModifiedBy, - Version = appScopesDbObject.Version + App = appScopesDbModel.App, + Org = appScopesDbModel.Org, + Created = appScopesDbModel.Created, + Scopes = JsonSerializer.Deserialize>(appScopesDbModel.Scopes, s_jsonOptions), + CreatedBy = appScopesDbModel.CreatedBy, + LastModifiedBy = appScopesDbModel.LastModifiedBy, + Version = appScopesDbModel.Version }; } } diff --git a/backend/src/Designer/Repository/ORMImplementation/Mappers/BuildMapper.cs b/backend/src/Designer/Repository/ORMImplementation/Mappers/BuildMapper.cs new file mode 100644 index 00000000000..45ba665316d --- /dev/null +++ b/backend/src/Designer/Repository/ORMImplementation/Mappers/BuildMapper.cs @@ -0,0 +1,30 @@ +using System; +using Altinn.Studio.Designer.Repository.Models; +using Altinn.Studio.Designer.Repository.ORMImplementation.Models; +using Altinn.Studio.Designer.TypedHttpClients.AzureDevOps.Enums; + +namespace Altinn.Studio.Designer.Repository.ORMImplementation.Mappers; + +public static class BuildMapper +{ + public static BuildEntity MapToModel(BuildDbModel buildDbModel) => + new() + { + Id = buildDbModel.ExternalId, + Status = Enum.Parse(buildDbModel.Status, true), + Result = Enum.Parse(buildDbModel.Result, true), + Started = buildDbModel.Started?.DateTime, + Finished = buildDbModel.Finished?.DateTime + }; + + public static BuildDbModel MapToDbModel(BuildEntity buildEntity, BuildType buildType) => + new() + { + ExternalId = buildEntity.Id, + Status = buildEntity.Status.ToString(), + Result = buildEntity.Result.ToString(), + Started = buildEntity.Started, + Finished = buildEntity.Finished, + BuildType = buildType + }; +} diff --git a/backend/src/Designer/Repository/ORMImplementation/Mappers/DeploymentMapper.cs b/backend/src/Designer/Repository/ORMImplementation/Mappers/DeploymentMapper.cs index 6541b1570a9..e97ad9e8ba2 100644 --- a/backend/src/Designer/Repository/ORMImplementation/Mappers/DeploymentMapper.cs +++ b/backend/src/Designer/Repository/ORMImplementation/Mappers/DeploymentMapper.cs @@ -3,8 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; using Altinn.Studio.Designer.Repository.Models; +using Altinn.Studio.Designer.Repository.ORMImplementation.Models; using Altinn.Studio.Designer.TypedHttpClients.AzureDevOps.Enums; -using Deployment = Altinn.Studio.Designer.Repository.ORMImplementation.Models.Deployment; namespace Altinn.Studio.Designer.Repository.ORMImplementation.Mappers; @@ -17,32 +17,45 @@ public static class DeploymentMapper Converters = { new JsonStringEnumConverter() } }; - public static Deployment MapToDbModel(DeploymentEntity deploymentEntity) + public static DeploymentDbModel MapToDbModel(DeploymentEntity deploymentEntity) { - return new Deployment + return new DeploymentDbModel { Buildid = deploymentEntity.Build.Id, Tagname = deploymentEntity.TagName, Org = deploymentEntity.Org, App = deploymentEntity.App, + EnvName = deploymentEntity.EnvName, Buildresult = deploymentEntity.Build.Result.ToEnumMemberAttributeValue(), Created = deploymentEntity.Created.ToUniversalTime(), - Entity = JsonSerializer.Serialize(deploymentEntity, s_jsonOptions) + Entity = JsonSerializer.Serialize(deploymentEntity, s_jsonOptions), + Build = BuildMapper.MapToDbModel(deploymentEntity.Build, BuildType.Deployment), }; } - public static Deployment MapToDbModel(long sequenceNo, DeploymentEntity deploymentEntity) + public static DeploymentDbModel MapToDbModel(DeploymentEntity deploymentEntity, long deploymentSequenceNo, long buildId) { var dbModel = MapToDbModel(deploymentEntity); - dbModel.Sequenceno = sequenceNo; + dbModel.Sequenceno = deploymentSequenceNo; + dbModel.InternalBuildId = buildId; + dbModel.Build.Id = buildId; return dbModel; } - public static DeploymentEntity MapToModel(Deployment deployment) + public static DeploymentEntity MapToModel(DeploymentDbModel dbObject) { - return JsonSerializer.Deserialize(deployment.Entity, s_jsonOptions); + return new DeploymentEntity + { + App = dbObject.App, + Org = dbObject.Org, + EnvName = dbObject.EnvName, + TagName = dbObject.Tagname, + Build = BuildMapper.MapToModel(dbObject.Build), + Created = dbObject.Created, + CreatedBy = dbObject.CreatedBy + }; } - public static IEnumerable MapToModels(IEnumerable deployments) + public static IEnumerable MapToModels(IEnumerable deployments) { return deployments.Select(MapToModel); } diff --git a/backend/src/Designer/Repository/ORMImplementation/Mappers/ReleaseMapper.cs b/backend/src/Designer/Repository/ORMImplementation/Mappers/ReleaseMapper.cs index 8b9f795bf67..3da48f6eaee 100644 --- a/backend/src/Designer/Repository/ORMImplementation/Mappers/ReleaseMapper.cs +++ b/backend/src/Designer/Repository/ORMImplementation/Mappers/ReleaseMapper.cs @@ -17,9 +17,9 @@ public static class ReleaseMapper Converters = { new JsonStringEnumConverter() } }; - public static Release MapToDbModel(ReleaseEntity releaseEntity) + public static ReleaseDbModel MapToDbModel(ReleaseEntity releaseEntity) { - return new Release + return new ReleaseDbModel { Buildid = releaseEntity.Build.Id, Tagname = releaseEntity.TagName, @@ -32,19 +32,19 @@ public static Release MapToDbModel(ReleaseEntity releaseEntity) }; } - public static Release MapToDbModel(long sequenceNo, ReleaseEntity releaseEntity) + public static ReleaseDbModel MapToDbModel(long sequenceNo, ReleaseEntity releaseEntity) { var dbModel = MapToDbModel(releaseEntity); dbModel.Sequenceno = sequenceNo; return dbModel; } - public static ReleaseEntity MapToModel(Release release) + public static ReleaseEntity MapToModel(ReleaseDbModel releaseDbModel) { - return JsonSerializer.Deserialize(release.Entity, s_jsonOptions); + return JsonSerializer.Deserialize(releaseDbModel.Entity, s_jsonOptions); } - public static IEnumerable MapToModels(IEnumerable releases) + public static IEnumerable MapToModels(IEnumerable releases) { return releases.Select(MapToModel); } diff --git a/backend/src/Designer/Repository/ORMImplementation/Models/AppScopesDbObject.cs b/backend/src/Designer/Repository/ORMImplementation/Models/AppScopesDbModel.cs similarity index 97% rename from backend/src/Designer/Repository/ORMImplementation/Models/AppScopesDbObject.cs rename to backend/src/Designer/Repository/ORMImplementation/Models/AppScopesDbModel.cs index e6de599b825..973fa785ffe 100644 --- a/backend/src/Designer/Repository/ORMImplementation/Models/AppScopesDbObject.cs +++ b/backend/src/Designer/Repository/ORMImplementation/Models/AppScopesDbModel.cs @@ -2,7 +2,7 @@ namespace Altinn.Studio.Designer.Repository.ORMImplementation.Models; -public class AppScopesDbObject +public class AppScopesDbModel { /// /// The unique identifier for the object diff --git a/backend/src/Designer/Repository/ORMImplementation/Models/BuildDbModel.cs b/backend/src/Designer/Repository/ORMImplementation/Models/BuildDbModel.cs new file mode 100644 index 00000000000..1cfefa29e48 --- /dev/null +++ b/backend/src/Designer/Repository/ORMImplementation/Models/BuildDbModel.cs @@ -0,0 +1,26 @@ +using System; + +namespace Altinn.Studio.Designer.Repository.ORMImplementation.Models; + +public class BuildDbModel +{ + public long Id { get; set; } + + public string ExternalId { get; set; } + + public string Status { get; set; } + + public string Result { get; set; } + + public BuildType BuildType { get; set; } + + public DateTimeOffset? Started { get; set; } + + public DateTimeOffset? Finished { get; set; } +} + +public enum BuildType +{ + Deployment = 0, + Release = 1 +} diff --git a/backend/src/Designer/Repository/ORMImplementation/Models/Deployment.cs b/backend/src/Designer/Repository/ORMImplementation/Models/DeploymentDbModel.cs similarity index 66% rename from backend/src/Designer/Repository/ORMImplementation/Models/Deployment.cs rename to backend/src/Designer/Repository/ORMImplementation/Models/DeploymentDbModel.cs index c6be15bfea9..d2db5878fcb 100644 --- a/backend/src/Designer/Repository/ORMImplementation/Models/Deployment.cs +++ b/backend/src/Designer/Repository/ORMImplementation/Models/DeploymentDbModel.cs @@ -2,7 +2,7 @@ namespace Altinn.Studio.Designer.Repository.ORMImplementation.Models; -public partial class Deployment +public partial class DeploymentDbModel { public long Sequenceno { get; set; } @@ -14,9 +14,17 @@ public partial class Deployment public string App { get; set; } = null!; + public string EnvName { get; set; } = null!; + public string Buildresult { get; set; } = null!; public DateTime Created { get; set; } + public string CreatedBy { get; set; } = null!; + public string Entity { get; set; } = null!; + + public long? InternalBuildId { get; set; } + + public BuildDbModel Build { get; set; } = null!; } diff --git a/backend/src/Designer/Repository/ORMImplementation/Models/Release.cs b/backend/src/Designer/Repository/ORMImplementation/Models/ReleaseDbModel.cs similarity index 93% rename from backend/src/Designer/Repository/ORMImplementation/Models/Release.cs rename to backend/src/Designer/Repository/ORMImplementation/Models/ReleaseDbModel.cs index 5cb779009b0..0ee089c3119 100644 --- a/backend/src/Designer/Repository/ORMImplementation/Models/Release.cs +++ b/backend/src/Designer/Repository/ORMImplementation/Models/ReleaseDbModel.cs @@ -2,7 +2,7 @@ namespace Altinn.Studio.Designer.Repository.ORMImplementation.Models; -public partial class Release +public partial class ReleaseDbModel { public long Sequenceno { get; set; } diff --git a/backend/src/Designer/Repository/ORMImplementation/ORMReleaseRepository.cs b/backend/src/Designer/Repository/ORMImplementation/ReleaseRepository.cs similarity index 96% rename from backend/src/Designer/Repository/ORMImplementation/ORMReleaseRepository.cs rename to backend/src/Designer/Repository/ORMImplementation/ReleaseRepository.cs index fb47f85754c..18bfec354f0 100644 --- a/backend/src/Designer/Repository/ORMImplementation/ORMReleaseRepository.cs +++ b/backend/src/Designer/Repository/ORMImplementation/ReleaseRepository.cs @@ -11,11 +11,11 @@ namespace Altinn.Studio.Designer.Repository.ORMImplementation; -public class ORMReleaseRepository : IReleaseRepository +public class ReleaseRepository : IReleaseRepository { private readonly DesignerdbContext _dbContext; - public ORMReleaseRepository(DesignerdbContext dbContext) + public ReleaseRepository(DesignerdbContext dbContext) { _dbContext = dbContext; } diff --git a/backend/src/Designer/Repository/ReleaseRepository.cs b/backend/src/Designer/Repository/ReleaseRepository.cs deleted file mode 100644 index 5f928eb9b5b..00000000000 --- a/backend/src/Designer/Repository/ReleaseRepository.cs +++ /dev/null @@ -1,215 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Threading.Tasks; -using Altinn.Studio.Designer.Configuration; -using Altinn.Studio.Designer.Repository.Models; -using Altinn.Studio.Designer.TypedHttpClients.AzureDevOps.Enums; -using Altinn.Studio.Designer.ViewModels.Request; -using Altinn.Studio.Designer.ViewModels.Request.Enums; -using Microsoft.Extensions.Logging; -using Newtonsoft.Json; -using Npgsql; -using NpgsqlTypes; - -namespace Altinn.Studio.Designer.Repository -{ - /// - /// Handles release repository. - /// - [ExcludeFromCodeCoverage] - public class ReleaseRepository : IReleaseRepository - { - private readonly string insertReleaseSql = "call designer.insert_release(@buildid, @tagname, @org, @app, @buildstatus, @buildresult, @created, @entity)"; - private readonly string getReleasesSql = "select designer.get_releases(@_org, @_app, @_limit, @_order_asc_desc)"; - private readonly string checkExistingReleaseSql = "select designer.check_existing_release(@_org, @_app, @_tagname, @_buildstatus, @_buildresult)"; - private readonly string getReleaseSql = "select designer.get_release(@_org, @_buildid)"; - private readonly string updateReleaseBuildSql = "call designer.update_release_build(@_org, @_buildid, @_buildstatus, @_buildresult, @_entity)"; - private readonly string _connectionString; - private readonly ILogger _logger; - - /// - /// Initializes a new instance of the class. - /// - public ReleaseRepository(PostgreSQLSettings postgresSettings, ILogger logger) - { - _connectionString = postgresSettings.FormattedConnectionString(); - _logger = logger; - } - - /// - public async Task Create(ReleaseEntity releaseEntity) - { - try - { - using NpgsqlConnection conn = new NpgsqlConnection(_connectionString); - await conn.OpenAsync(); - - NpgsqlCommand pgcom = new NpgsqlCommand(insertReleaseSql, conn); - pgcom.Parameters.AddWithValue("buildid", releaseEntity.Build.Id); - pgcom.Parameters.AddWithValue("tagname", releaseEntity.TagName); - pgcom.Parameters.AddWithValue("org", releaseEntity.Org); - pgcom.Parameters.AddWithValue("app", releaseEntity.App); - pgcom.Parameters.AddWithValue("buildstatus", releaseEntity.Build.Status.ToEnumMemberAttributeValue()); - pgcom.Parameters.AddWithValue("buildresult", releaseEntity.Build.Result.ToEnumMemberAttributeValue()); - pgcom.Parameters.AddWithValue("created", releaseEntity.Created); - pgcom.Parameters.AddWithValue("entity", JsonString(releaseEntity)); - - await pgcom.ExecuteNonQueryAsync(); - - return releaseEntity; - } - catch (Exception e) - { - _logger.LogError(e, "ReleaseRepository // Create // Exception"); - throw; - } - } - - /// - public async Task> Get(string org, string app, DocumentQueryModel query) - { - List searchResult = new List(); - - try - { - using NpgsqlConnection conn = new NpgsqlConnection(_connectionString); - await conn.OpenAsync(); - - NpgsqlCommand pgcom = new NpgsqlCommand(getReleasesSql, conn); - pgcom.Parameters.AddWithValue("_org", NpgsqlDbType.Varchar, org); - pgcom.Parameters.AddWithValue("_app", NpgsqlDbType.Varchar, app); - pgcom.Parameters.AddWithValue("_limit", NpgsqlDbType.Integer, query.Top ?? int.MaxValue); - pgcom.Parameters.AddWithValue("_order_asc_desc", NpgsqlDbType.Varchar, query.SortDirection == SortDirection.Ascending ? "asc" : "desc"); - - using (NpgsqlDataReader reader = pgcom.ExecuteReader()) - { - while (reader.Read()) - { - ReleaseEntity releaseEntity = Deserialize(reader[0].ToString()); - searchResult.Add(releaseEntity); - } - } - - return searchResult; - } - catch (Exception e) - { - _logger.LogError(e, "ReleaseRepository // Get(DocumentQueryModel) // Exception"); - throw; - } - } - - /// - public async Task> Get(string org, string app, string tagName, List buildStatus, List buildResult) - { - List searchResult = new List(); - - try - { - using NpgsqlConnection conn = new NpgsqlConnection(_connectionString); - await conn.OpenAsync(); - - NpgsqlCommand pgcom = new NpgsqlCommand(checkExistingReleaseSql, conn); - pgcom.Parameters.AddWithValue("_org", NpgsqlDbType.Varchar, org); - pgcom.Parameters.AddWithValue("_app", NpgsqlDbType.Varchar, app); - pgcom.Parameters.AddWithValue("_tagname", NpgsqlDbType.Varchar, tagName); - pgcom.Parameters.AddWithValue("_buildstatus", NpgsqlDbType.Array | NpgsqlDbType.Text, buildStatus ?? (object)DBNull.Value); - pgcom.Parameters.AddWithValue("_buildresult", NpgsqlDbType.Array | NpgsqlDbType.Text, buildResult ?? (object)DBNull.Value); - - using (NpgsqlDataReader reader = pgcom.ExecuteReader()) - { - while (reader.Read()) - { - ReleaseEntity releaseEntity = Deserialize(reader[0].ToString()); - searchResult.Add(releaseEntity); - } - } - - return searchResult; - } - catch (Exception e) - { - _logger.LogError(e, "ReleaseRepository // Get(DocumentQueryModel) // Exception"); - throw; - } - } - - /// - public async Task> Get(string org, string buildId) - { - List searchResult = new List(); - - try - { - using NpgsqlConnection conn = new NpgsqlConnection(_connectionString); - await conn.OpenAsync(); - - NpgsqlCommand pgcom = new NpgsqlCommand(getReleaseSql, conn); - pgcom.Parameters.AddWithValue("_org", NpgsqlDbType.Varchar, org); - pgcom.Parameters.AddWithValue("_buildid", NpgsqlDbType.Varchar, buildId); - - using (NpgsqlDataReader reader = pgcom.ExecuteReader()) - { - while (reader.Read()) - { - ReleaseEntity releaseEntity = Deserialize(reader[0].ToString()); - searchResult.Add(releaseEntity); - } - } - - return searchResult; - } - catch (Exception e) - { - _logger.LogError(e, "ReleaseRepository // Get(string org, string buildId) // Exception"); - throw; - } - } - - /// - public async Task GetSucceededReleaseFromDb(string org, string app, string tagName) - { - List buildResult = new List(); - buildResult.Add(BuildResult.Succeeded.ToEnumMemberAttributeValue()); - - IEnumerable releases = await Get(org, app, tagName, null, buildResult); - return releases.Single(); - } - - /// - public async Task Update(ReleaseEntity releaseEntity) - { - try - { - using NpgsqlConnection conn = new NpgsqlConnection(_connectionString); - await conn.OpenAsync(); - - NpgsqlCommand pgcom = new NpgsqlCommand(updateReleaseBuildSql, conn); - pgcom.Parameters.AddWithValue("_org", releaseEntity.Org); - pgcom.Parameters.AddWithValue("_buildid", releaseEntity.Build.Id); - pgcom.Parameters.AddWithValue("_buildstatus", releaseEntity.Build.Status.ToEnumMemberAttributeValue()); - pgcom.Parameters.AddWithValue("_buildresult", releaseEntity.Build.Result.ToEnumMemberAttributeValue()); - pgcom.Parameters.AddWithValue("_entity", JsonString(releaseEntity)); - - await pgcom.ExecuteNonQueryAsync(); - } - catch (Exception e) - { - _logger.LogError(e, "ReleaseRepository // Update // Exception"); - throw; - } - } - - private static string JsonString(ReleaseEntity releaseEntity) - { - return JsonConvert.SerializeObject(releaseEntity); - } - - private static ReleaseEntity Deserialize(string releaseEntityString) - { - return JsonConvert.DeserializeObject(releaseEntityString); - } - } -} diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/AppScopesRepository/Utils/AppScopesEntityAsserts.cs b/backend/tests/Designer.Tests/DbIntegrationTests/AppScopesRepository/Utils/AppScopesEntityAsserts.cs index 0af34fde58d..a7cafddcdf9 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/AppScopesRepository/Utils/AppScopesEntityAsserts.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/AppScopesRepository/Utils/AppScopesEntityAsserts.cs @@ -8,17 +8,13 @@ namespace Designer.Tests.DbIntegrationTests; public partial class EntityAssertions { - public static void AssertEqual(AppScopesEntity appScopesEntity, Altinn.Studio.Designer.Repository.ORMImplementation.Models.AppScopesDbObject dbRecord) + public static void AssertEqual(AppScopesEntity appScopesEntity, Altinn.Studio.Designer.Repository.ORMImplementation.Models.AppScopesDbModel dbRecord) { dbRecord.App.Should().BeEquivalentTo(appScopesEntity.App); dbRecord.Org.Should().BeEquivalentTo(appScopesEntity.Org); dbRecord.CreatedBy.Should().BeEquivalentTo(appScopesEntity.CreatedBy); dbRecord.LastModifiedBy.Should().BeEquivalentTo(appScopesEntity.LastModifiedBy); - // Allow precision loss up to 100 milliseconds - TimeSpan tolerance = TimeSpan.FromMilliseconds(100); - TimeSpan difference = (appScopesEntity.Created - dbRecord.Created).Duration(); - bool isWithinTolerance = difference <= tolerance; - isWithinTolerance.Should().BeTrue(); + dbRecord.Created.Should().BeCloseTo(appScopesEntity.Created, TimeSpan.FromMilliseconds(100)); dbRecord.Version.Should().Be(appScopesEntity.Version); var scopesFromDb = JsonSerializer.Deserialize>(dbRecord.Scopes, JsonOptions); @@ -32,11 +28,7 @@ public static void AssertEqual(AppScopesEntity expected, AppScopesEntity actual) actual.Org.Should().Be(expected.Org); actual.CreatedBy.Should().Be(expected.CreatedBy); actual.LastModifiedBy.Should().Be(expected.LastModifiedBy); - // Allow precision loss up to 100 milliseconds - TimeSpan tolerance = TimeSpan.FromMilliseconds(100); - TimeSpan difference = (expected.Created - actual.Created).Duration(); - bool isWithinTolerance = difference <= tolerance; - isWithinTolerance.Should().BeTrue(); + actual.Created.Should().BeCloseTo(expected.Created, TimeSpan.FromMilliseconds(100)); actual.Version.Should().Be(expected.Version); actual.Scopes.Should().BeEquivalentTo(expected.Scopes); diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/Base/DeploymentEntityIntegrationTestsBase.cs b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/Base/DeploymentEntityIntegrationTestsBase.cs index 881c6f19ef7..d84164a0261 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/Base/DeploymentEntityIntegrationTestsBase.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/Base/DeploymentEntityIntegrationTestsBase.cs @@ -22,6 +22,7 @@ protected async Task PrepareEntityInDatabase(DeploymentEntity deploymentEntity) await DbFixture.DbContext.Deployments.AddAsync(dbObject); await DbFixture.DbContext.SaveChangesAsync(); DbFixture.DbContext.Entry(dbObject).State = EntityState.Detached; + DbFixture.DbContext.Entry(dbObject.Build).State = EntityState.Detached; } protected async Task PrepareEntitiesInDatabase(IEnumerable deploymentEntities) @@ -33,10 +34,11 @@ protected async Task PrepareEntitiesInDatabase(IEnumerable dep foreach (var dbObject in dbObjects) { DbFixture.DbContext.Entry(dbObject).State = EntityState.Detached; + DbFixture.DbContext.Entry(dbObject.Build).State = EntityState.Detached; } } - private Altinn.Studio.Designer.Repository.ORMImplementation.Models.Deployment MapToDbObject(DeploymentEntity entity) => + private Altinn.Studio.Designer.Repository.ORMImplementation.Models.DeploymentDbModel MapToDbObject(DeploymentEntity entity) => new() { Buildid = entity.Build.Id, @@ -45,6 +47,18 @@ private Altinn.Studio.Designer.Repository.ORMImplementation.Models.Deployment Ma App = entity.App, Buildresult = entity.Build.Result.ToEnumMemberAttributeValue(), Created = entity.Created, - Entity = JsonSerializer.Serialize(entity, JsonOptions) + Entity = JsonSerializer.Serialize(entity, JsonOptions), + EnvName = entity.EnvName, + Build = MapBuildToDbModel(entity.Build) + }; + + private static Altinn.Studio.Designer.Repository.ORMImplementation.Models.BuildDbModel MapBuildToDbModel(BuildEntity buildEntity) => + new() + { + ExternalId = buildEntity.Id, + Status = buildEntity.Status.ToString(), + Result = buildEntity.Result.ToString(), + Started = buildEntity.Started, + Finished = buildEntity.Finished }; } diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/CreateIntegrationTests.cs b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/CreateIntegrationTests.cs index 6d9e0a01f8f..66a346fc4c0 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/CreateIntegrationTests.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/CreateIntegrationTests.cs @@ -18,11 +18,11 @@ public CreateIntegrationTests(DesignerDbFixture dbFixture) : base(dbFixture) [InlineData("ttd")] public async Task Create_ShouldInsertRecordInDatabase(string org) { - var repository = new ORMDeploymentRepository(DbFixture.DbContext); + var repository = new DeploymentRepository(DbFixture.DbContext); var buildId = Guid.NewGuid(); var deploymentEntity = EntityGenerationUtils.Deployment.GenerateDeploymentEntity(org, buildId: buildId.ToString()); await repository.Create(deploymentEntity); - var dbRecord = await DbFixture.DbContext.Deployments.AsNoTracking().FirstOrDefaultAsync(d => + var dbRecord = await DbFixture.DbContext.Deployments.Include(d => d.Build).AsNoTracking().FirstOrDefaultAsync(d => d.Org == org && d.App == deploymentEntity.App && d.Buildid == buildId.ToString()); diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/GetIntegrationTests.cs b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/GetIntegrationTests.cs index d18bee09bcb..9f9cd0d1cd7 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/GetIntegrationTests.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/GetIntegrationTests.cs @@ -26,7 +26,7 @@ public async Task Get_ShouldReturnCorrectRecordsFromDatabase(string org, string var deploymentEntities = EntityGenerationUtils.Deployment.GenerateDeploymentEntities(org, app, allEntitiesCount).ToList(); await PrepareEntitiesInDatabase(deploymentEntities); - var repository = new ORMDeploymentRepository(DbFixture.DbContext); + var repository = new DeploymentRepository(DbFixture.DbContext); var query = new DocumentQueryModel { Top = top, SortDirection = sortDirection }; var result = (await repository.Get(org, app, query)).ToList(); @@ -37,7 +37,10 @@ public async Task Get_ShouldReturnCorrectRecordsFromDatabase(string org, string .ToList(); result.Count.Should().Be(top); - result.Should().BeEquivalentTo(expectedEntities); + result.Should().BeEquivalentTo(expectedEntities, options => + options.Using(ctx => + ctx.Subject.Should().BeCloseTo(ctx.Expectation, TimeSpan.FromMilliseconds(200)) + ).WhenTypeIs()); } [Theory] @@ -49,7 +52,7 @@ public async Task Get_Without_TopDefined_ShouldReturnAllRecordsForGivenApp(strin var deploymentEntities = EntityGenerationUtils.Deployment.GenerateDeploymentEntities(org, app, allEntitiesCount).ToList(); await PrepareEntitiesInDatabase(deploymentEntities); - var repository = new ORMDeploymentRepository(DbFixture.DbContext); + var repository = new DeploymentRepository(DbFixture.DbContext); var query = new DocumentQueryModel { Top = null, @@ -62,8 +65,11 @@ public async Task Get_Without_TopDefined_ShouldReturnAllRecordsForGivenApp(strin : deploymentEntities.OrderByDescending(d => d.Created)) .ToList(); - result.Count().Should().Be(allEntitiesCount); - result.Should().BeEquivalentTo(expectedEntities); + result.Count.Should().Be(allEntitiesCount); + result.Should().BeEquivalentTo(expectedEntities, options => + options.Using(ctx => + ctx.Subject.Should().BeCloseTo(ctx.Expectation, TimeSpan.FromMilliseconds(200)) + ).WhenTypeIs()); } diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/GetSingleIntegrationTests.cs b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/GetSingleIntegrationTests.cs index 30d738ba0ca..e1c88912b39 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/GetSingleIntegrationTests.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/GetSingleIntegrationTests.cs @@ -1,3 +1,4 @@ +using System; using System.Threading.Tasks; using Altinn.Studio.Designer.Repository.ORMImplementation; using Designer.Tests.DbIntegrationTests.DeploymentEntityRepository.Base; @@ -20,8 +21,12 @@ public async Task Get_ShouldReturnRecordFromDatabase(string org) var deploymentEntity = EntityGenerationUtils.Deployment.GenerateDeploymentEntity(org); await PrepareEntityInDatabase(deploymentEntity); - var repository = new ORMDeploymentRepository(DbFixture.DbContext); + var repository = new DeploymentRepository(DbFixture.DbContext); var result = await repository.Get(deploymentEntity.Org, deploymentEntity.Build.Id); - result.Should().BeEquivalentTo(deploymentEntity); + + result.Should().BeEquivalentTo(deploymentEntity, options => + options.Using(ctx => + ctx.Subject.Should().BeCloseTo(ctx.Expectation, TimeSpan.FromMilliseconds(200)) + ).WhenTypeIs()); } } diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/OldRepositoryCompatibility/CreateGetCompatibilityTests.cs b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/OldRepositoryCompatibility/CreateGetCompatibilityTests.cs deleted file mode 100644 index dee2f35baac..00000000000 --- a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/OldRepositoryCompatibility/CreateGetCompatibilityTests.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System; -using System.Threading.Tasks; -using Altinn.Studio.Designer.Configuration; -using Altinn.Studio.Designer.Repository; -using Altinn.Studio.Designer.Repository.ORMImplementation; -using Designer.Tests.DbIntegrationTests.DeploymentEntityRepository.Base; -using Designer.Tests.Fixtures; -using FluentAssertions; -using Microsoft.Extensions.Logging; -using Moq; -using Xunit; - -namespace Designer.Tests.DbIntegrationTests.DeploymentEntityRepository.OldRepositoryCompatibility; - -public class CreateGetCompatibilityTests : DeploymentEntityIntegrationTestsBase -{ - public CreateGetCompatibilityTests(DesignerDbFixture dbFixture) : base(dbFixture) - { - } - - [Theory] - [InlineData("ttd")] - public async Task CreateOld_ShouldBeCompatibleWithNewGet(string org) - { - var oldRepository = CreateOldRepository(); - string buildId = Guid.NewGuid().ToString(); - var deploymentEntity = EntityGenerationUtils.Deployment.GenerateDeploymentEntity(org, buildId: buildId); - await oldRepository.Create(deploymentEntity); - var newRepository = new ORMDeploymentRepository(DbFixture.DbContext); - var queriedEntity = await newRepository.Get(org, buildId); - queriedEntity.Should().BeEquivalentTo(deploymentEntity); - } - - [Theory] - [InlineData("ttd")] - public async Task CreateNew_ShouldBeCompatibleWithOldGet(string org) - { - var newRepository = new ORMDeploymentRepository(DbFixture.DbContext); - string buildId = Guid.NewGuid().ToString(); - var deploymentEntity = EntityGenerationUtils.Deployment.GenerateDeploymentEntity(org, buildId: buildId); - await newRepository.Create(deploymentEntity); - var oldRepository = CreateOldRepository(); - var queriedEntity = await oldRepository.Get(org, buildId); - queriedEntity.Should().BeEquivalentTo(deploymentEntity); - } - - private DeploymentRepository CreateOldRepository() - { - var options = new PostgreSQLSettings - { - ConnectionString = DbFixture.ConnectionString - }; - return new DeploymentRepository(options, new Mock>().Object); - } -} diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/UpdateIntegrationTests.cs b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/UpdateIntegrationTests.cs index f5d959258e4..90b22603035 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/UpdateIntegrationTests.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/UpdateIntegrationTests.cs @@ -19,7 +19,7 @@ public UpdateIntegrationTests(DesignerDbFixture dbFixture) : base(dbFixture) [InlineData("ttd")] public async Task Update_ShouldUpdateRecordInDatabase(string org) { - var repository = new ORMDeploymentRepository(DbFixture.DbContext); + var repository = new DeploymentRepository(DbFixture.DbContext); var buildId = Guid.NewGuid(); var deploymentEntity = EntityGenerationUtils.Deployment.GenerateDeploymentEntity( org, @@ -35,7 +35,7 @@ public async Task Update_ShouldUpdateRecordInDatabase(string org) await repository.Update(deploymentEntity); - var dbRecord = await DbFixture.DbContext.Deployments.AsNoTracking().FirstOrDefaultAsync(d => + var dbRecord = await DbFixture.DbContext.Deployments.Include(d => d.Build).AsNoTracking().FirstOrDefaultAsync(d => d.Org == org && d.App == deploymentEntity.App && d.Buildid == buildId.ToString()); diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/Utils/DeploymentEntityAsserts.cs b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/Utils/DeploymentEntityAsserts.cs index 7c8a0250cb1..0b106ab4c05 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/Utils/DeploymentEntityAsserts.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/DeploymentEntityRepository/Utils/DeploymentEntityAsserts.cs @@ -1,19 +1,39 @@ +using System; using System.Text.Json; using Altinn.Studio.Designer.Repository.Models; +using Altinn.Studio.Designer.Repository.ORMImplementation.Models; using FluentAssertions; namespace Designer.Tests.DbIntegrationTests; public static partial class EntityAssertions { - public static void AssertEqual(DeploymentEntity deploymentEntity, Altinn.Studio.Designer.Repository.ORMImplementation.Models.Deployment dbRecord) + public static void AssertEqual(DeploymentEntity deploymentEntity, Altinn.Studio.Designer.Repository.ORMImplementation.Models.DeploymentDbModel dbRecord) { dbRecord.App.Should().BeEquivalentTo(deploymentEntity.App); dbRecord.Org.Should().BeEquivalentTo(deploymentEntity.Org); dbRecord.Buildid.Should().BeEquivalentTo(deploymentEntity.Build.Id); dbRecord.Buildresult.Should().BeEquivalentTo(deploymentEntity.Build.Result.ToString()); dbRecord.Tagname.Should().BeEquivalentTo(deploymentEntity.TagName); + dbRecord.EnvName.Should().BeEquivalentTo(deploymentEntity.EnvName); var entityFromColumn = JsonSerializer.Deserialize(dbRecord.Entity, JsonOptions); entityFromColumn.Should().BeEquivalentTo(deploymentEntity); + + Altinn.Studio.Designer.Repository.ORMImplementation.Models.BuildDbModel buildDbModel = dbRecord.Build; + buildDbModel.ExternalId.Should().BeEquivalentTo(deploymentEntity.Build.Id); + buildDbModel.Status.Should().BeEquivalentTo(deploymentEntity.Build.Status.ToString()); + buildDbModel.Result.Should().BeEquivalentTo(deploymentEntity.Build.Result.ToString()); + buildDbModel.BuildType.Should().Be(BuildType.Deployment); + + buildDbModel.Started!.Value.UtcDateTime.Should().BeCloseTo(deploymentEntity.Build.Started!.Value, TimeSpan.FromMilliseconds(100)); + + if (!buildDbModel.Finished.HasValue) + { + deploymentEntity.Build.Finished.Should().BeNull(); + } + else + { + buildDbModel.Finished!.Value.UtcDateTime.Should().BeCloseTo(deploymentEntity.Build.Finished!.Value, TimeSpan.FromMilliseconds(100)); + } } } diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/Base/ReleaseEntityIntegrationTestsBase.cs b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/Base/ReleaseEntityIntegrationTestsBase.cs index e40d390c3a4..ea429ed37d5 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/Base/ReleaseEntityIntegrationTestsBase.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/Base/ReleaseEntityIntegrationTestsBase.cs @@ -36,7 +36,7 @@ protected async Task PrepareEntitiesInDatabase(IEnumerable releas } } - private Altinn.Studio.Designer.Repository.ORMImplementation.Models.Release MapToDbObject(ReleaseEntity entity) => + private Altinn.Studio.Designer.Repository.ORMImplementation.Models.ReleaseDbModel MapToDbObject(ReleaseEntity entity) => new() { Buildid = entity.Build.Id, diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/CreateIntegrationTests.cs b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/CreateIntegrationTests.cs index 413843e0e30..33acae15560 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/CreateIntegrationTests.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/CreateIntegrationTests.cs @@ -18,7 +18,7 @@ public CreateIntegrationTests(DesignerDbFixture dbFixture) : base(dbFixture) [InlineData("ttd")] public async Task Create_ShouldInsertRecordInDatabase(string org) { - var repository = new ORMReleaseRepository(DbFixture.DbContext); + var repository = new ReleaseRepository(DbFixture.DbContext); var buildId = Guid.NewGuid(); var releaseEntity = EntityGenerationUtils.Release.GenerateReleaseEntity(org, buildId: buildId.ToString()); await repository.Create(releaseEntity); diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetBuildStatusAndResultsFilterIntegrationTests.cs b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetBuildStatusAndResultsFilterIntegrationTests.cs index fac5e9caa45..2163ce15468 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetBuildStatusAndResultsFilterIntegrationTests.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetBuildStatusAndResultsFilterIntegrationTests.cs @@ -23,7 +23,7 @@ public async Task Get_ShouldReturnCorrectRecordsFromDatabase(string org, string { int numberOfEntities = statusReleaseCombinationsInDb.Count; string tagName = Guid.NewGuid().ToString(); - var repository = new ORMReleaseRepository(DbFixture.DbContext); + var repository = new ReleaseRepository(DbFixture.DbContext); var releaseEntities = EntityGenerationUtils.Release.GenerateReleaseEntities(org, app, numberOfEntities).ToList(); for (int i = 0; i < numberOfEntities; i++) { diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetSingleIntegrationTests.cs b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetSingleIntegrationTests.cs index 6a9369149e8..2957eab1497 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetSingleIntegrationTests.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetSingleIntegrationTests.cs @@ -19,7 +19,7 @@ public GetSingleIntegrationTests(DesignerDbFixture dbFixture) : base(dbFixture) [InlineData("ttd")] public async Task GetSingleAsync_WhenCalled_ShouldReturnSingleReleaseEntity(string releaseName) { - var repository = new ORMReleaseRepository(DbFixture.DbContext); + var repository = new ReleaseRepository(DbFixture.DbContext); var buildId = Guid.NewGuid(); var releaseEntity = EntityGenerationUtils.Release.GenerateReleaseEntity(releaseName, buildId: buildId.ToString()); await PrepareEntityInDatabase(releaseEntity); diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetSucceededReleaseFromDbIntegrationTests.cs b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetSucceededReleaseFromDbIntegrationTests.cs index eefbe351723..aefbacfd4f1 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetSucceededReleaseFromDbIntegrationTests.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetSucceededReleaseFromDbIntegrationTests.cs @@ -23,7 +23,7 @@ public async Task Get_ShouldReturnCorrectRecordsFromDatabase(string org, string { int numberOfEntities = statusReleaseCombinationsInDb.Count; string tagName = Guid.NewGuid().ToString(); - var repository = new ORMReleaseRepository(DbFixture.DbContext); + var repository = new ReleaseRepository(DbFixture.DbContext); var releaseEntities = EntityGenerationUtils.Release.GenerateReleaseEntities(org, app, numberOfEntities).ToList(); for (int i = 0; i < numberOfEntities; i++) { diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetTopAndSortedIntegrationTests.cs b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetTopAndSortedIntegrationTests.cs index a83c9d57984..f5be7080a23 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetTopAndSortedIntegrationTests.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/GetTopAndSortedIntegrationTests.cs @@ -26,7 +26,7 @@ public async Task Get_ShouldReturnCorrectRecordsFromDatabase(string org, string var releaseEntities = EntityGenerationUtils.Release.GenerateReleaseEntities(org, app, allEntitiesCount).ToList(); await PrepareEntitiesInDatabase(releaseEntities); - var repository = new ORMReleaseRepository(DbFixture.DbContext); + var repository = new ReleaseRepository(DbFixture.DbContext); var query = new DocumentQueryModel { Top = top, SortDirection = sortDirection }; var result = (await repository.Get(org, app, query)).ToList(); @@ -49,7 +49,7 @@ public async Task Get_Without_TopDefined_ShouldReturnAllRecordsForGivenApp(strin var releaseEntities = EntityGenerationUtils.Release.GenerateReleaseEntities(org, app, allEntitiesCount).ToList(); await PrepareEntitiesInDatabase(releaseEntities); - var repository = new ORMReleaseRepository(DbFixture.DbContext); + var repository = new ReleaseRepository(DbFixture.DbContext); var query = new DocumentQueryModel { Top = null, diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/OldRepositoryCompatibility/CreateGetCompatibilityTests.cs b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/OldRepositoryCompatibility/CreateGetCompatibilityTests.cs deleted file mode 100644 index 36d4b02d55a..00000000000 --- a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/OldRepositoryCompatibility/CreateGetCompatibilityTests.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Altinn.Studio.Designer.Configuration; -using Altinn.Studio.Designer.Repository; -using Altinn.Studio.Designer.Repository.ORMImplementation; -using Designer.Tests.DbIntegrationTests.ReleaseEntityRepository.Base; -using Designer.Tests.Fixtures; -using FluentAssertions; -using Microsoft.Extensions.Logging; -using Moq; -using Xunit; - -namespace Designer.Tests.DbIntegrationTests.ReleaseEntityRepository.OldRepositoryCompatibility; - -public class CreateGetCompatibilityTests : ReleaseEntityIntegrationTestsBase -{ - public CreateGetCompatibilityTests(DesignerDbFixture dbFixture) : base(dbFixture) - { - } - - [Theory] - [InlineData("ttd")] - public async Task CreateOld_ShouldBeCompatibleWithNewGet(string org) - { - var oldRepository = CreateOldRepository(); - string buildId = Guid.NewGuid().ToString(); - var releaseEntity = EntityGenerationUtils.Release.GenerateReleaseEntity(org, buildId: buildId); - await oldRepository.Create(releaseEntity); - var newRepository = new ORMReleaseRepository(DbFixture.DbContext); - var queriedEntity = (await newRepository.Get(org, buildId)).Single(); - queriedEntity.Should().BeEquivalentTo(releaseEntity); - } - - [Theory] - [InlineData("ttd")] - public async Task CreateNew_ShouldBeCompatibleWithOldGet(string org) - { - var newRepository = new ORMReleaseRepository(DbFixture.DbContext); - string buildId = Guid.NewGuid().ToString(); - var releaseEntity = EntityGenerationUtils.Release.GenerateReleaseEntity(org, buildId: buildId); - await newRepository.Create(releaseEntity); - var oldRepository = CreateOldRepository(); - var queriedEntity = (await oldRepository.Get(org, buildId)).Single(); - queriedEntity.Should().BeEquivalentTo(releaseEntity); - } - - private ReleaseRepository CreateOldRepository() - { - var options = new PostgreSQLSettings - { - ConnectionString = DbFixture.ConnectionString - }; - return new ReleaseRepository(options, new Mock>().Object); - } -} diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/UpdateIntegrationTests.cs b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/UpdateIntegrationTests.cs index b4393958d8d..61870528ace 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/UpdateIntegrationTests.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/UpdateIntegrationTests.cs @@ -20,7 +20,7 @@ public UpdateIntegrationTests(DesignerDbFixture dbFixture) : base(dbFixture) [InlineData("ttd")] public async Task UpdateReleaseEntityAsync_WhenCalled_ShouldUpdateReleaseEntity(string releaseName) { - var repository = new ORMReleaseRepository(DbFixture.DbContext); + var repository = new ReleaseRepository(DbFixture.DbContext); var buildId = Guid.NewGuid(); var releaseEntity = EntityGenerationUtils.Release.GenerateReleaseEntity(releaseName, buildId: buildId.ToString()); await PrepareEntityInDatabase(releaseEntity); diff --git a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/Utils/ReleaseEntityAsserts.cs b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/Utils/ReleaseEntityAsserts.cs index 5ec72950454..329e2078c26 100644 --- a/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/Utils/ReleaseEntityAsserts.cs +++ b/backend/tests/Designer.Tests/DbIntegrationTests/ReleaseEntityRepository/Utils/ReleaseEntityAsserts.cs @@ -8,7 +8,7 @@ namespace Designer.Tests.DbIntegrationTests; public static partial class EntityAssertions { - public static void AssertEqual(ReleaseEntity releaseEntity, Altinn.Studio.Designer.Repository.ORMImplementation.Models.Release dbRecord) + public static void AssertEqual(ReleaseEntity releaseEntity, Altinn.Studio.Designer.Repository.ORMImplementation.Models.ReleaseDbModel dbRecord) { dbRecord.App.Should().BeEquivalentTo(releaseEntity.App); dbRecord.Org.Should().BeEquivalentTo(releaseEntity.Org); @@ -18,10 +18,6 @@ public static void AssertEqual(ReleaseEntity releaseEntity, Altinn.Studio.Design var entityFromColumn = JsonSerializer.Deserialize(dbRecord.Entity, JsonOptions); entityFromColumn.Should().BeEquivalentTo(releaseEntity); - // Allow precision loss up to 100 milliseconds - TimeSpan tolerance = TimeSpan.FromMilliseconds(100); - TimeSpan difference = (releaseEntity.Created - dbRecord.Created).Duration(); - bool isWithinTolerance = difference <= tolerance; - isWithinTolerance.Should().BeTrue(); + dbRecord.Created.Should().BeCloseTo(releaseEntity.Created, TimeSpan.FromMilliseconds(100)); } } diff --git a/backend/tests/Designer.Tests/Fixtures/DesignerDbFixtureExtensions.cs b/backend/tests/Designer.Tests/Fixtures/DesignerDbFixtureExtensions.cs index 667f3f094bc..ad5d48082ae 100644 --- a/backend/tests/Designer.Tests/Fixtures/DesignerDbFixtureExtensions.cs +++ b/backend/tests/Designer.Tests/Fixtures/DesignerDbFixtureExtensions.cs @@ -25,7 +25,7 @@ public static async Task PrepareAppScopesEntityInDatabaseAsync(this DesignerDbFi } - private static Altinn.Studio.Designer.Repository.ORMImplementation.Models.AppScopesDbObject MapToDbObject(AppScopesEntity entity) => + private static Altinn.Studio.Designer.Repository.ORMImplementation.Models.AppScopesDbModel MapToDbObject(AppScopesEntity entity) => new() { App = entity.App, diff --git a/compose.yaml b/compose.yaml index 21efd5c0bf5..fcabb46f577 100644 --- a/compose.yaml +++ b/compose.yaml @@ -7,6 +7,7 @@ volumes: gitea-attachments-data: keys: pgdata: + pgadmindata: redisdata: services: @@ -189,6 +190,19 @@ services: - PGPASSWORD=${POSTGRES_PASSWORD} - PGDATABASE=designerdb + pgadmin: + image: dpage/pgadmin4 + ports: + - "81:80" + environment: + PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL:-pgadmin4@pgadmin.org} + PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD:-admin} + PGADMIN_CONFIG_SERVER_MODE: 'False' + volumes: + - pgadmindata:/var/lib/pgadmin + extra_hosts: + - 'host.docker.internal:host-gateway' + redis: image: redis:alpine container_name: redis @@ -207,3 +221,5 @@ services: - REDIS_HOSTS=redis ports: - "8081:8081" + +