Skip to content

Commit

Permalink
feat(311699): Add Weather CRUD endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
SulliNV authored and Geoffrey-Dulac committed Dec 16, 2024
1 parent 591fa53 commit 295d00a
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 28 deletions.
17 changes: 12 additions & 5 deletions template/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Placeholder
# Placeholder - Backend

{Project tag line}

Expand All @@ -18,7 +18,7 @@ The main prerequisites for a developer setup are:

Precise installations instructions are as follow:

### For WSL Users
### For WSL Users (windows)
- If you’re using Docker Desktop, remember to [enable it in your WSL distro](https://docs.docker.com/desktop/wsl/#enabling-docker-support-in-wsl-2-distros)
- Active WSL extension in VS Code.
- The ASP.NET Core developement certificate installed in WSL must be trusted in your Windows browser.
Expand All @@ -40,7 +40,7 @@ If you get a message " A valid HTTPS certificate is already present." it doesnt

```console
sudo apt update
sudo apt install dotnet-sdk-8.0
sudo apt install dotnet-sdk-9.0
dotnet dev-certs https --clean --import /mnt/c/[path]/https.pfx --password [password]
sudo mkdir /usr/local/share/ca-certificates/aspnet/
sudo -E dotnet dev-certs https -ep /usr/local/share/ca-certificates/aspnet/https.crt --format PEM
Expand Down Expand Up @@ -144,10 +144,11 @@ just code
In command palette (Mac: _command+shift+p_, windows: _ctrl+shift+p_) :
- .NET: Clean
- .NET: Build
- Run > Start Debugging > C# > [ProjectName].AppHost [Default]
- Run > Start Debugging > C# > Placeholder.AppHost [Default]

Later it can be run from *run & debug* tab.
*If you're running into an exception on migration just press **Continue** until everything is running, then wait until the database containers are ready and press **Play** ▶︎ on migration container.

Later it can be run from *run & debug* tab.

To get a list of other recipes:
```console
Expand All @@ -158,6 +159,12 @@ just --list

{More details/listing of features of the project}

## Migrations
The following recipe can be used to create new migrations based on template :
```console
just create-migration **migration-name**
```

## Breaking Changes

Please consult [BREAKING_CHANGES.md](BREAKING_CHANGES.md) for more information about version
Expand Down
2 changes: 1 addition & 1 deletion template/global.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"sdk": {
"version": "9.0.100",
"rollForward": "disable"
"rollForward": "major"
}
}
66 changes: 62 additions & 4 deletions template/src/Placeholder.ApiService/Endpoints/WeatherEndpoints.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using Placeholder.Core;
using Placeholder.Core.Models;
Expand All @@ -7,10 +8,67 @@ public static class WeatherEndpoints
{
public static void MapWeatherEndpoints(this IEndpointRouteBuilder endpoints)
{
endpoints.MapGet("/weather", async ([FromServices] IWeatherService weatherService) =>
{
return await weatherService.GetWeather();
});
var weatherGroup = endpoints.MapGroup("/weather");

endpoints.MapGet("/weathers", GetAllWeathers);
weatherGroup.MapGet("/{date}", GetWeather);
weatherGroup.MapPost("/", CreateWeather);
weatherGroup.MapPut("/", UpdateWeather);
weatherGroup.MapDelete("/{date}", DeleteWeather);
}

private static async Task<Results<Ok<IEnumerable<Weather>>, InternalServerError>> GetAllWeathers(IWeatherService weatherService)
{
try {
IEnumerable<Weather> weathers = await weatherService.GetWeathers();
return TypedResults.Ok(weathers);
} catch {
return TypedResults.InternalServerError();
}
}

private static async Task<Results<Ok<Weather>, NotFound, InternalServerError>> GetWeather(DateTime date, IWeatherService weatherService)
{
try {
Weather? weather = await weatherService.GetWeather(date);

if (weather is null) return TypedResults.NotFound();
return TypedResults.Ok(weather);
} catch {
return TypedResults.InternalServerError();
}
}

private static async Task<Results<Created<Weather>, NotFound, InternalServerError>> CreateWeather([FromBody] Weather weather, IWeatherService weatherService)
{
try {
Weather? weatherCreated = await weatherService.CreateWeather(weather);

return TypedResults.Created($"weather/{weatherCreated?.Date:O}", weatherCreated);
} catch {
return TypedResults.InternalServerError();
}
}

private static async Task<Results<Ok<Weather>, NotFound, InternalServerError>> UpdateWeather([FromBody] Weather weather, IWeatherService weatherService)
{
try {
Weather? weatherUpdated = await weatherService.UpdateWeather(weather);

if (weatherUpdated is null) return TypedResults.NotFound();
return TypedResults.Ok(weatherUpdated);
} catch {
return TypedResults.InternalServerError();
}
}

private static async Task<Results<Ok, InternalServerError>> DeleteWeather(DateTime date, IWeatherService weatherService)
{
try {
await weatherService.DeleteWeather(date);
return TypedResults.Ok();
} catch {
return TypedResults.InternalServerError();
}
}
}
6 changes: 5 additions & 1 deletion template/src/Placeholder.Core/Weather/IWeatherRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@ namespace Placeholder.Core;

public interface IWeatherRepository
{
Task<Weather[]> GetWeather();
Task<IEnumerable<Weather>> GetWeathers();
Task<Weather?> GetWeather(DateTime date);
Task<Weather?> CreateWeather(Weather weather);
Task<Weather?> UpdateWeather(Weather weather);
Task DeleteWeather(DateTime date);
}
48 changes: 47 additions & 1 deletion template/src/Placeholder.Core/Weather/IWeatherService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,51 @@ namespace Placeholder.Core;

public interface IWeatherService
{
Task<Weather[]> GetWeather();
/// <summary>
/// Get all weathers
/// </summary>
/// <returns>
/// A task that represents the asynchronous operation. The task result is an enumerable collection of <see cref="Weather"/> objects.
/// </returns>
Task<IEnumerable<Weather>> GetWeathers();

/// <summary>
/// Get weather by id
/// </summary>
/// <param name="id">The database identifier of the weather.</param>
/// <returns>
/// A task that represents the asynchronous operation. The task result is a <see cref="Weather"/> objects.
/// If the weather is not found, the task result is <c>null</c>.
/// </returns>
Task<Weather?> GetWeather(DateTime date);

/// <summary>
/// Create a new weather
/// </summary>
/// <param name="weather">The weather object to be added.</param>
/// <returns>
/// A task that represents the asynchronous operation. The task result is a <see cref="Weather"/> objects.
/// If the weather is not created, the task result is <c>null</c>.
/// </returns>
Task<Weather?> CreateWeather(Weather weather);

/// <summary>
/// Update weather by id
/// </summary>
/// <param name="id">The database identifier of the weather.</param>
/// <param name="weather">The weather object to be updated.</param>
/// <returns>
/// A task that represents the asynchronous operation. The task result is a <see cref="Weather"/> objects with the informations of the updated weather.
/// If the weather is not found, the task result is <c>null</c>.
/// </returns>
Task<Weather?> UpdateWeather(Weather weather);

/// <summary>
/// Delete weather by id
/// </summary>
/// <param name="id">The database identifier of the weather.</param>
/// <returns>
/// A task that represents the asynchronous operation. The task result is : Void.
/// </returns>
Task DeleteWeather(DateTime date);
}
4 changes: 2 additions & 2 deletions template/src/Placeholder.Core/Weather/Weather.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ public class Weather
{
public Weather() { }

public Weather(string date, int temperatureC, string? summary)
public Weather(DateTime date, int temperatureC, string? summary)
{
Date = date;
TemperatureC = temperatureC;
Summary = summary;
}

public string? Date { get; set; }
public DateTime? Date { get; set; }

public int TemperatureC { get; set; }

Expand Down
56 changes: 49 additions & 7 deletions template/src/Placeholder.Core/Weather/WeatherRepository.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,65 @@

namespace Placeholder.Core;

internal class WeatherRepository : IWeatherRepository
internal class WeatherRepository(DbConnection connection) : IWeatherRepository
{
private readonly DbConnection _dbConnection;
private readonly DbConnection _dbConnection = connection;

public WeatherRepository(DbConnection connection)
public async Task<IEnumerable<Weather>> GetWeathers()
{
_dbConnection = connection;
var query = @"
SELECT Date, TemperatureC, Summary
FROM Weather
";

var result = await _dbConnection.QueryAsync<Weather>(query);
return result.ToArray();
}

public async Task<Weather[]> GetWeather()
public async Task<Weather?> GetWeather(DateTime date)
{
var query = @"
SELECT Date, TemperatureC, Summary
FROM Weather
WHERE Date = @Date
";

var result = await _dbConnection.QueryAsync<Weather>(query);
return result.ToArray();
return await _dbConnection.QuerySingleOrDefaultAsync<Weather>(query, new { Date = date });
}

public async Task<Weather?> CreateWeather(Weather weather)
{
var query = @"
INSERT INTO Weather (Date, TemperatureC, Summary)
VALUES (@Date, @TemperatureC, @Summary)
RETURNING Date, TemperatureC, Summary
";

Weather? weatherCreated = await _dbConnection.QuerySingleOrDefaultAsync<Weather>(query, new {weather.Date, weather.TemperatureC, weather.Summary}) ?? null;
return weatherCreated;
}

public async Task<Weather?> UpdateWeather(Weather weather)
{
var query = @"
UPDATE Weather
SET TemperatureC = @TemperatureC, Summary = @Summary
WHERE Date = @Date
RETURNING Date, TemperatureC, Summary
";

Weather? weatherUpdated = await _dbConnection.QuerySingleOrDefaultAsync<Weather>(query, new { weather.Date, weather.TemperatureC, weather.Summary }) ?? null;
return weatherUpdated;
}

public async Task DeleteWeather(DateTime date)
{
var query = @"
DELETE
FROM Weather
WHERE Date = @Date
";

await _dbConnection.ExecuteAsync(query, new { Date = date });
}
}
29 changes: 22 additions & 7 deletions template/src/Placeholder.Core/Weather/WeatherService.cs
Original file line number Diff line number Diff line change
@@ -1,18 +1,33 @@
using Placeholder.Core;
using Placeholder.Core.Models;

namespace Placeholder.Core;

internal class WeatherService : IWeatherService
internal class WeatherService(IWeatherRepository weatherRepository) : IWeatherService
{
private readonly IWeatherRepository _weatherRepository;
public WeatherService(IWeatherRepository weatherRepository)
private readonly IWeatherRepository _weatherRepository = weatherRepository;

public async Task<IEnumerable<Weather>> GetWeathers()
{
return await _weatherRepository.GetWeathers();
}

public async Task<Weather?> GetWeather(DateTime date)
{
return await _weatherRepository.GetWeather(date);
}

public async Task<Weather?> CreateWeather(Weather weather)
{
return await _weatherRepository.CreateWeather(weather);
}

public async Task<Weather?> UpdateWeather(Weather weather)
{
_weatherRepository = weatherRepository;
return await _weatherRepository.UpdateWeather(weather);
}

public async Task<Weather[]> GetWeather()
public async Task DeleteWeather(DateTime date)
{
return await _weatherRepository.GetWeather();
await _weatherRepository.DeleteWeather(date);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ Summary VARCHAR(255)
);
");

// Create a unique index on the Date column
Execute.Sql(@"
CREATE UNIQUE INDEX IX_Weather_Date ON Weather(Date);
");

// Insert some test data
Execute.Sql(@"
INSERT INTO Weather (Date, TemperatureC, Summary) VALUES
Expand Down

0 comments on commit 295d00a

Please sign in to comment.