Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
70 changes: 36 additions & 34 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,53 @@
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.14.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.2" />
<PackageVersion Include="BenchmarkDotNet" Version="0.15.2" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="coverlet.msbuild" Version="6.0.4" />
<PackageVersion Include="GitHubActionsTestLogger" Version="2.4.1" />
<PackageVersion Include="Google.Api.CommonProtos" Version="2.2.0" />
<PackageVersion Include="Google.Protobuf" Version="3.28.2" />
<PackageVersion Include="Grpc.AspNetCore" Version="2.66.0" />
<PackageVersion Include="Google.Api.CommonProtos" Version="2.17.0" />
<PackageVersion Include="Google.Protobuf" Version="3.31.1" />
<PackageVersion Include="Grpc.AspNetCore" Version="2.71.0" />
<PackageVersion Include="Grpc.Core.Testing" Version="2.46.6" />
<PackageVersion Include="Grpc.Net.Client" Version="2.66.0" />
<PackageVersion Include="Grpc.Net.ClientFactory" Version="2.66.0" />
<PackageVersion Include="Grpc.Tools" Version="2.67.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="6.0.35" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="6.0.35" />
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.13.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.13.0" />
<PackageVersion Include="Grpc.Net.Client" Version="2.71.0" />
<PackageVersion Include="Grpc.Net.ClientFactory" Version="2.71.0" />
<PackageVersion Include="Grpc.Tools" Version="2.72.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.17" Condition="'$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net8'" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.6" Condition="'$(TargetFramework)' == 'net9.0' Or '$(TargetFramework)' == 'net9'" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="8.0.17" Condition="'$(TargetFramework)' == 'net8.0' Or '$(TargetFramework)' == 'net8'"/>
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="9.0.6" Condition="'$(TargetFramework)' == 'net9.0' Or '$(TargetFramework)' == 'net9'"/>
<PackageVersion Include="Microsoft.CodeAnalysis.Analyzers" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Common" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing" Version="1.1.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing" Version="1.1.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.SourceGenerators.Testing.XUnit" Version="1.1.2" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.13.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.13.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.14.0" />
<PackageVersion Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.14.0" />
<PackageVersion Include="Microsoft.DurableTask.Client.Grpc" Version="1.5.0" />
<PackageVersion Include="Microsoft.DurableTask.Worker.Grpc" Version="1.5.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.4" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="6.0.1" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="6.0.0" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
<PackageVersion Include="MinVer" Version="2.3.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="9.0.6" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.6" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="MinVer" Version="6.0.0" />
<PackageVersion Include="Moq" Version="4.20.72" />
<PackageVersion Include="protobuf-net.Grpc.AspNetCore" Version="1.2.2" />
<PackageVersion Include="Serilog.AspNetCore" Version="6.1.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="4.1.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="5.0.0" />
<PackageVersion Include="Shouldly" Version="4.2.1" />
<PackageVersion Include="System.Formats.Asn1" Version="6.0.1" />
<PackageVersion Include="System.Text.Json" Version="6.0.10" />
<PackageVersion Include="Serilog.AspNetCore" Version="9.0.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="Shouldly" Version="4.3.0" />
<PackageVersion Include="System.Formats.Asn1" Version="9.0.6" />
<PackageVersion Include="System.Text.Json" Version="9.0.6" />
<PackageVersion Include="xunit" Version="2.9.3" />
<PackageVersion Include="xunit.extensibility.core" Version="2.9.3" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.0.2" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.1" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ private async Task LoadAsync()
var subscribeConfigurationResponse = await daprClient.SubscribeConfiguration(store, keys, metadata, cts.Token);
await foreach (var items in subscribeConfigurationResponse.Source.WithCancellation(cts.Token))
{
var data = new Dictionary<string, string>(Data, StringComparer.OrdinalIgnoreCase);
var data = new Dictionary<string, string?>(Data, StringComparer.OrdinalIgnoreCase);
foreach (var item in items)
{
id = subscribeConfigurationResponse.Id;
Expand Down Expand Up @@ -121,4 +121,4 @@ private async Task LoadAsync()
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ private string NormalizeKey(string key)

private async Task LoadAsync()
{
var data = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
var data = new Dictionary<string, string?>(StringComparer.InvariantCultureIgnoreCase);

// Wait for the Dapr Sidecar to report healthy before attempting to fetch secrets.
using (var tokenSource = new CancellationTokenSource(sidecarWaitTimeout))
Expand Down Expand Up @@ -259,4 +259,4 @@ private async Task LoadAsync()
Data = data;
}
}
}
}
5 changes: 4 additions & 1 deletion src/Dapr.Jobs/DaprJobsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,15 @@ public abstract class DaprJobsClient(Autogenerated.DaprClient client, HttpClient
/// <param name="startingFrom">The optional point-in-time from which the job schedule should start.</param>
/// <param name="repeats">The optional number of times the job should be triggered.</param>
/// <param name="ttl">Represents when the job should expire. If both this and DueTime are set, TTL needs to represent a later point in time.</param>
/// <param name="overwrite">A flag indicating whether the job should be overwritten when submitted (true); otherwise false to require
/// to require that an existing job with the same name be deleted first.</param>
/// <param name="failurePolicyOptions">The characteristics of the policy to apply when a job fails to trigger.</param>
/// <param name="cancellationToken">Cancellation token.</param>
[Obsolete(
"The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public abstract Task ScheduleJobAsync(string jobName, DaprJobSchedule schedule,
ReadOnlyMemory<byte>? payload = null, DateTimeOffset? startingFrom = null, int? repeats = null,
DateTimeOffset? ttl = null,
DateTimeOffset? ttl = null, bool overwrite = false, IJobFailurePolicyOptions? failurePolicyOptions = null,
CancellationToken cancellationToken = default);

/// <summary>
Expand Down
43 changes: 41 additions & 2 deletions src/Dapr.Jobs/DaprJobsGrpcClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,17 +35,19 @@ internal sealed class DaprJobsGrpcClient(Autogenerated.Dapr.DaprClient client, H
/// <param name="startingFrom">The optional point-in-time from which the job schedule should start.</param>
/// <param name="repeats">The optional number of times the job should be triggered.</param>
/// <param name="ttl">Represents when the job should expire. If both this and DueTime are set, TTL needs to represent a later point in time.</param>
/// <param name="overwrite">A flag indicating whether the job should be overwritten when submitted (true); otherwise false to require that an existing job with the same name be deleted first.</param>
/// <param name="failurePolicyOptions">The characteristics of the policy to apply when a job fails to trigger.</param>
/// <param name="cancellationToken">Cancellation token.</param>
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public override async Task ScheduleJobAsync(string jobName, DaprJobSchedule schedule,
ReadOnlyMemory<byte>? payload = null, DateTimeOffset? startingFrom = null, int? repeats = null,
DateTimeOffset? ttl = null,
DateTimeOffset? ttl = null, bool overwrite = false, IJobFailurePolicyOptions? failurePolicyOptions = null,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(jobName, nameof(jobName));
ArgumentNullException.ThrowIfNull(schedule, nameof(schedule));

var job = new Autogenerated.Job { Name = jobName };
var job = new Autogenerated.Job { Name = jobName, Overwrite = overwrite };

//Set up the schedule (recurring or point in time)
if (schedule.IsPointInTimeExpression)
Expand Down Expand Up @@ -88,6 +90,43 @@ public override async Task ScheduleJobAsync(string jobName, DaprJobSchedule sche
job.Ttl = ((DateTimeOffset)ttl).ToString("O");
}

if (failurePolicyOptions is not null)
{
switch (failurePolicyOptions.Policy)
{
case JobFailurePolicy.Drop:
job.FailurePolicy = new Autogenerated.JobFailurePolicy
{
Drop = new Autogenerated.JobFailurePolicyDrop()
};
break;
case JobFailurePolicy.Constant:
var constantOptions = (JobFailurePolicyConstantOptions)failurePolicyOptions;
if (constantOptions.MaxRetries is null)
{
job.FailurePolicy = new Autogenerated.JobFailurePolicy
{
Constant = new Autogenerated.JobFailurePolicyConstant
{
Interval = constantOptions.Interval.ToDuration()
}
};
}
else
{
job.FailurePolicy = new Autogenerated.JobFailurePolicy
{
Constant = new Autogenerated.JobFailurePolicyConstant
{
Interval = constantOptions.Interval.ToDuration(),
MaxRetries = (uint)constantOptions.MaxRetries
}
};
}
break;
}
}

var envelope = new Autogenerated.ScheduleJobRequest { Job = job };

var grpcCallOptions = DaprClientUtilities.ConfigureGrpcCallOptions(typeof(DaprJobsClient).Assembly, this.DaprApiToken, cancellationToken);
Expand Down
15 changes: 12 additions & 3 deletions src/Dapr.Jobs/Extensions/DaprSerializationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,14 @@ public static class DaprJobsSerializationExtensions
/// <param name="repeats">The optional number of times the job should be triggered.</param>
/// <param name="jsonSerializerOptions">Optional JSON serialization options.</param>
/// <param name="ttl">Represents when the job should expire. If both this and DueTime are set, TTL needs to represent a later point in time.</param>
/// <param name="overwrite">A flag indicating whether the job should be overwritten when submitted (true); otherwise false to require that an existing job with the same name be deleted first.</param>
/// <param name="failurePolicyOptions">The characteristics of the policy to apply when a job fails to trigger.</param>
/// <param name="cancellationToken">Cancellation token.</param>
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public static async Task ScheduleJobWithPayloadAsync(this DaprJobsClient client, string jobName, DaprJobSchedule schedule,
object payload, DateTime? startingFrom = null, int? repeats = null, JsonSerializerOptions? jsonSerializerOptions = null, DateTimeOffset? ttl = null,
object payload, DateTime? startingFrom = null, int? repeats = null,
JsonSerializerOptions? jsonSerializerOptions = null, DateTimeOffset? ttl = null,
bool overwrite = false, IJobFailurePolicyOptions? failurePolicyOptions = null,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(payload, nameof(payload));
Expand All @@ -50,7 +54,8 @@ public static async Task ScheduleJobWithPayloadAsync(this DaprJobsClient client,
var payloadBytes =
JsonSerializer.SerializeToUtf8Bytes(payload, serializerOptions);

await client.ScheduleJobAsync(jobName, schedule, payloadBytes, startingFrom, repeats, ttl, cancellationToken);
await client.ScheduleJobAsync(jobName, schedule, payloadBytes, startingFrom, repeats, ttl, overwrite,
failurePolicyOptions, cancellationToken);
}

/// <summary>
Expand All @@ -63,16 +68,20 @@ public static async Task ScheduleJobWithPayloadAsync(this DaprJobsClient client,
/// <param name="startingFrom">The optional point-in-time from which the job schedule should start.</param>
/// <param name="repeats">The optional number of times the job should be triggered.</param>
/// <param name="ttl">Represents when the job should expire. If both this and DueTime are set, TTL needs to represent a later point in time.</param>
/// <param name="overwrite">A flag indicating whether the job should be overwritten when submitted (true); otherwise false to require that an existing job with the same name be deleted first.</param>
/// <param name="failurePolicyOptions">The characteristics of the policy to apply when a job fails to trigger.</param>
/// <param name="cancellationToken">Cancellation token.</param>
[Obsolete("The API is currently not stable as it is in the Alpha stage. This attribute will be removed once it is stable.")]
public static async Task ScheduleJobWithPayloadAsync(this DaprJobsClient client, string jobName, DaprJobSchedule schedule,
string payload, DateTime? startingFrom = null, int? repeats = null, DateTimeOffset? ttl = null,
bool overwrite = false, IJobFailurePolicyOptions? failurePolicyOptions = null,
CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(payload, nameof(payload));

var payloadBytes = Encoding.UTF8.GetBytes(payload);

await client.ScheduleJobAsync(jobName, schedule, payloadBytes, startingFrom, repeats, ttl, cancellationToken);
await client.ScheduleJobAsync(jobName, schedule, payloadBytes, startingFrom, repeats, ttl, overwrite,
failurePolicyOptions, cancellationToken);
}
}
8 changes: 1 addition & 7 deletions src/Dapr.Jobs/Models/DaprJobSchedule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,7 @@ public static DaprJobSchedule FromCronExpression(CronExpressionBuilder builder)
/// Specifies a schedule using a Cron-like expression or '@' prefixed period strings.
/// </summary>
/// <param name="expression">The systemd Cron-like expression indicating when the job should be triggered.</param>
public static DaprJobSchedule FromExpression(string expression)
{
#if NET6_0
ArgumentNullException.ThrowIfNull(expression, nameof(expression));
#endif
return new DaprJobSchedule(expression);
}
public static DaprJobSchedule FromExpression(string expression) => new(expression);

/// <summary>
/// Specifies a schedule using a duration interval articulated via a <see cref="TimeSpan"/>.
Expand Down
25 changes: 25 additions & 0 deletions src/Dapr.Jobs/Models/IJobFailurePolicyOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// ------------------------------------------------------------------------
// Copyright 2025 The Dapr Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ------------------------------------------------------------------------

namespace Dapr.Jobs.Models;

/// <summary>
/// Interface for specifying job failure policy options.
/// </summary>
public interface IJobFailurePolicyOptions
{
/// <summary>
/// The type of policy to apply.
/// </summary>
public JobFailurePolicy Policy { get; }
}
Loading