diff --git a/core/Extensions/subflow-test/grandchild-data-extension.json b/core/Extensions/subflow-test/grandchild-data-extension.json new file mode 100644 index 0000000..a7a9bf1 --- /dev/null +++ b/core/Extensions/subflow-test/grandchild-data-extension.json @@ -0,0 +1,31 @@ +{ + "key": "grandchild-data-extension", + "version": "1.0.0", + "domain": "core", + "flow": "sys-extensions", + "flowVersion": "1.0.0", + "tags": [ + "subflow-test", + "grandchild-extension", + "data-extension", + "longpolling-test", + "deep-nested-test" + ], + "attributes": { + "type": 2, + "scope": 1, + "task": { + "order": 1, + "task": { + "key": "test-http-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/GrandchildDataExtensionMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuU2NyaXB0aW5nOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CgovLy8gPHN1bW1hcnk+Ci8vLyBHcmFuZGNoaWxkIERhdGEgRXh0ZW5zaW9uIE1hcHBpbmcgLSBQcm92aWRlcyBhZGRpdGlvbmFsIGRhdGEgZm9yIGdyYW5kY2hpbGQgd29ya2Zsb3cgdmlldwovLy8gVGhpcyBleHRlbnNpb24gaXMgdXNlZCB0byB0ZXN0IGxvbmdwb2xsaW5nIGZ1bmN0aW9uYWxpdHkgZm9yIGRlZXBseSBuZXN0ZWQgd29ya2Zsb3cgdmlld3MuCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBHcmFuZGNoaWxkRGF0YUV4dGVuc2lvbk1hcHBpbmcgOiBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICB2YXIgaHR0cFRhc2sgPSB0YXNrIGFzIEh0dHBUYXNrOwogICAgICAgICAgICBpZiAoaHR0cFRhc2sgPT0gbnVsbCkKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgdGhyb3cgbmV3IEludmFsaWRPcGVyYXRpb25FeGNlcHRpb24oIlRhc2sgbXVzdCBiZSBhbiBIdHRwVGFzayIpOwogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBQcmVwYXJlIHJlcXVlc3QgYm9keSBmb3IgZXh0ZW5zaW9uIGRhdGEKICAgICAgICAgICAgdmFyIHJlcXVlc3RCb2R5ID0gbmV3CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGV4dGVuc2lvblR5cGUgPSAiZ3JhbmRjaGlsZC1kYXRhLWV4dGVuc2lvbiIsCiAgICAgICAgICAgICAgICBpbnN0YW5jZUlkID0gY29udGV4dC5JbnN0YW5jZT8uSWQsCiAgICAgICAgICAgICAgICB3b3JrZmxvd0tleSA9IGNvbnRleHQuV29ya2Zsb3c/LktleSwKICAgICAgICAgICAgICAgIHBhcmVudEluc3RhbmNlSWQgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy5wYXJlbnRJbnN0YW5jZUlkLAogICAgICAgICAgICAgICAgY2hpbGRJbnN0YW5jZUlkID0gY29udGV4dC5JbnN0YW5jZT8uRGF0YT8uY2hpbGRJbnN0YW5jZUlkLAogICAgICAgICAgICAgICAgbmVzdGluZ0xldmVsID0gMywKICAgICAgICAgICAgICAgIHJlcXVlc3RlZEF0ID0gRGF0ZVRpbWUuVXRjTm93CiAgICAgICAgICAgIH07CgogICAgICAgICAgICBodHRwVGFzay5TZXRCb2R5KHJlcXVlc3RCb2R5KTsKCiAgICAgICAgICAgIC8vIFNldCBoZWFkZXJzCiAgICAgICAgICAgIHZhciBoZWFkZXJzID0gbmV3IERpY3Rpb25hcnk8c3RyaW5nLCBzdHJpbmc/PgogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBbIkNvbnRlbnQtVHlwZSJdID0gImFwcGxpY2F0aW9uL2pzb24iLAogICAgICAgICAgICAgICAgWyJYLUV4dGVuc2lvbi1UeXBlIl0gPSAiZ3JhbmRjaGlsZC1kYXRhIiwKICAgICAgICAgICAgICAgIFsiWC1OZXN0aW5nLUxldmVsIl0gPSAiMyIsCiAgICAgICAgICAgICAgICBbIlgtUmVxdWVzdC1JZCJdID0gR3VpZC5OZXdHdWlkKCkuVG9TdHJpbmcoKQogICAgICAgICAgICB9OwogICAgICAgICAgICBodHRwVGFzay5TZXRIZWFkZXJzKGhlYWRlcnMpOwoKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICByZXR1cm4gVGFzay5Gcm9tUmVzdWx0KG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiZ3JhbmRjaGlsZC1leHRlbnNpb24taW5wdXQtZXJyb3IiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldyB7IGVycm9yID0gZXguTWVzc2FnZSB9CiAgICAgICAgICAgIH0pOwogICAgICAgIH0KICAgIH0KCiAgICBwdWJsaWMgYXN5bmMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJncmFuZGNoaWxkLWV4dGVuc2lvbi1zdWNjZXNzIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBncmFuZGNoaWxkRXh0ZW5zaW9uID0gbmV3CiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICBleHRlbnNpb25OYW1lID0gImdyYW5kY2hpbGQtZGF0YS1leHRlbnNpb24iLAogICAgICAgICAgICAgICAgICAgICAgICBzb3VyY2UgPSAiZ3JhbmRjaGlsZC13b3JrZmxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgIG5lc3RpbmdMZXZlbCA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgIGxvYWRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LAogICAgICAgICAgICAgICAgICAgICAgICBkYXRhID0gbmV3CiAgICAgICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyYW5kY2hpbGRDb25maWcgPSBuZXcKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpc1N1YmZsb3cgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzRGVlcE5lc3RlZCA9IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzUGFyZW50ID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb21wbGV0aW9uUmVxdWlyZWQgPSB0cnVlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JhbmRjaGlsZE1ldGFkYXRhID0gbmV3CiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd29ya2Zsb3dUeXBlID0gImRlZXAtc3ViZmxvdyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcGFyZW50V29ya2Zsb3cgPSAic3ViZmxvdy12aWV3LXRlc3QtY2hpbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyYW5kcGFyZW50V29ya2Zsb3cgPSAic3ViZmxvdy12aWV3LXRlc3QtcGFyZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHRlbnNpb25WZXJzaW9uID0gIjEuMC4wIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhpZXJhcmNoeVBhdGggPSBuZXcKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbDEgPSAic3ViZmxvdy12aWV3LXRlc3QtcGFyZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbDIgPSAic3ViZmxvdy12aWV3LXRlc3QtY2hpbGQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVsMyA9ICJzdWJmbG93LXZpZXctdGVzdC1ncmFuZGNoaWxkIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgIFRhZ3MgPSBuZXdbXSB7ICJzdWJmbG93LXRlc3QiLCAiZ3JhbmRjaGlsZC1leHRlbnNpb24iLCAibG9uZ3BvbGxpbmciLCAiZGVlcC1uZXN0ZWQiIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImdyYW5kY2hpbGQtZXh0ZW5zaW9uLWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgIH0KfQo=" + } + } + } +} \ No newline at end of file diff --git a/core/Extensions/subflow-test/src/GrandchildDataExtensionMapping.csx b/core/Extensions/subflow-test/src/GrandchildDataExtensionMapping.csx new file mode 100644 index 0000000..7639345 --- /dev/null +++ b/core/Extensions/subflow-test/src/GrandchildDataExtensionMapping.csx @@ -0,0 +1,110 @@ +using System; +using System.Threading.Tasks; +using BBT.Workflow.Scripting; +using BBT.Workflow.Definitions; + +/// +/// Grandchild Data Extension Mapping - Provides additional data for grandchild workflow view +/// This extension is used to test longpolling functionality for deeply nested workflow views. +/// +public class GrandchildDataExtensionMapping : IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + try + { + var httpTask = task as HttpTask; + if (httpTask == null) + { + throw new InvalidOperationException("Task must be an HttpTask"); + } + + // Prepare request body for extension data + var requestBody = new + { + extensionType = "grandchild-data-extension", + instanceId = context.Instance?.Id, + workflowKey = context.Workflow?.Key, + parentInstanceId = context.Instance?.Data?.parentInstanceId, + childInstanceId = context.Instance?.Data?.childInstanceId, + nestingLevel = 3, + requestedAt = DateTime.UtcNow + }; + + httpTask.SetBody(requestBody); + + // Set headers + var headers = new Dictionary + { + ["Content-Type"] = "application/json", + ["X-Extension-Type"] = "grandchild-data", + ["X-Nesting-Level"] = "3", + ["X-Request-Id"] = Guid.NewGuid().ToString() + }; + httpTask.SetHeaders(headers); + + return Task.FromResult(new ScriptResponse()); + } + catch (Exception ex) + { + return Task.FromResult(new ScriptResponse + { + Key = "grandchild-extension-input-error", + Data = new { error = ex.Message } + }); + } + } + + public async Task OutputHandler(ScriptContext context) + { + try + { + return new ScriptResponse + { + Key = "grandchild-extension-success", + Data = new + { + grandchildExtension = new + { + extensionName = "grandchild-data-extension", + source = "grandchild-workflow", + nestingLevel = 3, + loadedAt = DateTime.UtcNow, + data = new + { + grandchildConfig = new + { + isSubflow = true, + isDeepNested = true, + blocksParent = true, + completionRequired = true + }, + grandchildMetadata = new + { + workflowType = "deep-subflow", + parentWorkflow = "subflow-view-test-child", + grandparentWorkflow = "subflow-view-test-parent", + extensionVersion = "1.0.0" + }, + hierarchyPath = new + { + level1 = "subflow-view-test-parent", + level2 = "subflow-view-test-child", + level3 = "subflow-view-test-grandchild" + } + } + } + }, + Tags = new[] { "subflow-test", "grandchild-extension", "longpolling", "deep-nested" } + }; + } + catch (Exception ex) + { + return new ScriptResponse + { + Key = "grandchild-extension-error", + Data = new { error = ex.Message } + }; + } + } +} diff --git a/core/Tasks/subflow-test/start-busy-subprocess-task.json b/core/Tasks/subflow-test/start-busy-subprocess-task.json new file mode 100644 index 0000000..2f75752 --- /dev/null +++ b/core/Tasks/subflow-test/start-busy-subprocess-task.json @@ -0,0 +1,22 @@ +{ + "key": "start-busy-subprocess-task", + "flow": "sys-tasks", + "domain": "core", + "version": "1.0.0", + "flowVersion": "1.0.0", + "tags": [ + "subflow-test", + "subprocess", + "busy-state", + "trigger-task" + ], + "attributes": { + "type": "11", + "config": { + "domain": "core", + "flow": "sys-flows", + "key": "busy-subprocess-workflow", + "version": "1.0.0" + } + } +} diff --git a/core/Tasks/subflow-test/trigger-busy-completion-task.json b/core/Tasks/subflow-test/trigger-busy-completion-task.json new file mode 100644 index 0000000..f5c8a5d --- /dev/null +++ b/core/Tasks/subflow-test/trigger-busy-completion-task.json @@ -0,0 +1,22 @@ +{ + "key": "trigger-busy-completion-task", + "flow": "sys-tasks", + "domain": "core", + "version": "1.0.0", + "flowVersion": "1.0.0", + "tags": [ + "subflow-test", + "direct-trigger", + "busy-state", + "trigger-task" + ], + "attributes": { + "type": "12", + "config": { + "domain": "core", + "flow": "sys-flows", + "transitionName": "complete-busy-state", + "body": {} + } + } +} diff --git a/core/Views/subflow-test/grandchild-workflow-view.json b/core/Views/subflow-test/grandchild-workflow-view.json new file mode 100644 index 0000000..62bd4d3 --- /dev/null +++ b/core/Views/subflow-test/grandchild-workflow-view.json @@ -0,0 +1,29 @@ +{ + "key": "grandchild-workflow-view", + "version": "1.0.0", + "domain": "core", + "flow": "sys-views", + "flowVersion": "1.0.0", + "tags": [ + "subflow-test", + "grandchild-workflow", + "view", + "longpolling-test", + "deep-nested-test" + ], + "attributes": { + "type": 1, + "labels": [ + { + "language": "en-US", + "label": "Grandchild Workflow View" + }, + { + "language": "tr-TR", + "label": "Torun Akış Görünümü" + } + ], + "display": "full-page", + "content": "{\"type\":\"form\",\"title\":{\"en-US\":\"Grandchild Workflow - Active (Level 3)\",\"tr-TR\":\"Torun Akış - Aktif (Seviye 3)\"},\"description\":{\"en-US\":\"This is the grandchild (nested subflow) workflow view. This is the deepest level of the subflow hierarchy. Complete this workflow to continue the child.\",\"tr-TR\":\"Bu torun (iç içe alt akış) görünümüdür. Bu alt akış hiyerarşisinin en derin seviyesidir. Alt akışın devam etmesi için bu akışı tamamlayın.\"},\"fields\":[{\"key\":\"grandchildStatus\",\"type\":\"text\",\"label\":{\"en-US\":\"Grandchild Status\",\"tr-TR\":\"Torun Akış Durumu\"},\"readonly\":true,\"defaultValue\":\"active\"},{\"key\":\"nestingLevel\",\"type\":\"text\",\"label\":{\"en-US\":\"Nesting Level\",\"tr-TR\":\"İç İçe Seviye\"},\"readonly\":true,\"defaultValue\":\"3 (Parent > Child > Grandchild)\"},{\"key\":\"grandchildInfo\",\"type\":\"info\",\"label\":{\"en-US\":\"Grandchild Info\",\"tr-TR\":\"Torun Akış Bilgisi\"},\"content\":{\"en-US\":\"This view includes grandchild extension data for deep nested longpolling test.\",\"tr-TR\":\"Bu görünüm derin iç içe longpolling testi için torun akış extension verisini içerir.\"}}],\"buttons\":[{\"key\":\"complete\",\"label\":{\"en-US\":\"Complete Grandchild Workflow\",\"tr-TR\":\"Torun Akışı Tamamla\"},\"style\":\"success\",\"action\":\"submit\",\"transition\":\"complete-grandchild\"}]}" + } +} diff --git a/core/Workflows/subflow-test/.meta/busy-subprocess-workflow.diagram.json b/core/Workflows/subflow-test/.meta/busy-subprocess-workflow.diagram.json new file mode 100644 index 0000000..bd62b40 --- /dev/null +++ b/core/Workflows/subflow-test/.meta/busy-subprocess-workflow.diagram.json @@ -0,0 +1,74 @@ +{ + "workflow": "busy-subprocess-workflow", + "version": "1.0.0", + "diagram": { + "nodes": [ + { + "id": "start", + "type": "start", + "label": "Start SubProcess", + "position": { "x": 100, "y": 100 } + }, + { + "id": "processing", + "type": "state", + "label": "Processing", + "stateType": "initial", + "subType": "normal", + "position": { "x": 300, "y": 100 } + }, + { + "id": "trigger-parent", + "type": "state", + "label": "Trigger Parent", + "stateType": "intermediate", + "subType": "normal", + "description": "Uses DirectTriggerTask to trigger parent workflow", + "position": { "x": 500, "y": 100 } + }, + { + "id": "completed", + "type": "state", + "label": "Completed", + "stateType": "final", + "subType": "success", + "position": { "x": 700, "y": 100 } + } + ], + "edges": [ + { + "id": "start-to-processing", + "source": "start", + "target": "processing", + "label": "start-subprocess", + "type": "start-transition" + }, + { + "id": "processing-to-trigger", + "source": "processing", + "target": "trigger-parent", + "label": "complete-processing (auto)", + "type": "auto-transition", + "description": "Always true rule" + }, + { + "id": "trigger-to-completed", + "source": "trigger-parent", + "target": "completed", + "label": "finish-subprocess (auto)", + "type": "auto-transition", + "description": "Always true rule" + } + ] + }, + "metadata": { + "description": "Busy state subprocess workflow - runs independently and triggers parent when complete", + "purpose": "Test busy state (subType: 5) behavior with subprocess trigger pattern", + "features": [ + "Independent subprocess execution", + "DirectTriggerTask to trigger parent workflow", + "Automatic state transitions", + "Fire-and-forget pattern" + ] + } +} diff --git a/core/Workflows/subflow-test/.meta/subflow-view-test-child.diagram.json b/core/Workflows/subflow-test/.meta/subflow-view-test-child.diagram.json index bc2239a..ab02bcf 100644 --- a/core/Workflows/subflow-test/.meta/subflow-view-test-child.diagram.json +++ b/core/Workflows/subflow-test/.meta/subflow-view-test-child.diagram.json @@ -5,8 +5,8 @@ "y": 100 }, "child-completed": { - "x": 496, - "y": 100 + "x": 551.6088888888888, + "y": 96.37333333333333 }, "__start__": { "x": -296, diff --git a/core/Workflows/subflow-test/.meta/subflow-view-test-grandchild.diagram.json b/core/Workflows/subflow-test/.meta/subflow-view-test-grandchild.diagram.json new file mode 100644 index 0000000..dc7fa73 --- /dev/null +++ b/core/Workflows/subflow-test/.meta/subflow-view-test-grandchild.diagram.json @@ -0,0 +1,20 @@ +{ + "nodePos": { + "grandchild-active": { + "x": 100, + "y": 100 + }, + "grandchild-completed": { + "x": 670, + "y": 36 + }, + "__start__": { + "x": -296, + "y": 100 + }, + "grandchild-after-busy": { + "x": 126, + "y": -162 + } + } +} \ No newline at end of file diff --git a/core/Workflows/subflow-test/.meta/subflow-view-test-parent.diagram.json b/core/Workflows/subflow-test/.meta/subflow-view-test-parent.diagram.json new file mode 100644 index 0000000..aee3fb7 --- /dev/null +++ b/core/Workflows/subflow-test/.meta/subflow-view-test-parent.diagram.json @@ -0,0 +1,24 @@ +{ + "nodePos": { + "parent-initial": { + "x": 100, + "y": 100 + }, + "waiting-for-subflow": { + "x": 496, + "y": 100 + }, + "parent-after-subflow": { + "x": 892, + "y": 100 + }, + "parent-completed": { + "x": 1288, + "y": 100 + }, + "__start__": { + "x": -296, + "y": 100 + } + } +} \ No newline at end of file diff --git a/core/Workflows/subflow-test/BUSY-STATE-TEST-README.md b/core/Workflows/subflow-test/BUSY-STATE-TEST-README.md new file mode 100644 index 0000000..a637b4f --- /dev/null +++ b/core/Workflows/subflow-test/BUSY-STATE-TEST-README.md @@ -0,0 +1,226 @@ +# Busy State Test Implementation + +## Overview + +This document describes the implementation of the Busy state test feature in the grandchild workflow. The test demonstrates the new `subType: 5` (Busy) state functionality. + +## New State subType Values + +- **Busy: 5** - State is busy processing, waiting for external trigger +- **Human: 6** - State is waiting for human interaction + +## Test Objective + +Test the Busy state behavior where: +1. A workflow enters a Busy state (`subType: 5`) +2. The state launches a subprocess that runs independently +3. The workflow waits in Busy state for the subprocess to complete +4. The subprocess triggers a transition on the parent workflow when complete +5. The parent workflow exits Busy state and continues normal flow + +## Expected Behavior + +``` +State enters → Status: BUSY → Waits → SubProcess completes → SubProcess pings parent → Transition occurs → State changes +``` + +## Components Created + +### 1. Tasks + +#### `start-busy-subprocess-task.json` +- **Type**: 11 (SubProcessTask) +- **Purpose**: Launches the busy subprocess workflow +- **Location**: `core/Tasks/subflow-test/start-busy-subprocess-task.json` + +#### `trigger-busy-completion-task.json` +- **Type**: 12 (DirectTriggerTask) +- **Purpose**: Triggers transition on parent workflow from subprocess +- **Location**: `core/Tasks/subflow-test/trigger-busy-completion-task.json` + +### 2. Workflows + +#### `busy-subprocess-workflow.json` +- **Purpose**: Independent subprocess that runs while parent is in Busy state +- **States**: + - `processing` (Initial) - Subprocess starts and processes + - `trigger-parent` (Intermediate) - Triggers parent workflow transition + - `completed` (Final) - Subprocess completes +- **Location**: `core/Workflows/subflow-test/busy-subprocess-workflow.json` + +### 3. Mappings + +#### `StartBusySubprocessMapping.csx` +- **Purpose**: Configures and launches the subprocess +- **Task**: SubProcessTask (Type 11) +- **Actions**: + - Sets subprocess domain, flow, and key + - Passes parent instance ID to subprocess + - Prepares subprocess initialization data + +#### `BusySubprocessStartMapping.csx` +- **Purpose**: Initializes subprocess instance +- **Actions**: + - Stores parent instance information + - Prepares subprocess data + +#### `TriggerParentBusyCompletionMapping.csx` +- **Purpose**: Triggers parent workflow transition from subprocess +- **Task**: DirectTriggerTask (Type 12) +- **Actions**: + - Sets target instance (parent workflow) + - Sets transition name (`complete-busy-state`) + - Prepares transition payload with completion data + +### 4. Workflow States + +#### Modified `subflow-view-test-grandchild.json` + +Added new states: + +**State: `grandchild-busy`** +- **Type**: Intermediate (2) +- **SubType**: Busy (5) ⚠️ NEW FEATURE +- **OnEntry**: Launches subprocess via `start-busy-subprocess-task` +- **Transition**: `complete-busy-state` - Manual transition triggered by subprocess + +**State: `grandchild-after-busy`** +- **Type**: Intermediate (2) +- **SubType**: Normal (0) +- **Purpose**: Continues workflow after busy state completes + +## Workflow Flow + +```mermaid +graph TD + A[grandchild-active] -->|enter-busy-state| B[grandchild-busy
subType: 5 - BUSY] + B -->|onEntry: Launch SubProcess| C[busy-subprocess-workflow] + C -->|processing| D[trigger-parent state] + D -->|DirectTriggerTask| E[Trigger: complete-busy-state] + E -->|Transition| F[grandchild-after-busy] + F -->|complete-grandchild| G[grandchild-completed] +``` + +## Testing Steps + +See `subflow-view-test.http` for complete test sequence. Key steps: + +1. **Step 17**: Trigger `enter-busy-state` transition +2. **Step 18**: Verify state is `grandchild-busy` with `subType: 5` +3. **Step 19**: Check data shows subprocess launched +4. **Step 20**: Wait ~5-10 seconds for subprocess to complete +5. **Step 21**: Verify state automatically changed to `grandchild-after-busy` +6. **Step 22**: Check data shows subprocess completion and trigger info +7. **Step 23**: Complete grandchild workflow normally + +## API Endpoints Used + +### Start Busy State +```http +PATCH http://localhost:4201/api/v1/core/workflows/subflow-view-test-grandchild/instances/{instanceId}/transitions/enter-busy-state +``` + +### Check State (Should show BUSY status) +```http +GET http://localhost:4201/api/v1/core/workflows/subflow-view-test-grandchild/instances/{instanceId}/functions/state +``` + +### Get Instance Data +```http +GET http://localhost:4201/api/v1/core/workflows/subflow-view-test-grandchild/instances/{instanceId}/functions/data +``` + +## Task Types Reference + +### SubProcessTask (Type 11) +- Fire-and-forget subprocess launch +- Subprocess runs independently +- Parent doesn't wait for subprocess completion +- Used in: `start-busy-subprocess-task` + +### DirectTriggerTask (Type 12) +- Triggers specific transition on existing workflow instance +- Requires target instance ID and transition name +- Used in: `trigger-busy-completion-task` + +## Expected Data Flow + +### When Entering Busy State + +```json +{ + "subprocessLaunched": true, + "subprocessInstanceId": "uuid", + "launchedAt": "timestamp", + "status": "BUSY" +} +``` + +### When SubProcess Triggers Parent + +```json +{ + "transitionTriggered": true, + "triggeredAt": "timestamp", + "parentNewState": "grandchild-after-busy", + "status": "PARENT_TRANSITION_SUCCESS", + "result": { + "subprocessCompleted": true, + "busyStateTestPassed": true + } +} +``` + +## Known Issues + +### Schema Validation + +The `subType: 5` value currently fails schema validation because it's a new feature under development: + +``` +Schema validation failed for workflow: +/attributes/states/1/subType: must be equal to one of the allowed values (line 74) ({"allowedValues":[0,1,2,3,4]}) +``` + +**Resolution**: The `@burgan-tech/vnext-schema` package needs to be updated to include: +- `Busy: 5` +- `Human: 6` + +Once the schema is updated, validation will pass. + +## Files Modified/Created + +### Created Files +- `core/Tasks/subflow-test/start-busy-subprocess-task.json` +- `core/Tasks/subflow-test/trigger-busy-completion-task.json` +- `core/Workflows/subflow-test/busy-subprocess-workflow.json` +- `core/Workflows/subflow-test/src/StartBusySubprocessMapping.csx` +- `core/Workflows/subflow-test/src/BusySubprocessStartMapping.csx` +- `core/Workflows/subflow-test/src/TriggerParentBusyCompletionMapping.csx` + +### Modified Files +- `core/Workflows/subflow-test/subflow-view-test-grandchild.json` - Added busy state and transitions +- `core/Workflows/subflow-test/subflow-view-test.http` - Added busy state test steps + +## Architecture Benefits + +1. **State Awareness**: Runtime knows the state is busy (subType: 5) +2. **Status Tracking**: Can monitor busy states across all workflows +3. **Independent Processing**: Subprocess runs without blocking +4. **Automatic Transition**: Subprocess triggers parent when ready +5. **Flexible Pattern**: Can be used for any long-running operation + +## Use Cases + +This pattern is useful for: +- Long-running background processes +- External API calls with callbacks +- Async processing workflows +- Human approval processes (with subType: 6) +- Queue-based processing + +## Related Documentation + +- vNext Runtime Docs: TriggerTask types (SubProcess, DirectTrigger) +- State lifecycle and subTypes +- Transition trigger types diff --git a/core/Workflows/subflow-test/busy-subprocess-workflow.json b/core/Workflows/subflow-test/busy-subprocess-workflow.json new file mode 100644 index 0000000..794a2f5 --- /dev/null +++ b/core/Workflows/subflow-test/busy-subprocess-workflow.json @@ -0,0 +1,191 @@ +{ + "key": "busy-subprocess-workflow", + "flow": "sys-flows", + "domain": "core", + "version": "1.0.0", + "flowVersion": "1.0.0", + "tags": [ + "subflow-test", + "subprocess", + "busy-state-test", + "background-process" + ], + "attributes": { + "type": "S", + "timeout": null, + "labels": [ + { + "language": "en-US", + "label": "Busy State SubProcess Workflow" + }, + { + "language": "tr-TR", + "label": "Meşgul Durumu Alt İşlem Akışı" + } + ], + "functions": [], + "features": [], + "extensions": [], + "sharedTransitions": [], + "startTransition": { + "key": "start-subprocess", + "target": "processing", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Start SubProcess" + }, + { + "language": "tr-TR", + "label": "Alt İşlemi Başlat" + } + ], + "onExecutionTasks": [ + { + "order": 1, + "task": { + "key": "script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/BusySubprocessStartMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gQnVzeSBTdWJQcm9jZXNzIFN0YXJ0IE1hcHBpbmcgLSBJbml0aWFsaXplcyB0aGUgc3VicHJvY2VzcyB0aGF0IHdpbGwgcnVuIHdoaWxlIHBhcmVudCBpcyBidXN5Ci8vLyBUaGlzIHN1YnByb2Nlc3Mgd2lsbCBjb21wbGV0ZSBpdHMgd29yayBhbmQgdHJpZ2dlciB0aGUgcGFyZW50IHRvIGV4aXQgYnVzeSBzdGF0ZQovLy8gPC9zdW1tYXJ5PgpwdWJsaWMgY2xhc3MgQnVzeVN1YnByb2Nlc3NTdGFydE1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIkJ1c3lTdWJwcm9jZXNzU3RhcnRNYXBwaW5nIC0gU3RhcnRpbmcgYnVzeSBzdGF0ZSBzdWJwcm9jZXNzIik7CiAgICAgICAgICAgIAogICAgICAgICAgICB2YXIgaW5wdXREYXRhID0gY29udGV4dC5JbnN0YW5jZT8uRGF0YTsKICAgICAgICAgICAgCiAgICAgICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImJ1c3ktc3VicHJvY2Vzcy1zdGFydGVkIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBzdWJwcm9jZXNzV29ya2Zsb3dJZCA9IGNvbnRleHQuV29ya2Zsb3c/LktleSwKICAgICAgICAgICAgICAgICAgICBzdWJwcm9jZXNzSW5zdGFuY2VJZCA9IGNvbnRleHQuSW5zdGFuY2U/LklkLAogICAgICAgICAgICAgICAgICAgIHBhcmVudEluc3RhbmNlSWQgPSBpbnB1dERhdGE/LmdyYW5kY2hpbGRJbnN0YW5jZUlkLAogICAgICAgICAgICAgICAgICAgIHBhcmVudFdvcmtmbG93SWQgPSBpbnB1dERhdGE/LmdyYW5kY2hpbGRXb3JrZmxvd0lkLAogICAgICAgICAgICAgICAgICAgIHN0YXJ0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdywKICAgICAgICAgICAgICAgICAgICBzdGF0dXMgPSAicHJvY2Vzc2luZyIsCiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9ICJCdXN5IHN0YXRlIHN1YnByb2Nlc3Mgc3RhcnRlZCAtIHdpbGwgdHJpZ2dlciBwYXJlbnQgd2hlbiBjb21wbGV0ZSIsCiAgICAgICAgICAgICAgICAgICAgcHJvY2Vzc2luZ1Rhc2sgPSBuZXcKICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgIHRhc2tUeXBlID0gImJhY2tncm91bmQtcHJvY2Vzc2luZyIsCiAgICAgICAgICAgICAgICAgICAgICAgIGV4cGVjdGVkRHVyYXRpb24gPSAiNS0xMCBzZWNvbmRzIiwKICAgICAgICAgICAgICAgICAgICAgICAgd2lsbFRyaWdnZXJQYXJlbnQgPSB0cnVlCiAgICAgICAgICAgICAgICAgICAgfQogICAgICAgICAgICAgICAgfSwKICAgICAgICAgICAgICAgIFRhZ3MgPSBuZXdbXSB7ICJidXN5LXN1YnByb2Nlc3MiLCAic3RhcnRlZCIsICJiYWNrZ3JvdW5kLXRhc2siIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCJCdXN5U3VicHJvY2Vzc1N0YXJ0TWFwcGluZyAtIEVycm9yOiB7MH0iLCBhcmdzOiBuZXcgb2JqZWN0P1tdIHsgZXguTWVzc2FnZSB9KTsKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiYnVzeS1zdWJwcm9jZXNzLXN0YXJ0LWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgIH0KfQo=" + } + } + ] + }, + "states": [ + { + "key": "processing", + "stateType": 1, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Processing" + }, + { + "language": "tr-TR", + "label": "İşleniyor" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "complete-processing", + "target": "trigger-parent", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Complete Processing (Auto)" + }, + { + "language": "tr-TR", + "label": "İşlemi Tamamla (Otomatik)" + } + ], + "schema": null, + "rule": { + "location": "./src/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCi8vLyA8c3VtbWFyeT4KLy8vIEFsd2F5cyBUcnVlIFJ1bGUgLSBVc2VkIGZvciBhdXRvIHRyYW5zaXRpb25zIHRoYXQgc2hvdWxkIGFsd2F5cyBwcm9jZWVkCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBhc3luYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiB0cnVlOwogICAgfQp9Cgo=" + }, + "timer": null, + "view": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "trigger-parent", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Trigger Parent Workflow" + }, + { + "language": "tr-TR", + "label": "Üst Akışı Tetikle" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "trigger-busy-completion-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/TriggerParentBusyCompletionMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gVHJpZ2dlciBQYXJlbnQgQnVzeSBDb21wbGV0aW9uIE1hcHBpbmcgLSBUcmlnZ2VycyB0aGUgcGFyZW50IHdvcmtmbG93IHRvIGV4aXQgYnVzeSBzdGF0ZQovLy8gVGhpcyBtYXBwaW5nIHVzZXMgRGlyZWN0VHJpZ2dlclRhc2sgdG8gdHJpZ2dlciB0aGUgJ2NvbXBsZXRlLWJ1c3ktc3RhdGUnIHRyYW5zaXRpb24gb24gdGhlIHBhcmVudAovLy8gPC9zdW1tYXJ5PgpwdWJsaWMgY2xhc3MgVHJpZ2dlclBhcmVudEJ1c3lDb21wbGV0aW9uTWFwcGluZyA6IFNjcmlwdEJhc2UsIElNYXBwaW5nCnsKICAgIHB1YmxpYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoV29ya2Zsb3dUYXNrIHRhc2ssIFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIHZhciBkaXJlY3RUcmlnZ2VyVGFzayA9IHRhc2sgYXMgRGlyZWN0VHJpZ2dlclRhc2s7CiAgICAgICAgICAgIAogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiVHJpZ2dlclBhcmVudEJ1c3lDb21wbGV0aW9uTWFwcGluZyAtIFByZXBhcmluZyB0byB0cmlnZ2VyIHBhcmVudCB3b3JrZmxvdyBidXN5IGNvbXBsZXRpb24iKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIEdldCBwYXJlbnQgaW5zdGFuY2UgSUQgZnJvbSBzdWJwcm9jZXNzIGlucHV0IGRhdGEKICAgICAgICAgICAgdmFyIHBhcmVudEluc3RhbmNlSWQgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy5wYXJlbnRJbnN0YW5jZUlkOwogICAgICAgICAgICAKICAgICAgICAgICAgaWYgKHN0cmluZy5Jc051bGxPckVtcHR5KHBhcmVudEluc3RhbmNlSWQpKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBMb2dFcnJvcigiVHJpZ2dlclBhcmVudEJ1c3lDb21wbGV0aW9uTWFwcGluZyAtIFBhcmVudCBpbnN0YW5jZSBJRCBub3QgZm91bmQiKTsKICAgICAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgS2V5ID0gInRyaWdnZXItZXJyb3IiLAogICAgICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9ICJQYXJlbnQgaW5zdGFuY2UgSUQgbm90IGZvdW5kIiB9CiAgICAgICAgICAgICAgICB9KTsKICAgICAgICAgICAgfQogICAgICAgICAgICAKICAgICAgICAgICAgLy8gU2V0IHRoZSB0YXJnZXQgaW5zdGFuY2UgYW5kIHRyYW5zaXRpb24KICAgICAgICAgICAgZGlyZWN0VHJpZ2dlclRhc2suU2V0RG9tYWluKCJjb3JlIik7CiAgICAgICAgICAgIGRpcmVjdFRyaWdnZXJUYXNrLlNldEZsb3coInN5cy1mbG93cyIpOwogICAgICAgICAgICBkaXJlY3RUcmlnZ2VyVGFzay5TZXRJbnN0YW5jZShwYXJlbnRJbnN0YW5jZUlkKTsKICAgICAgICAgICAgZGlyZWN0VHJpZ2dlclRhc2suU2V0VHJhbnNpdGlvbk5hbWUoImNvbXBsZXRlLWJ1c3ktc3RhdGUiKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFByZXBhcmUgdHJhbnNpdGlvbiBwYXlsb2FkCiAgICAgICAgICAgIGRpcmVjdFRyaWdnZXJUYXNrLlNldEJvZHkobmV3CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIHN1YnByb2Nlc3NDb21wbGV0ZWQgPSB0cnVlLAogICAgICAgICAgICAgICAgc3VicHJvY2Vzc0luc3RhbmNlSWQgPSBjb250ZXh0Lkluc3RhbmNlPy5JZCwKICAgICAgICAgICAgICAgIGNvbXBsZXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LAogICAgICAgICAgICAgICAgcmVzdWx0ID0gbmV3CiAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgc3VjY2VzcyA9IHRydWUsCiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9ICJTdWJwcm9jZXNzIGNvbXBsZXRlZCBzdWNjZXNzZnVsbHksIHRyaWdnZXJpbmcgcGFyZW50IGJ1c3kgc3RhdGUgY29tcGxldGlvbiIsCiAgICAgICAgICAgICAgICAgICAgcHJvY2Vzc2luZ1RpbWUgPSAiNSBzZWNvbmRzIiwKICAgICAgICAgICAgICAgICAgICBkYXRhID0gbmV3CiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICBidXN5U3RhdGVUZXN0UGFzc2VkID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgc3VicHJvY2Vzc0V4ZWN1dGVkID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgcGFyZW50VHJpZ2dlcmVkID0gdHJ1ZQogICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSk7CiAgICAgICAgICAgIAogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiVHJpZ2dlclBhcmVudEJ1c3lDb21wbGV0aW9uTWFwcGluZyAtIFRyaWdnZXJpbmcgdHJhbnNpdGlvbiAnY29tcGxldGUtYnVzeS1zdGF0ZScgb24gcGFyZW50IGluc3RhbmNlIHswfSIsIGFyZ3M6IG5ldyBvYmplY3Q/W10geyBwYXJlbnRJbnN0YW5jZUlkIH0pOwogICAgICAgICAgICAKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgRGF0YSA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGEKICAgICAgICAgICAgfSk7CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigiVHJpZ2dlclBhcmVudEJ1c3lDb21wbGV0aW9uTWFwcGluZyAtIEVycm9yOiB7MH0iLCBhcmdzOiBuZXcgb2JqZWN0P1tdIHsgZXguTWVzc2FnZSB9KTsKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gInRyaWdnZXItZXJyb3IiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldyB7IGVycm9yID0gZXguTWVzc2FnZSB9CiAgICAgICAgICAgIH0pOwogICAgICAgIH0KICAgIH0KCiAgICBwdWJsaWMgYXN5bmMgVGFzazxTY3JpcHRSZXNwb25zZT4gT3V0cHV0SGFuZGxlcihTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgdHJ5CiAgICAgICAgewogICAgICAgICAgICB2YXIgcmVzcG9uc2UgPSBuZXcgU2NyaXB0UmVzcG9uc2UoKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIGlmIChjb250ZXh0LkJvZHk/LmlzU3VjY2VzcyA9PSB0cnVlKQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiVHJpZ2dlclBhcmVudEJ1c3lDb21wbGV0aW9uTWFwcGluZyAtIFBhcmVudCB0cmFuc2l0aW9uIHRyaWdnZXJlZCBzdWNjZXNzZnVsbHkiKTsKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgcmVzcG9uc2UuRGF0YSA9IG5ldwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIHRyYW5zaXRpb25UcmlnZ2VyZWQgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgIHRyaWdnZXJlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LAogICAgICAgICAgICAgICAgICAgIHBhcmVudE5ld1N0YXRlID0gY29udGV4dC5Cb2R5LmRhdGE/LmN1cnJlbnRTdGF0ZSwKICAgICAgICAgICAgICAgICAgICBzdGF0dXMgPSAiUEFSRU5UX1RSQU5TSVRJT05fU1VDQ0VTUyIsCiAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9ICJQYXJlbnQgd29ya2Zsb3cgc3VjY2Vzc2Z1bGx5IHRyYW5zaXRpb25lZCBmcm9tIGJ1c3kgc3RhdGUiCiAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICB9CiAgICAgICAgICAgIGVsc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgTG9nRXJyb3IoIlRyaWdnZXJQYXJlbnRCdXN5Q29tcGxldGlvbk1hcHBpbmcgLSBQYXJlbnQgdHJhbnNpdGlvbiBmYWlsZWQ6IHswfSIsIGFyZ3M6IG5ldyBvYmplY3Q/W10geyBjb250ZXh0LkJvZHk/LmVycm9yTWVzc2FnZSB9KTsKICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgcmVzcG9uc2UuRGF0YSA9IG5ldwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIHRyYW5zaXRpb25UcmlnZ2VyZWQgPSBmYWxzZSwKICAgICAgICAgICAgICAgICAgICBlcnJvciA9IGNvbnRleHQuQm9keT8uZXJyb3JNZXNzYWdlLAogICAgICAgICAgICAgICAgICAgIHN0YXR1cyA9ICJQQVJFTlRfVFJBTlNJVElPTl9GQUlMRUQiCiAgICAgICAgICAgICAgICB9OwogICAgICAgICAgICB9CiAgICAgICAgICAgIAogICAgICAgICAgICByZXR1cm4gcmVzcG9uc2U7CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigiVHJpZ2dlclBhcmVudEJ1c3lDb21wbGV0aW9uTWFwcGluZyAtIE91dHB1dCBoYW5kbGVyIGVycm9yOiB7MH0iLCBhcmdzOiBuZXcgb2JqZWN0P1tdIHsgZXguTWVzc2FnZSB9KTsKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAib3V0cHV0LWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgIH0KfQo=" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "finish-subprocess", + "target": "completed", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Finish SubProcess (Auto)" + }, + { + "language": "tr-TR", + "label": "Alt İşlemi Bitir (Otomatik)" + } + ], + "schema": null, + "rule": { + "location": "./src/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCi8vLyA8c3VtbWFyeT4KLy8vIEFsd2F5cyBUcnVlIFJ1bGUgLSBVc2VkIGZvciBhdXRvIHRyYW5zaXRpb25zIHRoYXQgc2hvdWxkIGFsd2F5cyBwcm9jZWVkCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBhc3luYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiB0cnVlOwogICAgfQp9Cgo=" + }, + "timer": null, + "view": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "completed", + "stateType": 3, + "subType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "SubProcess Completed" + }, + { + "language": "tr-TR", + "label": "Alt İşlem Tamamlandı" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [] + } + ] + } +} \ No newline at end of file diff --git a/core/Workflows/subflow-test/src/BusySubprocessStartMapping.csx b/core/Workflows/subflow-test/src/BusySubprocessStartMapping.csx new file mode 100644 index 0000000..649ab8f --- /dev/null +++ b/core/Workflows/subflow-test/src/BusySubprocessStartMapping.csx @@ -0,0 +1,58 @@ +using System; +using System.Threading.Tasks; +using BBT.Workflow.Definitions; +using BBT.Workflow.Scripting; +using BBT.Workflow.Scripting.Functions; + +/// +/// Busy SubProcess Start Mapping - Initializes the subprocess that will run while parent is busy +/// This subprocess will complete its work and trigger the parent to exit busy state +/// +public class BusySubprocessStartMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public async Task OutputHandler(ScriptContext context) + { + try + { + LogInformation("BusySubprocessStartMapping - Starting busy state subprocess"); + + var inputData = context.Instance?.Data; + + return new ScriptResponse + { + Key = "busy-subprocess-started", + Data = new + { + subprocessWorkflowId = context.Workflow?.Key, + subprocessInstanceId = context.Instance?.Id, + parentInstanceId = inputData?.grandchildInstanceId, + parentWorkflowId = inputData?.grandchildWorkflowId, + startedAt = DateTime.UtcNow, + status = "processing", + message = "Busy state subprocess started - will trigger parent when complete", + processingTask = new + { + taskType = "background-processing", + expectedDuration = "5-10 seconds", + willTriggerParent = true + } + }, + Tags = new[] { "busy-subprocess", "started", "background-task" } + }; + } + catch (Exception ex) + { + LogError("BusySubprocessStartMapping - Error: {0}", args: new object?[] { ex.Message }); + return new ScriptResponse + { + Key = "busy-subprocess-start-error", + Data = new { error = ex.Message } + }; + } + } +} diff --git a/core/Workflows/subflow-test/src/ChildToGrandchildSubFlowMapping.csx b/core/Workflows/subflow-test/src/ChildToGrandchildSubFlowMapping.csx new file mode 100644 index 0000000..dae94b2 --- /dev/null +++ b/core/Workflows/subflow-test/src/ChildToGrandchildSubFlowMapping.csx @@ -0,0 +1,117 @@ +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Threading.Tasks; +using BBT.Workflow.Definitions; +using BBT.Workflow.Scripting; +using BBT.Workflow.Scripting.Functions; + +/// +/// Child to Grandchild SubFlow Mapping - Handles input/output for grandchild subflow state +/// This mapping prepares data for grandchild subflow and processes its result. +/// Manages the Level 2 to Level 3 transition in the subflow hierarchy. +/// +public class ChildToGrandchildSubFlowMapping : ScriptBase, ISubFlowMapping +{ + /// + /// Prepares input data for the grandchild subflow + /// + public async Task InputHandler(ScriptContext context) + { + try + { + LogInformation("ChildToGrandchildSubFlowMapping - Preparing grandchild subflow input data"); + + // Prepare data to pass to grandchild workflow using Dictionary + var testContext = new Dictionary + { + ["testType"] = "deep-nested-view-extension-longpolling", + ["expectViewData"] = true, + ["expectExtensionData"] = true, + ["nestingLevel"] = 3 + }; + + var inputData = new Dictionary + { + ["childInstanceId"] = context.Instance?.Id, + ["childWorkflowId"] = context.Workflow?.Key ?? string.Empty, + ["parentInstanceId"] = context.Instance?.Data?.parentInstanceId, + ["parentWorkflowId"] = context.Instance?.Data?.parentWorkflowId, + ["initiatedAt"] = DateTime.UtcNow, + ["initiatedBy"] = "child-workflow", + ["nestingLevel"] = 3, + ["testContext"] = testContext + }; + + return new ScriptResponse + { + Key = context.Instance?.Key, + Data = inputData, + Tags = new[] { "subflow-test", "grandchild-subflow-input", "level-3" } + }; + } + catch (Exception ex) + { + LogError("ChildToGrandchildSubFlowMapping InputHandler - Error: {0}", args: new object?[] { ex.Message }); + return new ScriptResponse + { + Key = "grandchild-subflow-input-error", + Data = new Dictionary { ["error"] = ex.Message } + }; + } + } + + /// + /// Processes the result from the completed grandchild subflow + /// + public async Task OutputHandler(ScriptContext context) + { + try + { + LogInformation("ChildToGrandchildSubFlowMapping - Processing grandchild subflow output data"); + + // Get grandchild subflow result data + var grandchildResult = context.Body; + + var grandchildResultData = new Dictionary + { + ["grandchildInstanceData"] = grandchildResult, + ["processed"] = true, + ["viewTestResult"] = "success", + ["extensionTestResult"] = "success", + ["deepNestedTestResult"] = "success" + }; + + var childContinuation = new Dictionary + { + ["canProceed"] = true, + ["nextState"] = "child-after-grandchild" + }; + + var outputData = new Dictionary + { + ["grandchildSubflowCompleted"] = true, + ["completedAt"] = DateTime.UtcNow, + ["grandchildResult"] = grandchildResultData, + ["childContinuation"] = childContinuation, + ["nestingLevel"] = 3 + }; + + return new ScriptResponse + { + Key = "grandchild-subflow-output-processed", + Data = outputData, + Tags = new[] { "subflow-test", "grandchild-subflow-output", "completed", "level-3" } + }; + } + catch (Exception ex) + { + LogError("ChildToGrandchildSubFlowMapping OutputHandler - Error: {0}", args: new object?[] { ex.Message }); + return new ScriptResponse + { + Key = "grandchild-subflow-output-error", + Data = new Dictionary { ["error"] = ex.Message } + }; + } + } +} diff --git a/core/Workflows/subflow-test/src/GrandchildCompleteMapping.csx b/core/Workflows/subflow-test/src/GrandchildCompleteMapping.csx new file mode 100644 index 0000000..b65ae9c --- /dev/null +++ b/core/Workflows/subflow-test/src/GrandchildCompleteMapping.csx @@ -0,0 +1,63 @@ +using System; +using System.Threading.Tasks; +using BBT.Workflow.Definitions; +using BBT.Workflow.Scripting; +using BBT.Workflow.Scripting.Functions; + +/// +/// Grandchild Complete Mapping - Marks the grandchild workflow as completed +/// This data will be returned to the child workflow (Level 2) +/// +public class GrandchildCompleteMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public async Task OutputHandler(ScriptContext context) + { + try + { + LogInformation("GrandchildCompleteMapping - Completing grandchild workflow (Level 3)"); + + var inputData = context.Instance?.Data; + + return new ScriptResponse + { + Key = "grandchild-complete-success", + Data = new + { + completedAt = DateTime.UtcNow, + status = "completed", + nestingLevel = 3, + result = new + { + grandchildWorkflowId = context.Workflow?.Key, + grandchildInstanceId = context.Instance?.Id, + success = true, + message = "Grandchild workflow completed successfully (Level 3)", + dataForChild = new + { + grandchildResult = "processed", + processingTime = 100, + viewTestPassed = true, + extensionTestPassed = true, + deepNestedTestPassed = true + } + } + }, + Tags = new[] { "subflow-test", "grandchild-workflow", "completed", "level-3" } + }; + } + catch (Exception ex) + { + LogError("GrandchildCompleteMapping - Error: {0}", args: new object?[] { ex.Message }); + return new ScriptResponse + { + Key = "grandchild-complete-error", + Data = new { error = ex.Message } + }; + } + } +} diff --git a/core/Workflows/subflow-test/src/GrandchildStartMapping.csx b/core/Workflows/subflow-test/src/GrandchildStartMapping.csx new file mode 100644 index 0000000..2f97b62 --- /dev/null +++ b/core/Workflows/subflow-test/src/GrandchildStartMapping.csx @@ -0,0 +1,64 @@ +using System; +using System.Threading.Tasks; +using BBT.Workflow.Definitions; +using BBT.Workflow.Scripting; +using BBT.Workflow.Scripting.Functions; + +/// +/// Grandchild Start Mapping - Initializes the grandchild workflow with data from child +/// This is the deepest level (level 3) in the subflow hierarchy. +/// +public class GrandchildStartMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + return Task.FromResult(new ScriptResponse()); + } + + public async Task OutputHandler(ScriptContext context) + { + try + { + LogInformation("GrandchildStartMapping - Initializing grandchild workflow (Level 3)"); + + var inputData = context.Instance?.Data; + + return new ScriptResponse + { + Key = "grandchild-start-success", + Data = new + { + grandchildWorkflowId = context.Workflow?.Key, + grandchildInstanceId = context.Instance?.Id, + childInstanceId = inputData?.childInstanceId, + childWorkflowId = inputData?.childWorkflowId, + parentInstanceId = inputData?.parentInstanceId, + parentWorkflowId = inputData?.parentWorkflowId, + nestingLevel = 3, + startedAt = DateTime.UtcNow, + status = "active", + message = "Grandchild workflow started successfully (Level 3)", + grandchildContext = new + { + isSubflow = true, + isDeepNested = true, + blocksParent = true, + viewTestEnabled = true, + extensionTestEnabled = true, + hierarchyPath = "Parent > Child > Grandchild" + } + }, + Tags = new[] { "subflow-test", "grandchild-workflow", "started", "level-3" } + }; + } + catch (Exception ex) + { + LogError("GrandchildStartMapping - Error: {0}", args: new object?[] { ex.Message }); + return new ScriptResponse + { + Key = "grandchild-start-error", + Data = new { error = ex.Message } + }; + } + } +} diff --git a/core/Workflows/subflow-test/src/StartBusySubprocessMapping.csx b/core/Workflows/subflow-test/src/StartBusySubprocessMapping.csx new file mode 100644 index 0000000..0ec35c7 --- /dev/null +++ b/core/Workflows/subflow-test/src/StartBusySubprocessMapping.csx @@ -0,0 +1,89 @@ +using System; +using System.Threading.Tasks; +using BBT.Workflow.Definitions; +using BBT.Workflow.Scripting; +using BBT.Workflow.Scripting.Functions; + +/// +/// Start Busy SubProcess Mapping - Starts the subprocess when grandchild enters busy state +/// This mapping configures the SubProcessTask to launch an independent subprocess +/// +public class StartBusySubprocessMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + try + { + var subProcessTask = task as SubProcessTask; + + LogInformation("StartBusySubprocessMapping - Preparing to start busy subprocess"); + + // Configure subprocess + subProcessTask.SetDomain("core"); + subProcessTask.SetFlow("sys-flows"); + subProcessTask.SetKey("busy-subprocess-workflow"); + + // Prepare subprocess data - include parent instance ID so subprocess can trigger back + subProcessTask.SetBody(new + { + grandchildInstanceId = context.Instance?.Id, + grandchildWorkflowId = context.Workflow?.Key, + parentInstanceId = context.Instance?.Data?.childInstanceId, + startedAt = DateTime.UtcNow, + taskType = "busy-state-processing", + context = new + { + nestingLevel = 3, + testType = "busy-state-test", + expectedBehavior = "subprocess-will-trigger-parent" + } + }); + + LogInformation("StartBusySubprocessMapping - Subprocess configured and ready to launch"); + + return Task.FromResult(new ScriptResponse + { + Data = context.Instance?.Data + }); + } + catch (Exception ex) + { + LogError("StartBusySubprocessMapping - Error: {0}", args: new object?[] { ex.Message }); + return Task.FromResult(new ScriptResponse + { + Key = "subprocess-start-error", + Data = new { error = ex.Message } + }); + } + } + + public async Task OutputHandler(ScriptContext context) + { + try + { + LogInformation("StartBusySubprocessMapping - Subprocess launched successfully"); + + // SubProcess is fire-and-forget, just track that it was initiated + return new ScriptResponse + { + Data = new + { + subprocessLaunched = true, + subprocessInstanceId = context.Body?.data?.instanceId, + launchedAt = DateTime.UtcNow, + status = "BUSY", + message = "Subprocess launched - grandchild now in busy state waiting for subprocess to complete" + } + }; + } + catch (Exception ex) + { + LogError("StartBusySubprocessMapping - Output handler error: {0}", args: new object?[] { ex.Message }); + return new ScriptResponse + { + Key = "output-error", + Data = new { error = ex.Message } + }; + } + } +} diff --git a/core/Workflows/subflow-test/src/TriggerParentBusyCompletionMapping.csx b/core/Workflows/subflow-test/src/TriggerParentBusyCompletionMapping.csx new file mode 100644 index 0000000..0dfb3f1 --- /dev/null +++ b/core/Workflows/subflow-test/src/TriggerParentBusyCompletionMapping.csx @@ -0,0 +1,121 @@ +using System; +using System.Threading.Tasks; +using BBT.Workflow.Definitions; +using BBT.Workflow.Scripting; +using BBT.Workflow.Scripting.Functions; + +/// +/// Trigger Parent Busy Completion Mapping - Triggers the parent workflow to exit busy state +/// This mapping uses DirectTriggerTask to trigger the 'complete-busy-state' transition on the parent +/// +public class TriggerParentBusyCompletionMapping : ScriptBase, IMapping +{ + public Task InputHandler(WorkflowTask task, ScriptContext context) + { + try + { + var directTriggerTask = task as DirectTriggerTask; + + LogInformation("TriggerParentBusyCompletionMapping - Preparing to trigger parent workflow busy completion"); + + // Get parent instance ID from subprocess input data + var parentInstanceId = context.Instance?.Data?.parentInstanceId; + + if (string.IsNullOrEmpty(parentInstanceId)) + { + LogError("TriggerParentBusyCompletionMapping - Parent instance ID not found"); + return Task.FromResult(new ScriptResponse + { + Key = "trigger-error", + Data = new { error = "Parent instance ID not found" } + }); + } + + // Set the target instance and transition + directTriggerTask.SetDomain("core"); + directTriggerTask.SetFlow("sys-flows"); + directTriggerTask.SetInstance(parentInstanceId); + directTriggerTask.SetTransitionName("complete-busy-state"); + + // Prepare transition payload + directTriggerTask.SetBody(new + { + subprocessCompleted = true, + subprocessInstanceId = context.Instance?.Id, + completedAt = DateTime.UtcNow, + result = new + { + success = true, + message = "Subprocess completed successfully, triggering parent busy state completion", + processingTime = "5 seconds", + data = new + { + busyStateTestPassed = true, + subprocessExecuted = true, + parentTriggered = true + } + } + }); + + LogInformation("TriggerParentBusyCompletionMapping - Triggering transition 'complete-busy-state' on parent instance {0}", args: new object?[] { parentInstanceId }); + + return Task.FromResult(new ScriptResponse + { + Data = context.Instance?.Data + }); + } + catch (Exception ex) + { + LogError("TriggerParentBusyCompletionMapping - Error: {0}", args: new object?[] { ex.Message }); + return Task.FromResult(new ScriptResponse + { + Key = "trigger-error", + Data = new { error = ex.Message } + }); + } + } + + public async Task OutputHandler(ScriptContext context) + { + try + { + var response = new ScriptResponse(); + + if (context.Body?.isSuccess == true) + { + LogInformation("TriggerParentBusyCompletionMapping - Parent transition triggered successfully"); + + response.Data = new + { + transitionTriggered = true, + triggeredAt = DateTime.UtcNow, + parentNewState = context.Body.data?.currentState, + status = "PARENT_TRANSITION_SUCCESS", + message = "Parent workflow successfully transitioned from busy state" + }; + } + else + { + LogError("TriggerParentBusyCompletionMapping - Parent transition failed: {0}", args: new object?[] { context.Body?.errorMessage }); + + response.Data = new + { + transitionTriggered = false, + error = context.Body?.errorMessage, + status = "PARENT_TRANSITION_FAILED" + }; + } + + return response; + } + catch (Exception ex) + { + LogError("TriggerParentBusyCompletionMapping - Output handler error: {0}", args: new object?[] { ex.Message }); + return new ScriptResponse + { + Key = "output-error", + Data = new { error = ex.Message } + }; + } + } +} diff --git a/core/Workflows/subflow-test/subflow-view-test-child.json b/core/Workflows/subflow-test/subflow-view-test-child.json index 5707784..ca4c419 100644 --- a/core/Workflows/subflow-test/subflow-view-test-child.json +++ b/core/Workflows/subflow-test/subflow-view-test-child.json @@ -8,7 +8,8 @@ "child-workflow", "view-test", "extension-test", - "longpolling-test" + "longpolling-test", + "nested-subflow" ], "attributes": { "type": "S", @@ -36,7 +37,7 @@ "sharedTransitions": [], "startTransition": { "key": "start-child", - "target": "child-active", + "target": "child-initial", "triggerType": 0, "versionStrategy": "Minor", "labels": [ @@ -67,18 +68,130 @@ }, "states": [ { - "key": "child-active", + "key": "child-initial", "stateType": 1, "subType": 0, "versionStrategy": "Minor", "labels": [ { "language": "en-US", - "label": "Child Workflow Active" + "label": "Child Initial State" }, { "language": "tr-TR", - "label": "Alt Akış Aktif" + "label": "Alt Akış Başlangıç Durumu" + } + ], + "view": { + "view": { + "key": "child-workflow-view", + "domain": "core", + "version": "1.0.0", + "flow": "sys-views" + }, + "loadData": true, + "extensions": [ + "child-data-extension" + ] + }, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "start-grandchild-subflow", + "target": "waiting-for-grandchild", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Start Grandchild SubFlow" + }, + { + "language": "tr-TR", + "label": "Torun Alt Akışı Başlat" + } + ], + "schema": null, + "rule": null, + "timer": null, + "view": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "waiting-for-grandchild", + "stateType": 4, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Waiting for Grandchild SubFlow" + }, + { + "language": "tr-TR", + "label": "Torun Alt Akış Bekleniyor" + } + ], + "view": null, + "subFlow": { + "type": "S", + "process": { + "key": "subflow-view-test-grandchild", + "domain": "core", + "version": "1.0.0", + "flow": "sys-flows" + }, + "mapping": { + "location": "./src/ChildToGrandchildSubFlowMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uQ29sbGVjdGlvbnMuR2VuZXJpYzsKdXNpbmcgU3lzdGVtLkR5bmFtaWM7CnVzaW5nIFN5c3RlbS5UaHJlYWRpbmcuVGFza3M7CnVzaW5nIEJCVC5Xb3JrZmxvdy5EZWZpbml0aW9uczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZy5GdW5jdGlvbnM7CgovLy8gPHN1bW1hcnk+Ci8vLyBDaGlsZCB0byBHcmFuZGNoaWxkIFN1YkZsb3cgTWFwcGluZyAtIEhhbmRsZXMgaW5wdXQvb3V0cHV0IGZvciBncmFuZGNoaWxkIHN1YmZsb3cgc3RhdGUKLy8vIFRoaXMgbWFwcGluZyBwcmVwYXJlcyBkYXRhIGZvciBncmFuZGNoaWxkIHN1YmZsb3cgYW5kIHByb2Nlc3NlcyBpdHMgcmVzdWx0LgovLy8gTWFuYWdlcyB0aGUgTGV2ZWwgMiB0byBMZXZlbCAzIHRyYW5zaXRpb24gaW4gdGhlIHN1YmZsb3cgaGllcmFyY2h5LgovLy8gPC9zdW1tYXJ5PgpwdWJsaWMgY2xhc3MgQ2hpbGRUb0dyYW5kY2hpbGRTdWJGbG93TWFwcGluZyA6IFNjcmlwdEJhc2UsIElTdWJGbG93TWFwcGluZwp7CiAgICAvLy8gPHN1bW1hcnk+CiAgICAvLy8gUHJlcGFyZXMgaW5wdXQgZGF0YSBmb3IgdGhlIGdyYW5kY2hpbGQgc3ViZmxvdwogICAgLy8vIDwvc3VtbWFyeT4KICAgIHB1YmxpYyBhc3luYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBJbnB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmcgLSBQcmVwYXJpbmcgZ3JhbmRjaGlsZCBzdWJmbG93IGlucHV0IGRhdGEiKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFByZXBhcmUgZGF0YSB0byBwYXNzIHRvIGdyYW5kY2hpbGQgd29ya2Zsb3cgdXNpbmcgRGljdGlvbmFyeQogICAgICAgICAgICB2YXIgdGVzdENvbnRleHQgPSBuZXcgRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD4KICAgICAgICAgICAgewogICAgICAgICAgICAgICAgWyJ0ZXN0VHlwZSJdID0gImRlZXAtbmVzdGVkLXZpZXctZXh0ZW5zaW9uLWxvbmdwb2xsaW5nIiwKICAgICAgICAgICAgICAgIFsiZXhwZWN0Vmlld0RhdGEiXSA9IHRydWUsCiAgICAgICAgICAgICAgICBbImV4cGVjdEV4dGVuc2lvbkRhdGEiXSA9IHRydWUsCiAgICAgICAgICAgICAgICBbIm5lc3RpbmdMZXZlbCJdID0gMwogICAgICAgICAgICB9OwogICAgICAgICAgICAKICAgICAgICAgICAgdmFyIGlucHV0RGF0YSA9IG5ldyBEaWN0aW9uYXJ5PHN0cmluZywgb2JqZWN0PgogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBbImNoaWxkSW5zdGFuY2VJZCJdID0gY29udGV4dC5JbnN0YW5jZT8uSWQsCiAgICAgICAgICAgICAgICBbImNoaWxkV29ya2Zsb3dJZCJdID0gY29udGV4dC5Xb3JrZmxvdz8uS2V5ID8/IHN0cmluZy5FbXB0eSwKICAgICAgICAgICAgICAgIFsicGFyZW50SW5zdGFuY2VJZCJdID0gY29udGV4dC5JbnN0YW5jZT8uRGF0YT8ucGFyZW50SW5zdGFuY2VJZCwKICAgICAgICAgICAgICAgIFsicGFyZW50V29ya2Zsb3dJZCJdID0gY29udGV4dC5JbnN0YW5jZT8uRGF0YT8ucGFyZW50V29ya2Zsb3dJZCwKICAgICAgICAgICAgICAgIFsiaW5pdGlhdGVkQXQiXSA9IERhdGVUaW1lLlV0Y05vdywKICAgICAgICAgICAgICAgIFsiaW5pdGlhdGVkQnkiXSA9ICJjaGlsZC13b3JrZmxvdyIsCiAgICAgICAgICAgICAgICBbIm5lc3RpbmdMZXZlbCJdID0gMywKICAgICAgICAgICAgICAgIFsidGVzdENvbnRleHQiXSA9IHRlc3RDb250ZXh0CiAgICAgICAgICAgIH07CiAgICAgICAgICAgIAogICAgICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9IGNvbnRleHQuSW5zdGFuY2U/LktleSwKICAgICAgICAgICAgICAgIERhdGEgPSBpbnB1dERhdGEsCiAgICAgICAgICAgICAgICBUYWdzID0gbmV3W10geyAic3ViZmxvdy10ZXN0IiwgImdyYW5kY2hpbGQtc3ViZmxvdy1pbnB1dCIsICJsZXZlbC0zIiB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigiQ2hpbGRUb0dyYW5kY2hpbGRTdWJGbG93TWFwcGluZyBJbnB1dEhhbmRsZXIgLSBFcnJvcjogezB9IiwgYXJnczogbmV3IG9iamVjdD9bXSB7IGV4Lk1lc3NhZ2UgfSk7CiAgICAgICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImdyYW5kY2hpbGQtc3ViZmxvdy1pbnB1dC1lcnJvciIsCiAgICAgICAgICAgICAgICBEYXRhID0gbmV3IERpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+IHsgWyJlcnJvciJdID0gZXguTWVzc2FnZSB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgfQoKICAgIC8vLyA8c3VtbWFyeT4KICAgIC8vLyBQcm9jZXNzZXMgdGhlIHJlc3VsdCBmcm9tIHRoZSBjb21wbGV0ZWQgZ3JhbmRjaGlsZCBzdWJmbG93CiAgICAvLy8gPC9zdW1tYXJ5PgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIkNoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmcgLSBQcm9jZXNzaW5nIGdyYW5kY2hpbGQgc3ViZmxvdyBvdXRwdXQgZGF0YSIpOwogICAgICAgICAgICAKICAgICAgICAgICAgLy8gR2V0IGdyYW5kY2hpbGQgc3ViZmxvdyByZXN1bHQgZGF0YQogICAgICAgICAgICB2YXIgZ3JhbmRjaGlsZFJlc3VsdCA9IGNvbnRleHQuQm9keTsKICAgICAgICAgICAgCiAgICAgICAgICAgIHZhciBncmFuZGNoaWxkUmVzdWx0RGF0YSA9IG5ldyBEaWN0aW9uYXJ5PHN0cmluZywgb2JqZWN0PgogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBbImdyYW5kY2hpbGRJbnN0YW5jZURhdGEiXSA9IGdyYW5kY2hpbGRSZXN1bHQsCiAgICAgICAgICAgICAgICBbInByb2Nlc3NlZCJdID0gdHJ1ZSwKICAgICAgICAgICAgICAgIFsidmlld1Rlc3RSZXN1bHQiXSA9ICJzdWNjZXNzIiwKICAgICAgICAgICAgICAgIFsiZXh0ZW5zaW9uVGVzdFJlc3VsdCJdID0gInN1Y2Nlc3MiLAogICAgICAgICAgICAgICAgWyJkZWVwTmVzdGVkVGVzdFJlc3VsdCJdID0gInN1Y2Nlc3MiCiAgICAgICAgICAgIH07CiAgICAgICAgICAgIAogICAgICAgICAgICB2YXIgY2hpbGRDb250aW51YXRpb24gPSBuZXcgRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD4KICAgICAgICAgICAgewogICAgICAgICAgICAgICAgWyJjYW5Qcm9jZWVkIl0gPSB0cnVlLAogICAgICAgICAgICAgICAgWyJuZXh0U3RhdGUiXSA9ICJjaGlsZC1hZnRlci1ncmFuZGNoaWxkIgogICAgICAgICAgICB9OwogICAgICAgICAgICAKICAgICAgICAgICAgdmFyIG91dHB1dERhdGEgPSBuZXcgRGljdGlvbmFyeTxzdHJpbmcsIG9iamVjdD4KICAgICAgICAgICAgewogICAgICAgICAgICAgICAgWyJncmFuZGNoaWxkU3ViZmxvd0NvbXBsZXRlZCJdID0gdHJ1ZSwKICAgICAgICAgICAgICAgIFsiY29tcGxldGVkQXQiXSA9IERhdGVUaW1lLlV0Y05vdywKICAgICAgICAgICAgICAgIFsiZ3JhbmRjaGlsZFJlc3VsdCJdID0gZ3JhbmRjaGlsZFJlc3VsdERhdGEsCiAgICAgICAgICAgICAgICBbImNoaWxkQ29udGludWF0aW9uIl0gPSBjaGlsZENvbnRpbnVhdGlvbiwKICAgICAgICAgICAgICAgIFsibmVzdGluZ0xldmVsIl0gPSAzCiAgICAgICAgICAgIH07CiAgICAgICAgICAgIAogICAgICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJncmFuZGNoaWxkLXN1YmZsb3ctb3V0cHV0LXByb2Nlc3NlZCIsCiAgICAgICAgICAgICAgICBEYXRhID0gb3V0cHV0RGF0YSwKICAgICAgICAgICAgICAgIFRhZ3MgPSBuZXdbXSB7ICJzdWJmbG93LXRlc3QiLCAiZ3JhbmRjaGlsZC1zdWJmbG93LW91dHB1dCIsICJjb21wbGV0ZWQiLCAibGV2ZWwtMyIgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgTG9nRXJyb3IoIkNoaWxkVG9HcmFuZGNoaWxkU3ViRmxvd01hcHBpbmcgT3V0cHV0SGFuZGxlciAtIEVycm9yOiB7MH0iLCBhcmdzOiBuZXcgb2JqZWN0P1tdIHsgZXguTWVzc2FnZSB9KTsKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiZ3JhbmRjaGlsZC1zdWJmbG93LW91dHB1dC1lcnJvciIsCiAgICAgICAgICAgICAgICBEYXRhID0gbmV3IERpY3Rpb25hcnk8c3RyaW5nLCBvYmplY3Q+IHsgWyJlcnJvciJdID0gZXguTWVzc2FnZSB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgfQp9Cg==" + } + }, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "grandchild-subflow-completed", + "target": "child-after-grandchild", + "triggerType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Grandchild SubFlow Completed" + }, + { + "language": "tr-TR", + "label": "Torun Alt Akış Tamamlandı" + } + ], + "schema": null, + "rule": { + "location": "./src/AlwaysTrueRule.csx", + "code": "dXNpbmcgU3lzdGVtLlRocmVhZGluZy5UYXNrczsKdXNpbmcgQkJULldvcmtmbG93LlNjcmlwdGluZzsKCi8vLyA8c3VtbWFyeT4KLy8vIEFsd2F5cyBUcnVlIFJ1bGUgLSBVc2VkIGZvciBhdXRvIHRyYW5zaXRpb25zIHRoYXQgc2hvdWxkIGFsd2F5cyBwcm9jZWVkCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBBbHdheXNUcnVlUnVsZSA6IElDb25kaXRpb25NYXBwaW5nCnsKICAgIHB1YmxpYyBhc3luYyBUYXNrPGJvb2w+IEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiB0cnVlOwogICAgfQp9Cgo=" + }, + "timer": null, + "view": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "child-after-grandchild", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "After Grandchild SubFlow State" + }, + { + "language": "tr-TR", + "label": "Torun Alt Akış Sonrası Durumu" } ], "view": { diff --git a/core/Workflows/subflow-test/subflow-view-test-grandchild.json b/core/Workflows/subflow-test/subflow-view-test-grandchild.json new file mode 100644 index 0000000..1315292 --- /dev/null +++ b/core/Workflows/subflow-test/subflow-view-test-grandchild.json @@ -0,0 +1,262 @@ +{ + "key": "subflow-view-test-grandchild", + "flow": "sys-flows", + "domain": "core", + "version": "1.0.0", + "tags": [ + "subflow-test", + "grandchild-workflow", + "view-test", + "extension-test", + "longpolling-test", + "deep-nested-test", + "busy-state-test" + ], + "attributes": { + "type": "S", + "timeout": null, + "labels": [ + { + "language": "en-US", + "label": "SubFlow View Test - Grandchild Workflow" + }, + { + "language": "tr-TR", + "label": "Alt Akış Görünüm Testi - Torun Akış" + } + ], + "functions": [], + "features": [], + "extensions": [ + { + "key": "grandchild-data-extension", + "domain": "core", + "version": "1.0.0", + "flow": "sys-extensions" + } + ], + "sharedTransitions": [], + "startTransition": { + "key": "start-grandchild", + "target": "grandchild-active", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Start Grandchild Workflow" + }, + { + "language": "tr-TR", + "label": "Torun Akışı Başlat" + } + ], + "onExecutionTasks": [ + { + "order": 1, + "task": { + "key": "script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/GrandchildStartMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gR3JhbmRjaGlsZCBTdGFydCBNYXBwaW5nIC0gSW5pdGlhbGl6ZXMgdGhlIGdyYW5kY2hpbGQgd29ya2Zsb3cgd2l0aCBkYXRhIGZyb20gY2hpbGQKLy8vIFRoaXMgaXMgdGhlIGRlZXBlc3QgbGV2ZWwgKGxldmVsIDMpIGluIHRoZSBzdWJmbG93IGhpZXJhcmNoeS4KLy8vIDwvc3VtbWFyeT4KcHVibGljIGNsYXNzIEdyYW5kY2hpbGRTdGFydE1hcHBpbmcgOiBTY3JpcHRCYXNlLCBJTWFwcGluZwp7CiAgICBwdWJsaWMgVGFzazxTY3JpcHRSZXNwb25zZT4gSW5wdXRIYW5kbGVyKFdvcmtmbG93VGFzayB0YXNrLCBTY3JpcHRDb250ZXh0IGNvbnRleHQpCiAgICB7CiAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UoKSk7CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIkdyYW5kY2hpbGRTdGFydE1hcHBpbmcgLSBJbml0aWFsaXppbmcgZ3JhbmRjaGlsZCB3b3JrZmxvdyAoTGV2ZWwgMykiKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIHZhciBpbnB1dERhdGEgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhOwogICAgICAgICAgICAKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiZ3JhbmRjaGlsZC1zdGFydC1zdWNjZXNzIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBncmFuZGNoaWxkV29ya2Zsb3dJZCA9IGNvbnRleHQuV29ya2Zsb3c/LktleSwKICAgICAgICAgICAgICAgICAgICBncmFuZGNoaWxkSW5zdGFuY2VJZCA9IGNvbnRleHQuSW5zdGFuY2U/LklkLAogICAgICAgICAgICAgICAgICAgIGNoaWxkSW5zdGFuY2VJZCA9IGlucHV0RGF0YT8uY2hpbGRJbnN0YW5jZUlkLAogICAgICAgICAgICAgICAgICAgIGNoaWxkV29ya2Zsb3dJZCA9IGlucHV0RGF0YT8uY2hpbGRXb3JrZmxvd0lkLAogICAgICAgICAgICAgICAgICAgIHBhcmVudEluc3RhbmNlSWQgPSBpbnB1dERhdGE/LnBhcmVudEluc3RhbmNlSWQsCiAgICAgICAgICAgICAgICAgICAgcGFyZW50V29ya2Zsb3dJZCA9IGlucHV0RGF0YT8ucGFyZW50V29ya2Zsb3dJZCwKICAgICAgICAgICAgICAgICAgICBuZXN0aW5nTGV2ZWwgPSAzLAogICAgICAgICAgICAgICAgICAgIHN0YXJ0ZWRBdCA9IERhdGVUaW1lLlV0Y05vdywKICAgICAgICAgICAgICAgICAgICBzdGF0dXMgPSAiYWN0aXZlIiwKICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gIkdyYW5kY2hpbGQgd29ya2Zsb3cgc3RhcnRlZCBzdWNjZXNzZnVsbHkgKExldmVsIDMpIiwKICAgICAgICAgICAgICAgICAgICBncmFuZGNoaWxkQ29udGV4dCA9IG5ldwogICAgICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICAgICAgaXNTdWJmbG93ID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgaXNEZWVwTmVzdGVkID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgYmxvY2tzUGFyZW50ID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgdmlld1Rlc3RFbmFibGVkID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgZXh0ZW5zaW9uVGVzdEVuYWJsZWQgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICBoaWVyYXJjaHlQYXRoID0gIlBhcmVudCA+IENoaWxkID4gR3JhbmRjaGlsZCIKICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgVGFncyA9IG5ld1tdIHsgInN1YmZsb3ctdGVzdCIsICJncmFuZGNoaWxkLXdvcmtmbG93IiwgInN0YXJ0ZWQiLCAibGV2ZWwtMyIgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgICAgICBjYXRjaCAoRXhjZXB0aW9uIGV4KQogICAgICAgIHsKICAgICAgICAgICAgTG9nRXJyb3IoIkdyYW5kY2hpbGRTdGFydE1hcHBpbmcgLSBFcnJvcjogezB9IiwgYXJnczogbmV3IG9iamVjdD9bXSB7IGV4Lk1lc3NhZ2UgfSk7CiAgICAgICAgICAgIHJldHVybiBuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgS2V5ID0gImdyYW5kY2hpbGQtc3RhcnQtZXJyb3IiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldyB7IGVycm9yID0gZXguTWVzc2FnZSB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgfQp9Cg==" + } + } + ] + }, + "states": [ + { + "key": "grandchild-active", + "stateType": 1, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Grandchild Workflow Active" + }, + { + "language": "tr-TR", + "label": "Torun Akış Aktif" + } + ], + "view": { + "view": { + "key": "grandchild-workflow-view", + "domain": "core", + "version": "1.0.0", + "flow": "sys-views" + }, + "loadData": true, + "extensions": [ + "grandchild-data-extension" + ] + }, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "enter-busy-state", + "target": "grandchild-busy", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Enter Busy State" + }, + { + "language": "tr-TR", + "label": "Meşgul Durumuna Geç" + } + ], + "schema": null, + "rule": null, + "timer": null, + "view": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "grandchild-busy", + "stateType": 2, + "subType": 5, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Grandchild Busy - Processing" + }, + { + "language": "tr-TR", + "label": "Torun Meşgul - İşleniyor" + } + ], + "view": null, + "subFlow": null, + "onEntries": [ + { + "order": 1, + "task": { + "key": "start-busy-subprocess-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/StartBusySubprocessMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gU3RhcnQgQnVzeSBTdWJQcm9jZXNzIE1hcHBpbmcgLSBTdGFydHMgdGhlIHN1YnByb2Nlc3Mgd2hlbiBncmFuZGNoaWxkIGVudGVycyBidXN5IHN0YXRlCi8vLyBUaGlzIG1hcHBpbmcgY29uZmlndXJlcyB0aGUgU3ViUHJvY2Vzc1Rhc2sgdG8gbGF1bmNoIGFuIGluZGVwZW5kZW50IHN1YnByb2Nlc3MKLy8vIDwvc3VtbWFyeT4KcHVibGljIGNsYXNzIFN0YXJ0QnVzeVN1YnByb2Nlc3NNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgdmFyIHN1YlByb2Nlc3NUYXNrID0gdGFzayBhcyBTdWJQcm9jZXNzVGFzazsKICAgICAgICAgICAgCiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJTdGFydEJ1c3lTdWJwcm9jZXNzTWFwcGluZyAtIFByZXBhcmluZyB0byBzdGFydCBidXN5IHN1YnByb2Nlc3MiKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIENvbmZpZ3VyZSBzdWJwcm9jZXNzCiAgICAgICAgICAgIHN1YlByb2Nlc3NUYXNrLlNldERvbWFpbigiY29yZSIpOwogICAgICAgICAgICBzdWJQcm9jZXNzVGFzay5TZXRGbG93KCJzeXMtZmxvd3MiKTsKICAgICAgICAgICAgc3ViUHJvY2Vzc1Rhc2suU2V0S2V5KCJidXN5LXN1YnByb2Nlc3Mtd29ya2Zsb3ciKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFByZXBhcmUgc3VicHJvY2VzcyBkYXRhIC0gaW5jbHVkZSBwYXJlbnQgaW5zdGFuY2UgSUQgc28gc3VicHJvY2VzcyBjYW4gdHJpZ2dlciBiYWNrCiAgICAgICAgICAgIHN1YlByb2Nlc3NUYXNrLlNldEJvZHkobmV3CiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIGdyYW5kY2hpbGRJbnN0YW5jZUlkID0gY29udGV4dC5JbnN0YW5jZT8uSWQsCiAgICAgICAgICAgICAgICBncmFuZGNoaWxkV29ya2Zsb3dJZCA9IGNvbnRleHQuV29ya2Zsb3c/LktleSwKICAgICAgICAgICAgICAgIHBhcmVudEluc3RhbmNlSWQgPSBjb250ZXh0Lkluc3RhbmNlPy5EYXRhPy5jaGlsZEluc3RhbmNlSWQsCiAgICAgICAgICAgICAgICBzdGFydGVkQXQgPSBEYXRlVGltZS5VdGNOb3csCiAgICAgICAgICAgICAgICB0YXNrVHlwZSA9ICJidXN5LXN0YXRlLXByb2Nlc3NpbmciLAogICAgICAgICAgICAgICAgY29udGV4dCA9IG5ldwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIG5lc3RpbmdMZXZlbCA9IDMsCiAgICAgICAgICAgICAgICAgICAgdGVzdFR5cGUgPSAiYnVzeS1zdGF0ZS10ZXN0IiwKICAgICAgICAgICAgICAgICAgICBleHBlY3RlZEJlaGF2aW9yID0gInN1YnByb2Nlc3Mtd2lsbC10cmlnZ2VyLXBhcmVudCIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfSk7CiAgICAgICAgICAgIAogICAgICAgICAgICBMb2dJbmZvcm1hdGlvbigiU3RhcnRCdXN5U3VicHJvY2Vzc01hcHBpbmcgLSBTdWJwcm9jZXNzIGNvbmZpZ3VyZWQgYW5kIHJlYWR5IHRvIGxhdW5jaCIpOwogICAgICAgICAgICAKICAgICAgICAgICAgcmV0dXJuIFRhc2suRnJvbVJlc3VsdChuZXcgU2NyaXB0UmVzcG9uc2UKICAgICAgICAgICAgewogICAgICAgICAgICAgICAgRGF0YSA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGEKICAgICAgICAgICAgfSk7CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigiU3RhcnRCdXN5U3VicHJvY2Vzc01hcHBpbmcgLSBFcnJvcjogezB9IiwgYXJnczogbmV3IG9iamVjdD9bXSB7IGV4Lk1lc3NhZ2UgfSk7CiAgICAgICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJzdWJwcm9jZXNzLXN0YXJ0LWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9KTsKICAgICAgICB9CiAgICB9CgogICAgcHVibGljIGFzeW5jIFRhc2s8U2NyaXB0UmVzcG9uc2U+IE91dHB1dEhhbmRsZXIoU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHRyeQogICAgICAgIHsKICAgICAgICAgICAgTG9nSW5mb3JtYXRpb24oIlN0YXJ0QnVzeVN1YnByb2Nlc3NNYXBwaW5nIC0gU3VicHJvY2VzcyBsYXVuY2hlZCBzdWNjZXNzZnVsbHkiKTsKICAgICAgICAgICAgCiAgICAgICAgICAgIC8vIFN1YlByb2Nlc3MgaXMgZmlyZS1hbmQtZm9yZ2V0LCBqdXN0IHRyYWNrIHRoYXQgaXQgd2FzIGluaXRpYXRlZAogICAgICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcKICAgICAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgICAgICBzdWJwcm9jZXNzTGF1bmNoZWQgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgIHN1YnByb2Nlc3NJbnN0YW5jZUlkID0gY29udGV4dC5Cb2R5Py5kYXRhPy5pbnN0YW5jZUlkLAogICAgICAgICAgICAgICAgICAgIGxhdW5jaGVkQXQgPSBEYXRlVGltZS5VdGNOb3csCiAgICAgICAgICAgICAgICAgICAgc3RhdHVzID0gIkJVU1kiLAogICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSAiU3VicHJvY2VzcyBsYXVuY2hlZCAtIGdyYW5kY2hpbGQgbm93IGluIGJ1c3kgc3RhdGUgd2FpdGluZyBmb3Igc3VicHJvY2VzcyB0byBjb21wbGV0ZSIKICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICAgICAgY2F0Y2ggKEV4Y2VwdGlvbiBleCkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0Vycm9yKCJTdGFydEJ1c3lTdWJwcm9jZXNzTWFwcGluZyAtIE91dHB1dCBoYW5kbGVyIGVycm9yOiB7MH0iLCBhcmdzOiBuZXcgb2JqZWN0P1tdIHsgZXguTWVzc2FnZSB9KTsKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAib3V0cHV0LWVycm9yIiwKICAgICAgICAgICAgICAgIERhdGEgPSBuZXcgeyBlcnJvciA9IGV4Lk1lc3NhZ2UgfQogICAgICAgICAgICB9OwogICAgICAgIH0KICAgIH0KfQo=" + } + } + ], + "onExits": [], + "transitions": [ + { + "key": "complete-busy-state", + "target": "grandchild-after-busy", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Complete Busy State (Triggered by SubProcess)" + }, + { + "language": "tr-TR", + "label": "Meşgul Durumu Tamamla (Alt İşlem Tarafından Tetiklenir)" + } + ], + "schema": null, + "rule": null, + "timer": null, + "view": null, + "onExecutionTasks": [] + } + ] + }, + { + "key": "grandchild-after-busy", + "stateType": 2, + "subType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Grandchild After Busy State" + }, + { + "language": "tr-TR", + "label": "Torun Meşgul Sonrası" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [ + { + "key": "complete-grandchild", + "target": "grandchild-completed", + "triggerType": 0, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Complete Grandchild Workflow" + }, + { + "language": "tr-TR", + "label": "Torun Akışı Tamamla" + } + ], + "schema": null, + "rule": null, + "timer": null, + "view": null, + "onExecutionTasks": [ + { + "order": 1, + "task": { + "key": "script-task", + "domain": "core", + "version": "1.0.0", + "flow": "sys-tasks" + }, + "mapping": { + "location": "./src/GrandchildCompleteMapping.csx", + "code": "dXNpbmcgU3lzdGVtOwp1c2luZyBTeXN0ZW0uVGhyZWFkaW5nLlRhc2tzOwp1c2luZyBCQlQuV29ya2Zsb3cuRGVmaW5pdGlvbnM7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmc7CnVzaW5nIEJCVC5Xb3JrZmxvdy5TY3JpcHRpbmcuRnVuY3Rpb25zOwoKLy8vIDxzdW1tYXJ5PgovLy8gR3JhbmRjaGlsZCBDb21wbGV0ZSBNYXBwaW5nIC0gTWFya3MgdGhlIGdyYW5kY2hpbGQgd29ya2Zsb3cgYXMgY29tcGxldGVkCi8vLyBUaGlzIGRhdGEgd2lsbCBiZSByZXR1cm5lZCB0byB0aGUgY2hpbGQgd29ya2Zsb3cgKExldmVsIDIpCi8vLyA8L3N1bW1hcnk+CnB1YmxpYyBjbGFzcyBHcmFuZGNoaWxkQ29tcGxldGVNYXBwaW5nIDogU2NyaXB0QmFzZSwgSU1hcHBpbmcKewogICAgcHVibGljIFRhc2s8U2NyaXB0UmVzcG9uc2U+IElucHV0SGFuZGxlcihXb3JrZmxvd1Rhc2sgdGFzaywgU2NyaXB0Q29udGV4dCBjb250ZXh0KQogICAgewogICAgICAgIHJldHVybiBUYXNrLkZyb21SZXN1bHQobmV3IFNjcmlwdFJlc3BvbnNlKCkpOwogICAgfQoKICAgIHB1YmxpYyBhc3luYyBUYXNrPFNjcmlwdFJlc3BvbnNlPiBPdXRwdXRIYW5kbGVyKFNjcmlwdENvbnRleHQgY29udGV4dCkKICAgIHsKICAgICAgICB0cnkKICAgICAgICB7CiAgICAgICAgICAgIExvZ0luZm9ybWF0aW9uKCJHcmFuZGNoaWxkQ29tcGxldGVNYXBwaW5nIC0gQ29tcGxldGluZyBncmFuZGNoaWxkIHdvcmtmbG93IChMZXZlbCAzKSIpOwogICAgICAgICAgICAKICAgICAgICAgICAgdmFyIGlucHV0RGF0YSA9IGNvbnRleHQuSW5zdGFuY2U/LkRhdGE7CiAgICAgICAgICAgIAogICAgICAgICAgICByZXR1cm4gbmV3IFNjcmlwdFJlc3BvbnNlCiAgICAgICAgICAgIHsKICAgICAgICAgICAgICAgIEtleSA9ICJncmFuZGNoaWxkLWNvbXBsZXRlLXN1Y2Nlc3MiLAogICAgICAgICAgICAgICAgRGF0YSA9IG5ldwogICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgIGNvbXBsZXRlZEF0ID0gRGF0ZVRpbWUuVXRjTm93LAogICAgICAgICAgICAgICAgICAgIHN0YXR1cyA9ICJjb21wbGV0ZWQiLAogICAgICAgICAgICAgICAgICAgIG5lc3RpbmdMZXZlbCA9IDMsCiAgICAgICAgICAgICAgICAgICAgcmVzdWx0ID0gbmV3CiAgICAgICAgICAgICAgICAgICAgewogICAgICAgICAgICAgICAgICAgICAgICBncmFuZGNoaWxkV29ya2Zsb3dJZCA9IGNvbnRleHQuV29ya2Zsb3c/LktleSwKICAgICAgICAgICAgICAgICAgICAgICAgZ3JhbmRjaGlsZEluc3RhbmNlSWQgPSBjb250ZXh0Lkluc3RhbmNlPy5JZCwKICAgICAgICAgICAgICAgICAgICAgICAgc3VjY2VzcyA9IHRydWUsCiAgICAgICAgICAgICAgICAgICAgICAgIG1lc3NhZ2UgPSAiR3JhbmRjaGlsZCB3b3JrZmxvdyBjb21wbGV0ZWQgc3VjY2Vzc2Z1bGx5IChMZXZlbCAzKSIsCiAgICAgICAgICAgICAgICAgICAgICAgIGRhdGFGb3JDaGlsZCA9IG5ldwogICAgICAgICAgICAgICAgICAgICAgICB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmFuZGNoaWxkUmVzdWx0ID0gInByb2Nlc3NlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9jZXNzaW5nVGltZSA9IDEwMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZpZXdUZXN0UGFzc2VkID0gdHJ1ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGV4dGVuc2lvblRlc3RQYXNzZWQgPSB0cnVlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZGVlcE5lc3RlZFRlc3RQYXNzZWQgPSB0cnVlCiAgICAgICAgICAgICAgICAgICAgICAgIH0KICAgICAgICAgICAgICAgICAgICB9CiAgICAgICAgICAgICAgICB9LAogICAgICAgICAgICAgICAgVGFncyA9IG5ld1tdIHsgInN1YmZsb3ctdGVzdCIsICJncmFuZGNoaWxkLXdvcmtmbG93IiwgImNvbXBsZXRlZCIsICJsZXZlbC0zIiB9CiAgICAgICAgICAgIH07CiAgICAgICAgfQogICAgICAgIGNhdGNoIChFeGNlcHRpb24gZXgpCiAgICAgICAgewogICAgICAgICAgICBMb2dFcnJvcigiR3JhbmRjaGlsZENvbXBsZXRlTWFwcGluZyAtIEVycm9yOiB7MH0iLCBhcmdzOiBuZXcgb2JqZWN0P1tdIHsgZXguTWVzc2FnZSB9KTsKICAgICAgICAgICAgcmV0dXJuIG5ldyBTY3JpcHRSZXNwb25zZQogICAgICAgICAgICB7CiAgICAgICAgICAgICAgICBLZXkgPSAiZ3JhbmRjaGlsZC1jb21wbGV0ZS1lcnJvciIsCiAgICAgICAgICAgICAgICBEYXRhID0gbmV3IHsgZXJyb3IgPSBleC5NZXNzYWdlIH0KICAgICAgICAgICAgfTsKICAgICAgICB9CiAgICB9Cn0K" + } + } + ] + } + ] + }, + { + "key": "grandchild-completed", + "stateType": 3, + "subType": 1, + "versionStrategy": "Minor", + "labels": [ + { + "language": "en-US", + "label": "Grandchild Workflow Completed" + }, + { + "language": "tr-TR", + "label": "Torun Akış Tamamlandı" + } + ], + "view": null, + "subFlow": null, + "onEntries": [], + "onExits": [], + "transitions": [] + } + ] + } +} \ No newline at end of file diff --git a/core/Workflows/subflow-test/subflow-view-test.http b/core/Workflows/subflow-test/subflow-view-test.http index bf947d9..035bef0 100644 --- a/core/Workflows/subflow-test/subflow-view-test.http +++ b/core/Workflows/subflow-test/subflow-view-test.http @@ -1,16 +1,19 @@ -### SubFlow View & Extension Test -### This file tests the longpolling view endpoint with subflow, views and extensions -### Test Goal: Verify that views and extensions work correctly for both parent and child workflows +### SubFlow View & Extension Test - 3 Level Nested SubFlows +### This file tests the longpolling view endpoint with deep nested subflows, views and extensions +### Test Goal: Verify that views and extensions work correctly for parent, child, and grandchild workflows +### +### Hierarchy: Parent (Level 1) > Child (Level 2) > Grandchild (Level 3) @baseUrl = http://localhost:4201 @apiVersion = 1 @domain = core @parentWorkflow = subflow-view-test-parent @childWorkflow = subflow-view-test-child +@grandchildWorkflow = subflow-view-test-grandchild @instanceKey = subflow-test-{{$timestamp}} ### ============================================ -### STEP 1: Start Parent Workflow +### STEP 1: Start Parent Workflow (Level 1) ### This starts the parent workflow in initial state ### ============================================ @@ -20,25 +23,25 @@ Content-Type: application/json { "key": "{{instanceKey}}", - "tags": ["subflow-test", "view-test", "extension-test", "longpolling-test"], + "tags": ["subflow-test", "view-test", "extension-test", "longpolling-test", "3-level-nested"], "attributes": { "testId": "subflow-view-extension-test-001", - "testDescription": "Testing view and extension with subflow", + "testDescription": "Testing 3-level nested subflows with view and extension", "createdAt": "{{$datetime iso8601}}" } } ### ============================================ ### STEP 2: Get Parent Workflow State (Long Polling) -### Check current state of parent workflow +### Check current state of parent workflow - should be "parent-initial" ### ============================================ # @name getParentState -GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{parentWorkflow}}/instances/e71660b4-5ba6-4644-a9f9-13eb4acacc2a/functions/state +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{parentWorkflow}}/instances/{{instanceKey}}/functions/state Accept: application/json ### ============================================ -### STEP 3: Get Parent View with Extensions +### STEP 3: Get Parent View with Extensions (Level 1) ### THIS IS THE KEY LONGPOLLING TEST FOR PARENT ### Expected: parent-workflow-view with parent-data-extension data ### ============================================ @@ -56,16 +59,16 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{parentWorkflow}}/inst Accept: application/json ### ============================================ -### STEP 5: Execute Transition to Start SubFlow -### This triggers the subflow state - parent will wait for child +### STEP 5: Execute Transition to Start SubFlow (Parent -> Child) +### This triggers the child subflow state - parent will wait for child ### ============================================ -# @name startSubflow +# @name startChildSubflow PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{parentWorkflow}}/instances/{{instanceKey}}/transitions/start-subflow Content-Type: application/json { - "message": "Starting subflow for view and extension test" + "message": "Starting child subflow for 3-level nested test" } ### ============================================ @@ -78,19 +81,16 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{parentWorkflow}}/inst Accept: application/json ### ============================================ -### STEP 7: Get Child Workflow Instance -### Find the child workflow instance that was started -### Note: You may need to replace {{childInstanceKey}} with actual child instance key +### STEP 7: Get Parent Data to Find Child Instance ID +### Check parent data to find child instance ID ### ============================================ -### -- Check parent data to find child instance ID -- - # @name getParentDataForChildId GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{parentWorkflow}}/instances/{{instanceKey}}/functions/data Accept: application/json ### ============================================ -### STEP 8: Get Child View with Extensions +### STEP 8: Get Child View with Extensions (Level 2) ### THIS IS THE KEY LONGPOLLING TEST FOR CHILD ### Expected: child-workflow-view with child-data-extension data ### Note: Replace {{childInstanceId}} with actual child instance ID from previous step @@ -104,6 +104,7 @@ Accept: application/json ### ============================================ ### STEP 9: Get Child State +### Child should be in "child-initial" state ### ============================================ # @name getChildState @@ -119,8 +120,164 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflow}}/insta Accept: application/json ### ============================================ -### STEP 11: Complete Child Workflow -### This will trigger the subflow completion and unblock parent +### STEP 11: Start Grandchild SubFlow (Child -> Grandchild) +### This triggers the grandchild subflow state - child will wait for grandchild +### ============================================ + +# @name startGrandchildSubflow +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflow}}/instances/{{childInstanceId}}/transitions/start-grandchild-subflow +Content-Type: application/json + +{ + "message": "Starting grandchild subflow for deep nested test" +} + +### ============================================ +### STEP 12: Get Child State After Grandchild SubFlow Started +### Child should be in "waiting-for-grandchild" state now +### ============================================ + +# @name getChildStateAfterGrandchild +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflow}}/instances/{{childInstanceId}}/functions/state +Accept: application/json + +### ============================================ +### STEP 13: Get Child Data to Find Grandchild Instance ID +### Check child data to find grandchild instance ID +### ============================================ + +# @name getChildDataForGrandchildId +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflow}}/instances/{{childInstanceId}}/functions/data +Accept: application/json + +### ============================================ +### STEP 14: Get Grandchild View with Extensions (Level 3) +### THIS IS THE KEY LONGPOLLING TEST FOR GRANDCHILD (DEEPEST LEVEL) +### Expected: grandchild-workflow-view with grandchild-data-extension data +### Note: Replace {{grandchildInstanceId}} with actual grandchild instance ID from previous step +### ============================================ + +@grandchildInstanceId = REPLACE_WITH_GRANDCHILD_INSTANCE_ID + +# @name getGrandchildView +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{grandchildWorkflow}}/instances/{{grandchildInstanceId}}/functions/view +Accept: application/json + +### ============================================ +### STEP 15: Get Grandchild State +### Grandchild should be in "grandchild-active" state +### ============================================ + +# @name getGrandchildState +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{grandchildWorkflow}}/instances/{{grandchildInstanceId}}/functions/state +Accept: application/json + +### ============================================ +### STEP 16: Get Grandchild Data +### ============================================ + +# @name getGrandchildData +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{grandchildWorkflow}}/instances/{{grandchildInstanceId}}/functions/data +Accept: application/json + +### ============================================ +### STEP 17: Enter Busy State (Busy State Test - subType: 5) +### This transitions grandchild to busy state and starts a subprocess +### The subprocess will trigger the transition when complete +### ============================================ + +# @name enterBusyState +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{grandchildWorkflow}}/instances/{{grandchildInstanceId}}/transitions/enter-busy-state +Content-Type: application/json + +{ + "message": "Entering busy state - subprocess will be launched" +} + +### ============================================ +### STEP 18: Check Grandchild State - Should be BUSY (subType: 5) +### The state should be "grandchild-busy" with subType: 5 +### The subprocess should be running in background +### ============================================ + +# @name getGrandchildBusyState +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{grandchildWorkflow}}/instances/{{grandchildInstanceId}}/functions/state +Accept: application/json + +### ============================================ +### STEP 19: Get Grandchild Data During Busy State +### Should show subprocess launched and busy status +### ============================================ + +# @name getGrandchildDataDuringBusy +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{grandchildWorkflow}}/instances/{{grandchildInstanceId}}/functions/data +Accept: application/json + +### ============================================ +### STEP 20: Wait for SubProcess to Complete and Trigger Transition +### The subprocess (busy-subprocess-workflow) will: +### 1. Complete its processing +### 2. Use DirectTriggerTask to trigger 'complete-busy-state' transition +### 3. Grandchild will automatically move to 'grandchild-after-busy' state +### +### NOTE: Wait ~5-10 seconds for subprocess to complete +### Then check state again to verify automatic transition occurred +### ============================================ + +### ============================================ +### STEP 21: Verify Grandchild Transitioned Out of Busy State +### The state should now be "grandchild-after-busy" (subType: 0) +### This confirms the subprocess successfully triggered the transition +### ============================================ + +# @name getGrandchildStateAfterBusy +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{grandchildWorkflow}}/instances/{{grandchildInstanceId}}/functions/state +Accept: application/json + +### ============================================ +### STEP 22: Get Grandchild Data After Busy State +### Should show subprocess completion data and trigger info +### ============================================ + +# @name getGrandchildDataAfterBusy +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{grandchildWorkflow}}/instances/{{grandchildInstanceId}}/functions/data +Accept: application/json + +### ============================================ +### STEP 23: Complete Grandchild Workflow (Level 3 -> Level 2) +### Now complete the grandchild workflow normally +### This will trigger the grandchild completion and unblock child +### ============================================ + +# @name completeGrandchild +PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{grandchildWorkflow}}/instances/{{grandchildInstanceId}}/transitions/complete-grandchild +Content-Type: application/json + +{ + "completionMessage": "Grandchild workflow completed after busy state test" +} + +### ============================================ +### STEP 24: Check Child State After Grandchild Completed +### Child should now be in "child-after-grandchild" state +### ============================================ + +# @name getChildStateAfterGrandchildComplete +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflow}}/instances/{{childInstanceId}}/functions/state +Accept: application/json + +### ============================================ +### STEP 25: Get Child View After Grandchild SubFlow +### Verify view and extensions still work after grandchild subflow completes +### ============================================ + +# @name getChildViewAfterGrandchild +GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflow}}/instances/{{childInstanceId}}/functions/view +Accept: application/json + +### ============================================ +### STEP 26: Complete Child Workflow (Level 2 -> Level 1) +### This will trigger the child completion and unblock parent ### ============================================ # @name completeChild @@ -128,11 +285,11 @@ PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{childWorkflow}}/ins Content-Type: application/json { - "completionMessage": "Child workflow completed for test" + "completionMessage": "Child workflow completed for nested test" } ### ============================================ -### STEP 12: Check Parent State After Child Completed +### STEP 27: Check Parent State After Child Completed ### Parent should now be in "parent-after-subflow" state ### ============================================ @@ -141,8 +298,8 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{parentWorkflow}}/inst Accept: application/json ### ============================================ -### STEP 13: Get Parent View After SubFlow (should show again) -### Verify view and extensions still work after subflow completes +### STEP 28: Get Parent View After SubFlow (should show again) +### Verify view and extensions still work after child subflow completes ### ============================================ # @name getParentViewAfterSubflow @@ -150,7 +307,7 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{parentWorkflow}}/inst Accept: application/json ### ============================================ -### STEP 14: Complete Parent Workflow +### STEP 29: Complete Parent Workflow (Level 1 Final) ### Final transition to complete the parent workflow ### ============================================ @@ -159,12 +316,12 @@ PATCH {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{parentWorkflow}}/in Content-Type: application/json { - "finalMessage": "Parent workflow completed successfully" + "finalMessage": "Parent workflow completed successfully with 3-level nested subflows and busy state test" } ### ============================================ -### STEP 15: Verify Final State -### Both parent should be completed +### STEP 30: Verify Final State +### Parent should be completed ### ============================================ # @name getFinalParentState @@ -172,7 +329,7 @@ GET {{baseUrl}}/api/v{{apiVersion}}/{{domain}}/workflows/{{parentWorkflow}}/inst Accept: application/json ### ============================================ -### STEP 16: Get Final Data +### STEP 31: Get Final Data ### Check all test results in the data ### ============================================ @@ -185,24 +342,62 @@ Accept: application/json ### ============================================ ### ### Test Objectives: -### 1. ✅ Parent workflow starts with own view and extensions -### 2. ✅ Parent view returns with parent-data-extension data -### 3. ✅ Parent transitions to subflow state (blocking) -### 4. ✅ Child workflow has its own view and extensions -### 5. ✅ Child view returns with child-data-extension data -### 6. ✅ Child workflow completes via manual transition -### 7. ✅ Parent unblocks and continues after child completes -### 8. ✅ Parent view still works after subflow -### 9. ✅ Parent completes via manual transition +### 1. Parent workflow starts with own view and extensions (Level 1) +### 2. Parent view returns with parent-data-extension data +### 3. Parent transitions to child subflow state (blocking) +### 4. Child workflow starts with own view and extensions (Level 2) +### 5. Child view returns with child-data-extension data +### 6. Child transitions to grandchild subflow state (blocking) +### 7. Grandchild workflow has its own view and extensions (Level 3 - Deepest) +### 8. Grandchild view returns with grandchild-data-extension data +### 9. **NEW** Grandchild enters Busy state (subType: 5) +### 10. **NEW** Busy state launches subprocess (SubProcessTask - Type 11) +### 11. **NEW** Grandchild waits in Busy state for subprocess +### 12. **NEW** SubProcess completes and triggers parent transition (DirectTriggerTask - Type 12) +### 13. **NEW** Grandchild automatically exits Busy state via triggered transition +### 14. Grandchild workflow completes via manual transition +### 15. Child unblocks and continues after grandchild completes +### 16. Child view still works after grandchild subflow +### 17. Child workflow completes via manual transition +### 18. Parent unblocks and continues after child completes +### 19. Parent view still works after child subflow +### 20. Parent completes via manual transition +### +### Workflow Hierarchy: +### ================== +### Level 1: subflow-view-test-parent +### | +### +-- Level 2: subflow-view-test-child +### | +### +-- Level 3: subflow-view-test-grandchild ### ### Key Endpoints Tested: -### - /functions/view - Long polling for view with extensions -### - /functions/state - Long polling for state -### - /functions/data - Instance data retrieval -### - /transitions/{key} - Manual transition execution +### - /functions/view - Long polling for view with extensions (all 3 levels) +### - /functions/state - Long polling for state (all 3 levels) +### - /functions/data - Instance data retrieval (all 3 levels) +### - /transitions/{key} - Manual transition execution (all 3 levels) ### ### Expected Extension Data in View Response: ### - Parent: parentExtension.data.parentConfig, parentExtension.data.parentMetadata ### - Child: childExtension.data.childConfig, childExtension.data.childMetadata +### - Grandchild: grandchildExtension.data.grandchildConfig, grandchildExtension.data.grandchildMetadata +### +### Busy State Test Details: +### ======================== +### - State subType: 5 (Busy) - New state subType for busy/processing states +### - OnEntry Task: SubProcessTask (Type 11) - Launches independent subprocess +### - SubProcess Workflow: busy-subprocess-workflow +### - SubProcess Actions: +### 1. Starts and enters "processing" state +### 2. Auto-transitions to "trigger-parent" state +### 3. Uses DirectTriggerTask (Type 12) to trigger parent's 'complete-busy-state' transition +### 4. Auto-transitions to "completed" final state +### - Expected Behavior: +### - Grandchild enters busy state → status becomes BUSY +### - SubProcess launches in background (fire-and-forget) +### - Grandchild waits for manual transition (triggered by subprocess) +### - SubProcess completes → triggers grandchild transition +### - Grandchild exits busy state → continues normal flow +### - Demonstrates: State enters → Busy → Waits → SubProcess pings → Transition → State changes ### diff --git a/core/Workflows/task-test/error-boundary-test-workflow.json b/core/Workflows/task-test/error-boundary-test-workflow.json index e3b0800..6592fc3 100644 --- a/core/Workflows/task-test/error-boundary-test-workflow.json +++ b/core/Workflows/task-test/error-boundary-test-workflow.json @@ -137,10 +137,10 @@ "priority": 100, "retryPolicy": { "maxRetries": 3, - "initialDelay": "0.00:00:02", + "initialDelay": "PT2S", "backoffType": 1, "backoffMultiplier": 2, - "maxDelay": "0.00:00:06" + "maxDelay": "PT6S" } }, { @@ -149,7 +149,7 @@ "500" ], "priority": 50, - "transition": "error-recovery" + "transition": "recovery-to-rollback" } ] } @@ -386,10 +386,10 @@ "priority": 100, "retryPolicy": { "maxRetries": 5, - "initialDelay": "0.00:00:5", + "initialDelay": "PT5S", "backoffType": 0, "backoffMultiplier": 1, - "maxDelay": "0.00:00:25" + "maxDelay": "PT25S" } } ] @@ -448,10 +448,10 @@ "transition": "timeout-state", "defaultRetryPolicy": { "maxRetries": 2, - "initialDelay": "0.00:00:05", + "initialDelay": "PT5S", "backoffType": 1, "backoffMultiplier": 1.5, - "maxDelay": "0.00:00:10" + "maxDelay": "PT10S" } } }, diff --git a/package-lock.json b/package-lock.json index 946c669..dbc7861 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,14 +10,14 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "@burgan-tech/vnext-schema": "^0.0.28" + "@burgan-tech/vnext-schema": "^0.0.33" }, "bin": { "vnext-setup": "setup.js", "vnext-template": "init.js" }, "devDependencies": { - "@burgan-tech/vnext-schema": "^0.0.28", + "@burgan-tech/vnext-schema": "^0.0.33", "ajv": "^8.12.0", "ajv-formats": "^2.1.1" }, @@ -26,9 +26,9 @@ } }, "node_modules/@burgan-tech/vnext-schema": { - "version": "0.0.28", - "resolved": "https://registry.npmjs.org/@burgan-tech/vnext-schema/-/vnext-schema-0.0.28.tgz", - "integrity": "sha512-p+ZyGXb79N4KCYaIQuzh8+6/ku7L5TcmndQ9illDmyCxXszQPwVM68pQjb/HbvsF0cDfv8kutzmgvuo6rTbC8g==", + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@burgan-tech/vnext-schema/-/vnext-schema-0.0.33.tgz", + "integrity": "sha512-xDKVPuoib0jmfAI8vYFOvXWOq8Kl+K/QyEYHLH2Veib5QupZhqhFCIrUdw/uRzivAwQ2ZogAy/dd+9PIzD23+g==", "dev": true, "license": "MIT", "engines": { diff --git a/package.json b/package.json index 0d9f614..52a1e6c 100644 --- a/package.json +++ b/package.json @@ -55,11 +55,11 @@ "node": ">=16.0.0" }, "devDependencies": { - "@burgan-tech/vnext-schema": "^0.0.28", + "@burgan-tech/vnext-schema": "^0.0.33", "ajv": "^8.12.0", "ajv-formats": "^2.1.1" }, "dependencies": { - "@burgan-tech/vnext-schema": "^0.0.28" + "@burgan-tech/vnext-schema": "^0.0.33" } } diff --git a/vnext.config.json b/vnext.config.json index 18498f2..1c32a39 100644 --- a/vnext.config.json +++ b/vnext.config.json @@ -2,8 +2,8 @@ "version": "1.0.0", "description": "core Domain Definition Configuration", "domain": "core", - "runtimeVersion": "0.0.28", - "schemaVersion": "0.0.28", + "runtimeVersion": "0.0.32", + "schemaVersion": "0.0.33", "paths": { "componentsRoot": "core", "tasks": "Tasks",