Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
52424ad
feat: add Quartz.NET integration (WIP)
alnuaimicoder Apr 2, 2026
e1871a3
chore: update package metadata for all projects
alnuaimicoder Apr 2, 2026
d56351c
feat: add PublicAPI.txt files for API tracking
alnuaimicoder Apr 2, 2026
453119e
docs: update README files with CommunityToolkit naming
alnuaimicoder Apr 2, 2026
f8449ce
docs: update integration progress tracker
alnuaimicoder Apr 2, 2026
c3f1472
test: add unit tests for hosting and client integrations
alnuaimicoder Apr 2, 2026
4557d3a
Add Quartz integration with tests and package versions
alnuaimicoder Apr 2, 2026
723b407
Add package versions and start Quartz example
alnuaimicoder Apr 2, 2026
e3946f1
Fix all build errors and update documentation
alnuaimicoder Apr 2, 2026
4b80392
Add Quartz integration to main README
alnuaimicoder Apr 2, 2026
cec1a09
Add Quartz tests to CI/CD workflow
alnuaimicoder Apr 2, 2026
4be5cea
Add Quartz projects to solution file
alnuaimicoder Apr 2, 2026
dae5caf
Merge branch 'main' into feature/add-quartz-integration
alnuaimicoder May 19, 2026
c9200c4
Merge branch 'main' into feature/add-quartz-integration
alnuaimicoder May 23, 2026
04d1303
Address Copilot AI PR review feedback for Quartz integration
alnuaimicoder May 23, 2026
cd6df0b
Merge branch 'main' into feature/add-quartz-integration
aaronpowell May 25, 2026
0526062
Merge branch 'main' into feature/add-quartz-integration
alnuaimicoder May 25, 2026
1d39b45
Remove PublicAPI files and unused Npgsql.EFCore package per review fe…
alnuaimicoder May 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions CommunityToolkit.Aspire.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,12 @@
<Folder Name="/examples/powershell/">
<Project Path="examples/powershell/CommunityToolkit.Aspire.PowerShell.AppHost/CommunityToolkit.Aspire.PowerShell.AppHost.csproj" />
</Folder>
<Folder Name="/examples/quartz/">
<Project Path="examples/quartz/CommunityToolkit.Aspire.Hosting.Quartz.ApiService/CommunityToolkit.Aspire.Hosting.Quartz.ApiService.csproj" />
<Project Path="examples/quartz/CommunityToolkit.Aspire.Hosting.Quartz.AppHost/CommunityToolkit.Aspire.Hosting.Quartz.AppHost.csproj" />
<Project Path="examples/quartz/CommunityToolkit.Aspire.Hosting.Quartz.ServiceDefaults/CommunityToolkit.Aspire.Hosting.Quartz.ServiceDefaults.csproj" />
<Project Path="examples/quartz/CommunityToolkit.Aspire.Hosting.Quartz.Web/CommunityToolkit.Aspire.Hosting.Quartz.Web.csproj" />
</Folder>
<Folder Name="/examples/ravendb/">
<Project Path="examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.ApiService/CommunityToolkit.Aspire.Hosting.RavenDB.ApiService.csproj" />
<Project Path="examples/ravendb/CommunityToolkit.Aspire.Hosting.RavenDB.ServiceDefaults/CommunityToolkit.Aspire.Hosting.RavenDB.ServiceDefaults.csproj" />
Expand Down Expand Up @@ -237,6 +243,7 @@
<Project Path="src/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.PowerShell/CommunityToolkit.Aspire.Hosting.PowerShell.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Python.Extensions/CommunityToolkit.Aspire.Hosting.Python.Extensions.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Quartz/CommunityToolkit.Aspire.Hosting.Quartz.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.RavenDB/CommunityToolkit.Aspire.Hosting.RavenDB.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Redis.Extensions/CommunityToolkit.Aspire.Hosting.Redis.Extensions.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Hosting.Rust/CommunityToolkit.Aspire.Hosting.Rust.csproj" />
Expand All @@ -256,6 +263,8 @@
<Project Path="src/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Minio.Client/CommunityToolkit.Aspire.Minio.Client.csproj" />
<Project Path="src/CommunityToolkit.Aspire.OllamaSharp/CommunityToolkit.Aspire.OllamaSharp.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Quartz/CommunityToolkit.Aspire.Quartz.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Quartz.Abstractions/CommunityToolkit.Aspire.Quartz.Abstractions.csproj" />
<Project Path="src/CommunityToolkit.Aspire.RavenDB.Client/CommunityToolkit.Aspire.RavenDB.Client.csproj" />
<Project Path="src/CommunityToolkit.Aspire.Sftp/CommunityToolkit.Aspire.Sftp.csproj" />
<Project Path="src/CommunityToolkit.Aspire.SurrealDb/CommunityToolkit.Aspire.SurrealDb.csproj" />
Expand Down Expand Up @@ -301,6 +310,7 @@
<Project Path="tests/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.Tests/CommunityToolkit.Aspire.Hosting.PostgreSQL.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.PowerShell.Tests/CommunityToolkit.Aspire.Hosting.PowerShell.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Perl.Tests/CommunityToolkit.Aspire.Hosting.Perl.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Quartz.Tests/CommunityToolkit.Aspire.Hosting.Quartz.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.RavenDB.Tests/CommunityToolkit.Aspire.Hosting.RavenDB.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Redis.Extensions.Tests/CommunityToolkit.Aspire.Hosting.Redis.Extensions.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Hosting.Rust.Tests/CommunityToolkit.Aspire.Hosting.Rust.Tests.csproj" />
Expand All @@ -320,6 +330,7 @@
<Project Path="tests/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite.Tests/CommunityToolkit.Aspire.Microsoft.EntityFrameworkCore.Sqlite.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Minio.Client.Tests/CommunityToolkit.Aspire.Minio.Client.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.OllamaSharp.Tests/CommunityToolkit.Aspire.OllamaSharp.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Quartz.Tests/CommunityToolkit.Aspire.Quartz.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.RavenDB.Client.Tests/CommunityToolkit.Aspire.RavenDB.Client.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.Sftp.Tests/CommunityToolkit.Aspire.Sftp.Tests.csproj" />
<Project Path="tests/CommunityToolkit.Aspire.SurrealDb.Tests/CommunityToolkit.Aspire.SurrealDb.Tests.csproj" />
Expand Down
6 changes: 6 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
<PackageVersion Include="Dapr.Client" Version="1.15.3" />
<PackageVersion Include="Microsoft.AspNetCore.Components.QuickGrid" Version="$(AspNetCoreVersion)" />
<PackageVersion Include="Microsoft.AspNetCore.OpenApi" Version="8.0.20" />
<PackageVersion Include="Microsoft.AspNetCore.SignalR.Client" Version="10.0.5" />
<PackageVersion Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down Expand Up @@ -77,14 +78,19 @@
<PackageVersion Include="MeiliSearch" Version="0.18.0" />
<PackageVersion Include="DuckDB.NET.Data.Full" Version="1.5.0" />
<PackageVersion Include="Microsoft.Data.Sqlite" Version="10.0.2" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.1.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.HealthChecks.EntityFrameworkCore" Version="$(DotNetExtensionsVersion)" />
<PackageVersion Include="Microsoft.SqlServer.DacFx" Version="170.2.70" />
<PackageVersion Include="Microsoft.Build" Version="17.11.48" />
<PackageVersion Include="Microsoft.Build.Framework" Version="17.11.48" />
<PackageVersion Include="Microsoft.NET.StringTools" Version="17.11.48" />
<PackageVersion Include="Microsoft.Build.Locator" Version="1.10.12" />
<PackageVersion Include="Npgsql" Version="10.0.2" />
<PackageVersion Include="ErikEJ.Dacpac.Chinook" Version="1.0.0" />
<PackageVersion Include="Quartz" Version="3.16.1" />
<PackageVersion Include="Quartz.Extensions.Hosting" Version="3.16.1" />
<PackageVersion Include="Quartz.Serialization.Json" Version="3.16.1" />
<PackageVersion Include="RavenDB.Client" Version="6.2.1" />
<PackageVersion Include="RavenDB.TestDriver" Version="6.2.1" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ This repository contains the source code for the Aspire Community Toolkit, a col
| - **Learn More**: [`Hosting.Bun`][bun-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Bun][bun-shields]][bun-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Bun][bun-shields-preview]][bun-nuget-preview] | A hosting integration for the Bun apps. |
| - **Learn More**: [`Hosting.Perl`][perl-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Perl][perl-shields]][perl-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Perl][perl-shields-preview]][perl-nuget-preview] | A hosting integration for Perl scripts and APIs. |
| - **Learn More**: [`Hosting.Python.Extensions`][python-ext-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Python.Extensions][python-ext-shields]][python-ext-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Python.Extensions][python-ext-shields-preview]][python-ext-nuget-preview] | An integration that contains some additional extensions for running python applications |
| - **Learn More**: [`Hosting.Quartz`][quartz-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Quartz][quartz-hosting-shields]][quartz-hosting-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Quartz][quartz-hosting-shields-preview]][quartz-hosting-nuget-preview] | An Aspire hosting integration for Quartz.NET background job scheduling with persistent storage and automatic migrations. |
| - **Learn More**: [`Quartz`][quartz-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Quartz][quartz-client-shields]][quartz-client-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Quartz][quartz-client-shields-preview]][quartz-client-nuget-preview] | An Aspire client integration for Quartz.NET job scheduling with idempotency and OpenTelemetry support. |
| - **Learn More**: [`Quartz.Abstractions`][quartz-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Quartz.Abstractions][quartz-abstractions-shields]][quartz-abstractions-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Quartz.Abstractions][quartz-abstractions-shields-preview]][quartz-abstractions-nuget-preview] | Core abstractions and interfaces for Quartz.NET integration. |
| - **Learn More**: [`Hosting.KurrentDB`][kurrentdb-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.KurrentDB][kurrentdb-shields]][kurrentdb-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.KurrentDB][kurrentdb-shields-preview]][kurrentdb-nuget-preview] | An Aspire hosting integration leveraging the [KurrentDB](https://www.kurrent.io) container. |
| - **Learn More**: [`KurrentDB`][kurrentdb-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.KurrentDB][kurrentdb-client-shields]][kurrentdb-client-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.KurrentDB][kurrentdb-client-shields-preview]][kurrentdb-client-nuget-preview] | An Aspire client integration for the [KurrentDB](https://github.com/kurrent-io/KurrentDB-Client-Dotnet) package. |
| - **Learn More**: [`Hosting.Flagd`][flagd-integration-docs] <br /> - Stable 📦: [![CommunityToolkit.Aspire.Hosting.Flagd][flagd-shields]][flagd-nuget] <br /> - Preview 📦: [![CommunityToolkit.Aspire.Hosting.Flagd][flagd-shields-preview]][flagd-nuget-preview] | An Aspire hosting integration for [flagd](https://flagd.dev), a feature flag evaluation engine. |
Expand Down Expand Up @@ -298,3 +301,16 @@ This project is supported by the [.NET Foundation](https://dotnetfoundation.org)
[azure-ext-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Azure.Extensions/
[azure-ext-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Azure.Extensions?label=nuget%20(preview)
[azure-ext-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Azure.Extensions/absoluteLatest
[quartz-integration-docs]: https://learn.microsoft.com/dotnet/aspire/community-toolkit/hosting-quartz
[quartz-hosting-shields]: https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Hosting.Quartz
[quartz-hosting-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Quartz/
[quartz-hosting-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Hosting.Quartz?label=nuget%20(preview)
[quartz-hosting-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Hosting.Quartz/absoluteLatest
[quartz-client-shields]: https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Quartz
[quartz-client-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Quartz/
[quartz-client-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Quartz?label=nuget%20(preview)
[quartz-client-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Quartz/absoluteLatest
[quartz-abstractions-shields]: https://img.shields.io/nuget/v/CommunityToolkit.Aspire.Quartz.Abstractions
[quartz-abstractions-nuget]: https://nuget.org/packages/CommunityToolkit.Aspire.Quartz.Abstractions/
[quartz-abstractions-shields-preview]: https://img.shields.io/nuget/vpre/CommunityToolkit.Aspire.Quartz.Abstractions?label=nuget%20(preview)
[quartz-abstractions-nuget-preview]: https://nuget.org/packages/CommunityToolkit.Aspire.Quartz.Abstractions/absoluteLatest
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\src\CommunityToolkit.Aspire.Quartz\CommunityToolkit.Aspire.Quartz.csproj" />
<ProjectReference Include="..\CommunityToolkit.Aspire.Hosting.Quartz.ServiceDefaults\CommunityToolkit.Aspire.Hosting.Quartz.ServiceDefaults.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using Quartz;

namespace CommunityToolkit.Aspire.Hosting.Quartz.ApiService.Extensions;

public static class QuartzExtensions
{
/// <summary>
/// Add a job and trigger from configuration
/// </summary>
public static void AddJobAndTrigger<T>(
this IServiceCollectionQuartzConfigurator quartz,
IConfiguration config)
where T : IJob
{
string jobName = typeof(T).Name;
var configKey = $"Quartz:{jobName}";
var cronSchedule = config[configKey];

if (string.IsNullOrEmpty(cronSchedule))
{
throw new Exception($"No Quartz.NET Cron schedule found for job in configuration at {configKey}");
}

var jobKey = new JobKey(jobName);

quartz.AddJob<T>(opts => opts
.WithIdentity(jobKey)
.StoreDurably());

quartz.AddTrigger(opts => opts
.ForJob(jobKey)
.WithIdentity($"{jobName}-trigger")
.WithCronSchedule(cronSchedule));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
using Microsoft.AspNetCore.SignalR;

namespace CommunityToolkit.Aspire.Hosting.Quartz.ApiService.Hubs;

/// <summary>
/// SignalR Hub for real-time Quartz job updates
/// </summary>
public class QuartzHub : Hub
{
private readonly ILogger<QuartzHub> _logger;

public QuartzHub(ILogger<QuartzHub> logger)
{
_logger = logger;
}

public override async Task OnConnectedAsync()
{
_logger.LogInformation("Client connected: {ConnectionId}", Context.ConnectionId);
await base.OnConnectedAsync();
}

public override async Task OnDisconnectedAsync(Exception? exception)
{
_logger.LogInformation("Client disconnected: {ConnectionId}", Context.ConnectionId);
await base.OnDisconnectedAsync(exception);
}

/// <summary>
/// Broadcast job scheduled event to all clients
/// </summary>
public async Task NotifyJobScheduled(string jobId, string jobType, string message)
{
await Clients.All.SendAsync("JobScheduled", new
{
jobId,
jobType,
message,
timestamp = DateTime.UtcNow
});
}

/// <summary>
/// Broadcast job started event to all clients
/// </summary>
public async Task NotifyJobStarted(string jobId, string jobType)
{
await Clients.All.SendAsync("JobStarted", new
{
jobId,
jobType,
timestamp = DateTime.UtcNow
});
}

/// <summary>
/// Broadcast job completed event to all clients
/// </summary>
public async Task NotifyJobCompleted(string jobId, string jobType, bool success, string? error = null)
{
await Clients.All.SendAsync("JobCompleted", new
{
jobId,
jobType,
success,
error,
timestamp = DateTime.UtcNow
});
}

/// <summary>
/// Broadcast job cancelled event to all clients
/// </summary>
public async Task NotifyJobCancelled(string jobId, string jobGroup)
{
await Clients.All.SendAsync("JobCancelled", new
{
jobId,
jobGroup,
timestamp = DateTime.UtcNow
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using Quartz;

namespace CommunityToolkit.Aspire.Hosting.Quartz.ApiService.Jobs;

/// <summary>
/// API health monitor job - demonstrates HTTP calls and retry logic
/// </summary>
public class ApiHealthMonitorJob : IJob
{
private readonly ILogger<ApiHealthMonitorJob> _logger;
private readonly IHttpClientFactory _httpClientFactory;

public const string Name = nameof(ApiHealthMonitorJob);
public const string Group = "monitoring";

public ApiHealthMonitorJob(
ILogger<ApiHealthMonitorJob> logger,
IHttpClientFactory httpClientFactory)
{
_logger = logger;
_httpClientFactory = httpClientFactory;
}

public async Task Execute(IJobExecutionContext context)
{
var data = context.MergedJobDataMap;
var apiUrl = data.GetString("apiUrl") ?? "https://api.example.com/health";
var timeout = data.GetInt("timeoutSeconds");
if (timeout == 0) timeout = 30;

_logger.LogInformation("🔍 Checking health of API: {ApiUrl}", apiUrl);

try
{
var client = _httpClientFactory.CreateClient();
client.Timeout = TimeSpan.FromSeconds(timeout);

var startTime = DateTime.UtcNow;
var response = await client.GetAsync(apiUrl, context.CancellationToken);
var duration = (DateTime.UtcNow - startTime).TotalMilliseconds;

if (response.IsSuccessStatusCode)
{
_logger.LogInformation(
"✅ API is healthy - Status: {StatusCode}, Response time: {Duration}ms",
(int)response.StatusCode, duration);
}
else
{
_logger.LogWarning(
"⚠️ API returned error - Status: {StatusCode}",
(int)response.StatusCode);
}
}
catch (Exception ex)
{
_logger.LogError(ex, "❌ API health check failed for {ApiUrl}", apiUrl);
throw; // Re-throw to trigger retry if configured
}
}
}
Loading