From 2a0411a93a228dd9217f170a0c23e628019a250a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikkel=20N=C3=B8rfeldt=20Friborg?= Date: Tue, 5 Nov 2024 17:07:35 +0100 Subject: [PATCH] GitHub Secrets setup test --- .github/workflows/DB-Angora.yml | 76 +++++++++ .../Services_InMemTest/RabbitService_MST.cs | 79 +++------ DB-AngoraREST/DB_DataStarter/DbInitializer.cs | 154 ------------------ DB-AngoraREST/Program.cs | 11 +- .../DB-Angora/apis1.arm.json | 132 +++++++++++++++ .../serviceDependencies.DB-Angora.json | 8 + .../Properties/serviceDependencies.json | 7 + DB-AngoraREST/Settings/ConnectionStrings.cs | 8 + DB-AngoraREST/Settings/EmailSettings.cs | 11 ++ DB-AngoraREST/Settings/JwtSettings.cs | 9 + 10 files changed, 283 insertions(+), 212 deletions(-) create mode 100644 .github/workflows/DB-Angora.yml delete mode 100644 DB-AngoraREST/DB_DataStarter/DbInitializer.cs create mode 100644 DB-AngoraREST/Properties/ServiceDependencies/DB-Angora/apis1.arm.json create mode 100644 DB-AngoraREST/Properties/serviceDependencies.DB-Angora.json create mode 100644 DB-AngoraREST/Properties/serviceDependencies.json create mode 100644 DB-AngoraREST/Settings/ConnectionStrings.cs create mode 100644 DB-AngoraREST/Settings/EmailSettings.cs create mode 100644 DB-AngoraREST/Settings/JwtSettings.cs diff --git a/.github/workflows/DB-Angora.yml b/.github/workflows/DB-Angora.yml new file mode 100644 index 0000000..0cce4b4 --- /dev/null +++ b/.github/workflows/DB-Angora.yml @@ -0,0 +1,76 @@ +name: Build and deploy .NET Core application to Web App DB-Angora with API Management Service DB-AngoraManagerAPI-DB-AngoraAPI +on: + push: + branches: + - master +env: + AZURE_WEBAPP_NAME: DB-Angora + AZURE_WEBAPP_PACKAGE_PATH: DB-AngoraREST\publish + AZURE_APIM_RESOURCE_PATH: / + AZURE_APIM_RESOURCEGROUP: MikksRG + AZURE_APIM_SERVICENAME: DB-AngoraManagerAPI + AZURE_APIM_API_ID: DB-AngoraAPI + AZURE_APIM_APPSERVICEURL: https://db-angora.azurewebsites.net + SWASHBUCLE_ASPNET_CORE_CLI_PACKAGE_VERSION: 5.6.3 + SWASHBUCKLE_DOTNET_CORE_VERSION: 3.1.x + API_IMPORT_SPECIFICATION_PATH: DB-AngoraREST\publish\swagger.json + API_IMPORT_DLL: DB-AngoraREST\bin\Release\net8.0\DB-AngoraREST.dll + API_IMPORT_VERSION: v1 + CONFIGURATION: Release + DOTNET_CORE_VERSION: 8.0.x + WORKING_DIRECTORY: DB-AngoraREST +jobs: + build: + runs-on: windows-latest + steps: + - uses: actions/checkout@v4 + - name: Setup .NET SDK + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ env.DOTNET_CORE_VERSION }} + - name: Setup SwashBuckle .NET Core + uses: actions/setup-dotnet@v3 + with: + dotnet-version: ${{ env.SWASHBUCKLE_DOTNET_CORE_VERSION }} + - name: Restore + run: dotnet restore ${{ env.WORKING_DIRECTORY }} + - name: Build + run: dotnet build ${{ env.WORKING_DIRECTORY }} --configuration ${{ env.CONFIGURATION }} --no-restore + - name: Test + run: dotnet test ${{ env.WORKING_DIRECTORY }} --no-build + - name: Publish + run: dotnet publish ${{ env.WORKING_DIRECTORY }} --configuration ${{ env.CONFIGURATION }} --no-build --output ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + - name: Install Swashbuckle CLI .NET Global Tool + run: dotnet tool install --global Swashbuckle.AspNetCore.Cli --version ${{ env.SWASHBUCLE_ASPNET_CORE_CLI_PACKAGE_VERSION }} + working-directory: ${{ env.WORKING_DIRECTORY }} + - name: Generate Open API Specification Document + run: swagger tofile --output "${{ env.API_IMPORT_SPECIFICATION_PATH }}" "${{ env.API_IMPORT_DLL }}" "${{ env.API_IMPORT_VERSION }}" + - name: Publish Artifacts + uses: actions/upload-artifact@v3 + with: + name: webapp + path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + deploy: + runs-on: windows-latest + needs: build + steps: + - name: Download artifact from build job + uses: actions/download-artifact@v3 + with: + name: webapp + path: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + - name: Deploy to Azure WebApp + uses: azure/webapps-deploy@v2 + with: + app-name: ${{ env.AZURE_WEBAPP_NAME }} + package: ${{ env.AZURE_WEBAPP_PACKAGE_PATH }} + publish-profile: ${{ secrets.DB_Angora_2FD5 }} + - name: Azure Login + uses: azure/login@v1 + with: + creds: ${{ secrets.DB_AngoraManagerAPI_SPN }} + - name: Import API into Azure API Management + run: az apim api import --path "${{ env.AZURE_APIM_RESOURCE_PATH }}" --resource-group "${{ env.AZURE_APIM_RESOURCEGROUP }}" --service-name "${{ env.AZURE_APIM_SERVICENAME }}" --api-id "${{ env.AZURE_APIM_API_ID }}" --service-url "${{ env.AZURE_APIM_APPSERVICEURL }}" --specification-path "${{ env.API_IMPORT_SPECIFICATION_PATH }}" --specification-format OpenApi --subscription-required false + - name: logout + run: > + az logout diff --git a/DB-AngoraMST/Services_InMemTest/RabbitService_MST.cs b/DB-AngoraMST/Services_InMemTest/RabbitService_MST.cs index b92d847..3f7b02c 100644 --- a/DB-AngoraMST/Services_InMemTest/RabbitService_MST.cs +++ b/DB-AngoraMST/Services_InMemTest/RabbitService_MST.cs @@ -3,6 +3,7 @@ using DB_AngoraLib.MockData; using DB_AngoraLib.Models; using DB_AngoraLib.Repository; +using DB_AngoraLib.SeededData; using DB_AngoraLib.Services.AccountService; using DB_AngoraLib.Services.BreederService; using DB_AngoraLib.Services.EmailService; @@ -24,69 +25,32 @@ namespace DB_AngoraMST.Services_InMemTest [TestClass] public class RabbitServices_MST { + private IRabbitService _rabbitService; - private IBreederService _breederService; + private Mock _breederServiceMock; + private Mock _validatorServiceMock; private DB_AngoraContext _context; - public RabbitServices_MST() + [TestInitialize] + public void Setup() { - // Setup in-memory database + // Configure in-memory database var options = new DbContextOptionsBuilder() - .UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()) + .UseInMemoryDatabase(databaseName: "TestDatabase") .Options; _context = new DB_AngoraContext(options); - _context.Database.EnsureCreated(); - - // Create repositories - var rabbitRepository = new GRepository(_context); - var breederRepository = new GRepository(_context); - // Initialize services with all required parameters - _breederService = new BreederServices(breederRepository); - var validatorService = new Rabbit_Validator(); - _rabbitService = new RabbitServices(rabbitRepository, _breederService, validatorService); - } + // Seed the database with initial data + SeedDatabase(_context); - [TestInitialize] - public void Setup() - { - // Add mock data to in-memory database - var mockUsersWithRoles = MockUsers.GetMockUsersWithRoles(); - foreach (var mockUserWithRole in mockUsersWithRoles) - { - _context.Users.Add(mockUserWithRole.User); - _context.SaveChanges(); - - foreach (var role in mockUserWithRole.Roles) - { - var roleId = MockRoles.GetMockRoles().First(r => r.Name == role).Id; - - // Assign role to user - var userRole = new IdentityUserRole - { - UserId = mockUserWithRole.User.Id, - RoleId = roleId - }; - _context.UserRoles.Add(userRole); - - // Assign claims to user - var roleClaims = RoleClaims.Get_AspNetRoleClaims(); - var claimsForRole = roleClaims.Where(rc => rc.RoleId == roleId).ToList(); - foreach (var claim in claimsForRole) - { - var userClaim = new IdentityUserClaim - { - UserId = mockUserWithRole.User.Id, - ClaimType = claim.ClaimType, - ClaimValue = claim.ClaimValue - }; - _context.UserClaims.Add(userClaim); - } - } - } + // Mock dependencies + _breederServiceMock = new Mock(); + _validatorServiceMock = new Mock(); - _context.SaveChanges(); + // Initialize RabbitServices with mocked dependencies + var repository = new GRepository(_context); + _rabbitService = new RabbitServices(repository, _breederServiceMock.Object, _validatorServiceMock.Object); } [TestCleanup] @@ -96,6 +60,12 @@ public void Cleanup() _context.Dispose(); } + private void SeedDatabase(DB_AngoraContext context) + { + var modelBuilder = new ModelBuilder(new Microsoft.EntityFrameworkCore.Metadata.Conventions.ConventionSet()); + SeedData.Seed(modelBuilder); + context.Database.EnsureCreated(); + } //-------------------------: ADD TESTS [TestMethod] @@ -469,9 +439,6 @@ await Assert.ThrowsExceptionAsync( var deletedRabbitOwned = await _context.Rabbits .FirstOrDefaultAsync(r => r.RightEarId == mockRabbitOwned.RightEarId && r.LeftEarId == mockRabbitOwned.LeftEarId); Assert.IsNull(deletedRabbitOwned); - } - - - + } } } diff --git a/DB-AngoraREST/DB_DataStarter/DbInitializer.cs b/DB-AngoraREST/DB_DataStarter/DbInitializer.cs deleted file mode 100644 index aac1503..0000000 --- a/DB-AngoraREST/DB_DataStarter/DbInitializer.cs +++ /dev/null @@ -1,154 +0,0 @@ -using DB_AngoraLib.EF_DbContext; -using DB_AngoraLib.MockData; -using DB_AngoraLib.Models; -using Microsoft.AspNetCore.Identity; -using System.Security.Claims; - -namespace DB_AngoraREST.DB_DataStarter -{ - public class DbInitializer - { - public static void Initialize(DB_AngoraContext context, UserManager userManager, RoleManager roleManager) - { - context.Database.EnsureCreated(); - - // Hvis databasen allerede indeholder brugere, skal vi ikke gøre noget mere. - if (context.Users.Any()) - { - return; - } - - // Opret roller - var mockRoles = MockRoles.GetMockRoles(); - foreach (var role in mockRoles) - { - if (!roleManager.RoleExistsAsync(role.Name).Result) - { - var result = roleManager.CreateAsync(role).Result; - if (!result.Succeeded) - { - throw new Exception($"Failed to create role {role.Name}"); - } - } - } - - // Tilføj RoleClaims til roller - var roleClaims = RoleClaims.Get_AspNetRoleClaims(); - foreach (var roleClaim in roleClaims) - { - var role = roleManager.FindByIdAsync(roleClaim.RoleId).Result; - if (role != null) - { - var claim = new Claim(roleClaim.ClaimType, roleClaim.ClaimValue); - var hasClaim = roleManager.GetClaimsAsync(role).Result.Any(c => c.Type == claim.Type && c.Value == claim.Value); - if (!hasClaim) - { - var result = roleManager.AddClaimAsync(role, claim).Result; - if (!result.Succeeded) - { - throw new Exception($"Failed to add claim {claim.Type} to role {role.Name}"); - } - } - } - } - - //// Brug MockDataInitializer til at tilføje mock brugere og deres roller - //var mockDataInitializer = new MockDataInitializer(context, userManager); // TODO: Test om dette virker fremfor nedestående //'kode' - //mockDataInitializer.Initialize(); - - - // Hent mock brugere og deres roller - var mockUsersWithRoles = MockUsers.GetMockUsersWithRoles(); - - foreach (var mockUserWithRole in mockUsersWithRoles) - { - // Opret brugeren - var user = mockUserWithRole.User; - var result = userManager.CreateAsync(user, user.Password).Result; - - if (result.Succeeded) - { - // Tildel roller til brugeren - foreach (var role in mockUserWithRole.Roles) - { - if (roleManager.RoleExistsAsync(role).Result) - { - userManager.AddToRoleAsync(user, role).Wait(); - } - } - - // Tildel unikke UserClaims til brugeren - var userClaims = MockUserClaims.GetMockUserClaimsForUser(user); - userManager.AddClaimsAsync(user, userClaims).Wait(); - } - } - - var mockRabbits = MockRabbits.GetMockRabbits(); - - // Indlæs alle kaninerne med Father_EarCombId og Mother_EarCombId sat til null - foreach (var rabbit in mockRabbits) - { - // Set the User property of the Rabbit object to the corresponding User - //rabbit.UserOwner = context.Users.FirstOrDefault(u => u.Id == rabbit.OwnerId); - rabbit.UserOwner = (Breeder?)context.Users.FirstOrDefault(u => u.Id == rabbit.OwnerId); - - // Flyt Father_EarCombId og Mother_EarCombId til placeholder properties og sæt dem til null - //rabbit.FatherId_Placeholder = rabbit.Father_EarCombId; - //rabbit.MotherId_Placeholder = rabbit.Mother_EarCombId; - //rabbit.Father_EarCombId = null; - //rabbit.Mother_EarCombId = null; - - context.Rabbits.Add(rabbit); - } - - context.SaveChanges(); - - // Opdater Father_EarCombId og Mother_EarCombId for hver kanin - foreach (var rabbit in mockRabbits) - { - var dbRabbit = context.Rabbits.FirstOrDefault(r => r.EarCombId == rabbit.EarCombId); - if (dbRabbit != null) - { - var father = context.Rabbits.FirstOrDefault(r => r.EarCombId == rabbit.FatherId_Placeholder); - var mother = context.Rabbits.FirstOrDefault(r => r.EarCombId == rabbit.MotherId_Placeholder); - - if (father != null) - { - dbRabbit.Father_EarCombId = father.EarCombId; - } - else if (rabbit.FatherId_Placeholder != null) - { - throw new Exception($"Father with EarCombId {rabbit.FatherId_Placeholder} not found for rabbit {rabbit.EarCombId}"); - } - - if (mother != null) - { - dbRabbit.Mother_EarCombId = mother.EarCombId; - } - else if (rabbit.MotherId_Placeholder != null) - { - throw new Exception($"Mother with EarCombId {rabbit.MotherId_Placeholder} not found for rabbit {rabbit.EarCombId}"); - } - } - } - - context.SaveChanges(); - - //// Tilføj mock BreederBrands uden Id - //var mockBreederBrands = MockBreederBrand.GetMockBreederBrands(); - //foreach (var breederBrand in mockBreederBrands) - //{ - // // Ensure the referenced user exists - // var user = context.Users.FirstOrDefault(u => u.Id == breederBrand.UserId); - // if (user != null) - // { - // breederBrand.Id = 0; // Ensure Id is set to 0 so it will be auto-generated - // context.BreederBrands.Add(breederBrand); - // } - //} - - //context.SaveChanges(); - - } - } -} \ No newline at end of file diff --git a/DB-AngoraREST/Program.cs b/DB-AngoraREST/Program.cs index 90cc93d..87e119e 100644 --- a/DB-AngoraREST/Program.cs +++ b/DB-AngoraREST/Program.cs @@ -10,7 +10,6 @@ using DB_AngoraLib.Services.TokenService; using DB_AngoraLib.Services.TransferService; using DB_AngoraLib.Services.ValidationService; -using DB_AngoraREST.DB_DataStarter; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; @@ -20,9 +19,16 @@ using System.Text.Json.Serialization; using DB_AngoraLib.Services.BreederBrandService; using DB_AngoraLib.Services.BreederService; +using DB_AngoraREST.Settings; var builder = WebApplication.CreateBuilder(args); +// Load secrets from environment variables +builder.Configuration.AddEnvironmentVariables(); +builder.Services.Configure(builder.Configuration.GetSection("ConnectionStrings")); +builder.Services.Configure(builder.Configuration.GetSection("Jwt")); +builder.Services.Configure(builder.Configuration.GetSection("EmailSettings")); + // Add services to the container. //-----------------: DB-AngoraLib Services @@ -68,7 +74,8 @@ // -----------------: DB CONNECTION-STRING & MIGRATION SETUP builder.Services.AddDbContext(options => - options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"), + //options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"), + options.UseSqlServer(builder.Configuration.GetConnectionString("SecretConnection"), b => b.MigrationsAssembly("DB-AngoraREST"))); diff --git a/DB-AngoraREST/Properties/ServiceDependencies/DB-Angora/apis1.arm.json b/DB-AngoraREST/Properties/ServiceDependencies/DB-Angora/apis1.arm.json new file mode 100644 index 0000000..7b84baa --- /dev/null +++ b/DB-AngoraREST/Properties/ServiceDependencies/DB-Angora/apis1.arm.json @@ -0,0 +1,132 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "resourceGroupName": { + "type": "string", + "defaultValue": "MikksRG", + "metadata": { + "_parameterType": "resourceGroup", + "description": "Name of the resource group for the resource. It is recommended to put resources under same resource group for better tracking." + } + }, + "resourceGroupLocation": { + "type": "string", + "defaultValue": "canadacentral", + "metadata": { + "_parameterType": "location", + "description": "Location of the resource group. Resource groups could have different location than resources." + } + }, + "resourceLocation": { + "type": "string", + "defaultValue": "[parameters('resourceGroupLocation')]", + "metadata": { + "_parameterType": "location", + "description": "Location of the resource. By default use resource group's location, unless the resource provider is not supported there." + } + } + }, + "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "name": "[parameters('resourceGroupName')]", + "location": "[parameters('resourceGroupLocation')]", + "apiVersion": "2019-10-01" + }, + { + "type": "Microsoft.Resources/deployments", + "name": "[concat(parameters('resourceGroupName'), 'Deployment', uniqueString(concat('DB-AngoraAPI', subscription().subscriptionId)))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "apiVersion": "2019-10-01", + "dependsOn": [ + "[parameters('resourceGroupName')]" + ], + "properties": { + "mode": "Incremental", + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "resources": [ + { + "name": "DB-AngoraManagerAPI", + "type": "Microsoft.ApiManagement/service", + "location": "[parameters('resourceLocation')]", + "properties": { + "publisherEmail": "mikk.fri@gmail.com", + "publisherName": "Mikkel Friborg", + "notificationSenderEmail": "apimgmt-noreply@mail.windowsazure.com", + "hostnameConfigurations": [ + { + "type": "Proxy", + "hostName": "db-angoramanagerapi.azure-api.net", + "encodedCertificate": null, + "keyVaultId": null, + "certificatePassword": null, + "negotiateClientCertificate": false, + "certificate": null, + "defaultSslBinding": true + } + ], + "publicIPAddresses": null, + "privateIPAddresses": null, + "additionalLocations": null, + "virtualNetworkConfiguration": null, + "customProperties": { + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls10": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Protocols.Tls11": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls10": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Tls11": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Security.Backend.Protocols.Ssl30": "False", + "Microsoft.WindowsAzure.ApiManagement.Gateway.Protocols.Server.Http2": "False" + }, + "virtualNetworkType": "None", + "certificates": null, + "disableGateway": false, + "apiVersionConstraint": { + "minApiVersion": null + } + }, + "sku": { + "name": "Consumption", + "capacity": 0 + }, + "apiVersion": "2019-12-01" + }, + { + "type": "Microsoft.ApiManagement/service/apis", + "name": "DB-AngoraManagerAPI/DB-AngoraAPI", + "properties": { + "displayName": "DB-AngoraAPI", + "apiRevision": "1", + "description": null, + "subscriptionRequired": true, + "serviceUrl": null, + "path": "", + "protocols": [ + "https" + ], + "authenticationSettings": { + "oAuth2": null, + "openid": null + }, + "subscriptionKeyParameterNames": { + "header": "Ocp-Apim-Subscription-Key", + "query": "subscription-key" + }, + "isCurrent": true + }, + "apiVersion": "2019-12-01", + "dependsOn": [ + "DB-AngoraManagerAPI" + ] + } + ] + } + } + } + ], + "metadata": { + "_dependencyType": "apis.azure" + } +} \ No newline at end of file diff --git a/DB-AngoraREST/Properties/serviceDependencies.DB-Angora.json b/DB-AngoraREST/Properties/serviceDependencies.DB-Angora.json new file mode 100644 index 0000000..8e12ff7 --- /dev/null +++ b/DB-AngoraREST/Properties/serviceDependencies.DB-Angora.json @@ -0,0 +1,8 @@ +{ + "dependencies": { + "apis1": { + "resourceId": "/subscriptions/[parameters('subscriptionId')]/resourceGroups/[parameters('resourceGroupName')]/providers/Microsoft.ApiManagement/service/DB-AngoraManagerAPI/apis/DB-AngoraAPI", + "type": "apis.azure" + } + } +} \ No newline at end of file diff --git a/DB-AngoraREST/Properties/serviceDependencies.json b/DB-AngoraREST/Properties/serviceDependencies.json new file mode 100644 index 0000000..e32266d --- /dev/null +++ b/DB-AngoraREST/Properties/serviceDependencies.json @@ -0,0 +1,7 @@ +{ + "dependencies": { + "apis1": { + "type": "apis" + } + } +} \ No newline at end of file diff --git a/DB-AngoraREST/Settings/ConnectionStrings.cs b/DB-AngoraREST/Settings/ConnectionStrings.cs new file mode 100644 index 0000000..32aeff1 --- /dev/null +++ b/DB-AngoraREST/Settings/ConnectionStrings.cs @@ -0,0 +1,8 @@ +namespace DB_AngoraREST.Settings +{ + public class ConnectionStrings + { + public string DefaultConnection { get; set; } + public string SecretConnection { get; set; } + } +} diff --git a/DB-AngoraREST/Settings/EmailSettings.cs b/DB-AngoraREST/Settings/EmailSettings.cs new file mode 100644 index 0000000..5ef371e --- /dev/null +++ b/DB-AngoraREST/Settings/EmailSettings.cs @@ -0,0 +1,11 @@ +namespace DB_AngoraREST.Settings +{ + public class EmailSettings + { + public string SmtpHost { get; set; } + public int SmtpPort { get; set; } + public string FromEmail { get; set; } + public string SmtpUser { get; set; } + public string SmtpPass { get; set; } + } +} diff --git a/DB-AngoraREST/Settings/JwtSettings.cs b/DB-AngoraREST/Settings/JwtSettings.cs new file mode 100644 index 0000000..bc95552 --- /dev/null +++ b/DB-AngoraREST/Settings/JwtSettings.cs @@ -0,0 +1,9 @@ +namespace DB_AngoraREST.Settings +{ + public class JwtSettings + { + public string Issuer { get; set; } + public string Audience { get; set; } + public string Key { get; set; } + } +}