From 557a58920d8baaaf35470d26e5625845d4fa55ba Mon Sep 17 00:00:00 2001 From: Reynaldo Montes de Oca Date: Fri, 7 Nov 2025 22:59:52 -0600 Subject: [PATCH] Add Aggregator pattern template for BizTalk migration - Aggregates messages from Azure Service Bus by CorrelationId - Implements BizTalk Server Aggregator pattern in Logic Apps Standard - Includes flat file decoding with XSD schema support - Sequential convoy processing for message ordering - 8 configurable parameters - Comprehensive error handling --- .../README.md | 452 ++++++++++++++++++ .../manifest.json | 122 +++++ .../parameters.json | 30 ++ .../workflow-dark.png | Bin 0 -> 27921 bytes .../workflow-light.png | Bin 0 -> 26331 bytes .../workflow.json | 431 +++++++++++++++++ manifest.json | 113 ++--- 7 files changed, 1092 insertions(+), 56 deletions(-) create mode 100644 aggregate-messages-servicebus-correlationid/README.md create mode 100644 aggregate-messages-servicebus-correlationid/manifest.json create mode 100644 aggregate-messages-servicebus-correlationid/parameters.json create mode 100644 aggregate-messages-servicebus-correlationid/workflow-dark.png create mode 100644 aggregate-messages-servicebus-correlationid/workflow-light.png create mode 100644 aggregate-messages-servicebus-correlationid/workflow.json diff --git a/aggregate-messages-servicebus-correlationid/README.md b/aggregate-messages-servicebus-correlationid/README.md new file mode 100644 index 0000000..9b3a178 --- /dev/null +++ b/aggregate-messages-servicebus-correlationid/README.md @@ -0,0 +1,452 @@ +# Aggregate messages from Azure Service Bus by CorrelationId + +## Overview + +This workflow template implements the **Aggregator enterprise integration pattern** for Azure Logic Apps Standard, designed to facilitate **migration from BizTalk Server** to Azure. The template retrieves messages from an Azure Service Bus queue in batches, decodes flat file content using an XSD schema, and groups messages by their CorrelationId property. + +This template replicates the functionality of BizTalk Server's Aggregator pattern, providing a cloud-native alternative that leverages Azure Logic Apps Standard capabilities while maintaining compatibility with existing BizTalk flat file schemas and integration patterns. + +## Pattern description + +The **Aggregator pattern** is an Enterprise Integration Pattern that collects related messages and combines them into a single, consolidated message. This template uses the CorrelationId property from Azure Service Bus messages to identify which messages belong together, making it ideal for scenarios where multiple related messages need to be processed as a unit. + +**For BizTalk Server users**: This pattern is equivalent to BizTalk's **Aggregator pattern with sequential convoy processing**. In BizTalk terminology: +- **Aggregator pattern** = The overall pattern (WHAT: collecting and combining related messages) +- **Sequential convoy** = The implementation mechanism (HOW: processing correlated messages one-by-one using correlation sets) + +This Logic Apps workflow combines both concepts: it implements the Aggregator pattern using sequential message processing within each batch, similar to how BizTalk orchestrations process convoy messages. + +## Architecture + +- **Trigger**: Azure Service Bus queue (peek-lock, non-session, batch mode) +- **Processing**: Sequential in-memory aggregation with flat file decoding +- **Output**: HTTP response with aggregated results grouped by CorrelationId +- **Error handling**: Scope-based error capture with detailed logging + +## Use cases + +This template is useful for the following scenarios: + +- **BizTalk Aggregator pattern migration**: Migrate BizTalk orchestrations that use Aggregator pattern with sequential or parallel convoy processing to Azure Logic Apps Standard +- **Order processing**: Aggregate multiple order line items (separate messages) into a complete order document +- **Document assembly**: Combine document fragments into a complete document (EDI segments, flat file records) +- **Data collection**: Group related telemetry or sensor data by device ID for batch analysis +- **Multi-step workflows**: Collect results from parallel operations using a correlation identifier before proceeding +- **Message batching**: Consolidate multiple messages with same business key for downstream processing + +### BizTalk Server migration considerations + +If you're migrating from BizTalk Server, this template provides equivalent functionality for: + +**BizTalk Aggregator Pattern Components:** +- **Correlation Sets** → **CorrelationId property**: Messages are grouped using Service Bus CorrelationId (equivalent to BizTalk correlation sets) +- **Sequential Convoy** → **Sequential processing**: Set `EnableSequentialProcessing=true` to process correlated messages one-by-one (equivalent to BizTalk sequential convoy orchestrations) +- **Message Collection** → **In-memory variables**: Uses `CorrelationGroups` dictionary to collect messages (equivalent to BizTalk orchestration variables) +- **XSD Schemas** → **Flat File Decoding**: Reuse your existing BizTalk flat file schemas without modification +- **Exception Blocks** → **Scope-based error handling**: Similar error capture and logging as BizTalk exception handlers +- **Receive Location Batching** → **Service Bus batch trigger**: Comparable to BizTalk receive locations with batching enabled + +**Key Pattern Mapping:** +- This workflow = BizTalk **Aggregator Orchestration** with **Sequential Convoy** +- NOT a parallel convoy (which would be `EnableSequentialProcessing=false`) + +**Pattern Hierarchy:** +``` +AGGREGATOR PATTERN (Enterprise Integration Pattern) +├── Purpose: Collect and combine related messages +├── Identifies messages by: CorrelationId property +│ +└── Implementation Mechanism: SEQUENTIAL CONVOY + ├── Processes messages: One-by-one in order + ├── Maintains: Message order and variable consistency + └── Alternative: Parallel Convoy (faster, no order guarantee) +``` + +**BizTalk Orchestration Equivalent:** +```csharp +// BizTalk Aggregator with Sequential Convoy +[CorrelationSet: OrderCorrelation] +while (receivedCount < expectedCount) +{ + Receive(OrderLinePort); // Sequential convoy + orderLines.Add(currentMessage); // Aggregation +} +CompleteOrder = Aggregate(orderLines); // Aggregator pattern result +Send(CompleteOrderPort); +``` + +**Logic Apps Workflow Equivalent:** +```json +Trigger: Service Bus (batch mode) +↓ +ForEach Message (sequential: concurrency=1) ← Sequential convoy + Extract CorrelationId ← Correlation set + Decode flat file + Add to CorrelationGroups[corrId] ← Aggregation +↓ +Build AggregatedResults ← Aggregator pattern result +Return HTTP Response +``` + +## Prerequisites + +Before you use this template, make sure that you have the following resources: + +### Azure Service Bus namespace and queue + +You need an Azure Service Bus namespace with a non-session queue configured: + +- **Sessions enabled**: Must be set to `false` +- **Lock duration**: At least 30 seconds (recommended: 5 minutes for large batches) +- **Max delivery count**: Configure based on your retry policy (recommended: 10) + +For more information, see [Create an Azure Service Bus namespace and queue](https://learn.microsoft.com/azure/service-bus-messaging/service-bus-quickstart-portal). + +### Flat file schema (XSD) + +You need an XSD schema that defines the structure of your flat file messages. Upload this schema to your logic app's `Artifacts/Schemas/` folder before deploying the workflow. + +**BizTalk Server compatibility**: This template supports BizTalk flat file schemas directly. You can export your existing BizTalk schemas and use them without modification in Azure Logic Apps Standard. The flat file decoding action in Logic Apps uses the same XSD schema format as BizTalk Server. + +For more information, see [Create schemas for flat file encoding and decoding](https://learn.microsoft.com/azure/logic-apps/logic-apps-enterprise-integration-flatfile). + +### Azure Service Bus connection + +You need to configure an Azure Service Bus connection in your logic app. The template uses the built-in Service Bus connector (ServiceProvider) for optimal performance in Azure Logic Apps Standard. + +For more information, see [Azure Service Bus connector overview](https://learn.microsoft.com/connectors/servicebus/). + +## Template parameters + +The template includes the following configurable parameters: + +### ServiceBusQueueName_#workflowname# +- **Type**: string +- **Required**: Yes +- **Default**: `"your-queue-name"` +- **Description**: The name of the Azure Service Bus queue to monitor for incoming messages. Replace `your-queue-name` with your actual queue name. + +### MaxBatchSize_#workflowname# +- **Type**: integer +- **Required**: Yes +- **Default**: `10` +- **Range**: 1-100 +- **Description**: The maximum number of messages to retrieve and process in a single batch. Adjust based on your message size and throughput requirements. +- **Performance guidance**: + - Small messages (<10KB): Use 50-100 for higher throughput + - Large messages (>100KB): Use 10-20 to avoid memory issues + - Strict ordering: Use lower values (5-10) for better control + +### FlatFileSchemaName_#workflowname# +- **Type**: string +- **Required**: Yes +- **Default**: `"Invoice.xsd"` +- **Description**: The name of the flat file schema (XSD) to use for decoding message content. This file must exist in your logic app's `Artifacts/Schemas/` folder. + +### DefaultCorrelationId_#workflowname# +- **Type**: string +- **Required**: No +- **Default**: `"NO_CORRELATION_ID"` +- **Description**: The fallback value to use when a message does not have a CorrelationId property. Messages with this value are grouped together. + +### ServiceBusConnectionName_#workflowname# +- **Type**: string +- **Required**: Yes +- **Default**: `"serviceBus"` +- **Description**: The name of the Azure Service Bus connection reference in your logic app's `connections.json` file. + +### EnableSequentialProcessing_#workflowname# +- **Type**: boolean +- **Required**: No +- **Default**: `true` +- **Description**: Controls message processing concurrency **within each batch**: + - `true`: Processes messages one-by-one sequentially (concurrency=1) to ensure consistent variable updates and avoid race conditions + - `false`: Allows parallel processing (up to 50 concurrent threads) for higher throughput, but aggregation order may vary + +**Note**: This setting does NOT control the order in which messages are retrieved from Service Bus. It only controls how messages within a single batch are processed by the workflow. + +### ResponseStatusCode + +- **Type**: Integer +- **Required**: No +- **Default**: `200` +- **Description**: HTTP status code to return in the response for successful processing. Standard value is 200 (OK). + +### ResponseContentType + +- **Type**: String +- **Required**: No +- **Default**: `application/json` +- **Description**: Content-Type header value for the HTTP response. Use 'application/json' for JSON-formatted responses. + +## How to use this template + +### Step 1: Create a logic app workflow from this template + +1. In the [Azure portal](https://portal.azure.com), open your Azure Logic Apps Standard resource. +2. On the logic app menu, select **Workflows**. +3. Select **Add** to create a new workflow. +4. In the template gallery, find and select **Aggregate messages from Azure Service Bus by CorrelationId**. +5. Provide values for the required parameters. +6. Select **Create** to create the workflow from the template. + +### Step 2: Configure your Azure Service Bus connection + +1. On your logic app menu, select **Connections**. +2. Select **Add** to create a new connection. +3. Search for and select **Azure Service Bus (Service Provider)**. +4. Provide your Azure Service Bus connection string. +5. Name the connection to match the `ServiceBusConnectionName_#workflowname#` parameter value. + +For more information, see [Azure Service Bus connector overview](https://learn.microsoft.com/connectors/servicebus/). + +### Step 3: Upload your flat file schema + +1. In your logic app, navigate to the `Artifacts/Schemas/` folder. +2. Upload your XSD schema file. +3. Make sure the file name matches the `FlatFileSchemaName_#workflowname#` parameter value. + +### Step 4: Test your workflow + +1. Send test messages to your Azure Service Bus queue with CorrelationId properties. +2. Monitor the workflow run history to verify that messages are grouped correctly. +3. Review the HTTP response to see the aggregated results. + +## Response format + +The workflow returns an HTTP response with the following JSON structure: + +```json +{ + "ProcessedBatchSize": 10, + "UniqueCorrelationIds": 3, + "AggregatedMessages": [ + { + "CorrelationId": "order-12345", + "MessageCount": 5, + "Messages": [ + { + "Invoice": { + "InvoiceId": "INV001", + "Items": [...] + } + } + ] + } + ], + "ProcessingTimestamp": "2025-10-31T12:00:00Z", + "Configuration": { + "QueueName": "your-queue-name", + "MaxBatchSize": 10, + "SchemaName": "Invoice.xsd", + "SequentialProcessing": true + } +} +``` + +The Response action returns the aggregated results as an HTTP response with the configured status code and content type. + +## Performance considerations + +### Batch size optimization + +The `MaxBatchSize_#workflowname#` parameter significantly impacts performance: + +| Message size | Recommended batch size | Rationale | +|--------------|----------------------|-----------| +| Small (<10KB) | 50-100 | Maximize throughput with minimal memory impact | +| Medium (10-100KB) | 20-50 | Balance throughput and memory usage | +| Large (>100KB) | 10-20 | Avoid memory issues and timeouts | + +### Sequential vs. parallel processing + +The `EnableSequentialProcessing_#workflowname#` parameter controls concurrency **within each batch**: + +- **Sequential (true)**: + - Processes messages one-by-one in the order they appear in the batch + - Prevents race conditions when updating `CorrelationGroups` variable + - Easier debugging with predictable execution flow + - Slower throughput (each message waits for previous to complete) + +- **Parallel (false)**: + - Processes up to 50 messages simultaneously + - 5-10x faster for large batches + - Aggregation order within same CorrelationId may vary + - Risk of race conditions if variables update simultaneously + +**Important**: Service Bus peek-lock guarantees FIFO order **only within a session-enabled queue**. This parameter controls processing order **after** messages are retrieved, not the retrieval order itself. + +### Lock duration + +Configure your Azure Service Bus queue's lock duration to be longer than your workflow's processing time to avoid messages being released back to the queue before processing completes. + +## Error handling + +The template includes comprehensive error handling: + +- **Scope-based error capture**: The `Process_Message_Scope` action wraps all message processing logic +- **Error logging**: Failed messages are logged with details including CorrelationId, MessageId, and error details +- **Continue on error**: The workflow continues processing remaining messages even if some messages fail +- **Error response format**: + +```json +{ + "ErrorType": "MessageProcessingFailed", + "CorrelationId": "order-12345", + "MessageId": "abc-123", + "ErrorDetails": {...}, + "Timestamp": "2025-10-31T12:00:00Z" +} +``` + +## Limitations + +Be aware of the following limitations: + +- **In-memory aggregation**: All messages in a batch are stored in memory during processing. Workflow execution timeout applies. +- **No cross-batch correlation**: Messages are aggregated only within a single batch. Messages with the same CorrelationId in different batches are not combined. +- **Lock renewal**: The template does not implement automatic lock renewal. Configure your queue's lock duration to exceed your workflow's processing time. +- **In-memory results**: The aggregated results are stored in workflow variables. For persistence, add actions to store results in Azure Storage, Cosmos DB, or other external systems. + +## Customization options + +You can customize this template to fit your specific needs: + +### Add message validation + +Insert a condition action after flat file decoding to validate message content before aggregation. + +### Implement cross-batch aggregation + +Use Azure Storage or Azure Cosmos DB to maintain aggregation state across multiple workflow runs. + +### Add dead-letter handling + +Configure dead-letter queue processing for messages that fail repeatedly. + +### Store aggregated results + +Add a Compose or HTTP action after `Log_Processing_Summary` to send results to external systems (Azure Storage, Cosmos DB, Event Hub, etc.). + +## Migrating from BizTalk Server + +This section provides guidance for organizations migrating BizTalk Server Aggregator patterns to Azure Logic Apps Standard. + +### BizTalk to Logic Apps mapping + +| BizTalk Server Component | Azure Logic Apps Equivalent | Notes | +|--------------------------|----------------------------|-------| +| Receive Location (with batching) | Service Bus trigger (peek-lock batch) | Use `MaxBatchSize` parameter | +| Flat File Disassembler | Flat File Decoding action | Compatible with BizTalk XSD schemas | +| Correlation Set | CorrelationId property | Use Service Bus message properties | +| Convoy Processing | Sequential processing + grouping | Set `EnableSequentialProcessing=true` | +| Aggregator Orchestration | Foreach + variables | In-memory aggregation with dictionaries | +| Exception Handler | Scope-based error handling | `Process_Message_Scope` and `Handle_Scope_Error` | +| Send Port | HTTP Response or downstream action | Configurable via `ResponseStatusCode` | + +### Migration steps + +1. **Export BizTalk schemas**: Export your flat file XSD schemas from BizTalk Server +2. **Upload to Logic Apps**: Place schemas in `Artifacts/Schemas/` folder +3. **Configure Service Bus**: Create Azure Service Bus queue to replace BizTalk receive location +4. **Deploy template**: Use this template as a starting point +5. **Test with sample data**: Use existing BizTalk test messages to validate behavior +6. **Adjust parameters**: Fine-tune `MaxBatchSize` and `EnableSequentialProcessing` based on your needs + +### Key differences from BizTalk + +- **Stateless batching**: Logic Apps processes one batch at a time; cross-batch correlation requires external storage +- **No automatic lock renewal**: Configure queue lock duration longer than workflow execution time +- **Cloud-native**: Leverages Azure platform capabilities (Application Insights, managed identities, etc.) +- **Simpler deployment**: No BizTalk Server infrastructure needed; serverless execution model + +### Resources for migration + +- [Migrate BizTalk Server applications to Azure Logic Apps](https://learn.microsoft.com/azure/logic-apps/logic-apps-move-from-biztalk) +- [Azure Integration Services](https://azure.microsoft.com/solutions/integration/) +- [BizTalk migration program](https://azure.microsoft.com/migration/biztalk/) + +## Troubleshooting + +### Messages not being grouped + +**Symptom**: All messages have different CorrelationIds in the output. + +**Solution**: Verify that your Azure Service Bus messages have the CorrelationId property set. Use the `DefaultCorrelationId_#workflowname#` parameter to identify messages without this property. + +### Flat file decoding fails + +**Symptom**: The workflow fails with "Schema not found" or decoding errors. + +**Solution**: +1. Verify that the XSD schema file exists in `Artifacts/Schemas/` +2. Check that the `FlatFileSchemaName_#workflowname#` parameter matches the file name exactly +3. Validate that your message content matches the schema format + +### Workflow times out + +**Symptom**: The workflow run exceeds the execution timeout. + +**Solution**: +1. Reduce the `MaxBatchSize_#workflowname#` parameter +2. Enable parallel processing by setting `EnableSequentialProcessing_#workflowname#` to `false` +3. Simplify your flat file schema to improve decoding performance + +### Connection errors + +**Symptom**: "Connection not found" or authentication errors. + +**Solution**: +1. Verify that the `ServiceBusConnectionName_#workflowname#` parameter matches your connection name in `connections.json` +2. Check that your Azure Service Bus connection string is valid and has the necessary permissions + +## Best practices + +Follow these best practices when using this template: + +1. **Start with small batches**: Begin with `MaxBatchSize_#workflowname#` set to 10 and increase gradually while monitoring performance. + +2. **Monitor run history**: Regularly review workflow run history to identify patterns in message processing times and error rates. + +3. **Use Application Insights**: Enable Application Insights integration to track detailed metrics and diagnose issues. + +4. **Version your schemas**: Include version numbers in schema file names (for example, `invoice-v1.xsd`) to support schema evolution. + +5. **Test error handling**: Send malformed messages to verify that error handling works as expected. + +6. **Configure lock duration**: Set your queue's lock duration to at least 2x your average workflow execution time. + +## Related templates + +You might also be interested in these related templates for BizTalk Server migration and integration patterns: + +- **Process messages with session support**: For scenarios requiring strict message ordering and state management (equivalent to BizTalk sequential convoys) +- **Batch message processing**: For simple batch processing without aggregation +- **Content-based router**: For routing messages to different destinations based on content (equivalent to BizTalk content-based routing) +- **Message transformation**: For XML and flat file transformations (equivalent to BizTalk maps) + +## Support and feedback + +For issues or questions about this template: + +- Review the [Azure Logic Apps documentation](https://learn.microsoft.com/azure/logic-apps/) +- Review [BizTalk Server migration guidance](https://learn.microsoft.com/azure/logic-apps/logic-apps-move-from-biztalk) +- Visit the [Azure Logic Apps community forum](https://learn.microsoft.com/answers/tags/140/azure-logic-apps) +- Report issues in the [Azure Logic Apps feedback forum](https://feedback.azure.com/d365community/forum/ca7c5c88-0925-ec11-b6e6-000d3a4f07b8) + +## Version history + +- **Version 1.0** (2025-11-01): Initial template release for BizTalk Server migration + - Supports Azure Logic Apps Standard (stateful workflows) + - Built-in Azure Service Bus connector for optimal performance + - Flat file decoding with XSD schemas (BizTalk-compatible) + - CorrelationId-based message grouping (equivalent to BizTalk convoy processing) + - Comprehensive error handling with Scope-based handlers + - 8 configurable parameters including response customization + - Sequential and parallel processing modes + - HTTP Response action for downstream integration + - Designed specifically for BizTalk Aggregator pattern migration + +## License + +This template is provided under the [MIT License](https://opensource.org/licenses/MIT). diff --git a/aggregate-messages-servicebus-correlationid/manifest.json b/aggregate-messages-servicebus-correlationid/manifest.json new file mode 100644 index 0000000..f474cfa --- /dev/null +++ b/aggregate-messages-servicebus-correlationid/manifest.json @@ -0,0 +1,122 @@ +{ + "title": "Aggregate messages from Azure Service Bus by CorrelationId", + "description": "Aggregates messages from an Azure Service Bus queue by grouping them using the CorrelationId property. Designed to replicate BizTalk Server Aggregator pattern in Azure Logic Apps Standard. Processes messages in batches, decodes flat file content using an XSD schema, and returns aggregated results.", + "prerequisites": "**Azure Service Bus**: You need an Azure Service Bus namespace with a non-session queue configured. **Flat file schema**: You need an XSD schema uploaded to the logic app's Artifacts/Schemas folder for message decoding (supports BizTalk flat file schemas). **Connection**: You need to configure an Azure Service Bus connection in your logic app. For more information, see [Azure Service Bus connector overview](https://learn.microsoft.com/connectors/servicebus/).", + "tags": [ + "Azure Service Bus", + "Aggregator", + "CorrelationId", + "Batch Processing", + "Flat File Decoding", + "Message Grouping", + "Integration Pattern", + "BizTalk Migration" + ], + "skus": [ + "standard" + ], + "kinds": [ + "stateful" + ], + "detailsDescription": "This workflow template implements the Aggregator enterprise integration pattern for Azure Logic Apps Standard, designed to facilitate migration from BizTalk Server to Azure. It retrieves messages from an Azure Service Bus queue in batches, decodes flat file content using XSD schemas (compatible with BizTalk flat file schemas), and groups messages by their CorrelationId property. The template uses built-in Service Bus operations for optimal performance and includes comprehensive error handling, making it ideal for organizations modernizing their BizTalk Server integration solutions.", + "details": [ + { + "name": "By", + "value": "Microsoft" + }, + { + "name": "Type", + "value": "Workflow" + }, + { + "name": "Trigger", + "value": "Azure Service Bus - When messages are available" + } + ], + "artifacts": [ + { + "type": "workflow", + "file": "workflow.json" + }, + { + "type": "workflow", + "file": "parameters.json" + } + ], + "images": { + "light": "workflow-light", + "dark": "workflow-dark" + }, + "parameters": [ + { + "name": "ServiceBusQueueName_#workflowname#", + "displayName": "Azure Service Bus queue name", + "type": "String", + "default": "your-queue-name", + "description": "The name of the Azure Service Bus queue to monitor for incoming messages. This queue must exist in your Service Bus namespace and should have sessions disabled.", + "required": true + }, + { + "name": "MaxBatchSize_#workflowname#", + "displayName": "Maximum batch size", + "type": "Int", + "default": "10", + "description": "The maximum number of messages to retrieve and process in a single batch. Valid range is 1-100. Adjust this value based on your message size and throughput requirements.", + "required": true + }, + { + "name": "FlatFileSchemaName_#workflowname#", + "displayName": "Flat file schema name", + "type": "String", + "default": "Invoice.xsd", + "description": "The name of the flat file schema (XSD) to use for decoding message content. This schema must be uploaded to your logic app's Artifacts/Schemas folder before deployment.", + "required": true + }, + { + "name": "DefaultCorrelationId_#workflowname#", + "displayName": "Default CorrelationId", + "type": "String", + "default": "NO_CORRELATION_ID", + "description": "The fallback value to use when a message does not have a CorrelationId property. Messages with this value will be grouped together.", + "required": false + }, + { + "name": "ServiceBusConnectionName_#workflowname#", + "displayName": "Azure Service Bus connection name", + "type": "String", + "default": "serviceBus", + "description": "The name of the Azure Service Bus connection reference in your logic app's connections.json file. This connection provides authentication credentials for accessing the Service Bus namespace.", + "required": true + }, + { + "name": "EnableSequentialProcessing_#workflowname#", + "displayName": "Enable sequential processing", + "type": "Bool", + "default": "true", + "description": "When set to true, processes messages sequentially (concurrency=1) to ensure order. When set to false, allows parallel processing for higher throughput. Use true for scenarios requiring strict message ordering.", + "required": false + }, + { + "name": "ResponseStatusCode_#workflowname#", + "displayName": "HTTP response status code", + "type": "Int", + "default": "200", + "description": "The HTTP status code to return in the response for successful processing. Standard value is 200 (OK).", + "required": false + }, + { + "name": "ResponseContentType_#workflowname#", + "displayName": "HTTP response content type", + "type": "String", + "default": "application/json", + "description": "The Content-Type header value for the HTTP response. Use 'application/json' for JSON-formatted responses.", + "required": false + } + ], + "connections": [ + { + "connectorId": "serviceBus_#workflowname#", + "kind": "inapp" + } + ] +} diff --git a/aggregate-messages-servicebus-correlationid/parameters.json b/aggregate-messages-servicebus-correlationid/parameters.json new file mode 100644 index 0000000..0319c35 --- /dev/null +++ b/aggregate-messages-servicebus-correlationid/parameters.json @@ -0,0 +1,30 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "ServiceBusQueueName_#workflowname#": { + "value": "insbcorrelation" + }, + "MaxBatchSize_#workflowname#": { + "value": 10 + }, + "FlatFileSchemaName_#workflowname#": { + "value": "Invoice.xsd" + }, + "DefaultCorrelationId_#workflowname#": { + "value": "NO_CORRELATION_ID" + }, + "ServiceBusConnectionName_#workflowname#": { + "value": "serviceBus" + }, + "EnableSequentialProcessing_#workflowname#": { + "value": true + }, + "ResponseStatusCode_#workflowname#": { + "value": 200 + }, + "ResponseContentType_#workflowname#": { + "value": "application/json" + } + } +} diff --git a/aggregate-messages-servicebus-correlationid/workflow-dark.png b/aggregate-messages-servicebus-correlationid/workflow-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..ef65fd945e5974435632b21fe98920cbe70a51ab GIT binary patch literal 27921 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 literal 0 HcmV?d00001 diff --git a/aggregate-messages-servicebus-correlationid/workflow.json b/aggregate-messages-servicebus-correlationid/workflow.json new file mode 100644 index 0000000..32444d7 --- /dev/null +++ b/aggregate-messages-servicebus-correlationid/workflow.json @@ -0,0 +1,431 @@ +{ + "definition": { + "$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "ServiceBusQueueName": { + "type": "string", + "defaultValue": "insbcorrelation", + "metadata": { + "description": "Name of the Azure Service Bus queue to monitor for incoming messages" + } + }, + "MaxBatchSize": { + "type": "int", + "defaultValue": 10, + "metadata": { + "description": "Maximum number of messages to retrieve and process in a single batch (1-100)" + } + }, + "FlatFileSchemaName": { + "type": "string", + "defaultValue": "Invoice.xsd", + "metadata": { + "description": "Name of the flat file schema (XSD) to use for message decoding" + } + }, + "DefaultCorrelationId": { + "type": "string", + "defaultValue": "NO_CORRELATION_ID", + "metadata": { + "description": "Default value to use when a message does not have a CorrelationId" + } + }, + "ServiceBusConnectionName": { + "type": "string", + "defaultValue": "serviceBus", + "metadata": { + "description": "Name of the Azure Service Bus connection defined in connections.json" + } + }, + "EnableSequentialProcessing": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "When true, processes messages sequentially (concurrency=1); when false, allows parallel processing" + } + }, + "ResponseStatusCode": { + "type": "int", + "defaultValue": 200, + "metadata": { + "description": "HTTP status code for successful response" + } + }, + "ResponseContentType": { + "type": "string", + "defaultValue": "application/json", + "metadata": { + "description": "Content-Type header for the HTTP response" + } + } + }, + "actions": { + "Process_Batch_Messages": { + "type": "Foreach", + "foreach": "@triggerBody()", + "actions": { + "Process_Message_Scope": { + "type": "Scope", + "actions": { + "Get_CorrelationId": { + "type": "Compose", + "inputs": "@coalesce(items('Process_Batch_Messages')?['correlationId'], parameters('DefaultCorrelationId'))", + "metadata": { + "description": "Extract CorrelationId from Azure Service Bus message properties. Falls back to DefaultCorrelationId if not present" + } + }, + "Decode_Flat_File": { + "type": "FlatFileDecoding", + "inputs": { + "content": "@items('Process_Batch_Messages')?['contentData']", + "schema": { + "source": "LogicApp", + "name": "@parameters('FlatFileSchemaName')" + } + }, + "runAfter": { + "Get_CorrelationId": [ + "SUCCEEDED" + ] + }, + "metadata": { + "description": "Decode flat file message content using the configured XSD schema. Converts positional text format to XML" + } + }, + "Get_Current_Group": { + "type": "Compose", + "inputs": "@if(contains(variables('CorrelationGroups'), outputs('Get_CorrelationId')), variables('CorrelationGroups')[outputs('Get_CorrelationId')], json('[]'))", + "runAfter": { + "Decode_Flat_File": [ + "SUCCEEDED" + ] + }, + "metadata": { + "description": "Retrieve existing messages for current CorrelationId from dictionary. Returns empty array if this is the first message for this CorrelationId" + } + }, + "Append_To_Group": { + "type": "Compose", + "inputs": "@union(outputs('Get_Current_Group'), array(body('Decode_Flat_File')))", + "runAfter": { + "Get_Current_Group": [ + "SUCCEEDED" + ] + }, + "metadata": { + "description": "Append newly decoded message to existing messages array for this CorrelationId using union operation" + } + }, + "Build_Updated_Groups": { + "type": "Compose", + "inputs": "@setProperty(variables('CorrelationGroups'), outputs('Get_CorrelationId'), outputs('Append_To_Group'))", + "runAfter": { + "Append_To_Group": [ + "SUCCEEDED" + ] + }, + "metadata": { + "description": "Build updated CorrelationGroups dictionary with new message array for current CorrelationId using setProperty function" + } + }, + "Update_CorrelationGroups": { + "type": "SetVariable", + "inputs": { + "name": "CorrelationGroups", + "value": "@outputs('Build_Updated_Groups')" + }, + "runAfter": { + "Build_Updated_Groups": [ + "SUCCEEDED" + ] + }, + "metadata": { + "description": "Update CorrelationGroups variable with new dictionary containing the appended message" + } + }, + "Check_If_New_CorrelationId": { + "type": "If", + "expression": { + "and": [ + { + "not": { + "contains": [ + "@variables('ProcessedCorrelationIds')", + "@outputs('Get_CorrelationId')" + ] + } + } + ] + }, + "actions": { + "Add_To_ProcessedIds": { + "type": "AppendToArrayVariable", + "inputs": { + "name": "ProcessedCorrelationIds", + "value": "@outputs('Get_CorrelationId')" + }, + "metadata": { + "description": "Add CorrelationId to tracking array to preserve order of first occurrence" + } + } + }, + "else": { + "actions": {} + }, + "runAfter": { + "Update_CorrelationGroups": [ + "SUCCEEDED" + ] + }, + "metadata": { + "description": "Check if this is the first message with this CorrelationId in current batch. If new, add to ProcessedCorrelationIds tracking array" + } + } + } + }, + "Handle_Scope_Error": { + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@result('Process_Message_Scope')[0]['status']", + "Failed" + ] + } + ] + }, + "actions": { + "Log_Error": { + "type": "Compose", + "inputs": { + "ErrorType": "MessageProcessingFailed", + "CorrelationId": "@coalesce(items('Process_Batch_Messages')?['correlationId'], 'UNKNOWN')", + "MessageId": "@items('Process_Batch_Messages')?['messageId']", + "ErrorDetails": "@result('Process_Message_Scope')[0]", + "Timestamp": "@utcNow()" + }, + "metadata": { + "description": "Compose error log entry with message details and scope failure information for troubleshooting" + } + } + }, + "else": { + "actions": {} + }, + "runAfter": { + "Process_Message_Scope": [ + "SUCCEEDED", + "FAILED", + "SKIPPED", + "TIMEDOUT" + ] + }, + "metadata": { + "description": "Error handler for Process_Message_Scope. Captures failures from FlatFileDecoding or other processing steps. Workflow continues processing remaining messages" + } + } + }, + "runAfter": { + "Initialize_ProcessedCorrelationIds": [ + "SUCCEEDED" + ] + }, + "runtimeConfiguration": { + "concurrency": { + "repetitions": 1 + } + }, + "metadata": { + "description": "Iterate through all messages in batch. Process each message: extract CorrelationId, decode flat file, and group by CorrelationId. Sequential processing (concurrency=1) ensures correct order" + } + }, + "Build_Aggregated_Messages_Scope": { + "type": "Scope", + "actions": { + "Build_Aggregated_Messages": { + "type": "Foreach", + "foreach": "@variables('ProcessedCorrelationIds')", + "actions": { + "Get_Messages_For_CorrelationId": { + "type": "Compose", + "inputs": "@variables('CorrelationGroups')[items('Build_Aggregated_Messages')]", + "metadata": { + "description": "Retrieve all messages for current CorrelationId from CorrelationGroups dictionary" + } + }, + "Build_Result_Object": { + "type": "Compose", + "inputs": { + "CorrelationId": "@items('Build_Aggregated_Messages')", + "MessageCount": "@length(outputs('Get_Messages_For_CorrelationId'))", + "Messages": "@outputs('Get_Messages_For_CorrelationId')" + }, + "runAfter": { + "Get_Messages_For_CorrelationId": [ + "SUCCEEDED" + ] + }, + "metadata": { + "description": "Build result object containing CorrelationId, count of messages, and array of decoded message bodies" + } + }, + "Append_To_Results": { + "type": "AppendToArrayVariable", + "inputs": { + "name": "AggregatedResults", + "value": "@outputs('Build_Result_Object')" + }, + "runAfter": { + "Build_Result_Object": [ + "SUCCEEDED" + ] + }, + "metadata": { + "description": "Add aggregated result object to final results array" + } + } + }, + "runtimeConfiguration": { + "concurrency": { + "repetitions": 1 + } + }, + "metadata": { + "description": "Build final aggregated results array. Iterates through ProcessedCorrelationIds (preserving order) and creates result objects with message groups" + } + } + }, + "runAfter": { + "Process_Batch_Messages": [ + "SUCCEEDED", + "FAILED" + ] + }, + "metadata": { + "description": "Scope for aggregation process with error handling support" + } + }, + "Handle_Aggregation_Error": { + "type": "If", + "expression": { + "and": [ + { + "equals": [ + "@result('Build_Aggregated_Messages_Scope')[0]['status']", + "Failed" + ] + } + ] + }, + "actions": { + "Log_Aggregation_Error": { + "type": "Compose", + "inputs": { + "ErrorType": "AggregationFailed", + "ErrorDetails": "@result('Build_Aggregated_Messages_Scope')[0]", + "ProcessedCorrelationIds": "@variables('ProcessedCorrelationIds')", + "PartialResults": "@variables('AggregatedResults')", + "Timestamp": "@utcNow()" + }, + "metadata": { + "description": "Log aggregation error with context including partial results and correlation IDs" + } + } + }, + "else": { + "actions": {} + }, + "runAfter": { + "Build_Aggregated_Messages_Scope": [ + "SUCCEEDED", + "FAILED", + "SKIPPED", + "TIMEDOUT" + ] + }, + "metadata": { + "description": "Error handler for aggregation scope. Logs errors but allows workflow to continue and return response" + } + }, + "Response": { + "type": "Response", + "kind": "http", + "inputs": { + "statusCode": "@parameters('ResponseStatusCode')", + "headers": { + "Content-Type": "@parameters('ResponseContentType')" + }, + "body": { + "ProcessedBatchSize": "@length(triggerBody())", + "UniqueCorrelationIds": "@length(variables('AggregatedResults'))", + "AggregatedMessages": "@variables('AggregatedResults')", + "ProcessingTimestamp": "@utcNow()", + "Configuration": { + "QueueName": "@parameters('ServiceBusQueueName')", + "MaxBatchSize": "@parameters('MaxBatchSize')", + "SchemaName": "@parameters('FlatFileSchemaName')", + "SequentialProcessing": "@parameters('EnableSequentialProcessing')" + } + } + }, + "runAfter": { + "Handle_Aggregation_Error": [ + "SUCCEEDED", + "FAILED", + "SKIPPED" + ] + }, + "metadata": { + "description": "Return HTTP response with aggregated results. Includes batch statistics, grouped messages by CorrelationId, processing timestamp, and configuration details" + } + }, + "Initialize_ProcessedCorrelationIds": { + "type": "InitializeVariable", + "inputs": { + "variables": [ + { + "name": "CorrelationGroups", + "type": "object", + "value": {} + }, + { + "name": "AggregatedResults", + "type": "array", + "value": [] + }, + { + "name": "ProcessedCorrelationIds", + "type": "array", + "value": [] + } + ] + }, + "runAfter": {} + } + }, + "outputs": {}, + "triggers": { + "When_messages_are_available_in_a_queue": { + "type": "ServiceProvider", + "inputs": { + "parameters": { + "queueName": "@parameters('ServiceBusQueueName')", + "isSessionsEnabled": false, + "maxMessageCount": "@parameters('MaxBatchSize')" + }, + "serviceProviderConfiguration": { + "connectionName": "serviceBus", + "operationId": "peekLockQueueMessagesV2", + "serviceProviderId": "/serviceProviders/serviceBus" + } + }, + "metadata": { + "description": "Built-in Azure Service Bus trigger (ServiceProvider). Retrieves batch of messages using peek-lock from non-session queue. Uses peekLockQueueMessagesV2 for better performance in Azure Logic Apps Standard" + } + } + } + }, + "kind": "Stateful" +} \ No newline at end of file diff --git a/manifest.json b/manifest.json index ba2ec48..18d67d7 100644 --- a/manifest.json +++ b/manifest.json @@ -1,58 +1,59 @@ [ - "analyze-images-ai-rag", - "get-message-ibm-mq-queue-browse-lock-abort", - "get-message-service-bus-queue-peek-lock-abandon", - "get-message-service-bus-queue-peek-lock-renew-sequential-convoy", - "get-message-service-bus-queue-peek-lock-renew", - "get-message-service-bus-topic-peek-lock-abandon", - "get-message-service-bus-topic-peek-lock-renew", - "get-sales-order-status-sap-teams-bapi", - "post-sap-goodsmovement-using-bapi", - "ingest-document-blob-openai-cosmos", - "ingest-doc-blob-document-intelligence-cosmos", - "ingest-doc-sharepoint-document-intelligence-cosmos", - "ingest-document-sharepoint-openai-cosmos", - "ingest-index-ai-azure-files-rag", - "ingest-index-ai-blob-storage-rag", - "ingest-index-ai-http-rag", - "ingest-index-ai-one-drive-business-rag", - "ingest-index-ai-sftp-rag", - "ingest-index-ai-sharepoint-rag", - "receive-request-send-response", - "send-alerts-business-process-tracking", - "sync-business-partner-sap-sharepoint-odata", - "sync-sales-order-sap-blob-storage-odata", - "try-catch", - "unblock-user-sap-teams-self-service-bapi", - "ingest-index-ai-azure-files-recurrence-rag", - "analyze-documents-ai-docintelligence", - "ingest-index-ai-azure-files-schedule-rag", - "ingest-index-ai-one-drive-schedule-rag", - "chat-with-documents-ai", - "accelerator-index-retrieve-ai-sharepoint-aisearch", - "accelerator-index-retrieve-resumes-sql", - "send-avs-logs-to-log-server", - "vectorize-onedriveforbusiness-aisearch-request", - "vectorize-onedrive-aisearch-request", - "vectorize-onedriveforbusiness-aisearch-schedule", - "vectorize-onedrive-aisearch-schedule", - "vectorize-sharepoint-aisearch-request", - "vectorize-sharepoint-aisearch-schedule", - "vectorize-azurefile-aisearch-request", - "vectorize-azurefile-aisearch-schedule", - "vectorize-amazons3-aisearch-request", - "vectorize-amazons3-aisearch-schedule", - "vectorize-dropbox-aisearch-request", - "vectorize-dropbox-aisearch-schedule", - "vectorize-sftp-aisearch-schedule", - "vectorize-sftp-aisearch-request", - "vectorize-azurequeue-aisearch-schedule", - "vectorize-azurequeue-aisearch-request", - "vectorize-servicebus-aisearch-schedule", - "tool-send-email-outlook", - "tool-get-weather-msn", - "tool-get-rows-dataverse", - "tool-get-rows-sql", - "tool-execute-query-sql", - "tool-call-uri-http" + "analyze-images-ai-rag", + "get-message-ibm-mq-queue-browse-lock-abort", + "get-message-service-bus-queue-peek-lock-abandon", + "get-message-service-bus-queue-peek-lock-renew-sequential-convoy", + "get-message-service-bus-queue-peek-lock-renew", + "get-message-service-bus-topic-peek-lock-abandon", + "get-message-service-bus-topic-peek-lock-renew", + "get-sales-order-status-sap-teams-bapi", + "post-sap-goodsmovement-using-bapi", + "ingest-document-blob-openai-cosmos", + "ingest-doc-blob-document-intelligence-cosmos", + "ingest-doc-sharepoint-document-intelligence-cosmos", + "ingest-document-sharepoint-openai-cosmos", + "ingest-index-ai-azure-files-rag", + "ingest-index-ai-blob-storage-rag", + "ingest-index-ai-http-rag", + "ingest-index-ai-one-drive-business-rag", + "ingest-index-ai-sftp-rag", + "ingest-index-ai-sharepoint-rag", + "receive-request-send-response", + "send-alerts-business-process-tracking", + "sync-business-partner-sap-sharepoint-odata", + "sync-sales-order-sap-blob-storage-odata", + "try-catch", + "unblock-user-sap-teams-self-service-bapi", + "ingest-index-ai-azure-files-recurrence-rag", + "analyze-documents-ai-docintelligence", + "ingest-index-ai-azure-files-schedule-rag", + "ingest-index-ai-one-drive-schedule-rag", + "chat-with-documents-ai", + "accelerator-index-retrieve-ai-sharepoint-aisearch", + "accelerator-index-retrieve-resumes-sql", + "send-avs-logs-to-log-server", + "vectorize-onedriveforbusiness-aisearch-request", + "vectorize-onedrive-aisearch-request", + "vectorize-onedriveforbusiness-aisearch-schedule", + "vectorize-onedrive-aisearch-schedule", + "vectorize-sharepoint-aisearch-request", + "vectorize-sharepoint-aisearch-schedule", + "vectorize-azurefile-aisearch-request", + "vectorize-azurefile-aisearch-schedule", + "vectorize-amazons3-aisearch-request", + "vectorize-amazons3-aisearch-schedule", + "vectorize-dropbox-aisearch-request", + "vectorize-dropbox-aisearch-schedule", + "vectorize-sftp-aisearch-schedule", + "vectorize-sftp-aisearch-request", + "vectorize-azurequeue-aisearch-schedule", + "vectorize-azurequeue-aisearch-request", + "vectorize-servicebus-aisearch-schedule", + "tool-send-email-outlook", + "tool-get-weather-msn", + "tool-get-rows-dataverse", + "tool-get-rows-sql", + "tool-execute-query-sql", + "tool-call-uri-http", + "aggregate-messages-servicebus-correlationid" ]