diff --git a/Backend/Backend.csproj b/Backend/Backend.csproj index 106d073..c5aa143 100644 --- a/Backend/Backend.csproj +++ b/Backend/Backend.csproj @@ -10,6 +10,8 @@ + + diff --git a/Backend/Database/PostgresDbContext.cs b/Backend/Database/PostgresDbContext.cs new file mode 100644 index 0000000..eff0cac --- /dev/null +++ b/Backend/Database/PostgresDbContext.cs @@ -0,0 +1,23 @@ +using Backend.Models; +using Microsoft.EntityFrameworkCore; + +namespace Backend.Database; + +/// +/// Contains all information on the postgresql database, for the object-database mapping. +/// This allows using C# to run SQL queries on the database. +/// +public class PostgresDbContext : DbContext +{ + /// Reference to the Activities relational table. + public DbSet Activities { get; set; } + /// Reference to the Enrollments relational table. + public DbSet Enrollments { get; set; } + + /// + /// Creates information how to set up the object-database mapping, from C# to SQL, on the postgresql database. + /// + /// All parameters that define the database connection. + public PostgresDbContext(DbContextOptions options) : base(options) + { } +} diff --git a/Backend/Models/Activity.cs b/Backend/Models/Activity.cs index 2a40159..2f6d774 100644 --- a/Backend/Models/Activity.cs +++ b/Backend/Models/Activity.cs @@ -1,6 +1,9 @@ +using Microsoft.EntityFrameworkCore; + #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. namespace Backend.Models; +[PrimaryKey(nameof(Id))] public class Activity { /// @@ -10,7 +13,7 @@ public class Activity /// /// The name of the activity. /// - public string Name { get; set; } + public string Name { get; set; } // TODO: set max string length for performance? /// /// A description or arbitrary length, explaining everything there is to know about the activity. /// diff --git a/Backend/Models/Participation.cs b/Backend/Models/Enrollment.cs similarity index 84% rename from Backend/Models/Participation.cs rename to Backend/Models/Enrollment.cs index 7b9b7fc..143cd9a 100644 --- a/Backend/Models/Participation.cs +++ b/Backend/Models/Enrollment.cs @@ -1,14 +1,14 @@ #pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. namespace Backend.Models; -public class Participation +public class Enrollment { /// - /// Reference to the unique identifier of the activity which is participated. + /// Reference to the unique identifier of the activity which is enrolled for. /// public uint ActivityId { get; set; } /// - /// The ID of the user, as determined by the used OAuth application, which participated in the activity. + /// The ID of the user, as determined by the used OAuth application, which enrolls for the activity. /// public string UserId { get; set; } } diff --git a/Backend/Program.cs b/Backend/Program.cs index 61ad702..5c4944b 100644 --- a/Backend/Program.cs +++ b/Backend/Program.cs @@ -1,4 +1,6 @@ +using Backend.Database; using Backend.Models; +using Microsoft.AspNetCore.Http.HttpResults; var builder = WebApplication.CreateBuilder(args); @@ -6,6 +8,7 @@ // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); +builder.Services.AddNpgsql(connectionString: builder.Configuration.GetConnectionString("Postgresql")); var app = builder.Build(); @@ -18,17 +21,41 @@ app.UseHttpsRedirection(); -app.MapGet("/activity", (uint activityId) => +app.MapGet("/activity", async (PostgresDbContext db, uint activityId) => { - return new Activity() - { - Id = activityId, - Name = "Some activity", - Description = "Some activity description", - DateTimeStart = DateTimeOffset.Now, - }; + // Simply fetch and return + Activity? activity = await db.Activities.FindAsync(activityId); + return activity != null ? Results.Ok(activity) : Results.NotFound(); + }) + // .WithName("GetWeatherForecast") + .WithOpenApi(); + +app.MapPost("/activity", async (PostgresDbContext db, Activity activity) => + { + // First, ensure activity with same id does not exist yet + Activity? currentActivity = await db.Activities.FindAsync(activity.Id); + if (currentActivity != null) + return Results.BadRequest("Activity already exists with this Id."); + + // Activity does not exist yet, create it + db.Activities.Add(activity); + await db.SaveChangesAsync(); + return Results.Ok(activity.Id); + }) + .WithOpenApi(); + +app.MapDelete("/activity", async (PostgresDbContext db, uint activityId) => + { + // First, check if activity exists + Activity? activity = await db.Activities.FindAsync(activityId); + if (activity == null) + return Results.NotFound(); + + // Remove activity + db.Activities.Remove(activity); + await db.SaveChangesAsync(); + return Results.Ok(); }) - .WithName("GetWeatherForecast") .WithOpenApi(); app.Run(); diff --git a/Backend/Tavern.http b/Backend/Tavern.http index 69f67b3..bb68d22 100644 --- a/Backend/Tavern.http +++ b/Backend/Tavern.http @@ -1,6 +1,6 @@ -@Tavern_HostAddress = http://localhost:5273 +@Tavern_HostAddress = http://localhost:8080 -GET {{Tavern_HostAddress}}/weatherforecast/ +GET {{Tavern_HostAddress}}/activity?activityId=1 Accept: application/json ### diff --git a/Backend/appsettings.json b/Backend/appsettings.json index 10f68b8..9a0ad70 100644 --- a/Backend/appsettings.json +++ b/Backend/appsettings.json @@ -1,4 +1,7 @@ { + "ConnectionStrings": { + "Postgresql": "postgres" + }, "Logging": { "LogLevel": { "Default": "Information", diff --git a/compose.yaml b/compose.yaml index aa75dd2..71b097c 100644 --- a/compose.yaml +++ b/compose.yaml @@ -1,6 +1,26 @@ services: - tavern-backend: - image: backend + backend: + restart: unless-stopped + ports: + - "8080:8080" build: context: . dockerfile: Backend/Dockerfile + networks: + - postgres-network + + postgres: + image: postgres:17 + restart: always + environment: + POSTGRES_PASSWORD: docker-secret # TODO: use docker secrets to store this password + volumes: + - postgres-data:$PGDATA + networks: + - postgres-network + +volumes: + postgres-data: + +networks: + postgres-network: \ No newline at end of file