From 691090ddb905b0687fed868ebc8935cca4fc9759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Mon, 29 Jan 2024 23:56:01 +0100 Subject: [PATCH 01/15] Closes #60 Introduce Bruno collection for easy API testing --- .../bruno/KSummarized/ToDo/List/Create.bru | 23 +++++++++++++++++++ .../bruno/KSummarized/ToDo/List/Delete.bru | 19 +++++++++++++++ backend/bruno/KSummarized/ToDo/List/Edit.bru | 19 +++++++++++++++ backend/bruno/KSummarized/ToDo/List/List.bru | 15 ++++++++++++ backend/bruno/KSummarized/bruno.json | 5 ++++ .../bruno/KSummarized/environments/Local.bru | 3 +++ 6 files changed, 84 insertions(+) create mode 100644 backend/bruno/KSummarized/ToDo/List/Create.bru create mode 100644 backend/bruno/KSummarized/ToDo/List/Delete.bru create mode 100644 backend/bruno/KSummarized/ToDo/List/Edit.bru create mode 100644 backend/bruno/KSummarized/ToDo/List/List.bru create mode 100644 backend/bruno/KSummarized/bruno.json create mode 100644 backend/bruno/KSummarized/environments/Local.bru diff --git a/backend/bruno/KSummarized/ToDo/List/Create.bru b/backend/bruno/KSummarized/ToDo/List/Create.bru new file mode 100644 index 0000000..a5895a6 --- /dev/null +++ b/backend/bruno/KSummarized/ToDo/List/Create.bru @@ -0,0 +1,23 @@ +meta { + name: Create + type: http + seq: 2 +} + +post { + url: https://localhost:5000/api/todo/lists + body: json + auth: bearer +} + +auth:bearer { + token: {{token}} +} + +body:json { + "ToDo" +} + +vars:post-response { + listId: res.body.id +} diff --git a/backend/bruno/KSummarized/ToDo/List/Delete.bru b/backend/bruno/KSummarized/ToDo/List/Delete.bru new file mode 100644 index 0000000..599b3cc --- /dev/null +++ b/backend/bruno/KSummarized/ToDo/List/Delete.bru @@ -0,0 +1,19 @@ +meta { + name: Delete + type: http + seq: 4 +} + +delete { + url: https://localhost:5000/api/todo/lists/{{listId}} + body: json + auth: bearer +} + +auth:bearer { + token: {{token}} +} + +body:json { + "ToDo" +} diff --git a/backend/bruno/KSummarized/ToDo/List/Edit.bru b/backend/bruno/KSummarized/ToDo/List/Edit.bru new file mode 100644 index 0000000..635062f --- /dev/null +++ b/backend/bruno/KSummarized/ToDo/List/Edit.bru @@ -0,0 +1,19 @@ +meta { + name: Edit + type: http + seq: 3 +} + +post { + url: https://localhost:5000/api/todo/lists/{{listId}} + body: json + auth: bearer +} + +auth:bearer { + token: {{token}} +} + +body:json { + "ToDo2" +} diff --git a/backend/bruno/KSummarized/ToDo/List/List.bru b/backend/bruno/KSummarized/ToDo/List/List.bru new file mode 100644 index 0000000..174fd00 --- /dev/null +++ b/backend/bruno/KSummarized/ToDo/List/List.bru @@ -0,0 +1,15 @@ +meta { + name: List + type: http + seq: 1 +} + +get { + url: https://localhost:5000/api/todo/lists + body: none + auth: bearer +} + +auth:bearer { + token: {{token}} +} diff --git a/backend/bruno/KSummarized/bruno.json b/backend/bruno/KSummarized/bruno.json new file mode 100644 index 0000000..a2739bb --- /dev/null +++ b/backend/bruno/KSummarized/bruno.json @@ -0,0 +1,5 @@ +{ + "version": "1", + "name": "KSummarized", + "type": "collection" +} \ No newline at end of file diff --git a/backend/bruno/KSummarized/environments/Local.bru b/backend/bruno/KSummarized/environments/Local.bru new file mode 100644 index 0000000..35d3972 --- /dev/null +++ b/backend/bruno/KSummarized/environments/Local.bru @@ -0,0 +1,3 @@ +vars:secret [ + token +] From 155eb653ae7d1abe4439df42ade6d77cf256d3da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Sat, 30 Mar 2024 00:04:39 +0100 Subject: [PATCH 02/15] Closes #60 Small refactoring. Bump up package with vulnerability. Remove not needed global.json file --- backend/src/api/Data/KeycloakJwtOptions.cs | 14 +++----------- backend/src/api/Program.cs | 10 +--------- backend/src/api/api.csproj | 2 +- global.json | 6 ------ 4 files changed, 5 insertions(+), 27 deletions(-) delete mode 100644 global.json diff --git a/backend/src/api/Data/KeycloakJwtOptions.cs b/backend/src/api/Data/KeycloakJwtOptions.cs index a72287e..42f458c 100644 --- a/backend/src/api/Data/KeycloakJwtOptions.cs +++ b/backend/src/api/Data/KeycloakJwtOptions.cs @@ -3,15 +3,7 @@ namespace api.Data; public class KeycloakJwtOptions { - public required string Issuer { get; set; } - public required string Audience { get; set; } - public required string Secret { get; set; } - - [SetsRequiredMembers] - public KeycloakJwtOptions(string issuer, string audience, string secret) - { - Issuer = issuer; - Audience = audience; - Secret = secret; - } + public required string Issuer { get; init; } + public required string Audience { get; init; } + public required string Secret { get; init; } } diff --git a/backend/src/api/Program.cs b/backend/src/api/Program.cs index 681bf74..35b8666 100644 --- a/backend/src/api/Program.cs +++ b/backend/src/api/Program.cs @@ -20,15 +20,7 @@ builder.Services.AddHttpContextAccessor(); builder.Host.UseSerilog(); builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration.GetConnectionString("KSummarized"))); - var keycloakJwtOptions = new KeycloakJwtOptions( - builder.Configuration.GetValue("KeycloakJwt:Issuer")!, - builder.Configuration.GetValue("KeycloakJwt:Audience")!, - builder.Configuration.GetValue("KeycloakJwt:Secret")! - ); - if(keycloakJwtOptions.Secret is null || keycloakJwtOptions.Issuer is null || keycloakJwtOptions.Audience is null) - { - throw new Exception("Can't start application without JWT options"); - } + var keycloakJwtOptions = builder.Configuration.GetRequiredSection("KeycloakJwt").Get()!; // Create RSA key for offline validation of Keycloak token RSA rsa = RSA.Create(); diff --git a/backend/src/api/api.csproj b/backend/src/api/api.csproj index 217deeb..6d537d7 100644 --- a/backend/src/api/api.csproj +++ b/backend/src/api/api.csproj @@ -20,7 +20,7 @@ - + diff --git a/global.json b/global.json deleted file mode 100644 index 8058dfb..0000000 --- a/global.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "sdk": { - "version": "7.0.405", - "rollForward": "latestFeature" - } -} From 1e7f2bd01cea32e75be2c732364172ab57a403c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Sat, 30 Mar 2024 22:34:33 +0100 Subject: [PATCH 03/15] Closes #60 Initial implementation of getting all of users Todo Lists. --- backend/src/api/Controllers/TodoController.cs | 27 ++++++++++ backend/src/api/Data/ApplicationDbContext.cs | 3 +- backend/src/api/Data/DAO/TodoListModel.cs | 14 +++++ backend/src/api/Data/DTO/TodoListDTO.cs | 3 ++ .../20240330212015_TodoList.Designer.cs | 51 +++++++++++++++++++ .../api/Migrations/20240330212015_TodoList.cs | 37 ++++++++++++++ .../ApplicationDbContextModelSnapshot.cs | 17 ++++--- backend/src/api/Program.cs | 4 +- backend/src/api/Services/Todo/ITodoService.cs | 8 +++ backend/src/api/Services/Todo/TodoService.cs | 24 +++++++++ 10 files changed, 177 insertions(+), 11 deletions(-) create mode 100644 backend/src/api/Controllers/TodoController.cs create mode 100644 backend/src/api/Data/DAO/TodoListModel.cs create mode 100644 backend/src/api/Data/DTO/TodoListDTO.cs create mode 100644 backend/src/api/Migrations/20240330212015_TodoList.Designer.cs create mode 100644 backend/src/api/Migrations/20240330212015_TodoList.cs create mode 100644 backend/src/api/Services/Todo/ITodoService.cs create mode 100644 backend/src/api/Services/Todo/TodoService.cs diff --git a/backend/src/api/Controllers/TodoController.cs b/backend/src/api/Controllers/TodoController.cs new file mode 100644 index 0000000..ee522a4 --- /dev/null +++ b/backend/src/api/Controllers/TodoController.cs @@ -0,0 +1,27 @@ +using api.Data.DTO; +using api.Services; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; + +namespace api.Controllers; + +[Authorize] +[Route("/api/todo")] +[ApiController] +public class TodoController : ControllerBase +{ + private readonly ITodoService _service; + + public TodoController(ITodoService service) => _service = service; + + [HttpGet("lists")] + public IActionResult List() + { + return Request.UserId() switch + { + null => Unauthorized(), + var user => Ok(_service.GetLists(user)), + }; + } + +} diff --git a/backend/src/api/Data/ApplicationDbContext.cs b/backend/src/api/Data/ApplicationDbContext.cs index 0ec7301..8bbcdb8 100644 --- a/backend/src/api/Data/ApplicationDbContext.cs +++ b/backend/src/api/Data/ApplicationDbContext.cs @@ -1,5 +1,4 @@ using api.Data.DAO; -using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; namespace api.Data; @@ -9,5 +8,5 @@ public class ApplicationDbContext : DbContext public ApplicationDbContext(DbContextOptions options) : base(options) { } - public DbSet Users { get; set; } + public DbSet TodoLists { get; set; } } diff --git a/backend/src/api/Data/DAO/TodoListModel.cs b/backend/src/api/Data/DAO/TodoListModel.cs new file mode 100644 index 0000000..f2f8a57 --- /dev/null +++ b/backend/src/api/Data/DAO/TodoListModel.cs @@ -0,0 +1,14 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; + +namespace api.Data.DAO; + +[Table("todo_lists")] +public class TodoListModel +{ + [Key] + public int Id { get; set; } + [MaxLength(512)] + public required string Name { get; set; } + public required Guid Owner { get; set; } +} diff --git a/backend/src/api/Data/DTO/TodoListDTO.cs b/backend/src/api/Data/DTO/TodoListDTO.cs new file mode 100644 index 0000000..275e8d4 --- /dev/null +++ b/backend/src/api/Data/DTO/TodoListDTO.cs @@ -0,0 +1,3 @@ +namespace api.Data.DTO; + +public record TodoListDTO(int Id, string Name); diff --git a/backend/src/api/Migrations/20240330212015_TodoList.Designer.cs b/backend/src/api/Migrations/20240330212015_TodoList.Designer.cs new file mode 100644 index 0000000..121acfa --- /dev/null +++ b/backend/src/api/Migrations/20240330212015_TodoList.Designer.cs @@ -0,0 +1,51 @@ +// +using System; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; +using api.Data; + +#nullable disable + +namespace api.Migrations +{ + [DbContext(typeof(ApplicationDbContext))] + [Migration("20240330212015_TodoList")] + partial class TodoList + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasAnnotation("ProductVersion", "8.0.3") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("api.Data.DAO.TodoListModel", b => + { + b.Property("Id") + .ValueGeneratedOnAdd() + .HasColumnType("integer"); + + NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + + b.Property("Name") + .IsRequired() + .HasMaxLength(512) + .HasColumnType("character varying(512)"); + + b.Property("Owner") + .HasColumnType("uuid"); + + b.HasKey("Id"); + + b.ToTable("todo_lists"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/backend/src/api/Migrations/20240330212015_TodoList.cs b/backend/src/api/Migrations/20240330212015_TodoList.cs new file mode 100644 index 0000000..e0bf4b0 --- /dev/null +++ b/backend/src/api/Migrations/20240330212015_TodoList.cs @@ -0,0 +1,37 @@ +using System; +using Microsoft.EntityFrameworkCore.Migrations; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace api.Migrations +{ + /// + public partial class TodoList : Migration + { + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "todo_lists", + columns: table => new + { + Id = table.Column(type: "integer", nullable: false) + .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), + Name = table.Column(type: "character varying(512)", maxLength: 512, nullable: false), + Owner = table.Column(type: "uuid", nullable: false) + }, + constraints: table => + { + table.PrimaryKey("PK_todo_lists", x => x.Id); + }); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "todo_lists"); + } + } +} diff --git a/backend/src/api/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/api/Migrations/ApplicationDbContextModelSnapshot.cs index c339c3c..1b642d3 100644 --- a/backend/src/api/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/api/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,4 +1,5 @@ // +using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; @@ -16,12 +17,12 @@ protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder - .HasAnnotation("ProductVersion", "7.0.2") + .HasAnnotation("ProductVersion", "8.0.3") .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - modelBuilder.Entity("api.Data.DAO.UserModel", b => + modelBuilder.Entity("api.Data.DAO.TodoListModel", b => { b.Property("Id") .ValueGeneratedOnAdd() @@ -29,17 +30,17 @@ protected override void BuildModel(ModelBuilder modelBuilder) NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - b.Property("Email") + b.Property("Name") .IsRequired() - .HasColumnType("text"); + .HasMaxLength(512) + .HasColumnType("character varying(512)"); - b.Property("KeycloakUuid") - .IsRequired() - .HasColumnType("text"); + b.Property("Owner") + .HasColumnType("uuid"); b.HasKey("Id"); - b.ToTable("Users"); + b.ToTable("todo_lists"); }); #pragma warning restore 612, 618 } diff --git a/backend/src/api/Program.cs b/backend/src/api/Program.cs index 35b8666..85ecb6c 100644 --- a/backend/src/api/Program.cs +++ b/backend/src/api/Program.cs @@ -7,8 +7,9 @@ using Serilog; using api; using System.Security.Cryptography; +using api.Services; -const string logFormat = "[{Timestamp:HH:mm:ss} {Level:u3}] {CorelationId} | {Message:lj}{NewLine}{Exception}"; +const string logFormat = "[{Timestamp:HH:mm:ss} {Level:u3}] {CorelationId} | {Message:lj}{NewLine}{Exception}"; Log.Logger = new LoggerConfiguration().Enrich.WithCorrelationId() .WriteTo .Console(outputTemplate: logFormat) @@ -61,6 +62,7 @@ c.SwaggerDoc("v1", new OpenApiInfo { Title = "api", Version = "v1" }); }); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddCors(options => { diff --git a/backend/src/api/Services/Todo/ITodoService.cs b/backend/src/api/Services/Todo/ITodoService.cs new file mode 100644 index 0000000..ff5f22a --- /dev/null +++ b/backend/src/api/Services/Todo/ITodoService.cs @@ -0,0 +1,8 @@ +using api.Data.DTO; + +namespace api.Services; + +public interface ITodoService +{ + public IEnumerable GetLists(string userId); +} \ No newline at end of file diff --git a/backend/src/api/Services/Todo/TodoService.cs b/backend/src/api/Services/Todo/TodoService.cs new file mode 100644 index 0000000..f616843 --- /dev/null +++ b/backend/src/api/Services/Todo/TodoService.cs @@ -0,0 +1,24 @@ +using api.Data; +using api.Data.DTO; +using Microsoft.EntityFrameworkCore; + +namespace api.Services; + +public class TodoService : ITodoService +{ + + private readonly ApplicationDbContext _context; + + public TodoService(ApplicationDbContext context) + { + _context = context; + } + + public IEnumerable GetLists(string userId) + { + return _context.TodoLists.AsNoTracking() + .Where(list => list.Owner.Equals(Guid.Parse(userId))) + .Select(list => new TodoListDTO(list.Id, list.Name)) + .AsEnumerable(); + } +} From e29add65d37148421bde0cb15dd519123d0895b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Mon, 9 Sep 2024 21:02:55 +0200 Subject: [PATCH 04/15] Closes #60 Upgrade to .net 8 --- backend/Dockerfile | 4 +- .../Controllers/AuthenticationController.cs | 64 ------------------- .../api/Controllers/GreetingsController.cs | 26 -------- backend/src/api/Data/DAO/UserModel.cs | 12 ---- backend/src/api/Data/DTO/UserDTO.cs | 12 ---- .../20240223084836_Init.Designer.cs | 50 --------------- .../src/api/Migrations/20240223084836_Init.cs | 36 ----------- backend/src/api/Program.cs | 3 - backend/src/api/RequestExtensions.cs | 20 ++++++ .../src/api/Services/Users/IUserService.cs | 8 --- backend/src/api/Services/Users/UserService.cs | 33 ---------- backend/src/api/api.csproj | 20 +++--- frontend/src/helpers/RequireAuth.tsx | 15 ----- 13 files changed, 34 insertions(+), 269 deletions(-) delete mode 100644 backend/src/api/Controllers/AuthenticationController.cs delete mode 100644 backend/src/api/Controllers/GreetingsController.cs delete mode 100644 backend/src/api/Data/DAO/UserModel.cs delete mode 100644 backend/src/api/Data/DTO/UserDTO.cs delete mode 100644 backend/src/api/Migrations/20240223084836_Init.Designer.cs delete mode 100644 backend/src/api/Migrations/20240223084836_Init.cs create mode 100644 backend/src/api/RequestExtensions.cs delete mode 100644 backend/src/api/Services/Users/IUserService.cs delete mode 100644 backend/src/api/Services/Users/UserService.cs diff --git a/backend/Dockerfile b/backend/Dockerfile index 5990f8d..7a65178 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,11 +1,11 @@ #See https://aka.ms/containerfastmode to understand how Visual Studio uses this Dockerfile to build your images for faster debugging. -FROM mcr.microsoft.com/dotnet/aspnet:7.0.10-alpine3.18 AS base +FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base WORKDIR /app EXPOSE 80 EXPOSE 443 -FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY src/api/api.csproj src/api/ RUN dotnet restore "src/api/api.csproj" diff --git a/backend/src/api/Controllers/AuthenticationController.cs b/backend/src/api/Controllers/AuthenticationController.cs deleted file mode 100644 index de8b353..0000000 --- a/backend/src/api/Controllers/AuthenticationController.cs +++ /dev/null @@ -1,64 +0,0 @@ -using api.Constants; -using api.Data.DTO; -using api.Services.Users; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; -using System.IdentityModel.Tokens.Jwt; - -namespace api.Controllers; - -[ApiController] -[Route("/api/auth")] -public class AuthenticationController : ControllerBase -{ - private readonly IUserService _userService; - private readonly ILogger _logger; - - public AuthenticationController(IUserService userService, ILogger logger) - { - _userService = userService; - _logger = logger; - } - - [HttpGet("create-user")] - [Authorize] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] - public async Task CreateUser() - { - var accessToken = Request.Headers["Authorization"].ToString().Replace("Bearer ", ""); - var jsonTokenData = new JwtSecurityTokenHandler().ReadJwtToken(accessToken); - var keycloakUuid = jsonTokenData.Subject; - var userEmail = jsonTokenData.Claims.FirstOrDefault(claim => claim.Type == "email")?.Value; - - if (userEmail == null) - { - _logger.LogInformation("User with keycloakUuid={keycloakUuid} tried to log in without email.", keycloakUuid); - return BadRequest("Required data missing"); - } - - var (isSuccess, error) = await _userService.CreateKeycloakUser( - new UserDTO - { - KeycloakUuid = keycloakUuid, - Email = userEmail - } - ); - - if (isSuccess) - { - _logger.LogInformation("The user {email} has been created successfully.", userEmail); - return Ok("User created"); - } - else - { - if (error == ErrorMessages.UserAlreadyExists) - { - _logger.LogInformation("The user {email} has already been created.", userEmail); - return Ok("User is already created"); - } - _logger.LogInformation("Failure during creation of {email} user.", userEmail); - return BadRequest(error); - } - } -} diff --git a/backend/src/api/Controllers/GreetingsController.cs b/backend/src/api/Controllers/GreetingsController.cs deleted file mode 100644 index 665bed0..0000000 --- a/backend/src/api/Controllers/GreetingsController.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Mvc; - -namespace api.Controllers; - -[ApiController] -[Route("/api/greetings")] -public class GreetingsController : ControllerBase -{ - [HttpGet] - [Authorize] - [ProducesResponseType(StatusCodes.Status200OK)] - [Route("user")] - public IActionResult Greet() - { - return Ok($"Hello {HttpContext?.User?.Identity?.Name ?? "World" }"); - } - - [HttpGet] - [ProducesResponseType(StatusCodes.Status200OK)] - [Route("HelloWorld")] - public IActionResult HelloWorld() - { - return Ok("Hello World!"); - } -} diff --git a/backend/src/api/Data/DAO/UserModel.cs b/backend/src/api/Data/DAO/UserModel.cs deleted file mode 100644 index 12253d3..0000000 --- a/backend/src/api/Data/DAO/UserModel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace api.Data.DAO; - -public class UserModel -{ - [Key] - public int Id { get; set; } - public required string KeycloakUuid { get; set; } - [EmailAddress] - public required string Email { get; set; } -} diff --git a/backend/src/api/Data/DTO/UserDTO.cs b/backend/src/api/Data/DTO/UserDTO.cs deleted file mode 100644 index 7079db2..0000000 --- a/backend/src/api/Data/DTO/UserDTO.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.ComponentModel.DataAnnotations; - -namespace api.Data.DTO; - -public class UserDTO -{ - [Required] - public required string KeycloakUuid { get; set; } - [Required] - [EmailAddress] - public required string Email { get; set; } -} diff --git a/backend/src/api/Migrations/20240223084836_Init.Designer.cs b/backend/src/api/Migrations/20240223084836_Init.Designer.cs deleted file mode 100644 index a363f14..0000000 --- a/backend/src/api/Migrations/20240223084836_Init.Designer.cs +++ /dev/null @@ -1,50 +0,0 @@ -// -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using api.Data; - -#nullable disable - -namespace api.Migrations -{ - [DbContext(typeof(ApplicationDbContext))] - [Migration("20240223084836_Init")] - partial class Init - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder - .HasAnnotation("ProductVersion", "7.0.2") - .HasAnnotation("Relational:MaxIdentifierLength", 63); - - NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); - - modelBuilder.Entity("api.Data.DAO.UserModel", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("integer"); - - NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); - - b.Property("Email") - .IsRequired() - .HasColumnType("text"); - - b.Property("KeycloakUuid") - .IsRequired() - .HasColumnType("text"); - - b.HasKey("Id"); - - b.ToTable("Users"); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/backend/src/api/Migrations/20240223084836_Init.cs b/backend/src/api/Migrations/20240223084836_Init.cs deleted file mode 100644 index 9f4ae7a..0000000 --- a/backend/src/api/Migrations/20240223084836_Init.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Microsoft.EntityFrameworkCore.Migrations; -using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; - -#nullable disable - -namespace api.Migrations -{ - /// - public partial class Init : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "Users", - columns: table => new - { - Id = table.Column(type: "integer", nullable: false) - .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - KeycloakUuid = table.Column(type: "text", nullable: false), - Email = table.Column(type: "text", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Users", x => x.Id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "Users"); - } - } -} diff --git a/backend/src/api/Program.cs b/backend/src/api/Program.cs index 85ecb6c..2fef2ba 100644 --- a/backend/src/api/Program.cs +++ b/backend/src/api/Program.cs @@ -41,8 +41,6 @@ ValidateIssuer = true, ValidateLifetime = true }; - builder.Services.AddSingleton(tokenValidationParameters); - builder.Services.AddSingleton(keycloakJwtOptions); builder.Services.AddAuthentication(options => { @@ -61,7 +59,6 @@ { c.SwaggerDoc("v1", new OpenApiInfo { Title = "api", Version = "v1" }); }); - builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddCors(options => diff --git a/backend/src/api/RequestExtensions.cs b/backend/src/api/RequestExtensions.cs new file mode 100644 index 0000000..2d45f01 --- /dev/null +++ b/backend/src/api/RequestExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.IdentityModel.JsonWebTokens; + +namespace api; + +public static class RequestExtensions +{ + public static string? UserId(this HttpRequest request) + { + try + { + var accessToken = request.Headers.Authorization.ToString().Split(' ')[1]; + var jsonTokenData = new JsonWebTokenHandler().ReadJsonWebToken(accessToken); + return jsonTokenData.Subject; + } + catch + { + return null; + } + } +} diff --git a/backend/src/api/Services/Users/IUserService.cs b/backend/src/api/Services/Users/IUserService.cs deleted file mode 100644 index 5b09ba7..0000000 --- a/backend/src/api/Services/Users/IUserService.cs +++ /dev/null @@ -1,8 +0,0 @@ -using api.Data.DTO; - -namespace api.Services.Users; - -public interface IUserService -{ - Task<(bool isSuccess, string? error)> CreateKeycloakUser(UserDTO user); -} diff --git a/backend/src/api/Services/Users/UserService.cs b/backend/src/api/Services/Users/UserService.cs deleted file mode 100644 index 08d440f..0000000 --- a/backend/src/api/Services/Users/UserService.cs +++ /dev/null @@ -1,33 +0,0 @@ -using api.Data; -using api.Data.DTO; -using api.Data.DAO; -using api.Constants; - -namespace api.Services.Users; - -public class UserService : IUserService -{ - private readonly ApplicationDbContext _applicationDbContext; - - public UserService(ApplicationDbContext applicationDbContext) - { - _applicationDbContext = applicationDbContext; - } - - public async Task<(bool isSuccess, string? error)> CreateKeycloakUser(UserDTO user) - { - var userInstance = _applicationDbContext.Users.FirstOrDefault(userInstance => userInstance.KeycloakUuid.Equals(user.KeycloakUuid)); - if (userInstance != null) - { - return (false, ErrorMessages.UserAlreadyExists); - } - var newUser = new UserModel() - { - KeycloakUuid = user.KeycloakUuid, - Email = user.Email - }; - await _applicationDbContext.Users.AddAsync(newUser); - await _applicationDbContext.SaveChangesAsync(); - return (true, null); - } -} diff --git a/backend/src/api/api.csproj b/backend/src/api/api.csproj index 6d537d7..1ed02ca 100644 --- a/backend/src/api/api.csproj +++ b/backend/src/api/api.csproj @@ -1,7 +1,7 @@ - net7.0 + net8.0 f7cb2e8e-61a4-4a0a-9259-839230fcfe2d Linux enable @@ -9,18 +9,22 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + - + + + + + diff --git a/frontend/src/helpers/RequireAuth.tsx b/frontend/src/helpers/RequireAuth.tsx index 3014338..8bfa471 100644 --- a/frontend/src/helpers/RequireAuth.tsx +++ b/frontend/src/helpers/RequireAuth.tsx @@ -7,21 +7,6 @@ import Constants from "./Constants"; export default function RequireAuth() { const { isLogin, keycloak } = useAuth(); - - const createUserInBackend = async () => { - const response = await fetch(`${Constants.BASE_URL}/auth/create-user`, { - headers: { Authorization: `Bearer ${keycloak!.token}` }, - }); - if (response.status !== 200) { - const errorMessage = await response.text(); - throw new Error(errorMessage); - } - }; - - if (isLogin) { - createUserInBackend(); - } - return isLogin ? :

Authenticating...

; } From 59edf1dcd97b2c9e1e8b18068f0a789cb2d17e2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Tue, 10 Sep 2024 09:52:50 +0200 Subject: [PATCH 05/15] Closes #60 Add Logs Remove the redirect for debuging --- backend/src/api/Controllers/TodoController.cs | 15 ++++++++++----- scripts/apply_migrations.ps1 | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/backend/src/api/Controllers/TodoController.cs b/backend/src/api/Controllers/TodoController.cs index ee522a4..35f9bad 100644 --- a/backend/src/api/Controllers/TodoController.cs +++ b/backend/src/api/Controllers/TodoController.cs @@ -1,5 +1,4 @@ -using api.Data.DTO; -using api.Services; +using api.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -11,17 +10,23 @@ namespace api.Controllers; public class TodoController : ControllerBase { private readonly ITodoService _service; + private readonly ILogger _logger; - public TodoController(ITodoService service) => _service = service; + public TodoController(ITodoService service, ILogger logger){ + _service = service; + _logger = logger; + } [HttpGet("lists")] public IActionResult List() { - return Request.UserId() switch + var userId = Request.UserId(); + _logger.LogInformation("User: {user} requested his lists", userId); + return userId switch { null => Unauthorized(), var user => Ok(_service.GetLists(user)), }; } -} +} \ No newline at end of file diff --git a/scripts/apply_migrations.ps1 b/scripts/apply_migrations.ps1 index 201925c..05aa6c2 100755 --- a/scripts/apply_migrations.ps1 +++ b/scripts/apply_migrations.ps1 @@ -13,7 +13,7 @@ docker compose up db -d 2>&1 > $null Write-Output "Generateing migration script" Set-Location ../backend/src/api mkdir migration_scripts 2>&1 > $null -dotnet ef migrations script --idempotent -o ./migration_scripts/migration.sql 2>&1 > $null +dotnet ef migrations script --idempotent -o ./migration_scripts/migration.sql Write-Output "Applying.." $passwd = "PGPASSWORD=" + $Env:POSTGRES_PASSWORD From 6601d55f96a03d3d8d09ef9f4dd5c9b31b54cd79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Tue, 10 Sep 2024 12:34:41 +0200 Subject: [PATCH 06/15] Closes #60 Update Bruno collections --- .../bruno/KSummarized/ToDo/List/Create.bru | 12 +++++----- .../bruno/KSummarized/environments/Local.bru | 10 +++++++- backend/bruno/KSummarized/token.bru | 23 +++++++++++++++++++ 3 files changed, 38 insertions(+), 7 deletions(-) create mode 100644 backend/bruno/KSummarized/token.bru diff --git a/backend/bruno/KSummarized/ToDo/List/Create.bru b/backend/bruno/KSummarized/ToDo/List/Create.bru index a5895a6..5632487 100644 --- a/backend/bruno/KSummarized/ToDo/List/Create.bru +++ b/backend/bruno/KSummarized/ToDo/List/Create.bru @@ -5,11 +5,15 @@ meta { } post { - url: https://localhost:5000/api/todo/lists - body: json + url: https://localhost:5000/api/todo/lists?name=kenobi + body: none auth: bearer } +params:query { + name: kenobi +} + auth:bearer { token: {{token}} } @@ -17,7 +21,3 @@ auth:bearer { body:json { "ToDo" } - -vars:post-response { - listId: res.body.id -} diff --git a/backend/bruno/KSummarized/environments/Local.bru b/backend/bruno/KSummarized/environments/Local.bru index 35d3972..805b24b 100644 --- a/backend/bruno/KSummarized/environments/Local.bru +++ b/backend/bruno/KSummarized/environments/Local.bru @@ -1,3 +1,11 @@ +vars { + keycloak_realm: KnowledgeSummarized + keycloak_url: http://localhost:8080 +} vars:secret [ - token + token, + keycloak_client_id, + keycloak_client_secret, + keycloak_username, + keycloak_password ] diff --git a/backend/bruno/KSummarized/token.bru b/backend/bruno/KSummarized/token.bru new file mode 100644 index 0000000..064ea26 --- /dev/null +++ b/backend/bruno/KSummarized/token.bru @@ -0,0 +1,23 @@ +meta { + name: Get access token + type: http + seq: 1 +} + +post { + url: {{keycloak_url}}/realms/{{keycloak_realm}}/protocol/openid-connect/token + body: formUrlEncoded + auth: none +} + +body:form-urlencoded { + client_id: {{keycloak_client_id}} + grant_type: password + client_secret: {{keycloak_client_secret}} + username: {{keycloak_username}} + password: {{keycloak_password}} +} + +vars:post-response { + token: res.body.access_token +} From 8d13ecad894f9723f83af7a3281dbd08f4e295ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Tue, 10 Sep 2024 12:37:33 +0200 Subject: [PATCH 07/15] Closes #60 Remove unused using --- backend/src/api/Program.cs | 203 ++++++++++++++++++------------------- 1 file changed, 101 insertions(+), 102 deletions(-) diff --git a/backend/src/api/Program.cs b/backend/src/api/Program.cs index 2fef2ba..f6d88a0 100644 --- a/backend/src/api/Program.cs +++ b/backend/src/api/Program.cs @@ -1,102 +1,101 @@ -using api.Data; -using api.Services.Users; -using Microsoft.AspNetCore.Authentication.JwtBearer; -using Microsoft.IdentityModel.Tokens; -using Microsoft.OpenApi.Models; -using Microsoft.EntityFrameworkCore; -using Serilog; -using api; -using System.Security.Cryptography; -using api.Services; - -const string logFormat = "[{Timestamp:HH:mm:ss} {Level:u3}] {CorelationId} | {Message:lj}{NewLine}{Exception}"; -Log.Logger = new LoggerConfiguration().Enrich.WithCorrelationId() - .WriteTo - .Console(outputTemplate: logFormat) - .CreateLogger(); - -try -{ - var builder = WebApplication.CreateBuilder(args); - builder.Services.AddHttpContextAccessor(); - builder.Host.UseSerilog(); - builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration.GetConnectionString("KSummarized"))); - var keycloakJwtOptions = builder.Configuration.GetRequiredSection("KeycloakJwt").Get()!; - - // Create RSA key for offline validation of Keycloak token - RSA rsa = RSA.Create(); - rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(keycloakJwtOptions.Secret), out _); - var rsaKeycloakSecurityKey = new RsaSecurityKey(rsa) - { - KeyId = Guid.NewGuid().ToString() - }; - - var tokenValidationParameters = new TokenValidationParameters() - { - ValidateIssuerSigningKey = true, - IssuerSigningKey = rsaKeycloakSecurityKey, - ValidAudience = keycloakJwtOptions.Audience, - ValidateAudience = true, - ValidIssuer = keycloakJwtOptions.Issuer, - ValidateIssuer = true, - ValidateLifetime = true - }; - - builder.Services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - }) - .AddJwtBearer(options => - { - options.SaveToken = true; - options.RequireHttpsMetadata = true; - options.TokenValidationParameters = tokenValidationParameters; - }); - - builder.Services.AddControllers(); - builder.Services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new OpenApiInfo { Title = "api", Version = "v1" }); - }); - builder.Services.AddScoped(); - - builder.Services.AddCors(options => - { - options.AddPolicy(name: "AllowAll", builder => - { - builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod(); - }); - }); - - var app = builder.Build(); - - if (app.Environment.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - app.UseSwagger(); - app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "api v1")); - } - - app.UseCors("AllowAll"); - app.UseAuthentication(); - - app.UseHttpsRedirection(); - app.UseHsts(); - - app.UseSerilogRequestLogging(); - app.UseRouting(); - app.UseAuthorization(); - - app.MapControllers(); - - app.Run(); -} -catch (Exception ex) -{ - Log.Fatal("Error starting the application: {Exception}", ex); -} -finally -{ - Log.CloseAndFlush(); -} +using api.Data; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; +using Microsoft.EntityFrameworkCore; +using Serilog; +using api; +using System.Security.Cryptography; +using api.Services; + +const string logFormat = "[{Timestamp:HH:mm:ss} {Level:u3}] {CorelationId} | {Message:lj}{NewLine}{Exception}"; +Log.Logger = new LoggerConfiguration().Enrich.WithCorrelationId() + .WriteTo + .Console(outputTemplate: logFormat) + .CreateLogger(); + +try +{ + var builder = WebApplication.CreateBuilder(args); + builder.Services.AddHttpContextAccessor(); + builder.Host.UseSerilog(); + builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration.GetConnectionString("KSummarized"))); + var keycloakJwtOptions = builder.Configuration.GetRequiredSection("KeycloakJwt").Get()!; + + // Create RSA key for offline validation of Keycloak token + RSA rsa = RSA.Create(); + rsa.ImportSubjectPublicKeyInfo(Convert.FromBase64String(keycloakJwtOptions.Secret), out _); + var rsaKeycloakSecurityKey = new RsaSecurityKey(rsa) + { + KeyId = Guid.NewGuid().ToString() + }; + + var tokenValidationParameters = new TokenValidationParameters() + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = rsaKeycloakSecurityKey, + ValidAudience = keycloakJwtOptions.Audience, + ValidateAudience = true, + ValidIssuer = keycloakJwtOptions.Issuer, + ValidateIssuer = true, + ValidateLifetime = true + }; + + builder.Services.AddAuthentication(options => + { + options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; + options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; + }) + .AddJwtBearer(options => + { + options.SaveToken = true; + options.RequireHttpsMetadata = true; + options.TokenValidationParameters = tokenValidationParameters; + }); + + builder.Services.AddControllers(); + builder.Services.AddSwaggerGen(c => + { + c.SwaggerDoc("v1", new OpenApiInfo { Title = "api", Version = "v1" }); + }); + builder.Services.AddScoped(); + + builder.Services.AddCors(options => + { + options.AddPolicy(name: "AllowAll", builder => + { + builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod(); + }); + }); + + var app = builder.Build(); + + if (app.Environment.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + app.UseSwagger(); + app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "api v1")); + } + + app.UseCors("AllowAll"); + app.UseAuthentication(); + + app.UseHttpsRedirection(); + app.UseHsts(); + + app.UseSerilogRequestLogging(); + app.UseRouting(); + app.UseAuthorization(); + + app.MapControllers(); + + app.Run(); +} +catch (Exception ex) +{ + Log.Fatal("Error starting the application: {Exception}", ex); +} +finally +{ + Log.CloseAndFlush(); +} From 7a5f829af23c0899e3f89846cd3a16dfe62d2814 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Tue, 10 Sep 2024 12:45:36 +0200 Subject: [PATCH 08/15] Closes #60 Add Create List --- backend/src/api/Controllers/TodoController.cs | 19 +++++++++++++++++-- backend/src/api/Services/Todo/ITodoService.cs | 16 +++++++++------- backend/src/api/Services/Todo/TodoService.cs | 10 +++++++++- 3 files changed, 35 insertions(+), 10 deletions(-) diff --git a/backend/src/api/Controllers/TodoController.cs b/backend/src/api/Controllers/TodoController.cs index 35f9bad..e650003 100644 --- a/backend/src/api/Controllers/TodoController.cs +++ b/backend/src/api/Controllers/TodoController.cs @@ -18,10 +18,10 @@ public TodoController(ITodoService service, ILogger logger){ } [HttpGet("lists")] - public IActionResult List() + public IActionResult GetLists() { var userId = Request.UserId(); - _logger.LogInformation("User: {user} requested his lists", userId); + _logger.LogDebug("User: {user} requested his lists", userId); return userId switch { null => Unauthorized(), @@ -29,4 +29,19 @@ public IActionResult List() }; } + [HttpPost("lists")] + public async Task CreateLists([FromQuery] string name) + { + var userId = Request.UserId(); + _logger.LogDebug("User: {user} created: {list}", userId, name); + return userId switch + { + null => Unauthorized(), + var user => await Create(user, name), + }; + async Task Create(string user, string name){ + var list = await _service.CreateList(user, name); + return Created(HttpContext.Request.Path.Add(new PathString($"/{list.Id}")), list); + } + } } \ No newline at end of file diff --git a/backend/src/api/Services/Todo/ITodoService.cs b/backend/src/api/Services/Todo/ITodoService.cs index ff5f22a..91bd29e 100644 --- a/backend/src/api/Services/Todo/ITodoService.cs +++ b/backend/src/api/Services/Todo/ITodoService.cs @@ -1,8 +1,10 @@ -using api.Data.DTO; - -namespace api.Services; - -public interface ITodoService -{ - public IEnumerable GetLists(string userId); +using api.Data.DAO; +using api.Data.DTO; + +namespace api.Services; + +public interface ITodoService +{ + Task CreateList(string user, string name); + public IEnumerable GetLists(string userId); } \ No newline at end of file diff --git a/backend/src/api/Services/Todo/TodoService.cs b/backend/src/api/Services/Todo/TodoService.cs index f616843..58fb12c 100644 --- a/backend/src/api/Services/Todo/TodoService.cs +++ b/backend/src/api/Services/Todo/TodoService.cs @@ -1,4 +1,5 @@ using api.Data; +using api.Data.DAO; using api.Data.DTO; using Microsoft.EntityFrameworkCore; @@ -21,4 +22,11 @@ public IEnumerable GetLists(string userId) .Select(list => new TodoListDTO(list.Id, list.Name)) .AsEnumerable(); } -} + + public async Task CreateList(string user, string name){ + var newList = new TodoListModel(){Name = name, Owner = Guid.Parse(user)}; + _context.TodoLists.Add(newList); + await _context.SaveChangesAsync(); + return newList; + } +} \ No newline at end of file From 87de18eebcbda34431fdd3b53533fe98a9ea9b95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Tue, 10 Sep 2024 13:14:43 +0200 Subject: [PATCH 09/15] Closes #60 Implement get for a single list --- backend/bruno/KSummarized/ToDo/List/Get.bru | 15 ++++++++++++ backend/src/api/Controllers/TodoController.cs | 24 +++++++++++++++---- backend/src/api/Services/Todo/ITodoService.cs | 3 ++- backend/src/api/Services/Todo/TodoService.cs | 17 +++++++++---- 4 files changed, 50 insertions(+), 9 deletions(-) create mode 100644 backend/bruno/KSummarized/ToDo/List/Get.bru diff --git a/backend/bruno/KSummarized/ToDo/List/Get.bru b/backend/bruno/KSummarized/ToDo/List/Get.bru new file mode 100644 index 0000000..6fd7964 --- /dev/null +++ b/backend/bruno/KSummarized/ToDo/List/Get.bru @@ -0,0 +1,15 @@ +meta { + name: Get + type: http + seq: 5 +} + +get { + url: https://localhost:5000/api/todo/lists/9 + body: none + auth: bearer +} + +auth:bearer { + token: {{token}} +} diff --git a/backend/src/api/Controllers/TodoController.cs b/backend/src/api/Controllers/TodoController.cs index e650003..0b8428f 100644 --- a/backend/src/api/Controllers/TodoController.cs +++ b/backend/src/api/Controllers/TodoController.cs @@ -1,4 +1,4 @@ -using api.Services; +using api.Services; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; @@ -12,7 +12,8 @@ public class TodoController : ControllerBase private readonly ITodoService _service; private readonly ILogger _logger; - public TodoController(ITodoService service, ILogger logger){ + public TodoController(ITodoService service, ILogger logger) + { _service = service; _logger = logger; } @@ -29,6 +30,20 @@ public IActionResult GetLists() }; } + [HttpGet("lists/{id}")] + public IActionResult GetList([FromRoute] int id) + { + var userId = Request.UserId(); + _logger.LogDebug("User: {user} requested his lists", userId); + if (userId is null) { return Unauthorized(); } + var list = _service.GetList(userId, id); + return list switch + { + null => NotFound(), + var user => Ok(list), + }; + } + [HttpPost("lists")] public async Task CreateLists([FromQuery] string name) { @@ -39,9 +54,10 @@ public async Task CreateLists([FromQuery] string name) null => Unauthorized(), var user => await Create(user, name), }; - async Task Create(string user, string name){ + async Task Create(string user, string name) + { var list = await _service.CreateList(user, name); return Created(HttpContext.Request.Path.Add(new PathString($"/{list.Id}")), list); } } -} \ No newline at end of file +} diff --git a/backend/src/api/Services/Todo/ITodoService.cs b/backend/src/api/Services/Todo/ITodoService.cs index 91bd29e..b6b7889 100644 --- a/backend/src/api/Services/Todo/ITodoService.cs +++ b/backend/src/api/Services/Todo/ITodoService.cs @@ -7,4 +7,5 @@ public interface ITodoService { Task CreateList(string user, string name); public IEnumerable GetLists(string userId); -} \ No newline at end of file + public TodoListDTO? GetList(string userId, int id); +} diff --git a/backend/src/api/Services/Todo/TodoService.cs b/backend/src/api/Services/Todo/TodoService.cs index 58fb12c..0c37fff 100644 --- a/backend/src/api/Services/Todo/TodoService.cs +++ b/backend/src/api/Services/Todo/TodoService.cs @@ -1,4 +1,4 @@ -using api.Data; +using api.Data; using api.Data.DAO; using api.Data.DTO; using Microsoft.EntityFrameworkCore; @@ -23,10 +23,19 @@ public IEnumerable GetLists(string userId) .AsEnumerable(); } - public async Task CreateList(string user, string name){ - var newList = new TodoListModel(){Name = name, Owner = Guid.Parse(user)}; + public TodoListDTO? GetList(string userId, int id) + { + var list = _context.TodoLists.AsNoTracking() + .SingleOrDefault(l => l.Owner.Equals(Guid.Parse(userId)) && l.Id == id); + if (list is not null) { return new(list.Id, list.Name); } + return null; + } + + public async Task CreateList(string user, string name) + { + var newList = new TodoListModel() { Name = name, Owner = Guid.Parse(user) }; _context.TodoLists.Add(newList); await _context.SaveChangesAsync(); return newList; } -} \ No newline at end of file +} From afb8c672f10c8ce00fa2c4919647f09e3790caf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Tue, 10 Sep 2024 19:34:34 +0200 Subject: [PATCH 10/15] Closes #60 Add deletion of a list --- backend/bruno/KSummarized/ToDo/List/Delete.bru | 2 +- backend/src/api/Controllers/TodoController.cs | 18 +++++++++++++++++- backend/src/api/Services/Todo/ITodoService.cs | 5 +++-- backend/src/api/Services/Todo/TodoService.cs | 11 +++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/backend/bruno/KSummarized/ToDo/List/Delete.bru b/backend/bruno/KSummarized/ToDo/List/Delete.bru index 599b3cc..efc2a0e 100644 --- a/backend/bruno/KSummarized/ToDo/List/Delete.bru +++ b/backend/bruno/KSummarized/ToDo/List/Delete.bru @@ -5,7 +5,7 @@ meta { } delete { - url: https://localhost:5000/api/todo/lists/{{listId}} + url: https://localhost:5000/api/todo/lists/9 body: json auth: bearer } diff --git a/backend/src/api/Controllers/TodoController.cs b/backend/src/api/Controllers/TodoController.cs index 0b8428f..4a69380 100644 --- a/backend/src/api/Controllers/TodoController.cs +++ b/backend/src/api/Controllers/TodoController.cs @@ -34,7 +34,7 @@ public IActionResult GetLists() public IActionResult GetList([FromRoute] int id) { var userId = Request.UserId(); - _logger.LogDebug("User: {user} requested his lists", userId); + _logger.LogDebug("User: {user} requested his list: {id}", userId, id); if (userId is null) { return Unauthorized(); } var list = _service.GetList(userId, id); return list switch @@ -44,6 +44,20 @@ public IActionResult GetList([FromRoute] int id) }; } + [HttpDelete("lists/{id}")] + public IActionResult DeleteList([FromRoute] int id) + { + var userId = Request.UserId(); + _logger.LogDebug("User: {user} deleted his list: {id}", userId, id); + if (userId is null) { return Unauthorized(); } + var success = _service.DeleteList(userId, id); + if (success) + { + return Ok(); + } + return BadRequest(); + } + [HttpPost("lists")] public async Task CreateLists([FromQuery] string name) { @@ -54,8 +68,10 @@ public async Task CreateLists([FromQuery] string name) null => Unauthorized(), var user => await Create(user, name), }; + async Task Create(string user, string name) { + //TODO: return DTO instead of DAO var list = await _service.CreateList(user, name); return Created(HttpContext.Request.Path.Add(new PathString($"/{list.Id}")), list); } diff --git a/backend/src/api/Services/Todo/ITodoService.cs b/backend/src/api/Services/Todo/ITodoService.cs index b6b7889..ab35faa 100644 --- a/backend/src/api/Services/Todo/ITodoService.cs +++ b/backend/src/api/Services/Todo/ITodoService.cs @@ -6,6 +6,7 @@ namespace api.Services; public interface ITodoService { Task CreateList(string user, string name); - public IEnumerable GetLists(string userId); - public TodoListDTO? GetList(string userId, int id); + IEnumerable GetLists(string userId); + TodoListDTO? GetList(string userId, int id); + bool DeleteList(string userId, int id); } diff --git a/backend/src/api/Services/Todo/TodoService.cs b/backend/src/api/Services/Todo/TodoService.cs index 0c37fff..88ffea6 100644 --- a/backend/src/api/Services/Todo/TodoService.cs +++ b/backend/src/api/Services/Todo/TodoService.cs @@ -27,6 +27,7 @@ public IEnumerable GetLists(string userId) { var list = _context.TodoLists.AsNoTracking() .SingleOrDefault(l => l.Owner.Equals(Guid.Parse(userId)) && l.Id == id); + //TODO: Consider creating a mapper instead of this manual new if (list is not null) { return new(list.Id, list.Name); } return null; } @@ -38,4 +39,14 @@ public async Task CreateList(string user, string name) await _context.SaveChangesAsync(); return newList; } + + public bool DeleteList(string userId, int id) + { + var list = _context.TodoLists.AsNoTracking() + .SingleOrDefault(l => l.Owner.Equals(Guid.Parse(userId)) && l.Id == id); + if (list is null) { return false; } + _context.TodoLists.Remove(list); + _context.SaveChanges(); + return true; + } } From 52034da2fa19535dacb89272c3137e3acb88b699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Tue, 10 Sep 2024 19:36:54 +0200 Subject: [PATCH 11/15] Closes #60 Cleanup sln --- backend/UnitTests/UnitTests.csproj | 24 ---- backend/UnitTests/Usings.cs | 1 - backend/ksummarized.sln | 107 +++++++----------- backend/src/infrastructure/Class1.cs | 6 + .../src/infrastructure/infrastructure.csproj | 9 ++ .../test/ksummarized.UnitTests/UnitTest1.cs | 13 --- .../ksummarized.UnitTests.csproj | 16 --- backend/{UnitTests => test/unit}/UnitTest1.cs | 18 +-- backend/test/unit/unit.csproj | 23 ++++ 9 files changed, 90 insertions(+), 127 deletions(-) delete mode 100644 backend/UnitTests/UnitTests.csproj delete mode 100644 backend/UnitTests/Usings.cs create mode 100644 backend/src/infrastructure/Class1.cs create mode 100644 backend/src/infrastructure/infrastructure.csproj delete mode 100644 backend/test/ksummarized.UnitTests/UnitTest1.cs delete mode 100644 backend/test/ksummarized.UnitTests/ksummarized.UnitTests.csproj rename backend/{UnitTests => test/unit}/UnitTest1.cs (75%) create mode 100644 backend/test/unit/unit.csproj diff --git a/backend/UnitTests/UnitTests.csproj b/backend/UnitTests/UnitTests.csproj deleted file mode 100644 index 4ba4b01..0000000 --- a/backend/UnitTests/UnitTests.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net7.0 - enable - enable - - false - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - diff --git a/backend/UnitTests/Usings.cs b/backend/UnitTests/Usings.cs deleted file mode 100644 index 8c927eb..0000000 --- a/backend/UnitTests/Usings.cs +++ /dev/null @@ -1 +0,0 @@ -global using Xunit; \ No newline at end of file diff --git a/backend/ksummarized.sln b/backend/ksummarized.sln index 58eff14..20d82c5 100644 --- a/backend/ksummarized.sln +++ b/backend/ksummarized.sln @@ -1,64 +1,43 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.4.33213.308 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{D7A1B70F-EE88-4B3C-A209-FE2517ADD026}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "api", "src\api\api.csproj", "{917A96E2-DAE3-432B-8C65-362D34194104}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A2E313A3-EE03-4A78-ABCE-42E7A4B7029A}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D12C229D-2E15-43BD-951F-EF05AE7633D9}" - ProjectSection(SolutionItems) = preProject - .editorconfig = .editorconfig - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{D21A0FAE-B25F-4975-9EBC-76E24CDA1969}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {917A96E2-DAE3-432B-8C65-362D34194104}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Debug|Any CPU.Build.0 = Debug|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Debug|x64.ActiveCfg = Debug|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Debug|x64.Build.0 = Debug|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Debug|x86.ActiveCfg = Debug|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Debug|x86.Build.0 = Debug|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Release|Any CPU.ActiveCfg = Release|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Release|Any CPU.Build.0 = Release|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Release|x64.ActiveCfg = Release|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Release|x64.Build.0 = Release|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Release|x86.ActiveCfg = Release|Any CPU - {917A96E2-DAE3-432B-8C65-362D34194104}.Release|x86.Build.0 = Release|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Debug|x64.ActiveCfg = Debug|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Debug|x64.Build.0 = Debug|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Debug|x86.ActiveCfg = Debug|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Debug|x86.Build.0 = Debug|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Release|Any CPU.Build.0 = Release|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Release|x64.ActiveCfg = Release|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Release|x64.Build.0 = Release|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Release|x86.ActiveCfg = Release|Any CPU - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {917A96E2-DAE3-432B-8C65-362D34194104} = {D7A1B70F-EE88-4B3C-A209-FE2517ADD026} - {D21A0FAE-B25F-4975-9EBC-76E24CDA1969} = {A2E313A3-EE03-4A78-ABCE-42E7A4B7029A} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {EB55458A-327D-41D5-8211-352C32273E00} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DE2804D2-3D3D-4A41-91C7-4FEEF71A4434}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "api", "src\api\api.csproj", "{949171D5-4018-4C40-AB2B-687B39F20974}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{4CA15B69-5E75-48E2-BE26-EA818B675CFC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "unit", "test\unit\unit.csproj", "{B930D2FA-7FC6-4772-89EA-6B925EDD2EBC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "infrastructure", "src\infrastructure\infrastructure.csproj", "{9D4BB729-24FB-4B6E-9EFD-B8C2E0A2B1EB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {949171D5-4018-4C40-AB2B-687B39F20974}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {949171D5-4018-4C40-AB2B-687B39F20974}.Debug|Any CPU.Build.0 = Debug|Any CPU + {949171D5-4018-4C40-AB2B-687B39F20974}.Release|Any CPU.ActiveCfg = Release|Any CPU + {949171D5-4018-4C40-AB2B-687B39F20974}.Release|Any CPU.Build.0 = Release|Any CPU + {B930D2FA-7FC6-4772-89EA-6B925EDD2EBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B930D2FA-7FC6-4772-89EA-6B925EDD2EBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B930D2FA-7FC6-4772-89EA-6B925EDD2EBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B930D2FA-7FC6-4772-89EA-6B925EDD2EBC}.Release|Any CPU.Build.0 = Release|Any CPU + {9D4BB729-24FB-4B6E-9EFD-B8C2E0A2B1EB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D4BB729-24FB-4B6E-9EFD-B8C2E0A2B1EB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D4BB729-24FB-4B6E-9EFD-B8C2E0A2B1EB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D4BB729-24FB-4B6E-9EFD-B8C2E0A2B1EB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {949171D5-4018-4C40-AB2B-687B39F20974} = {DE2804D2-3D3D-4A41-91C7-4FEEF71A4434} + {B930D2FA-7FC6-4772-89EA-6B925EDD2EBC} = {4CA15B69-5E75-48E2-BE26-EA818B675CFC} + {9D4BB729-24FB-4B6E-9EFD-B8C2E0A2B1EB} = {DE2804D2-3D3D-4A41-91C7-4FEEF71A4434} + EndGlobalSection +EndGlobal diff --git a/backend/src/infrastructure/Class1.cs b/backend/src/infrastructure/Class1.cs new file mode 100644 index 0000000..47bb804 --- /dev/null +++ b/backend/src/infrastructure/Class1.cs @@ -0,0 +1,6 @@ +namespace infrastructure; + +public class Class1 +{ + +} diff --git a/backend/src/infrastructure/infrastructure.csproj b/backend/src/infrastructure/infrastructure.csproj new file mode 100644 index 0000000..bb23fb7 --- /dev/null +++ b/backend/src/infrastructure/infrastructure.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/backend/test/ksummarized.UnitTests/UnitTest1.cs b/backend/test/ksummarized.UnitTests/UnitTest1.cs deleted file mode 100644 index 33840dd..0000000 --- a/backend/test/ksummarized.UnitTests/UnitTest1.cs +++ /dev/null @@ -1,13 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace ksummarized.UnitTests -{ - [TestClass] - public class UnitTest1 - { - [TestMethod] - public void TestMethod1() - { - } - } -} diff --git a/backend/test/ksummarized.UnitTests/ksummarized.UnitTests.csproj b/backend/test/ksummarized.UnitTests/ksummarized.UnitTests.csproj deleted file mode 100644 index d6f1239..0000000 --- a/backend/test/ksummarized.UnitTests/ksummarized.UnitTests.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - net7.0 - - false - - - - - - - - - - diff --git a/backend/UnitTests/UnitTest1.cs b/backend/test/unit/UnitTest1.cs similarity index 75% rename from backend/UnitTests/UnitTest1.cs rename to backend/test/unit/UnitTest1.cs index d88634b..d334e44 100644 --- a/backend/UnitTests/UnitTest1.cs +++ b/backend/test/unit/UnitTest1.cs @@ -1,10 +1,10 @@ -namespace UnitTests; - -public class UnitTest1 -{ - [Fact] - public void Test1() - { - - } +namespace unit; + +public class UnitTest1 +{ + [Fact] + public void Test1() + { + + } } \ No newline at end of file diff --git a/backend/test/unit/unit.csproj b/backend/test/unit/unit.csproj new file mode 100644 index 0000000..3aa9860 --- /dev/null +++ b/backend/test/unit/unit.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + From 75c58800c87d065a6cda62ddab76254532694910 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Tue, 10 Sep 2024 20:08:09 +0200 Subject: [PATCH 12/15] Closes #60 Add renamming of lists --- backend/bruno/KSummarized/ToDo/List/Edit.bru | 8 +++-- backend/src/api/Controllers/TodoController.cs | 34 +++++++++++++++++-- backend/src/api/Services/Todo/ITodoService.cs | 1 + backend/src/api/Services/Todo/TodoService.cs | 13 ++++++- 4 files changed, 49 insertions(+), 7 deletions(-) diff --git a/backend/bruno/KSummarized/ToDo/List/Edit.bru b/backend/bruno/KSummarized/ToDo/List/Edit.bru index 635062f..c1e0e45 100644 --- a/backend/bruno/KSummarized/ToDo/List/Edit.bru +++ b/backend/bruno/KSummarized/ToDo/List/Edit.bru @@ -4,8 +4,8 @@ meta { seq: 3 } -post { - url: https://localhost:5000/api/todo/lists/{{listId}} +put { + url: https://localhost:5000/api/todo/lists/5 body: json auth: bearer } @@ -15,5 +15,7 @@ auth:bearer { } body:json { - "ToDo2" + { + "name":"Vax" + } } diff --git a/backend/src/api/Controllers/TodoController.cs b/backend/src/api/Controllers/TodoController.cs index 4a69380..5436e8c 100644 --- a/backend/src/api/Controllers/TodoController.cs +++ b/backend/src/api/Controllers/TodoController.cs @@ -59,14 +59,14 @@ public IActionResult DeleteList([FromRoute] int id) } [HttpPost("lists")] - public async Task CreateLists([FromQuery] string name) + public async Task CreateLists([FromQuery] Request request) { var userId = Request.UserId(); - _logger.LogDebug("User: {user} created: {list}", userId, name); + _logger.LogDebug("User: {user} created: {list}", userId, request.Name); return userId switch { null => Unauthorized(), - var user => await Create(user, name), + var user => await Create(user, request.Name), }; async Task Create(string user, string name) @@ -76,4 +76,32 @@ async Task Create(string user, string name) return Created(HttpContext.Request.Path.Add(new PathString($"/{list.Id}")), list); } } + + [HttpPut("lists/{id}")] + public async Task RenameList([FromRoute] int id, [FromBody] Request request) + { + var userId = Request.UserId(); + _logger.LogDebug("User: {user} created: {list}", userId, request.Name); + return userId switch + { + null => Unauthorized(), + var user => await Rename(user, id, request.Name), + }; + + async Task Rename(string user, int id, string name) + { + //TODO: return DTO instead of DAO + var list = await _service.RenameList(user, id, name); + if (list) + { + return Ok(); + } + return BadRequest(); + } + } +} + +public class Request +{ + public required string Name { get; set; } } diff --git a/backend/src/api/Services/Todo/ITodoService.cs b/backend/src/api/Services/Todo/ITodoService.cs index ab35faa..fe5788e 100644 --- a/backend/src/api/Services/Todo/ITodoService.cs +++ b/backend/src/api/Services/Todo/ITodoService.cs @@ -9,4 +9,5 @@ public interface ITodoService IEnumerable GetLists(string userId); TodoListDTO? GetList(string userId, int id); bool DeleteList(string userId, int id); + Task RenameList(string user, int id, string name); } diff --git a/backend/src/api/Services/Todo/TodoService.cs b/backend/src/api/Services/Todo/TodoService.cs index 88ffea6..8b24c70 100644 --- a/backend/src/api/Services/Todo/TodoService.cs +++ b/backend/src/api/Services/Todo/TodoService.cs @@ -42,11 +42,22 @@ public async Task CreateList(string user, string name) public bool DeleteList(string userId, int id) { - var list = _context.TodoLists.AsNoTracking() + var list = _context.TodoLists .SingleOrDefault(l => l.Owner.Equals(Guid.Parse(userId)) && l.Id == id); if (list is null) { return false; } _context.TodoLists.Remove(list); _context.SaveChanges(); return true; } + + public async Task RenameList(string userId, int id, string name) + { + var list = _context.TodoLists + .SingleOrDefault(l => l.Owner.Equals(Guid.Parse(userId)) && l.Id == id); + + if (list is null) { return false; } + list.Name = name; + await _context.SaveChangesAsync(); + return true; + } } From 3436b389aca7422b30090f5cf209f4e948942a64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Tue, 10 Sep 2024 20:12:38 +0200 Subject: [PATCH 13/15] Closes #60 Updated Bruno collection --- backend/bruno/KSummarized/ToDo/List/Create.bru | 8 ++++++-- backend/bruno/KSummarized/ToDo/List/Delete.bru | 2 +- backend/bruno/KSummarized/ToDo/List/Edit.bru | 4 ++-- backend/bruno/KSummarized/ToDo/List/Get.bru | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/backend/bruno/KSummarized/ToDo/List/Create.bru b/backend/bruno/KSummarized/ToDo/List/Create.bru index 5632487..fc67a0d 100644 --- a/backend/bruno/KSummarized/ToDo/List/Create.bru +++ b/backend/bruno/KSummarized/ToDo/List/Create.bru @@ -5,13 +5,13 @@ meta { } post { - url: https://localhost:5000/api/todo/lists?name=kenobi + url: https://localhost:5000/api/todo/lists?name=demo body: none auth: bearer } params:query { - name: kenobi + name: demo } auth:bearer { @@ -21,3 +21,7 @@ auth:bearer { body:json { "ToDo" } + +vars:post-response { + listId: res.body.id +} diff --git a/backend/bruno/KSummarized/ToDo/List/Delete.bru b/backend/bruno/KSummarized/ToDo/List/Delete.bru index efc2a0e..599b3cc 100644 --- a/backend/bruno/KSummarized/ToDo/List/Delete.bru +++ b/backend/bruno/KSummarized/ToDo/List/Delete.bru @@ -5,7 +5,7 @@ meta { } delete { - url: https://localhost:5000/api/todo/lists/9 + url: https://localhost:5000/api/todo/lists/{{listId}} body: json auth: bearer } diff --git a/backend/bruno/KSummarized/ToDo/List/Edit.bru b/backend/bruno/KSummarized/ToDo/List/Edit.bru index c1e0e45..f03ee4f 100644 --- a/backend/bruno/KSummarized/ToDo/List/Edit.bru +++ b/backend/bruno/KSummarized/ToDo/List/Edit.bru @@ -5,7 +5,7 @@ meta { } put { - url: https://localhost:5000/api/todo/lists/5 + url: https://localhost:5000/api/todo/lists/{{listId}} body: json auth: bearer } @@ -16,6 +16,6 @@ auth:bearer { body:json { { - "name":"Vax" + "name": "Renamed" } } diff --git a/backend/bruno/KSummarized/ToDo/List/Get.bru b/backend/bruno/KSummarized/ToDo/List/Get.bru index 6fd7964..9012765 100644 --- a/backend/bruno/KSummarized/ToDo/List/Get.bru +++ b/backend/bruno/KSummarized/ToDo/List/Get.bru @@ -5,7 +5,7 @@ meta { } get { - url: https://localhost:5000/api/todo/lists/9 + url: https://localhost:5000/api/todo/lists/{{listId}} body: none auth: bearer } From 08ff0b79482a02b07dba3e3200d1456bc06e140b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Tue, 10 Sep 2024 21:30:18 +0200 Subject: [PATCH 14/15] Closes #60 Modularize backend --- backend/Dockerfile | 1 + backend/src/api/Constants/ErrorMessages.cs | 6 ------ backend/src/api/Controllers/TodoController.cs | 2 +- backend/src/api/Program.cs | 14 +++++++++----- backend/src/api/Services/Todo/ITodoService.cs | 13 ------------- backend/src/api/api.csproj | 10 +++++++--- backend/src/core/Ports/ITodoService.cs | 10 ++++++++++ backend/src/core/TodoList.cs | 10 ++++++++++ backend/src/core/core.csproj | 9 +++++++++ backend/src/infrastructure/Class1.cs | 6 ------ .../Data/ApplicationDbContext.cs | 3 +-- .../Data}/TodoListModel.cs | 4 ++-- .../Data}/TodoService.cs | 19 +++++++++---------- .../Keycloack}/KeycloakJwtOptions.cs | 4 +--- .../20240330212015_TodoList.Designer.cs | 6 +++--- .../Migrations/20240330212015_TodoList.cs | 4 ++-- .../ApplicationDbContextModelSnapshot.cs | 6 +++--- .../src/infrastructure/infrastructure.csproj | 13 +++++++++++++ scripts/apply_migrations.ps1 | 6 +++--- 19 files changed, 84 insertions(+), 62 deletions(-) delete mode 100644 backend/src/api/Constants/ErrorMessages.cs delete mode 100644 backend/src/api/Services/Todo/ITodoService.cs create mode 100644 backend/src/core/Ports/ITodoService.cs create mode 100644 backend/src/core/TodoList.cs create mode 100644 backend/src/core/core.csproj delete mode 100644 backend/src/infrastructure/Class1.cs rename backend/src/{api => infrastructure}/Data/ApplicationDbContext.cs (85%) rename backend/src/{api/Data/DAO => infrastructure/Data}/TodoListModel.cs (77%) rename backend/src/{api/Services/Todo => infrastructure/Data}/TodoService.cs (71%) rename backend/src/{api/Data => infrastructure/Keycloack}/KeycloakJwtOptions.cs (74%) rename backend/src/{api => infrastructure}/Migrations/20240330212015_TodoList.Designer.cs (94%) rename backend/src/{api => infrastructure}/Migrations/20240330212015_TodoList.cs (96%) rename backend/src/{api => infrastructure}/Migrations/ApplicationDbContextModelSnapshot.cs (94%) diff --git a/backend/Dockerfile b/backend/Dockerfile index 7a65178..25dc31c 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -5,6 +5,7 @@ WORKDIR /app EXPOSE 80 EXPOSE 443 +#Todo: clean it up for multiproject build FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY src/api/api.csproj src/api/ diff --git a/backend/src/api/Constants/ErrorMessages.cs b/backend/src/api/Constants/ErrorMessages.cs deleted file mode 100644 index 12f82b7..0000000 --- a/backend/src/api/Constants/ErrorMessages.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace api.Constants; - -public static class ErrorMessages -{ - public const string UserAlreadyExists = "User already exists"; -} diff --git a/backend/src/api/Controllers/TodoController.cs b/backend/src/api/Controllers/TodoController.cs index 5436e8c..4506d68 100644 --- a/backend/src/api/Controllers/TodoController.cs +++ b/backend/src/api/Controllers/TodoController.cs @@ -1,4 +1,4 @@ -using api.Services; +using core.Ports; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; diff --git a/backend/src/api/Program.cs b/backend/src/api/Program.cs index f6d88a0..4e0557e 100644 --- a/backend/src/api/Program.cs +++ b/backend/src/api/Program.cs @@ -1,12 +1,13 @@ -using api.Data; using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; using Microsoft.OpenApi.Models; -using Microsoft.EntityFrameworkCore; using Serilog; -using api; using System.Security.Cryptography; -using api.Services; +using api; +using infrastructure.Data; +using infrastructure.Keycloak; +using core.Ports; const string logFormat = "[{Timestamp:HH:mm:ss} {Level:u3}] {CorelationId} | {Message:lj}{NewLine}{Exception}"; Log.Logger = new LoggerConfiguration().Enrich.WithCorrelationId() @@ -19,7 +20,10 @@ var builder = WebApplication.CreateBuilder(args); builder.Services.AddHttpContextAccessor(); builder.Host.UseSerilog(); - builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration.GetConnectionString("KSummarized"))); + builder.Services.AddDbContext( + options => options.UseNpgsql(builder.Configuration.GetConnectionString("KSummarized"), + x => x.MigrationsAssembly("infrastructure") + )); var keycloakJwtOptions = builder.Configuration.GetRequiredSection("KeycloakJwt").Get()!; // Create RSA key for offline validation of Keycloak token diff --git a/backend/src/api/Services/Todo/ITodoService.cs b/backend/src/api/Services/Todo/ITodoService.cs deleted file mode 100644 index fe5788e..0000000 --- a/backend/src/api/Services/Todo/ITodoService.cs +++ /dev/null @@ -1,13 +0,0 @@ -using api.Data.DAO; -using api.Data.DTO; - -namespace api.Services; - -public interface ITodoService -{ - Task CreateList(string user, string name); - IEnumerable GetLists(string userId); - TodoListDTO? GetList(string userId, int id); - bool DeleteList(string userId, int id); - Task RenameList(string user, int id, string name); -} diff --git a/backend/src/api/api.csproj b/backend/src/api/api.csproj index 1ed02ca..47086bf 100644 --- a/backend/src/api/api.csproj +++ b/backend/src/api/api.csproj @@ -11,10 +11,9 @@ - - - all + runtime; build; native; contentfiles; analyzers; buildtransitive + all @@ -27,4 +26,9 @@ + + + + + diff --git a/backend/src/core/Ports/ITodoService.cs b/backend/src/core/Ports/ITodoService.cs new file mode 100644 index 0000000..21ef07a --- /dev/null +++ b/backend/src/core/Ports/ITodoService.cs @@ -0,0 +1,10 @@ +namespace core.Ports; + +public interface ITodoService +{ + Task CreateList(string user, string name); + IEnumerable GetLists(string userId); + TodoList? GetList(string userId, int id); + bool DeleteList(string userId, int id); + Task RenameList(string user, int id, string name); +} diff --git a/backend/src/core/TodoList.cs b/backend/src/core/TodoList.cs new file mode 100644 index 0000000..8392f04 --- /dev/null +++ b/backend/src/core/TodoList.cs @@ -0,0 +1,10 @@ +namespace core; + +public class TodoList +{ + public int Id { get; set; } + public required string Name { get; set; } + public required Guid Owner { get; set; } + + public bool IsAuthorized(Guid person) => Owner.Equals(person); +} diff --git a/backend/src/core/core.csproj b/backend/src/core/core.csproj new file mode 100644 index 0000000..bb23fb7 --- /dev/null +++ b/backend/src/core/core.csproj @@ -0,0 +1,9 @@ + + + + net8.0 + enable + enable + + + diff --git a/backend/src/infrastructure/Class1.cs b/backend/src/infrastructure/Class1.cs deleted file mode 100644 index 47bb804..0000000 --- a/backend/src/infrastructure/Class1.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace infrastructure; - -public class Class1 -{ - -} diff --git a/backend/src/api/Data/ApplicationDbContext.cs b/backend/src/infrastructure/Data/ApplicationDbContext.cs similarity index 85% rename from backend/src/api/Data/ApplicationDbContext.cs rename to backend/src/infrastructure/Data/ApplicationDbContext.cs index 8bbcdb8..df8dfd3 100644 --- a/backend/src/api/Data/ApplicationDbContext.cs +++ b/backend/src/infrastructure/Data/ApplicationDbContext.cs @@ -1,7 +1,6 @@ -using api.Data.DAO; using Microsoft.EntityFrameworkCore; -namespace api.Data; +namespace infrastructure.Data; public class ApplicationDbContext : DbContext { diff --git a/backend/src/api/Data/DAO/TodoListModel.cs b/backend/src/infrastructure/Data/TodoListModel.cs similarity index 77% rename from backend/src/api/Data/DAO/TodoListModel.cs rename to backend/src/infrastructure/Data/TodoListModel.cs index f2f8a57..d838b5b 100644 --- a/backend/src/api/Data/DAO/TodoListModel.cs +++ b/backend/src/infrastructure/Data/TodoListModel.cs @@ -1,7 +1,7 @@ -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -namespace api.Data.DAO; +namespace infrastructure.Data; [Table("todo_lists")] public class TodoListModel diff --git a/backend/src/api/Services/Todo/TodoService.cs b/backend/src/infrastructure/Data/TodoService.cs similarity index 71% rename from backend/src/api/Services/Todo/TodoService.cs rename to backend/src/infrastructure/Data/TodoService.cs index 8b24c70..3d2dcc0 100644 --- a/backend/src/api/Services/Todo/TodoService.cs +++ b/backend/src/infrastructure/Data/TodoService.cs @@ -1,9 +1,8 @@ -using api.Data; -using api.Data.DAO; -using api.Data.DTO; using Microsoft.EntityFrameworkCore; +using core.Ports; +using core; -namespace api.Services; +namespace infrastructure.Data; public class TodoService : ITodoService { @@ -15,29 +14,29 @@ public TodoService(ApplicationDbContext context) _context = context; } - public IEnumerable GetLists(string userId) + public IEnumerable GetLists(string userId) { return _context.TodoLists.AsNoTracking() .Where(list => list.Owner.Equals(Guid.Parse(userId))) - .Select(list => new TodoListDTO(list.Id, list.Name)) + .Select(list => new TodoList { Id = list.Id, Name = list.Name, Owner = list.Owner }) .AsEnumerable(); } - public TodoListDTO? GetList(string userId, int id) + public TodoList? GetList(string userId, int id) { var list = _context.TodoLists.AsNoTracking() .SingleOrDefault(l => l.Owner.Equals(Guid.Parse(userId)) && l.Id == id); //TODO: Consider creating a mapper instead of this manual new - if (list is not null) { return new(list.Id, list.Name); } + if (list is not null) { return new() { Id = list.Id, Name = list.Name, Owner = list.Owner }; } return null; } - public async Task CreateList(string user, string name) + public async Task CreateList(string user, string name) { var newList = new TodoListModel() { Name = name, Owner = Guid.Parse(user) }; _context.TodoLists.Add(newList); await _context.SaveChangesAsync(); - return newList; + return new TodoList() { Id = newList.Id, Name = newList.Name, Owner = newList.Owner }; } public bool DeleteList(string userId, int id) diff --git a/backend/src/api/Data/KeycloakJwtOptions.cs b/backend/src/infrastructure/Keycloack/KeycloakJwtOptions.cs similarity index 74% rename from backend/src/api/Data/KeycloakJwtOptions.cs rename to backend/src/infrastructure/Keycloack/KeycloakJwtOptions.cs index 42f458c..83a139c 100644 --- a/backend/src/api/Data/KeycloakJwtOptions.cs +++ b/backend/src/infrastructure/Keycloack/KeycloakJwtOptions.cs @@ -1,6 +1,4 @@ -using System.Diagnostics.CodeAnalysis; - -namespace api.Data; +namespace infrastructure.Keycloak; public class KeycloakJwtOptions { public required string Issuer { get; init; } diff --git a/backend/src/api/Migrations/20240330212015_TodoList.Designer.cs b/backend/src/infrastructure/Migrations/20240330212015_TodoList.Designer.cs similarity index 94% rename from backend/src/api/Migrations/20240330212015_TodoList.Designer.cs rename to backend/src/infrastructure/Migrations/20240330212015_TodoList.Designer.cs index 121acfa..ba9fe02 100644 --- a/backend/src/api/Migrations/20240330212015_TodoList.Designer.cs +++ b/backend/src/infrastructure/Migrations/20240330212015_TodoList.Designer.cs @@ -1,15 +1,15 @@ -// +// using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using api.Data; +using infrastructure.Data; #nullable disable -namespace api.Migrations +namespace infrastructure.Migrations { [DbContext(typeof(ApplicationDbContext))] [Migration("20240330212015_TodoList")] diff --git a/backend/src/api/Migrations/20240330212015_TodoList.cs b/backend/src/infrastructure/Migrations/20240330212015_TodoList.cs similarity index 96% rename from backend/src/api/Migrations/20240330212015_TodoList.cs rename to backend/src/infrastructure/Migrations/20240330212015_TodoList.cs index e0bf4b0..13b4cef 100644 --- a/backend/src/api/Migrations/20240330212015_TodoList.cs +++ b/backend/src/infrastructure/Migrations/20240330212015_TodoList.cs @@ -1,10 +1,10 @@ -using System; +using System; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; #nullable disable -namespace api.Migrations +namespace infrastructure.Migrations { /// public partial class TodoList : Migration diff --git a/backend/src/api/Migrations/ApplicationDbContextModelSnapshot.cs b/backend/src/infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs similarity index 94% rename from backend/src/api/Migrations/ApplicationDbContextModelSnapshot.cs rename to backend/src/infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs index 1b642d3..42e6371 100644 --- a/backend/src/api/Migrations/ApplicationDbContextModelSnapshot.cs +++ b/backend/src/infrastructure/Migrations/ApplicationDbContextModelSnapshot.cs @@ -1,14 +1,14 @@ -// +// using System; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage.ValueConversion; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; -using api.Data; +using infrastructure.Data; #nullable disable -namespace api.Migrations +namespace infrastructure.Migrations { [DbContext(typeof(ApplicationDbContext))] partial class ApplicationDbContextModelSnapshot : ModelSnapshot diff --git a/backend/src/infrastructure/infrastructure.csproj b/backend/src/infrastructure/infrastructure.csproj index bb23fb7..7d93446 100644 --- a/backend/src/infrastructure/infrastructure.csproj +++ b/backend/src/infrastructure/infrastructure.csproj @@ -6,4 +6,17 @@ enable + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + diff --git a/scripts/apply_migrations.ps1 b/scripts/apply_migrations.ps1 index 05aa6c2..773a305 100755 --- a/scripts/apply_migrations.ps1 +++ b/scripts/apply_migrations.ps1 @@ -11,9 +11,9 @@ get-content ../.env | foreach { Write-Host "Starting DB" docker compose up db -d 2>&1 > $null Write-Output "Generateing migration script" -Set-Location ../backend/src/api +Set-Location ../backend mkdir migration_scripts 2>&1 > $null -dotnet ef migrations script --idempotent -o ./migration_scripts/migration.sql +dotnet ef migrations script --idempotent -o ./migration_scripts/migration.sql -p ./src/infrastructure -s ./src/api Write-Output "Applying.." $passwd = "PGPASSWORD=" + $Env:POSTGRES_PASSWORD @@ -24,7 +24,7 @@ docker run --network host -e $passwd -v ${src}:/migrations/ --rm postgres ` Write-Output "Cleanup" Remove-Item -Recurse -Force migration_scripts -Set-Location ../../../scripts +Set-Location ../scripts Write-Output "Migrations had been applyed!" Write-Host "Stoping DB" docker stop ks-database 2>&1 > $null From 7d2e6a56a660b5f7c0636e296dffa3bda00ced6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Tracewicz?= Date: Wed, 11 Sep 2024 15:22:03 +0200 Subject: [PATCH 15/15] Closes #60 Address code review comments --- backend/bruno/KSummarized/ToDo/List/Create.bru | 12 +++++------- backend/bruno/KSummarized/ToDo/List/Delete.bru | 2 +- backend/bruno/KSummarized/ToDo/List/Get.bru | 2 +- backend/bruno/KSummarized/ToDo/List/List.bru | 2 +- .../KSummarized/ToDo/List/{Edit.bru => Rename.bru} | 4 ++-- backend/bruno/KSummarized/environments/Local.bru | 1 + backend/src/api/Controllers/TodoController.cs | 4 ++-- 7 files changed, 13 insertions(+), 14 deletions(-) rename backend/bruno/KSummarized/ToDo/List/{Edit.bru => Rename.bru} (67%) diff --git a/backend/bruno/KSummarized/ToDo/List/Create.bru b/backend/bruno/KSummarized/ToDo/List/Create.bru index fc67a0d..b3e2ba6 100644 --- a/backend/bruno/KSummarized/ToDo/List/Create.bru +++ b/backend/bruno/KSummarized/ToDo/List/Create.bru @@ -5,21 +5,19 @@ meta { } post { - url: https://localhost:5000/api/todo/lists?name=demo - body: none + url: https://{{api_base_url}}/api/todo/lists + body: json auth: bearer } -params:query { - name: demo -} - auth:bearer { token: {{token}} } body:json { - "ToDo" + { + "name": "Demo" + } } vars:post-response { diff --git a/backend/bruno/KSummarized/ToDo/List/Delete.bru b/backend/bruno/KSummarized/ToDo/List/Delete.bru index 599b3cc..996430a 100644 --- a/backend/bruno/KSummarized/ToDo/List/Delete.bru +++ b/backend/bruno/KSummarized/ToDo/List/Delete.bru @@ -5,7 +5,7 @@ meta { } delete { - url: https://localhost:5000/api/todo/lists/{{listId}} + url: https://{{api_base_url}}/api/todo/lists/{{listId}} body: json auth: bearer } diff --git a/backend/bruno/KSummarized/ToDo/List/Get.bru b/backend/bruno/KSummarized/ToDo/List/Get.bru index 9012765..df97d89 100644 --- a/backend/bruno/KSummarized/ToDo/List/Get.bru +++ b/backend/bruno/KSummarized/ToDo/List/Get.bru @@ -5,7 +5,7 @@ meta { } get { - url: https://localhost:5000/api/todo/lists/{{listId}} + url: https://{{api_base_url}}/api/todo/lists/{{listId}} body: none auth: bearer } diff --git a/backend/bruno/KSummarized/ToDo/List/List.bru b/backend/bruno/KSummarized/ToDo/List/List.bru index 174fd00..7b6343c 100644 --- a/backend/bruno/KSummarized/ToDo/List/List.bru +++ b/backend/bruno/KSummarized/ToDo/List/List.bru @@ -5,7 +5,7 @@ meta { } get { - url: https://localhost:5000/api/todo/lists + url: https://{{api_base_url}}/api/todo/lists body: none auth: bearer } diff --git a/backend/bruno/KSummarized/ToDo/List/Edit.bru b/backend/bruno/KSummarized/ToDo/List/Rename.bru similarity index 67% rename from backend/bruno/KSummarized/ToDo/List/Edit.bru rename to backend/bruno/KSummarized/ToDo/List/Rename.bru index f03ee4f..cf40331 100644 --- a/backend/bruno/KSummarized/ToDo/List/Edit.bru +++ b/backend/bruno/KSummarized/ToDo/List/Rename.bru @@ -1,11 +1,11 @@ meta { - name: Edit + name: Rename type: http seq: 3 } put { - url: https://localhost:5000/api/todo/lists/{{listId}} + url: https://{{api_base_url}}/api/todo/lists/{{listId}} body: json auth: bearer } diff --git a/backend/bruno/KSummarized/environments/Local.bru b/backend/bruno/KSummarized/environments/Local.bru index 805b24b..4dc37f4 100644 --- a/backend/bruno/KSummarized/environments/Local.bru +++ b/backend/bruno/KSummarized/environments/Local.bru @@ -1,6 +1,7 @@ vars { keycloak_realm: KnowledgeSummarized keycloak_url: http://localhost:8080 + api_base_url: localhost:5000 } vars:secret [ token, diff --git a/backend/src/api/Controllers/TodoController.cs b/backend/src/api/Controllers/TodoController.cs index 4506d68..0a37197 100644 --- a/backend/src/api/Controllers/TodoController.cs +++ b/backend/src/api/Controllers/TodoController.cs @@ -59,7 +59,7 @@ public IActionResult DeleteList([FromRoute] int id) } [HttpPost("lists")] - public async Task CreateLists([FromQuery] Request request) + public async Task CreateLists([FromBody] Request request) { var userId = Request.UserId(); _logger.LogDebug("User: {user} created: {list}", userId, request.Name); @@ -81,7 +81,7 @@ async Task Create(string user, string name) public async Task RenameList([FromRoute] int id, [FromBody] Request request) { var userId = Request.UserId(); - _logger.LogDebug("User: {user} created: {list}", userId, request.Name); + _logger.LogDebug("User: {user} renamed: {id} to: {list}", userId, id, request.Name); return userId switch { null => Unauthorized(),