From 1c94754a94bbd37ec8452ec7e4e9e266eb1b9f5a Mon Sep 17 00:00:00 2001 From: "naiyuantian@microsoft.com" Date: Tue, 14 Jul 2026 09:45:46 -0700 Subject: [PATCH 1/2] initial commit --- .../CustomExceptionProperties.csproj | 19 +++ .../CustomExceptionProperties/Functions.cs | 140 ++++++++++++++++++ .../CustomExceptionProperties/Program.cs | 18 +++ .../CustomExceptionProperties/README.md | 74 +++++++++ .../CustomExceptionProperties/host.json | 20 +++ .../CustomExceptionProperties/test.http | 22 +++ .../CustomExceptionProperties/README.md | 78 ++++++++++ .../CustomExceptionProperties/host.json | 21 +++ .../CustomExceptionProperties/package.json | 13 ++ .../functions/customExceptionProperties.js | 102 +++++++++++++ 10 files changed, 507 insertions(+) create mode 100644 samples/durable-functions/dotnet/CustomExceptionProperties/CustomExceptionProperties.csproj create mode 100644 samples/durable-functions/dotnet/CustomExceptionProperties/Functions.cs create mode 100644 samples/durable-functions/dotnet/CustomExceptionProperties/Program.cs create mode 100644 samples/durable-functions/dotnet/CustomExceptionProperties/README.md create mode 100644 samples/durable-functions/dotnet/CustomExceptionProperties/host.json create mode 100644 samples/durable-functions/dotnet/CustomExceptionProperties/test.http create mode 100644 samples/durable-functions/javascript/CustomExceptionProperties/README.md create mode 100644 samples/durable-functions/javascript/CustomExceptionProperties/host.json create mode 100644 samples/durable-functions/javascript/CustomExceptionProperties/package.json create mode 100644 samples/durable-functions/javascript/CustomExceptionProperties/src/functions/customExceptionProperties.js diff --git a/samples/durable-functions/dotnet/CustomExceptionProperties/CustomExceptionProperties.csproj b/samples/durable-functions/dotnet/CustomExceptionProperties/CustomExceptionProperties.csproj new file mode 100644 index 00000000..3e674598 --- /dev/null +++ b/samples/durable-functions/dotnet/CustomExceptionProperties/CustomExceptionProperties.csproj @@ -0,0 +1,19 @@ + + + net8.0 + v4 + Exe + enable + enable + CustomExceptionProperties + + + + + + + + + + + diff --git a/samples/durable-functions/dotnet/CustomExceptionProperties/Functions.cs b/samples/durable-functions/dotnet/CustomExceptionProperties/Functions.cs new file mode 100644 index 00000000..266f557b --- /dev/null +++ b/samples/durable-functions/dotnet/CustomExceptionProperties/Functions.cs @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; +using Microsoft.DurableTask; +using Microsoft.DurableTask.Client; +using Microsoft.DurableTask.Worker; + +namespace CustomExceptionProperties; + +// ============================================================================= +// Orchestration + activity +// ============================================================================= + +/// +/// Demonstrates custom FailureDetails properties. An activity throws a +/// ; the registered +/// attaches its structured fields to +/// the failure. The orchestration catches the propagated +/// and returns its FailureDetails, +/// including the custom Properties. +/// +public static class CustomExceptionOrchestration +{ + [Function(nameof(OrchestrationWithCustomException))] + public static async Task OrchestrationWithCustomException( + [OrchestrationTrigger] TaskOrchestrationContext context) + { + try + { +#pragma warning disable DURABLE2001 // Activity has no input + await context.CallActivityAsync(nameof(BusinessActivity)); +#pragma warning restore DURABLE2001 + } + catch (TaskFailedException ex) + { + // FailureDetails.Properties contains the values supplied by the provider. + return ex.FailureDetails; + } + + // Should never reach here — the activity always throws. + return null; + } + + [Function(nameof(BusinessActivity))] + public static void BusinessActivity([ActivityTrigger] TaskActivityContext context) + { + throw new BusinessValidationException( + message: "Business logic validation failed", + stringProperty: "validation-error-123", + intProperty: 100, + longProperty: 999999999L, + dateTimeProperty: new DateTime(2025, 10, 15, 14, 30, 0, DateTimeKind.Utc), + dictionaryProperty: new Dictionary + { + ["error_code"] = "VALIDATION_FAILED", + ["retry_count"] = 3, + ["is_critical"] = true, + }, + listProperty: new List { "error1", "error2", 500, null }, + nullProperty: null); + } + + [Function(nameof(OrchestrationWithCustomException) + "_Start")] + public static async Task Start( + [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "orchestrators/customException")] HttpRequestData req, + [DurableClient] DurableTaskClient client) + { + string instanceId = await client.ScheduleNewOrchestrationInstanceAsync(nameof(OrchestrationWithCustomException)); + return client.CreateCheckStatusResponse(req, instanceId); + } +} + +// ============================================================================= +// Custom exception + provider +// ============================================================================= + +/// +/// Business exception carrying structured properties that should be surfaced on +/// FailureDetails.Properties. +/// +[Serializable] +public class BusinessValidationException : Exception +{ + public BusinessValidationException( + string message, + string stringProperty, + int intProperty, + long longProperty, + DateTime dateTimeProperty, + IDictionary dictionaryProperty, + IList listProperty, + object? nullProperty) + : base(message) + { + this.StringProperty = stringProperty; + this.IntProperty = intProperty; + this.LongProperty = longProperty; + this.DateTimeProperty = dateTimeProperty; + this.DictionaryProperty = dictionaryProperty; + this.ListProperty = listProperty; + this.NullProperty = nullProperty; + } + + public BusinessValidationException(string message) : base(message) { } + + public string? StringProperty { get; } + public int? IntProperty { get; } + public long? LongProperty { get; } + public DateTime? DateTimeProperty { get; } + public IDictionary? DictionaryProperty { get; } + public IList? ListProperty { get; } + public object? NullProperty { get; } +} + +/// +/// Maps thrown exceptions to the custom properties attached to FailureDetails. +/// Returning null opts out of adding properties for a given exception. +/// +public class BusinessExceptionPropertiesProvider : IExceptionPropertiesProvider +{ + public IDictionary? GetExceptionProperties(Exception exception) + { + return exception switch + { + BusinessValidationException e => new Dictionary + { + ["StringProperty"] = e.StringProperty, + ["IntProperty"] = e.IntProperty, + ["LongProperty"] = e.LongProperty, + ["DateTimeProperty"] = e.DateTimeProperty, + ["DictionaryProperty"] = e.DictionaryProperty, + ["ListProperty"] = e.ListProperty, + ["NullProperty"] = e.NullProperty, + }, + _ => null, + }; + } +} diff --git a/samples/durable-functions/dotnet/CustomExceptionProperties/Program.cs b/samples/durable-functions/dotnet/CustomExceptionProperties/Program.cs new file mode 100644 index 00000000..a0935909 --- /dev/null +++ b/samples/durable-functions/dotnet/CustomExceptionProperties/Program.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +using CustomExceptionProperties; +using Microsoft.Azure.Functions.Worker.Builder; +using Microsoft.DurableTask.Worker; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +FunctionsApplicationBuilder builder = FunctionsApplication.CreateBuilder(args); + +// Register the custom exception properties provider. When an activity throws, +// the Durable worker consults this provider to extract structured properties +// from the exception and attaches them to FailureDetails.Properties, which is +// then surfaced to the orchestration that observes the failure. +builder.Services.AddSingleton(); + +builder.Build().Run(); diff --git a/samples/durable-functions/dotnet/CustomExceptionProperties/README.md b/samples/durable-functions/dotnet/CustomExceptionProperties/README.md new file mode 100644 index 00000000..155bb3a2 --- /dev/null +++ b/samples/durable-functions/dotnet/CustomExceptionProperties/README.md @@ -0,0 +1,74 @@ +# Custom Exception Properties with Durable Functions (.NET) + +.NET | Durable Functions + +## Description + +Demonstrates attaching **custom properties to `FailureDetails`** when an activity throws, using the Durable Task Scheduler (DTS) backend. + +When an activity throws a `BusinessValidationException`, the registered `IExceptionPropertiesProvider` extracts structured fields (string, int, long, date-time, dictionary, list, null) from the exception. The Durable worker propagates those values onto `FailureDetails.Properties`, which the orchestration observes when it catches the failure — making rich, typed error context available to the orchestrator instead of just a message and stack trace. + +## Prerequisites + +1. [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) +2. [Docker](https://www.docker.com/products/docker-desktop/) (for running the emulator) +3. [Azure Functions Core Tools v4](https://learn.microsoft.com/azure/azure-functions/functions-run-local) + +> **Note:** The `IExceptionPropertiesProvider` API requires `Microsoft.DurableTask.Worker` >= 1.24.2 and the Durable Functions host extension >= 3.14.0 (via `Microsoft.Azure.Functions.Worker.Extensions.DurableTask` >= 1.18.0). The DTS backend requires `Microsoft.Azure.Functions.Worker.Extensions.DurableTask.AzureManaged` >= 1.9.0. These versions are pinned in the `.csproj`. + +## Quick Run + +1. Start the Durable Task Scheduler emulator: + ```bash + docker run -d -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest + ``` + +2. Start the Function app: + ```bash + cd samples/durable-functions/dotnet/CustomExceptionProperties + func start + ``` + +3. Start the orchestration: + ```bash + curl -X POST http://localhost:7071/api/orchestrators/customException + ``` + +4. Follow the `statusQueryGetUri` from the response to see the completed output. + +5. View in the dashboard: http://localhost:8082 + +## Expected Output + +The orchestration returns the caught `FailureDetails`, whose `Properties` carry the custom values supplied by the provider: + +```json +{ + "errorType": "CustomExceptionProperties.BusinessValidationException", + "errorMessage": "Business logic validation failed", + "properties": { + "StringProperty": "validation-error-123", + "IntProperty": 100, + "LongProperty": 999999999, + "DateTimeProperty": "2025-10-15T14:30:00Z", + "DictionaryProperty": { + "error_code": "VALIDATION_FAILED", + "retry_count": 3, + "is_critical": true + }, + "ListProperty": ["error1", "error2", 500, null], + "NullProperty": null + } +} +``` + +## How It Works + +- `BusinessExceptionPropertiesProvider` implements `Microsoft.DurableTask.Worker.IExceptionPropertiesProvider` and is registered as a singleton in `Program.cs`. +- `BusinessActivity` throws a `BusinessValidationException` carrying the structured fields. +- `OrchestrationWithCustomException` calls the activity, catches the propagated `TaskFailedException`, and returns `ex.FailureDetails` — including the custom `Properties`. + +## Learn More + +- [Durable Functions error handling](https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-error-handling) +- [Durable Task Scheduler Documentation](https://aka.ms/dts-documentation) diff --git a/samples/durable-functions/dotnet/CustomExceptionProperties/host.json b/samples/durable-functions/dotnet/CustomExceptionProperties/host.json new file mode 100644 index 00000000..50a73621 --- /dev/null +++ b/samples/durable-functions/dotnet/CustomExceptionProperties/host.json @@ -0,0 +1,20 @@ +{ + "version": "2.0", + "logging": { + "logLevel": { + "default": "Information", + "DurableTask.Core": "Warning", + "Microsoft.DurableTask": "Information", + "Host.Triggers.DurableTask": "Information" + } + }, + "extensions": { + "durableTask": { + "hubName": "default", + "storageProvider": { + "type": "azureManaged", + "connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING" + } + } + } +} diff --git a/samples/durable-functions/dotnet/CustomExceptionProperties/test.http b/samples/durable-functions/dotnet/CustomExceptionProperties/test.http new file mode 100644 index 00000000..3381d16a --- /dev/null +++ b/samples/durable-functions/dotnet/CustomExceptionProperties/test.http @@ -0,0 +1,22 @@ +# ============================================================================= +# Custom Exception Properties — Durable Functions (.NET isolated) Demo +# ============================================================================= +# +# Prerequisites: +# 1. DTS emulator: docker run -d -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest +# 2. App running: func start --port 7071 +# +# The orchestration calls an activity that throws BusinessValidationException. +# A registered IExceptionPropertiesProvider attaches structured properties to +# FailureDetails.Properties, which the orchestration returns as its output. +# ============================================================================= + +@baseUrl = http://localhost:7071/api + + +### Start the orchestration +# @name customException +POST {{baseUrl}}/orchestrators/customException + +### Check status — the output holds FailureDetails, including custom Properties +GET {{customException.response.body.statusQueryGetUri}} diff --git a/samples/durable-functions/javascript/CustomExceptionProperties/README.md b/samples/durable-functions/javascript/CustomExceptionProperties/README.md new file mode 100644 index 00000000..e4bd6206 --- /dev/null +++ b/samples/durable-functions/javascript/CustomExceptionProperties/README.md @@ -0,0 +1,78 @@ +# Custom Exception Properties — Durable Functions JavaScript + +JavaScript | Durable Functions + +## Description + +Demonstrates attaching **custom properties to `FailureDetails`** when an activity throws, using the Node.js v4 programming model and the Durable Task Scheduler (DTS) backend. + +An activity throws a `BusinessValidationException` carrying structured fields (string, int, long, date-time, dictionary, list, null). A provider registered via `df.app.setExceptionPropertiesProvider(...)` extracts those fields, and the Durable worker propagates them onto `FailureDetails.Properties`. The orchestration catches the propagated `TaskFailedError` and returns its `failureDetails`, so the rich error context is available to the orchestrator instead of just a message and stack trace. + +## Prerequisites + +1. [Node.js 18+](https://nodejs.org/) (use Node 22 LTS; the Functions host does not yet support Node 24) +2. [Azure Functions Core Tools v4](https://learn.microsoft.com/azure/azure-functions/functions-run-local) +3. [Docker](https://www.docker.com/products/docker-desktop/) (for the emulator) + +> **Note:** This sample relies on functionality that is still rolling out in preview: +> - The `durable-functions` npm package must include `df.app.setExceptionPropertiesProvider` (the custom exception properties feature). This is not yet on the public npm registry — install a build from the feature branch until it ships. +> - The Durable Functions host extension must be >= 3.14.0 (with AzureManaged >= 1.9.0). At the time of writing, the latest **public** preview bundle (`Microsoft.Azure.Functions.ExtensionBundle.Preview` 4.30.0) still ships extension 3.1.0 / AzureManaged 0.4.2-alpha, which will **not** surface `Properties`. Use a preview bundle that ships extension >= 3.14.0 once it is published; `host.json` already targets the preview bundle so it will pick up the feature automatically. + +## Quick Run + +1. Start the Durable Task Scheduler emulator: + ```bash + docker run -d -p 8080:8080 -p 8082:8082 mcr.microsoft.com/dts/dts-emulator:latest + ``` + +2. Install dependencies and run: + ```bash + cd samples/durable-functions/javascript/CustomExceptionProperties + npm install + func start + ``` + +3. Start the orchestration: + ```bash + curl -X POST http://localhost:7071/api/StartCustomException + ``` + +4. Follow the `statusQueryGetUri` from the response to see the completed output. + +5. View in the dashboard: http://localhost:8082 + +## Expected Output + +The orchestration returns the caught `failureDetails`, whose `properties` carry the custom values supplied by the provider: + +```json +{ + "errorType": "BusinessValidationException", + "errorMessage": "Business logic validation failed", + "properties": { + "StringProperty": "validation-error-123", + "IntProperty": 100, + "LongProperty": 999999999, + "DateTimeProperty": "2025-10-15T14:30:00.000Z", + "DictionaryProperty": { + "error_code": "VALIDATION_FAILED", + "retry_count": 3, + "is_critical": true + }, + "ListProperty": ["error1", "error2", 500, null], + "NullProperty": null + } +} +``` + +## How It Works + +- `df.app.setExceptionPropertiesProvider({ getExceptionProperties(error) { ... } })` registers a global provider once at app startup. +- `businessActivity` throws a `BusinessValidationException` carrying the structured fields. +- `orchestrationWithCustomException` calls the activity, catches the propagated `TaskFailedError`, and returns `e.failureDetails` — including the custom `properties`. + +## Learn More + +- [Durable Functions JavaScript API Reference](https://learn.microsoft.com/javascript/api/durable-functions/) +- [Durable Functions error handling](https://learn.microsoft.com/azure/azure-functions/durable/durable-functions-error-handling) +- [Durable Task Scheduler Documentation](https://aka.ms/dts-documentation) diff --git a/samples/durable-functions/javascript/CustomExceptionProperties/host.json b/samples/durable-functions/javascript/CustomExceptionProperties/host.json new file mode 100644 index 00000000..47790b09 --- /dev/null +++ b/samples/durable-functions/javascript/CustomExceptionProperties/host.json @@ -0,0 +1,21 @@ +{ + "version": "2.0", + "logging": { + "logLevel": { + "DurableTask.Core": "Warning" + } + }, + "extensions": { + "durableTask": { + "hubName": "default", + "storageProvider": { + "type": "azureManaged", + "connectionStringName": "DURABLE_TASK_SCHEDULER_CONNECTION_STRING" + } + } + }, + "extensionBundle": { + "id": "Microsoft.Azure.Functions.ExtensionBundle.Preview", + "version": "[4.*, 5.0.0)" + } +} diff --git a/samples/durable-functions/javascript/CustomExceptionProperties/package.json b/samples/durable-functions/javascript/CustomExceptionProperties/package.json new file mode 100644 index 00000000..a8391fda --- /dev/null +++ b/samples/durable-functions/javascript/CustomExceptionProperties/package.json @@ -0,0 +1,13 @@ +{ + "name": "durable-functions-custom-exception-properties-js", + "version": "1.0.0", + "description": "Durable Functions JavaScript sample: custom FailureDetails properties with Durable Task Scheduler", + "main": "src/functions/*.js", + "scripts": { + "start": "func start" + }, + "dependencies": { + "@azure/functions": "^4.5.0", + "durable-functions": "^3.5.0" + } +} diff --git a/samples/durable-functions/javascript/CustomExceptionProperties/src/functions/customExceptionProperties.js b/samples/durable-functions/javascript/CustomExceptionProperties/src/functions/customExceptionProperties.js new file mode 100644 index 00000000..72c3853c --- /dev/null +++ b/samples/durable-functions/javascript/CustomExceptionProperties/src/functions/customExceptionProperties.js @@ -0,0 +1,102 @@ +const { app } = require("@azure/functions"); +const df = require("durable-functions"); +const { TaskFailedError } = df; + +// ============================================================================= +// Custom exception carrying structured properties. +// ============================================================================= +class BusinessValidationException extends Error { + constructor( + message, + stringProperty, + intProperty, + longProperty, + dateTimeProperty, + dictionaryProperty, + listProperty, + nullProperty + ) { + super(message); + this.name = "BusinessValidationException"; + // Fix the prototype chain (necessary when extending built-ins). + Object.setPrototypeOf(this, new.target.prototype); + this.stringProperty = stringProperty; + this.intProperty = intProperty; + this.longProperty = longProperty; + this.dateTimeProperty = dateTimeProperty; + this.dictionaryProperty = dictionaryProperty; + this.listProperty = listProperty; + this.nullProperty = nullProperty; + } +} + +// ============================================================================= +// Register a global provider that surfaces custom properties from thrown +// exceptions into FailureDetails.Properties. Return `undefined` to opt out for +// a particular error. +// ============================================================================= +df.app.setExceptionPropertiesProvider({ + getExceptionProperties(error) { + if (error instanceof BusinessValidationException) { + return { + StringProperty: error.stringProperty, + IntProperty: error.intProperty, + LongProperty: error.longProperty, + DateTimeProperty: error.dateTimeProperty, + DictionaryProperty: error.dictionaryProperty, + ListProperty: error.listProperty, + NullProperty: error.nullProperty, + }; + } + return undefined; + }, +}); + +// Activity: throws an exception carrying custom properties. +df.app.activity("businessActivity", { + handler: () => { + throw new BusinessValidationException( + "Business logic validation failed", + "validation-error-123", + 100, + 999999999, + "2025-10-15T14:30:00.000Z", + { + error_code: "VALIDATION_FAILED", + retry_count: 3, + is_critical: true, + }, + ["error1", "error2", 500, null], + null + ); + }, +}); + +// Orchestrator: calls the activity, catches the propagated TaskFailedError, and +// returns its FailureDetails (which includes the custom Properties). +df.app.orchestration("orchestrationWithCustomException", function* (context) { + try { + yield context.df.callActivity("businessActivity"); + } catch (e) { + if (e instanceof TaskFailedError) { + return e.failureDetails; + } + throw e; + } + // Should never reach here — the activity always throws. + return null; +}); + +// HTTP trigger: start the orchestration. +app.http("StartCustomException", { + route: "StartCustomException", + methods: ["POST"], + authLevel: "anonymous", + extraInputs: [df.input.durableClient()], + handler: async (request, context) => { + const client = df.getClient(context); + const instanceId = await client.startNew("orchestrationWithCustomException"); + context.log(`Started orchestration with ID = '${instanceId}'.`); + return client.createCheckStatusResponse(request, instanceId); + }, +}); From a9ae7553136b8913e980d2834737ebaf04bb8882 Mon Sep 17 00:00:00 2001 From: "naiyuantian@microsoft.com" Date: Tue, 14 Jul 2026 11:06:15 -0700 Subject: [PATCH 2/2] udpate --- .../CustomExceptionPropertyProvider.csproj} | 6 ++++-- .../Functions.cs | 2 +- .../Program.cs | 2 +- .../README.md | 11 +++++++---- .../host.json | 0 .../test.http | 2 +- .../CustomExceptionProperties/package.json | 13 ------------- .../README.md | 11 ++++++----- .../host.json | 4 ++-- .../CustomExceptionPropertyProvider/package.json | 13 +++++++++++++ .../functions/customExceptionPropertyProvider.js} | 0 11 files changed, 35 insertions(+), 29 deletions(-) rename samples/durable-functions/dotnet/{CustomExceptionProperties/CustomExceptionProperties.csproj => CustomExceptionPropertyProvider/CustomExceptionPropertyProvider.csproj} (71%) rename samples/durable-functions/dotnet/{CustomExceptionProperties => CustomExceptionPropertyProvider}/Functions.cs (99%) rename samples/durable-functions/dotnet/{CustomExceptionProperties => CustomExceptionPropertyProvider}/Program.cs (95%) rename samples/durable-functions/dotnet/{CustomExceptionProperties => CustomExceptionPropertyProvider}/README.md (80%) rename samples/durable-functions/dotnet/{CustomExceptionProperties => CustomExceptionPropertyProvider}/host.json (100%) rename samples/durable-functions/dotnet/{CustomExceptionProperties => CustomExceptionPropertyProvider}/test.http (91%) delete mode 100644 samples/durable-functions/javascript/CustomExceptionProperties/package.json rename samples/durable-functions/javascript/{CustomExceptionProperties => CustomExceptionPropertyProvider}/README.md (79%) rename samples/durable-functions/javascript/{CustomExceptionProperties => CustomExceptionPropertyProvider}/host.json (79%) create mode 100644 samples/durable-functions/javascript/CustomExceptionPropertyProvider/package.json rename samples/durable-functions/javascript/{CustomExceptionProperties/src/functions/customExceptionProperties.js => CustomExceptionPropertyProvider/src/functions/customExceptionPropertyProvider.js} (100%) diff --git a/samples/durable-functions/dotnet/CustomExceptionProperties/CustomExceptionProperties.csproj b/samples/durable-functions/dotnet/CustomExceptionPropertyProvider/CustomExceptionPropertyProvider.csproj similarity index 71% rename from samples/durable-functions/dotnet/CustomExceptionProperties/CustomExceptionProperties.csproj rename to samples/durable-functions/dotnet/CustomExceptionPropertyProvider/CustomExceptionPropertyProvider.csproj index 3e674598..1d2aad47 100644 --- a/samples/durable-functions/dotnet/CustomExceptionProperties/CustomExceptionProperties.csproj +++ b/samples/durable-functions/dotnet/CustomExceptionPropertyProvider/CustomExceptionPropertyProvider.csproj @@ -5,15 +5,17 @@ Exe enable enable - CustomExceptionProperties + CustomExceptionPropertyProvider + + - + diff --git a/samples/durable-functions/dotnet/CustomExceptionProperties/Functions.cs b/samples/durable-functions/dotnet/CustomExceptionPropertyProvider/Functions.cs similarity index 99% rename from samples/durable-functions/dotnet/CustomExceptionProperties/Functions.cs rename to samples/durable-functions/dotnet/CustomExceptionPropertyProvider/Functions.cs index 266f557b..bcdd69da 100644 --- a/samples/durable-functions/dotnet/CustomExceptionProperties/Functions.cs +++ b/samples/durable-functions/dotnet/CustomExceptionPropertyProvider/Functions.cs @@ -7,7 +7,7 @@ using Microsoft.DurableTask.Client; using Microsoft.DurableTask.Worker; -namespace CustomExceptionProperties; +namespace CustomExceptionPropertyProvider; // ============================================================================= // Orchestration + activity diff --git a/samples/durable-functions/dotnet/CustomExceptionProperties/Program.cs b/samples/durable-functions/dotnet/CustomExceptionPropertyProvider/Program.cs similarity index 95% rename from samples/durable-functions/dotnet/CustomExceptionProperties/Program.cs rename to samples/durable-functions/dotnet/CustomExceptionPropertyProvider/Program.cs index a0935909..07cc96c0 100644 --- a/samples/durable-functions/dotnet/CustomExceptionProperties/Program.cs +++ b/samples/durable-functions/dotnet/CustomExceptionPropertyProvider/Program.cs @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -using CustomExceptionProperties; +using CustomExceptionPropertyProvider; using Microsoft.Azure.Functions.Worker.Builder; using Microsoft.DurableTask.Worker; using Microsoft.Extensions.DependencyInjection; diff --git a/samples/durable-functions/dotnet/CustomExceptionProperties/README.md b/samples/durable-functions/dotnet/CustomExceptionPropertyProvider/README.md similarity index 80% rename from samples/durable-functions/dotnet/CustomExceptionProperties/README.md rename to samples/durable-functions/dotnet/CustomExceptionPropertyProvider/README.md index 155bb3a2..9d890567 100644 --- a/samples/durable-functions/dotnet/CustomExceptionProperties/README.md +++ b/samples/durable-functions/dotnet/CustomExceptionPropertyProvider/README.md @@ -1,4 +1,4 @@ -# Custom Exception Properties with Durable Functions (.NET) +# Custom Exception Property Provider with Durable Functions (.NET) .NET | Durable Functions @@ -14,7 +14,10 @@ When an activity throws a `BusinessValidationException`, the registered `IExcept 2. [Docker](https://www.docker.com/products/docker-desktop/) (for running the emulator) 3. [Azure Functions Core Tools v4](https://learn.microsoft.com/azure/azure-functions/functions-run-local) -> **Note:** The `IExceptionPropertiesProvider` API requires `Microsoft.DurableTask.Worker` >= 1.24.2 and the Durable Functions host extension >= 3.14.0 (via `Microsoft.Azure.Functions.Worker.Extensions.DurableTask` >= 1.18.0). The DTS backend requires `Microsoft.Azure.Functions.Worker.Extensions.DurableTask.AzureManaged` >= 1.9.0. These versions are pinned in the `.csproj`. +> **Note:** The custom exception property provider requires these minimum versions (the sample references the latest available): +> - `Microsoft.Azure.Functions.Worker.Extensions.DurableTask` >= 1.9.0 +> - `Microsoft.DurableTask.Worker` >= 1.16.1 (provides `IExceptionPropertiesProvider`) +> - `Microsoft.Azure.Functions.Worker.Extensions.DurableTask.AzureManaged` >= 1.2.0 (for the Durable Task Scheduler backend) ## Quick Run @@ -25,7 +28,7 @@ When an activity throws a `BusinessValidationException`, the registered `IExcept 2. Start the Function app: ```bash - cd samples/durable-functions/dotnet/CustomExceptionProperties + cd samples/durable-functions/dotnet/CustomExceptionPropertyProvider func start ``` @@ -44,7 +47,7 @@ The orchestration returns the caught `FailureDetails`, whose `Properties` carry ```json { - "errorType": "CustomExceptionProperties.BusinessValidationException", + "errorType": "CustomExceptionPropertyProvider.BusinessValidationException", "errorMessage": "Business logic validation failed", "properties": { "StringProperty": "validation-error-123", diff --git a/samples/durable-functions/dotnet/CustomExceptionProperties/host.json b/samples/durable-functions/dotnet/CustomExceptionPropertyProvider/host.json similarity index 100% rename from samples/durable-functions/dotnet/CustomExceptionProperties/host.json rename to samples/durable-functions/dotnet/CustomExceptionPropertyProvider/host.json diff --git a/samples/durable-functions/dotnet/CustomExceptionProperties/test.http b/samples/durable-functions/dotnet/CustomExceptionPropertyProvider/test.http similarity index 91% rename from samples/durable-functions/dotnet/CustomExceptionProperties/test.http rename to samples/durable-functions/dotnet/CustomExceptionPropertyProvider/test.http index 3381d16a..46a50f84 100644 --- a/samples/durable-functions/dotnet/CustomExceptionProperties/test.http +++ b/samples/durable-functions/dotnet/CustomExceptionPropertyProvider/test.http @@ -1,5 +1,5 @@ # ============================================================================= -# Custom Exception Properties — Durable Functions (.NET isolated) Demo +# Custom Exception Property Provider — Durable Functions (.NET isolated) Demo # ============================================================================= # # Prerequisites: diff --git a/samples/durable-functions/javascript/CustomExceptionProperties/package.json b/samples/durable-functions/javascript/CustomExceptionProperties/package.json deleted file mode 100644 index a8391fda..00000000 --- a/samples/durable-functions/javascript/CustomExceptionProperties/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "durable-functions-custom-exception-properties-js", - "version": "1.0.0", - "description": "Durable Functions JavaScript sample: custom FailureDetails properties with Durable Task Scheduler", - "main": "src/functions/*.js", - "scripts": { - "start": "func start" - }, - "dependencies": { - "@azure/functions": "^4.5.0", - "durable-functions": "^3.5.0" - } -} diff --git a/samples/durable-functions/javascript/CustomExceptionProperties/README.md b/samples/durable-functions/javascript/CustomExceptionPropertyProvider/README.md similarity index 79% rename from samples/durable-functions/javascript/CustomExceptionProperties/README.md rename to samples/durable-functions/javascript/CustomExceptionPropertyProvider/README.md index e4bd6206..cd7db8d9 100644 --- a/samples/durable-functions/javascript/CustomExceptionProperties/README.md +++ b/samples/durable-functions/javascript/CustomExceptionPropertyProvider/README.md @@ -1,4 +1,4 @@ -# Custom Exception Properties — Durable Functions JavaScript +# Custom Exception Property Provider — Durable Functions JavaScript JavaScript | Durable Functions @@ -14,9 +14,10 @@ An activity throws a `BusinessValidationException` carrying structured fields (s 2. [Azure Functions Core Tools v4](https://learn.microsoft.com/azure/azure-functions/functions-run-local) 3. [Docker](https://www.docker.com/products/docker-desktop/) (for the emulator) -> **Note:** This sample relies on functionality that is still rolling out in preview: -> - The `durable-functions` npm package must include `df.app.setExceptionPropertiesProvider` (the custom exception properties feature). This is not yet on the public npm registry — install a build from the feature branch until it ships. -> - The Durable Functions host extension must be >= 3.14.0 (with AzureManaged >= 1.9.0). At the time of writing, the latest **public** preview bundle (`Microsoft.Azure.Functions.ExtensionBundle.Preview` 4.30.0) still ships extension 3.1.0 / AzureManaged 0.4.2-alpha, which will **not** surface `Properties`. Use a preview bundle that ships extension >= 3.14.0 once it is published; `host.json` already targets the preview bundle so it will pick up the feature automatically. +> **Note:** The custom exception property provider requires: +> - The `durable-functions` npm package must include `df.app.setExceptionPropertiesProvider`. +> - An extension bundle that ships the feature. Use the **main** bundle `Microsoft.Azure.Functions.ExtensionBundle` >= **4.37.1** (configured in `host.json`), or the **preview** bundle `Microsoft.Azure.Functions.ExtensionBundle.Preview` >= **4.44.0**. Older bundles will **not** surface `Properties`. +> - The Durable Task Scheduler (azure-managed) SDK >= **1.2.0**. ## Quick Run @@ -27,7 +28,7 @@ An activity throws a `BusinessValidationException` carrying structured fields (s 2. Install dependencies and run: ```bash - cd samples/durable-functions/javascript/CustomExceptionProperties + cd samples/durable-functions/javascript/CustomExceptionPropertyProvider npm install func start ``` diff --git a/samples/durable-functions/javascript/CustomExceptionProperties/host.json b/samples/durable-functions/javascript/CustomExceptionPropertyProvider/host.json similarity index 79% rename from samples/durable-functions/javascript/CustomExceptionProperties/host.json rename to samples/durable-functions/javascript/CustomExceptionPropertyProvider/host.json index 47790b09..7c0e97a6 100644 --- a/samples/durable-functions/javascript/CustomExceptionProperties/host.json +++ b/samples/durable-functions/javascript/CustomExceptionPropertyProvider/host.json @@ -15,7 +15,7 @@ } }, "extensionBundle": { - "id": "Microsoft.Azure.Functions.ExtensionBundle.Preview", - "version": "[4.*, 5.0.0)" + "id": "Microsoft.Azure.Functions.ExtensionBundle", + "version": "[4.37.1, 5.0.0)" } } diff --git a/samples/durable-functions/javascript/CustomExceptionPropertyProvider/package.json b/samples/durable-functions/javascript/CustomExceptionPropertyProvider/package.json new file mode 100644 index 00000000..8e64792f --- /dev/null +++ b/samples/durable-functions/javascript/CustomExceptionPropertyProvider/package.json @@ -0,0 +1,13 @@ +{ + "name": "durable-functions-custom-exception-property-provider-js", + "version": "1.0.0", + "description": "Durable Functions JavaScript sample: custom exception property provider for FailureDetails with Durable Task Scheduler", + "main": "src/functions/*.js", + "scripts": { + "start": "func start" + }, + "dependencies": { + "@azure/functions": "^4.5.0", + "durable-functions": "^3.5.0" + } +} diff --git a/samples/durable-functions/javascript/CustomExceptionProperties/src/functions/customExceptionProperties.js b/samples/durable-functions/javascript/CustomExceptionPropertyProvider/src/functions/customExceptionPropertyProvider.js similarity index 100% rename from samples/durable-functions/javascript/CustomExceptionProperties/src/functions/customExceptionProperties.js rename to samples/durable-functions/javascript/CustomExceptionPropertyProvider/src/functions/customExceptionPropertyProvider.js