From 41d1cc4fc03f0471d8681f0081c8c67b7007ff3c Mon Sep 17 00:00:00 2001 From: Steve Date: Mon, 29 Dec 2025 16:03:16 -0600 Subject: [PATCH] Added Card model, updated database initialization, udated readme. --- README.md | 25 +++++-- SDG-Backend-Barracuda/Models/CardModel.cs | 50 +++++++++++++ .../Models/IDatabaseModel.cs | 10 +++ SDG-Backend-Barracuda/Program.cs | 73 +++++++++++++------ .../SDG-Backend-Barracuda.csproj | 6 +- .../SDG-Backend-Barracuda.http | 26 ++++++- SDG-Backend-Barracuda/appsettings.json | 3 + 7 files changed, 161 insertions(+), 32 deletions(-) create mode 100644 SDG-Backend-Barracuda/Models/CardModel.cs create mode 100644 SDG-Backend-Barracuda/Models/IDatabaseModel.cs diff --git a/README.md b/README.md index 49eded3..c652442 100644 --- a/README.md +++ b/README.md @@ -15,27 +15,40 @@ Follow these steps to set up your local PostgreSQL database: If you haven't already, create a database and a user with appropriate permissions. You can use `psql` or a tool like pgAdmin. ```sql --- Create the database -CREATE DATABASE "SDG-Backend"; --- Create the user +-- Create SDG-Backend-Admin CREATE USER "SDG-Backend-Admin" WITH ENCRYPTED PASSWORD 'password'; --- Grant privileges -GRANT ALL PRIVILEGES ON DATABASE "SDG-Backend" TO "SDG-Backend-Admin"; +-- Create the database +CREATE DATABASE "SDG-Backend" OWNER "SDG-Backend-Admin"; +``` + +### 2. Configure Connection String +#### Initialize User Secrets +Run this in the project directory (`SDG-Backend-Barracuda\SDG-Backend-Barracuda`): +```bash +dotnet user-secrets init +``` + +#### Set the Connection String +Replace `password` if you used a different one during setup: +```bash +dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Host=localhost;Database=SDG-Backend;Username=SDG-Backend-Admin;Password=password;Maximum Pool Size=100;" ``` ## Development ### Installing Dependencies -The project uses `Npgsql` and `Dapper`. These can be installed via: +The project uses `Npgsql`, `Dapper`, and `Npgsql.DependencyInjection`. ```bash dotnet add package Npgsql dotnet add package Dapper +dotnet add package Npgsql.DependencyInjection ``` ### Running the Application +From the root directory: ```bash dotnet run --project SDG-Backend-Barracuda ``` \ No newline at end of file diff --git a/SDG-Backend-Barracuda/Models/CardModel.cs b/SDG-Backend-Barracuda/Models/CardModel.cs new file mode 100644 index 0000000..25bed95 --- /dev/null +++ b/SDG-Backend-Barracuda/Models/CardModel.cs @@ -0,0 +1,50 @@ +using Dapper; +using Npgsql; + +namespace SDG_Backend_Barracuda.Models; + +public record Card(int Id, string Value); +public record CardInput(string Value); + +public interface ICardModel : IDatabaseModel +{ +} + +public class CardModel(NpgsqlDataSource dataSource) : ICardModel +{ + public async Task> GetAllAsync() + { + await using var connection = await dataSource.OpenConnectionAsync(); + var sql = """SELECT "Id", "Value" FROM "Card";"""; + return await connection.QueryAsync(sql); + } + + public async Task GetByIdAsync(int id) + { + await using var connection = await dataSource.OpenConnectionAsync(); + var sql = """SELECT "Id", "Value" FROM "Card" WHERE "Id" = @Id;"""; + return await connection.QueryFirstOrDefaultAsync(sql, new { Id = id }); + } + + public async Task CreateAsync(CardInput input) + { + await using var connection = await dataSource.OpenConnectionAsync(); + var sql = """INSERT INTO "Card" ("Value") VALUES (@Value) RETURNING "Id", "Value";"""; + return await connection.QuerySingleAsync(sql, input); + } + + public async Task UpdateAsync(int id, CardInput input) + { + await using var connection = await dataSource.OpenConnectionAsync(); + var sql = """UPDATE "Card" SET "Value" = @Value WHERE "Id" = @Id RETURNING "Id", "Value";"""; + return await connection.QueryFirstOrDefaultAsync(sql, new { Id = id, Value = input.Value }); + } + + public async Task DeleteAsync(int id) + { + await using var connection = await dataSource.OpenConnectionAsync(); + var sql = """DELETE FROM "Card" WHERE "Id" = @Id;"""; + var rowsAffected = await connection.ExecuteAsync(sql, new { Id = id }); + return rowsAffected > 0; + } +} diff --git a/SDG-Backend-Barracuda/Models/IDatabaseModel.cs b/SDG-Backend-Barracuda/Models/IDatabaseModel.cs new file mode 100644 index 0000000..2defbb4 --- /dev/null +++ b/SDG-Backend-Barracuda/Models/IDatabaseModel.cs @@ -0,0 +1,10 @@ +namespace SDG_Backend_Barracuda.Models; + +public interface IDatabaseModel +{ + Task> GetAllAsync(); + Task GetByIdAsync(int id); + Task CreateAsync(TInput input); + Task UpdateAsync(int id, TInput input); + Task DeleteAsync(int id); +} diff --git a/SDG-Backend-Barracuda/Program.cs b/SDG-Backend-Barracuda/Program.cs index d5e0ef3..d0bd4dc 100644 --- a/SDG-Backend-Barracuda/Program.cs +++ b/SDG-Backend-Barracuda/Program.cs @@ -1,11 +1,34 @@ +using Dapper; +using Npgsql; +using SDG_Backend_Barracuda.Models; + var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi builder.Services.AddOpenApi(); +// Register NpgsqlDataSource +var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); +builder.Services.AddNpgsqlDataSource(connectionString!); + +// Register Data Tables +builder.Services.AddScoped(); + var app = builder.Build(); +// Ensure the table exists +using (var scope = app.Services.CreateScope()) +{ + var dataSource = scope.ServiceProvider.GetRequiredService(); + await using var connection = await dataSource.OpenConnectionAsync(); + await connection.ExecuteAsync(@" + CREATE TABLE IF NOT EXISTS ""Card"" ( + ""Id"" SERIAL PRIMARY KEY, + ""Value"" TEXT NOT NULL + );"); +} + // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { @@ -14,28 +37,34 @@ if (app.Environment.IsDevelopment()) app.UseHttpsRedirection(); -var summaries = new[] +app.MapPost("/card", async (CardInput input, ICardModel card) => { - "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" -}; + var newCard = await card.CreateAsync(input); + return Results.Created($"/card/{newCard.Id}", newCard); +}); -app.MapGet("/weatherforecast", () => - { - var forecast = Enumerable.Range(1, 5).Select(index => - new WeatherForecast - ( - DateOnly.FromDateTime(DateTime.Now.AddDays(index)), - Random.Shared.Next(-20, 55), - summaries[Random.Shared.Next(summaries.Length)] - )) - .ToArray(); - return forecast; - }) - .WithName("GetWeatherForecast"); - -app.Run(); - -record WeatherForecast(DateOnly Date, int TemperatureC, string? Summary) +app.MapGet("/card", async (ICardModel card) => { - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); -} \ No newline at end of file + var result = await card.GetAllAsync(); + return Results.Ok(result); +}); + +app.MapGet("/card/{id}", async (int id, ICardModel card) => +{ + var newCard = await card.GetByIdAsync(id); + return newCard is not null ? Results.Ok(newCard) : Results.NotFound(); +}); + +app.MapPut("/card/{id}", async (int id, CardInput input, ICardModel card) => +{ + var newCard = await card.UpdateAsync(id, input); + return newCard is not null ? Results.Ok(newCard) : Results.NotFound(); +}); + +app.MapDelete("/card/{id}", async (int id, ICardModel card) => +{ + var success = await card.DeleteAsync(id); + return success ? Results.NoContent() : Results.NotFound(); +}); + +app.Run(); \ No newline at end of file diff --git a/SDG-Backend-Barracuda/SDG-Backend-Barracuda.csproj b/SDG-Backend-Barracuda/SDG-Backend-Barracuda.csproj index 56c2e52..02e4fcf 100644 --- a/SDG-Backend-Barracuda/SDG-Backend-Barracuda.csproj +++ b/SDG-Backend-Barracuda/SDG-Backend-Barracuda.csproj @@ -1,16 +1,18 @@ - + net10.0 enable enable SDG_Backend_Barracuda - + 7ceb2efb-dc1a-496c-aa6c-18efbd2a3411 + + diff --git a/SDG-Backend-Barracuda/SDG-Backend-Barracuda.http b/SDG-Backend-Barracuda/SDG-Backend-Barracuda.http index 4d8d6b3..9e103ee 100644 --- a/SDG-Backend-Barracuda/SDG-Backend-Barracuda.http +++ b/SDG-Backend-Barracuda/SDG-Backend-Barracuda.http @@ -1,6 +1,28 @@ @SDG_Backend_Barracuda_HostAddress = http://localhost:5164 -GET {{SDG_Backend_Barracuda_HostAddress}}/weatherforecast/ +### Get all cards +GET {{SDG_Backend_Barracuda_HostAddress}}/card/ Accept: application/json -### +### Create a new card +POST {{SDG_Backend_Barracuda_HostAddress}}/card +Content-Type: application/json + +{ + "value": "Sample Card Value" +} + +### Get a card by ID +GET {{SDG_Backend_Barracuda_HostAddress}}/card/1 +Accept: application/json + +### Update a card +PUT {{SDG_Backend_Barracuda_HostAddress}}/card/1 +Content-Type: application/json + +{ + "value": "Updated Card Value" +} + +### Delete a card +DELETE {{SDG_Backend_Barracuda_HostAddress}}/card/1 diff --git a/SDG-Backend-Barracuda/appsettings.json b/SDG-Backend-Barracuda/appsettings.json index 10f68b8..db25040 100644 --- a/SDG-Backend-Barracuda/appsettings.json +++ b/SDG-Backend-Barracuda/appsettings.json @@ -1,4 +1,7 @@ { + "ConnectionStrings": { + "DefaultConnection": "" + }, "Logging": { "LogLevel": { "Default": "Information",