diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2439f2d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,52 @@ +name: ci + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + dotnet-version: [ '8.0.x' ] + + steps: + - uses: actions/checkout@v2 + - name: Setup .NET Core SDK ${{ matrix.dotnet-version }} + uses: actions/setup-dotnet@v3.0.3 + with: + dotnet-version: ${{ matrix.dotnet-version }} + - name: Install Dependencies + run: dotnet restore Overflow.sln + - name: Build + run: dotnet build --configuration Debug --no-restore Overflow.sln + - name: Test + run: dotnet test --no-restore --verbosity normal Overflow.sln + + build-Docker: + + runs-on: ubuntu-latest + needs: [build, build-Js] # for ignoring bad builds + if: github.event_name == 'push' && (github.ref == 'refs/heads/master' || startsWith(github.ref, 'refs/tags/')) + + steps: + - uses: actions/checkout@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v2 + + - name: Login to DockerHub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build Web Container + uses: docker/build-push-action@v5 + with: + push: true + tags: | + sarsoo/overflow:latest + sarsoo/overflow:${{ github.ref_name }} + file: Dockerfile diff --git a/Overflow.CLI/Program.cs b/Overflow.CLI/Program.cs index 20bf4a2..f841cc4 100644 --- a/Overflow.CLI/Program.cs +++ b/Overflow.CLI/Program.cs @@ -7,9 +7,9 @@ using Overflow.SouthernWater; var driver = new MongoClient("mongodb://localhost"); -var api = new SouthernWater(new HttpClient()); +var api = new SouthernWaterApi(new HttpClient()); await api.LoadApiUrl(); var runner = new SouthernWaterApiJobRunnerPersisting(api, NullLogger.Instance, driver.GetDatabase("overflow")); -await runner.LoadSpills(); \ No newline at end of file +await runner.LoadSpills(5); \ No newline at end of file diff --git a/Overflow.Model/Overflow.Model.csproj b/Overflow.Model/Overflow.Model.csproj deleted file mode 100644 index 60f94b6..0000000 --- a/Overflow.Model/Overflow.Model.csproj +++ /dev/null @@ -1,14 +0,0 @@ - - - - net8.0 - enable - enable - - - - - - - - diff --git a/Overflow.Test/UnitTest1.cs b/Overflow.Test/UnitTest1.cs index 5b0ecbe..1df63a2 100644 --- a/Overflow.Test/UnitTest1.cs +++ b/Overflow.Test/UnitTest1.cs @@ -10,7 +10,7 @@ public class Tests [Test] public async Task Test1() { - var southern = new SouthernWater.SouthernWater(new HttpClient()); + var southern = new SouthernWater.SouthernWaterApi(new HttpClient()); await southern.LoadApiUrl(); var spills = await southern.GetSpills(); } diff --git a/Overflow.Web.Client/Components/SpillsTable.razor b/Overflow.Web.Client/Components/SpillsTable.razor new file mode 100644 index 0000000..bb39bcf --- /dev/null +++ b/Overflow.Web.Client/Components/SpillsTable.razor @@ -0,0 +1,43 @@ +@using Overflow.SouthernWater +@rendermode RenderMode.InteractiveAuto + +@if (Job == null) +{ +

+ Loading... +

+} +else +{ + + + @if (ShowIds) + { + + + + + + } + + + + + + + + + +} + +@code { + [Parameter] public SouthernWaterApiJob? Job { get; set; } + [Parameter] public bool ShowIds { get; set; } +} \ No newline at end of file diff --git a/Overflow.Web/Components/App.razor b/Overflow.Web/Components/App.razor index 274230e..a14c5ed 100644 --- a/Overflow.Web/Components/App.razor +++ b/Overflow.Web/Components/App.razor @@ -5,6 +5,7 @@ + @@ -15,6 +16,7 @@ + \ No newline at end of file diff --git a/Overflow.Web/Components/Layout/NavMenu.razor b/Overflow.Web/Components/Layout/NavMenu.razor index ba8fb3f..ba2a1f5 100644 --- a/Overflow.Web/Components/Layout/NavMenu.razor +++ b/Overflow.Web/Components/Layout/NavMenu.razor @@ -1,6 +1,6 @@  @@ -21,8 +21,8 @@ diff --git a/Overflow.Web/Components/Pages/Home.razor b/Overflow.Web/Components/Pages/Home.razor index dfcdf75..84011ef 100644 --- a/Overflow.Web/Components/Pages/Home.razor +++ b/Overflow.Web/Components/Pages/Home.razor @@ -1,7 +1,5 @@ @page "/" -Home +Overflow -

Hello, world!

- -Welcome to your new app. \ No newline at end of file +

Overflow

diff --git a/Overflow.Web/Components/Pages/Spills.razor b/Overflow.Web/Components/Pages/Spills.razor new file mode 100644 index 0000000..ab1031b --- /dev/null +++ b/Overflow.Web/Components/Pages/Spills.razor @@ -0,0 +1,33 @@ +@page "/spills" +@using MongoDB.Driver +@using Overflow.SouthernWater +@rendermode RenderMode.InteractiveServer + +Southern Water Spills + +

Spills

+ +

This component demonstrates showing data.

+ + + + + + + + + + +@code { + private SouthernWaterApiJob? job; + [Inject] private IMongoDatabase database { get; set; } + private bool showIds; + + protected override async Task OnInitializedAsync() + { + job = database.GetCollection(Static.CollectionName) + .AsQueryable() + .OrderByDescending(j => j.EndTime) + .FirstOrDefault(); + } +} \ No newline at end of file diff --git a/Overflow.Web/Components/Pages/Weather.razor b/Overflow.Web/Components/Pages/Weather.razor deleted file mode 100644 index f31f2ee..0000000 --- a/Overflow.Web/Components/Pages/Weather.razor +++ /dev/null @@ -1,67 +0,0 @@ -@page "/weather" -@attribute [StreamRendering] - -Weather - -

Weather

- -

This component demonstrates showing data.

- -@if (forecasts == null) -{ -

- Loading... -

-} -else -{ - - - - - - - - - - - @foreach (var forecast in forecasts) - { - - - - - - - } - -
DateTemp. (C)Temp. (F)Summary
@forecast.Date.ToShortDateString()@forecast.TemperatureC@forecast.TemperatureF@forecast.Summary
-} - -@code { - private WeatherForecast[]? forecasts; - - protected override async Task OnInitializedAsync() - { - // Simulate asynchronous loading to demonstrate streaming rendering - await Task.Delay(500); - - var startDate = DateOnly.FromDateTime(DateTime.Now); - var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; - forecasts = Enumerable.Range(1, 5).Select(index => new WeatherForecast - { - Date = startDate.AddDays(index), - TemperatureC = Random.Shared.Next(-20, 55), - Summary = summaries[Random.Shared.Next(summaries.Length)] - }).ToArray(); - } - - private class WeatherForecast - { - public DateOnly Date { get; set; } - public int TemperatureC { get; set; } - public string? Summary { get; set; } - public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); - } - -} \ No newline at end of file diff --git a/Overflow.Web/Components/_Imports.razor b/Overflow.Web/Components/_Imports.razor index 8fddc45..170cc09 100644 --- a/Overflow.Web/Components/_Imports.razor +++ b/Overflow.Web/Components/_Imports.razor @@ -7,4 +7,8 @@ @using Microsoft.AspNetCore.Components.Web.Virtualization @using Microsoft.JSInterop @using Overflow.Web -@using Overflow.Web.Components \ No newline at end of file +@using Overflow.Web.Components +@using Overflow.Web.Client.Pages +@using Overflow.Web.Client.Components +@using Radzen +@using Radzen.Blazor \ No newline at end of file diff --git a/Overflow.Web/Overflow.Web.csproj b/Overflow.Web/Overflow.Web.csproj index 6ca2719..0facca4 100644 --- a/Overflow.Web/Overflow.Web.csproj +++ b/Overflow.Web/Overflow.Web.csproj @@ -7,11 +7,25 @@ + + + + + + + + + <_ContentIncludedByDefault Remove="wwwroot\app.css" /> + <_ContentIncludedByDefault Remove="wwwroot\bootstrap\bootstrap.min.css" /> + <_ContentIncludedByDefault Remove="wwwroot\bootstrap\bootstrap.min.css.map" /> + <_ContentIncludedByDefault Remove="wwwroot\favicon.png" /> + + diff --git a/Overflow.Web/Program.cs b/Overflow.Web/Program.cs index 8696daa..f189b9e 100644 --- a/Overflow.Web/Program.cs +++ b/Overflow.Web/Program.cs @@ -1,13 +1,18 @@ using Overflow.Web.Components; using Overflow; + using MongoDB.Driver; +using Overflow.SouthernWater; +using Quartz; +using Quartz.AspNetCore; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddRazorComponents() - .AddInteractiveServerComponents(); + .AddInteractiveServerComponents() + .AddInteractiveWebAssemblyComponents(); var driver = new MongoClient(builder.Configuration.GetConnectionString("Default")); builder.Services.AddSingleton(driver); @@ -15,6 +20,32 @@ builder.Services.AddScoped(s => s.GetRequiredService(builder.Configuration.GetSection("Quartz")); + +// if you are using persistent job store, you might want to alter some options +builder.Services.Configure(options => +{ + options.Scheduling.IgnoreDuplicates = true; // default: false + options.Scheduling.OverWriteExistingData = true; // default: true +}); + +builder.Services.AddQuartz(q => +{ + // base Quartz scheduler, job and trigger configuration +}); + +// ASP.NET Core hosting +builder.Services.AddQuartzServer(options => +{ + // when shutting down we want jobs to complete gracefully + options.WaitForJobsToComplete = false; +}); + +builder.Services.AddHttpClient(); +builder.Services.AddSingleton(); +builder.Services.AddScoped(); + var app = builder.Build(); // Configure the HTTP request pipeline. @@ -32,6 +63,7 @@ app.UseAntiforgery(); app.MapControllers(); app.MapRazorComponents() - .AddInteractiveServerRenderMode(); + .AddInteractiveServerRenderMode() + .AddInteractiveWebAssemblyRenderMode(); app.Run(); \ No newline at end of file diff --git a/Overflow.Web/appsettings.json b/Overflow.Web/appsettings.json index 10f68b8..aa4a1ae 100644 --- a/Overflow.Web/appsettings.json +++ b/Overflow.Web/appsettings.json @@ -5,5 +5,8 @@ "Microsoft.AspNetCore": "Warning" } }, + "ConnectionStrings": { + "Default": "mongodb://localhost" + }, "AllowedHosts": "*" } diff --git a/Overflow.sln b/Overflow.sln index 77dfc4e..d847d91 100644 --- a/Overflow.sln +++ b/Overflow.sln @@ -8,6 +8,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Overflow.Web", "Overflow.We EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Overflow.CLI", "Overflow.CLI\Overflow.CLI.csproj", "{393EF268-E745-4550-9607-DBE6B4C456DA}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Overflow.Web.Client", "Overflow.Web.Client\Overflow.Web.Client.csproj", "{FDA03F0A-260B-4110-B5B6-19CCFFBB1618}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -30,5 +32,9 @@ Global {393EF268-E745-4550-9607-DBE6B4C456DA}.Debug|Any CPU.Build.0 = Debug|Any CPU {393EF268-E745-4550-9607-DBE6B4C456DA}.Release|Any CPU.ActiveCfg = Release|Any CPU {393EF268-E745-4550-9607-DBE6B4C456DA}.Release|Any CPU.Build.0 = Release|Any CPU + {FDA03F0A-260B-4110-B5B6-19CCFFBB1618}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FDA03F0A-260B-4110-B5B6-19CCFFBB1618}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FDA03F0A-260B-4110-B5B6-19CCFFBB1618}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FDA03F0A-260B-4110-B5B6-19CCFFBB1618}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/Overflow/SouthernWater/SouthernWater.cs b/Overflow/SouthernWater/SouthernWaterApi.cs similarity index 98% rename from Overflow/SouthernWater/SouthernWater.cs rename to Overflow/SouthernWater/SouthernWaterApi.cs index 99453b1..e225b8c 100644 --- a/Overflow/SouthernWater/SouthernWater.cs +++ b/Overflow/SouthernWater/SouthernWaterApi.cs @@ -4,7 +4,7 @@ using System.Text.RegularExpressions; namespace Overflow.SouthernWater; -public partial class SouthernWater +public partial class SouthernWaterApi { private readonly HttpClient _client; @@ -13,7 +13,7 @@ public partial class SouthernWater private string baseUrl; private string apiKey; - public SouthernWater(HttpClient client) + public SouthernWaterApi(HttpClient client) { _client = client; } diff --git a/Overflow/SouthernWater/SouthernWaterApiJob.cs b/Overflow/SouthernWater/SouthernWaterApiJob.cs index 494a22d..9975d37 100644 --- a/Overflow/SouthernWater/SouthernWaterApiJob.cs +++ b/Overflow/SouthernWater/SouthernWaterApiJob.cs @@ -1,9 +1,11 @@ using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; namespace Overflow.SouthernWater; public class SouthernWaterApiJob { + [BsonId] public ObjectId _id { get; set; } public DateTime StartTime { get; set; } public DateTime? EndTime { get; set; } diff --git a/Overflow/SouthernWater/SouthernWaterApiJobRunner.cs b/Overflow/SouthernWater/SouthernWaterApiJobRunner.cs index 92d293e..847558e 100644 --- a/Overflow/SouthernWater/SouthernWaterApiJobRunner.cs +++ b/Overflow/SouthernWater/SouthernWaterApiJobRunner.cs @@ -4,11 +4,11 @@ using MongoDB.Driver; namespace Overflow.SouthernWater; -public class SouthernWaterApiJobRunner(SouthernWater client, ILogger logger) +public class SouthernWaterApiJobRunner(SouthernWaterApi client, ILogger logger) { protected readonly ILogger _logger = logger; - public async Task LoadSpills() + public async Task LoadSpills(int? pageLimit = null) { var interval = TimeSpan.FromSeconds(30); var job = new SouthernWaterApiJob @@ -28,7 +28,7 @@ public class SouthernWaterApiJobRunner(SouthernWater client, ILogger logger, IMongoDatabase mongo) : SouthernWaterApiJobRunner(client, logger) { - private readonly IMongoCollection _collection = mongo.GetCollection("southern_water_api_job"); + private readonly IMongoCollection _collection = mongo.GetCollection(Static.CollectionName); protected override async Task JobCreated(SouthernWaterApiJob job) { diff --git a/Overflow/SouthernWater/Spill.cs b/Overflow/SouthernWater/Spill.cs index e9efc04..a215224 100644 --- a/Overflow/SouthernWater/Spill.cs +++ b/Overflow/SouthernWater/Spill.cs @@ -6,9 +6,6 @@ namespace Overflow.SouthernWater; public class Spill { - [JsonIgnore] - public ObjectId _id { get; set; } - [JsonPropertyName("id")] public int sw_id { get; set; } public int eventId { get; set; } diff --git a/Overflow/Static.cs b/Overflow/Static.cs new file mode 100644 index 0000000..4b96244 --- /dev/null +++ b/Overflow/Static.cs @@ -0,0 +1,7 @@ +namespace Overflow; + +public static class Static +{ + public static readonly string DatabaseName = "overflow"; + public static readonly string CollectionName = "southern_water_api_job"; +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..3946969 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Overflow + +Tracking and analysing the storm overflow usage of Southern Water. + +## Tech Stack + +- .NET ASP.NET Core +- MongoDB +- Blazor Web \ No newline at end of file